sparkecoder 0.1.56 → 0.1.58
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-CqKMYoJv.d.ts → index-DqLDYWhb.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-CZsTAZ90.d.ts → search-DINnDTgj.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/{static/yfBGAcI8gI55lwQllPu-z → TuFKgULSvgTGHxXzZoeMo}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/yfBGAcI8gI55lwQllPu-z → TuFKgULSvgTGHxXzZoeMo}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/yfBGAcI8gI55lwQllPu-z → TuFKgULSvgTGHxXzZoeMo}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{yfBGAcI8gI55lwQllPu-z → static/TuFKgULSvgTGHxXzZoeMo}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{yfBGAcI8gI55lwQllPu-z → static/TuFKgULSvgTGHxXzZoeMo}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{yfBGAcI8gI55lwQllPu-z → static/TuFKgULSvgTGHxXzZoeMo}/_ssgManifest.js +0 -0
- /package/web/.next/static/{yfBGAcI8gI55lwQllPu-z → TuFKgULSvgTGHxXzZoeMo}/_buildManifest.js +0 -0
- /package/web/.next/static/{yfBGAcI8gI55lwQllPu-z → TuFKgULSvgTGHxXzZoeMo}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{yfBGAcI8gI55lwQllPu-z → TuFKgULSvgTGHxXzZoeMo}/_ssgManifest.js +0 -0
package/dist/server/index.js
CHANGED
|
@@ -1461,13 +1461,13 @@ var semantic_search_exports = {};
|
|
|
1461
1461
|
__export(semantic_search_exports, {
|
|
1462
1462
|
createSemanticSearchTool: () => createSemanticSearchTool
|
|
1463
1463
|
});
|
|
1464
|
-
import { tool as
|
|
1465
|
-
import { z as
|
|
1466
|
-
import { existsSync as
|
|
1464
|
+
import { tool as tool8 } from "ai";
|
|
1465
|
+
import { z as z9 } from "zod";
|
|
1466
|
+
import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
|
|
1467
1467
|
import { join as join4 } from "path";
|
|
1468
1468
|
import { minimatch as minimatch3 } from "minimatch";
|
|
1469
1469
|
function createSemanticSearchTool(options) {
|
|
1470
|
-
return
|
|
1470
|
+
return tool8({
|
|
1471
1471
|
description: `Search the codebase using semantic similarity. This tool finds code by understanding its meaning, not just matching text.
|
|
1472
1472
|
|
|
1473
1473
|
Use this tool when:
|
|
@@ -1532,7 +1532,7 @@ Returns matching code snippets with file paths, line numbers, and relevance scor
|
|
|
1532
1532
|
continue;
|
|
1533
1533
|
}
|
|
1534
1534
|
const fullPath = join4(options.workingDirectory, filePath);
|
|
1535
|
-
if (!
|
|
1535
|
+
if (!existsSync11(fullPath)) {
|
|
1536
1536
|
continue;
|
|
1537
1537
|
}
|
|
1538
1538
|
let snippet = "";
|
|
@@ -1587,11 +1587,11 @@ var init_semantic_search = __esm({
|
|
|
1587
1587
|
"use strict";
|
|
1588
1588
|
init_semantic();
|
|
1589
1589
|
init_config();
|
|
1590
|
-
semanticSearchInputSchema =
|
|
1591
|
-
query:
|
|
1592
|
-
topK:
|
|
1593
|
-
filePattern:
|
|
1594
|
-
language:
|
|
1590
|
+
semanticSearchInputSchema = z9.object({
|
|
1591
|
+
query: z9.string().describe("Natural language search query describing what you want to find"),
|
|
1592
|
+
topK: z9.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
|
|
1593
|
+
filePattern: z9.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
|
|
1594
|
+
language: z9.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
|
|
1595
1595
|
});
|
|
1596
1596
|
}
|
|
1597
1597
|
});
|
|
@@ -1602,27 +1602,27 @@ import { Hono as Hono5 } from "hono";
|
|
|
1602
1602
|
import { serve } from "@hono/node-server";
|
|
1603
1603
|
import { cors } from "hono/cors";
|
|
1604
1604
|
import { logger } from "hono/logger";
|
|
1605
|
-
import { existsSync as
|
|
1606
|
-
import { resolve as
|
|
1605
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
1606
|
+
import { resolve as resolve10, dirname as dirname7, join as join8 } from "path";
|
|
1607
1607
|
import { spawn as spawn2 } from "child_process";
|
|
1608
1608
|
import { createServer as createNetServer } from "net";
|
|
1609
|
-
import { fileURLToPath as
|
|
1609
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1610
1610
|
|
|
1611
1611
|
// src/server/routes/sessions.ts
|
|
1612
1612
|
init_db();
|
|
1613
1613
|
import { Hono } from "hono";
|
|
1614
1614
|
import { zValidator } from "@hono/zod-validator";
|
|
1615
|
-
import { z as
|
|
1616
|
-
import { existsSync as
|
|
1617
|
-
import { readdir as
|
|
1618
|
-
import { join as join5, basename as
|
|
1615
|
+
import { z as z13 } from "zod";
|
|
1616
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
|
|
1617
|
+
import { readdir as readdir5 } from "fs/promises";
|
|
1618
|
+
import { join as join5, basename as basename4, extname as extname6, relative as relative9 } from "path";
|
|
1619
1619
|
import { nanoid as nanoid4 } from "nanoid";
|
|
1620
1620
|
|
|
1621
1621
|
// src/agent/index.ts
|
|
1622
1622
|
import {
|
|
1623
1623
|
streamText as streamText2,
|
|
1624
1624
|
generateText as generateText3,
|
|
1625
|
-
tool as
|
|
1625
|
+
tool as tool11,
|
|
1626
1626
|
stepCountIs as stepCountIs2
|
|
1627
1627
|
} from "ai";
|
|
1628
1628
|
|
|
@@ -1645,7 +1645,7 @@ var SUBAGENT_MODELS = {
|
|
|
1645
1645
|
// src/agent/index.ts
|
|
1646
1646
|
init_db();
|
|
1647
1647
|
init_config();
|
|
1648
|
-
import { z as
|
|
1648
|
+
import { z as z12 } from "zod";
|
|
1649
1649
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1650
1650
|
|
|
1651
1651
|
// src/tools/bash.ts
|
|
@@ -1932,8 +1932,8 @@ async function listSessionTerminals(sessionId, workingDirectory) {
|
|
|
1932
1932
|
const terminalsDir = join2(workingDirectory, LOG_BASE_DIR, sessionId, "terminals");
|
|
1933
1933
|
const terminals2 = [];
|
|
1934
1934
|
try {
|
|
1935
|
-
const { readdir:
|
|
1936
|
-
const entries = await
|
|
1935
|
+
const { readdir: readdir6 } = await import("fs/promises");
|
|
1936
|
+
const entries = await readdir6(terminalsDir, { withFileTypes: true });
|
|
1937
1937
|
for (const entry of entries) {
|
|
1938
1938
|
if (entry.isDirectory()) {
|
|
1939
1939
|
const meta = await getMeta(entry.name, workingDirectory, sessionId);
|
|
@@ -2736,7 +2736,11 @@ async function createClient(serverId, handle, root) {
|
|
|
2736
2736
|
dynamicRegistration: true
|
|
2737
2737
|
},
|
|
2738
2738
|
documentSymbol: {
|
|
2739
|
-
dynamicRegistration: true
|
|
2739
|
+
dynamicRegistration: true,
|
|
2740
|
+
hierarchicalDocumentSymbolSupport: true,
|
|
2741
|
+
symbolKind: {
|
|
2742
|
+
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]
|
|
2743
|
+
}
|
|
2740
2744
|
}
|
|
2741
2745
|
},
|
|
2742
2746
|
workspace: {
|
|
@@ -2833,7 +2837,7 @@ async function createClient(serverId, handle, root) {
|
|
|
2833
2837
|
},
|
|
2834
2838
|
async waitForDiagnostics(filePath, timeoutMs = 5e3) {
|
|
2835
2839
|
const normalized = normalizePath(filePath);
|
|
2836
|
-
return new Promise((
|
|
2840
|
+
return new Promise((resolve11) => {
|
|
2837
2841
|
const startTime = Date.now();
|
|
2838
2842
|
let debounceTimer;
|
|
2839
2843
|
let resolved = false;
|
|
@@ -2852,7 +2856,7 @@ async function createClient(serverId, handle, root) {
|
|
|
2852
2856
|
if (resolved) return;
|
|
2853
2857
|
resolved = true;
|
|
2854
2858
|
cleanup();
|
|
2855
|
-
|
|
2859
|
+
resolve11(diagnostics.get(normalized) || []);
|
|
2856
2860
|
};
|
|
2857
2861
|
const onDiagnostic = () => {
|
|
2858
2862
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -2878,6 +2882,100 @@ async function createClient(serverId, handle, root) {
|
|
|
2878
2882
|
getAllDiagnostics() {
|
|
2879
2883
|
return new Map(diagnostics);
|
|
2880
2884
|
},
|
|
2885
|
+
async getDefinition(filePath, line, character) {
|
|
2886
|
+
const normalized = normalizePath(filePath);
|
|
2887
|
+
if (!fileVersions.has(normalized)) {
|
|
2888
|
+
await client.notifyOpen(normalized);
|
|
2889
|
+
}
|
|
2890
|
+
try {
|
|
2891
|
+
const result = await connection.sendRequest("textDocument/definition", {
|
|
2892
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
2893
|
+
position: { line, character }
|
|
2894
|
+
});
|
|
2895
|
+
if (!result) return [];
|
|
2896
|
+
const items = Array.isArray(result) ? result : [result];
|
|
2897
|
+
return items.map((r) => ({
|
|
2898
|
+
uri: r.targetUri || r.uri,
|
|
2899
|
+
range: r.targetRange || r.range
|
|
2900
|
+
}));
|
|
2901
|
+
} catch (error) {
|
|
2902
|
+
console.error("[lsp] Error getting definition:", error);
|
|
2903
|
+
return [];
|
|
2904
|
+
}
|
|
2905
|
+
},
|
|
2906
|
+
async getReferences(filePath, line, character, includeDeclaration = false) {
|
|
2907
|
+
const normalized = normalizePath(filePath);
|
|
2908
|
+
if (!fileVersions.has(normalized)) {
|
|
2909
|
+
await client.notifyOpen(normalized);
|
|
2910
|
+
}
|
|
2911
|
+
try {
|
|
2912
|
+
const result = await connection.sendRequest("textDocument/references", {
|
|
2913
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
2914
|
+
position: { line, character },
|
|
2915
|
+
context: { includeDeclaration }
|
|
2916
|
+
});
|
|
2917
|
+
return result || [];
|
|
2918
|
+
} catch (error) {
|
|
2919
|
+
console.error("[lsp] Error getting references:", error);
|
|
2920
|
+
return [];
|
|
2921
|
+
}
|
|
2922
|
+
},
|
|
2923
|
+
async getHover(filePath, line, character) {
|
|
2924
|
+
const normalized = normalizePath(filePath);
|
|
2925
|
+
if (!fileVersions.has(normalized)) {
|
|
2926
|
+
await client.notifyOpen(normalized);
|
|
2927
|
+
}
|
|
2928
|
+
try {
|
|
2929
|
+
const result = await connection.sendRequest("textDocument/hover", {
|
|
2930
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
2931
|
+
position: { line, character }
|
|
2932
|
+
});
|
|
2933
|
+
if (!result || !result.contents) return null;
|
|
2934
|
+
if (typeof result.contents === "string") return result.contents;
|
|
2935
|
+
if (result.contents.value) return result.contents.value;
|
|
2936
|
+
if (Array.isArray(result.contents)) {
|
|
2937
|
+
return result.contents.map((c) => typeof c === "string" ? c : c.value).join("\n");
|
|
2938
|
+
}
|
|
2939
|
+
return null;
|
|
2940
|
+
} catch (error) {
|
|
2941
|
+
console.error("[lsp] Error getting hover:", error);
|
|
2942
|
+
return null;
|
|
2943
|
+
}
|
|
2944
|
+
},
|
|
2945
|
+
async getDocumentSymbols(filePath) {
|
|
2946
|
+
const normalized = normalizePath(filePath);
|
|
2947
|
+
if (!fileVersions.has(normalized)) {
|
|
2948
|
+
await client.notifyOpen(normalized);
|
|
2949
|
+
}
|
|
2950
|
+
try {
|
|
2951
|
+
const result = await connection.sendRequest("textDocument/documentSymbol", {
|
|
2952
|
+
textDocument: { uri: pathToFileURL(normalized).href }
|
|
2953
|
+
});
|
|
2954
|
+
if (!result || result.length === 0) return [];
|
|
2955
|
+
if (result[0].range) {
|
|
2956
|
+
return result;
|
|
2957
|
+
}
|
|
2958
|
+
return result.map((si) => ({
|
|
2959
|
+
name: si.name,
|
|
2960
|
+
kind: si.kind,
|
|
2961
|
+
range: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
2962
|
+
selectionRange: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
2963
|
+
detail: si.containerName
|
|
2964
|
+
}));
|
|
2965
|
+
} catch (error) {
|
|
2966
|
+
console.error("[lsp] Error getting document symbols:", error);
|
|
2967
|
+
return [];
|
|
2968
|
+
}
|
|
2969
|
+
},
|
|
2970
|
+
async findWorkspaceSymbols(query) {
|
|
2971
|
+
try {
|
|
2972
|
+
const result = await connection.sendRequest("workspace/symbol", { query });
|
|
2973
|
+
return result || [];
|
|
2974
|
+
} catch (error) {
|
|
2975
|
+
console.error("[lsp] Error finding workspace symbols:", error);
|
|
2976
|
+
return [];
|
|
2977
|
+
}
|
|
2978
|
+
},
|
|
2881
2979
|
async shutdown() {
|
|
2882
2980
|
try {
|
|
2883
2981
|
await connection.sendRequest("shutdown");
|
|
@@ -3000,6 +3098,24 @@ async function getAllDiagnostics() {
|
|
|
3000
3098
|
}
|
|
3001
3099
|
return results;
|
|
3002
3100
|
}
|
|
3101
|
+
async function getReferences(filePath, line, character, includeDeclaration = false) {
|
|
3102
|
+
const normalized = normalizePath(filePath);
|
|
3103
|
+
const client = await getClientForFile(normalized);
|
|
3104
|
+
if (!client) return [];
|
|
3105
|
+
return client.getReferences(normalized, line, character, includeDeclaration);
|
|
3106
|
+
}
|
|
3107
|
+
async function getHover(filePath, line, character) {
|
|
3108
|
+
const normalized = normalizePath(filePath);
|
|
3109
|
+
const client = await getClientForFile(normalized);
|
|
3110
|
+
if (!client) return null;
|
|
3111
|
+
return client.getHover(normalized, line, character);
|
|
3112
|
+
}
|
|
3113
|
+
async function getDocumentSymbols(filePath) {
|
|
3114
|
+
const normalized = normalizePath(filePath);
|
|
3115
|
+
const client = await getClientForFile(normalized);
|
|
3116
|
+
if (!client) return [];
|
|
3117
|
+
return client.getDocumentSymbols(normalized);
|
|
3118
|
+
}
|
|
3003
3119
|
async function formatDiagnosticsOutput(filePath, options = {}) {
|
|
3004
3120
|
const diagnostics = await getDiagnostics(filePath);
|
|
3005
3121
|
return formatDiagnosticsForAgent(filePath, diagnostics, options);
|
|
@@ -3095,7 +3211,7 @@ Working directory: ${options.workingDirectory}`,
|
|
|
3095
3211
|
isChunked: true
|
|
3096
3212
|
});
|
|
3097
3213
|
if (chunkCount > 1) {
|
|
3098
|
-
await new Promise((
|
|
3214
|
+
await new Promise((resolve11) => setTimeout(resolve11, 0));
|
|
3099
3215
|
}
|
|
3100
3216
|
}
|
|
3101
3217
|
}
|
|
@@ -3623,8 +3739,8 @@ ${file.relativePath}:`);
|
|
|
3623
3739
|
}
|
|
3624
3740
|
|
|
3625
3741
|
// src/tools/search.ts
|
|
3626
|
-
import { tool as
|
|
3627
|
-
import { z as
|
|
3742
|
+
import { tool as tool10 } from "ai";
|
|
3743
|
+
import { z as z11 } from "zod";
|
|
3628
3744
|
|
|
3629
3745
|
// src/agent/subagent.ts
|
|
3630
3746
|
import {
|
|
@@ -3796,8 +3912,8 @@ var Subagent = class {
|
|
|
3796
3912
|
if (eventQueue.length > 0) {
|
|
3797
3913
|
yield eventQueue.shift();
|
|
3798
3914
|
} else if (!done) {
|
|
3799
|
-
const event = await new Promise((
|
|
3800
|
-
resolveNext =
|
|
3915
|
+
const event = await new Promise((resolve11) => {
|
|
3916
|
+
resolveNext = resolve11;
|
|
3801
3917
|
});
|
|
3802
3918
|
if (event) {
|
|
3803
3919
|
yield event;
|
|
@@ -3809,14 +3925,466 @@ var Subagent = class {
|
|
|
3809
3925
|
};
|
|
3810
3926
|
|
|
3811
3927
|
// src/agent/subagents/search.ts
|
|
3812
|
-
import { tool as
|
|
3813
|
-
import { z as
|
|
3928
|
+
import { tool as tool9 } from "ai";
|
|
3929
|
+
import { z as z10 } from "zod";
|
|
3814
3930
|
import { exec as exec4 } from "child_process";
|
|
3815
3931
|
import { promisify as promisify4 } from "util";
|
|
3816
|
-
import { readFile as
|
|
3817
|
-
import { resolve as
|
|
3818
|
-
import { existsSync as
|
|
3932
|
+
import { readFile as readFile8, stat as stat3, readdir as readdir4 } from "fs/promises";
|
|
3933
|
+
import { resolve as resolve9, relative as relative8, isAbsolute as isAbsolute5 } from "path";
|
|
3934
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3819
3935
|
init_semantic();
|
|
3936
|
+
|
|
3937
|
+
// src/tools/code-graph.ts
|
|
3938
|
+
import { tool as tool7 } from "ai";
|
|
3939
|
+
import { z as z8 } from "zod";
|
|
3940
|
+
import { resolve as resolve8, relative as relative7, isAbsolute as isAbsolute4, basename as basename3 } from "path";
|
|
3941
|
+
import { readFile as readFile7, readdir as readdir3 } from "fs/promises";
|
|
3942
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3943
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3944
|
+
import { execFileSync } from "child_process";
|
|
3945
|
+
var codeGraphInputSchema = z8.object({
|
|
3946
|
+
symbol: z8.string().describe(
|
|
3947
|
+
"The symbol name to inspect (function, component, class, type, variable, etc.)"
|
|
3948
|
+
),
|
|
3949
|
+
filePath: z8.string().optional().describe(
|
|
3950
|
+
"File path where the symbol is defined. If omitted, searches the workspace via grep."
|
|
3951
|
+
),
|
|
3952
|
+
depth: z8.number().optional().default(2).describe(
|
|
3953
|
+
"How many levels of references to traverse upward (default: 2, max: 3). Level 1 = direct usages, level 2 = usages of those usages."
|
|
3954
|
+
)
|
|
3955
|
+
});
|
|
3956
|
+
function isPageFile(filePath) {
|
|
3957
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
3958
|
+
if (/\/app\/(.+\/)?(page|layout|loading|error|not-found)\.(tsx?|jsx?)$/.test(normalized)) return true;
|
|
3959
|
+
if (/\/pages\/(?!_|api\/).+\.(tsx?|jsx?)$/.test(normalized)) return true;
|
|
3960
|
+
return false;
|
|
3961
|
+
}
|
|
3962
|
+
function extractRoutePath(filePath, workingDirectory) {
|
|
3963
|
+
const rel = relative7(workingDirectory, filePath).replace(/\\/g, "/");
|
|
3964
|
+
const appMatch = rel.match(/(?:src\/)?app((?:\/[^/]+)*?)\/(?:page|layout|loading|error|not-found)\.\w+$/);
|
|
3965
|
+
if (appMatch) return appMatch[1] || "/";
|
|
3966
|
+
const pagesMatch = rel.match(/(?:src\/)?pages(\/.*?)(?:\/index)?\.\w+$/);
|
|
3967
|
+
if (pagesMatch) return pagesMatch[1] || "/";
|
|
3968
|
+
return void 0;
|
|
3969
|
+
}
|
|
3970
|
+
function symbolKindName(kind) {
|
|
3971
|
+
const names = {
|
|
3972
|
+
[5 /* Class */]: "class",
|
|
3973
|
+
[12 /* Function */]: "function",
|
|
3974
|
+
[6 /* Method */]: "method",
|
|
3975
|
+
[7 /* Property */]: "property",
|
|
3976
|
+
[13 /* Variable */]: "variable",
|
|
3977
|
+
[11 /* Interface */]: "interface",
|
|
3978
|
+
[10 /* Enum */]: "enum",
|
|
3979
|
+
[14 /* Constant */]: "constant",
|
|
3980
|
+
[9 /* Constructor */]: "constructor",
|
|
3981
|
+
[2 /* Module */]: "module",
|
|
3982
|
+
[3 /* Namespace */]: "namespace",
|
|
3983
|
+
[26 /* TypeParameter */]: "type_param",
|
|
3984
|
+
[8 /* Field */]: "field",
|
|
3985
|
+
[22 /* EnumMember */]: "enum_member",
|
|
3986
|
+
[19 /* Object */]: "object"
|
|
3987
|
+
};
|
|
3988
|
+
return names[kind] || "symbol";
|
|
3989
|
+
}
|
|
3990
|
+
function findContainingSymbol(symbols, line, character) {
|
|
3991
|
+
for (const sym of symbols) {
|
|
3992
|
+
if (!sym.range) continue;
|
|
3993
|
+
const { start, end } = sym.range;
|
|
3994
|
+
const afterStart = line > start.line || line === start.line && character >= start.character;
|
|
3995
|
+
const beforeEnd = line < end.line || line === end.line && character < end.character;
|
|
3996
|
+
if (afterStart && beforeEnd) {
|
|
3997
|
+
if (sym.children?.length) {
|
|
3998
|
+
const child = findContainingSymbol(sym.children, line, character);
|
|
3999
|
+
if (child) return child;
|
|
4000
|
+
}
|
|
4001
|
+
return sym;
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
return null;
|
|
4005
|
+
}
|
|
4006
|
+
function findSymbolByName(symbols, name) {
|
|
4007
|
+
for (const sym of symbols) {
|
|
4008
|
+
if (sym.name === name && sym.selectionRange) return sym;
|
|
4009
|
+
if (sym.children) {
|
|
4010
|
+
const found = findSymbolByName(sym.children, name);
|
|
4011
|
+
if (found) return found;
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
return null;
|
|
4015
|
+
}
|
|
4016
|
+
function cleanHoverText(text) {
|
|
4017
|
+
return text.replace(/```\w*\n?/g, "").replace(/\n```/g, "").trim();
|
|
4018
|
+
}
|
|
4019
|
+
async function grepForSymbol(symbol, workingDirectory) {
|
|
4020
|
+
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4021
|
+
const rgPatterns = [
|
|
4022
|
+
`(export\\s+)?(default\\s+)?(function|const|let|var|class|interface|type|enum)\\s+${escaped}\\b`,
|
|
4023
|
+
`(export\\s+)?(default\\s+)?\\b${escaped}\\s*[=:(]`
|
|
4024
|
+
];
|
|
4025
|
+
for (const pattern of rgPatterns) {
|
|
4026
|
+
try {
|
|
4027
|
+
const result = execFileSync("rg", [
|
|
4028
|
+
"-n",
|
|
4029
|
+
"--no-heading",
|
|
4030
|
+
"-e",
|
|
4031
|
+
pattern,
|
|
4032
|
+
"--glob",
|
|
4033
|
+
"*.{ts,tsx,js,jsx}",
|
|
4034
|
+
"-m",
|
|
4035
|
+
"5"
|
|
4036
|
+
], {
|
|
4037
|
+
cwd: workingDirectory,
|
|
4038
|
+
encoding: "utf-8",
|
|
4039
|
+
timeout: 5e3,
|
|
4040
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4041
|
+
}).trim();
|
|
4042
|
+
if (result) {
|
|
4043
|
+
const firstLine = result.split("\n")[0];
|
|
4044
|
+
const match = firstLine.match(/^(.+?):(\d+):(.*)/);
|
|
4045
|
+
if (match) {
|
|
4046
|
+
const col = match[3].indexOf(symbol);
|
|
4047
|
+
return {
|
|
4048
|
+
filePath: resolve8(workingDirectory, match[1]),
|
|
4049
|
+
line: parseInt(match[2]) - 1,
|
|
4050
|
+
char: col >= 0 ? col : 0
|
|
4051
|
+
};
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
} catch {
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
const defPattern = new RegExp(
|
|
4058
|
+
`(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${escaped}\\b`
|
|
4059
|
+
);
|
|
4060
|
+
const SUPPORTED_EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
4061
|
+
const IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage"]);
|
|
4062
|
+
async function search(dir, maxFiles) {
|
|
4063
|
+
if (maxFiles <= 0) return null;
|
|
4064
|
+
let remaining = maxFiles;
|
|
4065
|
+
try {
|
|
4066
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
4067
|
+
for (const entry of entries) {
|
|
4068
|
+
if (remaining <= 0) return null;
|
|
4069
|
+
const fullPath = resolve8(dir, entry.name);
|
|
4070
|
+
if (entry.isDirectory()) {
|
|
4071
|
+
if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
4072
|
+
const found = await search(fullPath, remaining);
|
|
4073
|
+
if (found) return found;
|
|
4074
|
+
remaining -= 10;
|
|
4075
|
+
} else if (entry.isFile()) {
|
|
4076
|
+
const ext = entry.name.substring(entry.name.lastIndexOf("."));
|
|
4077
|
+
if (!SUPPORTED_EXTS.has(ext)) continue;
|
|
4078
|
+
remaining--;
|
|
4079
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
4080
|
+
const lines = content.split("\n");
|
|
4081
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4082
|
+
if (defPattern.test(lines[i])) {
|
|
4083
|
+
const col = lines[i].indexOf(symbol);
|
|
4084
|
+
if (col >= 0) {
|
|
4085
|
+
return { filePath: fullPath, line: i, char: col };
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
} catch {
|
|
4092
|
+
}
|
|
4093
|
+
return null;
|
|
4094
|
+
}
|
|
4095
|
+
return search(workingDirectory, 200);
|
|
4096
|
+
}
|
|
4097
|
+
var MAX_REF_FILES = 15;
|
|
4098
|
+
var MAX_LEVEL2_PARENTS = 8;
|
|
4099
|
+
var MAX_LEVEL2_SYMBOLS_PER_PARENT = 3;
|
|
4100
|
+
function createCodeGraphTool(options) {
|
|
4101
|
+
return tool7({
|
|
4102
|
+
description: `Inspect a symbol's type information and usage graph using the TypeScript language server.
|
|
4103
|
+
|
|
4104
|
+
Given a symbol name (function, component, class, type, etc.), this tool will:
|
|
4105
|
+
1. Find its definition and full type signature (parameters, return type)
|
|
4106
|
+
2. Find all references \u2014 what components/functions/files use this symbol
|
|
4107
|
+
3. Identify which pages/routes contain it in their component tree
|
|
4108
|
+
4. Show the file's symbol structure for surrounding context
|
|
4109
|
+
|
|
4110
|
+
Use this to understand:
|
|
4111
|
+
- Component hierarchies (what renders what, which pages are affected)
|
|
4112
|
+
- Type signatures and parameter/return types before making changes
|
|
4113
|
+
- How deeply a symbol is used across the codebase
|
|
4114
|
+
- What will break if you change something
|
|
4115
|
+
|
|
4116
|
+
Supports TypeScript, JavaScript, TSX, JSX files.
|
|
4117
|
+
Working directory: ${options.workingDirectory}`,
|
|
4118
|
+
inputSchema: codeGraphInputSchema,
|
|
4119
|
+
execute: async ({ symbol, filePath, depth }) => {
|
|
4120
|
+
const maxDepth = Math.min(depth ?? 2, 3);
|
|
4121
|
+
try {
|
|
4122
|
+
let defFilePath;
|
|
4123
|
+
let defLine = 0;
|
|
4124
|
+
let defChar = 0;
|
|
4125
|
+
let defSymbol = null;
|
|
4126
|
+
if (filePath) {
|
|
4127
|
+
const absPath = isAbsolute4(filePath) ? filePath : resolve8(options.workingDirectory, filePath);
|
|
4128
|
+
if (!existsSync10(absPath)) {
|
|
4129
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
4130
|
+
}
|
|
4131
|
+
if (!isSupported(absPath)) {
|
|
4132
|
+
return { success: false, error: `File type not supported. Supports: ${getSupportedExtensions().join(", ")}` };
|
|
4133
|
+
}
|
|
4134
|
+
await touchFile(absPath, true);
|
|
4135
|
+
const symbols = await getDocumentSymbols(absPath);
|
|
4136
|
+
defSymbol = findSymbolByName(symbols, symbol);
|
|
4137
|
+
if (defSymbol) {
|
|
4138
|
+
defFilePath = absPath;
|
|
4139
|
+
defLine = defSymbol.selectionRange.start.line;
|
|
4140
|
+
defChar = defSymbol.selectionRange.start.character;
|
|
4141
|
+
} else {
|
|
4142
|
+
const content = await readFile7(absPath, "utf-8");
|
|
4143
|
+
const lines2 = content.split("\n");
|
|
4144
|
+
const defPattern = new RegExp(
|
|
4145
|
+
`(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`
|
|
4146
|
+
);
|
|
4147
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
4148
|
+
if (defPattern.test(lines2[i])) {
|
|
4149
|
+
const col = lines2[i].indexOf(symbol);
|
|
4150
|
+
if (col !== -1) {
|
|
4151
|
+
defFilePath = absPath;
|
|
4152
|
+
defLine = i;
|
|
4153
|
+
defChar = col;
|
|
4154
|
+
break;
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
if (!defFilePath) {
|
|
4159
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
4160
|
+
const col = lines2[i].indexOf(symbol);
|
|
4161
|
+
if (col !== -1) {
|
|
4162
|
+
defFilePath = absPath;
|
|
4163
|
+
defLine = i;
|
|
4164
|
+
defChar = col;
|
|
4165
|
+
break;
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
} else {
|
|
4171
|
+
const found = await grepForSymbol(symbol, options.workingDirectory);
|
|
4172
|
+
if (found) {
|
|
4173
|
+
defFilePath = found.filePath;
|
|
4174
|
+
defLine = found.line;
|
|
4175
|
+
defChar = found.char;
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
if (!defFilePath) {
|
|
4179
|
+
return {
|
|
4180
|
+
success: false,
|
|
4181
|
+
error: `Could not find symbol "${symbol}" in the codebase. Try providing a filePath.`
|
|
4182
|
+
};
|
|
4183
|
+
}
|
|
4184
|
+
await touchFile(defFilePath, true);
|
|
4185
|
+
const rawHover = await getHover(defFilePath, defLine, defChar);
|
|
4186
|
+
const typeInfo = rawHover ? cleanHoverText(rawHover) : null;
|
|
4187
|
+
const fileSymbols = await getDocumentSymbols(defFilePath);
|
|
4188
|
+
if (!defSymbol && fileSymbols.length > 0) {
|
|
4189
|
+
defSymbol = findSymbolByName(fileSymbols, symbol);
|
|
4190
|
+
}
|
|
4191
|
+
const references = await getReferences(defFilePath, defLine, defChar, false);
|
|
4192
|
+
const refsByFile = /* @__PURE__ */ new Map();
|
|
4193
|
+
for (const ref of references) {
|
|
4194
|
+
const refPath = fileURLToPath2(ref.uri);
|
|
4195
|
+
if (!refsByFile.has(refPath)) {
|
|
4196
|
+
refsByFile.set(refPath, []);
|
|
4197
|
+
}
|
|
4198
|
+
refsByFile.get(refPath).push(ref);
|
|
4199
|
+
}
|
|
4200
|
+
const refFileInfos = [];
|
|
4201
|
+
let processed = 0;
|
|
4202
|
+
for (const [refPath, locs] of refsByFile) {
|
|
4203
|
+
if (processed >= MAX_REF_FILES) break;
|
|
4204
|
+
if (refPath === defFilePath) continue;
|
|
4205
|
+
processed++;
|
|
4206
|
+
const relPath = relative7(options.workingDirectory, refPath);
|
|
4207
|
+
const pageFile = isPageFile(refPath);
|
|
4208
|
+
const routePath = pageFile ? extractRoutePath(refPath, options.workingDirectory) : void 0;
|
|
4209
|
+
await touchFile(refPath, false);
|
|
4210
|
+
const refFileSymbols = await getDocumentSymbols(refPath);
|
|
4211
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4212
|
+
for (const loc of locs) {
|
|
4213
|
+
const container = findContainingSymbol(
|
|
4214
|
+
refFileSymbols,
|
|
4215
|
+
loc.range.start.line,
|
|
4216
|
+
loc.range.start.character
|
|
4217
|
+
);
|
|
4218
|
+
if (container && !seen.has(container.name)) {
|
|
4219
|
+
let containerHover = null;
|
|
4220
|
+
try {
|
|
4221
|
+
const raw = await getHover(
|
|
4222
|
+
refPath,
|
|
4223
|
+
container.selectionRange.start.line,
|
|
4224
|
+
container.selectionRange.start.character
|
|
4225
|
+
);
|
|
4226
|
+
if (raw) containerHover = cleanHoverText(raw).split("\n")[0];
|
|
4227
|
+
} catch {
|
|
4228
|
+
}
|
|
4229
|
+
seen.set(container.name, {
|
|
4230
|
+
name: container.name,
|
|
4231
|
+
kind: symbolKindName(container.kind),
|
|
4232
|
+
line: container.selectionRange.start.line + 1,
|
|
4233
|
+
char: container.selectionRange.start.character,
|
|
4234
|
+
typeInfo: containerHover || void 0
|
|
4235
|
+
});
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
refFileInfos.push({
|
|
4239
|
+
filePath: refPath,
|
|
4240
|
+
relativePath: relPath,
|
|
4241
|
+
isPage: pageFile,
|
|
4242
|
+
routePath,
|
|
4243
|
+
containingSymbols: Array.from(seen.values())
|
|
4244
|
+
});
|
|
4245
|
+
}
|
|
4246
|
+
const level2Refs = [];
|
|
4247
|
+
if (maxDepth >= 2) {
|
|
4248
|
+
for (const refFile of refFileInfos.slice(0, MAX_LEVEL2_PARENTS)) {
|
|
4249
|
+
for (const sym of refFile.containingSymbols.slice(0, MAX_LEVEL2_SYMBOLS_PER_PARENT)) {
|
|
4250
|
+
try {
|
|
4251
|
+
const symLineIdx = sym.line - 1;
|
|
4252
|
+
const symChar = sym.char;
|
|
4253
|
+
const l2Locations = await getReferences(
|
|
4254
|
+
refFile.filePath,
|
|
4255
|
+
symLineIdx,
|
|
4256
|
+
symChar,
|
|
4257
|
+
false
|
|
4258
|
+
);
|
|
4259
|
+
const l2Nodes = [];
|
|
4260
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
4261
|
+
for (const loc of l2Locations.slice(0, 10)) {
|
|
4262
|
+
const l2Path = fileURLToPath2(loc.uri);
|
|
4263
|
+
if (l2Path === refFile.filePath || l2Path === defFilePath) continue;
|
|
4264
|
+
if (seenPaths.has(l2Path)) continue;
|
|
4265
|
+
seenPaths.add(l2Path);
|
|
4266
|
+
const l2Rel = relative7(options.workingDirectory, l2Path);
|
|
4267
|
+
const l2Page = isPageFile(l2Path);
|
|
4268
|
+
const l2Route = l2Page ? extractRoutePath(l2Path, options.workingDirectory) : void 0;
|
|
4269
|
+
let containerName;
|
|
4270
|
+
try {
|
|
4271
|
+
await touchFile(l2Path, false);
|
|
4272
|
+
const l2Symbols = await getDocumentSymbols(l2Path);
|
|
4273
|
+
const container = findContainingSymbol(l2Symbols, loc.range.start.line, loc.range.start.character);
|
|
4274
|
+
if (container) containerName = container.name;
|
|
4275
|
+
} catch {
|
|
4276
|
+
}
|
|
4277
|
+
l2Nodes.push({
|
|
4278
|
+
relativePath: l2Rel,
|
|
4279
|
+
isPage: l2Page,
|
|
4280
|
+
routePath: l2Route,
|
|
4281
|
+
containingSymbol: containerName
|
|
4282
|
+
});
|
|
4283
|
+
}
|
|
4284
|
+
if (l2Nodes.length > 0) {
|
|
4285
|
+
level2Refs.push({
|
|
4286
|
+
parentSymbol: sym.name,
|
|
4287
|
+
parentFile: refFile.relativePath,
|
|
4288
|
+
refs: l2Nodes
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
} catch {
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
const relDefPath = relative7(options.workingDirectory, defFilePath);
|
|
4297
|
+
const lines = [];
|
|
4298
|
+
lines.push(`=== ${symbol} ===`);
|
|
4299
|
+
lines.push(`File: ${relDefPath}:${defLine + 1}`);
|
|
4300
|
+
if (defSymbol) lines.push(`Kind: ${symbolKindName(defSymbol.kind)}`);
|
|
4301
|
+
if (typeInfo) lines.push(`Type: ${typeInfo}`);
|
|
4302
|
+
const externalRefCount = references.filter((r) => fileURLToPath2(r.uri) !== defFilePath).length;
|
|
4303
|
+
const externalFileCount = refsByFile.size - (refsByFile.has(defFilePath) ? 1 : 0);
|
|
4304
|
+
if (refFileInfos.length > 0) {
|
|
4305
|
+
lines.push("");
|
|
4306
|
+
lines.push(`=== Referenced by (${externalRefCount} usages across ${externalFileCount} files) ===`);
|
|
4307
|
+
const pages = refFileInfos.filter((r) => r.isPage);
|
|
4308
|
+
const nonPages = refFileInfos.filter((r) => !r.isPage);
|
|
4309
|
+
if (pages.length > 0) {
|
|
4310
|
+
lines.push("");
|
|
4311
|
+
lines.push("Pages/Routes:");
|
|
4312
|
+
for (const page of pages) {
|
|
4313
|
+
lines.push(` ${page.relativePath}${page.routePath ? ` \u2192 ${page.routePath}` : ""}`);
|
|
4314
|
+
for (const s of page.containingSymbols) {
|
|
4315
|
+
lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${s.typeInfo ? ` \u2014 ${s.typeInfo}` : ""}`);
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
if (nonPages.length > 0) {
|
|
4320
|
+
lines.push("");
|
|
4321
|
+
lines.push("Components/Functions:");
|
|
4322
|
+
for (const ref of nonPages) {
|
|
4323
|
+
lines.push(` ${ref.relativePath}`);
|
|
4324
|
+
for (const s of ref.containingSymbols) {
|
|
4325
|
+
const typePart = s.typeInfo && s.typeInfo.length < 120 ? ` \u2014 ${s.typeInfo}` : "";
|
|
4326
|
+
lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${typePart}`);
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
} else {
|
|
4331
|
+
lines.push("");
|
|
4332
|
+
lines.push("No external references found (symbol may be unused or only used within the same file).");
|
|
4333
|
+
}
|
|
4334
|
+
if (level2Refs.length > 0) {
|
|
4335
|
+
lines.push("");
|
|
4336
|
+
lines.push("=== Extended tree (level 2) ===");
|
|
4337
|
+
for (const l2 of level2Refs) {
|
|
4338
|
+
lines.push("");
|
|
4339
|
+
lines.push(`${l2.parentSymbol} (${l2.parentFile}) is used by:`);
|
|
4340
|
+
for (const ref of l2.refs) {
|
|
4341
|
+
const tag = ref.isPage ? " [PAGE]" : "";
|
|
4342
|
+
const route = ref.routePath ? ` \u2192 ${ref.routePath}` : "";
|
|
4343
|
+
const container = ref.containingSymbol ? ` in ${ref.containingSymbol}` : "";
|
|
4344
|
+
lines.push(` \u2514\u2500\u2500 ${ref.relativePath}${tag}${route}${container}`);
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
if (fileSymbols.length > 0) {
|
|
4349
|
+
lines.push("");
|
|
4350
|
+
lines.push(`=== File structure (${basename3(defFilePath)}) ===`);
|
|
4351
|
+
for (const sym of fileSymbols) {
|
|
4352
|
+
const marker = sym.name === symbol ? " \u2190 target" : "";
|
|
4353
|
+
lines.push(` ${sym.name} (${symbolKindName(sym.kind)}:${sym.selectionRange.start.line + 1})${marker}`);
|
|
4354
|
+
if (sym.children) {
|
|
4355
|
+
for (const child of sym.children.slice(0, 10)) {
|
|
4356
|
+
lines.push(` \u2514\u2500\u2500 ${child.name} (${symbolKindName(child.kind)}:${child.selectionRange.start.line + 1})`);
|
|
4357
|
+
}
|
|
4358
|
+
if (sym.children.length > 10) {
|
|
4359
|
+
lines.push(` ... and ${sym.children.length - 10} more`);
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
const formattedResult = lines.join("\n");
|
|
4365
|
+
return {
|
|
4366
|
+
success: true,
|
|
4367
|
+
symbol,
|
|
4368
|
+
filePath: relDefPath,
|
|
4369
|
+
line: defLine + 1,
|
|
4370
|
+
kind: defSymbol ? symbolKindName(defSymbol.kind) : void 0,
|
|
4371
|
+
typeInfo: typeInfo || void 0,
|
|
4372
|
+
referenceCount: externalRefCount,
|
|
4373
|
+
referenceFiles: externalFileCount,
|
|
4374
|
+
pages: refFileInfos.filter((r) => r.isPage).map((r) => ({ path: r.relativePath, route: r.routePath })),
|
|
4375
|
+
formattedResult
|
|
4376
|
+
};
|
|
4377
|
+
} catch (error) {
|
|
4378
|
+
return {
|
|
4379
|
+
success: false,
|
|
4380
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
});
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
// src/agent/subagents/search.ts
|
|
3820
4388
|
var execAsync4 = promisify4(exec4);
|
|
3821
4389
|
var MAX_OUTPUT_CHARS4 = 1e4;
|
|
3822
4390
|
var MAX_FILE_SIZE3 = 1 * 1024 * 1024;
|
|
@@ -3846,17 +4414,20 @@ ${contextBlock}
|
|
|
3846
4414
|
- **glob**: Find files matching a name pattern. Best for file discovery.
|
|
3847
4415
|
- **read_file**: Read contents of a specific file. Use to examine code found in searches.
|
|
3848
4416
|
- **list_dir**: List directory contents. Use to understand project structure.
|
|
4417
|
+
- **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.
|
|
3849
4418
|
|
|
3850
4419
|
## Search Strategy
|
|
3851
4420
|
|
|
3852
4421
|
1. **Start with semantic_search** if available - it finds code by meaning, which is the fastest way to explore
|
|
3853
4422
|
2. **Use grep** for exact symbol/string matches (function names, class names, imports)
|
|
3854
|
-
3. **Use
|
|
3855
|
-
4. **
|
|
3856
|
-
5. **
|
|
4423
|
+
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.
|
|
4424
|
+
4. **Use glob** for file discovery by name patterns
|
|
4425
|
+
5. **Read key files** to get actual code content and understand context
|
|
4426
|
+
6. **Run searches in PARALLEL** - make multiple tool calls at once to cover different angles simultaneously. This is critical for speed.
|
|
3857
4427
|
|
|
3858
4428
|
### Tool Selection Guide
|
|
3859
4429
|
- Know the exact name? Use **grep** (e.g. \`getUserById\`, \`class AuthService\`)
|
|
4430
|
+
- Need type info, references, or impact analysis? Use **code_graph** (e.g. \`code_graph({ symbol: "UserCard" })\`)
|
|
3860
4431
|
- Exploring a concept? Use **semantic_search** (e.g. "how does authentication work")
|
|
3861
4432
|
- Looking for files? Use **glob** (e.g. \`**/*.config.ts\`, \`**/auth/**\`)
|
|
3862
4433
|
- Need file content? Use **read_file** with optional line ranges for large files
|
|
@@ -3893,17 +4464,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3893
4464
|
async getToolsAsync(options) {
|
|
3894
4465
|
const workingDirectory = options.workingDirectory;
|
|
3895
4466
|
const tools = {
|
|
3896
|
-
grep:
|
|
4467
|
+
grep: tool9({
|
|
3897
4468
|
description: "Search for patterns in files using ripgrep. Returns matching lines with file paths and line numbers.",
|
|
3898
|
-
inputSchema:
|
|
3899
|
-
pattern:
|
|
3900
|
-
path:
|
|
3901
|
-
fileType:
|
|
3902
|
-
maxResults:
|
|
4469
|
+
inputSchema: z10.object({
|
|
4470
|
+
pattern: z10.string().describe("The regex pattern to search for"),
|
|
4471
|
+
path: z10.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
|
|
4472
|
+
fileType: z10.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
|
|
4473
|
+
maxResults: z10.number().optional().default(50).describe("Maximum number of results to return")
|
|
3903
4474
|
}),
|
|
3904
4475
|
execute: async ({ pattern, path, fileType, maxResults }) => {
|
|
3905
4476
|
try {
|
|
3906
|
-
const searchPath = path ?
|
|
4477
|
+
const searchPath = path ? resolve9(workingDirectory, path) : workingDirectory;
|
|
3907
4478
|
let args = ["rg", "--line-number", "--no-heading"];
|
|
3908
4479
|
if (fileType) {
|
|
3909
4480
|
args.push("--type", fileType);
|
|
@@ -3940,11 +4511,11 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3940
4511
|
}
|
|
3941
4512
|
}
|
|
3942
4513
|
}),
|
|
3943
|
-
glob:
|
|
4514
|
+
glob: tool9({
|
|
3944
4515
|
description: "Find files matching a glob pattern. Returns list of matching file paths.",
|
|
3945
|
-
inputSchema:
|
|
3946
|
-
pattern:
|
|
3947
|
-
maxResults:
|
|
4516
|
+
inputSchema: z10.object({
|
|
4517
|
+
pattern: z10.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
|
|
4518
|
+
maxResults: z10.number().optional().default(100).describe("Maximum number of files to return")
|
|
3948
4519
|
}),
|
|
3949
4520
|
execute: async ({ pattern, maxResults }) => {
|
|
3950
4521
|
try {
|
|
@@ -3971,17 +4542,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3971
4542
|
}
|
|
3972
4543
|
}
|
|
3973
4544
|
}),
|
|
3974
|
-
read_file:
|
|
4545
|
+
read_file: tool9({
|
|
3975
4546
|
description: "Read the contents of a file. Use this to examine specific files found in search.",
|
|
3976
|
-
inputSchema:
|
|
3977
|
-
path:
|
|
3978
|
-
startLine:
|
|
3979
|
-
endLine:
|
|
4547
|
+
inputSchema: z10.object({
|
|
4548
|
+
path: z10.string().describe("Path to the file (relative to working directory or absolute)"),
|
|
4549
|
+
startLine: z10.number().optional().describe("Start reading from this line (1-indexed)"),
|
|
4550
|
+
endLine: z10.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
|
|
3980
4551
|
}),
|
|
3981
4552
|
execute: async ({ path, startLine, endLine }) => {
|
|
3982
4553
|
try {
|
|
3983
|
-
const absolutePath =
|
|
3984
|
-
if (!
|
|
4554
|
+
const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
|
|
4555
|
+
if (!existsSync12(absolutePath)) {
|
|
3985
4556
|
return {
|
|
3986
4557
|
success: false,
|
|
3987
4558
|
error: `File not found: ${path}`
|
|
@@ -3994,7 +4565,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
3994
4565
|
error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Use startLine/endLine to read portions.`
|
|
3995
4566
|
};
|
|
3996
4567
|
}
|
|
3997
|
-
let content = await
|
|
4568
|
+
let content = await readFile8(absolutePath, "utf-8");
|
|
3998
4569
|
if (startLine !== void 0 || endLine !== void 0) {
|
|
3999
4570
|
const lines = content.split("\n");
|
|
4000
4571
|
const start = (startLine ?? 1) - 1;
|
|
@@ -4003,7 +4574,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4003
4574
|
}
|
|
4004
4575
|
return {
|
|
4005
4576
|
success: true,
|
|
4006
|
-
path:
|
|
4577
|
+
path: relative8(workingDirectory, absolutePath),
|
|
4007
4578
|
content: truncateOutput(content, MAX_OUTPUT_CHARS4),
|
|
4008
4579
|
lineCount: content.split("\n").length
|
|
4009
4580
|
};
|
|
@@ -4015,17 +4586,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4015
4586
|
}
|
|
4016
4587
|
}
|
|
4017
4588
|
}),
|
|
4018
|
-
list_dir:
|
|
4589
|
+
list_dir: tool9({
|
|
4019
4590
|
description: "List contents of a directory. Shows files and subdirectories.",
|
|
4020
|
-
inputSchema:
|
|
4021
|
-
path:
|
|
4022
|
-
recursive:
|
|
4023
|
-
maxDepth:
|
|
4591
|
+
inputSchema: z10.object({
|
|
4592
|
+
path: z10.string().optional().default(".").describe("Directory path (relative to working directory)"),
|
|
4593
|
+
recursive: z10.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
|
|
4594
|
+
maxDepth: z10.number().optional().default(2).describe("Maximum depth for recursive listing")
|
|
4024
4595
|
}),
|
|
4025
4596
|
execute: async ({ path, recursive, maxDepth }) => {
|
|
4026
4597
|
try {
|
|
4027
|
-
const absolutePath =
|
|
4028
|
-
if (!
|
|
4598
|
+
const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
|
|
4599
|
+
if (!existsSync12(absolutePath)) {
|
|
4029
4600
|
return {
|
|
4030
4601
|
success: false,
|
|
4031
4602
|
error: `Directory not found: ${path}`
|
|
@@ -4049,20 +4620,20 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4049
4620
|
const files = stdout.trim().split("\n").filter(Boolean);
|
|
4050
4621
|
return {
|
|
4051
4622
|
success: true,
|
|
4052
|
-
path:
|
|
4623
|
+
path: relative8(workingDirectory, absolutePath) || ".",
|
|
4053
4624
|
files,
|
|
4054
4625
|
count: files.length,
|
|
4055
4626
|
recursive: true
|
|
4056
4627
|
};
|
|
4057
4628
|
} else {
|
|
4058
|
-
const entries = await
|
|
4629
|
+
const entries = await readdir4(absolutePath, { withFileTypes: true });
|
|
4059
4630
|
const items = entries.slice(0, 200).map((e) => ({
|
|
4060
4631
|
name: e.name,
|
|
4061
4632
|
type: e.isDirectory() ? "directory" : "file"
|
|
4062
4633
|
}));
|
|
4063
4634
|
return {
|
|
4064
4635
|
success: true,
|
|
4065
|
-
path:
|
|
4636
|
+
path: relative8(workingDirectory, absolutePath) || ".",
|
|
4066
4637
|
items,
|
|
4067
4638
|
count: items.length
|
|
4068
4639
|
};
|
|
@@ -4074,6 +4645,9 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4074
4645
|
};
|
|
4075
4646
|
}
|
|
4076
4647
|
}
|
|
4648
|
+
}),
|
|
4649
|
+
code_graph: createCodeGraphTool({
|
|
4650
|
+
workingDirectory
|
|
4077
4651
|
})
|
|
4078
4652
|
};
|
|
4079
4653
|
try {
|
|
@@ -4167,6 +4741,26 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4167
4741
|
context: m.symbolName || m.language
|
|
4168
4742
|
});
|
|
4169
4743
|
}
|
|
4744
|
+
} else if (step.toolName === "code_graph" && output.success) {
|
|
4745
|
+
matchCount += output.referenceCount || 0;
|
|
4746
|
+
if (output.filePath) {
|
|
4747
|
+
findings.push({
|
|
4748
|
+
type: "file",
|
|
4749
|
+
path: output.filePath,
|
|
4750
|
+
lineNumber: output.line,
|
|
4751
|
+
content: output.typeInfo ? truncateOutput(output.typeInfo, 300) : void 0,
|
|
4752
|
+
relevance: "high",
|
|
4753
|
+
context: `${output.kind || "symbol"}${output.referenceCount ? `, ${output.referenceCount} refs` : ""}`
|
|
4754
|
+
});
|
|
4755
|
+
}
|
|
4756
|
+
for (const page of (output.pages || []).slice(0, 10)) {
|
|
4757
|
+
findings.push({
|
|
4758
|
+
type: "file",
|
|
4759
|
+
path: page.path,
|
|
4760
|
+
relevance: "high",
|
|
4761
|
+
context: page.route ? `route: ${page.route}` : "page"
|
|
4762
|
+
});
|
|
4763
|
+
}
|
|
4170
4764
|
}
|
|
4171
4765
|
}
|
|
4172
4766
|
}
|
|
@@ -4188,7 +4782,7 @@ function createSearchSubagent(model) {
|
|
|
4188
4782
|
// src/tools/search.ts
|
|
4189
4783
|
var MAX_RESULT_CHARS = 1e4;
|
|
4190
4784
|
function createSearchTool(options) {
|
|
4191
|
-
return
|
|
4785
|
+
return tool10({
|
|
4192
4786
|
description: `Delegate an explore task to the explore_agent tool. Use this when you need to:
|
|
4193
4787
|
- Find files or code matching a pattern
|
|
4194
4788
|
- Explore the codebase structure
|
|
@@ -4198,11 +4792,12 @@ function createSearchTool(options) {
|
|
|
4198
4792
|
The Explore agent will explore the codebase and return a summary of findings.
|
|
4199
4793
|
This is more thorough than a simple grep because it can follow references and understand context.
|
|
4200
4794
|
It also has access to semantic search to find code by meaning, not just text.
|
|
4795
|
+
It can also use code_graph to inspect a symbol's type hierarchy, references, and which pages/routes use it.
|
|
4201
4796
|
|
|
4202
4797
|
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.`,
|
|
4203
|
-
inputSchema:
|
|
4204
|
-
query:
|
|
4205
|
-
context:
|
|
4798
|
+
inputSchema: z11.object({
|
|
4799
|
+
query: z11.string().describe("What to search for. Be specific about what you're looking for."),
|
|
4800
|
+
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.")
|
|
4206
4801
|
}),
|
|
4207
4802
|
execute: async ({ query, context }, toolOptions) => {
|
|
4208
4803
|
const toolCallId = toolOptions.toolCallId || `explore_agent_${Date.now()}`;
|
|
@@ -4346,6 +4941,9 @@ async function createTools(options) {
|
|
|
4346
4941
|
sessionId: options.sessionId,
|
|
4347
4942
|
workingDirectory: options.workingDirectory,
|
|
4348
4943
|
onProgress: options.onSearchProgress
|
|
4944
|
+
}),
|
|
4945
|
+
code_graph: createCodeGraphTool({
|
|
4946
|
+
workingDirectory: options.workingDirectory
|
|
4349
4947
|
})
|
|
4350
4948
|
};
|
|
4351
4949
|
if (options.enableSemanticSearch !== false) {
|
|
@@ -4437,6 +5035,7 @@ You have access to powerful tools for:
|
|
|
4437
5035
|
- **todo**: Manage your task list to track progress on complex operations
|
|
4438
5036
|
- **load_skill**: Load specialized knowledge documents for specific tasks
|
|
4439
5037
|
- **explore_agent**: Explore agent for semantic discovery - for exploratory questions and finding code by meaning
|
|
5038
|
+
- **code_graph**: Inspect a symbol's type hierarchy and usage graph via the TypeScript language server
|
|
4440
5039
|
|
|
4441
5040
|
|
|
4442
5041
|
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.
|
|
@@ -4526,6 +5125,33 @@ linter({ paths: ["src/"] }) // Check all files in a directory
|
|
|
4526
5125
|
\`\`\`
|
|
4527
5126
|
Use this proactively after making code changes to catch errors early.
|
|
4528
5127
|
|
|
5128
|
+
### Code Graph Tool
|
|
5129
|
+
The code_graph tool uses the TypeScript language server to inspect a symbol's type hierarchy and usage graph:
|
|
5130
|
+
\`\`\`
|
|
5131
|
+
code_graph({ symbol: "UserCard" }) // Search workspace for symbol
|
|
5132
|
+
code_graph({ symbol: "UserCard", filePath: "src/components.tsx" }) // Look up in a specific file
|
|
5133
|
+
code_graph({ symbol: "formatUser", filePath: "utils.ts", depth: 2 }) // Traverse 2 levels up the reference tree
|
|
5134
|
+
\`\`\`
|
|
5135
|
+
|
|
5136
|
+
**What it returns:**
|
|
5137
|
+
- The symbol's full type signature (parameters, return type)
|
|
5138
|
+
- All files/functions/components that reference it (grouped into pages vs components)
|
|
5139
|
+
- Which Next.js pages/routes contain it in their component tree
|
|
5140
|
+
- Level-2 transitive usages (who uses the things that use this symbol)
|
|
5141
|
+
- The file's symbol structure for surrounding context
|
|
5142
|
+
|
|
5143
|
+
**When to use code_graph:**
|
|
5144
|
+
- **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.
|
|
5145
|
+
- **Before making changes** to a function/component \u2014 understand what depends on it and what will break
|
|
5146
|
+
- **To understand component hierarchies** \u2014 what renders what, which pages are affected across the *entire* codebase (not just the current page)
|
|
5147
|
+
- **To get type signatures** (props, params, return types) without reading entire files
|
|
5148
|
+
- **After a devtools selection** when the task involves refactoring, changing props, or anything that could impact other consumers
|
|
5149
|
+
|
|
5150
|
+
**When NOT to use code_graph:**
|
|
5151
|
+
- For exploratory "how does X work?" questions \u2014 use \`explore_agent\` instead
|
|
5152
|
+
- For exact string searches \u2014 use grep/rg directly
|
|
5153
|
+
- For non-TypeScript/JavaScript files \u2014 code_graph only supports TS/JS/TSX/JSX
|
|
5154
|
+
|
|
4529
5155
|
### Searching and Exploration
|
|
4530
5156
|
|
|
4531
5157
|
**Choose the right search approach:**
|
|
@@ -4533,7 +5159,8 @@ Use this proactively after making code changes to catch errors early.
|
|
|
4533
5159
|
0. **Use paths to your advantage \u2014 skip searching if you already have what you need.**
|
|
4534
5160
|
- 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.
|
|
4535
5161
|
- 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.
|
|
4536
|
-
- If the file path doesn't exist
|
|
5162
|
+
- **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.
|
|
5163
|
+
- **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.
|
|
4537
5164
|
- Read up and down component trees when you have the file path or page path to find what you're looking for.
|
|
4538
5165
|
1. **Use the \`explore_agent\` tool (Explore agent)** for:
|
|
4539
5166
|
- Semantic/exploratory questions: "How does authentication work?", "Where is user data processed?"
|
|
@@ -4551,7 +5178,14 @@ Use this proactively after making code changes to catch errors early.
|
|
|
4551
5178
|
- If you skip the \`context\` field, the explore agent is searching completely blind and will waste time guessing.
|
|
4552
5179
|
- NEVER call \`explore_agent\` with only a \`query\` and no \`context\` when the user's message contains devtools or component information.
|
|
4553
5180
|
|
|
4554
|
-
2. **Use
|
|
5181
|
+
2. **Use the \`code_graph\` tool** for:
|
|
5182
|
+
- Understanding what depends on a specific symbol before changing it
|
|
5183
|
+
- Tracing component/function usage up to page-level routes
|
|
5184
|
+
- Getting type signatures (params, return types) without reading full files
|
|
5185
|
+
- Finding exact components usages in the codebase
|
|
5186
|
+
- Answering "what will break if I change this?" or "which pages use this component?"
|
|
5187
|
+
|
|
5188
|
+
3. **Use direct commands (grep/rg, find)** for:
|
|
4555
5189
|
- Exact string matches: \`rg "functionName"\`, \`rg "class MyClass"\`
|
|
4556
5190
|
- Finding files by name: \`find . -name "*.config.ts"\`
|
|
4557
5191
|
- Simple pattern matching when you know exactly what you're looking for
|
|
@@ -4559,7 +5193,11 @@ Use this proactively after making code changes to catch errors early.
|
|
|
4559
5193
|
|
|
4560
5194
|
**Examples:**
|
|
4561
5195
|
- User selected \`<LandingButton>\` at \`src/components/LandingButton.tsx:12\` \u2192 Just \`read_file("src/components/LandingButton.tsx")\`. Do NOT call explore_agent.
|
|
5196
|
+
- 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.
|
|
5197
|
+
- 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.
|
|
4562
5198
|
- "Where is the API authentication handled?" (no file path given) \u2192 Use \`explore_agent\` tool
|
|
5199
|
+
- "What pages use the UserCard component?" \u2192 Use \`code_graph({ symbol: "UserCard" })\`
|
|
5200
|
+
- "What's the type signature of formatUser?" \u2192 Use \`code_graph({ symbol: "formatUser", filePath: "utils.ts" })\`
|
|
4563
5201
|
- "Find all usages of getUserById" \u2192 Use \`rg "getUserById"\`
|
|
4564
5202
|
- "How does the payment flow work?" \u2192 Use \`explore_agent\` tool
|
|
4565
5203
|
- "Find files named config" \u2192 Use \`find . -name "*config*"\`
|
|
@@ -5131,9 +5769,9 @@ ${prompt}` });
|
|
|
5131
5769
|
wrappedTools[name] = originalTool;
|
|
5132
5770
|
continue;
|
|
5133
5771
|
}
|
|
5134
|
-
wrappedTools[name] =
|
|
5772
|
+
wrappedTools[name] = tool11({
|
|
5135
5773
|
description: originalTool.description || "",
|
|
5136
|
-
inputSchema: originalTool.inputSchema ||
|
|
5774
|
+
inputSchema: originalTool.inputSchema || z12.object({}),
|
|
5137
5775
|
execute: async (input, toolOptions) => {
|
|
5138
5776
|
const toolCallId = toolOptions.toolCallId || nanoid3();
|
|
5139
5777
|
const execution = toolExecutionQueries.create({
|
|
@@ -5147,8 +5785,8 @@ ${prompt}` });
|
|
|
5147
5785
|
this.pendingApprovals.set(toolCallId, await execution);
|
|
5148
5786
|
options.onApprovalRequired?.(await execution);
|
|
5149
5787
|
await sessionQueries.updateStatus(this.session.id, "waiting");
|
|
5150
|
-
const approved = await new Promise((
|
|
5151
|
-
approvalResolvers.set(toolCallId, { resolve:
|
|
5788
|
+
const approved = await new Promise((resolve11) => {
|
|
5789
|
+
approvalResolvers.set(toolCallId, { resolve: resolve11, sessionId: this.session.id });
|
|
5152
5790
|
});
|
|
5153
5791
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
5154
5792
|
approvalResolvers.delete(toolCallId);
|
|
@@ -5274,18 +5912,18 @@ function cleanupPendingInputs() {
|
|
|
5274
5912
|
}
|
|
5275
5913
|
}
|
|
5276
5914
|
}
|
|
5277
|
-
var createSessionSchema =
|
|
5278
|
-
name:
|
|
5279
|
-
workingDirectory:
|
|
5280
|
-
model:
|
|
5281
|
-
toolApprovals:
|
|
5915
|
+
var createSessionSchema = z13.object({
|
|
5916
|
+
name: z13.string().optional(),
|
|
5917
|
+
workingDirectory: z13.string().optional(),
|
|
5918
|
+
model: z13.string().optional(),
|
|
5919
|
+
toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
|
|
5282
5920
|
});
|
|
5283
|
-
var paginationQuerySchema =
|
|
5284
|
-
limit:
|
|
5285
|
-
offset:
|
|
5921
|
+
var paginationQuerySchema = z13.object({
|
|
5922
|
+
limit: z13.string().optional(),
|
|
5923
|
+
offset: z13.string().optional()
|
|
5286
5924
|
});
|
|
5287
|
-
var messagesQuerySchema =
|
|
5288
|
-
limit:
|
|
5925
|
+
var messagesQuerySchema = z13.object({
|
|
5926
|
+
limit: z13.string().optional()
|
|
5289
5927
|
});
|
|
5290
5928
|
sessions.get(
|
|
5291
5929
|
"/",
|
|
@@ -5424,10 +6062,10 @@ sessions.get("/:id/tools", async (c) => {
|
|
|
5424
6062
|
count: executions.length
|
|
5425
6063
|
});
|
|
5426
6064
|
});
|
|
5427
|
-
var updateSessionSchema =
|
|
5428
|
-
model:
|
|
5429
|
-
name:
|
|
5430
|
-
toolApprovals:
|
|
6065
|
+
var updateSessionSchema = z13.object({
|
|
6066
|
+
model: z13.string().optional(),
|
|
6067
|
+
name: z13.string().optional(),
|
|
6068
|
+
toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
|
|
5431
6069
|
});
|
|
5432
6070
|
sessions.patch(
|
|
5433
6071
|
"/:id",
|
|
@@ -5497,8 +6135,8 @@ sessions.post("/:id/clear", async (c) => {
|
|
|
5497
6135
|
await agent.clearContext();
|
|
5498
6136
|
return c.json({ success: true, sessionId: id });
|
|
5499
6137
|
});
|
|
5500
|
-
var pendingInputSchema =
|
|
5501
|
-
text:
|
|
6138
|
+
var pendingInputSchema = z13.object({
|
|
6139
|
+
text: z13.string()
|
|
5502
6140
|
});
|
|
5503
6141
|
sessions.post(
|
|
5504
6142
|
"/:id/pending-input",
|
|
@@ -5529,13 +6167,13 @@ sessions.get("/:id/pending-input", async (c) => {
|
|
|
5529
6167
|
createdAt: pending.createdAt.toISOString()
|
|
5530
6168
|
});
|
|
5531
6169
|
});
|
|
5532
|
-
var devtoolsContextSchema =
|
|
5533
|
-
url:
|
|
5534
|
-
path:
|
|
5535
|
-
pageName:
|
|
5536
|
-
screenWidth:
|
|
5537
|
-
screenHeight:
|
|
5538
|
-
devicePixelRatio:
|
|
6170
|
+
var devtoolsContextSchema = z13.object({
|
|
6171
|
+
url: z13.string(),
|
|
6172
|
+
path: z13.string(),
|
|
6173
|
+
pageName: z13.string().optional(),
|
|
6174
|
+
screenWidth: z13.number().optional(),
|
|
6175
|
+
screenHeight: z13.number().optional(),
|
|
6176
|
+
devicePixelRatio: z13.number().optional()
|
|
5539
6177
|
});
|
|
5540
6178
|
sessions.post(
|
|
5541
6179
|
"/:id/devtools-context",
|
|
@@ -5707,7 +6345,7 @@ function getAttachmentsDir(sessionId) {
|
|
|
5707
6345
|
}
|
|
5708
6346
|
function ensureAttachmentsDir(sessionId) {
|
|
5709
6347
|
const dir = getAttachmentsDir(sessionId);
|
|
5710
|
-
if (!
|
|
6348
|
+
if (!existsSync13(dir)) {
|
|
5711
6349
|
mkdirSync3(dir, { recursive: true });
|
|
5712
6350
|
}
|
|
5713
6351
|
return dir;
|
|
@@ -5719,7 +6357,7 @@ sessions.get("/:id/attachments", async (c) => {
|
|
|
5719
6357
|
return c.json({ error: "Session not found" }, 404);
|
|
5720
6358
|
}
|
|
5721
6359
|
const dir = getAttachmentsDir(sessionId);
|
|
5722
|
-
if (!
|
|
6360
|
+
if (!existsSync13(dir)) {
|
|
5723
6361
|
return c.json({ sessionId, attachments: [], count: 0 });
|
|
5724
6362
|
}
|
|
5725
6363
|
const files = readdirSync(dir);
|
|
@@ -5758,7 +6396,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
5758
6396
|
const dir = ensureAttachmentsDir(sessionId);
|
|
5759
6397
|
const id = nanoid4(10);
|
|
5760
6398
|
const ext = extname6(file.name) || "";
|
|
5761
|
-
const safeFilename = `${id}_${
|
|
6399
|
+
const safeFilename = `${id}_${basename4(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
5762
6400
|
const filePath = join5(dir, safeFilename);
|
|
5763
6401
|
const arrayBuffer = await file.arrayBuffer();
|
|
5764
6402
|
writeFileSync2(filePath, Buffer.from(arrayBuffer));
|
|
@@ -5784,7 +6422,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
5784
6422
|
const dir = ensureAttachmentsDir(sessionId);
|
|
5785
6423
|
const id = nanoid4(10);
|
|
5786
6424
|
const ext = extname6(body.filename) || "";
|
|
5787
|
-
const safeFilename = `${id}_${
|
|
6425
|
+
const safeFilename = `${id}_${basename4(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
5788
6426
|
const filePath = join5(dir, safeFilename);
|
|
5789
6427
|
let base64Data = body.data;
|
|
5790
6428
|
if (base64Data.includes(",")) {
|
|
@@ -5814,7 +6452,7 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
5814
6452
|
return c.json({ error: "Session not found" }, 404);
|
|
5815
6453
|
}
|
|
5816
6454
|
const dir = getAttachmentsDir(sessionId);
|
|
5817
|
-
if (!
|
|
6455
|
+
if (!existsSync13(dir)) {
|
|
5818
6456
|
return c.json({ error: "Attachment not found" }, 404);
|
|
5819
6457
|
}
|
|
5820
6458
|
const files = readdirSync(dir);
|
|
@@ -5826,10 +6464,10 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
5826
6464
|
unlinkSync(filePath);
|
|
5827
6465
|
return c.json({ success: true, id: attachmentId });
|
|
5828
6466
|
});
|
|
5829
|
-
var filesQuerySchema =
|
|
5830
|
-
query:
|
|
6467
|
+
var filesQuerySchema = z13.object({
|
|
6468
|
+
query: z13.string().optional(),
|
|
5831
6469
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
5832
|
-
limit:
|
|
6470
|
+
limit: z13.string().optional()
|
|
5833
6471
|
// Max results (default 50)
|
|
5834
6472
|
});
|
|
5835
6473
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -5902,11 +6540,11 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
5902
6540
|
return results;
|
|
5903
6541
|
}
|
|
5904
6542
|
try {
|
|
5905
|
-
const entries = await
|
|
6543
|
+
const entries = await readdir5(currentDir, { withFileTypes: true });
|
|
5906
6544
|
for (const entry of entries) {
|
|
5907
6545
|
if (results.length >= limit * 2) break;
|
|
5908
6546
|
const fullPath = join5(currentDir, entry.name);
|
|
5909
|
-
const relativePath =
|
|
6547
|
+
const relativePath = relative9(baseDir, fullPath);
|
|
5910
6548
|
if (entry.isDirectory() && IGNORED_DIRECTORIES.has(entry.name)) {
|
|
5911
6549
|
continue;
|
|
5912
6550
|
}
|
|
@@ -5953,7 +6591,7 @@ sessions.get(
|
|
|
5953
6591
|
return c.json({ error: "Session not found" }, 404);
|
|
5954
6592
|
}
|
|
5955
6593
|
const workingDirectory = session.workingDirectory;
|
|
5956
|
-
if (!
|
|
6594
|
+
if (!existsSync13(workingDirectory)) {
|
|
5957
6595
|
return c.json({
|
|
5958
6596
|
sessionId,
|
|
5959
6597
|
workingDirectory,
|
|
@@ -6007,8 +6645,8 @@ sessions.get(
|
|
|
6007
6645
|
init_db();
|
|
6008
6646
|
import { Hono as Hono2 } from "hono";
|
|
6009
6647
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
6010
|
-
import { z as
|
|
6011
|
-
import { existsSync as
|
|
6648
|
+
import { z as z14 } from "zod";
|
|
6649
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
6012
6650
|
import { join as join6 } from "path";
|
|
6013
6651
|
init_config();
|
|
6014
6652
|
|
|
@@ -6149,7 +6787,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
|
|
|
6149
6787
|
toolCallId,
|
|
6150
6788
|
argsTextDelta: chunk
|
|
6151
6789
|
}));
|
|
6152
|
-
await new Promise((
|
|
6790
|
+
await new Promise((resolve11) => setTimeout(resolve11, 0));
|
|
6153
6791
|
}
|
|
6154
6792
|
}
|
|
6155
6793
|
function buildDevtoolsContextXml(sessionId) {
|
|
@@ -6172,30 +6810,30 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
6172
6810
|
${prompt}`;
|
|
6173
6811
|
}
|
|
6174
6812
|
var agents = new Hono2();
|
|
6175
|
-
var attachmentSchema =
|
|
6176
|
-
type:
|
|
6177
|
-
data:
|
|
6813
|
+
var attachmentSchema = z14.object({
|
|
6814
|
+
type: z14.enum(["image", "file"]),
|
|
6815
|
+
data: z14.string(),
|
|
6178
6816
|
// base64 data URL or raw base64
|
|
6179
|
-
mediaType:
|
|
6180
|
-
filename:
|
|
6817
|
+
mediaType: z14.string().optional(),
|
|
6818
|
+
filename: z14.string().optional()
|
|
6181
6819
|
});
|
|
6182
|
-
var runPromptSchema =
|
|
6183
|
-
prompt:
|
|
6820
|
+
var runPromptSchema = z14.object({
|
|
6821
|
+
prompt: z14.string(),
|
|
6184
6822
|
// Can be empty if attachments are provided
|
|
6185
|
-
attachments:
|
|
6823
|
+
attachments: z14.array(attachmentSchema).optional()
|
|
6186
6824
|
}).refine(
|
|
6187
6825
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
6188
6826
|
{ message: "Either prompt or attachments must be provided" }
|
|
6189
6827
|
);
|
|
6190
|
-
var quickStartSchema =
|
|
6191
|
-
prompt:
|
|
6192
|
-
name:
|
|
6193
|
-
workingDirectory:
|
|
6194
|
-
model:
|
|
6195
|
-
toolApprovals:
|
|
6828
|
+
var quickStartSchema = z14.object({
|
|
6829
|
+
prompt: z14.string().min(1),
|
|
6830
|
+
name: z14.string().optional(),
|
|
6831
|
+
workingDirectory: z14.string().optional(),
|
|
6832
|
+
model: z14.string().optional(),
|
|
6833
|
+
toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
|
|
6196
6834
|
});
|
|
6197
|
-
var rejectSchema =
|
|
6198
|
-
reason:
|
|
6835
|
+
var rejectSchema = z14.object({
|
|
6836
|
+
reason: z14.string().optional()
|
|
6199
6837
|
}).optional();
|
|
6200
6838
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
6201
6839
|
function getAttachmentsDirectory(sessionId) {
|
|
@@ -6204,7 +6842,7 @@ function getAttachmentsDirectory(sessionId) {
|
|
|
6204
6842
|
}
|
|
6205
6843
|
function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
6206
6844
|
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
6207
|
-
if (!
|
|
6845
|
+
if (!existsSync14(attachmentsDir)) {
|
|
6208
6846
|
mkdirSync4(attachmentsDir, { recursive: true });
|
|
6209
6847
|
}
|
|
6210
6848
|
let filename = attachment.filename;
|
|
@@ -6382,7 +7020,7 @@ ${prompt}` });
|
|
|
6382
7020
|
chunkIndex,
|
|
6383
7021
|
chunkCount
|
|
6384
7022
|
}));
|
|
6385
|
-
await new Promise((
|
|
7023
|
+
await new Promise((resolve11) => setTimeout(resolve11, 0));
|
|
6386
7024
|
}
|
|
6387
7025
|
},
|
|
6388
7026
|
onStepFinish: async () => {
|
|
@@ -6846,7 +7484,7 @@ agents.post(
|
|
|
6846
7484
|
chunkIndex,
|
|
6847
7485
|
chunkCount
|
|
6848
7486
|
}));
|
|
6849
|
-
await new Promise((
|
|
7487
|
+
await new Promise((resolve11) => setTimeout(resolve11, 0));
|
|
6850
7488
|
}
|
|
6851
7489
|
},
|
|
6852
7490
|
onStepFinish: async () => {
|
|
@@ -6983,11 +7621,11 @@ agents.post(
|
|
|
6983
7621
|
init_config();
|
|
6984
7622
|
import { Hono as Hono3 } from "hono";
|
|
6985
7623
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
6986
|
-
import { z as
|
|
7624
|
+
import { z as z15 } from "zod";
|
|
6987
7625
|
import { readFileSync as readFileSync5 } from "fs";
|
|
6988
|
-
import { fileURLToPath as
|
|
7626
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6989
7627
|
import { dirname as dirname6, join as join7 } from "path";
|
|
6990
|
-
var __filename =
|
|
7628
|
+
var __filename = fileURLToPath3(import.meta.url);
|
|
6991
7629
|
var __dirname = dirname6(__filename);
|
|
6992
7630
|
var possiblePaths = [
|
|
6993
7631
|
join7(__dirname, "../package.json"),
|
|
@@ -7093,9 +7731,9 @@ health.get("/api-keys", async (c) => {
|
|
|
7093
7731
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
7094
7732
|
});
|
|
7095
7733
|
});
|
|
7096
|
-
var setApiKeySchema =
|
|
7097
|
-
provider:
|
|
7098
|
-
apiKey:
|
|
7734
|
+
var setApiKeySchema = z15.object({
|
|
7735
|
+
provider: z15.string(),
|
|
7736
|
+
apiKey: z15.string().min(1)
|
|
7099
7737
|
});
|
|
7100
7738
|
health.post(
|
|
7101
7739
|
"/api-keys",
|
|
@@ -7134,13 +7772,13 @@ health.delete("/api-keys/:provider", async (c) => {
|
|
|
7134
7772
|
// src/server/routes/terminals.ts
|
|
7135
7773
|
import { Hono as Hono4 } from "hono";
|
|
7136
7774
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
7137
|
-
import { z as
|
|
7775
|
+
import { z as z16 } from "zod";
|
|
7138
7776
|
init_db();
|
|
7139
7777
|
var terminals = new Hono4();
|
|
7140
|
-
var spawnSchema =
|
|
7141
|
-
command:
|
|
7142
|
-
cwd:
|
|
7143
|
-
name:
|
|
7778
|
+
var spawnSchema = z16.object({
|
|
7779
|
+
command: z16.string(),
|
|
7780
|
+
cwd: z16.string().optional(),
|
|
7781
|
+
name: z16.string().optional()
|
|
7144
7782
|
});
|
|
7145
7783
|
terminals.post(
|
|
7146
7784
|
"/:sessionId/terminals",
|
|
@@ -7221,8 +7859,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
7221
7859
|
// We don't track exit codes in tmux mode
|
|
7222
7860
|
});
|
|
7223
7861
|
});
|
|
7224
|
-
var logsQuerySchema =
|
|
7225
|
-
tail:
|
|
7862
|
+
var logsQuerySchema = z16.object({
|
|
7863
|
+
tail: z16.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
7226
7864
|
});
|
|
7227
7865
|
terminals.get(
|
|
7228
7866
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -7246,8 +7884,8 @@ terminals.get(
|
|
|
7246
7884
|
});
|
|
7247
7885
|
}
|
|
7248
7886
|
);
|
|
7249
|
-
var killSchema =
|
|
7250
|
-
signal:
|
|
7887
|
+
var killSchema = z16.object({
|
|
7888
|
+
signal: z16.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
7251
7889
|
});
|
|
7252
7890
|
terminals.post(
|
|
7253
7891
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -7261,8 +7899,8 @@ terminals.post(
|
|
|
7261
7899
|
return c.json({ success: true, message: "Terminal killed" });
|
|
7262
7900
|
}
|
|
7263
7901
|
);
|
|
7264
|
-
var writeSchema =
|
|
7265
|
-
input:
|
|
7902
|
+
var writeSchema = z16.object({
|
|
7903
|
+
input: z16.string()
|
|
7266
7904
|
});
|
|
7267
7905
|
terminals.post(
|
|
7268
7906
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -7531,13 +8169,13 @@ var DEFAULT_WEB_PORT = 6969;
|
|
|
7531
8169
|
var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
|
|
7532
8170
|
function getWebDirectory() {
|
|
7533
8171
|
try {
|
|
7534
|
-
const currentDir = dirname7(
|
|
7535
|
-
const webDir =
|
|
7536
|
-
if (
|
|
8172
|
+
const currentDir = dirname7(fileURLToPath4(import.meta.url));
|
|
8173
|
+
const webDir = resolve10(currentDir, "..", "web");
|
|
8174
|
+
if (existsSync15(webDir) && existsSync15(join8(webDir, "package.json"))) {
|
|
7537
8175
|
return webDir;
|
|
7538
8176
|
}
|
|
7539
|
-
const altWebDir =
|
|
7540
|
-
if (
|
|
8177
|
+
const altWebDir = resolve10(currentDir, "..", "..", "web");
|
|
8178
|
+
if (existsSync15(altWebDir) && existsSync15(join8(altWebDir, "package.json"))) {
|
|
7541
8179
|
return altWebDir;
|
|
7542
8180
|
}
|
|
7543
8181
|
return null;
|
|
@@ -7560,18 +8198,18 @@ async function isSparkcoderWebRunning(port) {
|
|
|
7560
8198
|
}
|
|
7561
8199
|
}
|
|
7562
8200
|
function isPortInUse(port) {
|
|
7563
|
-
return new Promise((
|
|
8201
|
+
return new Promise((resolve11) => {
|
|
7564
8202
|
const server = createNetServer();
|
|
7565
8203
|
server.once("error", (err) => {
|
|
7566
8204
|
if (err.code === "EADDRINUSE") {
|
|
7567
|
-
|
|
8205
|
+
resolve11(true);
|
|
7568
8206
|
} else {
|
|
7569
|
-
|
|
8207
|
+
resolve11(false);
|
|
7570
8208
|
}
|
|
7571
8209
|
});
|
|
7572
8210
|
server.once("listening", () => {
|
|
7573
8211
|
server.close();
|
|
7574
|
-
|
|
8212
|
+
resolve11(false);
|
|
7575
8213
|
});
|
|
7576
8214
|
server.listen(port, "0.0.0.0");
|
|
7577
8215
|
});
|
|
@@ -7596,14 +8234,14 @@ async function findWebPort(preferredPort) {
|
|
|
7596
8234
|
}
|
|
7597
8235
|
function hasProductionBuild(webDir) {
|
|
7598
8236
|
const buildIdPath = join8(webDir, ".next", "BUILD_ID");
|
|
7599
|
-
return
|
|
8237
|
+
return existsSync15(buildIdPath);
|
|
7600
8238
|
}
|
|
7601
8239
|
function hasSourceFiles(webDir) {
|
|
7602
8240
|
const appDir = join8(webDir, "src", "app");
|
|
7603
8241
|
const pagesDir = join8(webDir, "src", "pages");
|
|
7604
8242
|
const rootAppDir = join8(webDir, "app");
|
|
7605
8243
|
const rootPagesDir = join8(webDir, "pages");
|
|
7606
|
-
return
|
|
8244
|
+
return existsSync15(appDir) || existsSync15(pagesDir) || existsSync15(rootAppDir) || existsSync15(rootPagesDir);
|
|
7607
8245
|
}
|
|
7608
8246
|
function getStandaloneServerPath(webDir) {
|
|
7609
8247
|
const possiblePaths2 = [
|
|
@@ -7611,14 +8249,14 @@ function getStandaloneServerPath(webDir) {
|
|
|
7611
8249
|
join8(webDir, ".next", "standalone", "web", "server.js")
|
|
7612
8250
|
];
|
|
7613
8251
|
for (const serverPath of possiblePaths2) {
|
|
7614
|
-
if (
|
|
8252
|
+
if (existsSync15(serverPath)) {
|
|
7615
8253
|
return serverPath;
|
|
7616
8254
|
}
|
|
7617
8255
|
}
|
|
7618
8256
|
return null;
|
|
7619
8257
|
}
|
|
7620
8258
|
function runCommand(command, args, cwd, env) {
|
|
7621
|
-
return new Promise((
|
|
8259
|
+
return new Promise((resolve11) => {
|
|
7622
8260
|
const child = spawn2(command, args, {
|
|
7623
8261
|
cwd,
|
|
7624
8262
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7633,10 +8271,10 @@ function runCommand(command, args, cwd, env) {
|
|
|
7633
8271
|
output += data.toString();
|
|
7634
8272
|
});
|
|
7635
8273
|
child.on("close", (code) => {
|
|
7636
|
-
|
|
8274
|
+
resolve11({ success: code === 0, output });
|
|
7637
8275
|
});
|
|
7638
8276
|
child.on("error", (err) => {
|
|
7639
|
-
|
|
8277
|
+
resolve11({ success: false, output: err.message });
|
|
7640
8278
|
});
|
|
7641
8279
|
});
|
|
7642
8280
|
}
|
|
@@ -7651,8 +8289,8 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
7651
8289
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
7652
8290
|
return { process: null, port: actualPort };
|
|
7653
8291
|
}
|
|
7654
|
-
const usePnpm =
|
|
7655
|
-
const useNpm = !usePnpm &&
|
|
8292
|
+
const usePnpm = existsSync15(join8(webDir, "pnpm-lock.yaml"));
|
|
8293
|
+
const useNpm = !usePnpm && existsSync15(join8(webDir, "package-lock.json"));
|
|
7656
8294
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
7657
8295
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
7658
8296
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
@@ -7720,10 +8358,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
7720
8358
|
let started = false;
|
|
7721
8359
|
let exited = false;
|
|
7722
8360
|
let exitCode = null;
|
|
7723
|
-
const startedPromise = new Promise((
|
|
8361
|
+
const startedPromise = new Promise((resolve11) => {
|
|
7724
8362
|
const timeout = setTimeout(() => {
|
|
7725
8363
|
if (!started && !exited) {
|
|
7726
|
-
|
|
8364
|
+
resolve11(false);
|
|
7727
8365
|
}
|
|
7728
8366
|
}, startupTimeout);
|
|
7729
8367
|
child.stdout?.on("data", (data) => {
|
|
@@ -7737,7 +8375,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
7737
8375
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
7738
8376
|
started = true;
|
|
7739
8377
|
clearTimeout(timeout);
|
|
7740
|
-
|
|
8378
|
+
resolve11(true);
|
|
7741
8379
|
}
|
|
7742
8380
|
});
|
|
7743
8381
|
child.stderr?.on("data", (data) => {
|
|
@@ -7749,14 +8387,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
7749
8387
|
child.on("error", (err) => {
|
|
7750
8388
|
if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
|
|
7751
8389
|
clearTimeout(timeout);
|
|
7752
|
-
|
|
8390
|
+
resolve11(false);
|
|
7753
8391
|
});
|
|
7754
8392
|
child.on("exit", (code) => {
|
|
7755
8393
|
exited = true;
|
|
7756
8394
|
exitCode = code;
|
|
7757
8395
|
if (!started) {
|
|
7758
8396
|
clearTimeout(timeout);
|
|
7759
|
-
|
|
8397
|
+
resolve11(false);
|
|
7760
8398
|
}
|
|
7761
8399
|
webUIProcess = null;
|
|
7762
8400
|
});
|
|
@@ -7849,7 +8487,7 @@ async function startServer(options = {}) {
|
|
|
7849
8487
|
if (options.workingDirectory) {
|
|
7850
8488
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
7851
8489
|
}
|
|
7852
|
-
if (!
|
|
8490
|
+
if (!existsSync15(config.resolvedWorkingDirectory)) {
|
|
7853
8491
|
mkdirSync5(config.resolvedWorkingDirectory, { recursive: true });
|
|
7854
8492
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
7855
8493
|
}
|