sparkecoder 0.1.57 → 0.1.59

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 (131) hide show
  1. package/dist/agent/index.d.ts +2 -2
  2. package/dist/agent/index.js +706 -68
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +824 -186
  5. package/dist/cli.js.map +1 -1
  6. package/dist/{index-CiS57eVy.d.ts → index-Csad1Nx4.d.ts} +1 -1
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +811 -173
  9. package/dist/index.js.map +1 -1
  10. package/dist/{search-B_PLXXvn.d.ts → search-BETuS1vh.d.ts} +1 -0
  11. package/dist/server/index.js +811 -173
  12. package/dist/server/index.js.map +1 -1
  13. package/dist/tools/index.d.ts +49 -3
  14. package/dist/tools/index.js +659 -60
  15. package/dist/tools/index.js.map +1 -1
  16. package/package.json +1 -1
  17. package/web/.next/BUILD_ID +1 -1
  18. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  19. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  20. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  21. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  22. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  23. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  25. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  40. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  41. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  42. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  74. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  75. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  76. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  82. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  83. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  85. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  88. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f661d73d._.js → 2374f_076f03ec._.js} +1 -1
  92. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_e35df3d7._.js → 2374f_19289e11._.js} +1 -1
  93. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_6ced4caf._.js → 2374f_2f0d9f6f._.js} +1 -1
  94. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_aade2a6d._.js → 2374f_35475cbe._.js} +1 -1
  95. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_562d2f4f._.js → 2374f_40e35a02._.js} +1 -1
  96. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_05c00b91._.js → 2374f_4666c827._.js} +1 -1
  97. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_ac284eb3._.js → 2374f_4858a1ea._.js} +1 -1
  98. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_39f76acc._.js → 2374f_51385fed._.js} +1 -1
  99. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_ef435bac._.js → 2374f_7db22cde._.js} +1 -1
  100. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_800db4e3._.js → 2374f_90b8e4fb._.js} +1 -1
  101. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1fa4a79a._.js → 2374f_b17fce11._.js} +1 -1
  102. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d118a207._.js → 2374f_b4b86c1f._.js} +1 -1
  103. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_934cb548._.js → 2374f_d8b9ce38._.js} +1 -1
  104. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c272cbb4._.js → 2374f_fb95e3c9._.js} +1 -1
  105. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f44222d9._.js → [root-of-the-server]__7775f784._.js} +2 -2
  106. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__8cdc7b0b._.js → [root-of-the-server]__bd396152._.js} +4 -4
  107. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_0a13adb9._.js → web_645f4b90._.js} +2 -2
  108. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  109. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  110. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  111. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  112. package/web/.next/standalone/web/.next/static/chunks/2868b007ce5163fc.css +1 -0
  113. package/web/.next/standalone/web/.next/static/chunks/{ca717ddd7c8f37e8.js → 631b023d37a08635.js} +6 -6
  114. package/web/.next/standalone/web/.next/static/static/chunks/2868b007ce5163fc.css +1 -0
  115. package/web/.next/standalone/web/.next/static/static/chunks/{ca717ddd7c8f37e8.js → 631b023d37a08635.js} +6 -6
  116. package/web/.next/standalone/web/src/components/ai-elements/code-graph-tool.tsx +202 -0
  117. package/web/.next/standalone/web/src/components/chat-interface.tsx +49 -0
  118. package/web/.next/static/chunks/2868b007ce5163fc.css +1 -0
  119. package/web/.next/static/chunks/{ca717ddd7c8f37e8.js → 631b023d37a08635.js} +6 -6
  120. package/web/.next/standalone/web/.next/static/chunks/7228b2394d1fb347.css +0 -1
  121. package/web/.next/standalone/web/.next/static/static/chunks/7228b2394d1fb347.css +0 -1
  122. package/web/.next/static/chunks/7228b2394d1fb347.css +0 -1
  123. /package/web/.next/standalone/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → static/u5qqIWWrYpWW_mZUgKKjg}/_buildManifest.js +0 -0
  124. /package/web/.next/standalone/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → static/u5qqIWWrYpWW_mZUgKKjg}/_clientMiddlewareManifest.json +0 -0
  125. /package/web/.next/standalone/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → static/u5qqIWWrYpWW_mZUgKKjg}/_ssgManifest.js +0 -0
  126. /package/web/.next/standalone/web/.next/static/{static/hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_buildManifest.js +0 -0
  127. /package/web/.next/standalone/web/.next/static/{static/hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_clientMiddlewareManifest.json +0 -0
  128. /package/web/.next/standalone/web/.next/static/{static/hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_ssgManifest.js +0 -0
  129. /package/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_buildManifest.js +0 -0
  130. /package/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_clientMiddlewareManifest.json +0 -0
  131. /package/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_ssgManifest.js +0 -0
@@ -1071,13 +1071,13 @@ var semantic_search_exports = {};
1071
1071
  __export(semantic_search_exports, {
1072
1072
  createSemanticSearchTool: () => createSemanticSearchTool
1073
1073
  });
1074
- import { tool as tool7 } from "ai";
1075
- import { z as z8 } from "zod";
1076
- import { existsSync as existsSync10, readFileSync as readFileSync4 } from "fs";
1074
+ import { tool as tool8 } from "ai";
1075
+ import { z as z9 } from "zod";
1076
+ import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
1077
1077
  import { join as join4 } from "path";
1078
1078
  import { minimatch as minimatch3 } from "minimatch";
1079
1079
  function createSemanticSearchTool(options) {
1080
- return tool7({
1080
+ return tool8({
1081
1081
  description: `Search the codebase using semantic similarity. This tool finds code by understanding its meaning, not just matching text.
1082
1082
 
1083
1083
  Use this tool when:
@@ -1142,7 +1142,7 @@ Returns matching code snippets with file paths, line numbers, and relevance scor
1142
1142
  continue;
1143
1143
  }
1144
1144
  const fullPath = join4(options.workingDirectory, filePath);
1145
- if (!existsSync10(fullPath)) {
1145
+ if (!existsSync11(fullPath)) {
1146
1146
  continue;
1147
1147
  }
1148
1148
  let snippet = "";
@@ -1197,11 +1197,11 @@ var init_semantic_search = __esm({
1197
1197
  "use strict";
1198
1198
  init_semantic();
1199
1199
  init_config();
1200
- semanticSearchInputSchema = z8.object({
1201
- query: z8.string().describe("Natural language search query describing what you want to find"),
1202
- topK: z8.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
1203
- filePattern: z8.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
1204
- language: z8.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
1200
+ semanticSearchInputSchema = z9.object({
1201
+ query: z9.string().describe("Natural language search query describing what you want to find"),
1202
+ topK: z9.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
1203
+ filePattern: z9.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
1204
+ language: z9.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
1205
1205
  });
1206
1206
  }
1207
1207
  });
@@ -1210,7 +1210,7 @@ var init_semantic_search = __esm({
1210
1210
  import {
1211
1211
  streamText as streamText2,
1212
1212
  generateText as generateText3,
1213
- tool as tool10,
1213
+ tool as tool11,
1214
1214
  stepCountIs as stepCountIs2
1215
1215
  } from "ai";
1216
1216
 
@@ -1233,7 +1233,7 @@ var SUBAGENT_MODELS = {
1233
1233
  // src/agent/index.ts
1234
1234
  init_db();
1235
1235
  init_config();
1236
- import { z as z11 } from "zod";
1236
+ import { z as z12 } from "zod";
1237
1237
  import { nanoid as nanoid3 } from "nanoid";
1238
1238
 
1239
1239
  // src/tools/bash.ts
@@ -2140,7 +2140,11 @@ async function createClient(serverId, handle, root) {
2140
2140
  dynamicRegistration: true
2141
2141
  },
2142
2142
  documentSymbol: {
2143
- dynamicRegistration: true
2143
+ dynamicRegistration: true,
2144
+ hierarchicalDocumentSymbolSupport: true,
2145
+ symbolKind: {
2146
+ valueSet: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
2147
+ }
2144
2148
  }
2145
2149
  },
2146
2150
  workspace: {
@@ -2237,7 +2241,7 @@ async function createClient(serverId, handle, root) {
2237
2241
  },
2238
2242
  async waitForDiagnostics(filePath, timeoutMs = 5e3) {
2239
2243
  const normalized = normalizePath(filePath);
2240
- return new Promise((resolve9) => {
2244
+ return new Promise((resolve10) => {
2241
2245
  const startTime = Date.now();
2242
2246
  let debounceTimer;
2243
2247
  let resolved = false;
@@ -2256,7 +2260,7 @@ async function createClient(serverId, handle, root) {
2256
2260
  if (resolved) return;
2257
2261
  resolved = true;
2258
2262
  cleanup();
2259
- resolve9(diagnostics.get(normalized) || []);
2263
+ resolve10(diagnostics.get(normalized) || []);
2260
2264
  };
2261
2265
  const onDiagnostic = () => {
2262
2266
  if (debounceTimer) clearTimeout(debounceTimer);
@@ -2282,6 +2286,100 @@ async function createClient(serverId, handle, root) {
2282
2286
  getAllDiagnostics() {
2283
2287
  return new Map(diagnostics);
2284
2288
  },
2289
+ async getDefinition(filePath, line, character) {
2290
+ const normalized = normalizePath(filePath);
2291
+ if (!fileVersions.has(normalized)) {
2292
+ await client.notifyOpen(normalized);
2293
+ }
2294
+ try {
2295
+ const result = await connection.sendRequest("textDocument/definition", {
2296
+ textDocument: { uri: pathToFileURL(normalized).href },
2297
+ position: { line, character }
2298
+ });
2299
+ if (!result) return [];
2300
+ const items = Array.isArray(result) ? result : [result];
2301
+ return items.map((r) => ({
2302
+ uri: r.targetUri || r.uri,
2303
+ range: r.targetRange || r.range
2304
+ }));
2305
+ } catch (error) {
2306
+ console.error("[lsp] Error getting definition:", error);
2307
+ return [];
2308
+ }
2309
+ },
2310
+ async getReferences(filePath, line, character, includeDeclaration = false) {
2311
+ const normalized = normalizePath(filePath);
2312
+ if (!fileVersions.has(normalized)) {
2313
+ await client.notifyOpen(normalized);
2314
+ }
2315
+ try {
2316
+ const result = await connection.sendRequest("textDocument/references", {
2317
+ textDocument: { uri: pathToFileURL(normalized).href },
2318
+ position: { line, character },
2319
+ context: { includeDeclaration }
2320
+ });
2321
+ return result || [];
2322
+ } catch (error) {
2323
+ console.error("[lsp] Error getting references:", error);
2324
+ return [];
2325
+ }
2326
+ },
2327
+ async getHover(filePath, line, character) {
2328
+ const normalized = normalizePath(filePath);
2329
+ if (!fileVersions.has(normalized)) {
2330
+ await client.notifyOpen(normalized);
2331
+ }
2332
+ try {
2333
+ const result = await connection.sendRequest("textDocument/hover", {
2334
+ textDocument: { uri: pathToFileURL(normalized).href },
2335
+ position: { line, character }
2336
+ });
2337
+ if (!result || !result.contents) return null;
2338
+ if (typeof result.contents === "string") return result.contents;
2339
+ if (result.contents.value) return result.contents.value;
2340
+ if (Array.isArray(result.contents)) {
2341
+ return result.contents.map((c) => typeof c === "string" ? c : c.value).join("\n");
2342
+ }
2343
+ return null;
2344
+ } catch (error) {
2345
+ console.error("[lsp] Error getting hover:", error);
2346
+ return null;
2347
+ }
2348
+ },
2349
+ async getDocumentSymbols(filePath) {
2350
+ const normalized = normalizePath(filePath);
2351
+ if (!fileVersions.has(normalized)) {
2352
+ await client.notifyOpen(normalized);
2353
+ }
2354
+ try {
2355
+ const result = await connection.sendRequest("textDocument/documentSymbol", {
2356
+ textDocument: { uri: pathToFileURL(normalized).href }
2357
+ });
2358
+ if (!result || result.length === 0) return [];
2359
+ if (result[0].range) {
2360
+ return result;
2361
+ }
2362
+ return result.map((si) => ({
2363
+ name: si.name,
2364
+ kind: si.kind,
2365
+ range: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
2366
+ selectionRange: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
2367
+ detail: si.containerName
2368
+ }));
2369
+ } catch (error) {
2370
+ console.error("[lsp] Error getting document symbols:", error);
2371
+ return [];
2372
+ }
2373
+ },
2374
+ async findWorkspaceSymbols(query) {
2375
+ try {
2376
+ const result = await connection.sendRequest("workspace/symbol", { query });
2377
+ return result || [];
2378
+ } catch (error) {
2379
+ console.error("[lsp] Error finding workspace symbols:", error);
2380
+ return [];
2381
+ }
2382
+ },
2285
2383
  async shutdown() {
2286
2384
  try {
2287
2385
  await connection.sendRequest("shutdown");
@@ -2404,6 +2502,24 @@ async function getAllDiagnostics() {
2404
2502
  }
2405
2503
  return results;
2406
2504
  }
2505
+ async function getReferences(filePath, line, character, includeDeclaration = false) {
2506
+ const normalized = normalizePath(filePath);
2507
+ const client = await getClientForFile(normalized);
2508
+ if (!client) return [];
2509
+ return client.getReferences(normalized, line, character, includeDeclaration);
2510
+ }
2511
+ async function getHover(filePath, line, character) {
2512
+ const normalized = normalizePath(filePath);
2513
+ const client = await getClientForFile(normalized);
2514
+ if (!client) return null;
2515
+ return client.getHover(normalized, line, character);
2516
+ }
2517
+ async function getDocumentSymbols(filePath) {
2518
+ const normalized = normalizePath(filePath);
2519
+ const client = await getClientForFile(normalized);
2520
+ if (!client) return [];
2521
+ return client.getDocumentSymbols(normalized);
2522
+ }
2407
2523
  async function formatDiagnosticsOutput(filePath, options = {}) {
2408
2524
  const diagnostics = await getDiagnostics(filePath);
2409
2525
  return formatDiagnosticsForAgent(filePath, diagnostics, options);
@@ -2499,7 +2615,7 @@ Working directory: ${options.workingDirectory}`,
2499
2615
  isChunked: true
2500
2616
  });
2501
2617
  if (chunkCount > 1) {
2502
- await new Promise((resolve9) => setTimeout(resolve9, 0));
2618
+ await new Promise((resolve10) => setTimeout(resolve10, 0));
2503
2619
  }
2504
2620
  }
2505
2621
  }
@@ -3027,8 +3143,8 @@ ${file.relativePath}:`);
3027
3143
  }
3028
3144
 
3029
3145
  // src/tools/search.ts
3030
- import { tool as tool9 } from "ai";
3031
- import { z as z10 } from "zod";
3146
+ import { tool as tool10 } from "ai";
3147
+ import { z as z11 } from "zod";
3032
3148
 
3033
3149
  // src/agent/subagent.ts
3034
3150
  import {
@@ -3200,8 +3316,8 @@ var Subagent = class {
3200
3316
  if (eventQueue.length > 0) {
3201
3317
  yield eventQueue.shift();
3202
3318
  } else if (!done) {
3203
- const event = await new Promise((resolve9) => {
3204
- resolveNext = resolve9;
3319
+ const event = await new Promise((resolve10) => {
3320
+ resolveNext = resolve10;
3205
3321
  });
3206
3322
  if (event) {
3207
3323
  yield event;
@@ -3213,14 +3329,466 @@ var Subagent = class {
3213
3329
  };
3214
3330
 
3215
3331
  // src/agent/subagents/search.ts
3216
- import { tool as tool8 } from "ai";
3217
- import { z as z9 } from "zod";
3332
+ import { tool as tool9 } from "ai";
3333
+ import { z as z10 } from "zod";
3218
3334
  import { exec as exec4 } from "child_process";
3219
3335
  import { promisify as promisify4 } from "util";
3220
- import { readFile as readFile7, stat as stat3, readdir as readdir3 } from "fs/promises";
3221
- import { resolve as resolve8, relative as relative7, isAbsolute as isAbsolute4 } from "path";
3222
- import { existsSync as existsSync11 } from "fs";
3336
+ import { readFile as readFile8, stat as stat3, readdir as readdir4 } from "fs/promises";
3337
+ import { resolve as resolve9, relative as relative8, isAbsolute as isAbsolute5 } from "path";
3338
+ import { existsSync as existsSync12 } from "fs";
3223
3339
  init_semantic();
3340
+
3341
+ // src/tools/code-graph.ts
3342
+ import { tool as tool7 } from "ai";
3343
+ import { z as z8 } from "zod";
3344
+ import { resolve as resolve8, relative as relative7, isAbsolute as isAbsolute4, basename as basename3 } from "path";
3345
+ import { readFile as readFile7, readdir as readdir3 } from "fs/promises";
3346
+ import { existsSync as existsSync10 } from "fs";
3347
+ import { fileURLToPath as fileURLToPath2 } from "url";
3348
+ import { execFileSync } from "child_process";
3349
+ var codeGraphInputSchema = z8.object({
3350
+ symbol: z8.string().describe(
3351
+ "The symbol name to inspect (function, component, class, type, variable, etc.)"
3352
+ ),
3353
+ filePath: z8.string().optional().describe(
3354
+ "File path where the symbol is defined. If omitted, searches the workspace via grep."
3355
+ ),
3356
+ depth: z8.number().optional().default(2).describe(
3357
+ "How many levels of references to traverse upward (default: 2, max: 3). Level 1 = direct usages, level 2 = usages of those usages."
3358
+ )
3359
+ });
3360
+ function isPageFile(filePath) {
3361
+ const normalized = filePath.replace(/\\/g, "/");
3362
+ if (/\/app\/(.+\/)?(page|layout|loading|error|not-found)\.(tsx?|jsx?)$/.test(normalized)) return true;
3363
+ if (/\/pages\/(?!_|api\/).+\.(tsx?|jsx?)$/.test(normalized)) return true;
3364
+ return false;
3365
+ }
3366
+ function extractRoutePath(filePath, workingDirectory) {
3367
+ const rel = relative7(workingDirectory, filePath).replace(/\\/g, "/");
3368
+ const appMatch = rel.match(/(?:src\/)?app((?:\/[^/]+)*?)\/(?:page|layout|loading|error|not-found)\.\w+$/);
3369
+ if (appMatch) return appMatch[1] || "/";
3370
+ const pagesMatch = rel.match(/(?:src\/)?pages(\/.*?)(?:\/index)?\.\w+$/);
3371
+ if (pagesMatch) return pagesMatch[1] || "/";
3372
+ return void 0;
3373
+ }
3374
+ function symbolKindName(kind) {
3375
+ const names = {
3376
+ [5 /* Class */]: "class",
3377
+ [12 /* Function */]: "function",
3378
+ [6 /* Method */]: "method",
3379
+ [7 /* Property */]: "property",
3380
+ [13 /* Variable */]: "variable",
3381
+ [11 /* Interface */]: "interface",
3382
+ [10 /* Enum */]: "enum",
3383
+ [14 /* Constant */]: "constant",
3384
+ [9 /* Constructor */]: "constructor",
3385
+ [2 /* Module */]: "module",
3386
+ [3 /* Namespace */]: "namespace",
3387
+ [26 /* TypeParameter */]: "type_param",
3388
+ [8 /* Field */]: "field",
3389
+ [22 /* EnumMember */]: "enum_member",
3390
+ [19 /* Object */]: "object"
3391
+ };
3392
+ return names[kind] || "symbol";
3393
+ }
3394
+ function findContainingSymbol(symbols, line, character) {
3395
+ for (const sym of symbols) {
3396
+ if (!sym.range) continue;
3397
+ const { start, end } = sym.range;
3398
+ const afterStart = line > start.line || line === start.line && character >= start.character;
3399
+ const beforeEnd = line < end.line || line === end.line && character < end.character;
3400
+ if (afterStart && beforeEnd) {
3401
+ if (sym.children?.length) {
3402
+ const child = findContainingSymbol(sym.children, line, character);
3403
+ if (child) return child;
3404
+ }
3405
+ return sym;
3406
+ }
3407
+ }
3408
+ return null;
3409
+ }
3410
+ function findSymbolByName(symbols, name) {
3411
+ for (const sym of symbols) {
3412
+ if (sym.name === name && sym.selectionRange) return sym;
3413
+ if (sym.children) {
3414
+ const found = findSymbolByName(sym.children, name);
3415
+ if (found) return found;
3416
+ }
3417
+ }
3418
+ return null;
3419
+ }
3420
+ function cleanHoverText(text) {
3421
+ return text.replace(/```\w*\n?/g, "").replace(/\n```/g, "").trim();
3422
+ }
3423
+ async function grepForSymbol(symbol, workingDirectory) {
3424
+ const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3425
+ const rgPatterns = [
3426
+ `(export\\s+)?(default\\s+)?(function|const|let|var|class|interface|type|enum)\\s+${escaped}\\b`,
3427
+ `(export\\s+)?(default\\s+)?\\b${escaped}\\s*[=:(]`
3428
+ ];
3429
+ for (const pattern of rgPatterns) {
3430
+ try {
3431
+ const result = execFileSync("rg", [
3432
+ "-n",
3433
+ "--no-heading",
3434
+ "-e",
3435
+ pattern,
3436
+ "--glob",
3437
+ "*.{ts,tsx,js,jsx}",
3438
+ "-m",
3439
+ "5"
3440
+ ], {
3441
+ cwd: workingDirectory,
3442
+ encoding: "utf-8",
3443
+ timeout: 5e3,
3444
+ stdio: ["pipe", "pipe", "pipe"]
3445
+ }).trim();
3446
+ if (result) {
3447
+ const firstLine = result.split("\n")[0];
3448
+ const match = firstLine.match(/^(.+?):(\d+):(.*)/);
3449
+ if (match) {
3450
+ const col = match[3].indexOf(symbol);
3451
+ return {
3452
+ filePath: resolve8(workingDirectory, match[1]),
3453
+ line: parseInt(match[2]) - 1,
3454
+ char: col >= 0 ? col : 0
3455
+ };
3456
+ }
3457
+ }
3458
+ } catch {
3459
+ }
3460
+ }
3461
+ const defPattern = new RegExp(
3462
+ `(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${escaped}\\b`
3463
+ );
3464
+ const SUPPORTED_EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
3465
+ const IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage"]);
3466
+ async function search(dir, maxFiles) {
3467
+ if (maxFiles <= 0) return null;
3468
+ let remaining = maxFiles;
3469
+ try {
3470
+ const entries = await readdir3(dir, { withFileTypes: true });
3471
+ for (const entry of entries) {
3472
+ if (remaining <= 0) return null;
3473
+ const fullPath = resolve8(dir, entry.name);
3474
+ if (entry.isDirectory()) {
3475
+ if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
3476
+ const found = await search(fullPath, remaining);
3477
+ if (found) return found;
3478
+ remaining -= 10;
3479
+ } else if (entry.isFile()) {
3480
+ const ext = entry.name.substring(entry.name.lastIndexOf("."));
3481
+ if (!SUPPORTED_EXTS.has(ext)) continue;
3482
+ remaining--;
3483
+ const content = await readFile7(fullPath, "utf-8");
3484
+ const lines = content.split("\n");
3485
+ for (let i = 0; i < lines.length; i++) {
3486
+ if (defPattern.test(lines[i])) {
3487
+ const col = lines[i].indexOf(symbol);
3488
+ if (col >= 0) {
3489
+ return { filePath: fullPath, line: i, char: col };
3490
+ }
3491
+ }
3492
+ }
3493
+ }
3494
+ }
3495
+ } catch {
3496
+ }
3497
+ return null;
3498
+ }
3499
+ return search(workingDirectory, 200);
3500
+ }
3501
+ var MAX_REF_FILES = 15;
3502
+ var MAX_LEVEL2_PARENTS = 8;
3503
+ var MAX_LEVEL2_SYMBOLS_PER_PARENT = 3;
3504
+ function createCodeGraphTool(options) {
3505
+ return tool7({
3506
+ description: `Inspect a symbol's type information and usage graph using the TypeScript language server.
3507
+
3508
+ Given a symbol name (function, component, class, type, etc.), this tool will:
3509
+ 1. Find its definition and full type signature (parameters, return type)
3510
+ 2. Find all references \u2014 what components/functions/files use this symbol
3511
+ 3. Identify which pages/routes contain it in their component tree
3512
+ 4. Show the file's symbol structure for surrounding context
3513
+
3514
+ Use this to understand:
3515
+ - Component hierarchies (what renders what, which pages are affected)
3516
+ - Type signatures and parameter/return types before making changes
3517
+ - How deeply a symbol is used across the codebase
3518
+ - What will break if you change something
3519
+
3520
+ Supports TypeScript, JavaScript, TSX, JSX files.
3521
+ Working directory: ${options.workingDirectory}`,
3522
+ inputSchema: codeGraphInputSchema,
3523
+ execute: async ({ symbol, filePath, depth }) => {
3524
+ const maxDepth = Math.min(depth ?? 2, 3);
3525
+ try {
3526
+ let defFilePath;
3527
+ let defLine = 0;
3528
+ let defChar = 0;
3529
+ let defSymbol = null;
3530
+ if (filePath) {
3531
+ const absPath = isAbsolute4(filePath) ? filePath : resolve8(options.workingDirectory, filePath);
3532
+ if (!existsSync10(absPath)) {
3533
+ return { success: false, error: `File not found: ${filePath}` };
3534
+ }
3535
+ if (!isSupported(absPath)) {
3536
+ return { success: false, error: `File type not supported. Supports: ${getSupportedExtensions().join(", ")}` };
3537
+ }
3538
+ await touchFile(absPath, true);
3539
+ const symbols = await getDocumentSymbols(absPath);
3540
+ defSymbol = findSymbolByName(symbols, symbol);
3541
+ if (defSymbol) {
3542
+ defFilePath = absPath;
3543
+ defLine = defSymbol.selectionRange.start.line;
3544
+ defChar = defSymbol.selectionRange.start.character;
3545
+ } else {
3546
+ const content = await readFile7(absPath, "utf-8");
3547
+ const lines2 = content.split("\n");
3548
+ const defPattern = new RegExp(
3549
+ `(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`
3550
+ );
3551
+ for (let i = 0; i < lines2.length; i++) {
3552
+ if (defPattern.test(lines2[i])) {
3553
+ const col = lines2[i].indexOf(symbol);
3554
+ if (col !== -1) {
3555
+ defFilePath = absPath;
3556
+ defLine = i;
3557
+ defChar = col;
3558
+ break;
3559
+ }
3560
+ }
3561
+ }
3562
+ if (!defFilePath) {
3563
+ for (let i = 0; i < lines2.length; i++) {
3564
+ const col = lines2[i].indexOf(symbol);
3565
+ if (col !== -1) {
3566
+ defFilePath = absPath;
3567
+ defLine = i;
3568
+ defChar = col;
3569
+ break;
3570
+ }
3571
+ }
3572
+ }
3573
+ }
3574
+ } else {
3575
+ const found = await grepForSymbol(symbol, options.workingDirectory);
3576
+ if (found) {
3577
+ defFilePath = found.filePath;
3578
+ defLine = found.line;
3579
+ defChar = found.char;
3580
+ }
3581
+ }
3582
+ if (!defFilePath) {
3583
+ return {
3584
+ success: false,
3585
+ error: `Could not find symbol "${symbol}" in the codebase. Try providing a filePath.`
3586
+ };
3587
+ }
3588
+ await touchFile(defFilePath, true);
3589
+ const rawHover = await getHover(defFilePath, defLine, defChar);
3590
+ const typeInfo = rawHover ? cleanHoverText(rawHover) : null;
3591
+ const fileSymbols = await getDocumentSymbols(defFilePath);
3592
+ if (!defSymbol && fileSymbols.length > 0) {
3593
+ defSymbol = findSymbolByName(fileSymbols, symbol);
3594
+ }
3595
+ const references = await getReferences(defFilePath, defLine, defChar, false);
3596
+ const refsByFile = /* @__PURE__ */ new Map();
3597
+ for (const ref of references) {
3598
+ const refPath = fileURLToPath2(ref.uri);
3599
+ if (!refsByFile.has(refPath)) {
3600
+ refsByFile.set(refPath, []);
3601
+ }
3602
+ refsByFile.get(refPath).push(ref);
3603
+ }
3604
+ const refFileInfos = [];
3605
+ let processed = 0;
3606
+ for (const [refPath, locs] of refsByFile) {
3607
+ if (processed >= MAX_REF_FILES) break;
3608
+ if (refPath === defFilePath) continue;
3609
+ processed++;
3610
+ const relPath = relative7(options.workingDirectory, refPath);
3611
+ const pageFile = isPageFile(refPath);
3612
+ const routePath = pageFile ? extractRoutePath(refPath, options.workingDirectory) : void 0;
3613
+ await touchFile(refPath, false);
3614
+ const refFileSymbols = await getDocumentSymbols(refPath);
3615
+ const seen = /* @__PURE__ */ new Map();
3616
+ for (const loc of locs) {
3617
+ const container = findContainingSymbol(
3618
+ refFileSymbols,
3619
+ loc.range.start.line,
3620
+ loc.range.start.character
3621
+ );
3622
+ if (container && !seen.has(container.name)) {
3623
+ let containerHover = null;
3624
+ try {
3625
+ const raw = await getHover(
3626
+ refPath,
3627
+ container.selectionRange.start.line,
3628
+ container.selectionRange.start.character
3629
+ );
3630
+ if (raw) containerHover = cleanHoverText(raw).split("\n")[0];
3631
+ } catch {
3632
+ }
3633
+ seen.set(container.name, {
3634
+ name: container.name,
3635
+ kind: symbolKindName(container.kind),
3636
+ line: container.selectionRange.start.line + 1,
3637
+ char: container.selectionRange.start.character,
3638
+ typeInfo: containerHover || void 0
3639
+ });
3640
+ }
3641
+ }
3642
+ refFileInfos.push({
3643
+ filePath: refPath,
3644
+ relativePath: relPath,
3645
+ isPage: pageFile,
3646
+ routePath,
3647
+ containingSymbols: Array.from(seen.values())
3648
+ });
3649
+ }
3650
+ const level2Refs = [];
3651
+ if (maxDepth >= 2) {
3652
+ for (const refFile of refFileInfos.slice(0, MAX_LEVEL2_PARENTS)) {
3653
+ for (const sym of refFile.containingSymbols.slice(0, MAX_LEVEL2_SYMBOLS_PER_PARENT)) {
3654
+ try {
3655
+ const symLineIdx = sym.line - 1;
3656
+ const symChar = sym.char;
3657
+ const l2Locations = await getReferences(
3658
+ refFile.filePath,
3659
+ symLineIdx,
3660
+ symChar,
3661
+ false
3662
+ );
3663
+ const l2Nodes = [];
3664
+ const seenPaths = /* @__PURE__ */ new Set();
3665
+ for (const loc of l2Locations.slice(0, 10)) {
3666
+ const l2Path = fileURLToPath2(loc.uri);
3667
+ if (l2Path === refFile.filePath || l2Path === defFilePath) continue;
3668
+ if (seenPaths.has(l2Path)) continue;
3669
+ seenPaths.add(l2Path);
3670
+ const l2Rel = relative7(options.workingDirectory, l2Path);
3671
+ const l2Page = isPageFile(l2Path);
3672
+ const l2Route = l2Page ? extractRoutePath(l2Path, options.workingDirectory) : void 0;
3673
+ let containerName;
3674
+ try {
3675
+ await touchFile(l2Path, false);
3676
+ const l2Symbols = await getDocumentSymbols(l2Path);
3677
+ const container = findContainingSymbol(l2Symbols, loc.range.start.line, loc.range.start.character);
3678
+ if (container) containerName = container.name;
3679
+ } catch {
3680
+ }
3681
+ l2Nodes.push({
3682
+ relativePath: l2Rel,
3683
+ isPage: l2Page,
3684
+ routePath: l2Route,
3685
+ containingSymbol: containerName
3686
+ });
3687
+ }
3688
+ if (l2Nodes.length > 0) {
3689
+ level2Refs.push({
3690
+ parentSymbol: sym.name,
3691
+ parentFile: refFile.relativePath,
3692
+ refs: l2Nodes
3693
+ });
3694
+ }
3695
+ } catch {
3696
+ }
3697
+ }
3698
+ }
3699
+ }
3700
+ const relDefPath = relative7(options.workingDirectory, defFilePath);
3701
+ const lines = [];
3702
+ lines.push(`=== ${symbol} ===`);
3703
+ lines.push(`File: ${relDefPath}:${defLine + 1}`);
3704
+ if (defSymbol) lines.push(`Kind: ${symbolKindName(defSymbol.kind)}`);
3705
+ if (typeInfo) lines.push(`Type: ${typeInfo}`);
3706
+ const externalRefCount = references.filter((r) => fileURLToPath2(r.uri) !== defFilePath).length;
3707
+ const externalFileCount = refsByFile.size - (refsByFile.has(defFilePath) ? 1 : 0);
3708
+ if (refFileInfos.length > 0) {
3709
+ lines.push("");
3710
+ lines.push(`=== Referenced by (${externalRefCount} usages across ${externalFileCount} files) ===`);
3711
+ const pages = refFileInfos.filter((r) => r.isPage);
3712
+ const nonPages = refFileInfos.filter((r) => !r.isPage);
3713
+ if (pages.length > 0) {
3714
+ lines.push("");
3715
+ lines.push("Pages/Routes:");
3716
+ for (const page of pages) {
3717
+ lines.push(` ${page.relativePath}${page.routePath ? ` \u2192 ${page.routePath}` : ""}`);
3718
+ for (const s of page.containingSymbols) {
3719
+ lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${s.typeInfo ? ` \u2014 ${s.typeInfo}` : ""}`);
3720
+ }
3721
+ }
3722
+ }
3723
+ if (nonPages.length > 0) {
3724
+ lines.push("");
3725
+ lines.push("Components/Functions:");
3726
+ for (const ref of nonPages) {
3727
+ lines.push(` ${ref.relativePath}`);
3728
+ for (const s of ref.containingSymbols) {
3729
+ const typePart = s.typeInfo && s.typeInfo.length < 120 ? ` \u2014 ${s.typeInfo}` : "";
3730
+ lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${typePart}`);
3731
+ }
3732
+ }
3733
+ }
3734
+ } else {
3735
+ lines.push("");
3736
+ lines.push("No external references found (symbol may be unused or only used within the same file).");
3737
+ }
3738
+ if (level2Refs.length > 0) {
3739
+ lines.push("");
3740
+ lines.push("=== Extended tree (level 2) ===");
3741
+ for (const l2 of level2Refs) {
3742
+ lines.push("");
3743
+ lines.push(`${l2.parentSymbol} (${l2.parentFile}) is used by:`);
3744
+ for (const ref of l2.refs) {
3745
+ const tag = ref.isPage ? " [PAGE]" : "";
3746
+ const route = ref.routePath ? ` \u2192 ${ref.routePath}` : "";
3747
+ const container = ref.containingSymbol ? ` in ${ref.containingSymbol}` : "";
3748
+ lines.push(` \u2514\u2500\u2500 ${ref.relativePath}${tag}${route}${container}`);
3749
+ }
3750
+ }
3751
+ }
3752
+ if (fileSymbols.length > 0) {
3753
+ lines.push("");
3754
+ lines.push(`=== File structure (${basename3(defFilePath)}) ===`);
3755
+ for (const sym of fileSymbols) {
3756
+ const marker = sym.name === symbol ? " \u2190 target" : "";
3757
+ lines.push(` ${sym.name} (${symbolKindName(sym.kind)}:${sym.selectionRange.start.line + 1})${marker}`);
3758
+ if (sym.children) {
3759
+ for (const child of sym.children.slice(0, 10)) {
3760
+ lines.push(` \u2514\u2500\u2500 ${child.name} (${symbolKindName(child.kind)}:${child.selectionRange.start.line + 1})`);
3761
+ }
3762
+ if (sym.children.length > 10) {
3763
+ lines.push(` ... and ${sym.children.length - 10} more`);
3764
+ }
3765
+ }
3766
+ }
3767
+ }
3768
+ const formattedResult = lines.join("\n");
3769
+ return {
3770
+ success: true,
3771
+ symbol,
3772
+ filePath: relDefPath,
3773
+ line: defLine + 1,
3774
+ kind: defSymbol ? symbolKindName(defSymbol.kind) : void 0,
3775
+ typeInfo: typeInfo || void 0,
3776
+ referenceCount: externalRefCount,
3777
+ referenceFiles: externalFileCount,
3778
+ pages: refFileInfos.filter((r) => r.isPage).map((r) => ({ path: r.relativePath, route: r.routePath })),
3779
+ formattedResult
3780
+ };
3781
+ } catch (error) {
3782
+ return {
3783
+ success: false,
3784
+ error: error instanceof Error ? error.message : String(error)
3785
+ };
3786
+ }
3787
+ }
3788
+ });
3789
+ }
3790
+
3791
+ // src/agent/subagents/search.ts
3224
3792
  var execAsync4 = promisify4(exec4);
3225
3793
  var MAX_OUTPUT_CHARS4 = 1e4;
3226
3794
  var MAX_FILE_SIZE3 = 1 * 1024 * 1024;
@@ -3250,17 +3818,20 @@ ${contextBlock}
3250
3818
  - **glob**: Find files matching a name pattern. Best for file discovery.
3251
3819
  - **read_file**: Read contents of a specific file. Use to examine code found in searches.
3252
3820
  - **list_dir**: List directory contents. Use to understand project structure.
3821
+ - **code_graph**: Inspect a symbol's type hierarchy, references, and usage graph via the TypeScript language server. Returns type signatures, all files that reference the symbol, and which pages/routes contain it. Best for understanding component/function relationships and impact analysis.
3253
3822
 
3254
3823
  ## Search Strategy
3255
3824
 
3256
3825
  1. **Start with semantic_search** if available - it finds code by meaning, which is the fastest way to explore
3257
3826
  2. **Use grep** for exact symbol/string matches (function names, class names, imports)
3258
- 3. **Use glob** for file discovery by name patterns
3259
- 4. **Read key files** to get actual code content and understand context
3260
- 5. **Run searches in PARALLEL** - make multiple tool calls at once to cover different angles simultaneously. This is critical for speed.
3827
+ 3. **Use code_graph** when you need to understand a symbol's type signature, what depends on it, or which pages use it. It's much more precise than grep for understanding relationships.
3828
+ 4. **Use glob** for file discovery by name patterns
3829
+ 5. **Read key files** to get actual code content and understand context
3830
+ 6. **Run searches in PARALLEL** - make multiple tool calls at once to cover different angles simultaneously. This is critical for speed.
3261
3831
 
3262
3832
  ### Tool Selection Guide
3263
3833
  - Know the exact name? Use **grep** (e.g. \`getUserById\`, \`class AuthService\`)
3834
+ - Need type info, references, or impact analysis? Use **code_graph** (e.g. \`code_graph({ symbol: "UserCard" })\`)
3264
3835
  - Exploring a concept? Use **semantic_search** (e.g. "how does authentication work")
3265
3836
  - Looking for files? Use **glob** (e.g. \`**/*.config.ts\`, \`**/auth/**\`)
3266
3837
  - Need file content? Use **read_file** with optional line ranges for large files
@@ -3297,17 +3868,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3297
3868
  async getToolsAsync(options) {
3298
3869
  const workingDirectory = options.workingDirectory;
3299
3870
  const tools = {
3300
- grep: tool8({
3871
+ grep: tool9({
3301
3872
  description: "Search for patterns in files using ripgrep. Returns matching lines with file paths and line numbers.",
3302
- inputSchema: z9.object({
3303
- pattern: z9.string().describe("The regex pattern to search for"),
3304
- path: z9.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
3305
- fileType: z9.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
3306
- maxResults: z9.number().optional().default(50).describe("Maximum number of results to return")
3873
+ inputSchema: z10.object({
3874
+ pattern: z10.string().describe("The regex pattern to search for"),
3875
+ path: z10.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
3876
+ fileType: z10.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
3877
+ maxResults: z10.number().optional().default(50).describe("Maximum number of results to return")
3307
3878
  }),
3308
3879
  execute: async ({ pattern, path, fileType, maxResults }) => {
3309
3880
  try {
3310
- const searchPath = path ? resolve8(workingDirectory, path) : workingDirectory;
3881
+ const searchPath = path ? resolve9(workingDirectory, path) : workingDirectory;
3311
3882
  let args = ["rg", "--line-number", "--no-heading"];
3312
3883
  if (fileType) {
3313
3884
  args.push("--type", fileType);
@@ -3344,11 +3915,11 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3344
3915
  }
3345
3916
  }
3346
3917
  }),
3347
- glob: tool8({
3918
+ glob: tool9({
3348
3919
  description: "Find files matching a glob pattern. Returns list of matching file paths.",
3349
- inputSchema: z9.object({
3350
- pattern: z9.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
3351
- maxResults: z9.number().optional().default(100).describe("Maximum number of files to return")
3920
+ inputSchema: z10.object({
3921
+ pattern: z10.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
3922
+ maxResults: z10.number().optional().default(100).describe("Maximum number of files to return")
3352
3923
  }),
3353
3924
  execute: async ({ pattern, maxResults }) => {
3354
3925
  try {
@@ -3375,17 +3946,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3375
3946
  }
3376
3947
  }
3377
3948
  }),
3378
- read_file: tool8({
3949
+ read_file: tool9({
3379
3950
  description: "Read the contents of a file. Use this to examine specific files found in search.",
3380
- inputSchema: z9.object({
3381
- path: z9.string().describe("Path to the file (relative to working directory or absolute)"),
3382
- startLine: z9.number().optional().describe("Start reading from this line (1-indexed)"),
3383
- endLine: z9.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
3951
+ inputSchema: z10.object({
3952
+ path: z10.string().describe("Path to the file (relative to working directory or absolute)"),
3953
+ startLine: z10.number().optional().describe("Start reading from this line (1-indexed)"),
3954
+ endLine: z10.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
3384
3955
  }),
3385
3956
  execute: async ({ path, startLine, endLine }) => {
3386
3957
  try {
3387
- const absolutePath = isAbsolute4(path) ? path : resolve8(workingDirectory, path);
3388
- if (!existsSync11(absolutePath)) {
3958
+ const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
3959
+ if (!existsSync12(absolutePath)) {
3389
3960
  return {
3390
3961
  success: false,
3391
3962
  error: `File not found: ${path}`
@@ -3398,7 +3969,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3398
3969
  error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Use startLine/endLine to read portions.`
3399
3970
  };
3400
3971
  }
3401
- let content = await readFile7(absolutePath, "utf-8");
3972
+ let content = await readFile8(absolutePath, "utf-8");
3402
3973
  if (startLine !== void 0 || endLine !== void 0) {
3403
3974
  const lines = content.split("\n");
3404
3975
  const start = (startLine ?? 1) - 1;
@@ -3407,7 +3978,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3407
3978
  }
3408
3979
  return {
3409
3980
  success: true,
3410
- path: relative7(workingDirectory, absolutePath),
3981
+ path: relative8(workingDirectory, absolutePath),
3411
3982
  content: truncateOutput(content, MAX_OUTPUT_CHARS4),
3412
3983
  lineCount: content.split("\n").length
3413
3984
  };
@@ -3419,17 +3990,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3419
3990
  }
3420
3991
  }
3421
3992
  }),
3422
- list_dir: tool8({
3993
+ list_dir: tool9({
3423
3994
  description: "List contents of a directory. Shows files and subdirectories.",
3424
- inputSchema: z9.object({
3425
- path: z9.string().optional().default(".").describe("Directory path (relative to working directory)"),
3426
- recursive: z9.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
3427
- maxDepth: z9.number().optional().default(2).describe("Maximum depth for recursive listing")
3995
+ inputSchema: z10.object({
3996
+ path: z10.string().optional().default(".").describe("Directory path (relative to working directory)"),
3997
+ recursive: z10.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
3998
+ maxDepth: z10.number().optional().default(2).describe("Maximum depth for recursive listing")
3428
3999
  }),
3429
4000
  execute: async ({ path, recursive, maxDepth }) => {
3430
4001
  try {
3431
- const absolutePath = isAbsolute4(path) ? path : resolve8(workingDirectory, path);
3432
- if (!existsSync11(absolutePath)) {
4002
+ const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
4003
+ if (!existsSync12(absolutePath)) {
3433
4004
  return {
3434
4005
  success: false,
3435
4006
  error: `Directory not found: ${path}`
@@ -3453,20 +4024,20 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3453
4024
  const files = stdout.trim().split("\n").filter(Boolean);
3454
4025
  return {
3455
4026
  success: true,
3456
- path: relative7(workingDirectory, absolutePath) || ".",
4027
+ path: relative8(workingDirectory, absolutePath) || ".",
3457
4028
  files,
3458
4029
  count: files.length,
3459
4030
  recursive: true
3460
4031
  };
3461
4032
  } else {
3462
- const entries = await readdir3(absolutePath, { withFileTypes: true });
4033
+ const entries = await readdir4(absolutePath, { withFileTypes: true });
3463
4034
  const items = entries.slice(0, 200).map((e) => ({
3464
4035
  name: e.name,
3465
4036
  type: e.isDirectory() ? "directory" : "file"
3466
4037
  }));
3467
4038
  return {
3468
4039
  success: true,
3469
- path: relative7(workingDirectory, absolutePath) || ".",
4040
+ path: relative8(workingDirectory, absolutePath) || ".",
3470
4041
  items,
3471
4042
  count: items.length
3472
4043
  };
@@ -3478,6 +4049,9 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3478
4049
  };
3479
4050
  }
3480
4051
  }
4052
+ }),
4053
+ code_graph: createCodeGraphTool({
4054
+ workingDirectory
3481
4055
  })
3482
4056
  };
3483
4057
  try {
@@ -3571,6 +4145,26 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
3571
4145
  context: m.symbolName || m.language
3572
4146
  });
3573
4147
  }
4148
+ } else if (step.toolName === "code_graph" && output.success) {
4149
+ matchCount += output.referenceCount || 0;
4150
+ if (output.filePath) {
4151
+ findings.push({
4152
+ type: "file",
4153
+ path: output.filePath,
4154
+ lineNumber: output.line,
4155
+ content: output.typeInfo ? truncateOutput(output.typeInfo, 300) : void 0,
4156
+ relevance: "high",
4157
+ context: `${output.kind || "symbol"}${output.referenceCount ? `, ${output.referenceCount} refs` : ""}`
4158
+ });
4159
+ }
4160
+ for (const page of (output.pages || []).slice(0, 10)) {
4161
+ findings.push({
4162
+ type: "file",
4163
+ path: page.path,
4164
+ relevance: "high",
4165
+ context: page.route ? `route: ${page.route}` : "page"
4166
+ });
4167
+ }
3574
4168
  }
3575
4169
  }
3576
4170
  }
@@ -3592,7 +4186,7 @@ function createSearchSubagent(model) {
3592
4186
  // src/tools/search.ts
3593
4187
  var MAX_RESULT_CHARS = 1e4;
3594
4188
  function createSearchTool(options) {
3595
- return tool9({
4189
+ return tool10({
3596
4190
  description: `Delegate an explore task to the explore_agent tool. Use this when you need to:
3597
4191
  - Find files or code matching a pattern
3598
4192
  - Explore the codebase structure
@@ -3602,11 +4196,12 @@ function createSearchTool(options) {
3602
4196
  The Explore agent will explore the codebase and return a summary of findings.
3603
4197
  This is more thorough than a simple grep because it can follow references and understand context.
3604
4198
  It also has access to semantic search to find code by meaning, not just text.
4199
+ It can also use code_graph to inspect a symbol's type hierarchy, references, and which pages/routes use it.
3605
4200
 
3606
4201
  CRITICAL: The explore agent has ZERO context. It cannot see the conversation, the user's message, devtools data, or any prior context. You MUST pass ALL relevant context via the "context" parameter. If the user selected a component (component name, file path, HTML, component stack) or there is a <devtools-context> block, you MUST copy that information into the "context" field verbatim. Without it the explore agent is searching blind.`,
3607
- inputSchema: z10.object({
3608
- query: z10.string().describe("What to search for. Be specific about what you're looking for."),
3609
- context: z10.string().describe("ALL context the explore agent needs. It has ZERO context on its own - no conversation history, no devtools data, nothing. You MUST include: any selected component info (name, file path, HTML, component stack), any <devtools-context> block (page URL, path, viewport), and any other relevant details from the user message. The explore agent literally only sees the query and this context field.")
4202
+ inputSchema: z11.object({
4203
+ query: z11.string().describe("What to search for. Be specific about what you're looking for."),
4204
+ context: z11.string().describe("ALL context the explore agent needs. It has ZERO context on its own - no conversation history, no devtools data, nothing. You MUST include: any selected component info (name, file path, HTML, component stack), any <devtools-context> block (page URL, path, viewport), and any other relevant details from the user message. The explore agent literally only sees the query and this context field.")
3610
4205
  }),
3611
4206
  execute: async ({ query, context }, toolOptions) => {
3612
4207
  const toolCallId = toolOptions.toolCallId || `explore_agent_${Date.now()}`;
@@ -3750,6 +4345,9 @@ async function createTools(options) {
3750
4345
  sessionId: options.sessionId,
3751
4346
  workingDirectory: options.workingDirectory,
3752
4347
  onProgress: options.onSearchProgress
4348
+ }),
4349
+ code_graph: createCodeGraphTool({
4350
+ workingDirectory: options.workingDirectory
3753
4351
  })
3754
4352
  };
3755
4353
  if (options.enableSemanticSearch !== false) {
@@ -3841,6 +4439,7 @@ You have access to powerful tools for:
3841
4439
  - **todo**: Manage your task list to track progress on complex operations
3842
4440
  - **load_skill**: Load specialized knowledge documents for specific tasks
3843
4441
  - **explore_agent**: Explore agent for semantic discovery - for exploratory questions and finding code by meaning
4442
+ - **code_graph**: Inspect a symbol's type hierarchy and usage graph via the TypeScript language server
3844
4443
 
3845
4444
 
3846
4445
  IMPORTANT: If you have zero context of where you are working, always explore it first to understand the structure before doing things for the user.
@@ -3930,6 +4529,33 @@ linter({ paths: ["src/"] }) // Check all files in a directory
3930
4529
  \`\`\`
3931
4530
  Use this proactively after making code changes to catch errors early.
3932
4531
 
4532
+ ### Code Graph Tool
4533
+ The code_graph tool uses the TypeScript language server to inspect a symbol's type hierarchy and usage graph:
4534
+ \`\`\`
4535
+ code_graph({ symbol: "UserCard" }) // Search workspace for symbol
4536
+ code_graph({ symbol: "UserCard", filePath: "src/components.tsx" }) // Look up in a specific file
4537
+ code_graph({ symbol: "formatUser", filePath: "utils.ts", depth: 2 }) // Traverse 2 levels up the reference tree
4538
+ \`\`\`
4539
+
4540
+ **What it returns:**
4541
+ - The symbol's full type signature (parameters, return type)
4542
+ - All files/functions/components that reference it (grouped into pages vs components)
4543
+ - Which Next.js pages/routes contain it in their component tree
4544
+ - Level-2 transitive usages (who uses the things that use this symbol)
4545
+ - The file's symbol structure for surrounding context
4546
+
4547
+ **When to use code_graph:**
4548
+ - **To locate a component/function by name** when you don't have the file path \u2014 e.g. a user mentions a component from devtools but the path is missing or mangled. Just pass the symbol name and it will find the definition.
4549
+ - **Before making changes** to a function/component \u2014 understand what depends on it and what will break
4550
+ - **To understand component hierarchies** \u2014 what renders what, which pages are affected across the *entire* codebase (not just the current page)
4551
+ - **To get type signatures** (props, params, return types) without reading entire files
4552
+ - **After a devtools selection** when the task involves refactoring, changing props, or anything that could impact other consumers
4553
+
4554
+ **When NOT to use code_graph:**
4555
+ - For exploratory "how does X work?" questions \u2014 use \`explore_agent\` instead
4556
+ - For exact string searches \u2014 use grep/rg directly
4557
+ - For non-TypeScript/JavaScript files \u2014 code_graph only supports TS/JS/TSX/JSX
4558
+
3933
4559
  ### Searching and Exploration
3934
4560
 
3935
4561
  **Choose the right search approach:**
@@ -3937,7 +4563,8 @@ Use this proactively after making code changes to catch errors early.
3937
4563
  0. **Use paths to your advantage \u2014 skip searching if you already have what you need.**
3938
4564
  - If the user selected a component via devtools and you can see the component name, file path, and/or line number, you ALREADY know where the code is. Just use \`read_file\` to read that file directly \u2014 do NOT call \`explore_agent\` to "find" something you already have the location of.
3939
4565
  - If you received a **page path** (e.g. \`/dashboard\`, \`/settings/profile\`), map it to the corresponding file in the project structure. In Next.js this means \`app/dashboard/page.tsx\`, \`app/settings/profile/page.tsx\`, etc. In other frameworks, check the routing convention (e.g. \`pages/\`, \`src/routes/\`). Use \`read_file\` on the mapped path directly.
3940
- - If the file path doesn't exist or \`read_file\` fails, fall back to grep to locate it (e.g. \`rg "ComponentName" --type ts -l\`) since devtools paths can sometimes be slightly off or truncated.
4566
+ - **If the file path is missing, truncated, or doesn't exist** (common with devtools \u2014 webpack paths can be mangled), use \`code_graph({ symbol: "ComponentName" })\` to locate the component. This searches the workspace for the symbol definition AND returns its type info, references, and page locations in one call \u2014 much better than raw grep for components.
4567
+ - **After reading a devtools-selected component**, if the task involves changes that could affect other consumers (refactoring, changing props, renaming), use \`code_graph\` to see ALL files and pages that depend on it \u2014 the devtools component stack only shows the current page's hierarchy, not the full picture.
3941
4568
  - Read up and down component trees when you have the file path or page path to find what you're looking for.
3942
4569
  1. **Use the \`explore_agent\` tool (Explore agent)** for:
3943
4570
  - Semantic/exploratory questions: "How does authentication work?", "Where is user data processed?"
@@ -3955,7 +4582,14 @@ Use this proactively after making code changes to catch errors early.
3955
4582
  - If you skip the \`context\` field, the explore agent is searching completely blind and will waste time guessing.
3956
4583
  - NEVER call \`explore_agent\` with only a \`query\` and no \`context\` when the user's message contains devtools or component information.
3957
4584
 
3958
- 2. **Use direct commands (grep/rg, find)** for:
4585
+ 2. **Use the \`code_graph\` tool** for:
4586
+ - Understanding what depends on a specific symbol before changing it
4587
+ - Tracing component/function usage up to page-level routes
4588
+ - Getting type signatures (params, return types) without reading full files
4589
+ - Finding exact components usages in the codebase
4590
+ - Answering "what will break if I change this?" or "which pages use this component?"
4591
+
4592
+ 3. **Use direct commands (grep/rg, find)** for:
3959
4593
  - Exact string matches: \`rg "functionName"\`, \`rg "class MyClass"\`
3960
4594
  - Finding files by name: \`find . -name "*.config.ts"\`
3961
4595
  - Simple pattern matching when you know exactly what you're looking for
@@ -3963,7 +4597,11 @@ Use this proactively after making code changes to catch errors early.
3963
4597
 
3964
4598
  **Examples:**
3965
4599
  - User selected \`<LandingButton>\` at \`src/components/LandingButton.tsx:12\` \u2192 Just \`read_file("src/components/LandingButton.tsx")\`. Do NOT call explore_agent.
4600
+ - User selected \`<PricingCard>\` but no file path in the component stack \u2192 Use \`code_graph({ symbol: "PricingCard" })\` to find its definition, type info, and all usages at once.
4601
+ - User selected \`<UserCard>\` and says "refactor the props" \u2192 First \`read_file\` the component, then \`code_graph({ symbol: "UserCard" })\` to see every file/page that depends on it before changing the interface.
3966
4602
  - "Where is the API authentication handled?" (no file path given) \u2192 Use \`explore_agent\` tool
4603
+ - "What pages use the UserCard component?" \u2192 Use \`code_graph({ symbol: "UserCard" })\`
4604
+ - "What's the type signature of formatUser?" \u2192 Use \`code_graph({ symbol: "formatUser", filePath: "utils.ts" })\`
3967
4605
  - "Find all usages of getUserById" \u2192 Use \`rg "getUserById"\`
3968
4606
  - "How does the payment flow work?" \u2192 Use \`explore_agent\` tool
3969
4607
  - "Find files named config" \u2192 Use \`find . -name "*config*"\`
@@ -4535,9 +5173,9 @@ ${prompt}` });
4535
5173
  wrappedTools[name] = originalTool;
4536
5174
  continue;
4537
5175
  }
4538
- wrappedTools[name] = tool10({
5176
+ wrappedTools[name] = tool11({
4539
5177
  description: originalTool.description || "",
4540
- inputSchema: originalTool.inputSchema || z11.object({}),
5178
+ inputSchema: originalTool.inputSchema || z12.object({}),
4541
5179
  execute: async (input, toolOptions) => {
4542
5180
  const toolCallId = toolOptions.toolCallId || nanoid3();
4543
5181
  const execution = toolExecutionQueries.create({
@@ -4551,8 +5189,8 @@ ${prompt}` });
4551
5189
  this.pendingApprovals.set(toolCallId, await execution);
4552
5190
  options.onApprovalRequired?.(await execution);
4553
5191
  await sessionQueries.updateStatus(this.session.id, "waiting");
4554
- const approved = await new Promise((resolve9) => {
4555
- approvalResolvers.set(toolCallId, { resolve: resolve9, sessionId: this.session.id });
5192
+ const approved = await new Promise((resolve10) => {
5193
+ approvalResolvers.set(toolCallId, { resolve: resolve10, sessionId: this.session.id });
4556
5194
  });
4557
5195
  const resolverData = approvalResolvers.get(toolCallId);
4558
5196
  approvalResolvers.delete(toolCallId);