@askexenow/exe-os 0.9.69 → 0.9.70
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/deploy/stack-manifests/v0.9.json +96 -16
- package/dist/bin/agentic-ontology-backfill.js +6 -0
- package/dist/bin/agentic-reflection-backfill.js +6 -0
- package/dist/bin/agentic-semantic-label.js +6 -0
- package/dist/bin/backfill-conversations.js +6 -0
- package/dist/bin/backfill-responses.js +6 -0
- package/dist/bin/backfill-vectors.js +6 -0
- package/dist/bin/bulk-sync-postgres.js +6 -0
- package/dist/bin/cleanup-stale-review-tasks.js +6 -0
- package/dist/bin/cli.js +1257 -178
- package/dist/bin/exe-agent.js +6 -0
- package/dist/bin/exe-assign.js +6 -0
- package/dist/bin/exe-boot.js +6 -0
- package/dist/bin/exe-call.js +6 -0
- package/dist/bin/exe-cloud.js +6 -0
- package/dist/bin/exe-dispatch.js +6 -0
- package/dist/bin/exe-doctor.js +6 -0
- package/dist/bin/exe-export-behaviors.js +6 -0
- package/dist/bin/exe-forget.js +6 -0
- package/dist/bin/exe-gateway.js +151 -110
- package/dist/bin/exe-heartbeat.js +6 -0
- package/dist/bin/exe-kill.js +6 -0
- package/dist/bin/exe-launch-agent.js +6 -0
- package/dist/bin/exe-new-employee.js +6 -0
- package/dist/bin/exe-pending-messages.js +6 -0
- package/dist/bin/exe-pending-notifications.js +6 -0
- package/dist/bin/exe-pending-reviews.js +6 -0
- package/dist/bin/exe-rename.js +13 -4
- package/dist/bin/exe-review.js +6 -0
- package/dist/bin/exe-search.js +6 -0
- package/dist/bin/exe-session-cleanup.js +6 -0
- package/dist/bin/exe-start-codex.js +6 -0
- package/dist/bin/exe-start-opencode.js +6 -0
- package/dist/bin/exe-status.js +6 -0
- package/dist/bin/exe-team.js +6 -0
- package/dist/bin/git-sweep.js +6 -0
- package/dist/bin/graph-backfill.js +150 -110
- package/dist/bin/graph-export.js +6 -0
- package/dist/bin/intercom-check.js +6 -0
- package/dist/bin/registry-proxy.js +207 -0
- package/dist/bin/scan-tasks.js +6 -0
- package/dist/bin/setup.js +6 -0
- package/dist/bin/shard-migrate.js +6 -0
- package/dist/bin/stack-update.js +128 -0
- package/dist/gateway/index.js +151 -110
- package/dist/hooks/bug-report-worker.js +6 -0
- package/dist/hooks/codex-stop-task-finalizer.js +6 -0
- package/dist/hooks/commit-complete.js +6 -0
- package/dist/hooks/error-recall.js +6 -0
- package/dist/hooks/ingest.js +6 -0
- package/dist/hooks/instructions-loaded.js +6 -0
- package/dist/hooks/notification.js +6 -0
- package/dist/hooks/post-compact.js +6 -0
- package/dist/hooks/post-tool-combined.js +6 -0
- package/dist/hooks/pre-compact.js +6 -0
- package/dist/hooks/pre-tool-use.js +6 -0
- package/dist/hooks/prompt-submit.js +6 -0
- package/dist/hooks/session-end.js +6 -0
- package/dist/hooks/session-start.js +6 -0
- package/dist/hooks/stop.js +6 -0
- package/dist/hooks/subagent-stop.js +6 -0
- package/dist/hooks/summary-worker.js +6 -0
- package/dist/index.js +151 -110
- package/dist/lib/employee-templates.js +6 -0
- package/dist/lib/exe-daemon.js +382 -234
- package/dist/lib/hybrid-search.js +6 -0
- package/dist/lib/registry-proxy.js +162 -0
- package/dist/lib/schedules.js +6 -0
- package/dist/lib/store.js +6 -0
- package/dist/mcp/server.js +318 -222
- package/dist/runtime/index.js +6 -0
- package/dist/tui/App.js +6 -0
- package/package.json +3 -2
- package/stack.release.json +6 -4
- package/stack.release.schema.json +89 -18
package/dist/mcp/server.js
CHANGED
|
@@ -4772,6 +4772,12 @@ var init_platform_procedures = __esm({
|
|
|
4772
4772
|
priority: "p0",
|
|
4773
4773
|
content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
|
|
4774
4774
|
},
|
|
4775
|
+
{
|
|
4776
|
+
title: "Code context first for repository orientation",
|
|
4777
|
+
domain: "workflow",
|
|
4778
|
+
priority: "p1",
|
|
4779
|
+
content: "Before broad repo exploration, symbol tracing, blast-radius review, or codebase Q&A, agents should use the consolidated code_context MCP tool instead of manual grep/read loops. Use action=index or stats to refresh/check the index; action=search with query, limit, offset, languages, paths, refresh_index for fresh multi-language code/doc search; action=trace for symbol imports/dependents; action=blast_radius for impact analysis before edits. CLI parity exists via exe-os code-context init|index|status|stats|search|doctor. Keep code_context separate from durable employee memory: promote only validated decisions, procedures, or lessons into store_memory/commit_memory."
|
|
4780
|
+
},
|
|
4775
4781
|
{
|
|
4776
4782
|
title: "Commit discipline \u2014 never leave verified work floating",
|
|
4777
4783
|
domain: "workflow",
|
|
@@ -18331,13 +18337,75 @@ import crypto13 from "crypto";
|
|
|
18331
18337
|
|
|
18332
18338
|
// src/lib/code-chunker.ts
|
|
18333
18339
|
import ts from "typescript";
|
|
18340
|
+
var LANGUAGE_BY_EXTENSION = {
|
|
18341
|
+
c: "c",
|
|
18342
|
+
cc: "cpp",
|
|
18343
|
+
cpp: "cpp",
|
|
18344
|
+
cxx: "cpp",
|
|
18345
|
+
h: "c",
|
|
18346
|
+
hh: "cpp",
|
|
18347
|
+
hpp: "cpp",
|
|
18348
|
+
cs: "csharp",
|
|
18349
|
+
css: "css",
|
|
18350
|
+
scss: "scss",
|
|
18351
|
+
f: "fortran",
|
|
18352
|
+
f90: "fortran",
|
|
18353
|
+
f95: "fortran",
|
|
18354
|
+
go: "go",
|
|
18355
|
+
html: "html",
|
|
18356
|
+
htm: "html",
|
|
18357
|
+
java: "java",
|
|
18358
|
+
js: "javascript",
|
|
18359
|
+
jsx: "javascript",
|
|
18360
|
+
json: "json",
|
|
18361
|
+
kt: "kotlin",
|
|
18362
|
+
kts: "kotlin",
|
|
18363
|
+
lua: "lua",
|
|
18364
|
+
md: "markdown",
|
|
18365
|
+
mdx: "mdx",
|
|
18366
|
+
pas: "pascal",
|
|
18367
|
+
php: "php",
|
|
18368
|
+
py: "python",
|
|
18369
|
+
r: "r",
|
|
18370
|
+
rb: "ruby",
|
|
18371
|
+
rs: "rust",
|
|
18372
|
+
scala: "scala",
|
|
18373
|
+
sc: "scala",
|
|
18374
|
+
sol: "solidity",
|
|
18375
|
+
sql: "sql",
|
|
18376
|
+
svelte: "svelte",
|
|
18377
|
+
swift: "swift",
|
|
18378
|
+
toml: "toml",
|
|
18379
|
+
ts: "typescript",
|
|
18380
|
+
tsx: "typescript",
|
|
18381
|
+
vue: "vue",
|
|
18382
|
+
xml: "xml",
|
|
18383
|
+
yaml: "yaml",
|
|
18384
|
+
yml: "yaml",
|
|
18385
|
+
sh: "shell",
|
|
18386
|
+
bash: "shell",
|
|
18387
|
+
zsh: "shell"
|
|
18388
|
+
};
|
|
18389
|
+
var TEXT_LIKE_LANGUAGES = /* @__PURE__ */ new Set(["json", "markdown", "mdx", "toml", "yaml", "xml", "html", "css", "scss", "sql"]);
|
|
18390
|
+
function languageForFile(filePath) {
|
|
18391
|
+
const base = filePath.split(/[\\/]/).pop()?.toLowerCase() ?? "";
|
|
18392
|
+
if (["dockerfile", "makefile", "rakefile", "gemfile"].includes(base)) return base;
|
|
18393
|
+
const ext = base.includes(".") ? base.split(".").pop() : void 0;
|
|
18394
|
+
return ext ? LANGUAGE_BY_EXTENSION[ext] : void 0;
|
|
18395
|
+
}
|
|
18334
18396
|
function chunkSourceFile(source, fileName = "file.ts") {
|
|
18397
|
+
const language = languageForFile(fileName);
|
|
18398
|
+
if (language === "typescript" || language === "javascript") {
|
|
18399
|
+
return chunkTypeScriptLike(source, fileName);
|
|
18400
|
+
}
|
|
18401
|
+
return chunkGenericSource(source, fileName, language);
|
|
18402
|
+
}
|
|
18403
|
+
function chunkTypeScriptLike(source, fileName) {
|
|
18335
18404
|
const sourceFile = ts.createSourceFile(
|
|
18336
18405
|
fileName,
|
|
18337
18406
|
source,
|
|
18338
18407
|
ts.ScriptTarget.Latest,
|
|
18339
18408
|
true,
|
|
18340
|
-
// setParentNodes
|
|
18341
18409
|
fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
18342
18410
|
);
|
|
18343
18411
|
const chunks = [];
|
|
@@ -18359,144 +18427,117 @@ function chunkSourceFile(source, fileName = "file.ts") {
|
|
|
18359
18427
|
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
18360
18428
|
return node.name?.getText(sourceFile) ?? "(anonymous)";
|
|
18361
18429
|
}
|
|
18362
|
-
if (ts.isClassDeclaration(node))
|
|
18363
|
-
|
|
18364
|
-
|
|
18365
|
-
if (ts.
|
|
18366
|
-
|
|
18367
|
-
|
|
18368
|
-
if (ts.isTypeAliasDeclaration(node)) {
|
|
18369
|
-
return node.name.getText(sourceFile);
|
|
18370
|
-
}
|
|
18371
|
-
if (ts.isEnumDeclaration(node)) {
|
|
18372
|
-
return node.name.getText(sourceFile);
|
|
18373
|
-
}
|
|
18374
|
-
if (ts.isVariableStatement(node)) {
|
|
18375
|
-
const decls = node.declarationList.declarations;
|
|
18376
|
-
return decls.map((d) => d.name.getText(sourceFile)).join(", ");
|
|
18377
|
-
}
|
|
18378
|
-
if (ts.isExportAssignment(node)) {
|
|
18379
|
-
return "default export";
|
|
18380
|
-
}
|
|
18430
|
+
if (ts.isClassDeclaration(node)) return node.name?.getText(sourceFile) ?? "(anonymous class)";
|
|
18431
|
+
if (ts.isInterfaceDeclaration(node)) return node.name.getText(sourceFile);
|
|
18432
|
+
if (ts.isTypeAliasDeclaration(node)) return node.name.getText(sourceFile);
|
|
18433
|
+
if (ts.isEnumDeclaration(node)) return node.name.getText(sourceFile);
|
|
18434
|
+
if (ts.isVariableStatement(node)) return node.declarationList.declarations.map((d) => d.name.getText(sourceFile)).join(", ");
|
|
18435
|
+
if (ts.isExportAssignment(node)) return "default export";
|
|
18381
18436
|
return "(unknown)";
|
|
18382
18437
|
}
|
|
18383
18438
|
function visitTopLevel(node) {
|
|
18384
18439
|
if (ts.isImportDeclaration(node)) {
|
|
18385
|
-
importLines.push({
|
|
18386
|
-
start: getLineNumber(node.getStart(sourceFile)),
|
|
18387
|
-
end: getLineNumber(node.getEnd())
|
|
18388
|
-
});
|
|
18440
|
+
importLines.push({ start: getLineNumber(node.getStart(sourceFile)), end: getLineNumber(node.getEnd()) });
|
|
18389
18441
|
return;
|
|
18390
18442
|
}
|
|
18391
18443
|
if (ts.isFunctionDeclaration(node)) {
|
|
18392
|
-
chunks.push({
|
|
18393
|
-
kind: "function",
|
|
18394
|
-
name: getName(node),
|
|
18395
|
-
text: getNodeText(node),
|
|
18396
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18397
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18398
|
-
comment: getLeadingComment(node)
|
|
18399
|
-
});
|
|
18444
|
+
chunks.push({ kind: "function", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
18400
18445
|
return;
|
|
18401
18446
|
}
|
|
18402
18447
|
if (ts.isClassDeclaration(node)) {
|
|
18403
|
-
chunks.push({
|
|
18404
|
-
kind: "class",
|
|
18405
|
-
name: getName(node),
|
|
18406
|
-
text: getNodeText(node),
|
|
18407
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18408
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18409
|
-
comment: getLeadingComment(node)
|
|
18410
|
-
});
|
|
18411
|
-
return;
|
|
18412
|
-
}
|
|
18413
|
-
if (ts.isInterfaceDeclaration(node)) {
|
|
18414
|
-
chunks.push({
|
|
18415
|
-
kind: "type",
|
|
18416
|
-
name: getName(node),
|
|
18417
|
-
text: getNodeText(node),
|
|
18418
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18419
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18420
|
-
comment: getLeadingComment(node)
|
|
18421
|
-
});
|
|
18422
|
-
return;
|
|
18423
|
-
}
|
|
18424
|
-
if (ts.isTypeAliasDeclaration(node)) {
|
|
18425
|
-
chunks.push({
|
|
18426
|
-
kind: "type",
|
|
18427
|
-
name: getName(node),
|
|
18428
|
-
text: getNodeText(node),
|
|
18429
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18430
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18431
|
-
comment: getLeadingComment(node)
|
|
18432
|
-
});
|
|
18448
|
+
chunks.push({ kind: "class", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
18433
18449
|
return;
|
|
18434
18450
|
}
|
|
18435
|
-
if (ts.isEnumDeclaration(node)) {
|
|
18436
|
-
chunks.push({
|
|
18437
|
-
kind: "type",
|
|
18438
|
-
name: getName(node),
|
|
18439
|
-
text: getNodeText(node),
|
|
18440
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18441
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18442
|
-
comment: getLeadingComment(node)
|
|
18443
|
-
});
|
|
18451
|
+
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node)) {
|
|
18452
|
+
chunks.push({ kind: "type", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
18444
18453
|
return;
|
|
18445
18454
|
}
|
|
18446
18455
|
if (ts.isVariableStatement(node)) {
|
|
18447
|
-
const
|
|
18448
|
-
|
|
18449
|
-
(d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))
|
|
18450
|
-
);
|
|
18451
|
-
chunks.push({
|
|
18452
|
-
kind: isFnLike ? "function" : "variable",
|
|
18453
|
-
name: getName(node),
|
|
18454
|
-
text: getNodeText(node),
|
|
18455
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18456
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18457
|
-
comment: getLeadingComment(node)
|
|
18458
|
-
});
|
|
18456
|
+
const isFnLike = node.declarationList.declarations.some((d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer)));
|
|
18457
|
+
chunks.push({ kind: isFnLike ? "function" : "variable", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
18459
18458
|
return;
|
|
18460
18459
|
}
|
|
18461
18460
|
if (ts.isExportAssignment(node)) {
|
|
18462
|
-
chunks.push({
|
|
18463
|
-
kind: "export",
|
|
18464
|
-
name: "default export",
|
|
18465
|
-
text: getNodeText(node),
|
|
18466
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18467
|
-
endLine: getLineNumber(node.getEnd()),
|
|
18468
|
-
comment: getLeadingComment(node)
|
|
18469
|
-
});
|
|
18461
|
+
chunks.push({ kind: "export", name: "default export", text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
18470
18462
|
return;
|
|
18471
18463
|
}
|
|
18472
18464
|
if (ts.isExpressionStatement(node)) {
|
|
18473
18465
|
const text3 = getNodeText(node);
|
|
18474
|
-
if (text3.length > 10) {
|
|
18475
|
-
chunks.push({
|
|
18476
|
-
kind: "other",
|
|
18477
|
-
name: text3.slice(0, 40).replace(/\n/g, " "),
|
|
18478
|
-
text: text3,
|
|
18479
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18480
|
-
endLine: getLineNumber(node.getEnd())
|
|
18481
|
-
});
|
|
18482
|
-
}
|
|
18483
|
-
return;
|
|
18466
|
+
if (text3.length > 10) chunks.push({ kind: "other", name: text3.slice(0, 40).replace(/\n/g, " "), text: text3, startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()) });
|
|
18484
18467
|
}
|
|
18485
18468
|
}
|
|
18486
18469
|
sourceFile.statements.forEach(visitTopLevel);
|
|
18487
18470
|
if (importLines.length > 0) {
|
|
18488
18471
|
const startLine = importLines[0].start;
|
|
18489
18472
|
const endLine = importLines[importLines.length - 1].end;
|
|
18490
|
-
|
|
18491
|
-
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18473
|
+
chunks.unshift({ kind: "import", name: `${importLines.length} imports`, text: lines.slice(startLine - 1, endLine).join("\n"), startLine, endLine });
|
|
18474
|
+
}
|
|
18475
|
+
chunks.sort((a, b) => a.startLine - b.startLine);
|
|
18476
|
+
return chunks.length > 0 ? chunks : chunkByWindows(source, 80);
|
|
18477
|
+
}
|
|
18478
|
+
function chunkGenericSource(source, _fileName, language) {
|
|
18479
|
+
const lines = source.split("\n");
|
|
18480
|
+
if (source.trim().length === 0) return [];
|
|
18481
|
+
if (language && TEXT_LIKE_LANGUAGES.has(language)) return chunkTextLike(lines, language);
|
|
18482
|
+
const chunks = [];
|
|
18483
|
+
const patterns = [
|
|
18484
|
+
{ kind: "function", regex: /^\s*(?:async\s+)?def\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
18485
|
+
{ kind: "class", regex: /^\s*class\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
18486
|
+
{ kind: "function", regex: /^\s*(?:pub\s+)?fn\s+([A-Za-z_][\w]*)\s*[<(]/, name: (m) => m[1] },
|
|
18487
|
+
{ kind: "class", regex: /^\s*(?:pub\s+)?(?:struct|enum|trait)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
18488
|
+
{ kind: "function", regex: /^\s*func\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
18489
|
+
{ kind: "function", regex: /^\s*(?:public|private|protected|static|final|suspend|fun|override|open|internal|export|async|func|function|subroutine)\s+.*?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
18490
|
+
{ kind: "function", regex: /^\s*function\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
18491
|
+
{ kind: "type", regex: /^\s*(?:interface|type|enum|record|data\s+class|case\s+class|contract|library)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
18492
|
+
{ kind: "section", regex: /^\s{0,3}#{1,6}\s+(.+)$/, name: (m) => m[1].trim() }
|
|
18493
|
+
];
|
|
18494
|
+
const starts = [];
|
|
18495
|
+
for (let i = 0; i < lines.length; i++) {
|
|
18496
|
+
const line = lines[i];
|
|
18497
|
+
for (const pattern of patterns) {
|
|
18498
|
+
const match = line.match(pattern.regex);
|
|
18499
|
+
if (match) {
|
|
18500
|
+
starts.push({ line: i + 1, kind: pattern.kind, name: pattern.name(match) });
|
|
18501
|
+
break;
|
|
18502
|
+
}
|
|
18503
|
+
}
|
|
18504
|
+
}
|
|
18505
|
+
if (starts.length === 0) return chunkByWindows(source, 80);
|
|
18506
|
+
for (let i = 0; i < starts.length; i++) {
|
|
18507
|
+
const start = starts[i];
|
|
18508
|
+
const next = starts[i + 1]?.line ?? lines.length + 1;
|
|
18509
|
+
const endLine = Math.max(start.line, next - 1);
|
|
18510
|
+
chunks.push({
|
|
18511
|
+
kind: start.kind,
|
|
18512
|
+
name: start.name,
|
|
18513
|
+
text: lines.slice(start.line - 1, endLine).join("\n"),
|
|
18514
|
+
startLine: start.line,
|
|
18496
18515
|
endLine
|
|
18497
18516
|
});
|
|
18498
18517
|
}
|
|
18499
|
-
chunks
|
|
18518
|
+
return chunks;
|
|
18519
|
+
}
|
|
18520
|
+
function chunkTextLike(lines, language) {
|
|
18521
|
+
if (language === "markdown" || language === "mdx") {
|
|
18522
|
+
const starts = lines.map((line, i) => ({ line, i })).filter(({ line }) => /^\s{0,3}#{1,6}\s+/.test(line));
|
|
18523
|
+
if (starts.length > 0) {
|
|
18524
|
+
return starts.map((start, idx) => {
|
|
18525
|
+
const end = starts[idx + 1]?.i ?? lines.length;
|
|
18526
|
+
const title = start.line.replace(/^\s{0,3}#{1,6}\s+/, "").trim();
|
|
18527
|
+
return { kind: "section", name: title, text: lines.slice(start.i, end).join("\n"), startLine: start.i + 1, endLine: end };
|
|
18528
|
+
});
|
|
18529
|
+
}
|
|
18530
|
+
}
|
|
18531
|
+
return chunkByWindows(lines.join("\n"), 80);
|
|
18532
|
+
}
|
|
18533
|
+
function chunkByWindows(source, windowLines) {
|
|
18534
|
+
const lines = source.split("\n");
|
|
18535
|
+
const chunks = [];
|
|
18536
|
+
for (let i = 0; i < lines.length; i += windowLines) {
|
|
18537
|
+
const end = Math.min(lines.length, i + windowLines);
|
|
18538
|
+
const text3 = lines.slice(i, end).join("\n");
|
|
18539
|
+
if (text3.trim()) chunks.push({ kind: "other", name: `lines ${i + 1}-${end}`, text: text3, startLine: i + 1, endLine: end });
|
|
18540
|
+
}
|
|
18500
18541
|
return chunks;
|
|
18501
18542
|
}
|
|
18502
18543
|
function summarizeChunk(chunk, filePath) {
|
|
@@ -18515,13 +18556,14 @@ function summarizeChunk(chunk, filePath) {
|
|
|
18515
18556
|
return `Variable ${chunk.name} in ${location}`;
|
|
18516
18557
|
case "export":
|
|
18517
18558
|
return `Default export in ${location}`;
|
|
18559
|
+
case "section":
|
|
18560
|
+
return `Section ${chunk.name} in ${location}`;
|
|
18518
18561
|
default:
|
|
18519
18562
|
return `${chunk.kind} in ${location}`;
|
|
18520
18563
|
}
|
|
18521
18564
|
}
|
|
18522
18565
|
function isChunkable(filePath) {
|
|
18523
|
-
|
|
18524
|
-
return ext === "ts" || ext === "tsx" || ext === "js" || ext === "jsx";
|
|
18566
|
+
return Boolean(languageForFile(filePath));
|
|
18525
18567
|
}
|
|
18526
18568
|
|
|
18527
18569
|
// src/lib/graph-rag.ts
|
|
@@ -26962,12 +27004,11 @@ import { z as z88 } from "zod";
|
|
|
26962
27004
|
init_config();
|
|
26963
27005
|
import crypto19 from "crypto";
|
|
26964
27006
|
import path50 from "path";
|
|
26965
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync21, readFileSync as readFileSync32, statSync as statSync9, writeFileSync as writeFileSync23 } from "fs";
|
|
27007
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync21, readFileSync as readFileSync32, readdirSync as readdirSync14, statSync as statSync9, writeFileSync as writeFileSync23 } from "fs";
|
|
26966
27008
|
import { spawnSync } from "child_process";
|
|
26967
|
-
var INDEX_VERSION =
|
|
26968
|
-
var DEFAULT_MAX_FILES =
|
|
26969
|
-
var
|
|
26970
|
-
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build"]);
|
|
27009
|
+
var INDEX_VERSION = 2;
|
|
27010
|
+
var DEFAULT_MAX_FILES = 5e3;
|
|
27011
|
+
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
26971
27012
|
function normalizeProjectRoot(projectRoot) {
|
|
26972
27013
|
return path50.resolve(projectRoot || process.cwd());
|
|
26973
27014
|
}
|
|
@@ -26985,78 +27026,53 @@ function getCodeContextIndexPath(projectRoot) {
|
|
|
26985
27026
|
return path50.join(indexDir(), `${rootHash}.json`);
|
|
26986
27027
|
}
|
|
26987
27028
|
function currentBranch(projectRoot) {
|
|
26988
|
-
const result2 = spawnSync("git", ["branch", "--show-current"], {
|
|
26989
|
-
cwd: projectRoot,
|
|
26990
|
-
encoding: "utf8",
|
|
26991
|
-
timeout: 2e3
|
|
26992
|
-
});
|
|
27029
|
+
const result2 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
26993
27030
|
const branch = result2.status === 0 ? result2.stdout.trim() : "";
|
|
26994
27031
|
return branch || "detached-or-unknown";
|
|
26995
27032
|
}
|
|
26996
27033
|
function shouldIgnore(relPath) {
|
|
26997
|
-
const parts = relPath.split(
|
|
27034
|
+
const parts = relPath.split(/[\\/]/);
|
|
26998
27035
|
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
26999
27036
|
}
|
|
27000
|
-
function
|
|
27001
|
-
|
|
27037
|
+
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
27038
|
+
for (const entry of readdirSync14(dir, { withFileTypes: true })) {
|
|
27039
|
+
const abs = path50.join(dir, entry.name);
|
|
27040
|
+
const rel = path50.relative(projectRoot, abs).replaceAll(path50.sep, "/");
|
|
27041
|
+
if (shouldIgnore(rel)) continue;
|
|
27042
|
+
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
27043
|
+
else if (entry.isFile()) out.push(rel);
|
|
27044
|
+
}
|
|
27045
|
+
return out;
|
|
27002
27046
|
}
|
|
27003
27047
|
function listCodeFiles(projectRoot, maxFiles) {
|
|
27004
|
-
const git = spawnSync("git", ["ls-files",
|
|
27005
|
-
cwd: projectRoot,
|
|
27006
|
-
encoding: "utf8",
|
|
27007
|
-
timeout: 5e3,
|
|
27008
|
-
maxBuffer: 1024 * 1024 * 8
|
|
27009
|
-
});
|
|
27048
|
+
const git = spawnSync("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
27010
27049
|
let files = [];
|
|
27011
27050
|
if (git.status === 0 && git.stdout.trim()) {
|
|
27012
27051
|
files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
27013
27052
|
} else {
|
|
27014
|
-
const rg = spawnSync("rg", ["--files",
|
|
27015
|
-
|
|
27016
|
-
encoding: "utf8",
|
|
27017
|
-
timeout: 5e3,
|
|
27018
|
-
maxBuffer: 1024 * 1024 * 8
|
|
27019
|
-
});
|
|
27020
|
-
if (rg.status === 0 && rg.stdout.trim()) {
|
|
27021
|
-
files = rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
27022
|
-
}
|
|
27053
|
+
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
27054
|
+
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
27023
27055
|
}
|
|
27024
|
-
return files.filter((file) =>
|
|
27056
|
+
return files.map((file) => file.replaceAll(path50.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
27025
27057
|
}
|
|
27026
27058
|
function parseImportPaths(importText) {
|
|
27027
27059
|
const paths = [];
|
|
27028
|
-
const
|
|
27029
|
-
|
|
27030
|
-
|
|
27031
|
-
|
|
27032
|
-
while ((match = bareRegex.exec(importText)) !== null) {
|
|
27033
|
-
if (!paths.includes(match[1])) paths.push(match[1]);
|
|
27034
|
-
}
|
|
27035
|
-
const requireRegex = /require\(["']([^"']+)["']\)/g;
|
|
27036
|
-
while ((match = requireRegex.exec(importText)) !== null) {
|
|
27037
|
-
if (!paths.includes(match[1])) paths.push(match[1]);
|
|
27060
|
+
const patterns = [/from\s+["']([^"']+)["']/g, /^import\s+["']([^"']+)["']/gm, /require\(["']([^"']+)["']\)/g, /use\s+([A-Za-z0-9_:]+)::/g, /#include\s+[<"]([^>"]+)[>"]/g];
|
|
27061
|
+
for (const regex of patterns) {
|
|
27062
|
+
let match;
|
|
27063
|
+
while ((match = regex.exec(importText)) !== null) if (!paths.includes(match[1])) paths.push(match[1]);
|
|
27038
27064
|
}
|
|
27039
27065
|
return paths;
|
|
27040
27066
|
}
|
|
27041
27067
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
27042
27068
|
if (!importPath.startsWith(".")) return null;
|
|
27043
27069
|
const base = path50.posix.normalize(path50.posix.join(path50.posix.dirname(fromFile.replaceAll(path50.sep, "/")), importPath));
|
|
27044
|
-
const withoutKnownExt = base.replace(/\.(?:
|
|
27045
|
-
const candidates = [
|
|
27046
|
-
|
|
27047
|
-
`${withoutKnownExt}
|
|
27048
|
-
|
|
27049
|
-
|
|
27050
|
-
`${withoutKnownExt}.jsx`,
|
|
27051
|
-
`${base}.ts`,
|
|
27052
|
-
`${base}.tsx`,
|
|
27053
|
-
`${base}.js`,
|
|
27054
|
-
`${base}.jsx`,
|
|
27055
|
-
path50.posix.join(base, "index.ts"),
|
|
27056
|
-
path50.posix.join(base, "index.tsx"),
|
|
27057
|
-
path50.posix.join(base, "index.js"),
|
|
27058
|
-
path50.posix.join(base, "index.jsx")
|
|
27059
|
-
];
|
|
27070
|
+
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
27071
|
+
const candidates = [base];
|
|
27072
|
+
for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
|
|
27073
|
+
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
27074
|
+
}
|
|
27075
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path50.posix.join(base, indexName));
|
|
27060
27076
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
27061
27077
|
}
|
|
27062
27078
|
function symbolId(filePath, chunk) {
|
|
@@ -27082,39 +27098,32 @@ function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
|
27082
27098
|
try {
|
|
27083
27099
|
stat = statSync9(absPath);
|
|
27084
27100
|
} catch {
|
|
27085
|
-
return null;
|
|
27101
|
+
return { record: null, reused: false };
|
|
27086
27102
|
}
|
|
27087
|
-
if (!stat.isFile()) return null;
|
|
27103
|
+
if (!stat.isFile()) return { record: null, reused: false };
|
|
27104
|
+
const language = languageForFile(relPath);
|
|
27105
|
+
if (!language || !isChunkable(relPath)) return { record: null, reused: false };
|
|
27088
27106
|
const source = readFileSync32(absPath, "utf8");
|
|
27089
27107
|
const hash = hashText(source);
|
|
27090
|
-
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size) {
|
|
27091
|
-
return previous;
|
|
27108
|
+
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size && previous.language === language) {
|
|
27109
|
+
return { record: previous, reused: true };
|
|
27092
27110
|
}
|
|
27093
|
-
if (!isChunkable(relPath)) return null;
|
|
27094
27111
|
const chunks = chunkSourceFile(source, relPath);
|
|
27095
27112
|
const imports = chunks.filter((chunk) => chunk.kind === "import").flatMap((chunk) => parseImportPaths(chunk.text));
|
|
27096
27113
|
const resolvedImports = imports.map((importPath) => resolveImport(relPath, importPath, allFiles)).filter((file) => Boolean(file));
|
|
27097
|
-
const symbols = chunks.filter((chunk) => chunk.
|
|
27114
|
+
const symbols = chunks.filter((chunk) => chunk.name !== "(unknown)").map((chunk) => ({
|
|
27098
27115
|
id: symbolId(relPath, chunk),
|
|
27099
27116
|
name: chunk.name,
|
|
27100
27117
|
kind: chunk.kind,
|
|
27101
27118
|
filePath: relPath,
|
|
27119
|
+
language,
|
|
27102
27120
|
startLine: chunk.startLine,
|
|
27103
27121
|
endLine: chunk.endLine,
|
|
27104
27122
|
summary: summarizeChunk(chunk, relPath),
|
|
27105
|
-
text: chunk.text.slice(0,
|
|
27123
|
+
text: chunk.text.slice(0, 8e3),
|
|
27106
27124
|
comment: chunk.comment
|
|
27107
27125
|
}));
|
|
27108
|
-
return {
|
|
27109
|
-
path: relPath,
|
|
27110
|
-
absPath,
|
|
27111
|
-
hash,
|
|
27112
|
-
mtimeMs: stat.mtimeMs,
|
|
27113
|
-
size: stat.size,
|
|
27114
|
-
imports,
|
|
27115
|
-
resolvedImports,
|
|
27116
|
-
symbols
|
|
27117
|
-
};
|
|
27126
|
+
return { record: { path: relPath, absPath, language, hash, mtimeMs: stat.mtimeMs, size: stat.size, imports, resolvedImports, symbols }, reused: false };
|
|
27118
27127
|
}
|
|
27119
27128
|
function buildCodeContextIndex(options = {}) {
|
|
27120
27129
|
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
@@ -27124,17 +27133,27 @@ function buildCodeContextIndex(options = {}) {
|
|
|
27124
27133
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
27125
27134
|
const allFiles = new Set(files.map((file) => file.replaceAll(path50.sep, "/")));
|
|
27126
27135
|
const fileRecords = {};
|
|
27136
|
+
let rebuiltFiles = 0;
|
|
27137
|
+
let reusedFiles = 0;
|
|
27138
|
+
let skippedFiles = 0;
|
|
27127
27139
|
for (const rel of files) {
|
|
27128
27140
|
const normalized = rel.replaceAll(path50.sep, "/");
|
|
27129
|
-
const record = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
27130
|
-
if (record)
|
|
27131
|
-
|
|
27141
|
+
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
27142
|
+
if (record) {
|
|
27143
|
+
fileRecords[normalized] = record;
|
|
27144
|
+
if (reused) reusedFiles++;
|
|
27145
|
+
else rebuiltFiles++;
|
|
27146
|
+
} else skippedFiles++;
|
|
27147
|
+
}
|
|
27148
|
+
const languageBreakdown = {};
|
|
27149
|
+
for (const file of Object.values(fileRecords)) languageBreakdown[file.language] = (languageBreakdown[file.language] ?? 0) + 1;
|
|
27132
27150
|
const index = {
|
|
27133
27151
|
version: INDEX_VERSION,
|
|
27134
27152
|
projectRoot,
|
|
27135
27153
|
rootHash: hashText(projectRoot).slice(0, 16),
|
|
27136
27154
|
branch,
|
|
27137
27155
|
indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27156
|
+
stats: { filesSeen: files.length, filesIndexed: Object.keys(fileRecords).length, rebuiltFiles, reusedFiles, skippedFiles, languageBreakdown },
|
|
27138
27157
|
files: fileRecords
|
|
27139
27158
|
};
|
|
27140
27159
|
saveIndex(index);
|
|
@@ -27162,13 +27181,60 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
27162
27181
|
}
|
|
27163
27182
|
return buildCodeContextIndex(options);
|
|
27164
27183
|
}
|
|
27184
|
+
function normalizeLanguage(language) {
|
|
27185
|
+
return language.toLowerCase().replace(/^c\+\+$/, "cpp").replace(/^c#$/, "csharp").replace(/^js$/, "javascript").replace(/^ts$/, "typescript");
|
|
27186
|
+
}
|
|
27165
27187
|
function tokenize(query) {
|
|
27166
|
-
|
|
27188
|
+
const raw = query.toLowerCase().replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-z0-9_.$/-]+/).map((s) => s.trim()).filter((s) => s.length >= 2);
|
|
27189
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
27190
|
+
for (const term of raw) {
|
|
27191
|
+
expanded.add(term);
|
|
27192
|
+
for (const part of term.split(/[_.$/-]+/)) if (part.length >= 2) expanded.add(part);
|
|
27193
|
+
const dashed = term.replace(/[_.$/]+/g, "-");
|
|
27194
|
+
if (dashed.length >= 2) expanded.add(dashed);
|
|
27195
|
+
if (!term.includes("-")) expanded.add(`${term}s`);
|
|
27196
|
+
if (term.endsWith("s") && term.length > 3) expanded.add(term.slice(0, -1));
|
|
27197
|
+
}
|
|
27198
|
+
return [...expanded];
|
|
27199
|
+
}
|
|
27200
|
+
function ngrams(terms) {
|
|
27201
|
+
const grams = [...terms];
|
|
27202
|
+
for (let i = 0; i < terms.length - 1; i++) grams.push(`${terms[i]} ${terms[i + 1]}`);
|
|
27203
|
+
return grams;
|
|
27204
|
+
}
|
|
27205
|
+
function globToRegex(pattern) {
|
|
27206
|
+
let out = "";
|
|
27207
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
27208
|
+
const ch = pattern[i];
|
|
27209
|
+
const next = pattern[i + 1];
|
|
27210
|
+
if (ch === "*" && next === "*") {
|
|
27211
|
+
out += ".*";
|
|
27212
|
+
i++;
|
|
27213
|
+
continue;
|
|
27214
|
+
}
|
|
27215
|
+
if (ch === "*") {
|
|
27216
|
+
out += "[^/]*";
|
|
27217
|
+
continue;
|
|
27218
|
+
}
|
|
27219
|
+
if (".+^${}()|[]\\".includes(ch)) out += `\\${ch}`;
|
|
27220
|
+
else out += ch;
|
|
27221
|
+
}
|
|
27222
|
+
return new RegExp(`^${out}$`);
|
|
27223
|
+
}
|
|
27224
|
+
function matchesPath(filePath, patterns) {
|
|
27225
|
+
if (!patterns || patterns.length === 0) return true;
|
|
27226
|
+
const normalized = filePath.replaceAll(path50.sep, "/");
|
|
27227
|
+
return patterns.some((pattern) => {
|
|
27228
|
+
const p = pattern.replaceAll(path50.sep, "/").replace(/^\.\//, "");
|
|
27229
|
+
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
27230
|
+
});
|
|
27167
27231
|
}
|
|
27168
27232
|
function scoreSymbol(symbol, terms) {
|
|
27233
|
+
const grams = ngrams(terms);
|
|
27169
27234
|
const haystacks = {
|
|
27170
27235
|
name: symbol.name.toLowerCase(),
|
|
27171
27236
|
path: symbol.filePath.toLowerCase(),
|
|
27237
|
+
language: symbol.language.toLowerCase(),
|
|
27172
27238
|
summary: symbol.summary.toLowerCase(),
|
|
27173
27239
|
text: symbol.text.toLowerCase()
|
|
27174
27240
|
};
|
|
@@ -27176,41 +27242,73 @@ function scoreSymbol(symbol, terms) {
|
|
|
27176
27242
|
const matches = [];
|
|
27177
27243
|
for (const term of terms) {
|
|
27178
27244
|
if (haystacks.name === term) {
|
|
27179
|
-
score +=
|
|
27245
|
+
score += 100;
|
|
27180
27246
|
matches.push(`name=${term}`);
|
|
27181
27247
|
continue;
|
|
27182
27248
|
}
|
|
27183
27249
|
if (haystacks.name.includes(term)) {
|
|
27184
|
-
score +=
|
|
27250
|
+
score += 45;
|
|
27185
27251
|
matches.push(`name~${term}`);
|
|
27186
27252
|
}
|
|
27187
27253
|
if (haystacks.path.includes(term)) {
|
|
27188
|
-
score +=
|
|
27254
|
+
score += 18;
|
|
27189
27255
|
matches.push(`path~${term}`);
|
|
27190
27256
|
}
|
|
27257
|
+
if (haystacks.language === term) {
|
|
27258
|
+
score += 18;
|
|
27259
|
+
matches.push(`language=${term}`);
|
|
27260
|
+
}
|
|
27191
27261
|
if (haystacks.summary.includes(term)) {
|
|
27192
|
-
score +=
|
|
27262
|
+
score += 16;
|
|
27193
27263
|
matches.push(`summary~${term}`);
|
|
27194
27264
|
}
|
|
27195
27265
|
if (haystacks.text.includes(term)) {
|
|
27196
|
-
score +=
|
|
27266
|
+
score += 5;
|
|
27197
27267
|
matches.push(`text~${term}`);
|
|
27198
27268
|
}
|
|
27199
27269
|
}
|
|
27270
|
+
for (const gram of grams.filter((g) => g.includes(" "))) {
|
|
27271
|
+
if (haystacks.text.includes(gram) || haystacks.summary.includes(gram)) {
|
|
27272
|
+
score += 20;
|
|
27273
|
+
matches.push(`phrase~${gram}`);
|
|
27274
|
+
}
|
|
27275
|
+
}
|
|
27276
|
+
const uniqueMatches = new Set(matches.map((m) => m.replace(/^[^=~]+[=~]/, ""))).size;
|
|
27277
|
+
score += uniqueMatches * 3;
|
|
27200
27278
|
return { score, matches };
|
|
27201
27279
|
}
|
|
27280
|
+
function filteredFiles(index, options = {}) {
|
|
27281
|
+
const languages = options.languages?.map(normalizeLanguage).filter(Boolean);
|
|
27282
|
+
return Object.values(index.files).filter((file) => {
|
|
27283
|
+
if (languages && languages.length > 0 && !languages.includes(normalizeLanguage(file.language))) return false;
|
|
27284
|
+
return matchesPath(file.path, options.paths);
|
|
27285
|
+
});
|
|
27286
|
+
}
|
|
27202
27287
|
function searchCodeContext(query, options = {}) {
|
|
27203
27288
|
const terms = tokenize(query);
|
|
27204
27289
|
if (terms.length === 0) return [];
|
|
27205
|
-
const index = loadOrBuildCodeContextIndex(options);
|
|
27290
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
27206
27291
|
const results = [];
|
|
27207
|
-
for (const file of
|
|
27292
|
+
for (const file of filteredFiles(index, options)) {
|
|
27208
27293
|
for (const symbol of file.symbols) {
|
|
27209
27294
|
const scored = scoreSymbol(symbol, terms);
|
|
27210
|
-
if (scored.score > 0)
|
|
27295
|
+
if (scored.score > 0) {
|
|
27296
|
+
results.push({
|
|
27297
|
+
symbol,
|
|
27298
|
+
score: scored.score,
|
|
27299
|
+
matches: scored.matches,
|
|
27300
|
+
filePath: symbol.filePath,
|
|
27301
|
+
language: symbol.language,
|
|
27302
|
+
content: symbol.text,
|
|
27303
|
+
startLine: symbol.startLine,
|
|
27304
|
+
endLine: symbol.endLine
|
|
27305
|
+
});
|
|
27306
|
+
}
|
|
27211
27307
|
}
|
|
27212
27308
|
}
|
|
27213
|
-
|
|
27309
|
+
const offset = Math.max(0, options.offset ?? 0);
|
|
27310
|
+
const limit = options.limit ?? 20;
|
|
27311
|
+
return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
27214
27312
|
}
|
|
27215
27313
|
function dependentsMap(index) {
|
|
27216
27314
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -27238,19 +27336,10 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
27238
27336
|
const index = loadOrBuildCodeContextIndex(options);
|
|
27239
27337
|
const matches = findSymbols(index, symbolName, options.limit ?? 20);
|
|
27240
27338
|
const dependents = dependentsMap(index);
|
|
27241
|
-
return {
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
|
|
27245
|
-
const file = index.files[symbol.filePath];
|
|
27246
|
-
return {
|
|
27247
|
-
symbol,
|
|
27248
|
-
imports: file.resolvedImports,
|
|
27249
|
-
dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(),
|
|
27250
|
-
relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20)
|
|
27251
|
-
};
|
|
27252
|
-
})
|
|
27253
|
-
};
|
|
27339
|
+
return { query: symbolName, matches, definitions: matches.map((symbol) => {
|
|
27340
|
+
const file = index.files[symbol.filePath];
|
|
27341
|
+
return { symbol, imports: file.resolvedImports, dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(), relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20) };
|
|
27342
|
+
}) };
|
|
27254
27343
|
}
|
|
27255
27344
|
function resolveTargetFile(index, input) {
|
|
27256
27345
|
if (input.filePath) {
|
|
@@ -27290,20 +27379,10 @@ function analyzeBlastRadius(input) {
|
|
|
27290
27379
|
const lower = file.toLowerCase();
|
|
27291
27380
|
return (lower.includes("test") || lower.includes("spec")) && (lower.includes(targetBase) || (symbolLower ? index.files[file].symbols.some((s) => s.text.toLowerCase().includes(symbolLower)) : false));
|
|
27292
27381
|
});
|
|
27293
|
-
for (const test of tests) {
|
|
27294
|
-
if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
|
|
27295
|
-
}
|
|
27382
|
+
for (const test of tests) if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
|
|
27296
27383
|
const impactedFiles = [...impacted.entries()].map(([filePath, value]) => ({ filePath, distance: value.distance, reason: value.reason })).sort((a, b) => a.distance - b.distance || a.filePath.localeCompare(b.filePath));
|
|
27297
27384
|
const nonTestImpacted = impactedFiles.filter((f) => !f.filePath.match(/(test|spec)\./i)).length;
|
|
27298
|
-
|
|
27299
|
-
return {
|
|
27300
|
-
target: target.target,
|
|
27301
|
-
targetFile: target.filePath,
|
|
27302
|
-
impactedFiles,
|
|
27303
|
-
tests,
|
|
27304
|
-
symbolsInTarget: index.files[target.filePath]?.symbols ?? [],
|
|
27305
|
-
riskLevel
|
|
27306
|
-
};
|
|
27385
|
+
return { target: target.target, targetFile: target.filePath, impactedFiles, tests, symbolsInTarget: index.files[target.filePath]?.symbols ?? [], riskLevel: nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low" };
|
|
27307
27386
|
}
|
|
27308
27387
|
function getCodeContextStats(options = {}) {
|
|
27309
27388
|
const index = loadOrBuildCodeContextIndex(options);
|
|
@@ -27311,9 +27390,14 @@ function getCodeContextStats(options = {}) {
|
|
|
27311
27390
|
projectRoot: index.projectRoot,
|
|
27312
27391
|
branch: index.branch,
|
|
27313
27392
|
indexedAt: index.indexedAt,
|
|
27393
|
+
indexAgeMs: Math.max(0, Date.now() - Date.parse(index.indexedAt)),
|
|
27314
27394
|
files: Object.keys(index.files).length,
|
|
27315
27395
|
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
27316
27396
|
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
27397
|
+
languageBreakdown: index.stats.languageBreakdown,
|
|
27398
|
+
rebuiltFiles: index.stats.rebuiltFiles,
|
|
27399
|
+
reusedFiles: index.stats.reusedFiles,
|
|
27400
|
+
skippedFiles: index.stats.skippedFiles,
|
|
27317
27401
|
indexPath: getCodeContextIndexPath(index.projectRoot)
|
|
27318
27402
|
};
|
|
27319
27403
|
}
|
|
@@ -27337,10 +27421,14 @@ function registerCodeContext(server2) {
|
|
|
27337
27421
|
file_path: z88.string().optional().describe("File path for blast_radius"),
|
|
27338
27422
|
force: z88.boolean().optional().describe("Force rebuild before answering"),
|
|
27339
27423
|
limit: z88.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
27424
|
+
offset: z88.coerce.number().int().min(0).optional().describe("Search pagination offset"),
|
|
27425
|
+
refresh_index: z88.boolean().optional().describe("Refresh/rebuild index before searching"),
|
|
27426
|
+
languages: z88.array(z88.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
|
|
27427
|
+
paths: z88.array(z88.string()).optional().describe("Path/glob filters"),
|
|
27340
27428
|
depth: z88.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
27341
27429
|
max_files: z88.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
|
|
27342
27430
|
}
|
|
27343
|
-
}, async ({ action, project_root, query, symbol, file_path, force, limit, depth, max_files }) => {
|
|
27431
|
+
}, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files }) => {
|
|
27344
27432
|
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
27345
27433
|
if (action === "index") {
|
|
27346
27434
|
const index = buildCodeContextIndex(opts);
|
|
@@ -27358,7 +27446,15 @@ function registerCodeContext(server2) {
|
|
|
27358
27446
|
}
|
|
27359
27447
|
if (action === "search") {
|
|
27360
27448
|
if (!query) return errorResult10('code_context action "search" requires query');
|
|
27361
|
-
return jsonResult({
|
|
27449
|
+
return jsonResult({
|
|
27450
|
+
query,
|
|
27451
|
+
limit: limit ?? 20,
|
|
27452
|
+
offset: offset ?? 0,
|
|
27453
|
+
refresh_index: refresh_index ?? false,
|
|
27454
|
+
languages: languages ?? [],
|
|
27455
|
+
paths: paths ?? [],
|
|
27456
|
+
results: searchCodeContext(query, { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths })
|
|
27457
|
+
});
|
|
27362
27458
|
}
|
|
27363
27459
|
if (action === "trace") {
|
|
27364
27460
|
if (!symbol) return errorResult10('code_context action "trace" requires symbol');
|