Skip to content

idea: pre-write syntax guard hook for do-mode edits — inspired by SWE-agent's ACI edit-tool linting #849

Description

@github-actions

Summary

Add an optional PreToolUse hook check — sibling to hooks/path-guard.js, reusing its exact enforcement point — that syntax-checks the result of a write-tool call (Edit/Write/MultiEdit) before it lands on disk, and denies (exit 2) the individual tool call if the edit would leave the file syntactically invalid. This is a per-call, mechanical, parse-only gate — cheaper and earlier than either of the two "did the task succeed" checks already proposed for this repo.

Landscape basis

Gap vs. the two existing validation ideas

This repo already has two adjacent, but mechanically distinct, proposals in the backlog:

Neither operates at the individual tool-call boundary. Both can only detect a broken build after the worker has already finished and handed back a result — by then a multi-file edit sequence may have compounded a syntax break across several files, and the caller only learns about it from the final report. SWE-agent's ACI instead blocks the specific malformed edit synchronously, the same instant it would be written — architecturally identical to how path-guard.js already blocks a specific out-of-root write synchronously. No existing issue proposes this per-call, pre-write mechanism (confirmed: no open/closed issue matches "syntax" or "PreToolUse" + "edit" combined).

Proposed design

  • New pure module src/syntax-guard.js, mirroring src/path-guard.js's shape: given (toolName, toolInput), compute the resulting file content the write would produce (Edit/MultiEdit's old_string/new_string diff applied in-memory, Write's content directly) and run a fast, parse-only check keyed by file extension — no linting, no build, no tests (that's idea: post-do validation gate — declarative validators in agent.json run after do-mode tasks #509's job). Start narrow: .json via JSON.parse, .js/.mjs/.cjs via node --check (or node:vm's Script constructor, sourceless, no execution) — extensible registry, silently skip unrecognized extensions (never block what it can't parse).
  • hooks/path-guard.js (or a new sibling hook wired the same way in the do-mode --settings temp file) calls this check after the existing path/protected-config checks pass, same stdin payload, same deny()/allow() shape, same denial audit log.
  • Opt-in via agent.json manifest (x-agentmesh.syntaxGuard: true, default false) — matches idea: post-do validation gate — declarative validators in agent.json run after do-mode tasks #509's opt-in shape so no peer's behavior changes unless it asks in. A denied call is not a task failure: like an out-of-root path-guard denial, the worker sees the hook's stderr and can self-correct within the same session (no framework-level auto-retry — the invariant is upheld the same way path-guard already upholds it today).

Security / invariants

  • No new write surface: purely an additional synchronous denial path on the existing PreToolUse hook, same as path-guard.
  • Anti-spoof: the check reads only the tool's own tool_input, never caller-supplied bookkeeping fields.
  • No Bash-in-do concern: applies only to WRITE_TOOLS (Edit/Write/MultiEdit/NotebookEdit), which are already the sole write surface.
  • Fail-open on unsupported/unparseable-by-design file types (never false-block a .md/.py/binary write because the checker doesn't know that language) — matches the repo's "failure is data, not a silent block" spirit by scoping strictly to what it can prove is broken.

Dedup confirmation

Searched all open/closed issues for "syntax", "lint", "PreToolUse" (2026-07-28). #509 and #825 are the nearest matches and are cited above as explicitly distinct (post-task vs. per-call; mechanical-command vs. parse-only vs. semantic-review). No issue proposes a pre-write, tool-call-level syntax gate.

Sources

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedBlocked on a dependencygenerated:analystAnalyst agent–generated improvement idea (analyst-daily-review)needs-humanspec:draftSpec being drafted

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions