Files
YHHandreasonix 401c6b8cb0 feat(code-query): tree-sitter get_symbols + find_in_code (TS/JS/Python/Go/Rust/Java) (#1387)
* feat(code-query): tree-sitter get_symbols + find_in_code tools for TS/TSX/JS

Two grammar-aware tools for single-file code structure queries, scoped
per the #556 RFC's "narrowly-scoped one-tier pilot" constraint.

- get_symbols: outlines top-level + nested symbols (function, class,
  method, interface, type, enum, namespace) with 1-based positions
- find_in_code: identifier search with role classification (call /
  definition / reference), AST-filtered so comments + string literals
  do not match; handles `new X()` and `obj.X()` as calls on X

Bundles tree-sitter-typescript + tree-sitter-javascript wasm from the
official npm packages (~3.4 MB in dist/grammars/). Non-TS/JS files
return an "unsupported; use search_content" hint. Within-file only —
cross-file resolution stays out of scope per the RFC.

Closes #556

* feat(code-query): extend to Python / Go / Rust / Java

Adds 4 grammars beyond the TS/TSX/JS/JSX pilot: tree-sitter-python,
tree-sitter-go, tree-sitter-rust, tree-sitter-java. Total bundled
grammar wasm now 5.5 MB.

Per-language symbol queries land function / class / method / interface /
type / enum / namespace / property kinds with consistent shape. Methods
detected via post-process when a `function_definition` / `function_item`
sits inside a class, impl, or trait container; Rust impl blocks set the
parent name from impl's `type` field so `impl Foo` and `impl Trait for
Foo` both attribute methods to Foo.

find_in_code's classifier learns each language's call shape:
- Python `call`, Java `method_invocation`, `object_creation_expression`
- Go selector_expression, Rust field_expression, Python attribute, JS
  member_expression — all handled uniformly via field-name lookups so
  obj.X() is a call on X across languages

---------

Co-authored-by: reasonix <[email protected]>
2026-05-19 23:39:22 -07:00

40 lines
1.8 KiB
TypeScript

import { resolve } from "node:path";
import { ToolRegistry } from "../src/tools.js";
import { registerCodeQueryTools } from "../src/tools/code-query.js";
const rootDir = resolve(import.meta.dirname, "..");
const reg = new ToolRegistry();
registerCodeQueryTools(reg, { rootDir });
async function dispatchPretty(name: string, args: object): Promise<void> {
const raw = await reg.dispatch(name, JSON.stringify(args));
console.log(`\n=== ${name} ${JSON.stringify(args)} ===`);
try {
console.log(JSON.stringify(JSON.parse(raw), null, 2).slice(0, 1500));
} catch {
console.log(raw.slice(0, 1500));
}
}
const cases: Array<[string, object]> = [
["get_symbols", { path: "src/tools.ts" }],
["get_symbols", { path: "src/code/setup.ts" }],
["get_symbols", { path: "src/code-query/parser.ts" }],
["get_symbols", { path: "tests/fixtures/code-query/sample.py" }],
["get_symbols", { path: "tests/fixtures/code-query/sample.go" }],
["get_symbols", { path: "tests/fixtures/code-query/sample.rs" }],
["get_symbols", { path: "tests/fixtures/code-query/Sample.java" }],
["get_symbols", { path: "package.json" }],
["find_in_code", { path: "src/code/setup.ts", name: "registerCodeQueryTools" }],
["find_in_code", { path: "src/tools/code-query.ts", name: "register", kind: "call" }],
["find_in_code", { path: "src/code-query/parser.ts", name: "Parser", kind: "call" }],
["find_in_code", { path: "tests/fixtures/code-query/sample.py", name: "greet", kind: "call" }],
["find_in_code", { path: "tests/fixtures/code-query/sample.go", name: "Hello", kind: "call" }],
["find_in_code", { path: "tests/fixtures/code-query/sample.rs", name: "hello", kind: "call" }],
["find_in_code", { path: "tests/fixtures/code-query/Sample.java", name: "greet", kind: "call" }],
];
for (const [name, args] of cases) {
await dispatchPretty(name, args);
}