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.
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +706 -68
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +824 -186
- package/dist/cli.js.map +1 -1
- package/dist/{index-CiS57eVy.d.ts → index-Csad1Nx4.d.ts} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +811 -173
- package/dist/index.js.map +1 -1
- package/dist/{search-B_PLXXvn.d.ts → search-BETuS1vh.d.ts} +1 -0
- package/dist/server/index.js +811 -173
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +49 -3
- package/dist/tools/index.js +659 -60
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f661d73d._.js → 2374f_076f03ec._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_e35df3d7._.js → 2374f_19289e11._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_6ced4caf._.js → 2374f_2f0d9f6f._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_aade2a6d._.js → 2374f_35475cbe._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_562d2f4f._.js → 2374f_40e35a02._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_05c00b91._.js → 2374f_4666c827._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_ac284eb3._.js → 2374f_4858a1ea._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_39f76acc._.js → 2374f_51385fed._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_ef435bac._.js → 2374f_7db22cde._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_800db4e3._.js → 2374f_90b8e4fb._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1fa4a79a._.js → 2374f_b17fce11._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d118a207._.js → 2374f_b4b86c1f._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_934cb548._.js → 2374f_d8b9ce38._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c272cbb4._.js → 2374f_fb95e3c9._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f44222d9._.js → [root-of-the-server]__7775f784._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__8cdc7b0b._.js → [root-of-the-server]__bd396152._.js} +4 -4
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_0a13adb9._.js → web_645f4b90._.js} +2 -2
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/2868b007ce5163fc.css +1 -0
- package/web/.next/standalone/web/.next/static/chunks/{ca717ddd7c8f37e8.js → 631b023d37a08635.js} +6 -6
- package/web/.next/standalone/web/.next/static/static/chunks/2868b007ce5163fc.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/{ca717ddd7c8f37e8.js → 631b023d37a08635.js} +6 -6
- package/web/.next/standalone/web/src/components/ai-elements/code-graph-tool.tsx +202 -0
- package/web/.next/standalone/web/src/components/chat-interface.tsx +49 -0
- package/web/.next/static/chunks/2868b007ce5163fc.css +1 -0
- package/web/.next/static/chunks/{ca717ddd7c8f37e8.js → 631b023d37a08635.js} +6 -6
- package/web/.next/standalone/web/.next/static/chunks/7228b2394d1fb347.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/7228b2394d1fb347.css +0 -1
- package/web/.next/static/chunks/7228b2394d1fb347.css +0 -1
- /package/web/.next/standalone/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → static/u5qqIWWrYpWW_mZUgKKjg}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → static/u5qqIWWrYpWW_mZUgKKjg}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → static/u5qqIWWrYpWW_mZUgKKjg}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_ssgManifest.js +0 -0
- /package/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_buildManifest.js +0 -0
- /package/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{hfhBlcNXMR3DzRN78F_xX → u5qqIWWrYpWW_mZUgKKjg}/_ssgManifest.js +0 -0
package/dist/tools/index.js
CHANGED
|
@@ -637,13 +637,13 @@ var semantic_search_exports = {};
|
|
|
637
637
|
__export(semantic_search_exports, {
|
|
638
638
|
createSemanticSearchTool: () => createSemanticSearchTool
|
|
639
639
|
});
|
|
640
|
-
import { tool as
|
|
641
|
-
import { z as
|
|
642
|
-
import { existsSync as
|
|
640
|
+
import { tool as tool8 } from "ai";
|
|
641
|
+
import { z as z9 } from "zod";
|
|
642
|
+
import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
|
|
643
643
|
import { join as join4 } from "path";
|
|
644
644
|
import { minimatch as minimatch3 } from "minimatch";
|
|
645
645
|
function createSemanticSearchTool(options) {
|
|
646
|
-
return
|
|
646
|
+
return tool8({
|
|
647
647
|
description: `Search the codebase using semantic similarity. This tool finds code by understanding its meaning, not just matching text.
|
|
648
648
|
|
|
649
649
|
Use this tool when:
|
|
@@ -708,7 +708,7 @@ Returns matching code snippets with file paths, line numbers, and relevance scor
|
|
|
708
708
|
continue;
|
|
709
709
|
}
|
|
710
710
|
const fullPath = join4(options.workingDirectory, filePath);
|
|
711
|
-
if (!
|
|
711
|
+
if (!existsSync11(fullPath)) {
|
|
712
712
|
continue;
|
|
713
713
|
}
|
|
714
714
|
let snippet = "";
|
|
@@ -763,11 +763,11 @@ var init_semantic_search = __esm({
|
|
|
763
763
|
"use strict";
|
|
764
764
|
init_semantic();
|
|
765
765
|
init_config();
|
|
766
|
-
semanticSearchInputSchema =
|
|
767
|
-
query:
|
|
768
|
-
topK:
|
|
769
|
-
filePattern:
|
|
770
|
-
language:
|
|
766
|
+
semanticSearchInputSchema = z9.object({
|
|
767
|
+
query: z9.string().describe("Natural language search query describing what you want to find"),
|
|
768
|
+
topK: z9.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
|
|
769
|
+
filePattern: z9.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
|
|
770
|
+
language: z9.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
|
|
771
771
|
});
|
|
772
772
|
}
|
|
773
773
|
});
|
|
@@ -1670,7 +1670,11 @@ async function createClient(serverId, handle, root) {
|
|
|
1670
1670
|
dynamicRegistration: true
|
|
1671
1671
|
},
|
|
1672
1672
|
documentSymbol: {
|
|
1673
|
-
dynamicRegistration: true
|
|
1673
|
+
dynamicRegistration: true,
|
|
1674
|
+
hierarchicalDocumentSymbolSupport: true,
|
|
1675
|
+
symbolKind: {
|
|
1676
|
+
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]
|
|
1677
|
+
}
|
|
1674
1678
|
}
|
|
1675
1679
|
},
|
|
1676
1680
|
workspace: {
|
|
@@ -1767,7 +1771,7 @@ async function createClient(serverId, handle, root) {
|
|
|
1767
1771
|
},
|
|
1768
1772
|
async waitForDiagnostics(filePath, timeoutMs = 5e3) {
|
|
1769
1773
|
const normalized = normalizePath(filePath);
|
|
1770
|
-
return new Promise((
|
|
1774
|
+
return new Promise((resolve10) => {
|
|
1771
1775
|
const startTime = Date.now();
|
|
1772
1776
|
let debounceTimer;
|
|
1773
1777
|
let resolved = false;
|
|
@@ -1786,7 +1790,7 @@ async function createClient(serverId, handle, root) {
|
|
|
1786
1790
|
if (resolved) return;
|
|
1787
1791
|
resolved = true;
|
|
1788
1792
|
cleanup();
|
|
1789
|
-
|
|
1793
|
+
resolve10(diagnostics.get(normalized) || []);
|
|
1790
1794
|
};
|
|
1791
1795
|
const onDiagnostic = () => {
|
|
1792
1796
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -1812,6 +1816,100 @@ async function createClient(serverId, handle, root) {
|
|
|
1812
1816
|
getAllDiagnostics() {
|
|
1813
1817
|
return new Map(diagnostics);
|
|
1814
1818
|
},
|
|
1819
|
+
async getDefinition(filePath, line, character) {
|
|
1820
|
+
const normalized = normalizePath(filePath);
|
|
1821
|
+
if (!fileVersions.has(normalized)) {
|
|
1822
|
+
await client.notifyOpen(normalized);
|
|
1823
|
+
}
|
|
1824
|
+
try {
|
|
1825
|
+
const result = await connection.sendRequest("textDocument/definition", {
|
|
1826
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
1827
|
+
position: { line, character }
|
|
1828
|
+
});
|
|
1829
|
+
if (!result) return [];
|
|
1830
|
+
const items = Array.isArray(result) ? result : [result];
|
|
1831
|
+
return items.map((r) => ({
|
|
1832
|
+
uri: r.targetUri || r.uri,
|
|
1833
|
+
range: r.targetRange || r.range
|
|
1834
|
+
}));
|
|
1835
|
+
} catch (error) {
|
|
1836
|
+
console.error("[lsp] Error getting definition:", error);
|
|
1837
|
+
return [];
|
|
1838
|
+
}
|
|
1839
|
+
},
|
|
1840
|
+
async getReferences(filePath, line, character, includeDeclaration = false) {
|
|
1841
|
+
const normalized = normalizePath(filePath);
|
|
1842
|
+
if (!fileVersions.has(normalized)) {
|
|
1843
|
+
await client.notifyOpen(normalized);
|
|
1844
|
+
}
|
|
1845
|
+
try {
|
|
1846
|
+
const result = await connection.sendRequest("textDocument/references", {
|
|
1847
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
1848
|
+
position: { line, character },
|
|
1849
|
+
context: { includeDeclaration }
|
|
1850
|
+
});
|
|
1851
|
+
return result || [];
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
console.error("[lsp] Error getting references:", error);
|
|
1854
|
+
return [];
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
async getHover(filePath, line, character) {
|
|
1858
|
+
const normalized = normalizePath(filePath);
|
|
1859
|
+
if (!fileVersions.has(normalized)) {
|
|
1860
|
+
await client.notifyOpen(normalized);
|
|
1861
|
+
}
|
|
1862
|
+
try {
|
|
1863
|
+
const result = await connection.sendRequest("textDocument/hover", {
|
|
1864
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
1865
|
+
position: { line, character }
|
|
1866
|
+
});
|
|
1867
|
+
if (!result || !result.contents) return null;
|
|
1868
|
+
if (typeof result.contents === "string") return result.contents;
|
|
1869
|
+
if (result.contents.value) return result.contents.value;
|
|
1870
|
+
if (Array.isArray(result.contents)) {
|
|
1871
|
+
return result.contents.map((c) => typeof c === "string" ? c : c.value).join("\n");
|
|
1872
|
+
}
|
|
1873
|
+
return null;
|
|
1874
|
+
} catch (error) {
|
|
1875
|
+
console.error("[lsp] Error getting hover:", error);
|
|
1876
|
+
return null;
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
async getDocumentSymbols(filePath) {
|
|
1880
|
+
const normalized = normalizePath(filePath);
|
|
1881
|
+
if (!fileVersions.has(normalized)) {
|
|
1882
|
+
await client.notifyOpen(normalized);
|
|
1883
|
+
}
|
|
1884
|
+
try {
|
|
1885
|
+
const result = await connection.sendRequest("textDocument/documentSymbol", {
|
|
1886
|
+
textDocument: { uri: pathToFileURL(normalized).href }
|
|
1887
|
+
});
|
|
1888
|
+
if (!result || result.length === 0) return [];
|
|
1889
|
+
if (result[0].range) {
|
|
1890
|
+
return result;
|
|
1891
|
+
}
|
|
1892
|
+
return result.map((si) => ({
|
|
1893
|
+
name: si.name,
|
|
1894
|
+
kind: si.kind,
|
|
1895
|
+
range: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
1896
|
+
selectionRange: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
1897
|
+
detail: si.containerName
|
|
1898
|
+
}));
|
|
1899
|
+
} catch (error) {
|
|
1900
|
+
console.error("[lsp] Error getting document symbols:", error);
|
|
1901
|
+
return [];
|
|
1902
|
+
}
|
|
1903
|
+
},
|
|
1904
|
+
async findWorkspaceSymbols(query) {
|
|
1905
|
+
try {
|
|
1906
|
+
const result = await connection.sendRequest("workspace/symbol", { query });
|
|
1907
|
+
return result || [];
|
|
1908
|
+
} catch (error) {
|
|
1909
|
+
console.error("[lsp] Error finding workspace symbols:", error);
|
|
1910
|
+
return [];
|
|
1911
|
+
}
|
|
1912
|
+
},
|
|
1815
1913
|
async shutdown() {
|
|
1816
1914
|
try {
|
|
1817
1915
|
await connection.sendRequest("shutdown");
|
|
@@ -1934,6 +2032,24 @@ async function getAllDiagnostics() {
|
|
|
1934
2032
|
}
|
|
1935
2033
|
return results;
|
|
1936
2034
|
}
|
|
2035
|
+
async function getReferences(filePath, line, character, includeDeclaration = false) {
|
|
2036
|
+
const normalized = normalizePath(filePath);
|
|
2037
|
+
const client = await getClientForFile(normalized);
|
|
2038
|
+
if (!client) return [];
|
|
2039
|
+
return client.getReferences(normalized, line, character, includeDeclaration);
|
|
2040
|
+
}
|
|
2041
|
+
async function getHover(filePath, line, character) {
|
|
2042
|
+
const normalized = normalizePath(filePath);
|
|
2043
|
+
const client = await getClientForFile(normalized);
|
|
2044
|
+
if (!client) return null;
|
|
2045
|
+
return client.getHover(normalized, line, character);
|
|
2046
|
+
}
|
|
2047
|
+
async function getDocumentSymbols(filePath) {
|
|
2048
|
+
const normalized = normalizePath(filePath);
|
|
2049
|
+
const client = await getClientForFile(normalized);
|
|
2050
|
+
if (!client) return [];
|
|
2051
|
+
return client.getDocumentSymbols(normalized);
|
|
2052
|
+
}
|
|
1937
2053
|
async function formatDiagnosticsOutput(filePath, options = {}) {
|
|
1938
2054
|
const diagnostics = await getDiagnostics(filePath);
|
|
1939
2055
|
return formatDiagnosticsForAgent(filePath, diagnostics, options);
|
|
@@ -2029,7 +2145,7 @@ Working directory: ${options.workingDirectory}`,
|
|
|
2029
2145
|
isChunked: true
|
|
2030
2146
|
});
|
|
2031
2147
|
if (chunkCount > 1) {
|
|
2032
|
-
await new Promise((
|
|
2148
|
+
await new Promise((resolve10) => setTimeout(resolve10, 0));
|
|
2033
2149
|
}
|
|
2034
2150
|
}
|
|
2035
2151
|
}
|
|
@@ -2736,8 +2852,8 @@ ${file.relativePath}:`);
|
|
|
2736
2852
|
}
|
|
2737
2853
|
|
|
2738
2854
|
// src/tools/search.ts
|
|
2739
|
-
import { tool as
|
|
2740
|
-
import { z as
|
|
2855
|
+
import { tool as tool10 } from "ai";
|
|
2856
|
+
import { z as z11 } from "zod";
|
|
2741
2857
|
|
|
2742
2858
|
// src/agent/subagent.ts
|
|
2743
2859
|
import {
|
|
@@ -2922,8 +3038,8 @@ var Subagent = class {
|
|
|
2922
3038
|
if (eventQueue.length > 0) {
|
|
2923
3039
|
yield eventQueue.shift();
|
|
2924
3040
|
} else if (!done) {
|
|
2925
|
-
const event = await new Promise((
|
|
2926
|
-
resolveNext =
|
|
3041
|
+
const event = await new Promise((resolve10) => {
|
|
3042
|
+
resolveNext = resolve10;
|
|
2927
3043
|
});
|
|
2928
3044
|
if (event) {
|
|
2929
3045
|
yield event;
|
|
@@ -2935,14 +3051,466 @@ var Subagent = class {
|
|
|
2935
3051
|
};
|
|
2936
3052
|
|
|
2937
3053
|
// src/agent/subagents/search.ts
|
|
2938
|
-
import { tool as
|
|
2939
|
-
import { z as
|
|
3054
|
+
import { tool as tool9 } from "ai";
|
|
3055
|
+
import { z as z10 } from "zod";
|
|
2940
3056
|
import { exec as exec4 } from "child_process";
|
|
2941
3057
|
import { promisify as promisify4 } from "util";
|
|
2942
|
-
import { readFile as
|
|
2943
|
-
import { resolve as
|
|
2944
|
-
import { existsSync as
|
|
3058
|
+
import { readFile as readFile8, stat as stat3, readdir as readdir4 } from "fs/promises";
|
|
3059
|
+
import { resolve as resolve9, relative as relative8, isAbsolute as isAbsolute5 } from "path";
|
|
3060
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2945
3061
|
init_semantic();
|
|
3062
|
+
|
|
3063
|
+
// src/tools/code-graph.ts
|
|
3064
|
+
import { tool as tool7 } from "ai";
|
|
3065
|
+
import { z as z8 } from "zod";
|
|
3066
|
+
import { resolve as resolve8, relative as relative7, isAbsolute as isAbsolute4, basename as basename3 } from "path";
|
|
3067
|
+
import { readFile as readFile7, readdir as readdir3 } from "fs/promises";
|
|
3068
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3069
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3070
|
+
import { execFileSync } from "child_process";
|
|
3071
|
+
var codeGraphInputSchema = z8.object({
|
|
3072
|
+
symbol: z8.string().describe(
|
|
3073
|
+
"The symbol name to inspect (function, component, class, type, variable, etc.)"
|
|
3074
|
+
),
|
|
3075
|
+
filePath: z8.string().optional().describe(
|
|
3076
|
+
"File path where the symbol is defined. If omitted, searches the workspace via grep."
|
|
3077
|
+
),
|
|
3078
|
+
depth: z8.number().optional().default(2).describe(
|
|
3079
|
+
"How many levels of references to traverse upward (default: 2, max: 3). Level 1 = direct usages, level 2 = usages of those usages."
|
|
3080
|
+
)
|
|
3081
|
+
});
|
|
3082
|
+
function isPageFile(filePath) {
|
|
3083
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
3084
|
+
if (/\/app\/(.+\/)?(page|layout|loading|error|not-found)\.(tsx?|jsx?)$/.test(normalized)) return true;
|
|
3085
|
+
if (/\/pages\/(?!_|api\/).+\.(tsx?|jsx?)$/.test(normalized)) return true;
|
|
3086
|
+
return false;
|
|
3087
|
+
}
|
|
3088
|
+
function extractRoutePath(filePath, workingDirectory) {
|
|
3089
|
+
const rel = relative7(workingDirectory, filePath).replace(/\\/g, "/");
|
|
3090
|
+
const appMatch = rel.match(/(?:src\/)?app((?:\/[^/]+)*?)\/(?:page|layout|loading|error|not-found)\.\w+$/);
|
|
3091
|
+
if (appMatch) return appMatch[1] || "/";
|
|
3092
|
+
const pagesMatch = rel.match(/(?:src\/)?pages(\/.*?)(?:\/index)?\.\w+$/);
|
|
3093
|
+
if (pagesMatch) return pagesMatch[1] || "/";
|
|
3094
|
+
return void 0;
|
|
3095
|
+
}
|
|
3096
|
+
function symbolKindName(kind) {
|
|
3097
|
+
const names = {
|
|
3098
|
+
[5 /* Class */]: "class",
|
|
3099
|
+
[12 /* Function */]: "function",
|
|
3100
|
+
[6 /* Method */]: "method",
|
|
3101
|
+
[7 /* Property */]: "property",
|
|
3102
|
+
[13 /* Variable */]: "variable",
|
|
3103
|
+
[11 /* Interface */]: "interface",
|
|
3104
|
+
[10 /* Enum */]: "enum",
|
|
3105
|
+
[14 /* Constant */]: "constant",
|
|
3106
|
+
[9 /* Constructor */]: "constructor",
|
|
3107
|
+
[2 /* Module */]: "module",
|
|
3108
|
+
[3 /* Namespace */]: "namespace",
|
|
3109
|
+
[26 /* TypeParameter */]: "type_param",
|
|
3110
|
+
[8 /* Field */]: "field",
|
|
3111
|
+
[22 /* EnumMember */]: "enum_member",
|
|
3112
|
+
[19 /* Object */]: "object"
|
|
3113
|
+
};
|
|
3114
|
+
return names[kind] || "symbol";
|
|
3115
|
+
}
|
|
3116
|
+
function findContainingSymbol(symbols, line, character) {
|
|
3117
|
+
for (const sym of symbols) {
|
|
3118
|
+
if (!sym.range) continue;
|
|
3119
|
+
const { start, end } = sym.range;
|
|
3120
|
+
const afterStart = line > start.line || line === start.line && character >= start.character;
|
|
3121
|
+
const beforeEnd = line < end.line || line === end.line && character < end.character;
|
|
3122
|
+
if (afterStart && beforeEnd) {
|
|
3123
|
+
if (sym.children?.length) {
|
|
3124
|
+
const child = findContainingSymbol(sym.children, line, character);
|
|
3125
|
+
if (child) return child;
|
|
3126
|
+
}
|
|
3127
|
+
return sym;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
return null;
|
|
3131
|
+
}
|
|
3132
|
+
function findSymbolByName(symbols, name) {
|
|
3133
|
+
for (const sym of symbols) {
|
|
3134
|
+
if (sym.name === name && sym.selectionRange) return sym;
|
|
3135
|
+
if (sym.children) {
|
|
3136
|
+
const found = findSymbolByName(sym.children, name);
|
|
3137
|
+
if (found) return found;
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
return null;
|
|
3141
|
+
}
|
|
3142
|
+
function cleanHoverText(text) {
|
|
3143
|
+
return text.replace(/```\w*\n?/g, "").replace(/\n```/g, "").trim();
|
|
3144
|
+
}
|
|
3145
|
+
async function grepForSymbol(symbol, workingDirectory) {
|
|
3146
|
+
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3147
|
+
const rgPatterns = [
|
|
3148
|
+
`(export\\s+)?(default\\s+)?(function|const|let|var|class|interface|type|enum)\\s+${escaped}\\b`,
|
|
3149
|
+
`(export\\s+)?(default\\s+)?\\b${escaped}\\s*[=:(]`
|
|
3150
|
+
];
|
|
3151
|
+
for (const pattern of rgPatterns) {
|
|
3152
|
+
try {
|
|
3153
|
+
const result = execFileSync("rg", [
|
|
3154
|
+
"-n",
|
|
3155
|
+
"--no-heading",
|
|
3156
|
+
"-e",
|
|
3157
|
+
pattern,
|
|
3158
|
+
"--glob",
|
|
3159
|
+
"*.{ts,tsx,js,jsx}",
|
|
3160
|
+
"-m",
|
|
3161
|
+
"5"
|
|
3162
|
+
], {
|
|
3163
|
+
cwd: workingDirectory,
|
|
3164
|
+
encoding: "utf-8",
|
|
3165
|
+
timeout: 5e3,
|
|
3166
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3167
|
+
}).trim();
|
|
3168
|
+
if (result) {
|
|
3169
|
+
const firstLine = result.split("\n")[0];
|
|
3170
|
+
const match = firstLine.match(/^(.+?):(\d+):(.*)/);
|
|
3171
|
+
if (match) {
|
|
3172
|
+
const col = match[3].indexOf(symbol);
|
|
3173
|
+
return {
|
|
3174
|
+
filePath: resolve8(workingDirectory, match[1]),
|
|
3175
|
+
line: parseInt(match[2]) - 1,
|
|
3176
|
+
char: col >= 0 ? col : 0
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
} catch {
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
const defPattern = new RegExp(
|
|
3184
|
+
`(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${escaped}\\b`
|
|
3185
|
+
);
|
|
3186
|
+
const SUPPORTED_EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
3187
|
+
const IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage"]);
|
|
3188
|
+
async function search(dir, maxFiles) {
|
|
3189
|
+
if (maxFiles <= 0) return null;
|
|
3190
|
+
let remaining = maxFiles;
|
|
3191
|
+
try {
|
|
3192
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
3193
|
+
for (const entry of entries) {
|
|
3194
|
+
if (remaining <= 0) return null;
|
|
3195
|
+
const fullPath = resolve8(dir, entry.name);
|
|
3196
|
+
if (entry.isDirectory()) {
|
|
3197
|
+
if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3198
|
+
const found = await search(fullPath, remaining);
|
|
3199
|
+
if (found) return found;
|
|
3200
|
+
remaining -= 10;
|
|
3201
|
+
} else if (entry.isFile()) {
|
|
3202
|
+
const ext = entry.name.substring(entry.name.lastIndexOf("."));
|
|
3203
|
+
if (!SUPPORTED_EXTS.has(ext)) continue;
|
|
3204
|
+
remaining--;
|
|
3205
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
3206
|
+
const lines = content.split("\n");
|
|
3207
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3208
|
+
if (defPattern.test(lines[i])) {
|
|
3209
|
+
const col = lines[i].indexOf(symbol);
|
|
3210
|
+
if (col >= 0) {
|
|
3211
|
+
return { filePath: fullPath, line: i, char: col };
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
} catch {
|
|
3218
|
+
}
|
|
3219
|
+
return null;
|
|
3220
|
+
}
|
|
3221
|
+
return search(workingDirectory, 200);
|
|
3222
|
+
}
|
|
3223
|
+
var MAX_REF_FILES = 15;
|
|
3224
|
+
var MAX_LEVEL2_PARENTS = 8;
|
|
3225
|
+
var MAX_LEVEL2_SYMBOLS_PER_PARENT = 3;
|
|
3226
|
+
function createCodeGraphTool(options) {
|
|
3227
|
+
return tool7({
|
|
3228
|
+
description: `Inspect a symbol's type information and usage graph using the TypeScript language server.
|
|
3229
|
+
|
|
3230
|
+
Given a symbol name (function, component, class, type, etc.), this tool will:
|
|
3231
|
+
1. Find its definition and full type signature (parameters, return type)
|
|
3232
|
+
2. Find all references \u2014 what components/functions/files use this symbol
|
|
3233
|
+
3. Identify which pages/routes contain it in their component tree
|
|
3234
|
+
4. Show the file's symbol structure for surrounding context
|
|
3235
|
+
|
|
3236
|
+
Use this to understand:
|
|
3237
|
+
- Component hierarchies (what renders what, which pages are affected)
|
|
3238
|
+
- Type signatures and parameter/return types before making changes
|
|
3239
|
+
- How deeply a symbol is used across the codebase
|
|
3240
|
+
- What will break if you change something
|
|
3241
|
+
|
|
3242
|
+
Supports TypeScript, JavaScript, TSX, JSX files.
|
|
3243
|
+
Working directory: ${options.workingDirectory}`,
|
|
3244
|
+
inputSchema: codeGraphInputSchema,
|
|
3245
|
+
execute: async ({ symbol, filePath, depth }) => {
|
|
3246
|
+
const maxDepth = Math.min(depth ?? 2, 3);
|
|
3247
|
+
try {
|
|
3248
|
+
let defFilePath;
|
|
3249
|
+
let defLine = 0;
|
|
3250
|
+
let defChar = 0;
|
|
3251
|
+
let defSymbol = null;
|
|
3252
|
+
if (filePath) {
|
|
3253
|
+
const absPath = isAbsolute4(filePath) ? filePath : resolve8(options.workingDirectory, filePath);
|
|
3254
|
+
if (!existsSync10(absPath)) {
|
|
3255
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
3256
|
+
}
|
|
3257
|
+
if (!isSupported(absPath)) {
|
|
3258
|
+
return { success: false, error: `File type not supported. Supports: ${getSupportedExtensions().join(", ")}` };
|
|
3259
|
+
}
|
|
3260
|
+
await touchFile(absPath, true);
|
|
3261
|
+
const symbols = await getDocumentSymbols(absPath);
|
|
3262
|
+
defSymbol = findSymbolByName(symbols, symbol);
|
|
3263
|
+
if (defSymbol) {
|
|
3264
|
+
defFilePath = absPath;
|
|
3265
|
+
defLine = defSymbol.selectionRange.start.line;
|
|
3266
|
+
defChar = defSymbol.selectionRange.start.character;
|
|
3267
|
+
} else {
|
|
3268
|
+
const content = await readFile7(absPath, "utf-8");
|
|
3269
|
+
const lines2 = content.split("\n");
|
|
3270
|
+
const defPattern = new RegExp(
|
|
3271
|
+
`(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`
|
|
3272
|
+
);
|
|
3273
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
3274
|
+
if (defPattern.test(lines2[i])) {
|
|
3275
|
+
const col = lines2[i].indexOf(symbol);
|
|
3276
|
+
if (col !== -1) {
|
|
3277
|
+
defFilePath = absPath;
|
|
3278
|
+
defLine = i;
|
|
3279
|
+
defChar = col;
|
|
3280
|
+
break;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
if (!defFilePath) {
|
|
3285
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
3286
|
+
const col = lines2[i].indexOf(symbol);
|
|
3287
|
+
if (col !== -1) {
|
|
3288
|
+
defFilePath = absPath;
|
|
3289
|
+
defLine = i;
|
|
3290
|
+
defChar = col;
|
|
3291
|
+
break;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
} else {
|
|
3297
|
+
const found = await grepForSymbol(symbol, options.workingDirectory);
|
|
3298
|
+
if (found) {
|
|
3299
|
+
defFilePath = found.filePath;
|
|
3300
|
+
defLine = found.line;
|
|
3301
|
+
defChar = found.char;
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
if (!defFilePath) {
|
|
3305
|
+
return {
|
|
3306
|
+
success: false,
|
|
3307
|
+
error: `Could not find symbol "${symbol}" in the codebase. Try providing a filePath.`
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
await touchFile(defFilePath, true);
|
|
3311
|
+
const rawHover = await getHover(defFilePath, defLine, defChar);
|
|
3312
|
+
const typeInfo = rawHover ? cleanHoverText(rawHover) : null;
|
|
3313
|
+
const fileSymbols = await getDocumentSymbols(defFilePath);
|
|
3314
|
+
if (!defSymbol && fileSymbols.length > 0) {
|
|
3315
|
+
defSymbol = findSymbolByName(fileSymbols, symbol);
|
|
3316
|
+
}
|
|
3317
|
+
const references = await getReferences(defFilePath, defLine, defChar, false);
|
|
3318
|
+
const refsByFile = /* @__PURE__ */ new Map();
|
|
3319
|
+
for (const ref of references) {
|
|
3320
|
+
const refPath = fileURLToPath2(ref.uri);
|
|
3321
|
+
if (!refsByFile.has(refPath)) {
|
|
3322
|
+
refsByFile.set(refPath, []);
|
|
3323
|
+
}
|
|
3324
|
+
refsByFile.get(refPath).push(ref);
|
|
3325
|
+
}
|
|
3326
|
+
const refFileInfos = [];
|
|
3327
|
+
let processed = 0;
|
|
3328
|
+
for (const [refPath, locs] of refsByFile) {
|
|
3329
|
+
if (processed >= MAX_REF_FILES) break;
|
|
3330
|
+
if (refPath === defFilePath) continue;
|
|
3331
|
+
processed++;
|
|
3332
|
+
const relPath = relative7(options.workingDirectory, refPath);
|
|
3333
|
+
const pageFile = isPageFile(refPath);
|
|
3334
|
+
const routePath = pageFile ? extractRoutePath(refPath, options.workingDirectory) : void 0;
|
|
3335
|
+
await touchFile(refPath, false);
|
|
3336
|
+
const refFileSymbols = await getDocumentSymbols(refPath);
|
|
3337
|
+
const seen = /* @__PURE__ */ new Map();
|
|
3338
|
+
for (const loc of locs) {
|
|
3339
|
+
const container = findContainingSymbol(
|
|
3340
|
+
refFileSymbols,
|
|
3341
|
+
loc.range.start.line,
|
|
3342
|
+
loc.range.start.character
|
|
3343
|
+
);
|
|
3344
|
+
if (container && !seen.has(container.name)) {
|
|
3345
|
+
let containerHover = null;
|
|
3346
|
+
try {
|
|
3347
|
+
const raw = await getHover(
|
|
3348
|
+
refPath,
|
|
3349
|
+
container.selectionRange.start.line,
|
|
3350
|
+
container.selectionRange.start.character
|
|
3351
|
+
);
|
|
3352
|
+
if (raw) containerHover = cleanHoverText(raw).split("\n")[0];
|
|
3353
|
+
} catch {
|
|
3354
|
+
}
|
|
3355
|
+
seen.set(container.name, {
|
|
3356
|
+
name: container.name,
|
|
3357
|
+
kind: symbolKindName(container.kind),
|
|
3358
|
+
line: container.selectionRange.start.line + 1,
|
|
3359
|
+
char: container.selectionRange.start.character,
|
|
3360
|
+
typeInfo: containerHover || void 0
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
refFileInfos.push({
|
|
3365
|
+
filePath: refPath,
|
|
3366
|
+
relativePath: relPath,
|
|
3367
|
+
isPage: pageFile,
|
|
3368
|
+
routePath,
|
|
3369
|
+
containingSymbols: Array.from(seen.values())
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3372
|
+
const level2Refs = [];
|
|
3373
|
+
if (maxDepth >= 2) {
|
|
3374
|
+
for (const refFile of refFileInfos.slice(0, MAX_LEVEL2_PARENTS)) {
|
|
3375
|
+
for (const sym of refFile.containingSymbols.slice(0, MAX_LEVEL2_SYMBOLS_PER_PARENT)) {
|
|
3376
|
+
try {
|
|
3377
|
+
const symLineIdx = sym.line - 1;
|
|
3378
|
+
const symChar = sym.char;
|
|
3379
|
+
const l2Locations = await getReferences(
|
|
3380
|
+
refFile.filePath,
|
|
3381
|
+
symLineIdx,
|
|
3382
|
+
symChar,
|
|
3383
|
+
false
|
|
3384
|
+
);
|
|
3385
|
+
const l2Nodes = [];
|
|
3386
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
3387
|
+
for (const loc of l2Locations.slice(0, 10)) {
|
|
3388
|
+
const l2Path = fileURLToPath2(loc.uri);
|
|
3389
|
+
if (l2Path === refFile.filePath || l2Path === defFilePath) continue;
|
|
3390
|
+
if (seenPaths.has(l2Path)) continue;
|
|
3391
|
+
seenPaths.add(l2Path);
|
|
3392
|
+
const l2Rel = relative7(options.workingDirectory, l2Path);
|
|
3393
|
+
const l2Page = isPageFile(l2Path);
|
|
3394
|
+
const l2Route = l2Page ? extractRoutePath(l2Path, options.workingDirectory) : void 0;
|
|
3395
|
+
let containerName;
|
|
3396
|
+
try {
|
|
3397
|
+
await touchFile(l2Path, false);
|
|
3398
|
+
const l2Symbols = await getDocumentSymbols(l2Path);
|
|
3399
|
+
const container = findContainingSymbol(l2Symbols, loc.range.start.line, loc.range.start.character);
|
|
3400
|
+
if (container) containerName = container.name;
|
|
3401
|
+
} catch {
|
|
3402
|
+
}
|
|
3403
|
+
l2Nodes.push({
|
|
3404
|
+
relativePath: l2Rel,
|
|
3405
|
+
isPage: l2Page,
|
|
3406
|
+
routePath: l2Route,
|
|
3407
|
+
containingSymbol: containerName
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3410
|
+
if (l2Nodes.length > 0) {
|
|
3411
|
+
level2Refs.push({
|
|
3412
|
+
parentSymbol: sym.name,
|
|
3413
|
+
parentFile: refFile.relativePath,
|
|
3414
|
+
refs: l2Nodes
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
} catch {
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
const relDefPath = relative7(options.workingDirectory, defFilePath);
|
|
3423
|
+
const lines = [];
|
|
3424
|
+
lines.push(`=== ${symbol} ===`);
|
|
3425
|
+
lines.push(`File: ${relDefPath}:${defLine + 1}`);
|
|
3426
|
+
if (defSymbol) lines.push(`Kind: ${symbolKindName(defSymbol.kind)}`);
|
|
3427
|
+
if (typeInfo) lines.push(`Type: ${typeInfo}`);
|
|
3428
|
+
const externalRefCount = references.filter((r) => fileURLToPath2(r.uri) !== defFilePath).length;
|
|
3429
|
+
const externalFileCount = refsByFile.size - (refsByFile.has(defFilePath) ? 1 : 0);
|
|
3430
|
+
if (refFileInfos.length > 0) {
|
|
3431
|
+
lines.push("");
|
|
3432
|
+
lines.push(`=== Referenced by (${externalRefCount} usages across ${externalFileCount} files) ===`);
|
|
3433
|
+
const pages = refFileInfos.filter((r) => r.isPage);
|
|
3434
|
+
const nonPages = refFileInfos.filter((r) => !r.isPage);
|
|
3435
|
+
if (pages.length > 0) {
|
|
3436
|
+
lines.push("");
|
|
3437
|
+
lines.push("Pages/Routes:");
|
|
3438
|
+
for (const page of pages) {
|
|
3439
|
+
lines.push(` ${page.relativePath}${page.routePath ? ` \u2192 ${page.routePath}` : ""}`);
|
|
3440
|
+
for (const s of page.containingSymbols) {
|
|
3441
|
+
lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${s.typeInfo ? ` \u2014 ${s.typeInfo}` : ""}`);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
if (nonPages.length > 0) {
|
|
3446
|
+
lines.push("");
|
|
3447
|
+
lines.push("Components/Functions:");
|
|
3448
|
+
for (const ref of nonPages) {
|
|
3449
|
+
lines.push(` ${ref.relativePath}`);
|
|
3450
|
+
for (const s of ref.containingSymbols) {
|
|
3451
|
+
const typePart = s.typeInfo && s.typeInfo.length < 120 ? ` \u2014 ${s.typeInfo}` : "";
|
|
3452
|
+
lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${typePart}`);
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
} else {
|
|
3457
|
+
lines.push("");
|
|
3458
|
+
lines.push("No external references found (symbol may be unused or only used within the same file).");
|
|
3459
|
+
}
|
|
3460
|
+
if (level2Refs.length > 0) {
|
|
3461
|
+
lines.push("");
|
|
3462
|
+
lines.push("=== Extended tree (level 2) ===");
|
|
3463
|
+
for (const l2 of level2Refs) {
|
|
3464
|
+
lines.push("");
|
|
3465
|
+
lines.push(`${l2.parentSymbol} (${l2.parentFile}) is used by:`);
|
|
3466
|
+
for (const ref of l2.refs) {
|
|
3467
|
+
const tag = ref.isPage ? " [PAGE]" : "";
|
|
3468
|
+
const route = ref.routePath ? ` \u2192 ${ref.routePath}` : "";
|
|
3469
|
+
const container = ref.containingSymbol ? ` in ${ref.containingSymbol}` : "";
|
|
3470
|
+
lines.push(` \u2514\u2500\u2500 ${ref.relativePath}${tag}${route}${container}`);
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
if (fileSymbols.length > 0) {
|
|
3475
|
+
lines.push("");
|
|
3476
|
+
lines.push(`=== File structure (${basename3(defFilePath)}) ===`);
|
|
3477
|
+
for (const sym of fileSymbols) {
|
|
3478
|
+
const marker = sym.name === symbol ? " \u2190 target" : "";
|
|
3479
|
+
lines.push(` ${sym.name} (${symbolKindName(sym.kind)}:${sym.selectionRange.start.line + 1})${marker}`);
|
|
3480
|
+
if (sym.children) {
|
|
3481
|
+
for (const child of sym.children.slice(0, 10)) {
|
|
3482
|
+
lines.push(` \u2514\u2500\u2500 ${child.name} (${symbolKindName(child.kind)}:${child.selectionRange.start.line + 1})`);
|
|
3483
|
+
}
|
|
3484
|
+
if (sym.children.length > 10) {
|
|
3485
|
+
lines.push(` ... and ${sym.children.length - 10} more`);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
const formattedResult = lines.join("\n");
|
|
3491
|
+
return {
|
|
3492
|
+
success: true,
|
|
3493
|
+
symbol,
|
|
3494
|
+
filePath: relDefPath,
|
|
3495
|
+
line: defLine + 1,
|
|
3496
|
+
kind: defSymbol ? symbolKindName(defSymbol.kind) : void 0,
|
|
3497
|
+
typeInfo: typeInfo || void 0,
|
|
3498
|
+
referenceCount: externalRefCount,
|
|
3499
|
+
referenceFiles: externalFileCount,
|
|
3500
|
+
pages: refFileInfos.filter((r) => r.isPage).map((r) => ({ path: r.relativePath, route: r.routePath })),
|
|
3501
|
+
formattedResult
|
|
3502
|
+
};
|
|
3503
|
+
} catch (error) {
|
|
3504
|
+
return {
|
|
3505
|
+
success: false,
|
|
3506
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3507
|
+
};
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
// src/agent/subagents/search.ts
|
|
2946
3514
|
var execAsync4 = promisify4(exec4);
|
|
2947
3515
|
var MAX_OUTPUT_CHARS4 = 1e4;
|
|
2948
3516
|
var MAX_FILE_SIZE3 = 1 * 1024 * 1024;
|
|
@@ -2972,17 +3540,20 @@ ${contextBlock}
|
|
|
2972
3540
|
- **glob**: Find files matching a name pattern. Best for file discovery.
|
|
2973
3541
|
- **read_file**: Read contents of a specific file. Use to examine code found in searches.
|
|
2974
3542
|
- **list_dir**: List directory contents. Use to understand project structure.
|
|
3543
|
+
- **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.
|
|
2975
3544
|
|
|
2976
3545
|
## Search Strategy
|
|
2977
3546
|
|
|
2978
3547
|
1. **Start with semantic_search** if available - it finds code by meaning, which is the fastest way to explore
|
|
2979
3548
|
2. **Use grep** for exact symbol/string matches (function names, class names, imports)
|
|
2980
|
-
3. **Use
|
|
2981
|
-
4. **
|
|
2982
|
-
5. **
|
|
3549
|
+
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.
|
|
3550
|
+
4. **Use glob** for file discovery by name patterns
|
|
3551
|
+
5. **Read key files** to get actual code content and understand context
|
|
3552
|
+
6. **Run searches in PARALLEL** - make multiple tool calls at once to cover different angles simultaneously. This is critical for speed.
|
|
2983
3553
|
|
|
2984
3554
|
### Tool Selection Guide
|
|
2985
3555
|
- Know the exact name? Use **grep** (e.g. \`getUserById\`, \`class AuthService\`)
|
|
3556
|
+
- Need type info, references, or impact analysis? Use **code_graph** (e.g. \`code_graph({ symbol: "UserCard" })\`)
|
|
2986
3557
|
- Exploring a concept? Use **semantic_search** (e.g. "how does authentication work")
|
|
2987
3558
|
- Looking for files? Use **glob** (e.g. \`**/*.config.ts\`, \`**/auth/**\`)
|
|
2988
3559
|
- Need file content? Use **read_file** with optional line ranges for large files
|
|
@@ -3019,17 +3590,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3019
3590
|
async getToolsAsync(options) {
|
|
3020
3591
|
const workingDirectory = options.workingDirectory;
|
|
3021
3592
|
const tools = {
|
|
3022
|
-
grep:
|
|
3593
|
+
grep: tool9({
|
|
3023
3594
|
description: "Search for patterns in files using ripgrep. Returns matching lines with file paths and line numbers.",
|
|
3024
|
-
inputSchema:
|
|
3025
|
-
pattern:
|
|
3026
|
-
path:
|
|
3027
|
-
fileType:
|
|
3028
|
-
maxResults:
|
|
3595
|
+
inputSchema: z10.object({
|
|
3596
|
+
pattern: z10.string().describe("The regex pattern to search for"),
|
|
3597
|
+
path: z10.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
|
|
3598
|
+
fileType: z10.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
|
|
3599
|
+
maxResults: z10.number().optional().default(50).describe("Maximum number of results to return")
|
|
3029
3600
|
}),
|
|
3030
3601
|
execute: async ({ pattern, path, fileType, maxResults }) => {
|
|
3031
3602
|
try {
|
|
3032
|
-
const searchPath = path ?
|
|
3603
|
+
const searchPath = path ? resolve9(workingDirectory, path) : workingDirectory;
|
|
3033
3604
|
let args = ["rg", "--line-number", "--no-heading"];
|
|
3034
3605
|
if (fileType) {
|
|
3035
3606
|
args.push("--type", fileType);
|
|
@@ -3066,11 +3637,11 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3066
3637
|
}
|
|
3067
3638
|
}
|
|
3068
3639
|
}),
|
|
3069
|
-
glob:
|
|
3640
|
+
glob: tool9({
|
|
3070
3641
|
description: "Find files matching a glob pattern. Returns list of matching file paths.",
|
|
3071
|
-
inputSchema:
|
|
3072
|
-
pattern:
|
|
3073
|
-
maxResults:
|
|
3642
|
+
inputSchema: z10.object({
|
|
3643
|
+
pattern: z10.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
|
|
3644
|
+
maxResults: z10.number().optional().default(100).describe("Maximum number of files to return")
|
|
3074
3645
|
}),
|
|
3075
3646
|
execute: async ({ pattern, maxResults }) => {
|
|
3076
3647
|
try {
|
|
@@ -3097,17 +3668,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3097
3668
|
}
|
|
3098
3669
|
}
|
|
3099
3670
|
}),
|
|
3100
|
-
read_file:
|
|
3671
|
+
read_file: tool9({
|
|
3101
3672
|
description: "Read the contents of a file. Use this to examine specific files found in search.",
|
|
3102
|
-
inputSchema:
|
|
3103
|
-
path:
|
|
3104
|
-
startLine:
|
|
3105
|
-
endLine:
|
|
3673
|
+
inputSchema: z10.object({
|
|
3674
|
+
path: z10.string().describe("Path to the file (relative to working directory or absolute)"),
|
|
3675
|
+
startLine: z10.number().optional().describe("Start reading from this line (1-indexed)"),
|
|
3676
|
+
endLine: z10.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
|
|
3106
3677
|
}),
|
|
3107
3678
|
execute: async ({ path, startLine, endLine }) => {
|
|
3108
3679
|
try {
|
|
3109
|
-
const absolutePath =
|
|
3110
|
-
if (!
|
|
3680
|
+
const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
|
|
3681
|
+
if (!existsSync12(absolutePath)) {
|
|
3111
3682
|
return {
|
|
3112
3683
|
success: false,
|
|
3113
3684
|
error: `File not found: ${path}`
|
|
@@ -3120,7 +3691,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3120
3691
|
error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Use startLine/endLine to read portions.`
|
|
3121
3692
|
};
|
|
3122
3693
|
}
|
|
3123
|
-
let content = await
|
|
3694
|
+
let content = await readFile8(absolutePath, "utf-8");
|
|
3124
3695
|
if (startLine !== void 0 || endLine !== void 0) {
|
|
3125
3696
|
const lines = content.split("\n");
|
|
3126
3697
|
const start = (startLine ?? 1) - 1;
|
|
@@ -3129,7 +3700,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3129
3700
|
}
|
|
3130
3701
|
return {
|
|
3131
3702
|
success: true,
|
|
3132
|
-
path:
|
|
3703
|
+
path: relative8(workingDirectory, absolutePath),
|
|
3133
3704
|
content: truncateOutput(content, MAX_OUTPUT_CHARS4),
|
|
3134
3705
|
lineCount: content.split("\n").length
|
|
3135
3706
|
};
|
|
@@ -3141,17 +3712,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3141
3712
|
}
|
|
3142
3713
|
}
|
|
3143
3714
|
}),
|
|
3144
|
-
list_dir:
|
|
3715
|
+
list_dir: tool9({
|
|
3145
3716
|
description: "List contents of a directory. Shows files and subdirectories.",
|
|
3146
|
-
inputSchema:
|
|
3147
|
-
path:
|
|
3148
|
-
recursive:
|
|
3149
|
-
maxDepth:
|
|
3717
|
+
inputSchema: z10.object({
|
|
3718
|
+
path: z10.string().optional().default(".").describe("Directory path (relative to working directory)"),
|
|
3719
|
+
recursive: z10.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
|
|
3720
|
+
maxDepth: z10.number().optional().default(2).describe("Maximum depth for recursive listing")
|
|
3150
3721
|
}),
|
|
3151
3722
|
execute: async ({ path, recursive, maxDepth }) => {
|
|
3152
3723
|
try {
|
|
3153
|
-
const absolutePath =
|
|
3154
|
-
if (!
|
|
3724
|
+
const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
|
|
3725
|
+
if (!existsSync12(absolutePath)) {
|
|
3155
3726
|
return {
|
|
3156
3727
|
success: false,
|
|
3157
3728
|
error: `Directory not found: ${path}`
|
|
@@ -3175,20 +3746,20 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3175
3746
|
const files = stdout.trim().split("\n").filter(Boolean);
|
|
3176
3747
|
return {
|
|
3177
3748
|
success: true,
|
|
3178
|
-
path:
|
|
3749
|
+
path: relative8(workingDirectory, absolutePath) || ".",
|
|
3179
3750
|
files,
|
|
3180
3751
|
count: files.length,
|
|
3181
3752
|
recursive: true
|
|
3182
3753
|
};
|
|
3183
3754
|
} else {
|
|
3184
|
-
const entries = await
|
|
3755
|
+
const entries = await readdir4(absolutePath, { withFileTypes: true });
|
|
3185
3756
|
const items = entries.slice(0, 200).map((e) => ({
|
|
3186
3757
|
name: e.name,
|
|
3187
3758
|
type: e.isDirectory() ? "directory" : "file"
|
|
3188
3759
|
}));
|
|
3189
3760
|
return {
|
|
3190
3761
|
success: true,
|
|
3191
|
-
path:
|
|
3762
|
+
path: relative8(workingDirectory, absolutePath) || ".",
|
|
3192
3763
|
items,
|
|
3193
3764
|
count: items.length
|
|
3194
3765
|
};
|
|
@@ -3200,6 +3771,9 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3200
3771
|
};
|
|
3201
3772
|
}
|
|
3202
3773
|
}
|
|
3774
|
+
}),
|
|
3775
|
+
code_graph: createCodeGraphTool({
|
|
3776
|
+
workingDirectory
|
|
3203
3777
|
})
|
|
3204
3778
|
};
|
|
3205
3779
|
try {
|
|
@@ -3293,6 +3867,26 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3293
3867
|
context: m.symbolName || m.language
|
|
3294
3868
|
});
|
|
3295
3869
|
}
|
|
3870
|
+
} else if (step.toolName === "code_graph" && output.success) {
|
|
3871
|
+
matchCount += output.referenceCount || 0;
|
|
3872
|
+
if (output.filePath) {
|
|
3873
|
+
findings.push({
|
|
3874
|
+
type: "file",
|
|
3875
|
+
path: output.filePath,
|
|
3876
|
+
lineNumber: output.line,
|
|
3877
|
+
content: output.typeInfo ? truncateOutput(output.typeInfo, 300) : void 0,
|
|
3878
|
+
relevance: "high",
|
|
3879
|
+
context: `${output.kind || "symbol"}${output.referenceCount ? `, ${output.referenceCount} refs` : ""}`
|
|
3880
|
+
});
|
|
3881
|
+
}
|
|
3882
|
+
for (const page of (output.pages || []).slice(0, 10)) {
|
|
3883
|
+
findings.push({
|
|
3884
|
+
type: "file",
|
|
3885
|
+
path: page.path,
|
|
3886
|
+
relevance: "high",
|
|
3887
|
+
context: page.route ? `route: ${page.route}` : "page"
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3296
3890
|
}
|
|
3297
3891
|
}
|
|
3298
3892
|
}
|
|
@@ -3314,7 +3908,7 @@ function createSearchSubagent(model) {
|
|
|
3314
3908
|
// src/tools/search.ts
|
|
3315
3909
|
var MAX_RESULT_CHARS = 1e4;
|
|
3316
3910
|
function createSearchTool(options) {
|
|
3317
|
-
return
|
|
3911
|
+
return tool10({
|
|
3318
3912
|
description: `Delegate an explore task to the explore_agent tool. Use this when you need to:
|
|
3319
3913
|
- Find files or code matching a pattern
|
|
3320
3914
|
- Explore the codebase structure
|
|
@@ -3324,11 +3918,12 @@ function createSearchTool(options) {
|
|
|
3324
3918
|
The Explore agent will explore the codebase and return a summary of findings.
|
|
3325
3919
|
This is more thorough than a simple grep because it can follow references and understand context.
|
|
3326
3920
|
It also has access to semantic search to find code by meaning, not just text.
|
|
3921
|
+
It can also use code_graph to inspect a symbol's type hierarchy, references, and which pages/routes use it.
|
|
3327
3922
|
|
|
3328
3923
|
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.`,
|
|
3329
|
-
inputSchema:
|
|
3330
|
-
query:
|
|
3331
|
-
context:
|
|
3924
|
+
inputSchema: z11.object({
|
|
3925
|
+
query: z11.string().describe("What to search for. Be specific about what you're looking for."),
|
|
3926
|
+
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.")
|
|
3332
3927
|
}),
|
|
3333
3928
|
execute: async ({ query, context }, toolOptions) => {
|
|
3334
3929
|
const toolCallId = toolOptions.toolCallId || `explore_agent_${Date.now()}`;
|
|
@@ -3472,6 +4067,9 @@ async function createTools(options) {
|
|
|
3472
4067
|
sessionId: options.sessionId,
|
|
3473
4068
|
workingDirectory: options.workingDirectory,
|
|
3474
4069
|
onProgress: options.onSearchProgress
|
|
4070
|
+
}),
|
|
4071
|
+
code_graph: createCodeGraphTool({
|
|
4072
|
+
workingDirectory: options.workingDirectory
|
|
3475
4073
|
})
|
|
3476
4074
|
};
|
|
3477
4075
|
if (options.enableSemanticSearch !== false) {
|
|
@@ -3491,6 +4089,7 @@ async function createTools(options) {
|
|
|
3491
4089
|
}
|
|
3492
4090
|
export {
|
|
3493
4091
|
createBashTool,
|
|
4092
|
+
createCodeGraphTool,
|
|
3494
4093
|
createLinterTool,
|
|
3495
4094
|
createLoadSkillTool,
|
|
3496
4095
|
createReadFileTool,
|