@mrclrchtr/supi-tree-sitter 1.16.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +18 -37
  2. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  4. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  6. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  8. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  9. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  10. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  11. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  12. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  13. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  14. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  15. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +3 -2
  16. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +7 -0
  17. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +56 -2
  18. package/node_modules/@mrclrchtr/supi-code-runtime/src/evidence-badge.ts +40 -0
  19. package/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +5 -0
  20. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +76 -3
  21. package/node_modules/@mrclrchtr/supi-code-runtime/src/workspace/runtime.ts +37 -2
  22. package/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  23. package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  24. package/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  25. package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  26. package/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  27. package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  28. package/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  29. package/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  30. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  31. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  32. package/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  33. package/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  34. package/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  35. package/node_modules/web-tree-sitter/LICENSE +21 -0
  36. package/node_modules/web-tree-sitter/README.md +265 -0
  37. package/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +4661 -0
  38. package/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +7 -0
  39. package/node_modules/web-tree-sitter/debug/web-tree-sitter.js +4605 -0
  40. package/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +7 -0
  41. package/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  42. package/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +57 -0
  43. package/node_modules/web-tree-sitter/package.json +100 -0
  44. package/node_modules/web-tree-sitter/web-tree-sitter.cjs +4063 -0
  45. package/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +7 -0
  46. package/node_modules/web-tree-sitter/web-tree-sitter.d.cts +1025 -0
  47. package/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +58 -0
  48. package/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1025 -0
  49. package/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +58 -0
  50. package/node_modules/web-tree-sitter/web-tree-sitter.js +4007 -0
  51. package/node_modules/web-tree-sitter/web-tree-sitter.js.map +7 -0
  52. package/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  53. package/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +55 -0
  54. package/package.json +6 -12
  55. package/src/api.ts +29 -0
  56. package/src/index.ts +28 -0
  57. package/src/language.ts +5 -0
  58. package/src/provider/tree-sitter-provider.ts +14 -2
  59. package/src/session/runtime-controller.ts +157 -0
  60. package/src/session/session.ts +8 -1
  61. package/src/tool/call-sites.ts +86 -0
  62. package/src/tool/callees.ts +35 -16
  63. package/src/tool/structure.ts +1 -0
  64. package/src/types.ts +10 -0
  65. package/src/extension.ts +0 -1
  66. package/src/tool/formatting.ts +0 -104
  67. package/src/tool/guidance.ts +0 -35
  68. package/src/tool/handlers.ts +0 -196
  69. package/src/tool/register-tools.ts +0 -107
  70. package/src/tool/tool-specs.ts +0 -117
  71. package/src/tree-sitter.ts +0 -47
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-tree-sitter",
3
- "version": "1.16.1",
3
+ "version": "2.0.0",
4
4
  "description": "SuPi Tree-sitter extension — structural AST analysis for pi",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -16,7 +16,6 @@
16
16
  "access": "public"
17
17
  },
18
18
  "keywords": [
19
- "pi-package",
20
19
  "pi",
21
20
  "pi-coding-agent",
22
21
  "pi-extension",
@@ -26,6 +25,7 @@
26
25
  "code-navigation",
27
26
  "structural-search"
28
27
  ],
28
+ "type": "module",
29
29
  "files": [
30
30
  "src/**/*.ts",
31
31
  "src/**/*.json",
@@ -35,12 +35,13 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "web-tree-sitter": "^0.26.8",
38
- "@mrclrchtr/supi-core": "1.16.1",
39
- "@mrclrchtr/supi-code-runtime": "1.16.1"
38
+ "@mrclrchtr/supi-code-runtime": "2.0.0",
39
+ "@mrclrchtr/supi-core": "2.0.0"
40
40
  },
41
41
  "bundledDependencies": [
42
42
  "@mrclrchtr/supi-code-runtime",
43
- "@mrclrchtr/supi-core"
43
+ "@mrclrchtr/supi-core",
44
+ "web-tree-sitter"
44
45
  ],
45
46
  "peerDependencies": {
46
47
  "@earendil-works/pi-ai": "*",
@@ -58,16 +59,9 @@
58
59
  "optional": true
59
60
  }
60
61
  },
61
- "pi": {
62
- "extensions": [
63
- "./src/extension.ts"
64
- ],
65
- "image": "https://raw.githubusercontent.com/mrclrchtr/supi/main/screenshots/supi-tree-sitter.png"
66
- },
67
62
  "main": "src/api.ts",
