Skip to content

feat(tree-sitter-bash): add a pure-TypeScript bash parser and an agent-core-v2 bashParser service#2016

Open
sailist wants to merge 6 commits into
MoonshotAI:mainfrom
sailist:feat/tree-sitter-bash
Open

feat(tree-sitter-bash): add a pure-TypeScript bash parser and an agent-core-v2 bashParser service#2016
sailist wants to merge 6 commits into
MoonshotAI:mainfrom
sailist:feat/tree-sitter-bash

Conversation

@sailist

@sailist sailist commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

None — the problem is explained below.

Problem

The Bash tool's permission matching currently treats the entire command string as a single glob subject, so a compound command like git status && rm -rf / cannot be split and matched per command. The repo had no bash parser at all, and the off-the-shelf option (web-tree-sitter + wasm) brings wasm asset loading and runtime-specific packaging concerns.

What changed

New packages/tree-sitter-bash — a pure-TypeScript bash parser (zero runtime deps, no wasm).

  • Hand-written lexer + recursive-descent parser producing a syntax tree whose named node types match tree-sitter-bash 0.25.0 one-to-one, with UTF-16 code-unit offsets so node.text === source.slice(start, end).
  • Full grammar coverage: lists/pipelines, the complete redirect operator set, heredocs, expansions and substitutions, compound commands (if/while/for/case/function), test commands, arithmetic expansion, arrays, brace expressions.
  • Deterministic resource bounds: a wall-clock + node-count budget (default 50 ms / 50k nodes) plus per-chain recursion depth caps; budget exhaustion returns { ok: false, reason: 'aborted' } and malformed input returns hasError: true — parsing never throws, so callers can always degrade safely.
  • Differential test infrastructure: 478 fixtures plus the official tree-sitter-bash v0.25.0 corpus compared byte-for-byte against the real wasm in tests; the 30 intentional deviations (reference quirks, invalid-input recovery shapes) are documented in the package README and pinned by fixtures, with a three-way consistency check between fixtures, the deviation registry, and the README. Seeded deterministic fuzz and performance smoke tests included. 853 tests, ~4 s.

New bashParser domain in packages/agent-core-v2.

  • App-scope (L1) IBashParserService: parse(source, options) adapts the package into the DI container and returns a wire-safe DTO (cyclic parent links dropped) so results can cross the RPC boundary. No consumers yet — per-command permission matching is the intended follow-up.

Repo wiring: flake.nix workspace lists, root AGENTS.md project map, and a changeset (internal capability, no user-facing behavior change).

This approach fits Kimi Code because it avoids wasm/native loading in the Node runtime, keeps a deterministic failure mode for hostile input, and keeps the parser free of safety policy so permission rules can evolve in the consuming domains.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

sailist added 6 commits July 21, 2026 13:48
Add packages/tree-sitter-bash with the SyntaxNode tree model (UTF-16
code-unit offsets, tree-sitter-bash named-node type names), a parse
budget (deadline + node cap) with an aborted ParseResult variant, and
a placeholder parse() to be replaced by the real lexer/parser.
Cover the permission-analysis grammar subset: lists, pipelines,
commands, words, quotes, expansions, command/process substitution,
subshells, the full redirect operator set, heredocs and comments,
with tree-sitter-bash named-node type names. Long scan loops check
the parse deadline via budget.progress() so large literals cannot
exhaust the node cap; parse depth is bounded on all recursion
chains.
Add compound commands (if/while/until/for/c-style-for/select/case/
function/compound/do_group), test commands with reference-exact
extglob/regex right-hand-side rules, a Pratt expression engine for
arithmetic expansion shared across arith/c-for/test modes, arrays
and subscripts, declaration/unset commands, ansi-c/translated
strings and brace expressions. Reserved words are recognized only
in statement position. Case-aware balanced scanning keeps command
substitutions intact around case items, expression leftovers are
kept as ERROR nodes instead of dropped, and recursion depth is
bounded per chain (substitution 150, parse 500, lexer scan 1024).
…erf suites

Turn the ad-hoc wasm comparison work into permanent infrastructure:
a differential helper pinning reference-equivalent and
known-difference samples against the real tree-sitter-bash wasm
(478 match / 79 known-diff fixtures plus the official v0.25.0
corpus), a three-way consistency check between fixtures, the
known-difference registry and the README, deterministic seeded
fuzz with tree-integrity assertions, and performance smoke tests.
Converges 15 further divergence groups found by systematic probing
and documents the rest; the full suite is 853 tests in ~4s.
Wrap the pure @moonshot-ai/tree-sitter-bash package as
IBashParserService (L1, no dependencies): parse(source, options)
returns a wire-safe BashParseResult whose nodes drop the cyclic
parent link so trees can cross the RPC boundary. Budget exhaustion
surfaces as { ok: false, reason: 'aborted' }, malformed input as
hasError — never a throw.
@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a9ded49

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@a9ded49
npx https://pkg.pr.new/@moonshot-ai/kimi-code@a9ded49

commit: a9ded49

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a9ded4966c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

startIndex: node.startIndex,
endIndex: node.endIndex,
isNamed: node.isNamed,
children: node.children.map(snapshot),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Snapshot deep syntax trees iteratively

When callers parse a long but budget-acceptable arithmetic expression, e.g. echo $((1+1+...)) with a few thousand operands and a larger timeoutMs, the parser returns a deeply left-nested binary_expression tree under the default maxNodes; this recursive map(snapshot) then overflows the JS call stack and makes IBashParserService.parse throw RangeError instead of returning hasError/aborted. Since this service is meant to be the wire-safe RPC adapter, the DTO conversion needs the same iterative approach as materialize.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant