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/cli.js
CHANGED
|
@@ -1872,7 +1872,7 @@ function formatError(error) {
|
|
|
1872
1872
|
}
|
|
1873
1873
|
}
|
|
1874
1874
|
function sleep(ms) {
|
|
1875
|
-
return new Promise((
|
|
1875
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
1876
1876
|
}
|
|
1877
1877
|
function isPathExcluded(relativePath, exclude) {
|
|
1878
1878
|
return exclude.some((pattern) => {
|
|
@@ -1890,7 +1890,7 @@ function isPathExcluded(relativePath, exclude) {
|
|
|
1890
1890
|
}
|
|
1891
1891
|
async function walkDirectory(dir, include, exclude, baseDir) {
|
|
1892
1892
|
const { readdirSync: readdirSync2 } = await import("fs");
|
|
1893
|
-
const { join: join10, relative:
|
|
1893
|
+
const { join: join10, relative: relative10 } = await import("path");
|
|
1894
1894
|
const files = [];
|
|
1895
1895
|
function walk(currentDir) {
|
|
1896
1896
|
let entries;
|
|
@@ -1901,7 +1901,7 @@ async function walkDirectory(dir, include, exclude, baseDir) {
|
|
|
1901
1901
|
}
|
|
1902
1902
|
for (const entry of entries) {
|
|
1903
1903
|
const fullPath = join10(currentDir, entry.name);
|
|
1904
|
-
const relativePath =
|
|
1904
|
+
const relativePath = relative10(baseDir, fullPath);
|
|
1905
1905
|
if (isPathExcluded(relativePath, exclude)) {
|
|
1906
1906
|
continue;
|
|
1907
1907
|
}
|
|
@@ -2267,13 +2267,13 @@ var semantic_search_exports = {};
|
|
|
2267
2267
|
__export(semantic_search_exports, {
|
|
2268
2268
|
createSemanticSearchTool: () => createSemanticSearchTool
|
|
2269
2269
|
});
|
|
2270
|
-
import { tool as
|
|
2271
|
-
import { z as
|
|
2272
|
-
import { existsSync as
|
|
2270
|
+
import { tool as tool8 } from "ai";
|
|
2271
|
+
import { z as z9 } from "zod";
|
|
2272
|
+
import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
|
|
2273
2273
|
import { join as join4 } from "path";
|
|
2274
2274
|
import { minimatch as minimatch3 } from "minimatch";
|
|
2275
2275
|
function createSemanticSearchTool(options) {
|
|
2276
|
-
return
|
|
2276
|
+
return tool8({
|
|
2277
2277
|
description: `Search the codebase using semantic similarity. This tool finds code by understanding its meaning, not just matching text.
|
|
2278
2278
|
|
|
2279
2279
|
Use this tool when:
|
|
@@ -2338,7 +2338,7 @@ Returns matching code snippets with file paths, line numbers, and relevance scor
|
|
|
2338
2338
|
continue;
|
|
2339
2339
|
}
|
|
2340
2340
|
const fullPath = join4(options.workingDirectory, filePath);
|
|
2341
|
-
if (!
|
|
2341
|
+
if (!existsSync11(fullPath)) {
|
|
2342
2342
|
continue;
|
|
2343
2343
|
}
|
|
2344
2344
|
let snippet = "";
|
|
@@ -2393,11 +2393,11 @@ var init_semantic_search = __esm({
|
|
|
2393
2393
|
"use strict";
|
|
2394
2394
|
init_semantic();
|
|
2395
2395
|
init_config();
|
|
2396
|
-
semanticSearchInputSchema =
|
|
2397
|
-
query:
|
|
2398
|
-
topK:
|
|
2399
|
-
filePattern:
|
|
2400
|
-
language:
|
|
2396
|
+
semanticSearchInputSchema = z9.object({
|
|
2397
|
+
query: z9.string().describe("Natural language search query describing what you want to find"),
|
|
2398
|
+
topK: z9.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
|
|
2399
|
+
filePattern: z9.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
|
|
2400
|
+
language: z9.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
|
|
2401
2401
|
});
|
|
2402
2402
|
}
|
|
2403
2403
|
});
|
|
@@ -2415,27 +2415,27 @@ import { Hono as Hono5 } from "hono";
|
|
|
2415
2415
|
import { serve } from "@hono/node-server";
|
|
2416
2416
|
import { cors } from "hono/cors";
|
|
2417
2417
|
import { logger } from "hono/logger";
|
|
2418
|
-
import { existsSync as
|
|
2419
|
-
import { resolve as
|
|
2418
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
2419
|
+
import { resolve as resolve10, dirname as dirname7, join as join8 } from "path";
|
|
2420
2420
|
import { spawn as spawn2 } from "child_process";
|
|
2421
2421
|
import { createServer as createNetServer } from "net";
|
|
2422
|
-
import { fileURLToPath as
|
|
2422
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2423
2423
|
|
|
2424
2424
|
// src/server/routes/sessions.ts
|
|
2425
2425
|
init_db();
|
|
2426
2426
|
import { Hono } from "hono";
|
|
2427
2427
|
import { zValidator } from "@hono/zod-validator";
|
|
2428
|
-
import { z as
|
|
2429
|
-
import { existsSync as
|
|
2430
|
-
import { readdir as
|
|
2431
|
-
import { join as join5, basename as
|
|
2428
|
+
import { z as z13 } from "zod";
|
|
2429
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
|
|
2430
|
+
import { readdir as readdir5 } from "fs/promises";
|
|
2431
|
+
import { join as join5, basename as basename4, extname as extname6, relative as relative9 } from "path";
|
|
2432
2432
|
import { nanoid as nanoid4 } from "nanoid";
|
|
2433
2433
|
|
|
2434
2434
|
// src/agent/index.ts
|
|
2435
2435
|
import {
|
|
2436
2436
|
streamText as streamText2,
|
|
2437
2437
|
generateText as generateText3,
|
|
2438
|
-
tool as
|
|
2438
|
+
tool as tool11,
|
|
2439
2439
|
stepCountIs as stepCountIs2
|
|
2440
2440
|
} from "ai";
|
|
2441
2441
|
|
|
@@ -2458,7 +2458,7 @@ var SUBAGENT_MODELS = {
|
|
|
2458
2458
|
// src/agent/index.ts
|
|
2459
2459
|
init_db();
|
|
2460
2460
|
init_config();
|
|
2461
|
-
import { z as
|
|
2461
|
+
import { z as z12 } from "zod";
|
|
2462
2462
|
import { nanoid as nanoid3 } from "nanoid";
|
|
2463
2463
|
|
|
2464
2464
|
// src/tools/bash.ts
|
|
@@ -2745,8 +2745,8 @@ async function listSessionTerminals(sessionId, workingDirectory) {
|
|
|
2745
2745
|
const terminalsDir = join2(workingDirectory, LOG_BASE_DIR, sessionId, "terminals");
|
|
2746
2746
|
const terminals2 = [];
|
|
2747
2747
|
try {
|
|
2748
|
-
const { readdir:
|
|
2749
|
-
const entries = await
|
|
2748
|
+
const { readdir: readdir6 } = await import("fs/promises");
|
|
2749
|
+
const entries = await readdir6(terminalsDir, { withFileTypes: true });
|
|
2750
2750
|
for (const entry of entries) {
|
|
2751
2751
|
if (entry.isDirectory()) {
|
|
2752
2752
|
const meta = await getMeta(entry.name, workingDirectory, sessionId);
|
|
@@ -3549,7 +3549,11 @@ async function createClient(serverId, handle, root) {
|
|
|
3549
3549
|
dynamicRegistration: true
|
|
3550
3550
|
},
|
|
3551
3551
|
documentSymbol: {
|
|
3552
|
-
dynamicRegistration: true
|
|
3552
|
+
dynamicRegistration: true,
|
|
3553
|
+
hierarchicalDocumentSymbolSupport: true,
|
|
3554
|
+
symbolKind: {
|
|
3555
|
+
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]
|
|
3556
|
+
}
|
|
3553
3557
|
}
|
|
3554
3558
|
},
|
|
3555
3559
|
workspace: {
|
|
@@ -3646,7 +3650,7 @@ async function createClient(serverId, handle, root) {
|
|
|
3646
3650
|
},
|
|
3647
3651
|
async waitForDiagnostics(filePath, timeoutMs = 5e3) {
|
|
3648
3652
|
const normalized = normalizePath(filePath);
|
|
3649
|
-
return new Promise((
|
|
3653
|
+
return new Promise((resolve12) => {
|
|
3650
3654
|
const startTime = Date.now();
|
|
3651
3655
|
let debounceTimer;
|
|
3652
3656
|
let resolved = false;
|
|
@@ -3665,7 +3669,7 @@ async function createClient(serverId, handle, root) {
|
|
|
3665
3669
|
if (resolved) return;
|
|
3666
3670
|
resolved = true;
|
|
3667
3671
|
cleanup();
|
|
3668
|
-
|
|
3672
|
+
resolve12(diagnostics.get(normalized) || []);
|
|
3669
3673
|
};
|
|
3670
3674
|
const onDiagnostic = () => {
|
|
3671
3675
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -3691,6 +3695,100 @@ async function createClient(serverId, handle, root) {
|
|
|
3691
3695
|
getAllDiagnostics() {
|
|
3692
3696
|
return new Map(diagnostics);
|
|
3693
3697
|
},
|
|
3698
|
+
async getDefinition(filePath, line, character) {
|
|
3699
|
+
const normalized = normalizePath(filePath);
|
|
3700
|
+
if (!fileVersions.has(normalized)) {
|
|
3701
|
+
await client.notifyOpen(normalized);
|
|
3702
|
+
}
|
|
3703
|
+
try {
|
|
3704
|
+
const result = await connection.sendRequest("textDocument/definition", {
|
|
3705
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
3706
|
+
position: { line, character }
|
|
3707
|
+
});
|
|
3708
|
+
if (!result) return [];
|
|
3709
|
+
const items = Array.isArray(result) ? result : [result];
|
|
3710
|
+
return items.map((r) => ({
|
|
3711
|
+
uri: r.targetUri || r.uri,
|
|
3712
|
+
range: r.targetRange || r.range
|
|
3713
|
+
}));
|
|
3714
|
+
} catch (error) {
|
|
3715
|
+
console.error("[lsp] Error getting definition:", error);
|
|
3716
|
+
return [];
|
|
3717
|
+
}
|
|
3718
|
+
},
|
|
3719
|
+
async getReferences(filePath, line, character, includeDeclaration = false) {
|
|
3720
|
+
const normalized = normalizePath(filePath);
|
|
3721
|
+
if (!fileVersions.has(normalized)) {
|
|
3722
|
+
await client.notifyOpen(normalized);
|
|
3723
|
+
}
|
|
3724
|
+
try {
|
|
3725
|
+
const result = await connection.sendRequest("textDocument/references", {
|
|
3726
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
3727
|
+
position: { line, character },
|
|
3728
|
+
context: { includeDeclaration }
|
|
3729
|
+
});
|
|
3730
|
+
return result || [];
|
|
3731
|
+
} catch (error) {
|
|
3732
|
+
console.error("[lsp] Error getting references:", error);
|
|
3733
|
+
return [];
|
|
3734
|
+
}
|
|
3735
|
+
},
|
|
3736
|
+
async getHover(filePath, line, character) {
|
|
3737
|
+
const normalized = normalizePath(filePath);
|
|
3738
|
+
if (!fileVersions.has(normalized)) {
|
|
3739
|
+
await client.notifyOpen(normalized);
|
|
3740
|
+
}
|
|
3741
|
+
try {
|
|
3742
|
+
const result = await connection.sendRequest("textDocument/hover", {
|
|
3743
|
+
textDocument: { uri: pathToFileURL(normalized).href },
|
|
3744
|
+
position: { line, character }
|
|
3745
|
+
});
|
|
3746
|
+
if (!result || !result.contents) return null;
|
|
3747
|
+
if (typeof result.contents === "string") return result.contents;
|
|
3748
|
+
if (result.contents.value) return result.contents.value;
|
|
3749
|
+
if (Array.isArray(result.contents)) {
|
|
3750
|
+
return result.contents.map((c) => typeof c === "string" ? c : c.value).join("\n");
|
|
3751
|
+
}
|
|
3752
|
+
return null;
|
|
3753
|
+
} catch (error) {
|
|
3754
|
+
console.error("[lsp] Error getting hover:", error);
|
|
3755
|
+
return null;
|
|
3756
|
+
}
|
|
3757
|
+
},
|
|
3758
|
+
async getDocumentSymbols(filePath) {
|
|
3759
|
+
const normalized = normalizePath(filePath);
|
|
3760
|
+
if (!fileVersions.has(normalized)) {
|
|
3761
|
+
await client.notifyOpen(normalized);
|
|
3762
|
+
}
|
|
3763
|
+
try {
|
|
3764
|
+
const result = await connection.sendRequest("textDocument/documentSymbol", {
|
|
3765
|
+
textDocument: { uri: pathToFileURL(normalized).href }
|
|
3766
|
+
});
|
|
3767
|
+
if (!result || result.length === 0) return [];
|
|
3768
|
+
if (result[0].range) {
|
|
3769
|
+
return result;
|
|
3770
|
+
}
|
|
3771
|
+
return result.map((si) => ({
|
|
3772
|
+
name: si.name,
|
|
3773
|
+
kind: si.kind,
|
|
3774
|
+
range: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
3775
|
+
selectionRange: si.location?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
3776
|
+
detail: si.containerName
|
|
3777
|
+
}));
|
|
3778
|
+
} catch (error) {
|
|
3779
|
+
console.error("[lsp] Error getting document symbols:", error);
|
|
3780
|
+
return [];
|
|
3781
|
+
}
|
|
3782
|
+
},
|
|
3783
|
+
async findWorkspaceSymbols(query) {
|
|
3784
|
+
try {
|
|
3785
|
+
const result = await connection.sendRequest("workspace/symbol", { query });
|
|
3786
|
+
return result || [];
|
|
3787
|
+
} catch (error) {
|
|
3788
|
+
console.error("[lsp] Error finding workspace symbols:", error);
|
|
3789
|
+
return [];
|
|
3790
|
+
}
|
|
3791
|
+
},
|
|
3694
3792
|
async shutdown() {
|
|
3695
3793
|
try {
|
|
3696
3794
|
await connection.sendRequest("shutdown");
|
|
@@ -3813,6 +3911,24 @@ async function getAllDiagnostics() {
|
|
|
3813
3911
|
}
|
|
3814
3912
|
return results;
|
|
3815
3913
|
}
|
|
3914
|
+
async function getReferences(filePath, line, character, includeDeclaration = false) {
|
|
3915
|
+
const normalized = normalizePath(filePath);
|
|
3916
|
+
const client = await getClientForFile(normalized);
|
|
3917
|
+
if (!client) return [];
|
|
3918
|
+
return client.getReferences(normalized, line, character, includeDeclaration);
|
|
3919
|
+
}
|
|
3920
|
+
async function getHover(filePath, line, character) {
|
|
3921
|
+
const normalized = normalizePath(filePath);
|
|
3922
|
+
const client = await getClientForFile(normalized);
|
|
3923
|
+
if (!client) return null;
|
|
3924
|
+
return client.getHover(normalized, line, character);
|
|
3925
|
+
}
|
|
3926
|
+
async function getDocumentSymbols(filePath) {
|
|
3927
|
+
const normalized = normalizePath(filePath);
|
|
3928
|
+
const client = await getClientForFile(normalized);
|
|
3929
|
+
if (!client) return [];
|
|
3930
|
+
return client.getDocumentSymbols(normalized);
|
|
3931
|
+
}
|
|
3816
3932
|
async function formatDiagnosticsOutput(filePath, options = {}) {
|
|
3817
3933
|
const diagnostics = await getDiagnostics(filePath);
|
|
3818
3934
|
return formatDiagnosticsForAgent(filePath, diagnostics, options);
|
|
@@ -3908,7 +4024,7 @@ Working directory: ${options.workingDirectory}`,
|
|
|
3908
4024
|
isChunked: true
|
|
3909
4025
|
});
|
|
3910
4026
|
if (chunkCount > 1) {
|
|
3911
|
-
await new Promise((
|
|
4027
|
+
await new Promise((resolve12) => setTimeout(resolve12, 0));
|
|
3912
4028
|
}
|
|
3913
4029
|
}
|
|
3914
4030
|
}
|
|
@@ -4436,8 +4552,8 @@ ${file.relativePath}:`);
|
|
|
4436
4552
|
}
|
|
4437
4553
|
|
|
4438
4554
|
// src/tools/search.ts
|
|
4439
|
-
import { tool as
|
|
4440
|
-
import { z as
|
|
4555
|
+
import { tool as tool10 } from "ai";
|
|
4556
|
+
import { z as z11 } from "zod";
|
|
4441
4557
|
|
|
4442
4558
|
// src/agent/subagent.ts
|
|
4443
4559
|
import {
|
|
@@ -4609,8 +4725,8 @@ var Subagent = class {
|
|
|
4609
4725
|
if (eventQueue.length > 0) {
|
|
4610
4726
|
yield eventQueue.shift();
|
|
4611
4727
|
} else if (!done) {
|
|
4612
|
-
const event = await new Promise((
|
|
4613
|
-
resolveNext =
|
|
4728
|
+
const event = await new Promise((resolve12) => {
|
|
4729
|
+
resolveNext = resolve12;
|
|
4614
4730
|
});
|
|
4615
4731
|
if (event) {
|
|
4616
4732
|
yield event;
|
|
@@ -4622,14 +4738,466 @@ var Subagent = class {
|
|
|
4622
4738
|
};
|
|
4623
4739
|
|
|
4624
4740
|
// src/agent/subagents/search.ts
|
|
4625
|
-
import { tool as
|
|
4626
|
-
import { z as
|
|
4741
|
+
import { tool as tool9 } from "ai";
|
|
4742
|
+
import { z as z10 } from "zod";
|
|
4627
4743
|
import { exec as exec4 } from "child_process";
|
|
4628
4744
|
import { promisify as promisify4 } from "util";
|
|
4629
|
-
import { readFile as
|
|
4630
|
-
import { resolve as
|
|
4631
|
-
import { existsSync as
|
|
4745
|
+
import { readFile as readFile8, stat as stat3, readdir as readdir4 } from "fs/promises";
|
|
4746
|
+
import { resolve as resolve9, relative as relative8, isAbsolute as isAbsolute5 } from "path";
|
|
4747
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4632
4748
|
init_semantic();
|
|
4749
|
+
|
|
4750
|
+
// src/tools/code-graph.ts
|
|
4751
|
+
import { tool as tool7 } from "ai";
|
|
4752
|
+
import { z as z8 } from "zod";
|
|
4753
|
+
import { resolve as resolve8, relative as relative7, isAbsolute as isAbsolute4, basename as basename3 } from "path";
|
|
4754
|
+
import { readFile as readFile7, readdir as readdir3 } from "fs/promises";
|
|
4755
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4756
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4757
|
+
import { execFileSync } from "child_process";
|
|
4758
|
+
var codeGraphInputSchema = z8.object({
|
|
4759
|
+
symbol: z8.string().describe(
|
|
4760
|
+
"The symbol name to inspect (function, component, class, type, variable, etc.)"
|
|
4761
|
+
),
|
|
4762
|
+
filePath: z8.string().optional().describe(
|
|
4763
|
+
"File path where the symbol is defined. If omitted, searches the workspace via grep."
|
|
4764
|
+
),
|
|
4765
|
+
depth: z8.number().optional().default(2).describe(
|
|
4766
|
+
"How many levels of references to traverse upward (default: 2, max: 3). Level 1 = direct usages, level 2 = usages of those usages."
|
|
4767
|
+
)
|
|
4768
|
+
});
|
|
4769
|
+
function isPageFile(filePath) {
|
|
4770
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
4771
|
+
if (/\/app\/(.+\/)?(page|layout|loading|error|not-found)\.(tsx?|jsx?)$/.test(normalized)) return true;
|
|
4772
|
+
if (/\/pages\/(?!_|api\/).+\.(tsx?|jsx?)$/.test(normalized)) return true;
|
|
4773
|
+
return false;
|
|
4774
|
+
}
|
|
4775
|
+
function extractRoutePath(filePath, workingDirectory) {
|
|
4776
|
+
const rel = relative7(workingDirectory, filePath).replace(/\\/g, "/");
|
|
4777
|
+
const appMatch = rel.match(/(?:src\/)?app((?:\/[^/]+)*?)\/(?:page|layout|loading|error|not-found)\.\w+$/);
|
|
4778
|
+
if (appMatch) return appMatch[1] || "/";
|
|
4779
|
+
const pagesMatch = rel.match(/(?:src\/)?pages(\/.*?)(?:\/index)?\.\w+$/);
|
|
4780
|
+
if (pagesMatch) return pagesMatch[1] || "/";
|
|
4781
|
+
return void 0;
|
|
4782
|
+
}
|
|
4783
|
+
function symbolKindName(kind) {
|
|
4784
|
+
const names = {
|
|
4785
|
+
[5 /* Class */]: "class",
|
|
4786
|
+
[12 /* Function */]: "function",
|
|
4787
|
+
[6 /* Method */]: "method",
|
|
4788
|
+
[7 /* Property */]: "property",
|
|
4789
|
+
[13 /* Variable */]: "variable",
|
|
4790
|
+
[11 /* Interface */]: "interface",
|
|
4791
|
+
[10 /* Enum */]: "enum",
|
|
4792
|
+
[14 /* Constant */]: "constant",
|
|
4793
|
+
[9 /* Constructor */]: "constructor",
|
|
4794
|
+
[2 /* Module */]: "module",
|
|
4795
|
+
[3 /* Namespace */]: "namespace",
|
|
4796
|
+
[26 /* TypeParameter */]: "type_param",
|
|
4797
|
+
[8 /* Field */]: "field",
|
|
4798
|
+
[22 /* EnumMember */]: "enum_member",
|
|
4799
|
+
[19 /* Object */]: "object"
|
|
4800
|
+
};
|
|
4801
|
+
return names[kind] || "symbol";
|
|
4802
|
+
}
|
|
4803
|
+
function findContainingSymbol(symbols, line, character) {
|
|
4804
|
+
for (const sym of symbols) {
|
|
4805
|
+
if (!sym.range) continue;
|
|
4806
|
+
const { start, end } = sym.range;
|
|
4807
|
+
const afterStart = line > start.line || line === start.line && character >= start.character;
|
|
4808
|
+
const beforeEnd = line < end.line || line === end.line && character < end.character;
|
|
4809
|
+
if (afterStart && beforeEnd) {
|
|
4810
|
+
if (sym.children?.length) {
|
|
4811
|
+
const child = findContainingSymbol(sym.children, line, character);
|
|
4812
|
+
if (child) return child;
|
|
4813
|
+
}
|
|
4814
|
+
return sym;
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
return null;
|
|
4818
|
+
}
|
|
4819
|
+
function findSymbolByName(symbols, name) {
|
|
4820
|
+
for (const sym of symbols) {
|
|
4821
|
+
if (sym.name === name && sym.selectionRange) return sym;
|
|
4822
|
+
if (sym.children) {
|
|
4823
|
+
const found = findSymbolByName(sym.children, name);
|
|
4824
|
+
if (found) return found;
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
return null;
|
|
4828
|
+
}
|
|
4829
|
+
function cleanHoverText(text) {
|
|
4830
|
+
return text.replace(/```\w*\n?/g, "").replace(/\n```/g, "").trim();
|
|
4831
|
+
}
|
|
4832
|
+
async function grepForSymbol(symbol, workingDirectory) {
|
|
4833
|
+
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4834
|
+
const rgPatterns = [
|
|
4835
|
+
`(export\\s+)?(default\\s+)?(function|const|let|var|class|interface|type|enum)\\s+${escaped}\\b`,
|
|
4836
|
+
`(export\\s+)?(default\\s+)?\\b${escaped}\\s*[=:(]`
|
|
4837
|
+
];
|
|
4838
|
+
for (const pattern of rgPatterns) {
|
|
4839
|
+
try {
|
|
4840
|
+
const result = execFileSync("rg", [
|
|
4841
|
+
"-n",
|
|
4842
|
+
"--no-heading",
|
|
4843
|
+
"-e",
|
|
4844
|
+
pattern,
|
|
4845
|
+
"--glob",
|
|
4846
|
+
"*.{ts,tsx,js,jsx}",
|
|
4847
|
+
"-m",
|
|
4848
|
+
"5"
|
|
4849
|
+
], {
|
|
4850
|
+
cwd: workingDirectory,
|
|
4851
|
+
encoding: "utf-8",
|
|
4852
|
+
timeout: 5e3,
|
|
4853
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4854
|
+
}).trim();
|
|
4855
|
+
if (result) {
|
|
4856
|
+
const firstLine = result.split("\n")[0];
|
|
4857
|
+
const match = firstLine.match(/^(.+?):(\d+):(.*)/);
|
|
4858
|
+
if (match) {
|
|
4859
|
+
const col = match[3].indexOf(symbol);
|
|
4860
|
+
return {
|
|
4861
|
+
filePath: resolve8(workingDirectory, match[1]),
|
|
4862
|
+
line: parseInt(match[2]) - 1,
|
|
4863
|
+
char: col >= 0 ? col : 0
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
}
|
|
4867
|
+
} catch {
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
const defPattern = new RegExp(
|
|
4871
|
+
`(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${escaped}\\b`
|
|
4872
|
+
);
|
|
4873
|
+
const SUPPORTED_EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
4874
|
+
const IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage"]);
|
|
4875
|
+
async function search(dir, maxFiles) {
|
|
4876
|
+
if (maxFiles <= 0) return null;
|
|
4877
|
+
let remaining = maxFiles;
|
|
4878
|
+
try {
|
|
4879
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
4880
|
+
for (const entry of entries) {
|
|
4881
|
+
if (remaining <= 0) return null;
|
|
4882
|
+
const fullPath = resolve8(dir, entry.name);
|
|
4883
|
+
if (entry.isDirectory()) {
|
|
4884
|
+
if (IGNORED_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
4885
|
+
const found = await search(fullPath, remaining);
|
|
4886
|
+
if (found) return found;
|
|
4887
|
+
remaining -= 10;
|
|
4888
|
+
} else if (entry.isFile()) {
|
|
4889
|
+
const ext = entry.name.substring(entry.name.lastIndexOf("."));
|
|
4890
|
+
if (!SUPPORTED_EXTS.has(ext)) continue;
|
|
4891
|
+
remaining--;
|
|
4892
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
4893
|
+
const lines = content.split("\n");
|
|
4894
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4895
|
+
if (defPattern.test(lines[i])) {
|
|
4896
|
+
const col = lines[i].indexOf(symbol);
|
|
4897
|
+
if (col >= 0) {
|
|
4898
|
+
return { filePath: fullPath, line: i, char: col };
|
|
4899
|
+
}
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
} catch {
|
|
4905
|
+
}
|
|
4906
|
+
return null;
|
|
4907
|
+
}
|
|
4908
|
+
return search(workingDirectory, 200);
|
|
4909
|
+
}
|
|
4910
|
+
var MAX_REF_FILES = 15;
|
|
4911
|
+
var MAX_LEVEL2_PARENTS = 8;
|
|
4912
|
+
var MAX_LEVEL2_SYMBOLS_PER_PARENT = 3;
|
|
4913
|
+
function createCodeGraphTool(options) {
|
|
4914
|
+
return tool7({
|
|
4915
|
+
description: `Inspect a symbol's type information and usage graph using the TypeScript language server.
|
|
4916
|
+
|
|
4917
|
+
Given a symbol name (function, component, class, type, etc.), this tool will:
|
|
4918
|
+
1. Find its definition and full type signature (parameters, return type)
|
|
4919
|
+
2. Find all references \u2014 what components/functions/files use this symbol
|
|
4920
|
+
3. Identify which pages/routes contain it in their component tree
|
|
4921
|
+
4. Show the file's symbol structure for surrounding context
|
|
4922
|
+
|
|
4923
|
+
Use this to understand:
|
|
4924
|
+
- Component hierarchies (what renders what, which pages are affected)
|
|
4925
|
+
- Type signatures and parameter/return types before making changes
|
|
4926
|
+
- How deeply a symbol is used across the codebase
|
|
4927
|
+
- What will break if you change something
|
|
4928
|
+
|
|
4929
|
+
Supports TypeScript, JavaScript, TSX, JSX files.
|
|
4930
|
+
Working directory: ${options.workingDirectory}`,
|
|
4931
|
+
inputSchema: codeGraphInputSchema,
|
|
4932
|
+
execute: async ({ symbol, filePath, depth }) => {
|
|
4933
|
+
const maxDepth = Math.min(depth ?? 2, 3);
|
|
4934
|
+
try {
|
|
4935
|
+
let defFilePath;
|
|
4936
|
+
let defLine = 0;
|
|
4937
|
+
let defChar = 0;
|
|
4938
|
+
let defSymbol = null;
|
|
4939
|
+
if (filePath) {
|
|
4940
|
+
const absPath = isAbsolute4(filePath) ? filePath : resolve8(options.workingDirectory, filePath);
|
|
4941
|
+
if (!existsSync10(absPath)) {
|
|
4942
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
4943
|
+
}
|
|
4944
|
+
if (!isSupported(absPath)) {
|
|
4945
|
+
return { success: false, error: `File type not supported. Supports: ${getSupportedExtensions().join(", ")}` };
|
|
4946
|
+
}
|
|
4947
|
+
await touchFile(absPath, true);
|
|
4948
|
+
const symbols = await getDocumentSymbols(absPath);
|
|
4949
|
+
defSymbol = findSymbolByName(symbols, symbol);
|
|
4950
|
+
if (defSymbol) {
|
|
4951
|
+
defFilePath = absPath;
|
|
4952
|
+
defLine = defSymbol.selectionRange.start.line;
|
|
4953
|
+
defChar = defSymbol.selectionRange.start.character;
|
|
4954
|
+
} else {
|
|
4955
|
+
const content = await readFile7(absPath, "utf-8");
|
|
4956
|
+
const lines2 = content.split("\n");
|
|
4957
|
+
const defPattern = new RegExp(
|
|
4958
|
+
`(export|function|const|let|var|class|interface|type|enum)\\s+.*\\b${symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`
|
|
4959
|
+
);
|
|
4960
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
4961
|
+
if (defPattern.test(lines2[i])) {
|
|
4962
|
+
const col = lines2[i].indexOf(symbol);
|
|
4963
|
+
if (col !== -1) {
|
|
4964
|
+
defFilePath = absPath;
|
|
4965
|
+
defLine = i;
|
|
4966
|
+
defChar = col;
|
|
4967
|
+
break;
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
if (!defFilePath) {
|
|
4972
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
4973
|
+
const col = lines2[i].indexOf(symbol);
|
|
4974
|
+
if (col !== -1) {
|
|
4975
|
+
defFilePath = absPath;
|
|
4976
|
+
defLine = i;
|
|
4977
|
+
defChar = col;
|
|
4978
|
+
break;
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
} else {
|
|
4984
|
+
const found = await grepForSymbol(symbol, options.workingDirectory);
|
|
4985
|
+
if (found) {
|
|
4986
|
+
defFilePath = found.filePath;
|
|
4987
|
+
defLine = found.line;
|
|
4988
|
+
defChar = found.char;
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
if (!defFilePath) {
|
|
4992
|
+
return {
|
|
4993
|
+
success: false,
|
|
4994
|
+
error: `Could not find symbol "${symbol}" in the codebase. Try providing a filePath.`
|
|
4995
|
+
};
|
|
4996
|
+
}
|
|
4997
|
+
await touchFile(defFilePath, true);
|
|
4998
|
+
const rawHover = await getHover(defFilePath, defLine, defChar);
|
|
4999
|
+
const typeInfo = rawHover ? cleanHoverText(rawHover) : null;
|
|
5000
|
+
const fileSymbols = await getDocumentSymbols(defFilePath);
|
|
5001
|
+
if (!defSymbol && fileSymbols.length > 0) {
|
|
5002
|
+
defSymbol = findSymbolByName(fileSymbols, symbol);
|
|
5003
|
+
}
|
|
5004
|
+
const references = await getReferences(defFilePath, defLine, defChar, false);
|
|
5005
|
+
const refsByFile = /* @__PURE__ */ new Map();
|
|
5006
|
+
for (const ref of references) {
|
|
5007
|
+
const refPath = fileURLToPath2(ref.uri);
|
|
5008
|
+
if (!refsByFile.has(refPath)) {
|
|
5009
|
+
refsByFile.set(refPath, []);
|
|
5010
|
+
}
|
|
5011
|
+
refsByFile.get(refPath).push(ref);
|
|
5012
|
+
}
|
|
5013
|
+
const refFileInfos = [];
|
|
5014
|
+
let processed = 0;
|
|
5015
|
+
for (const [refPath, locs] of refsByFile) {
|
|
5016
|
+
if (processed >= MAX_REF_FILES) break;
|
|
5017
|
+
if (refPath === defFilePath) continue;
|
|
5018
|
+
processed++;
|
|
5019
|
+
const relPath = relative7(options.workingDirectory, refPath);
|
|
5020
|
+
const pageFile = isPageFile(refPath);
|
|
5021
|
+
const routePath = pageFile ? extractRoutePath(refPath, options.workingDirectory) : void 0;
|
|
5022
|
+
await touchFile(refPath, false);
|
|
5023
|
+
const refFileSymbols = await getDocumentSymbols(refPath);
|
|
5024
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5025
|
+
for (const loc of locs) {
|
|
5026
|
+
const container = findContainingSymbol(
|
|
5027
|
+
refFileSymbols,
|
|
5028
|
+
loc.range.start.line,
|
|
5029
|
+
loc.range.start.character
|
|
5030
|
+
);
|
|
5031
|
+
if (container && !seen.has(container.name)) {
|
|
5032
|
+
let containerHover = null;
|
|
5033
|
+
try {
|
|
5034
|
+
const raw = await getHover(
|
|
5035
|
+
refPath,
|
|
5036
|
+
container.selectionRange.start.line,
|
|
5037
|
+
container.selectionRange.start.character
|
|
5038
|
+
);
|
|
5039
|
+
if (raw) containerHover = cleanHoverText(raw).split("\n")[0];
|
|
5040
|
+
} catch {
|
|
5041
|
+
}
|
|
5042
|
+
seen.set(container.name, {
|
|
5043
|
+
name: container.name,
|
|
5044
|
+
kind: symbolKindName(container.kind),
|
|
5045
|
+
line: container.selectionRange.start.line + 1,
|
|
5046
|
+
char: container.selectionRange.start.character,
|
|
5047
|
+
typeInfo: containerHover || void 0
|
|
5048
|
+
});
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
refFileInfos.push({
|
|
5052
|
+
filePath: refPath,
|
|
5053
|
+
relativePath: relPath,
|
|
5054
|
+
isPage: pageFile,
|
|
5055
|
+
routePath,
|
|
5056
|
+
containingSymbols: Array.from(seen.values())
|
|
5057
|
+
});
|
|
5058
|
+
}
|
|
5059
|
+
const level2Refs = [];
|
|
5060
|
+
if (maxDepth >= 2) {
|
|
5061
|
+
for (const refFile of refFileInfos.slice(0, MAX_LEVEL2_PARENTS)) {
|
|
5062
|
+
for (const sym of refFile.containingSymbols.slice(0, MAX_LEVEL2_SYMBOLS_PER_PARENT)) {
|
|
5063
|
+
try {
|
|
5064
|
+
const symLineIdx = sym.line - 1;
|
|
5065
|
+
const symChar = sym.char;
|
|
5066
|
+
const l2Locations = await getReferences(
|
|
5067
|
+
refFile.filePath,
|
|
5068
|
+
symLineIdx,
|
|
5069
|
+
symChar,
|
|
5070
|
+
false
|
|
5071
|
+
);
|
|
5072
|
+
const l2Nodes = [];
|
|
5073
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
5074
|
+
for (const loc of l2Locations.slice(0, 10)) {
|
|
5075
|
+
const l2Path = fileURLToPath2(loc.uri);
|
|
5076
|
+
if (l2Path === refFile.filePath || l2Path === defFilePath) continue;
|
|
5077
|
+
if (seenPaths.has(l2Path)) continue;
|
|
5078
|
+
seenPaths.add(l2Path);
|
|
5079
|
+
const l2Rel = relative7(options.workingDirectory, l2Path);
|
|
5080
|
+
const l2Page = isPageFile(l2Path);
|
|
5081
|
+
const l2Route = l2Page ? extractRoutePath(l2Path, options.workingDirectory) : void 0;
|
|
5082
|
+
let containerName;
|
|
5083
|
+
try {
|
|
5084
|
+
await touchFile(l2Path, false);
|
|
5085
|
+
const l2Symbols = await getDocumentSymbols(l2Path);
|
|
5086
|
+
const container = findContainingSymbol(l2Symbols, loc.range.start.line, loc.range.start.character);
|
|
5087
|
+
if (container) containerName = container.name;
|
|
5088
|
+
} catch {
|
|
5089
|
+
}
|
|
5090
|
+
l2Nodes.push({
|
|
5091
|
+
relativePath: l2Rel,
|
|
5092
|
+
isPage: l2Page,
|
|
5093
|
+
routePath: l2Route,
|
|
5094
|
+
containingSymbol: containerName
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
if (l2Nodes.length > 0) {
|
|
5098
|
+
level2Refs.push({
|
|
5099
|
+
parentSymbol: sym.name,
|
|
5100
|
+
parentFile: refFile.relativePath,
|
|
5101
|
+
refs: l2Nodes
|
|
5102
|
+
});
|
|
5103
|
+
}
|
|
5104
|
+
} catch {
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
}
|
|
5109
|
+
const relDefPath = relative7(options.workingDirectory, defFilePath);
|
|
5110
|
+
const lines = [];
|
|
5111
|
+
lines.push(`=== ${symbol} ===`);
|
|
5112
|
+
lines.push(`File: ${relDefPath}:${defLine + 1}`);
|
|
5113
|
+
if (defSymbol) lines.push(`Kind: ${symbolKindName(defSymbol.kind)}`);
|
|
5114
|
+
if (typeInfo) lines.push(`Type: ${typeInfo}`);
|
|
5115
|
+
const externalRefCount = references.filter((r) => fileURLToPath2(r.uri) !== defFilePath).length;
|
|
5116
|
+
const externalFileCount = refsByFile.size - (refsByFile.has(defFilePath) ? 1 : 0);
|
|
5117
|
+
if (refFileInfos.length > 0) {
|
|
5118
|
+
lines.push("");
|
|
5119
|
+
lines.push(`=== Referenced by (${externalRefCount} usages across ${externalFileCount} files) ===`);
|
|
5120
|
+
const pages = refFileInfos.filter((r) => r.isPage);
|
|
5121
|
+
const nonPages = refFileInfos.filter((r) => !r.isPage);
|
|
5122
|
+
if (pages.length > 0) {
|
|
5123
|
+
lines.push("");
|
|
5124
|
+
lines.push("Pages/Routes:");
|
|
5125
|
+
for (const page of pages) {
|
|
5126
|
+
lines.push(` ${page.relativePath}${page.routePath ? ` \u2192 ${page.routePath}` : ""}`);
|
|
5127
|
+
for (const s of page.containingSymbols) {
|
|
5128
|
+
lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${s.typeInfo ? ` \u2014 ${s.typeInfo}` : ""}`);
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
if (nonPages.length > 0) {
|
|
5133
|
+
lines.push("");
|
|
5134
|
+
lines.push("Components/Functions:");
|
|
5135
|
+
for (const ref of nonPages) {
|
|
5136
|
+
lines.push(` ${ref.relativePath}`);
|
|
5137
|
+
for (const s of ref.containingSymbols) {
|
|
5138
|
+
const typePart = s.typeInfo && s.typeInfo.length < 120 ? ` \u2014 ${s.typeInfo}` : "";
|
|
5139
|
+
lines.push(` \u2514\u2500\u2500 ${s.name} (${s.kind}:${s.line})${typePart}`);
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
} else {
|
|
5144
|
+
lines.push("");
|
|
5145
|
+
lines.push("No external references found (symbol may be unused or only used within the same file).");
|
|
5146
|
+
}
|
|
5147
|
+
if (level2Refs.length > 0) {
|
|
5148
|
+
lines.push("");
|
|
5149
|
+
lines.push("=== Extended tree (level 2) ===");
|
|
5150
|
+
for (const l2 of level2Refs) {
|
|
5151
|
+
lines.push("");
|
|
5152
|
+
lines.push(`${l2.parentSymbol} (${l2.parentFile}) is used by:`);
|
|
5153
|
+
for (const ref of l2.refs) {
|
|
5154
|
+
const tag = ref.isPage ? " [PAGE]" : "";
|
|
5155
|
+
const route = ref.routePath ? ` \u2192 ${ref.routePath}` : "";
|
|
5156
|
+
const container = ref.containingSymbol ? ` in ${ref.containingSymbol}` : "";
|
|
5157
|
+
lines.push(` \u2514\u2500\u2500 ${ref.relativePath}${tag}${route}${container}`);
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
if (fileSymbols.length > 0) {
|
|
5162
|
+
lines.push("");
|
|
5163
|
+
lines.push(`=== File structure (${basename3(defFilePath)}) ===`);
|
|
5164
|
+
for (const sym of fileSymbols) {
|
|
5165
|
+
const marker = sym.name === symbol ? " \u2190 target" : "";
|
|
5166
|
+
lines.push(` ${sym.name} (${symbolKindName(sym.kind)}:${sym.selectionRange.start.line + 1})${marker}`);
|
|
5167
|
+
if (sym.children) {
|
|
5168
|
+
for (const child of sym.children.slice(0, 10)) {
|
|
5169
|
+
lines.push(` \u2514\u2500\u2500 ${child.name} (${symbolKindName(child.kind)}:${child.selectionRange.start.line + 1})`);
|
|
5170
|
+
}
|
|
5171
|
+
if (sym.children.length > 10) {
|
|
5172
|
+
lines.push(` ... and ${sym.children.length - 10} more`);
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
const formattedResult = lines.join("\n");
|
|
5178
|
+
return {
|
|
5179
|
+
success: true,
|
|
5180
|
+
symbol,
|
|
5181
|
+
filePath: relDefPath,
|
|
5182
|
+
line: defLine + 1,
|
|
5183
|
+
kind: defSymbol ? symbolKindName(defSymbol.kind) : void 0,
|
|
5184
|
+
typeInfo: typeInfo || void 0,
|
|
5185
|
+
referenceCount: externalRefCount,
|
|
5186
|
+
referenceFiles: externalFileCount,
|
|
5187
|
+
pages: refFileInfos.filter((r) => r.isPage).map((r) => ({ path: r.relativePath, route: r.routePath })),
|
|
5188
|
+
formattedResult
|
|
5189
|
+
};
|
|
5190
|
+
} catch (error) {
|
|
5191
|
+
return {
|
|
5192
|
+
success: false,
|
|
5193
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5194
|
+
};
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
});
|
|
5198
|
+
}
|
|
5199
|
+
|
|
5200
|
+
// src/agent/subagents/search.ts
|
|
4633
5201
|
var execAsync4 = promisify4(exec4);
|
|
4634
5202
|
var MAX_OUTPUT_CHARS4 = 1e4;
|
|
4635
5203
|
var MAX_FILE_SIZE3 = 1 * 1024 * 1024;
|
|
@@ -4659,17 +5227,20 @@ ${contextBlock}
|
|
|
4659
5227
|
- **glob**: Find files matching a name pattern. Best for file discovery.
|
|
4660
5228
|
- **read_file**: Read contents of a specific file. Use to examine code found in searches.
|
|
4661
5229
|
- **list_dir**: List directory contents. Use to understand project structure.
|
|
5230
|
+
- **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.
|
|
4662
5231
|
|
|
4663
5232
|
## Search Strategy
|
|
4664
5233
|
|
|
4665
5234
|
1. **Start with semantic_search** if available - it finds code by meaning, which is the fastest way to explore
|
|
4666
5235
|
2. **Use grep** for exact symbol/string matches (function names, class names, imports)
|
|
4667
|
-
3. **Use
|
|
4668
|
-
4. **
|
|
4669
|
-
5. **
|
|
5236
|
+
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.
|
|
5237
|
+
4. **Use glob** for file discovery by name patterns
|
|
5238
|
+
5. **Read key files** to get actual code content and understand context
|
|
5239
|
+
6. **Run searches in PARALLEL** - make multiple tool calls at once to cover different angles simultaneously. This is critical for speed.
|
|
4670
5240
|
|
|
4671
5241
|
### Tool Selection Guide
|
|
4672
5242
|
- Know the exact name? Use **grep** (e.g. \`getUserById\`, \`class AuthService\`)
|
|
5243
|
+
- Need type info, references, or impact analysis? Use **code_graph** (e.g. \`code_graph({ symbol: "UserCard" })\`)
|
|
4673
5244
|
- Exploring a concept? Use **semantic_search** (e.g. "how does authentication work")
|
|
4674
5245
|
- Looking for files? Use **glob** (e.g. \`**/*.config.ts\`, \`**/auth/**\`)
|
|
4675
5246
|
- Need file content? Use **read_file** with optional line ranges for large files
|
|
@@ -4706,17 +5277,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4706
5277
|
async getToolsAsync(options) {
|
|
4707
5278
|
const workingDirectory = options.workingDirectory;
|
|
4708
5279
|
const tools = {
|
|
4709
|
-
grep:
|
|
5280
|
+
grep: tool9({
|
|
4710
5281
|
description: "Search for patterns in files using ripgrep. Returns matching lines with file paths and line numbers.",
|
|
4711
|
-
inputSchema:
|
|
4712
|
-
pattern:
|
|
4713
|
-
path:
|
|
4714
|
-
fileType:
|
|
4715
|
-
maxResults:
|
|
5282
|
+
inputSchema: z10.object({
|
|
5283
|
+
pattern: z10.string().describe("The regex pattern to search for"),
|
|
5284
|
+
path: z10.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
|
|
5285
|
+
fileType: z10.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
|
|
5286
|
+
maxResults: z10.number().optional().default(50).describe("Maximum number of results to return")
|
|
4716
5287
|
}),
|
|
4717
5288
|
execute: async ({ pattern, path, fileType, maxResults }) => {
|
|
4718
5289
|
try {
|
|
4719
|
-
const searchPath = path ?
|
|
5290
|
+
const searchPath = path ? resolve9(workingDirectory, path) : workingDirectory;
|
|
4720
5291
|
let args = ["rg", "--line-number", "--no-heading"];
|
|
4721
5292
|
if (fileType) {
|
|
4722
5293
|
args.push("--type", fileType);
|
|
@@ -4753,11 +5324,11 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4753
5324
|
}
|
|
4754
5325
|
}
|
|
4755
5326
|
}),
|
|
4756
|
-
glob:
|
|
5327
|
+
glob: tool9({
|
|
4757
5328
|
description: "Find files matching a glob pattern. Returns list of matching file paths.",
|
|
4758
|
-
inputSchema:
|
|
4759
|
-
pattern:
|
|
4760
|
-
maxResults:
|
|
5329
|
+
inputSchema: z10.object({
|
|
5330
|
+
pattern: z10.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
|
|
5331
|
+
maxResults: z10.number().optional().default(100).describe("Maximum number of files to return")
|
|
4761
5332
|
}),
|
|
4762
5333
|
execute: async ({ pattern, maxResults }) => {
|
|
4763
5334
|
try {
|
|
@@ -4784,17 +5355,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4784
5355
|
}
|
|
4785
5356
|
}
|
|
4786
5357
|
}),
|
|
4787
|
-
read_file:
|
|
5358
|
+
read_file: tool9({
|
|
4788
5359
|
description: "Read the contents of a file. Use this to examine specific files found in search.",
|
|
4789
|
-
inputSchema:
|
|
4790
|
-
path:
|
|
4791
|
-
startLine:
|
|
4792
|
-
endLine:
|
|
5360
|
+
inputSchema: z10.object({
|
|
5361
|
+
path: z10.string().describe("Path to the file (relative to working directory or absolute)"),
|
|
5362
|
+
startLine: z10.number().optional().describe("Start reading from this line (1-indexed)"),
|
|
5363
|
+
endLine: z10.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
|
|
4793
5364
|
}),
|
|
4794
5365
|
execute: async ({ path, startLine, endLine }) => {
|
|
4795
5366
|
try {
|
|
4796
|
-
const absolutePath =
|
|
4797
|
-
if (!
|
|
5367
|
+
const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
|
|
5368
|
+
if (!existsSync12(absolutePath)) {
|
|
4798
5369
|
return {
|
|
4799
5370
|
success: false,
|
|
4800
5371
|
error: `File not found: ${path}`
|
|
@@ -4807,7 +5378,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4807
5378
|
error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Use startLine/endLine to read portions.`
|
|
4808
5379
|
};
|
|
4809
5380
|
}
|
|
4810
|
-
let content = await
|
|
5381
|
+
let content = await readFile8(absolutePath, "utf-8");
|
|
4811
5382
|
if (startLine !== void 0 || endLine !== void 0) {
|
|
4812
5383
|
const lines = content.split("\n");
|
|
4813
5384
|
const start = (startLine ?? 1) - 1;
|
|
@@ -4816,7 +5387,7 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4816
5387
|
}
|
|
4817
5388
|
return {
|
|
4818
5389
|
success: true,
|
|
4819
|
-
path:
|
|
5390
|
+
path: relative8(workingDirectory, absolutePath),
|
|
4820
5391
|
content: truncateOutput(content, MAX_OUTPUT_CHARS4),
|
|
4821
5392
|
lineCount: content.split("\n").length
|
|
4822
5393
|
};
|
|
@@ -4828,17 +5399,17 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4828
5399
|
}
|
|
4829
5400
|
}
|
|
4830
5401
|
}),
|
|
4831
|
-
list_dir:
|
|
5402
|
+
list_dir: tool9({
|
|
4832
5403
|
description: "List contents of a directory. Shows files and subdirectories.",
|
|
4833
|
-
inputSchema:
|
|
4834
|
-
path:
|
|
4835
|
-
recursive:
|
|
4836
|
-
maxDepth:
|
|
5404
|
+
inputSchema: z10.object({
|
|
5405
|
+
path: z10.string().optional().default(".").describe("Directory path (relative to working directory)"),
|
|
5406
|
+
recursive: z10.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
|
|
5407
|
+
maxDepth: z10.number().optional().default(2).describe("Maximum depth for recursive listing")
|
|
4837
5408
|
}),
|
|
4838
5409
|
execute: async ({ path, recursive, maxDepth }) => {
|
|
4839
5410
|
try {
|
|
4840
|
-
const absolutePath =
|
|
4841
|
-
if (!
|
|
5411
|
+
const absolutePath = isAbsolute5(path) ? path : resolve9(workingDirectory, path);
|
|
5412
|
+
if (!existsSync12(absolutePath)) {
|
|
4842
5413
|
return {
|
|
4843
5414
|
success: false,
|
|
4844
5415
|
error: `Directory not found: ${path}`
|
|
@@ -4862,20 +5433,20 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4862
5433
|
const files = stdout.trim().split("\n").filter(Boolean);
|
|
4863
5434
|
return {
|
|
4864
5435
|
success: true,
|
|
4865
|
-
path:
|
|
5436
|
+
path: relative8(workingDirectory, absolutePath) || ".",
|
|
4866
5437
|
files,
|
|
4867
5438
|
count: files.length,
|
|
4868
5439
|
recursive: true
|
|
4869
5440
|
};
|
|
4870
5441
|
} else {
|
|
4871
|
-
const entries = await
|
|
5442
|
+
const entries = await readdir4(absolutePath, { withFileTypes: true });
|
|
4872
5443
|
const items = entries.slice(0, 200).map((e) => ({
|
|
4873
5444
|
name: e.name,
|
|
4874
5445
|
type: e.isDirectory() ? "directory" : "file"
|
|
4875
5446
|
}));
|
|
4876
5447
|
return {
|
|
4877
5448
|
success: true,
|
|
4878
|
-
path:
|
|
5449
|
+
path: relative8(workingDirectory, absolutePath) || ".",
|
|
4879
5450
|
items,
|
|
4880
5451
|
count: items.length
|
|
4881
5452
|
};
|
|
@@ -4887,6 +5458,9 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4887
5458
|
};
|
|
4888
5459
|
}
|
|
4889
5460
|
}
|
|
5461
|
+
}),
|
|
5462
|
+
code_graph: createCodeGraphTool({
|
|
5463
|
+
workingDirectory
|
|
4890
5464
|
})
|
|
4891
5465
|
};
|
|
4892
5466
|
try {
|
|
@@ -4980,6 +5554,26 @@ Keep it concise but INCLUDE THE ACTUAL DATA.`;
|
|
|
4980
5554
|
context: m.symbolName || m.language
|
|
4981
5555
|
});
|
|
4982
5556
|
}
|
|
5557
|
+
} else if (step.toolName === "code_graph" && output.success) {
|
|
5558
|
+
matchCount += output.referenceCount || 0;
|
|
5559
|
+
if (output.filePath) {
|
|
5560
|
+
findings.push({
|
|
5561
|
+
type: "file",
|
|
5562
|
+
path: output.filePath,
|
|
5563
|
+
lineNumber: output.line,
|
|
5564
|
+
content: output.typeInfo ? truncateOutput(output.typeInfo, 300) : void 0,
|
|
5565
|
+
relevance: "high",
|
|
5566
|
+
context: `${output.kind || "symbol"}${output.referenceCount ? `, ${output.referenceCount} refs` : ""}`
|
|
5567
|
+
});
|
|
5568
|
+
}
|
|
5569
|
+
for (const page of (output.pages || []).slice(0, 10)) {
|
|
5570
|
+
findings.push({
|
|
5571
|
+
type: "file",
|
|
5572
|
+
path: page.path,
|
|
5573
|
+
relevance: "high",
|
|
5574
|
+
context: page.route ? `route: ${page.route}` : "page"
|
|
5575
|
+
});
|
|
5576
|
+
}
|
|
4983
5577
|
}
|
|
4984
5578
|
}
|
|
4985
5579
|
}
|
|
@@ -5001,7 +5595,7 @@ function createSearchSubagent(model) {
|
|
|
5001
5595
|
// src/tools/search.ts
|
|
5002
5596
|
var MAX_RESULT_CHARS = 1e4;
|
|
5003
5597
|
function createSearchTool(options) {
|
|
5004
|
-
return
|
|
5598
|
+
return tool10({
|
|
5005
5599
|
description: `Delegate an explore task to the explore_agent tool. Use this when you need to:
|
|
5006
5600
|
- Find files or code matching a pattern
|
|
5007
5601
|
- Explore the codebase structure
|
|
@@ -5011,11 +5605,12 @@ function createSearchTool(options) {
|
|
|
5011
5605
|
The Explore agent will explore the codebase and return a summary of findings.
|
|
5012
5606
|
This is more thorough than a simple grep because it can follow references and understand context.
|
|
5013
5607
|
It also has access to semantic search to find code by meaning, not just text.
|
|
5608
|
+
It can also use code_graph to inspect a symbol's type hierarchy, references, and which pages/routes use it.
|
|
5014
5609
|
|
|
5015
5610
|
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.`,
|
|
5016
|
-
inputSchema:
|
|
5017
|
-
query:
|
|
5018
|
-
context:
|
|
5611
|
+
inputSchema: z11.object({
|
|
5612
|
+
query: z11.string().describe("What to search for. Be specific about what you're looking for."),
|
|
5613
|
+
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.")
|
|
5019
5614
|
}),
|
|
5020
5615
|
execute: async ({ query, context }, toolOptions) => {
|
|
5021
5616
|
const toolCallId = toolOptions.toolCallId || `explore_agent_${Date.now()}`;
|
|
@@ -5159,6 +5754,9 @@ async function createTools(options) {
|
|
|
5159
5754
|
sessionId: options.sessionId,
|
|
5160
5755
|
workingDirectory: options.workingDirectory,
|
|
5161
5756
|
onProgress: options.onSearchProgress
|
|
5757
|
+
}),
|
|
5758
|
+
code_graph: createCodeGraphTool({
|
|
5759
|
+
workingDirectory: options.workingDirectory
|
|
5162
5760
|
})
|
|
5163
5761
|
};
|
|
5164
5762
|
if (options.enableSemanticSearch !== false) {
|
|
@@ -5250,6 +5848,7 @@ You have access to powerful tools for:
|
|
|
5250
5848
|
- **todo**: Manage your task list to track progress on complex operations
|
|
5251
5849
|
- **load_skill**: Load specialized knowledge documents for specific tasks
|
|
5252
5850
|
- **explore_agent**: Explore agent for semantic discovery - for exploratory questions and finding code by meaning
|
|
5851
|
+
- **code_graph**: Inspect a symbol's type hierarchy and usage graph via the TypeScript language server
|
|
5253
5852
|
|
|
5254
5853
|
|
|
5255
5854
|
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.
|
|
@@ -5339,6 +5938,33 @@ linter({ paths: ["src/"] }) // Check all files in a directory
|
|
|
5339
5938
|
\`\`\`
|
|
5340
5939
|
Use this proactively after making code changes to catch errors early.
|
|
5341
5940
|
|
|
5941
|
+
### Code Graph Tool
|
|
5942
|
+
The code_graph tool uses the TypeScript language server to inspect a symbol's type hierarchy and usage graph:
|
|
5943
|
+
\`\`\`
|
|
5944
|
+
code_graph({ symbol: "UserCard" }) // Search workspace for symbol
|
|
5945
|
+
code_graph({ symbol: "UserCard", filePath: "src/components.tsx" }) // Look up in a specific file
|
|
5946
|
+
code_graph({ symbol: "formatUser", filePath: "utils.ts", depth: 2 }) // Traverse 2 levels up the reference tree
|
|
5947
|
+
\`\`\`
|
|
5948
|
+
|
|
5949
|
+
**What it returns:**
|
|
5950
|
+
- The symbol's full type signature (parameters, return type)
|
|
5951
|
+
- All files/functions/components that reference it (grouped into pages vs components)
|
|
5952
|
+
- Which Next.js pages/routes contain it in their component tree
|
|
5953
|
+
- Level-2 transitive usages (who uses the things that use this symbol)
|
|
5954
|
+
- The file's symbol structure for surrounding context
|
|
5955
|
+
|
|
5956
|
+
**When to use code_graph:**
|
|
5957
|
+
- **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.
|
|
5958
|
+
- **Before making changes** to a function/component \u2014 understand what depends on it and what will break
|
|
5959
|
+
- **To understand component hierarchies** \u2014 what renders what, which pages are affected across the *entire* codebase (not just the current page)
|
|
5960
|
+
- **To get type signatures** (props, params, return types) without reading entire files
|
|
5961
|
+
- **After a devtools selection** when the task involves refactoring, changing props, or anything that could impact other consumers
|
|
5962
|
+
|
|
5963
|
+
**When NOT to use code_graph:**
|
|
5964
|
+
- For exploratory "how does X work?" questions \u2014 use \`explore_agent\` instead
|
|
5965
|
+
- For exact string searches \u2014 use grep/rg directly
|
|
5966
|
+
- For non-TypeScript/JavaScript files \u2014 code_graph only supports TS/JS/TSX/JSX
|
|
5967
|
+
|
|
5342
5968
|
### Searching and Exploration
|
|
5343
5969
|
|
|
5344
5970
|
**Choose the right search approach:**
|
|
@@ -5346,7 +5972,8 @@ Use this proactively after making code changes to catch errors early.
|
|
|
5346
5972
|
0. **Use paths to your advantage \u2014 skip searching if you already have what you need.**
|
|
5347
5973
|
- 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.
|
|
5348
5974
|
- 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.
|
|
5349
|
-
- If the file path doesn't exist
|
|
5975
|
+
- **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.
|
|
5976
|
+
- **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.
|
|
5350
5977
|
- Read up and down component trees when you have the file path or page path to find what you're looking for.
|
|
5351
5978
|
1. **Use the \`explore_agent\` tool (Explore agent)** for:
|
|
5352
5979
|
- Semantic/exploratory questions: "How does authentication work?", "Where is user data processed?"
|
|
@@ -5364,7 +5991,14 @@ Use this proactively after making code changes to catch errors early.
|
|
|
5364
5991
|
- If you skip the \`context\` field, the explore agent is searching completely blind and will waste time guessing.
|
|
5365
5992
|
- NEVER call \`explore_agent\` with only a \`query\` and no \`context\` when the user's message contains devtools or component information.
|
|
5366
5993
|
|
|
5367
|
-
2. **Use
|
|
5994
|
+
2. **Use the \`code_graph\` tool** for:
|
|
5995
|
+
- Understanding what depends on a specific symbol before changing it
|
|
5996
|
+
- Tracing component/function usage up to page-level routes
|
|
5997
|
+
- Getting type signatures (params, return types) without reading full files
|
|
5998
|
+
- Finding exact components usages in the codebase
|
|
5999
|
+
- Answering "what will break if I change this?" or "which pages use this component?"
|
|
6000
|
+
|
|
6001
|
+
3. **Use direct commands (grep/rg, find)** for:
|
|
5368
6002
|
- Exact string matches: \`rg "functionName"\`, \`rg "class MyClass"\`
|
|
5369
6003
|
- Finding files by name: \`find . -name "*.config.ts"\`
|
|
5370
6004
|
- Simple pattern matching when you know exactly what you're looking for
|
|
@@ -5372,7 +6006,11 @@ Use this proactively after making code changes to catch errors early.
|
|
|
5372
6006
|
|
|
5373
6007
|
**Examples:**
|
|
5374
6008
|
- User selected \`<LandingButton>\` at \`src/components/LandingButton.tsx:12\` \u2192 Just \`read_file("src/components/LandingButton.tsx")\`. Do NOT call explore_agent.
|
|
6009
|
+
- 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.
|
|
6010
|
+
- 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.
|
|
5375
6011
|
- "Where is the API authentication handled?" (no file path given) \u2192 Use \`explore_agent\` tool
|
|
6012
|
+
- "What pages use the UserCard component?" \u2192 Use \`code_graph({ symbol: "UserCard" })\`
|
|
6013
|
+
- "What's the type signature of formatUser?" \u2192 Use \`code_graph({ symbol: "formatUser", filePath: "utils.ts" })\`
|
|
5376
6014
|
- "Find all usages of getUserById" \u2192 Use \`rg "getUserById"\`
|
|
5377
6015
|
- "How does the payment flow work?" \u2192 Use \`explore_agent\` tool
|
|
5378
6016
|
- "Find files named config" \u2192 Use \`find . -name "*config*"\`
|
|
@@ -5944,9 +6582,9 @@ ${prompt}` });
|
|
|
5944
6582
|
wrappedTools[name] = originalTool;
|
|
5945
6583
|
continue;
|
|
5946
6584
|
}
|
|
5947
|
-
wrappedTools[name] =
|
|
6585
|
+
wrappedTools[name] = tool11({
|
|
5948
6586
|
description: originalTool.description || "",
|
|
5949
|
-
inputSchema: originalTool.inputSchema ||
|
|
6587
|
+
inputSchema: originalTool.inputSchema || z12.object({}),
|
|
5950
6588
|
execute: async (input, toolOptions) => {
|
|
5951
6589
|
const toolCallId = toolOptions.toolCallId || nanoid3();
|
|
5952
6590
|
const execution = toolExecutionQueries.create({
|
|
@@ -5960,8 +6598,8 @@ ${prompt}` });
|
|
|
5960
6598
|
this.pendingApprovals.set(toolCallId, await execution);
|
|
5961
6599
|
options.onApprovalRequired?.(await execution);
|
|
5962
6600
|
await sessionQueries.updateStatus(this.session.id, "waiting");
|
|
5963
|
-
const approved = await new Promise((
|
|
5964
|
-
approvalResolvers.set(toolCallId, { resolve:
|
|
6601
|
+
const approved = await new Promise((resolve12) => {
|
|
6602
|
+
approvalResolvers.set(toolCallId, { resolve: resolve12, sessionId: this.session.id });
|
|
5965
6603
|
});
|
|
5966
6604
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
5967
6605
|
approvalResolvers.delete(toolCallId);
|
|
@@ -6087,18 +6725,18 @@ function cleanupPendingInputs() {
|
|
|
6087
6725
|
}
|
|
6088
6726
|
}
|
|
6089
6727
|
}
|
|
6090
|
-
var createSessionSchema =
|
|
6091
|
-
name:
|
|
6092
|
-
workingDirectory:
|
|
6093
|
-
model:
|
|
6094
|
-
toolApprovals:
|
|
6728
|
+
var createSessionSchema = z13.object({
|
|
6729
|
+
name: z13.string().optional(),
|
|
6730
|
+
workingDirectory: z13.string().optional(),
|
|
6731
|
+
model: z13.string().optional(),
|
|
6732
|
+
toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
|
|
6095
6733
|
});
|
|
6096
|
-
var paginationQuerySchema =
|
|
6097
|
-
limit:
|
|
6098
|
-
offset:
|
|
6734
|
+
var paginationQuerySchema = z13.object({
|
|
6735
|
+
limit: z13.string().optional(),
|
|
6736
|
+
offset: z13.string().optional()
|
|
6099
6737
|
});
|
|
6100
|
-
var messagesQuerySchema =
|
|
6101
|
-
limit:
|
|
6738
|
+
var messagesQuerySchema = z13.object({
|
|
6739
|
+
limit: z13.string().optional()
|
|
6102
6740
|
});
|
|
6103
6741
|
sessions.get(
|
|
6104
6742
|
"/",
|
|
@@ -6237,10 +6875,10 @@ sessions.get("/:id/tools", async (c) => {
|
|
|
6237
6875
|
count: executions.length
|
|
6238
6876
|
});
|
|
6239
6877
|
});
|
|
6240
|
-
var updateSessionSchema =
|
|
6241
|
-
model:
|
|
6242
|
-
name:
|
|
6243
|
-
toolApprovals:
|
|
6878
|
+
var updateSessionSchema = z13.object({
|
|
6879
|
+
model: z13.string().optional(),
|
|
6880
|
+
name: z13.string().optional(),
|
|
6881
|
+
toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
|
|
6244
6882
|
});
|
|
6245
6883
|
sessions.patch(
|
|
6246
6884
|
"/:id",
|
|
@@ -6310,8 +6948,8 @@ sessions.post("/:id/clear", async (c) => {
|
|
|
6310
6948
|
await agent.clearContext();
|
|
6311
6949
|
return c.json({ success: true, sessionId: id });
|
|
6312
6950
|
});
|
|
6313
|
-
var pendingInputSchema =
|
|
6314
|
-
text:
|
|
6951
|
+
var pendingInputSchema = z13.object({
|
|
6952
|
+
text: z13.string()
|
|
6315
6953
|
});
|
|
6316
6954
|
sessions.post(
|
|
6317
6955
|
"/:id/pending-input",
|
|
@@ -6342,13 +6980,13 @@ sessions.get("/:id/pending-input", async (c) => {
|
|
|
6342
6980
|
createdAt: pending.createdAt.toISOString()
|
|
6343
6981
|
});
|
|
6344
6982
|
});
|
|
6345
|
-
var devtoolsContextSchema =
|
|
6346
|
-
url:
|
|
6347
|
-
path:
|
|
6348
|
-
pageName:
|
|
6349
|
-
screenWidth:
|
|
6350
|
-
screenHeight:
|
|
6351
|
-
devicePixelRatio:
|
|
6983
|
+
var devtoolsContextSchema = z13.object({
|
|
6984
|
+
url: z13.string(),
|
|
6985
|
+
path: z13.string(),
|
|
6986
|
+
pageName: z13.string().optional(),
|
|
6987
|
+
screenWidth: z13.number().optional(),
|
|
6988
|
+
screenHeight: z13.number().optional(),
|
|
6989
|
+
devicePixelRatio: z13.number().optional()
|
|
6352
6990
|
});
|
|
6353
6991
|
sessions.post(
|
|
6354
6992
|
"/:id/devtools-context",
|
|
@@ -6520,7 +7158,7 @@ function getAttachmentsDir(sessionId) {
|
|
|
6520
7158
|
}
|
|
6521
7159
|
function ensureAttachmentsDir(sessionId) {
|
|
6522
7160
|
const dir = getAttachmentsDir(sessionId);
|
|
6523
|
-
if (!
|
|
7161
|
+
if (!existsSync13(dir)) {
|
|
6524
7162
|
mkdirSync3(dir, { recursive: true });
|
|
6525
7163
|
}
|
|
6526
7164
|
return dir;
|
|
@@ -6532,7 +7170,7 @@ sessions.get("/:id/attachments", async (c) => {
|
|
|
6532
7170
|
return c.json({ error: "Session not found" }, 404);
|
|
6533
7171
|
}
|
|
6534
7172
|
const dir = getAttachmentsDir(sessionId);
|
|
6535
|
-
if (!
|
|
7173
|
+
if (!existsSync13(dir)) {
|
|
6536
7174
|
return c.json({ sessionId, attachments: [], count: 0 });
|
|
6537
7175
|
}
|
|
6538
7176
|
const files = readdirSync(dir);
|
|
@@ -6571,7 +7209,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
6571
7209
|
const dir = ensureAttachmentsDir(sessionId);
|
|
6572
7210
|
const id = nanoid4(10);
|
|
6573
7211
|
const ext = extname6(file.name) || "";
|
|
6574
|
-
const safeFilename = `${id}_${
|
|
7212
|
+
const safeFilename = `${id}_${basename4(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
6575
7213
|
const filePath = join5(dir, safeFilename);
|
|
6576
7214
|
const arrayBuffer = await file.arrayBuffer();
|
|
6577
7215
|
writeFileSync2(filePath, Buffer.from(arrayBuffer));
|
|
@@ -6597,7 +7235,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
6597
7235
|
const dir = ensureAttachmentsDir(sessionId);
|
|
6598
7236
|
const id = nanoid4(10);
|
|
6599
7237
|
const ext = extname6(body.filename) || "";
|
|
6600
|
-
const safeFilename = `${id}_${
|
|
7238
|
+
const safeFilename = `${id}_${basename4(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
6601
7239
|
const filePath = join5(dir, safeFilename);
|
|
6602
7240
|
let base64Data = body.data;
|
|
6603
7241
|
if (base64Data.includes(",")) {
|
|
@@ -6627,7 +7265,7 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
6627
7265
|
return c.json({ error: "Session not found" }, 404);
|
|
6628
7266
|
}
|
|
6629
7267
|
const dir = getAttachmentsDir(sessionId);
|
|
6630
|
-
if (!
|
|
7268
|
+
if (!existsSync13(dir)) {
|
|
6631
7269
|
return c.json({ error: "Attachment not found" }, 404);
|
|
6632
7270
|
}
|
|
6633
7271
|
const files = readdirSync(dir);
|
|
@@ -6639,10 +7277,10 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
6639
7277
|
unlinkSync(filePath);
|
|
6640
7278
|
return c.json({ success: true, id: attachmentId });
|
|
6641
7279
|
});
|
|
6642
|
-
var filesQuerySchema =
|
|
6643
|
-
query:
|
|
7280
|
+
var filesQuerySchema = z13.object({
|
|
7281
|
+
query: z13.string().optional(),
|
|
6644
7282
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
6645
|
-
limit:
|
|
7283
|
+
limit: z13.string().optional()
|
|
6646
7284
|
// Max results (default 50)
|
|
6647
7285
|
});
|
|
6648
7286
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -6715,11 +7353,11 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
6715
7353
|
return results;
|
|
6716
7354
|
}
|
|
6717
7355
|
try {
|
|
6718
|
-
const entries = await
|
|
7356
|
+
const entries = await readdir5(currentDir, { withFileTypes: true });
|
|
6719
7357
|
for (const entry of entries) {
|
|
6720
7358
|
if (results.length >= limit * 2) break;
|
|
6721
7359
|
const fullPath = join5(currentDir, entry.name);
|
|
6722
|
-
const relativePath =
|
|
7360
|
+
const relativePath = relative9(baseDir, fullPath);
|
|
6723
7361
|
if (entry.isDirectory() && IGNORED_DIRECTORIES.has(entry.name)) {
|
|
6724
7362
|
continue;
|
|
6725
7363
|
}
|
|
@@ -6766,7 +7404,7 @@ sessions.get(
|
|
|
6766
7404
|
return c.json({ error: "Session not found" }, 404);
|
|
6767
7405
|
}
|
|
6768
7406
|
const workingDirectory = session.workingDirectory;
|
|
6769
|
-
if (!
|
|
7407
|
+
if (!existsSync13(workingDirectory)) {
|
|
6770
7408
|
return c.json({
|
|
6771
7409
|
sessionId,
|
|
6772
7410
|
workingDirectory,
|
|
@@ -6820,8 +7458,8 @@ sessions.get(
|
|
|
6820
7458
|
init_db();
|
|
6821
7459
|
import { Hono as Hono2 } from "hono";
|
|
6822
7460
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
6823
|
-
import { z as
|
|
6824
|
-
import { existsSync as
|
|
7461
|
+
import { z as z14 } from "zod";
|
|
7462
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
6825
7463
|
import { join as join6 } from "path";
|
|
6826
7464
|
init_config();
|
|
6827
7465
|
|
|
@@ -6962,7 +7600,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
|
|
|
6962
7600
|
toolCallId,
|
|
6963
7601
|
argsTextDelta: chunk
|
|
6964
7602
|
}));
|
|
6965
|
-
await new Promise((
|
|
7603
|
+
await new Promise((resolve12) => setTimeout(resolve12, 0));
|
|
6966
7604
|
}
|
|
6967
7605
|
}
|
|
6968
7606
|
function buildDevtoolsContextXml(sessionId) {
|
|
@@ -6985,30 +7623,30 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
6985
7623
|
${prompt}`;
|
|
6986
7624
|
}
|
|
6987
7625
|
var agents = new Hono2();
|
|
6988
|
-
var attachmentSchema =
|
|
6989
|
-
type:
|
|
6990
|
-
data:
|
|
7626
|
+
var attachmentSchema = z14.object({
|
|
7627
|
+
type: z14.enum(["image", "file"]),
|
|
7628
|
+
data: z14.string(),
|
|
6991
7629
|
// base64 data URL or raw base64
|
|
6992
|
-
mediaType:
|
|
6993
|
-
filename:
|
|
7630
|
+
mediaType: z14.string().optional(),
|
|
7631
|
+
filename: z14.string().optional()
|
|
6994
7632
|
});
|
|
6995
|
-
var runPromptSchema =
|
|
6996
|
-
prompt:
|
|
7633
|
+
var runPromptSchema = z14.object({
|
|
7634
|
+
prompt: z14.string(),
|
|
6997
7635
|
// Can be empty if attachments are provided
|
|
6998
|
-
attachments:
|
|
7636
|
+
attachments: z14.array(attachmentSchema).optional()
|
|
6999
7637
|
}).refine(
|
|
7000
7638
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
7001
7639
|
{ message: "Either prompt or attachments must be provided" }
|
|
7002
7640
|
);
|
|
7003
|
-
var quickStartSchema =
|
|
7004
|
-
prompt:
|
|
7005
|
-
name:
|
|
7006
|
-
workingDirectory:
|
|
7007
|
-
model:
|
|
7008
|
-
toolApprovals:
|
|
7641
|
+
var quickStartSchema = z14.object({
|
|
7642
|
+
prompt: z14.string().min(1),
|
|
7643
|
+
name: z14.string().optional(),
|
|
7644
|
+
workingDirectory: z14.string().optional(),
|
|
7645
|
+
model: z14.string().optional(),
|
|
7646
|
+
toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
|
|
7009
7647
|
});
|
|
7010
|
-
var rejectSchema =
|
|
7011
|
-
reason:
|
|
7648
|
+
var rejectSchema = z14.object({
|
|
7649
|
+
reason: z14.string().optional()
|
|
7012
7650
|
}).optional();
|
|
7013
7651
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
7014
7652
|
function getAttachmentsDirectory(sessionId) {
|
|
@@ -7017,7 +7655,7 @@ function getAttachmentsDirectory(sessionId) {
|
|
|
7017
7655
|
}
|
|
7018
7656
|
function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
7019
7657
|
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
7020
|
-
if (!
|
|
7658
|
+
if (!existsSync14(attachmentsDir)) {
|
|
7021
7659
|
mkdirSync4(attachmentsDir, { recursive: true });
|
|
7022
7660
|
}
|
|
7023
7661
|
let filename = attachment.filename;
|
|
@@ -7195,7 +7833,7 @@ ${prompt}` });
|
|
|
7195
7833
|
chunkIndex,
|
|
7196
7834
|
chunkCount
|
|
7197
7835
|
}));
|
|
7198
|
-
await new Promise((
|
|
7836
|
+
await new Promise((resolve12) => setTimeout(resolve12, 0));
|
|
7199
7837
|
}
|
|
7200
7838
|
},
|
|
7201
7839
|
onStepFinish: async () => {
|
|
@@ -7659,7 +8297,7 @@ agents.post(
|
|
|
7659
8297
|
chunkIndex,
|
|
7660
8298
|
chunkCount
|
|
7661
8299
|
}));
|
|
7662
|
-
await new Promise((
|
|
8300
|
+
await new Promise((resolve12) => setTimeout(resolve12, 0));
|
|
7663
8301
|
}
|
|
7664
8302
|
},
|
|
7665
8303
|
onStepFinish: async () => {
|
|
@@ -7796,11 +8434,11 @@ agents.post(
|
|
|
7796
8434
|
init_config();
|
|
7797
8435
|
import { Hono as Hono3 } from "hono";
|
|
7798
8436
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
7799
|
-
import { z as
|
|
8437
|
+
import { z as z15 } from "zod";
|
|
7800
8438
|
import { readFileSync as readFileSync5 } from "fs";
|
|
7801
|
-
import { fileURLToPath as
|
|
8439
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7802
8440
|
import { dirname as dirname6, join as join7 } from "path";
|
|
7803
|
-
var __filename =
|
|
8441
|
+
var __filename = fileURLToPath3(import.meta.url);
|
|
7804
8442
|
var __dirname = dirname6(__filename);
|
|
7805
8443
|
var possiblePaths = [
|
|
7806
8444
|
join7(__dirname, "../package.json"),
|
|
@@ -7906,9 +8544,9 @@ health.get("/api-keys", async (c) => {
|
|
|
7906
8544
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
7907
8545
|
});
|
|
7908
8546
|
});
|
|
7909
|
-
var setApiKeySchema =
|
|
7910
|
-
provider:
|
|
7911
|
-
apiKey:
|
|
8547
|
+
var setApiKeySchema = z15.object({
|
|
8548
|
+
provider: z15.string(),
|
|
8549
|
+
apiKey: z15.string().min(1)
|
|
7912
8550
|
});
|
|
7913
8551
|
health.post(
|
|
7914
8552
|
"/api-keys",
|
|
@@ -7947,13 +8585,13 @@ health.delete("/api-keys/:provider", async (c) => {
|
|
|
7947
8585
|
// src/server/routes/terminals.ts
|
|
7948
8586
|
import { Hono as Hono4 } from "hono";
|
|
7949
8587
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
7950
|
-
import { z as
|
|
8588
|
+
import { z as z16 } from "zod";
|
|
7951
8589
|
init_db();
|
|
7952
8590
|
var terminals = new Hono4();
|
|
7953
|
-
var spawnSchema =
|
|
7954
|
-
command:
|
|
7955
|
-
cwd:
|
|
7956
|
-
name:
|
|
8591
|
+
var spawnSchema = z16.object({
|
|
8592
|
+
command: z16.string(),
|
|
8593
|
+
cwd: z16.string().optional(),
|
|
8594
|
+
name: z16.string().optional()
|
|
7957
8595
|
});
|
|
7958
8596
|
terminals.post(
|
|
7959
8597
|
"/:sessionId/terminals",
|
|
@@ -8034,8 +8672,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
8034
8672
|
// We don't track exit codes in tmux mode
|
|
8035
8673
|
});
|
|
8036
8674
|
});
|
|
8037
|
-
var logsQuerySchema =
|
|
8038
|
-
tail:
|
|
8675
|
+
var logsQuerySchema = z16.object({
|
|
8676
|
+
tail: z16.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
8039
8677
|
});
|
|
8040
8678
|
terminals.get(
|
|
8041
8679
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -8059,8 +8697,8 @@ terminals.get(
|
|
|
8059
8697
|
});
|
|
8060
8698
|
}
|
|
8061
8699
|
);
|
|
8062
|
-
var killSchema =
|
|
8063
|
-
signal:
|
|
8700
|
+
var killSchema = z16.object({
|
|
8701
|
+
signal: z16.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
8064
8702
|
});
|
|
8065
8703
|
terminals.post(
|
|
8066
8704
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -8074,8 +8712,8 @@ terminals.post(
|
|
|
8074
8712
|
return c.json({ success: true, message: "Terminal killed" });
|
|
8075
8713
|
}
|
|
8076
8714
|
);
|
|
8077
|
-
var writeSchema =
|
|
8078
|
-
input:
|
|
8715
|
+
var writeSchema = z16.object({
|
|
8716
|
+
input: z16.string()
|
|
8079
8717
|
});
|
|
8080
8718
|
terminals.post(
|
|
8081
8719
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -8399,13 +9037,13 @@ var DEFAULT_WEB_PORT = 6969;
|
|
|
8399
9037
|
var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
|
|
8400
9038
|
function getWebDirectory() {
|
|
8401
9039
|
try {
|
|
8402
|
-
const currentDir = dirname7(
|
|
8403
|
-
const webDir =
|
|
8404
|
-
if (
|
|
9040
|
+
const currentDir = dirname7(fileURLToPath4(import.meta.url));
|
|
9041
|
+
const webDir = resolve10(currentDir, "..", "web");
|
|
9042
|
+
if (existsSync15(webDir) && existsSync15(join8(webDir, "package.json"))) {
|
|
8405
9043
|
return webDir;
|
|
8406
9044
|
}
|
|
8407
|
-
const altWebDir =
|
|
8408
|
-
if (
|
|
9045
|
+
const altWebDir = resolve10(currentDir, "..", "..", "web");
|
|
9046
|
+
if (existsSync15(altWebDir) && existsSync15(join8(altWebDir, "package.json"))) {
|
|
8409
9047
|
return altWebDir;
|
|
8410
9048
|
}
|
|
8411
9049
|
return null;
|
|
@@ -8428,18 +9066,18 @@ async function isSparkcoderWebRunning(port) {
|
|
|
8428
9066
|
}
|
|
8429
9067
|
}
|
|
8430
9068
|
function isPortInUse(port) {
|
|
8431
|
-
return new Promise((
|
|
9069
|
+
return new Promise((resolve12) => {
|
|
8432
9070
|
const server = createNetServer();
|
|
8433
9071
|
server.once("error", (err) => {
|
|
8434
9072
|
if (err.code === "EADDRINUSE") {
|
|
8435
|
-
|
|
9073
|
+
resolve12(true);
|
|
8436
9074
|
} else {
|
|
8437
|
-
|
|
9075
|
+
resolve12(false);
|
|
8438
9076
|
}
|
|
8439
9077
|
});
|
|
8440
9078
|
server.once("listening", () => {
|
|
8441
9079
|
server.close();
|
|
8442
|
-
|
|
9080
|
+
resolve12(false);
|
|
8443
9081
|
});
|
|
8444
9082
|
server.listen(port, "0.0.0.0");
|
|
8445
9083
|
});
|
|
@@ -8464,14 +9102,14 @@ async function findWebPort(preferredPort) {
|
|
|
8464
9102
|
}
|
|
8465
9103
|
function hasProductionBuild(webDir) {
|
|
8466
9104
|
const buildIdPath = join8(webDir, ".next", "BUILD_ID");
|
|
8467
|
-
return
|
|
9105
|
+
return existsSync15(buildIdPath);
|
|
8468
9106
|
}
|
|
8469
9107
|
function hasSourceFiles(webDir) {
|
|
8470
9108
|
const appDir = join8(webDir, "src", "app");
|
|
8471
9109
|
const pagesDir = join8(webDir, "src", "pages");
|
|
8472
9110
|
const rootAppDir = join8(webDir, "app");
|
|
8473
9111
|
const rootPagesDir = join8(webDir, "pages");
|
|
8474
|
-
return
|
|
9112
|
+
return existsSync15(appDir) || existsSync15(pagesDir) || existsSync15(rootAppDir) || existsSync15(rootPagesDir);
|
|
8475
9113
|
}
|
|
8476
9114
|
function getStandaloneServerPath(webDir) {
|
|
8477
9115
|
const possiblePaths2 = [
|
|
@@ -8479,14 +9117,14 @@ function getStandaloneServerPath(webDir) {
|
|
|
8479
9117
|
join8(webDir, ".next", "standalone", "web", "server.js")
|
|
8480
9118
|
];
|
|
8481
9119
|
for (const serverPath of possiblePaths2) {
|
|
8482
|
-
if (
|
|
9120
|
+
if (existsSync15(serverPath)) {
|
|
8483
9121
|
return serverPath;
|
|
8484
9122
|
}
|
|
8485
9123
|
}
|
|
8486
9124
|
return null;
|
|
8487
9125
|
}
|
|
8488
9126
|
function runCommand(command, args, cwd, env) {
|
|
8489
|
-
return new Promise((
|
|
9127
|
+
return new Promise((resolve12) => {
|
|
8490
9128
|
const child = spawn2(command, args, {
|
|
8491
9129
|
cwd,
|
|
8492
9130
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -8501,10 +9139,10 @@ function runCommand(command, args, cwd, env) {
|
|
|
8501
9139
|
output += data.toString();
|
|
8502
9140
|
});
|
|
8503
9141
|
child.on("close", (code) => {
|
|
8504
|
-
|
|
9142
|
+
resolve12({ success: code === 0, output });
|
|
8505
9143
|
});
|
|
8506
9144
|
child.on("error", (err) => {
|
|
8507
|
-
|
|
9145
|
+
resolve12({ success: false, output: err.message });
|
|
8508
9146
|
});
|
|
8509
9147
|
});
|
|
8510
9148
|
}
|
|
@@ -8519,8 +9157,8 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
8519
9157
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
8520
9158
|
return { process: null, port: actualPort };
|
|
8521
9159
|
}
|
|
8522
|
-
const usePnpm =
|
|
8523
|
-
const useNpm = !usePnpm &&
|
|
9160
|
+
const usePnpm = existsSync15(join8(webDir, "pnpm-lock.yaml"));
|
|
9161
|
+
const useNpm = !usePnpm && existsSync15(join8(webDir, "package-lock.json"));
|
|
8524
9162
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
8525
9163
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
8526
9164
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
@@ -8588,10 +9226,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
8588
9226
|
let started = false;
|
|
8589
9227
|
let exited = false;
|
|
8590
9228
|
let exitCode = null;
|
|
8591
|
-
const startedPromise = new Promise((
|
|
9229
|
+
const startedPromise = new Promise((resolve12) => {
|
|
8592
9230
|
const timeout = setTimeout(() => {
|
|
8593
9231
|
if (!started && !exited) {
|
|
8594
|
-
|
|
9232
|
+
resolve12(false);
|
|
8595
9233
|
}
|
|
8596
9234
|
}, startupTimeout);
|
|
8597
9235
|
child.stdout?.on("data", (data) => {
|
|
@@ -8605,7 +9243,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
8605
9243
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
8606
9244
|
started = true;
|
|
8607
9245
|
clearTimeout(timeout);
|
|
8608
|
-
|
|
9246
|
+
resolve12(true);
|
|
8609
9247
|
}
|
|
8610
9248
|
});
|
|
8611
9249
|
child.stderr?.on("data", (data) => {
|
|
@@ -8617,14 +9255,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
8617
9255
|
child.on("error", (err) => {
|
|
8618
9256
|
if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
|
|
8619
9257
|
clearTimeout(timeout);
|
|
8620
|
-
|
|
9258
|
+
resolve12(false);
|
|
8621
9259
|
});
|
|
8622
9260
|
child.on("exit", (code) => {
|
|
8623
9261
|
exited = true;
|
|
8624
9262
|
exitCode = code;
|
|
8625
9263
|
if (!started) {
|
|
8626
9264
|
clearTimeout(timeout);
|
|
8627
|
-
|
|
9265
|
+
resolve12(false);
|
|
8628
9266
|
}
|
|
8629
9267
|
webUIProcess = null;
|
|
8630
9268
|
});
|
|
@@ -8717,7 +9355,7 @@ async function startServer(options = {}) {
|
|
|
8717
9355
|
if (options.workingDirectory) {
|
|
8718
9356
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
8719
9357
|
}
|
|
8720
|
-
if (!
|
|
9358
|
+
if (!existsSync15(config.resolvedWorkingDirectory)) {
|
|
8721
9359
|
mkdirSync5(config.resolvedWorkingDirectory, { recursive: true });
|
|
8722
9360
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
8723
9361
|
}
|
|
@@ -9234,8 +9872,8 @@ function generateOpenAPISpec() {
|
|
|
9234
9872
|
init_config();
|
|
9235
9873
|
init_semantic();
|
|
9236
9874
|
init_db();
|
|
9237
|
-
import { writeFileSync as writeFileSync5, existsSync as
|
|
9238
|
-
import { resolve as
|
|
9875
|
+
import { writeFileSync as writeFileSync5, existsSync as existsSync16 } from "fs";
|
|
9876
|
+
import { resolve as resolve11, join as join9 } from "path";
|
|
9239
9877
|
async function apiRequest(baseUrl, path, options = {}) {
|
|
9240
9878
|
const url = `${baseUrl}${path}`;
|
|
9241
9879
|
const init = {
|
|
@@ -9270,13 +9908,13 @@ async function getActiveStream(baseUrl, sessionId) {
|
|
|
9270
9908
|
return { hasActiveStream: false };
|
|
9271
9909
|
}
|
|
9272
9910
|
function promptApproval(rl, toolName, input) {
|
|
9273
|
-
return new Promise((
|
|
9911
|
+
return new Promise((resolve12) => {
|
|
9274
9912
|
const inputStr = JSON.stringify(input);
|
|
9275
9913
|
const truncatedInput = inputStr.length > 100 ? inputStr.slice(0, 100) + "..." : inputStr;
|
|
9276
9914
|
console.log(chalk.dim(` Command: ${truncatedInput}`));
|
|
9277
9915
|
rl.question(chalk.yellow(` Approve? [y/n]: `), (answer) => {
|
|
9278
9916
|
const approved = answer.toLowerCase().startsWith("y");
|
|
9279
|
-
|
|
9917
|
+
resolve12(approved);
|
|
9280
9918
|
});
|
|
9281
9919
|
});
|
|
9282
9920
|
}
|
|
@@ -9459,9 +10097,9 @@ async function runChat(options) {
|
|
|
9459
10097
|
input: process.stdin,
|
|
9460
10098
|
output: process.stdout
|
|
9461
10099
|
});
|
|
9462
|
-
const apiKey = await new Promise((
|
|
10100
|
+
const apiKey = await new Promise((resolve12) => {
|
|
9463
10101
|
keyRl.question(chalk.cyan("Enter your AI Gateway API key: "), (answer) => {
|
|
9464
|
-
|
|
10102
|
+
resolve12(answer.trim());
|
|
9465
10103
|
});
|
|
9466
10104
|
});
|
|
9467
10105
|
keyRl.close();
|
|
@@ -9795,10 +10433,10 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
|
|
|
9795
10433
|
configPath = join9(appDataDir, "sparkecoder.config.json");
|
|
9796
10434
|
configLocation = "global";
|
|
9797
10435
|
} else {
|
|
9798
|
-
configPath =
|
|
10436
|
+
configPath = resolve11(process.cwd(), "sparkecoder.config.json");
|
|
9799
10437
|
configLocation = "local";
|
|
9800
10438
|
}
|
|
9801
|
-
if (
|
|
10439
|
+
if (existsSync16(configPath) && !options.force) {
|
|
9802
10440
|
console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
|
|
9803
10441
|
console.log(chalk.dim(` ${configPath}`));
|
|
9804
10442
|
return;
|
|
@@ -9861,7 +10499,7 @@ program.command("config").description("Show current configuration").option("-c,
|
|
|
9861
10499
|
});
|
|
9862
10500
|
program.command("index").description("Index repository for semantic search").option("--status", "Show index status instead of indexing").option("--full", "Force full re-index (ignore existing embeddings)").option("-w, --working-dir <path>", "Working directory (defaults to current directory)").option("-c, --config <path>", "Path to config file").option("-v, --verbose", "Show detailed progress").action(async (options) => {
|
|
9863
10501
|
try {
|
|
9864
|
-
const workingDir = options.workingDir ?
|
|
10502
|
+
const workingDir = options.workingDir ? resolve11(options.workingDir) : process.cwd();
|
|
9865
10503
|
let config = loadConfig(options.config, workingDir);
|
|
9866
10504
|
const remoteUrl = config.resolvedRemoteServer.url;
|
|
9867
10505
|
if (!remoteUrl) {
|
|
@@ -9995,7 +10633,7 @@ Indexing ${chalk.cyan(namespace)}...
|
|
|
9995
10633
|
});
|
|
9996
10634
|
program.command("search").description("Search indexed repository using semantic search").argument("<query>", "Search query").option("-n, --num <count>", "Number of results to show", "5").option("-w, --working-dir <path>", "Working directory (defaults to current directory)").option("-c, --config <path>", "Path to config file").action(async (query, options) => {
|
|
9997
10635
|
try {
|
|
9998
|
-
const workingDir = options.workingDir ?
|
|
10636
|
+
const workingDir = options.workingDir ? resolve11(options.workingDir) : process.cwd();
|
|
9999
10637
|
const config = loadConfig(options.config, workingDir);
|
|
10000
10638
|
const remoteUrl = config.resolvedRemoteServer.url;
|
|
10001
10639
|
if (!remoteUrl) {
|