68
63
  "exports": {
69
64
  "./api": "./src/api.ts",
70
- "./extension": "./src/extension.ts",
71
65
  "./package.json": "./package.json",
72
66
  "./provider/tree-sitter-provider": "./src/provider/tree-sitter-provider.ts"
73
67
  },
package/src/api.ts CHANGED
@@ -1,9 +1,38 @@
1
1
  // Public tree-sitter session factory, shared session service access, and shared types.
2
+ //
3
+ // This package is library-only — no pi extension surface.
4
+ // Exports structured runtime/service APIs only.
5
+ // Tool handler string-formatting lives in @mrclrchtr/supi-code-intelligence.
2
6
 
7
+ // Language detection helpers
8
+ export {
9
+ detectGrammar,
10
+ getSupportedExtension,
11
+ getSupportedExtensions,
12
+ isJsTsGrammar,
13
+ isSupportedFile,
14
+ } from "./language.ts";
15
+ export { TreeSitterRuntime } from "./session/runtime.ts";
16
+ export type {
17
+ TsControllerState,
18
+ TsStartResult,
19
+ } from "./session/runtime-controller.ts";
20
+ export { TreeSitterRuntimeController } from "./session/runtime-controller.ts";
3
21
  export { getSessionTreeSitterService } from "./session/service-registry.ts";
4
22
  export { createTreeSitterSession } from "./session/session.ts";
23
+
24
+ // Structural extraction services (consumed by supi-code-intelligence tool execution)
25
+ export { extractCallSites } from "./tool/call-sites.ts";
26
+ export { lookupCalleesAt } from "./tool/callees.ts";
27
+ export { extractExports } from "./tool/exports.ts";
28
+ export { extractImports } from "./tool/imports.ts";
29
+ export { lookupNodeAt } from "./tool/node-at.ts";
30
+ export { collectOutline } from "./tool/outline.ts";
31
+
32
+ // Shared types
5
33
  export type {
6
34
  CalleesAtResult,
35
+ CallSiteMatch,
7
36
  ExportRecord,
8
37
  GrammarId,
9
38
  ImportRecord,
package/src/index.ts CHANGED
@@ -1,9 +1,37 @@
1
1
  // Public session factory, shared session service access, and re-exports for @mrclrchtr/supi-tree-sitter.
2
+ //
3
+ // This package is library-only — no pi extension surface.
4
+ // Tool handler string-formatting lives in @mrclrchtr/supi-code-intelligence.
2
5
 
6
+ // Language detection helpers
7
+ export {
8
+ detectGrammar,
9
+ getSupportedExtension,
10
+ getSupportedExtensions,
11
+ isJsTsGrammar,
12
+ isSupportedFile,
13
+ } from "./language.ts";
14
+ // Provider implementation
15
+ export { createTreeSitterProvider } from "./provider/tree-sitter-provider.ts";
16
+ export { TreeSitterRuntime } from "./session/runtime.ts";
17
+ export type {
18
+ TsControllerState,
19
+ TsStartResult,
20
+ } from "./session/runtime-controller.ts";
21
+ export { TreeSitterRuntimeController } from "./session/runtime-controller.ts";
3
22
  export { getSessionTreeSitterService } from "./session/service-registry.ts";
4
23
  export { createTreeSitterSession } from "./session/session.ts";
24
+ // Structural extraction services (consumed by supi-code-intelligence tool execution)
25
+ export { extractCallSites } from "./tool/call-sites.ts";
26
+ export { lookupCalleesAt } from "./tool/callees.ts";
27
+ export { extractExports } from "./tool/exports.ts";
28
+ export { extractImports } from "./tool/imports.ts";
29
+ export { lookupNodeAt } from "./tool/node-at.ts";
30
+ export { collectOutline } from "./tool/outline.ts";
31
+ // Shared types
5
32
  export type {
6
33
  CalleesAtResult,
34
+ CallSiteMatch,
7
35
  ExportRecord,
8
36
  GrammarId,
9
37
  ImportRecord,
package/src/language.ts CHANGED
@@ -56,6 +56,11 @@ export function isSupportedFile(filePath: string): boolean {
56
56
  return detectGrammar(filePath) !== undefined;
57
57
  }
58
58
 
59
+ /** Return all supported file extensions (with leading dot). */
60
+ export function getSupportedExtensions(): string[] {
61
+ return [...SUPPORTED_EXTENSIONS];
62
+ }
63
+
59
64
  /** Get the file extension if it's supported, otherwise undefined. */
60
65
  export function getSupportedExtension(filePath: string): SupportedExtension | undefined {
61
66
  const ext = path.extname(filePath).toLowerCase();
@@ -3,6 +3,7 @@
3
3
 
4
4
  import type {
5
5
  CalleesData,
6
+ CallSite,
6
7
  CodeResult,
7
8
  ExportData,
8
9
  ImportData,
@@ -12,6 +13,7 @@ import type {
12
13
  } from "@mrclrchtr/supi-code-runtime/api";
13
14
  import type {
14
15
  CalleesAtResult,
16
+ CallSiteMatch,
15
17
  ExportRecord,
16
18
  ImportRecord,
17
19
  NodeAtResult,
@@ -27,8 +29,8 @@ import type {
27
29
  */
28
30
  export function createTreeSitterProvider(service: TreeSitterService): StructuralProvider {
29
31
  return {
30
- async calleesAt(file, line, character) {
31
- const result = await service.calleesAt(file, line, character);
32
+ async calleesAt(file, line, character, depth?) {
33
+ const result = await service.calleesAt(file, line, character, depth);
32
34
  return mapTreeSitterResult(result, mapCalleesAtResult);
33
35
  },
34
36
 
@@ -51,6 +53,11 @@ export function createTreeSitterProvider(service: TreeSitterService): Structural
51
53
  const result = await service.nodeAt(file, line, character);
52
54
  return mapTreeSitterResult(result, mapNodeAtResult);
53
55
  },
56
+
57
+ async callSites(file) {
58
+ const result = await service.callSites(file);
59
+ return mapTreeSitterResult(result, mapCallSites);
60
+ },
54
61
  };
55
62
  }
56
63
 
@@ -140,5 +147,10 @@ function mapCalleesAtResult(result: CalleesAtResult): CalleesData {
140
147
  name: c.name,
141
148
  startLine: c.range.startLine,
142
149
  })),
150
+ depth: result.depth,
143
151
  };
144
152
  }
153
+
154
+ function mapCallSites(matches: CallSiteMatch[]): CallSite[] {
155
+ return matches.map((m) => ({ name: m.name, startLine: m.startLine }));
156
+ }
@@ -0,0 +1,157 @@
1
+ // Tree-sitter session runtime controller — pi-independent lifecycle for structural analysis.
2
+ //
3
+ // This controller owns runtime creation/disposal for one cwd:
4
+ // - Creates and disposes the TreeSitterRuntime
5
+ // - Creates TreeSitterService from the runtime
6
+ // - Publishes SessionTreeSitterService state through the existing registry
7
+ // - Exposes ready/unavailable lifecycle for umbrella adapter consumption
8
+
9
+ import type { WorkspaceRuntime } from "@mrclrchtr/supi-code-runtime/api";
10
+ import type { SessionTreeSitterService, TreeSitterService } from "../types.ts";
11
+ import { TreeSitterRuntime } from "./runtime.ts";
12
+ import {
13
+ registerTreeSitterCapabilities,
14
+ unregisterTreeSitterCapabilities,
15
+ } from "./runtime-registration.ts";
16
+ import { clearSessionTreeSitterService, setSessionTreeSitterService } from "./service-registry.ts";
17
+ import { createTreeSitterService } from "./session.ts";
18
+
19
+ // ── Types ─────────────────────────────────────────────────────────────
20
+
21
+ /** Starting state before or after shutdown. */
22
+ export type TsControllerState = TsControllerInitial | TsControllerReady | TsControllerUnavailable;
23
+
24
+ interface TsControllerInitial {
25
+ kind: "initial";
26
+ }
27
+
28
+ interface TsControllerReady {
29
+ kind: "ready";
30
+ runtime: TreeSitterRuntime;
31
+ service: TreeSitterService;
32
+ }
33
+
34
+ interface TsControllerUnavailable {
35
+ kind: "unavailable";
36
+ reason: string;
37
+ }
38
+
39
+ /** Result type from {@link TreeSitterRuntimeController.start}. */
40
+ export type TsStartResult = { kind: "ready" } | { kind: "unavailable"; reason: string };
41
+
42
+ // ── Controller ────────────────────────────────────────────────────────
43
+
44
+ /**
45
+ * Pi-independent Tree-sitter session lifecycle controller.
46
+ *
47
+ * Use this in the umbrella extension (supi-code-intelligence) instead of
48
+ * reaching into substrate extension internals.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const controller = new TreeSitterRuntimeController(cwd);
53
+ * const result = await controller.start();
54
+ * if (result.kind === "ready") {
55
+ * // use controller.service, controller.runtime
56
+ * }
57
+ * // later
58
+ * await controller.shutdown();
59
+ * ```
60
+ */
61
+ export class TreeSitterRuntimeController {
62
+ readonly #cwd: string;
63
+ #state: TsControllerState;
64
+ #runtime: WorkspaceRuntime | null;
65
+
66
+ constructor(cwd: string, runtime?: WorkspaceRuntime) {
67
+ this.#cwd = cwd;
68
+ this.#state = { kind: "initial" };
69
+ this.#runtime = runtime ?? null;
70
+ }
71
+
72
+ /** The workspace cwd this controller was created for. */
73
+ get cwd(): string {
74
+ return this.#cwd;
75
+ }
76
+
77
+ /** Current controller state. */
78
+ get kind(): TsControllerState["kind"] {
79
+ return this.#state.kind;
80
+ }
81
+
82
+ /** The TreeSitterService (structural operations), available when state is "ready". */
83
+ get service(): SessionTreeSitterService | null {
84
+ return this.#state.kind === "ready" ? this.#state.service : null;
85
+ }
86
+
87
+ /** The TreeSitterRuntime, available when state is "ready". */
88
+ get runtime(): TreeSitterRuntime | null {
89
+ return this.#state.kind === "ready" ? this.#state.runtime : null;
90
+ }
91
+
92
+ /** Attach a WorkspaceRuntime for capability registration. */
93
+ setRuntime(runtime: WorkspaceRuntime): void {
94
+ this.#runtime = runtime;
95
+ }
96
+
97
+ /**
98
+ * Start the Tree-sitter session.
99
+ *
100
+ * Creates the runtime, wires the service, and publishes state
101
+ * into the shared registries. Validates that WASM initialization
102
+ * succeeds, returning `unavailable` if it fails.
103
+ */
104
+ async start(): Promise<TsStartResult> {
105
+ // Restart safety: dispose any existing runtime
106
+ if (this.#state.kind === "ready") {
107
+ this.#state.runtime.dispose();
108
+ if (this.#runtime) unregisterTreeSitterCapabilities(this.#runtime, this.#cwd);
109
+ clearSessionTreeSitterService(this.#cwd);
110
+ }
111
+
112
+ this.#state = { kind: "unavailable", reason: "Initializing Tree-sitter" };
113
+
114
+ try {
115
+ const treeSitterRuntime = new TreeSitterRuntime(this.#cwd);
116
+
117
+ // Probe WASM initialization by loading the JavaScript grammar
118
+ // (always vendored). This catches missing web-tree-sitter or
119
+ // corrupted WASM early rather than deferring to first tool use.
120
+ await treeSitterRuntime.ensureGrammarParser("javascript");
121
+
122
+ const service = createTreeSitterService(treeSitterRuntime);
123
+ setSessionTreeSitterService(this.#cwd, service);
124
+
125
+ if (this.#runtime) {
126
+ registerTreeSitterCapabilities(this.#runtime, this.#cwd, service);
127
+ }
128
+
129
+ this.#state = { kind: "ready", runtime: treeSitterRuntime, service };
130
+ return { kind: "ready" };
131
+ } catch (error: unknown) {
132
+ const reason = error instanceof Error ? error.message : String(error);
133
+ this.#state = { kind: "unavailable", reason };
134
+ clearSessionTreeSitterService(this.#cwd);
135
+ return { kind: "unavailable", reason };
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Shut down the Tree-sitter session.
141
+ *
142
+ * Disposes the runtime and clears all published state.
143
+ */
144
+ async shutdown(): Promise<void> {
145
+ if (this.#runtime && this.#cwd) {
146
+ unregisterTreeSitterCapabilities(this.#runtime, this.#cwd);
147
+ }
148
+
149
+ clearSessionTreeSitterService(this.#cwd);
150
+
151
+ if (this.#state.kind === "ready") {
152
+ this.#state.runtime.dispose();
153
+ }
154
+
155
+ this.#state = { kind: "initial" };
156
+ }
157
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { detectGrammar, isJsTsGrammar } from "../language.ts";
4
4
  import {
5
+ extractCallSites,
5
6
  extractExports,
6
7
  extractImports,
7
8
  extractOutline,
@@ -10,6 +11,7 @@ import {
10
11
  } from "../tool/structure.ts";
11
12
  import type {
12
13
  CalleesAtResult,
14
+ CallSiteMatch,
13
15
  ExportRecord,
14
16
  ImportRecord,
15
17
  NodeAtResult,
@@ -97,8 +99,13 @@ export function createTreeSitterService(runtime: TreeSitterRuntime): TreeSitterS
97
99
  file: string,
98
100
  line: number,
99
101
  character: number,
102
+ depth?: "direct" | "deep",
100
103
  ): Promise<TreeSitterResult<CalleesAtResult>> {
101
- return lookupCalleesAt(runtime, file, line, character);
104
+ return lookupCalleesAt(runtime, file, line, character, depth);
105
+ },
106
+
107
+ async callSites(file: string): Promise<TreeSitterResult<CallSiteMatch[]>> {
108
+ return extractCallSites(runtime, file);
102
109
  },
103
110
  };
104
111
  }
@@ -0,0 +1,86 @@
1
+ /** File-wide call-site extraction using per-grammar tree-sitter queries. */
2
+
3
+ import { detectGrammar } from "../language.ts";
4
+ import type { TreeSitterRuntime } from "../session/runtime.ts";
5
+ import type { CallSiteMatch, GrammarId, TreeSitterResult } from "../types.ts";
6
+
7
+ // ── Per-grammar call-site queries ─────────────────────────────────────
8
+
9
+ // JS/TS/TSX queries that capture the full callee expression, not just the leaf name.
10
+ // `(_)` matches any expression node, giving us the full callee including member chains
11
+ // and nested call expressions (e.g. `factory()()` captures `factory()` for the outer call).
12
+ const JS_TS_CALL_SITE_QUERY = [
13
+ // Regular calls: capture the whole callee expression
14
+ "(call_expression function: (_) @call)",
15
+ // New expressions: capture the constructor expression
16
+ "(new_expression constructor: (_) @call)",
17
+ ].join("\n");
18
+
19
+ const CALL_SITE_QUERIES: Partial<Record<GrammarId, string>> = {
20
+ javascript: JS_TS_CALL_SITE_QUERY,
21
+ typescript: JS_TS_CALL_SITE_QUERY,
22
+ tsx: JS_TS_CALL_SITE_QUERY,
23
+ python:
24
+ "(call function: (identifier) @call) (call function: (attribute attribute: (identifier) @call))",
25
+ rust: "(call_expression function: (identifier) @call) (call_expression function: (field_expression field: (field_identifier) @call)) (macro_invocation macro: (identifier) @call)",
26
+ go: "(call_expression function: (identifier) @call) (call_expression function: (selector_expression field: (field_identifier) @call))",
27
+ c: "(call_expression function: (identifier) @call)",
28
+ cpp: "(call_expression function: (identifier) @call) (call_expression function: (field_expression field: (field_identifier) @call))",
29
+ java: "(method_invocation name: (identifier) @call) (object_creation_expression type: (type_identifier) @call)",
30
+ kotlin:
31
+ "(call_expression . (simple_identifier) @call) (call_expression . (navigation_expression . (simple_identifier) @call))",
32
+ ruby: "(call method: (identifier) @call)",
33
+ bash: "(command name: (word) @call)",
34
+ r: "(call function: (identifier) @call)",
35
+ };
36
+
37
+ /**
38
+ * Extract all call-site callee expressions from a file.
39
+ *
40
+ * Returns a deduplicated list of { name, startLine } for every call-site
41
+ * captured by the grammar-specific query.
42
+ */
43
+ export async function extractCallSites(
44
+ runtime: TreeSitterRuntime,
45
+ filePath: string,
46
+ ): Promise<TreeSitterResult<CallSiteMatch[]>> {
47
+ const grammarId = detectGrammar(filePath);
48
+ if (!grammarId) {
49
+ return {
50
+ kind: "unsupported-language",
51
+ file: filePath,
52
+ message: `Unsupported file: ${filePath}`,
53
+ };
54
+ }
55
+
56
+ const queryStr = CALL_SITE_QUERIES[grammarId];
57
+ if (!queryStr) {
58
+ return {
59
+ kind: "unsupported-language",
60
+ file: filePath,
61
+ message: `callSites is not supported for ${grammarId} files`,
62
+ };
63
+ }
64
+
65
+ const queryResult = await runtime.queryFile(filePath, queryStr);
66
+ if (queryResult.kind !== "success") return queryResult;
67
+
68
+ const captures = queryResult.data;
69
+ const seen = new Set<string>();
70
+ const matches: CallSiteMatch[] = [];
71
+
72
+ for (const capture of captures) {
73
+ // Normalize: trim whitespace and collapse internal whitespace to single space
74
+ const name = capture.text.trim().replace(/\s+/g, " ");
75
+ if (name.length === 0) continue;
76
+
77
+ // Deduplicate by name + line
78
+ const key = `${name}:${capture.range.startLine}`;
79
+ if (seen.has(key)) continue;
80
+ seen.add(key);
81
+
82
+ matches.push({ name, startLine: capture.range.startLine });
83
+ }
84
+
85
+ return { kind: "success", data: matches };
86
+ }
@@ -14,6 +14,7 @@ export interface CalleesAtResult {
14
14
  name: string;
15
15
  range: SourceRange;
16
16
  }>;
17
+ depth: "direct" | "deep";
17
18
  }
18
19
 
19
20
  // ── Per-grammar callee queries ────────────────────────────────────────
@@ -103,13 +104,14 @@ function extractScopeName(_type: string, text: string): string {
103
104
  // ── Main entrypoint ──────────────────────────────────────────────────
104
105
 
105
106
  /**
106
- * Extract structural callee calls for a file at the given position.
107
+ * Extract direct structural callee calls for a file at the given position.
107
108
  *
108
109
  * 1. Parses the file with the Tree-sitter runtime.
109
- * 2. Finds the enclosing function/method scope at the position.
110
+ * 2. Finds the enclosing function/method/callback scope at the position.
110
111
  * 3. Runs a grammar-specific callee query.
111
- * 4. Filters to captures within the enclosing scope.
112
- * 5. Deduplicates by name.
112
+ * 4. Filters to captures within that enclosing scope.
113
+ * 5. Excludes nested function/method/callback scopes that do not contain the anchor.
114
+ * 6. Deduplicates by name.
113
115
  */
114
116
  /** Validate that coordinates are usable and grammar is supported. */
115
117
  function validateCalleeInput(
@@ -165,11 +167,13 @@ function findEnclosingScope(
165
167
  return null;
166
168
  }
167
169
 
170
+ // biome-ignore lint/complexity/useMaxParams: provider function needs runtime + coordinates + depth
168
171
  export async function lookupCalleesAt(
169
172
  runtime: TreeSitterRuntime,
170
173
  filePath: string,
171
174
  line: number,
172
175
  character: number,
176
+ depth: "direct" | "deep" = "direct",
173
177
  ): Promise<TreeSitterResult<CalleesAtResult>> {
174
178
  // Validate coordinates and grammar
175
179
  const validation = validateCalleeInput(filePath, line, character);
@@ -218,7 +222,13 @@ export async function lookupCalleesAt(
218
222
  };
219
223
  }
220
224
 
221
- const callees = filterCalleeCaptures(queryResult.data, enclosingNode, scopes, tsPoint.row);
225
+ const callees = filterCalleeCaptures(
226
+ queryResult.data,
227
+ enclosingNode,
228
+ scopes,
229
+ tsPoint.row,
230
+ depth,
231
+ );
222
232
 
223
233
  const enclosingRange = nodeToSourceRange(enclosingNode);
224
234
  const scopeName = extractScopeName(enclosingNode.type, enclosingNode.text);
@@ -231,6 +241,7 @@ export async function lookupCalleesAt(
231
241
  range: enclosingRange,
232
242
  },
233
243
  callees,
244
+ depth,
234
245
  },
235
246
  };
236
247
  } finally {
@@ -239,9 +250,9 @@ export async function lookupCalleesAt(
239
250
  }
240
251
 
241
252
  /**
242
- * Recursively collect line ranges of inner function/method scopes that do not
243
- * contain the anchor row. Captures from these ranges are excluded so that
244
- * nested function calls are not attributed to the parent scope.
253
+ * Recursively collect line ranges of inner function/method/callback scopes that
254
+ * do not contain the anchor row. Captures from these ranges are excluded so
255
+ * nested calls are not attributed to the parent scope.
245
256
  */
246
257
  function collectInnerScopes(
247
258
  node: {
@@ -281,18 +292,24 @@ function collectInnerScopes(
281
292
  }
282
293
 
283
294
  /**
284
- * Filter query captures to only those within the enclosing scope,
285
- * excluding any that fall within inner nested function scopes.
295
+ * Filter query captures to only those within the enclosing scope.
296
+ * In `direct` depth, excludes captures that fall within inner nested
297
+ * function/callback scopes. In `deep` depth, all captures within the
298
+ * enclosing scope are included regardless of nesting.
286
299
  */
300
+ // biome-ignore lint/complexity/useMaxParams: filtering needs captures + node context + depth
287
301
  function filterCalleeCaptures(
288
302
  captures: Array<{ range: SourceRange; text: string }>,
289
303
  // biome-ignore lint/suspicious/noExplicitAny: tree-sitter SyntaxNode is complex
290
304
  enclosingNode: any,
291
305
  scopeTypes: ReadonlySet<string>,
292
306
  anchorRow: number,
307
+ depth: "direct" | "deep" = "direct",
293
308
  ): Array<{ name: string; range: SourceRange }> {
294
309
  const excludeRanges: Array<{ startRow: number; endRow: number }> = [];
295
- collectInnerScopes(enclosingNode, scopeTypes, anchorRow, excludeRanges);
310
+ if (depth === "direct") {
311
+ collectInnerScopes(enclosingNode, scopeTypes, anchorRow, excludeRanges);
312
+ }
296
313
 
297
314
  const seen = new Set<string>();
298
315
  const callees: Array<{ name: string; range: SourceRange }> = [];
@@ -306,11 +323,13 @@ function filterCalleeCaptures(
306
323
  continue;
307
324
  }
308
325
 
309
- // Exclude captures that fall within inner nested function scopes
310
- const isInInner = excludeRanges.some(
311
- (exc) => capture.range.startLine >= exc.startRow && capture.range.endLine <= exc.endRow,
312
- );
313
- if (isInInner) continue;
326
+ // In direct depth, exclude captures that fall within inner nested function scopes
327
+ if (depth === "direct") {
328
+ const isInInner = excludeRanges.some(
329
+ (exc) => capture.range.startLine >= exc.startRow && capture.range.endLine <= exc.endRow,
330
+ );
331
+ if (isInInner) continue;
332
+ }
314
333
 
315
334
  const name = capture.text.replace(/\s+/g, "").slice(0, 60);
316
335
  if (name.length === 0 || seen.has(name)) continue;
@@ -1,5 +1,6 @@
1
1
  // Structural extraction services — re-exports from sub-modules.
2
2
 
3
+ export { extractCallSites } from "./call-sites.ts";
3
4
  export { lookupCalleesAt } from "./callees.ts";
4
5
  export { extractExports } from "./exports.ts";
5
6
  export { extractImports } from "./imports.ts";
package/src/types.ts CHANGED
@@ -56,6 +56,7 @@ export interface CalleesAtResult {
56
56
  name: string;
57
57
  range: SourceRange;
58
58
  }>;
59
+ depth: "direct" | "deep";
59
60
  }
60
61
 
61
62
  /** Query capture result. */
@@ -66,6 +67,12 @@ export interface QueryCapture {
66
67
  text: string;
67
68
  }
68
69
 
70
+ /** A single call-site match from tree-sitter analysis. */
71
+ export interface CallSiteMatch {
72
+ name: string;
73
+ startLine: number;
74
+ }
75
+
69
76
  /** Shared Tree-sitter service surface, independent of lifecycle ownership. */
70
77
  export interface TreeSitterService {
71
78
  /** Validate that a supported file can be read and parsed; does not expose the raw tree. */
@@ -85,7 +92,10 @@ export interface TreeSitterService {
85
92
  file: string,
86
93
  line: number,
87
94
  character: number,
95
+ depth?: "direct" | "deep",
88
96
  ): Promise<TreeSitterResult<CalleesAtResult>>;
97
+ /** Extract all call-site identifiers in a file. */
98
+ callSites(file: string): Promise<TreeSitterResult<CallSiteMatch[]>>;
89
99
  }
90
100
 
91
101
  /** Owned Tree-sitter session that must release its runtime resources. */
package/src/extension.ts DELETED
@@ -1 +0,0 @@
1
- export { default } from "./tree-sitter.ts";