v0.9.14: Embed ast-grep-core in the tools edit path to reject syntax-breaking file edits
- Dominant language
- Rust
- Stars
- 41k
- Forks
- 3.6k
- Avg merge
- 13h 59m
- Merged PRs (30d)
- 299
Description
## Why this matters
Agent file edits currently land as pure text patches. When the model produces a
patch whose result does not parse — unbalanced delimiters, truncated blocks,
stray conflict markers — the defect is only discovered by the next compile,
test run, or reviewer. Every such failure costs a turn cycle and erodes trust
in autonomous edits. A fast, dependency-light structural check at the edit
boundary converts this class of error into a clean, retryable tool result.
`ast-grep-core` gives us tree-sitter-quality parsing for a bounded language set
without shipping per-language servers or a full structural-search feature.
## Current behavior
- `crates/tui/src/tools/apply_patch.rs` parses unified-diff shape and applies
hunks textually; `file.rs`/`file_tool.rs` write content directly. Neither
path parses the post-edit file.
- `crates/tui/src/tools/lsp.rs` exposes model-callable LSP diagnostics, but it
requires a spawned language server and is not consulted automatically on
edits — no syntax gate exists on the edit path.
- No `ast-grep`/`tree-sitter` dependency exists anywhere in the workspace
(verified via grep of all Cargo.tomls at `31146342b`).
## Desired behavior
1. Add `ast-grep-core` (pinned, conservative version) as a dependency of
`crates/tools` — the shared tool-contracts crate — exposing a small
`syntax_check(language, source)` helper crate-side.
2. The file-edit tools (`apply_patch`, `edit_file`/`file_tool` writes) run the
check on the post-edit content for supported languages before reporting
success.
3. A syntax failure returns a typed tool error naming the offending region
(line/column where available) instead of silently writing a broken file.
Decide explicitly whether the file write is rolled back or written with a
visible diagnostic; record the choice in the tool result.
4. Unsupported languages fail open — the check is skipped and the edit proceeds
as today, with no new failure mode.
5. Start with Rust + TypeScript/JavaScript; the helper's language set is
explicitly enumerable so adding grammars is a one-line change.
## Evidence
- Edit path with no post-write parse: `crates/tui/src/tools/apply_patch.rs:232`,
`crates/tui/src/tools/file.rs`, `crates/tui/src/tools/file_tool.rs`.
- Shared contracts crate to host the dependency: `crates/tools/Cargo.toml`
(currently protocol/serde/tokio only).
- LSP tool is opt-in, not a gate: `crates/tui/src/tools/lsp.rs:1`.
## Acceptance criteria
- [ ] `ast-grep-core` builds in `crates/tools` and is exercised by a unit test
over a deliberately broken Rust and TS fixture.
- [ ] An `apply_patch` edit that produces unparsable Rust returns a typed tool
error naming the region; the failure is observable to the model so it can
retry.
- [ ] A syntactically valid edit reports success unchanged (no regression in
`cargo test -p codewhale-tui` tool tests).
- [ ] Unsupported file types proceed exactly as today.
- [ ] No model-visible payloads, paths, or file contents leak into logs beyond
existing tool-result conventions.
## Related
- #3980 proposed the broader structural code search + AST-backed edit-preview
scope and was closed as not planned (2026-09-11). This issue is deliberately
narrower: syntax validation on the existing edit path, no new search surface.
Contributor guide
Research direction
Read crates/tools/Cargo.toml and trace the edit flow through crates/tui/src/tools/apply_patch.rs, file.rs, and file_tool.rs; compare the existing LSP handling in lsp.rs. Run the relevant workspace tests, then verify Rust and TypeScript/JavaScript fixtures report typed syntax failures while valid edits and unsupported file types retain their current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, rust, typescript
- Domain
- cli, testing, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 66/100