refactor: replace shell-utils string parsing with tree-sitter AST#2652
refactor: replace shell-utils string parsing with tree-sitter AST#2652LaZzyMan wants to merge 1 commit intoQwenLM:mainfrom
Conversation
- Add sync AST functions to shellAstParser.ts (splitCommandsAST, getCommandRootAST, getCommandRootsAST, detectCommandSubstitutionAST, tokenizeCommandAST, isShellCommandReadOnlySync) - Add ensureParserInitStarted() for fire-and-forget lazy initialization - Migrate shell-utils.ts functions to AST-first with legacy fallback - Migrate rule-parser.ts splitCompoundCommand to AST-first - Migrate shell-semantics.ts extractShellOperations to AST-first - Add BASIC_READ_ONLY_COMMANDS fallback set for isCommandNeedsPermission - Delete deprecated shellReadOnlyChecker.ts and its tests - Add comprehensive tests for all new sync AST functions - Add AST-path integration tests for shell-semantics
📋 Review SummaryThis PR replaces string-based shell command parsing with a tree-sitter AST-first approach across multiple files ( 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
TLDR
Replace all string-based shell command parsing in
shell-utils.ts,rule-parser.ts, andshell-semantics.tswith tree-sitter AST-first approach, using the existingshellAstParser.tsinfrastructure. This significantly improves parsing robustness for quoted strings, heredocs, nested constructs, and complex compound commands.Screenshots / Video Demo
N/A — no user-facing change. This is an internal refactor of shell command parsing logic.
Dive Deeper
Architecture: Sync/Async Hybrid with Lazy Initialization
The key challenge is that tree-sitter WASM initialization is async (
Parser.init()+Language.load()), but most callers are synchronous. The solution:ensureParserInitStarted()— All sync AST functions call this when the parser isn't ready, triggering a fire-and-forgetinitParser(). The current call falls back to the legacy implementation, but the parser starts loading in the background.*Legacy()functions, ensuring correctness even if the parser fails to load.Changes by File
shellAstParser.tssplitCommandsAST,getCommandRootAST,getCommandRootsAST,detectCommandSubstitutionAST,tokenizeCommandAST,isShellCommandReadOnlySync,ensureParserInitStartedshell-utils.tssplitCommands,getCommandRoot,getCommandRoots,detectCommandSubstitution,isCommandNeedsPermissionto AST-first with legacy fallback. AddedBASIC_READ_ONLY_COMMANDSlightweight fallback set.rule-parser.tssplitCompoundCommandto AST-first with legacy fallbackshell-semantics.tsextractShellOperationsto AST-first tokenization with legacy fallbackshellReadOnlyChecker.tsisShellCommandReadOnlySyncin shellAstParser)shellAstParser.test.tsshell-semantics.test.tsThree-tier fallback for
isCommandNeedsPermissionisShellCommandReadOnlySync) — when parser is readyBASIC_READ_ONLY_COMMANDSset (getCommandRoot+ set lookup) — lightweight sync fallbackReviewer Test Plan
npx vitest run packages/core/src/utils/shellAstParser.test.ts packages/core/src/utils/shell-utils.test.ts packages/core/src/permissions/shell-semantics.test.ts packages/core/src/permissions/permission-manager.test.ts packages/core/src/tools/shell.test.tsnpm run testnpx tsc --noEmit --project packages/core/tsconfig.jsonnpm start, execute shell commands, verify permission prompts work correctly for both read-only and mutating commandsTesting Matrix
Linked issues / bugs
N/A — proactive refactor to improve shell parsing robustness.