|
| 1 | +# Critical Review Report — Phase 2: AST Auto-Expansion |
| 2 | + |
| 3 | +**Date:** 2026-06-04 |
| 4 | +**Reviewer:** Research Analyst (research-analyst mode) |
| 5 | +**Status:** ✅ **ACCEPTED** with minor recommendations |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +Phase 2 implements AST-based auto-expansion for `ContentRef.focus` — the ability to find a syntactic code block (function, class, method) by its name and return the entire block content with precise boundaries. |
| 12 | + |
| 13 | +### Changes Under Review |
| 14 | + |
| 15 | +| File | Role | |
| 16 | +| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | |
| 17 | +| `src/core/tools/ref/selector.ts` | Core implementation: `resolveAstBlock()` (vscode API), `resolveFocus()` (regex AST), `resolveContentRef()` (priority chain) | |
| 18 | +| `src/shared/tools.ts` | Types: `SelectorResult.method` now includes `"ast"` | |
| 19 | +| `src/core/tools/ref/__tests__/selector.spec.ts` | Tests for `resolveFocus()` and updated `resolveContentRef()` | |
| 20 | +| `src/core/tools/ref/sources/file.ts` | File source passes `cwd` to `resolveContentRef()` | |
| 21 | +| `src/core/tools/ref/sources/chat.ts` | Updated to pass `cwd` structure | |
| 22 | +| `src/core/tools/ref/sources/terminal.ts` | Updated to pass `cwd` structure | |
| 23 | +| `src/core/tools/ref/sources/tool.ts` | Updated to pass `cwd` structure | |
| 24 | +| `src/core/tools/ref/__tests__/sources.spec.ts` | Source resolver tests updated for `cwd` parameter | |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## File-by-File Analysis |
| 29 | + |
| 30 | +### 1. `src/core/tools/ref/selector.ts` |
| 31 | + |
| 32 | +#### `resolveAstBlock()` (lines 50–109) |
| 33 | + |
| 34 | +**Architecture:** Uses `vscode.executeDocumentSymbolProvider` to obtain the symbol tree, then finds the deepest symbol node containing the focus keyword position. |
| 35 | + |
| 36 | +**Strengths:** |
| 37 | + |
| 38 | +- ✅ Graceful fallback via `try/catch` — returns `null` when vscode API is unavailable (headless/test mode) |
| 39 | +- ✅ Dynamic import of `vscode` module (`const vs: any = await import("vscode")`) — standard pattern for test compatibility |
| 40 | +- ✅ Uses `document.positionAt(idx)` to map text offset → Position — correct VS Code API usage |
| 41 | +- ✅ `findDeepestContaining()` recursively searches children for most precise symbol — handles nested symbols |
| 42 | +- ✅ 1-based line number conversion (`symbol.range.start.line + 1`) — consistent with rest of codebase |
| 43 | + |
| 44 | +**Issues Found:** |
| 45 | + |
| 46 | +- ⚠️ **Line 71**: `symbols: any[]` — the `any` typing is justified (vscode types may not resolve in test context), but reduces type safety for the symbol tree traversal |
| 47 | +- ⚠️ **Line 72**: `vs.commands.executeCommand("vscode.executeDocumentSymbolProvider", uri)` — confirmed correct via Tavily research. Returns `DocumentSymbol[] | undefined`. The `children` field population depends on the language server implementation |
| 48 | + |
| 49 | +#### `resolveFocus()` (lines 176–195) |
| 50 | + |
| 51 | +**Language Support:** |
| 52 | + |
| 53 | +- ✅ TypeScript/JavaScript: function declarations, async functions, generators, arrow functions (block + expression), classes, methods |
| 54 | +- ✅ Python: `def`, `async def`, `class` with indentation-based block detection |
| 55 | +- ✅ Go: `func` with receiver methods |
| 56 | +- ✅ Rust: `fn` with return type |
| 57 | +- ✅ Java/C#/C++: methods with access modifiers |
| 58 | + |
| 59 | +**Edge Cases Covered:** |
| 60 | + |
| 61 | +- Empty source / empty focusName → returns null |
| 62 | +- Duplicate function names → picks the first (earliest) match |
| 63 | +- Nested braces → `findMatchingBrace()` correctly tracks depth |
| 64 | +- String literals with braces → skip tracking when inside strings |
| 65 | + |
| 66 | +**Issues Found:** |
| 67 | + |
| 68 | +- ⚠️ **Line 222**: `inTemplate` is declared but **never set to `true`**. Template literal strings (backtick) with `${}` expressions containing nested braces `{` could cause incorrect `findMatchingBrace()` results. However, this only affects focus keywords found inside template expressions — extremely unlikely for function/class names |
| 69 | +- ⚠️ **Line 312**: `methodPattern` — could match function calls that pass an object literal as argument: `calculateSum({a: 1})`. Mitigation: the `{` at the end makes this less likely |
| 70 | +- ⚠️ **Line 328**: `javaPattern` — could match variable declarations that use the focus name as a type rather than a function name |
| 71 | + |
| 72 | +#### `resolveContentRef()` (lines 1076–1175) |
| 73 | + |
| 74 | +**Priority Chain:** |
| 75 | + |
| 76 | +1. `ref.source === "file" && ref.startLine != null` → line range |
| 77 | +2. `ref.source === "file" && ref.focus` → `resolveAstBlock()` (vscode API) |
| 78 | +3. `ref.startAnchor` → anchor pair |
| 79 | +4. `ref.selector` → selector |
| 80 | +5. `ref.focus` → `resolveFocus()` (regex AST) → `resolveSelector()` fallback |
| 81 | +6. Error |
| 82 | + |
| 83 | +**Strengths:** |
| 84 | + |
| 85 | +- ✅ Clear priority chain with proper fallbacks |
| 86 | +- ✅ Vscode API AST → regex AST → selector text match — triple redundancy |
| 87 | +- ✅ `cwd` parameter correctly passed for file path resolution |
| 88 | + |
| 89 | +**Issues Found:** |
| 90 | + |
| 91 | +- ⚠️ **Line 1109**: `sourceId` format inconsistency — vscode AST path produces `file://${filePath}:${startLine}-${endLine}`, while all other paths use just `file://${filePath}`. This could confuse consumers that parse `sourceId` |
| 92 | +- ⚠️ **Double file read risk**: For `source === "file" && focus`, the file is opened via vscode API in `resolveAstBlock()`. If fallback is needed, `resolveFocus()` works on the already-read source text (provided by the caller like `resolveFileSource`). The caller reads the file again via `fs.readFile`. This is acceptable overhead for the fallback case |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +### 2. `src/shared/tools.ts` |
| 97 | + |
| 98 | +- ✅ `SelectorResult.method` correctly includes `"ast"` in the union type (line 121) |
| 99 | +- ✅ `ContentRef.focus` field properly typed as `string | undefined` (line 249) |
| 100 | +- ✅ `ContentRefParams.transform` and related types unchanged — no regressions |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +### 3. `src/core/tools/ref/__tests__/selector.spec.ts` |
| 105 | + |
| 106 | +**Coverage:** |
| 107 | + |
| 108 | +- ✅ `resolveContentRef`: line range, anchor, selector, focus priority |
| 109 | +- ✅ `resolveFocus`: 15 test cases covering TS/JS (functions, generators, async, arrows, classes, methods), Python (def, async def, class), Go (func, receiver), Rust (fn), Java/C# (modifiers), nested braces, duplicates |
| 110 | +- ✅ Edge cases: empty source, empty focusName, nonexistent function, fallback to selector |
| 111 | + |
| 112 | +**Issues Found:** |
| 113 | + |
| 114 | +- ❓ **No tests for `resolveAstBlock()`** — understandable since it requires vscode API. Could be tested via integration tests in the extension host |
| 115 | +- ✅ **All 253 tests pass** across 9 test files |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +### 4. `src/core/tools/ref/sources/file.ts` |
| 120 | + |
| 121 | +- ✅ Correctly passes `cwd` to `resolveContentRef()` (line 74) |
| 122 | +- ✅ Line range extraction has its own implementation (not using `resolveContentRef`) — efficient |
| 123 | +- ✅ File reading error handling is robust |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## Verification Results |
| 128 | + |
| 129 | +### TypeScript Compilation |
| 130 | + |
| 131 | +``` |
| 132 | +$ cd src && npx tsc --noEmit |
| 133 | +``` |
| 134 | + |
| 135 | +**Result:** ✅ **Clean** — no errors, no warnings |
| 136 | + |
| 137 | +### Test Suite |
| 138 | + |
| 139 | +``` |
| 140 | +$ cd src && npx vitest run core/tools/ref/__tests__/ |
| 141 | +
|
| 142 | +Test Files 9 passed (9) |
| 143 | +Tests 253 passed (253) |
| 144 | +Duration 2.80s |
| 145 | +``` |
| 146 | + |
| 147 | +**Result:** ✅ **All 253 tests pass** — no regressions |
| 148 | + |
| 149 | +### Type Safety Check: `SelectorResult` |
| 150 | + |
| 151 | +- `method` field correctly accepts `"ast"` as a valid value |
| 152 | +- `endLine` field is `number | undefined` — only populated by AST expansion |
| 153 | +- `confidence` is `1.0` for both vscode AST and regex AST methods — appropriate |
| 154 | +- All existing code that matches on `method` continues to work |
| 155 | + |
| 156 | +### Edge Cases Check |
| 157 | + |
| 158 | +| Scenario | Expected | Actual | Status | |
| 159 | +| ----------------------------------- | --------------------------- | --------------------------------- | ------ | |
| 160 | +| File not found | Error thrown | Error thrown | ✅ | |
| 161 | +| Focus not found in source | Fallback to selector | Fallback to selector | ✅ | |
| 162 | +| vscode API not available (headless) | Returns null, falls through | Returns null via catch | ✅ | |
| 163 | +| Empty source | Null returned | Null returned | ✅ | |
| 164 | +| Empty focusName | Null returned | Null returned | ✅ | |
| 165 | +| Duplicate function names | First match returned | First match returned (line 553) | ✅ | |
| 166 | +| Nested braces `{ { } }` | Correctly matched | `findMatchingBrace` handles depth | ✅ | |
| 167 | + |
| 168 | +### Regression Check |
| 169 | + |
| 170 | +- `resolveSelector()` (exact → normalized → fuzzy → word-boundary) — **unchanged** |
| 171 | +- `resolveAnchorPair()` — **unchanged** |
| 172 | +- `resolveContentRef()` for `source !== "file"` — **unchanged behavior** |
| 173 | +- Source resolvers (chat, terminal, tool) — only `cwd` parameter added, no behavioral change |
| 174 | + |
| 175 | +All existing tests pass — ✅ **No regressions detected** |
| 176 | + |
| 177 | +--- |
| 178 | + |
| 179 | +## External Research (Tavily / Context7) |
| 180 | + |
| 181 | +### `vscode.executeDocumentSymbolProvider` |
| 182 | + |
| 183 | +- **Source:** [VS Code API Reference](https://code.visualstudio.com/api/references/commands) |
| 184 | +- **Findings:** Returns `DocumentSymbol[]` with hierarchical `children` array. Quality depends on the language server implementation. Some LSPs (e.g., Haskell) may not populate `children` properly |
| 185 | +- **Impact:** The current implementation correctly handles undefined/missing children. Fallback to regex-based AST provides robustness for languages with poor LSP support |
| 186 | + |
| 187 | +### Tree-sitter Alternative |
| 188 | + |
| 189 | +- **Source:** `@vscode/tree-sitter-wasm` (npm), Tree-sitter documentation |
| 190 | +- **Findings:** VS Code provides `@vscode/tree-sitter-wasm` (v0.3.1) for direct tree-sitter WASM parsing. However, adding tree-sitter would significantly increase bundle size and complexity |
| 191 | +- **Decision:** Current approach (vscode DocumentSymbolProvider → regex AST → selector) is **appropriate** for a VS Code extension. Tree-sitter should only be considered if language coverage gaps become problematic |
| 192 | + |
| 193 | +### Best Practices |
| 194 | + |
| 195 | +- Dynamic import of vscode module for test compatibility — **confirmed standard pattern** |
| 196 | +- CancellationToken not required for `executeCommand` calls — **correct** |
| 197 | +- 1-based line numbers in user-facing output, 0-based in VS Code API — **correctly handled** |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## Verdict |
| 202 | + |
| 203 | +``` |
| 204 | +╔══════════════════════════════════════════════════════════════╗ |
| 205 | +║ ║ |
| 206 | +║ ✅ ACCEPTED ║ |
| 207 | +║ ║ |
| 208 | +║ Phase 2 is well-architected, properly implemented, ║ |
| 209 | +║ and passes all verification checks. ║ |
| 210 | +║ ║ |
| 211 | +╚══════════════════════════════════════════════════════════════╝ |
| 212 | +``` |
| 213 | + |
| 214 | +### Summary |
| 215 | + |
| 216 | +| Criterion | Result | |
| 217 | +| --------------------------- | -------------------------------- | |
| 218 | +| TypeScript (`tsc --noEmit`) | ✅ Clean | |
| 219 | +| Tests (253/253) | ✅ All pass | |
| 220 | +| No regressions | ✅ Confirmed | |
| 221 | +| Edge cases handled | ✅ Covered | |
| 222 | +| Best practices followed | ✅ Confirmed via Tavily research | |
| 223 | + |
| 224 | +### Recommendations (non-blocking) |
| 225 | + |
| 226 | +1. **🟢 LOW — Template literal handling in `findMatchingBrace()` (selector.ts:222)** |
| 227 | + |
| 228 | + ```typescript |
| 229 | + // In the string-skipping logic, add handling for backtick template literals |
| 230 | + if (ch === "`" && prev !== "\\") { |
| 231 | + if (!inTemplate) { |
| 232 | + inTemplate = true |
| 233 | + } else { |
| 234 | + inTemplate = false |
| 235 | + } |
| 236 | + } |
| 237 | + ``` |
| 238 | + |
| 239 | + Currently `inTemplate` is declared but never set, which could cause incorrect brace matching inside template literals with `${}` expressions. |
| 240 | + |
| 241 | +2. **🟢 LOW — `sourceId` format consistency** |
| 242 | + Consider using the same `sourceId` format for both vscode AST and regex AST paths in `resolveContentRef()`. Currently: |
| 243 | + |
| 244 | + - vscode AST → `file:///path/to/file.ts:10-20` |
| 245 | + - regex AST → `file:///path/to/file.ts` |
| 246 | + |
| 247 | + Align to `file:///path/to/file.ts:startLine-endLine` for both, or keep simple format for both. |
| 248 | + |
| 249 | +3. **🟢 LOW — Potential future enhancement: Tree-sitter integration** |
| 250 | + If gaps in language coverage are identified (e.g., LSPs that don't populate DocumentSymbol `children`), consider `@vscode/tree-sitter-wasm` as a complementary parsing strategy. |
| 251 | + |
| 252 | +### Final Statement |
| 253 | + |
| 254 | +Phase 2 — AST Auto-Expansion for `ContentRef.focus` is **ready for merge**. The implementation is robust, with proper fallback chains from vscode API → regex-based AST → text selector. All 253 tests pass, TypeScript compiles cleanly, and edge cases are well handled. The three recommendations above are non-critical and can be addressed in future iterations. |
0 commit comments