@askexenow/exe-os 0.9.69 → 0.9.71
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 +33 -0
- package/dist/bin/agentic-reflection-backfill.js +33 -0
- package/dist/bin/agentic-semantic-label.js +33 -0
- package/dist/bin/backfill-conversations.js +33 -0
- package/dist/bin/backfill-responses.js +33 -0
- package/dist/bin/backfill-vectors.js +33 -0
- package/dist/bin/bulk-sync-postgres.js +33 -0
- package/dist/bin/cleanup-stale-review-tasks.js +33 -0
- package/dist/bin/cli.js +1284 -178
- package/dist/bin/exe-agent.js +6 -0
- package/dist/bin/exe-assign.js +33 -0
- package/dist/bin/exe-boot.js +33 -0
- package/dist/bin/exe-call.js +6 -0
- package/dist/bin/exe-cloud.js +33 -0
- package/dist/bin/exe-dispatch.js +33 -0
- package/dist/bin/exe-doctor.js +33 -0
- package/dist/bin/exe-export-behaviors.js +33 -0
- package/dist/bin/exe-forget.js +33 -0
- package/dist/bin/exe-gateway.js +178 -110
- package/dist/bin/exe-heartbeat.js +33 -0
- package/dist/bin/exe-kill.js +33 -0
- package/dist/bin/exe-launch-agent.js +33 -0
- package/dist/bin/exe-new-employee.js +6 -0
- package/dist/bin/exe-pending-messages.js +33 -0
- package/dist/bin/exe-pending-notifications.js +33 -0
- package/dist/bin/exe-pending-reviews.js +33 -0
- package/dist/bin/exe-rename.js +40 -4
- package/dist/bin/exe-review.js +33 -0
- package/dist/bin/exe-search.js +33 -0
- package/dist/bin/exe-session-cleanup.js +33 -0
- package/dist/bin/exe-start-codex.js +33 -0
- package/dist/bin/exe-start-opencode.js +33 -0
- package/dist/bin/exe-status.js +33 -0
- package/dist/bin/exe-team.js +33 -0
- package/dist/bin/git-sweep.js +33 -0
- package/dist/bin/graph-backfill.js +177 -110
- package/dist/bin/graph-export.js +33 -0
- package/dist/bin/intercom-check.js +33 -0
- package/dist/bin/registry-proxy.js +207 -0
- package/dist/bin/scan-tasks.js +33 -0
- package/dist/bin/setup.js +33 -0
- package/dist/bin/shard-migrate.js +33 -0
- package/dist/bin/stack-update.js +128 -0
- package/dist/gateway/index.js +178 -110
- package/dist/hooks/bug-report-worker.js +33 -0
- package/dist/hooks/codex-stop-task-finalizer.js +33 -0
- package/dist/hooks/commit-complete.js +33 -0
- package/dist/hooks/error-recall.js +33 -0
- package/dist/hooks/ingest.js +33 -0
- package/dist/hooks/instructions-loaded.js +33 -0
- package/dist/hooks/notification.js +33 -0
- package/dist/hooks/post-compact.js +33 -0
- package/dist/hooks/post-tool-combined.js +698 -17
- package/dist/hooks/pre-compact.js +33 -0
- package/dist/hooks/pre-tool-use.js +33 -0
- package/dist/hooks/prompt-submit.js +314 -0
- package/dist/hooks/session-end.js +33 -0
- package/dist/hooks/session-start.js +33 -0
- package/dist/hooks/stop.js +279 -12
- package/dist/hooks/subagent-stop.js +33 -0
- package/dist/hooks/summary-worker.js +33 -0
- package/dist/index.js +178 -110
- package/dist/lib/cloud-sync.js +27 -0
- package/dist/lib/database.js +27 -0
- package/dist/lib/db.js +27 -0
- package/dist/lib/device-registry.js +27 -0
- package/dist/lib/employee-templates.js +6 -0
- package/dist/lib/exe-daemon.js +639 -259
- package/dist/lib/hybrid-search.js +33 -0
- package/dist/lib/registry-proxy.js +162 -0
- package/dist/lib/schedules.js +33 -0
- package/dist/lib/store.js +33 -0
- package/dist/mcp/server.js +561 -244
- package/dist/runtime/index.js +33 -0
- package/dist/tui/App.js +33 -0
- package/package.json +3 -2
- package/stack.release.json +6 -4
- package/stack.release.schema.json +89 -18
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -2847,6 +2847,33 @@ async function ensureSchema() {
|
|
|
2847
2847
|
CREATE INDEX IF NOT EXISTS idx_chat_history_session
|
|
2848
2848
|
ON chat_history(session_id, id);
|
|
2849
2849
|
`);
|
|
2850
|
+
await client.executeMultiple(`
|
|
2851
|
+
CREATE TABLE IF NOT EXISTS session_events (
|
|
2852
|
+
id TEXT PRIMARY KEY,
|
|
2853
|
+
agent_id TEXT NOT NULL,
|
|
2854
|
+
agent_role TEXT NOT NULL,
|
|
2855
|
+
session_id TEXT NOT NULL,
|
|
2856
|
+
session_scope TEXT,
|
|
2857
|
+
project_name TEXT NOT NULL,
|
|
2858
|
+
event_index INTEGER NOT NULL,
|
|
2859
|
+
event_type TEXT NOT NULL,
|
|
2860
|
+
tool_name TEXT,
|
|
2861
|
+
tool_use_id TEXT,
|
|
2862
|
+
content TEXT NOT NULL,
|
|
2863
|
+
payload_json TEXT,
|
|
2864
|
+
has_error INTEGER NOT NULL DEFAULT 0,
|
|
2865
|
+
created_at TEXT NOT NULL
|
|
2866
|
+
);
|
|
2867
|
+
|
|
2868
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_agent_time
|
|
2869
|
+
ON session_events(agent_id, created_at DESC);
|
|
2870
|
+
|
|
2871
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_session_index
|
|
2872
|
+
ON session_events(session_id, event_index);
|
|
2873
|
+
|
|
2874
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_scope_agent_time
|
|
2875
|
+
ON session_events(session_scope, agent_id, created_at DESC);
|
|
2876
|
+
`);
|
|
2850
2877
|
await client.executeMultiple(`
|
|
2851
2878
|
CREATE TABLE IF NOT EXISTS workspaces (
|
|
2852
2879
|
id TEXT PRIMARY KEY,
|
|
@@ -5790,6 +5817,12 @@ var init_platform_procedures = __esm({
|
|
|
5790
5817
|
priority: "p0",
|
|
5791
5818
|
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."
|
|
5792
5819
|
},
|
|
5820
|
+
{
|
|
5821
|
+
title: "Code context first for repository orientation",
|
|
5822
|
+
domain: "workflow",
|
|
5823
|
+
priority: "p1",
|
|
5824
|
+
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."
|
|
5825
|
+
},
|
|
5793
5826
|
{
|
|
5794
5827
|
title: "Commit discipline \u2014 never leave verified work floating",
|
|
5795
5828
|
domain: "workflow",
|
|
@@ -19020,13 +19053,25 @@ var init_export_graph = __esm({
|
|
|
19020
19053
|
|
|
19021
19054
|
// src/lib/code-chunker.ts
|
|
19022
19055
|
import ts from "typescript";
|
|
19056
|
+
function languageForFile(filePath) {
|
|
19057
|
+
const base = filePath.split(/[\\/]/).pop()?.toLowerCase() ?? "";
|
|
19058
|
+
if (["dockerfile", "makefile", "rakefile", "gemfile"].includes(base)) return base;
|
|
19059
|
+
const ext = base.includes(".") ? base.split(".").pop() : void 0;
|
|
19060
|
+
return ext ? LANGUAGE_BY_EXTENSION[ext] : void 0;
|
|
19061
|
+
}
|
|
19023
19062
|
function chunkSourceFile(source, fileName = "file.ts") {
|
|
19063
|
+
const language = languageForFile(fileName);
|
|
19064
|
+
if (language === "typescript" || language === "javascript") {
|
|
19065
|
+
return chunkTypeScriptLike(source, fileName);
|
|
19066
|
+
}
|
|
19067
|
+
return chunkGenericSource(source, fileName, language);
|
|
19068
|
+
}
|
|
19069
|
+
function chunkTypeScriptLike(source, fileName) {
|
|
19024
19070
|
const sourceFile = ts.createSourceFile(
|
|
19025
19071
|
fileName,
|
|
19026
19072
|
source,
|
|
19027
19073
|
ts.ScriptTarget.Latest,
|
|
19028
19074
|
true,
|
|
19029
|
-
// setParentNodes
|
|
19030
19075
|
fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
19031
19076
|
);
|
|
19032
19077
|
const chunks = [];
|
|
@@ -19048,144 +19093,117 @@ function chunkSourceFile(source, fileName = "file.ts") {
|
|
|
19048
19093
|
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
19049
19094
|
return node.name?.getText(sourceFile) ?? "(anonymous)";
|
|
19050
19095
|
}
|
|
19051
|
-
if (ts.isClassDeclaration(node))
|
|
19052
|
-
|
|
19053
|
-
|
|
19054
|
-
if (ts.
|
|
19055
|
-
|
|
19056
|
-
|
|
19057
|
-
if (ts.isTypeAliasDeclaration(node)) {
|
|
19058
|
-
return node.name.getText(sourceFile);
|
|
19059
|
-
}
|
|
19060
|
-
if (ts.isEnumDeclaration(node)) {
|
|
19061
|
-
return node.name.getText(sourceFile);
|
|
19062
|
-
}
|
|
19063
|
-
if (ts.isVariableStatement(node)) {
|
|
19064
|
-
const decls = node.declarationList.declarations;
|
|
19065
|
-
return decls.map((d) => d.name.getText(sourceFile)).join(", ");
|
|
19066
|
-
}
|
|
19067
|
-
if (ts.isExportAssignment(node)) {
|
|
19068
|
-
return "default export";
|
|
19069
|
-
}
|
|
19096
|
+
if (ts.isClassDeclaration(node)) return node.name?.getText(sourceFile) ?? "(anonymous class)";
|
|
19097
|
+
if (ts.isInterfaceDeclaration(node)) return node.name.getText(sourceFile);
|
|
19098
|
+
if (ts.isTypeAliasDeclaration(node)) return node.name.getText(sourceFile);
|
|
19099
|
+
if (ts.isEnumDeclaration(node)) return node.name.getText(sourceFile);
|
|
19100
|
+
if (ts.isVariableStatement(node)) return node.declarationList.declarations.map((d) => d.name.getText(sourceFile)).join(", ");
|
|
19101
|
+
if (ts.isExportAssignment(node)) return "default export";
|
|
19070
19102
|
return "(unknown)";
|
|
19071
19103
|
}
|
|
19072
19104
|
function visitTopLevel(node) {
|
|
19073
19105
|
if (ts.isImportDeclaration(node)) {
|
|
19074
|
-
importLines.push({
|
|
19075
|
-
start: getLineNumber(node.getStart(sourceFile)),
|
|
19076
|
-
end: getLineNumber(node.getEnd())
|
|
19077
|
-
});
|
|
19106
|
+
importLines.push({ start: getLineNumber(node.getStart(sourceFile)), end: getLineNumber(node.getEnd()) });
|
|
19078
19107
|
return;
|
|
19079
19108
|
}
|
|
19080
19109
|
if (ts.isFunctionDeclaration(node)) {
|
|
19081
|
-
chunks.push({
|
|
19082
|
-
kind: "function",
|
|
19083
|
-
name: getName(node),
|
|
19084
|
-
text: getNodeText(node),
|
|
19085
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19086
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19087
|
-
comment: getLeadingComment(node)
|
|
19088
|
-
});
|
|
19110
|
+
chunks.push({ kind: "function", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
19089
19111
|
return;
|
|
19090
19112
|
}
|
|
19091
19113
|
if (ts.isClassDeclaration(node)) {
|
|
19092
|
-
chunks.push({
|
|
19093
|
-
kind: "class",
|
|
19094
|
-
name: getName(node),
|
|
19095
|
-
text: getNodeText(node),
|
|
19096
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19097
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19098
|
-
comment: getLeadingComment(node)
|
|
19099
|
-
});
|
|
19114
|
+
chunks.push({ kind: "class", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
19100
19115
|
return;
|
|
19101
19116
|
}
|
|
19102
|
-
if (ts.isInterfaceDeclaration(node)) {
|
|
19103
|
-
chunks.push({
|
|
19104
|
-
kind: "type",
|
|
19105
|
-
name: getName(node),
|
|
19106
|
-
text: getNodeText(node),
|
|
19107
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19108
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19109
|
-
comment: getLeadingComment(node)
|
|
19110
|
-
});
|
|
19111
|
-
return;
|
|
19112
|
-
}
|
|
19113
|
-
if (ts.isTypeAliasDeclaration(node)) {
|
|
19114
|
-
chunks.push({
|
|
19115
|
-
kind: "type",
|
|
19116
|
-
name: getName(node),
|
|
19117
|
-
text: getNodeText(node),
|
|
19118
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19119
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19120
|
-
comment: getLeadingComment(node)
|
|
19121
|
-
});
|
|
19122
|
-
return;
|
|
19123
|
-
}
|
|
19124
|
-
if (ts.isEnumDeclaration(node)) {
|
|
19125
|
-
chunks.push({
|
|
19126
|
-
kind: "type",
|
|
19127
|
-
name: getName(node),
|
|
19128
|
-
text: getNodeText(node),
|
|
19129
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19130
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19131
|
-
comment: getLeadingComment(node)
|
|
19132
|
-
});
|
|
19117
|
+
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node)) {
|
|
19118
|
+
chunks.push({ kind: "type", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
19133
19119
|
return;
|
|
19134
19120
|
}
|
|
19135
19121
|
if (ts.isVariableStatement(node)) {
|
|
19136
|
-
const
|
|
19137
|
-
|
|
19138
|
-
(d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))
|
|
19139
|
-
);
|
|
19140
|
-
chunks.push({
|
|
19141
|
-
kind: isFnLike ? "function" : "variable",
|
|
19142
|
-
name: getName(node),
|
|
19143
|
-
text: getNodeText(node),
|
|
19144
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19145
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19146
|
-
comment: getLeadingComment(node)
|
|
19147
|
-
});
|
|
19122
|
+
const isFnLike = node.declarationList.declarations.some((d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer)));
|
|
19123
|
+
chunks.push({ kind: isFnLike ? "function" : "variable", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
19148
19124
|
return;
|
|
19149
19125
|
}
|
|
19150
19126
|
if (ts.isExportAssignment(node)) {
|
|
19151
|
-
chunks.push({
|
|
19152
|
-
kind: "export",
|
|
19153
|
-
name: "default export",
|
|
19154
|
-
text: getNodeText(node),
|
|
19155
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19156
|
-
endLine: getLineNumber(node.getEnd()),
|
|
19157
|
-
comment: getLeadingComment(node)
|
|
19158
|
-
});
|
|
19127
|
+
chunks.push({ kind: "export", name: "default export", text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
19159
19128
|
return;
|
|
19160
19129
|
}
|
|
19161
19130
|
if (ts.isExpressionStatement(node)) {
|
|
19162
19131
|
const text3 = getNodeText(node);
|
|
19163
|
-
if (text3.length > 10) {
|
|
19164
|
-
chunks.push({
|
|
19165
|
-
kind: "other",
|
|
19166
|
-
name: text3.slice(0, 40).replace(/\n/g, " "),
|
|
19167
|
-
text: text3,
|
|
19168
|
-
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
19169
|
-
endLine: getLineNumber(node.getEnd())
|
|
19170
|
-
});
|
|
19171
|
-
}
|
|
19172
|
-
return;
|
|
19132
|
+
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()) });
|
|
19173
19133
|
}
|
|
19174
19134
|
}
|
|
19175
19135
|
sourceFile.statements.forEach(visitTopLevel);
|
|
19176
19136
|
if (importLines.length > 0) {
|
|
19177
19137
|
const startLine = importLines[0].start;
|
|
19178
19138
|
const endLine = importLines[importLines.length - 1].end;
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19139
|
+
chunks.unshift({ kind: "import", name: `${importLines.length} imports`, text: lines.slice(startLine - 1, endLine).join("\n"), startLine, endLine });
|
|
19140
|
+
}
|
|
19141
|
+
chunks.sort((a, b) => a.startLine - b.startLine);
|
|
19142
|
+
return chunks.length > 0 ? chunks : chunkByWindows(source, 80);
|
|
19143
|
+
}
|
|
19144
|
+
function chunkGenericSource(source, _fileName, language) {
|
|
19145
|
+
const lines = source.split("\n");
|
|
19146
|
+
if (source.trim().length === 0) return [];
|
|
19147
|
+
if (language && TEXT_LIKE_LANGUAGES.has(language)) return chunkTextLike(lines, language);
|
|
19148
|
+
const chunks = [];
|
|
19149
|
+
const patterns = [
|
|
19150
|
+
{ kind: "function", regex: /^\s*(?:async\s+)?def\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
19151
|
+
{ kind: "class", regex: /^\s*class\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
19152
|
+
{ kind: "function", regex: /^\s*(?:pub\s+)?fn\s+([A-Za-z_][\w]*)\s*[<(]/, name: (m) => m[1] },
|
|
19153
|
+
{ kind: "class", regex: /^\s*(?:pub\s+)?(?:struct|enum|trait)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
19154
|
+
{ kind: "function", regex: /^\s*func\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
19155
|
+
{ 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] },
|
|
19156
|
+
{ kind: "function", regex: /^\s*function\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
19157
|
+
{ 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] },
|
|
19158
|
+
{ kind: "section", regex: /^\s{0,3}#{1,6}\s+(.+)$/, name: (m) => m[1].trim() }
|
|
19159
|
+
];
|
|
19160
|
+
const starts = [];
|
|
19161
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19162
|
+
const line = lines[i];
|
|
19163
|
+
for (const pattern of patterns) {
|
|
19164
|
+
const match = line.match(pattern.regex);
|
|
19165
|
+
if (match) {
|
|
19166
|
+
starts.push({ line: i + 1, kind: pattern.kind, name: pattern.name(match) });
|
|
19167
|
+
break;
|
|
19168
|
+
}
|
|
19169
|
+
}
|
|
19170
|
+
}
|
|
19171
|
+
if (starts.length === 0) return chunkByWindows(source, 80);
|
|
19172
|
+
for (let i = 0; i < starts.length; i++) {
|
|
19173
|
+
const start = starts[i];
|
|
19174
|
+
const next = starts[i + 1]?.line ?? lines.length + 1;
|
|
19175
|
+
const endLine = Math.max(start.line, next - 1);
|
|
19176
|
+
chunks.push({
|
|
19177
|
+
kind: start.kind,
|
|
19178
|
+
name: start.name,
|
|
19179
|
+
text: lines.slice(start.line - 1, endLine).join("\n"),
|
|
19180
|
+
startLine: start.line,
|
|
19185
19181
|
endLine
|
|
19186
19182
|
});
|
|
19187
19183
|
}
|
|
19188
|
-
chunks
|
|
19184
|
+
return chunks;
|
|
19185
|
+
}
|
|
19186
|
+
function chunkTextLike(lines, language) {
|
|
19187
|
+
if (language === "markdown" || language === "mdx") {
|
|
19188
|
+
const starts = lines.map((line, i) => ({ line, i })).filter(({ line }) => /^\s{0,3}#{1,6}\s+/.test(line));
|
|
19189
|
+
if (starts.length > 0) {
|
|
19190
|
+
return starts.map((start, idx) => {
|
|
19191
|
+
const end = starts[idx + 1]?.i ?? lines.length;
|
|
19192
|
+
const title = start.line.replace(/^\s{0,3}#{1,6}\s+/, "").trim();
|
|
19193
|
+
return { kind: "section", name: title, text: lines.slice(start.i, end).join("\n"), startLine: start.i + 1, endLine: end };
|
|
19194
|
+
});
|
|
19195
|
+
}
|
|
19196
|
+
}
|
|
19197
|
+
return chunkByWindows(lines.join("\n"), 80);
|
|
19198
|
+
}
|
|
19199
|
+
function chunkByWindows(source, windowLines) {
|
|
19200
|
+
const lines = source.split("\n");
|
|
19201
|
+
const chunks = [];
|
|
19202
|
+
for (let i = 0; i < lines.length; i += windowLines) {
|
|
19203
|
+
const end = Math.min(lines.length, i + windowLines);
|
|
19204
|
+
const text3 = lines.slice(i, end).join("\n");
|
|
19205
|
+
if (text3.trim()) chunks.push({ kind: "other", name: `lines ${i + 1}-${end}`, text: text3, startLine: i + 1, endLine: end });
|
|
19206
|
+
}
|
|
19189
19207
|
return chunks;
|
|
19190
19208
|
}
|
|
19191
19209
|
function summarizeChunk(chunk, filePath) {
|
|
@@ -19204,17 +19222,69 @@ function summarizeChunk(chunk, filePath) {
|
|
|
19204
19222
|
return `Variable ${chunk.name} in ${location}`;
|
|
19205
19223
|
case "export":
|
|
19206
19224
|
return `Default export in ${location}`;
|
|
19225
|
+
case "section":
|
|
19226
|
+
return `Section ${chunk.name} in ${location}`;
|
|
19207
19227
|
default:
|
|
19208
19228
|
return `${chunk.kind} in ${location}`;
|
|
19209
19229
|
}
|
|
19210
19230
|
}
|
|
19211
19231
|
function isChunkable(filePath) {
|
|
19212
|
-
|
|
19213
|
-
return ext === "ts" || ext === "tsx" || ext === "js" || ext === "jsx";
|
|
19232
|
+
return Boolean(languageForFile(filePath));
|
|
19214
19233
|
}
|
|
19234
|
+
var LANGUAGE_BY_EXTENSION, TEXT_LIKE_LANGUAGES;
|
|
19215
19235
|
var init_code_chunker = __esm({
|
|
19216
19236
|
"src/lib/code-chunker.ts"() {
|
|
19217
19237
|
"use strict";
|
|
19238
|
+
LANGUAGE_BY_EXTENSION = {
|
|
19239
|
+
c: "c",
|
|
19240
|
+
cc: "cpp",
|
|
19241
|
+
cpp: "cpp",
|
|
19242
|
+
cxx: "cpp",
|
|
19243
|
+
h: "c",
|
|
19244
|
+
hh: "cpp",
|
|
19245
|
+
hpp: "cpp",
|
|
19246
|
+
cs: "csharp",
|
|
19247
|
+
css: "css",
|
|
19248
|
+
scss: "scss",
|
|
19249
|
+
f: "fortran",
|
|
19250
|
+
f90: "fortran",
|
|
19251
|
+
f95: "fortran",
|
|
19252
|
+
go: "go",
|
|
19253
|
+
html: "html",
|
|
19254
|
+
htm: "html",
|
|
19255
|
+
java: "java",
|
|
19256
|
+
js: "javascript",
|
|
19257
|
+
jsx: "javascript",
|
|
19258
|
+
json: "json",
|
|
19259
|
+
kt: "kotlin",
|
|
19260
|
+
kts: "kotlin",
|
|
19261
|
+
lua: "lua",
|
|
19262
|
+
md: "markdown",
|
|
19263
|
+
mdx: "mdx",
|
|
19264
|
+
pas: "pascal",
|
|
19265
|
+
php: "php",
|
|
19266
|
+
py: "python",
|
|
19267
|
+
r: "r",
|
|
19268
|
+
rb: "ruby",
|
|
19269
|
+
rs: "rust",
|
|
19270
|
+
scala: "scala",
|
|
19271
|
+
sc: "scala",
|
|
19272
|
+
sol: "solidity",
|
|
19273
|
+
sql: "sql",
|
|
19274
|
+
svelte: "svelte",
|
|
19275
|
+
swift: "swift",
|
|
19276
|
+
toml: "toml",
|
|
19277
|
+
ts: "typescript",
|
|
19278
|
+
tsx: "typescript",
|
|
19279
|
+
vue: "vue",
|
|
19280
|
+
xml: "xml",
|
|
19281
|
+
yaml: "yaml",
|
|
19282
|
+
yml: "yaml",
|
|
19283
|
+
sh: "shell",
|
|
19284
|
+
bash: "shell",
|
|
19285
|
+
zsh: "shell"
|
|
19286
|
+
};
|
|
19287
|
+
TEXT_LIKE_LANGUAGES = /* @__PURE__ */ new Set(["json", "markdown", "mdx", "toml", "yaml", "xml", "html", "css", "scss", "sql"]);
|
|
19218
19288
|
}
|
|
19219
19289
|
});
|
|
19220
19290
|
|
|
@@ -29986,10 +30056,210 @@ var init_create_bug_report = __esm({
|
|
|
29986
30056
|
}
|
|
29987
30057
|
});
|
|
29988
30058
|
|
|
30059
|
+
// src/lib/session-events.ts
|
|
30060
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
30061
|
+
async function ensureSessionEventsTable(client) {
|
|
30062
|
+
await client.execute(`
|
|
30063
|
+
CREATE TABLE IF NOT EXISTS session_events (
|
|
30064
|
+
id TEXT PRIMARY KEY,
|
|
30065
|
+
agent_id TEXT NOT NULL,
|
|
30066
|
+
agent_role TEXT NOT NULL,
|
|
30067
|
+
session_id TEXT NOT NULL,
|
|
30068
|
+
session_scope TEXT,
|
|
30069
|
+
project_name TEXT NOT NULL,
|
|
30070
|
+
event_index INTEGER NOT NULL,
|
|
30071
|
+
event_type TEXT NOT NULL,
|
|
30072
|
+
tool_name TEXT,
|
|
30073
|
+
tool_use_id TEXT,
|
|
30074
|
+
content TEXT NOT NULL,
|
|
30075
|
+
payload_json TEXT,
|
|
30076
|
+
has_error INTEGER NOT NULL DEFAULT 0,
|
|
30077
|
+
created_at TEXT NOT NULL
|
|
30078
|
+
)
|
|
30079
|
+
`);
|
|
30080
|
+
await client.execute(`
|
|
30081
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_agent_time
|
|
30082
|
+
ON session_events(agent_id, created_at DESC)
|
|
30083
|
+
`);
|
|
30084
|
+
await client.execute(`
|
|
30085
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_session_index
|
|
30086
|
+
ON session_events(session_id, event_index)
|
|
30087
|
+
`);
|
|
30088
|
+
await client.execute(`
|
|
30089
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_scope_agent_time
|
|
30090
|
+
ON session_events(session_scope, agent_id, created_at DESC)
|
|
30091
|
+
`);
|
|
30092
|
+
}
|
|
30093
|
+
async function listRecentSessionEvents(client, options) {
|
|
30094
|
+
await ensureSessionEventsTable(client);
|
|
30095
|
+
const conditions = ["agent_id = ?"];
|
|
30096
|
+
const args = [options.agentId];
|
|
30097
|
+
if (options.sessionId) {
|
|
30098
|
+
conditions.push("session_id = ?");
|
|
30099
|
+
args.push(options.sessionId);
|
|
30100
|
+
}
|
|
30101
|
+
if (options.eventType) {
|
|
30102
|
+
conditions.push("event_type = ?");
|
|
30103
|
+
args.push(options.eventType);
|
|
30104
|
+
}
|
|
30105
|
+
if (options.projectName && options.projectName !== "all") {
|
|
30106
|
+
conditions.push("project_name = ?");
|
|
30107
|
+
args.push(options.projectName);
|
|
30108
|
+
}
|
|
30109
|
+
const scope = strictSessionScopeFilter(options.sessionScope);
|
|
30110
|
+
const where = `WHERE ${conditions.join(" AND ")}${scope.sql}`;
|
|
30111
|
+
args.push(...scope.args);
|
|
30112
|
+
args.push(Math.min(Math.max(options.limit ?? 20, 1), 100));
|
|
30113
|
+
const result2 = await client.execute({
|
|
30114
|
+
sql: `SELECT id, agent_id, agent_role, session_id, session_scope,
|
|
30115
|
+
project_name, event_index, event_type, tool_name, tool_use_id,
|
|
30116
|
+
content, payload_json, has_error, created_at
|
|
30117
|
+
FROM session_events
|
|
30118
|
+
${where}
|
|
30119
|
+
ORDER BY created_at DESC, event_index DESC
|
|
30120
|
+
LIMIT ?`,
|
|
30121
|
+
args
|
|
30122
|
+
});
|
|
30123
|
+
return result2.rows.map((row) => ({
|
|
30124
|
+
id: String(row.id),
|
|
30125
|
+
agentId: String(row.agent_id),
|
|
30126
|
+
agentRole: String(row.agent_role),
|
|
30127
|
+
sessionId: String(row.session_id),
|
|
30128
|
+
sessionScope: row.session_scope == null ? null : String(row.session_scope),
|
|
30129
|
+
projectName: String(row.project_name),
|
|
30130
|
+
eventIndex: Number(row.event_index),
|
|
30131
|
+
eventType: String(row.event_type),
|
|
30132
|
+
toolName: row.tool_name == null ? null : String(row.tool_name),
|
|
30133
|
+
toolUseId: row.tool_use_id == null ? null : String(row.tool_use_id),
|
|
30134
|
+
content: String(row.content),
|
|
30135
|
+
payloadJson: row.payload_json == null ? null : String(row.payload_json),
|
|
30136
|
+
hasError: Number(row.has_error) === 1,
|
|
30137
|
+
createdAt: String(row.created_at)
|
|
30138
|
+
}));
|
|
30139
|
+
}
|
|
30140
|
+
var init_session_events = __esm({
|
|
30141
|
+
"src/lib/session-events.ts"() {
|
|
30142
|
+
"use strict";
|
|
30143
|
+
init_task_scope();
|
|
30144
|
+
init_project_name();
|
|
30145
|
+
}
|
|
30146
|
+
});
|
|
30147
|
+
|
|
30148
|
+
// src/mcp/tools/get-session-events.ts
|
|
30149
|
+
import { z as z88 } from "zod";
|
|
30150
|
+
function canReadAgent(activeRole, activeAgent, requestedAgent) {
|
|
30151
|
+
return requestedAgent === activeAgent || activeRole === "COO" || activeRole === "CTO";
|
|
30152
|
+
}
|
|
30153
|
+
function formatEvent(event) {
|
|
30154
|
+
const header = [
|
|
30155
|
+
`[${event.createdAt}]`,
|
|
30156
|
+
`${event.agentId}`,
|
|
30157
|
+
`${event.eventType}`,
|
|
30158
|
+
`session=${event.sessionId}`,
|
|
30159
|
+
`#${event.eventIndex}`,
|
|
30160
|
+
event.toolName ? `tool=${event.toolName}` : "",
|
|
30161
|
+
event.hasError ? "ERROR" : ""
|
|
30162
|
+
].filter(Boolean).join(" ");
|
|
30163
|
+
return `${header}
|
|
30164
|
+
${event.content}`;
|
|
30165
|
+
}
|
|
30166
|
+
function registerGetSessionEvents(server) {
|
|
30167
|
+
server.registerTool(
|
|
30168
|
+
"get_session_events",
|
|
30169
|
+
{
|
|
30170
|
+
title: "Get Session Events",
|
|
30171
|
+
description: "Return exact recent chronological user prompts, assistant responses, and tool calls from the append-only session journal. Use this when asked what happened last, not semantic memory search.",
|
|
30172
|
+
inputSchema: {
|
|
30173
|
+
agent_id: z88.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
30174
|
+
session_id: z88.string().optional().describe("Optional exact runtime session id."),
|
|
30175
|
+
event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
|
|
30176
|
+
project_name: z88.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
|
|
30177
|
+
limit: z88.number().int().min(1).max(100).default(20).describe("Number of events to return.")
|
|
30178
|
+
}
|
|
30179
|
+
},
|
|
30180
|
+
async ({ agent_id, session_id, event_type, project_name, limit }) => {
|
|
30181
|
+
const active = getActiveAgent();
|
|
30182
|
+
const targetAgent = agent_id?.trim() || active.agentId;
|
|
30183
|
+
if (!canReadAgent(active.agentRole, active.agentId, targetAgent)) {
|
|
30184
|
+
return {
|
|
30185
|
+
content: [{ type: "text", text: "Permission denied: only COO/CTO may read another agent's session events." }],
|
|
30186
|
+
isError: true
|
|
30187
|
+
};
|
|
30188
|
+
}
|
|
30189
|
+
const client = await fastDbInit();
|
|
30190
|
+
const events = await listRecentSessionEvents(client, {
|
|
30191
|
+
agentId: targetAgent,
|
|
30192
|
+
sessionId: session_id,
|
|
30193
|
+
eventType: event_type,
|
|
30194
|
+
projectName: project_name,
|
|
30195
|
+
limit
|
|
30196
|
+
});
|
|
30197
|
+
if (events.length === 0) {
|
|
30198
|
+
return { content: [{ type: "text", text: "No session events found." }] };
|
|
30199
|
+
}
|
|
30200
|
+
return {
|
|
30201
|
+
content: [{ type: "text", text: events.map(formatEvent).join("\n\n---\n\n") }]
|
|
30202
|
+
};
|
|
30203
|
+
}
|
|
30204
|
+
);
|
|
30205
|
+
}
|
|
30206
|
+
function registerGetLastAssistantResponse(server) {
|
|
30207
|
+
server.registerTool(
|
|
30208
|
+
"get_last_assistant_response",
|
|
30209
|
+
{
|
|
30210
|
+
title: "Get Last Assistant Response",
|
|
30211
|
+
description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
|
|
30212
|
+
inputSchema: {
|
|
30213
|
+
agent_id: z88.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
30214
|
+
project_name: z88.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
|
|
30215
|
+
}
|
|
30216
|
+
},
|
|
30217
|
+
async ({ agent_id, project_name }) => {
|
|
30218
|
+
const active = getActiveAgent();
|
|
30219
|
+
const targetAgent = agent_id?.trim() || active.agentId;
|
|
30220
|
+
if (!canReadAgent(active.agentRole, active.agentId, targetAgent)) {
|
|
30221
|
+
return {
|
|
30222
|
+
content: [{ type: "text", text: "Permission denied: only COO/CTO may read another agent's last response." }],
|
|
30223
|
+
isError: true
|
|
30224
|
+
};
|
|
30225
|
+
}
|
|
30226
|
+
const client = await fastDbInit();
|
|
30227
|
+
const events = await listRecentSessionEvents(client, {
|
|
30228
|
+
agentId: targetAgent,
|
|
30229
|
+
eventType: "assistant_response",
|
|
30230
|
+
projectName: project_name,
|
|
30231
|
+
limit: 1
|
|
30232
|
+
});
|
|
30233
|
+
if (events.length === 0) {
|
|
30234
|
+
return { content: [{ type: "text", text: "No assistant response found in the session journal." }] };
|
|
30235
|
+
}
|
|
30236
|
+
return {
|
|
30237
|
+
content: [{ type: "text", text: formatEvent(events[0]) }]
|
|
30238
|
+
};
|
|
30239
|
+
}
|
|
30240
|
+
);
|
|
30241
|
+
}
|
|
30242
|
+
var EVENT_TYPE;
|
|
30243
|
+
var init_get_session_events = __esm({
|
|
30244
|
+
"src/mcp/tools/get-session-events.ts"() {
|
|
30245
|
+
"use strict";
|
|
30246
|
+
init_active_agent();
|
|
30247
|
+
init_fast_db_init();
|
|
30248
|
+
init_session_events();
|
|
30249
|
+
EVENT_TYPE = z88.enum([
|
|
30250
|
+
"user_prompt",
|
|
30251
|
+
"assistant_response",
|
|
30252
|
+
"tool_call",
|
|
30253
|
+
"tool_result",
|
|
30254
|
+
"system_event"
|
|
30255
|
+
]);
|
|
30256
|
+
}
|
|
30257
|
+
});
|
|
30258
|
+
|
|
29989
30259
|
// src/lib/code-context-index.ts
|
|
29990
30260
|
import crypto20 from "crypto";
|
|
29991
30261
|
import path54 from "path";
|
|
29992
|
-
import { existsSync as existsSync43, mkdirSync as mkdirSync21, readFileSync as readFileSync35, statSync as statSync10, writeFileSync as writeFileSync24 } from "fs";
|
|
30262
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync21, readFileSync as readFileSync35, readdirSync as readdirSync14, statSync as statSync10, writeFileSync as writeFileSync24 } from "fs";
|
|
29993
30263
|
import { spawnSync } from "child_process";
|
|
29994
30264
|
function normalizeProjectRoot(projectRoot) {
|
|
29995
30265
|
return path54.resolve(projectRoot || process.cwd());
|
|
@@ -30008,78 +30278,53 @@ function getCodeContextIndexPath(projectRoot) {
|
|
|
30008
30278
|
return path54.join(indexDir(), `${rootHash}.json`);
|
|
30009
30279
|
}
|
|
30010
30280
|
function currentBranch(projectRoot) {
|
|
30011
|
-
const result2 = spawnSync("git", ["branch", "--show-current"], {
|
|
30012
|
-
cwd: projectRoot,
|
|
30013
|
-
encoding: "utf8",
|
|
30014
|
-
timeout: 2e3
|
|
30015
|
-
});
|
|
30281
|
+
const result2 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
30016
30282
|
const branch = result2.status === 0 ? result2.stdout.trim() : "";
|
|
30017
30283
|
return branch || "detached-or-unknown";
|
|
30018
30284
|
}
|
|
30019
30285
|
function shouldIgnore(relPath) {
|
|
30020
|
-
const parts = relPath.split(
|
|
30286
|
+
const parts = relPath.split(/[\\/]/);
|
|
30021
30287
|
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
30022
30288
|
}
|
|
30023
|
-
function
|
|
30024
|
-
|
|
30289
|
+
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
30290
|
+
for (const entry of readdirSync14(dir, { withFileTypes: true })) {
|
|
30291
|
+
const abs = path54.join(dir, entry.name);
|
|
30292
|
+
const rel = path54.relative(projectRoot, abs).replaceAll(path54.sep, "/");
|
|
30293
|
+
if (shouldIgnore(rel)) continue;
|
|
30294
|
+
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
30295
|
+
else if (entry.isFile()) out.push(rel);
|
|
30296
|
+
}
|
|
30297
|
+
return out;
|
|
30025
30298
|
}
|
|
30026
30299
|
function listCodeFiles(projectRoot, maxFiles) {
|
|
30027
|
-
const git = spawnSync("git", ["ls-files",
|
|
30028
|
-
cwd: projectRoot,
|
|
30029
|
-
encoding: "utf8",
|
|
30030
|
-
timeout: 5e3,
|
|
30031
|
-
maxBuffer: 1024 * 1024 * 8
|
|
30032
|
-
});
|
|
30300
|
+
const git = spawnSync("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
30033
30301
|
let files = [];
|
|
30034
30302
|
if (git.status === 0 && git.stdout.trim()) {
|
|
30035
30303
|
files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
30036
30304
|
} else {
|
|
30037
|
-
const rg = spawnSync("rg", ["--files",
|
|
30038
|
-
|
|
30039
|
-
encoding: "utf8",
|
|
30040
|
-
timeout: 5e3,
|
|
30041
|
-
maxBuffer: 1024 * 1024 * 8
|
|
30042
|
-
});
|
|
30043
|
-
if (rg.status === 0 && rg.stdout.trim()) {
|
|
30044
|
-
files = rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
30045
|
-
}
|
|
30305
|
+
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
30306
|
+
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
30046
30307
|
}
|
|
30047
|
-
return files.filter((file) =>
|
|
30308
|
+
return files.map((file) => file.replaceAll(path54.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
30048
30309
|
}
|
|
30049
30310
|
function parseImportPaths2(importText) {
|
|
30050
30311
|
const paths = [];
|
|
30051
|
-
const
|
|
30052
|
-
|
|
30053
|
-
|
|
30054
|
-
|
|
30055
|
-
while ((match = bareRegex.exec(importText)) !== null) {
|
|
30056
|
-
if (!paths.includes(match[1])) paths.push(match[1]);
|
|
30057
|
-
}
|
|
30058
|
-
const requireRegex = /require\(["']([^"']+)["']\)/g;
|
|
30059
|
-
while ((match = requireRegex.exec(importText)) !== null) {
|
|
30060
|
-
if (!paths.includes(match[1])) paths.push(match[1]);
|
|
30312
|
+
const patterns = [/from\s+["']([^"']+)["']/g, /^import\s+["']([^"']+)["']/gm, /require\(["']([^"']+)["']\)/g, /use\s+([A-Za-z0-9_:]+)::/g, /#include\s+[<"]([^>"]+)[>"]/g];
|
|
30313
|
+
for (const regex of patterns) {
|
|
30314
|
+
let match;
|
|
30315
|
+
while ((match = regex.exec(importText)) !== null) if (!paths.includes(match[1])) paths.push(match[1]);
|
|
30061
30316
|
}
|
|
30062
30317
|
return paths;
|
|
30063
30318
|
}
|
|
30064
30319
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
30065
30320
|
if (!importPath.startsWith(".")) return null;
|
|
30066
30321
|
const base = path54.posix.normalize(path54.posix.join(path54.posix.dirname(fromFile.replaceAll(path54.sep, "/")), importPath));
|
|
30067
|
-
const withoutKnownExt = base.replace(/\.(?:
|
|
30068
|
-
const candidates = [
|
|
30069
|
-
|
|
30070
|
-
`${withoutKnownExt}
|
|
30071
|
-
|
|
30072
|
-
|
|
30073
|
-
`${withoutKnownExt}.jsx`,
|
|
30074
|
-
`${base}.ts`,
|
|
30075
|
-
`${base}.tsx`,
|
|
30076
|
-
`${base}.js`,
|
|
30077
|
-
`${base}.jsx`,
|
|
30078
|
-
path54.posix.join(base, "index.ts"),
|
|
30079
|
-
path54.posix.join(base, "index.tsx"),
|
|
30080
|
-
path54.posix.join(base, "index.js"),
|
|
30081
|
-
path54.posix.join(base, "index.jsx")
|
|
30082
|
-
];
|
|
30322
|
+
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
30323
|
+
const candidates = [base];
|
|
30324
|
+
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"]) {
|
|
30325
|
+
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
30326
|
+
}
|
|
30327
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path54.posix.join(base, indexName));
|
|
30083
30328
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
30084
30329
|
}
|
|
30085
30330
|
function symbolId(filePath, chunk) {
|
|
@@ -30105,39 +30350,32 @@ function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
|
30105
30350
|
try {
|
|
30106
30351
|
stat = statSync10(absPath);
|
|
30107
30352
|
} catch {
|
|
30108
|
-
return null;
|
|
30353
|
+
return { record: null, reused: false };
|
|
30109
30354
|
}
|
|
30110
|
-
if (!stat.isFile()) return null;
|
|
30355
|
+
if (!stat.isFile()) return { record: null, reused: false };
|
|
30356
|
+
const language = languageForFile(relPath);
|
|
30357
|
+
if (!language || !isChunkable(relPath)) return { record: null, reused: false };
|
|
30111
30358
|
const source = readFileSync35(absPath, "utf8");
|
|
30112
30359
|
const hash = hashText(source);
|
|
30113
|
-
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size) {
|
|
30114
|
-
return previous;
|
|
30360
|
+
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size && previous.language === language) {
|
|
30361
|
+
return { record: previous, reused: true };
|
|
30115
30362
|
}
|
|
30116
|
-
if (!isChunkable(relPath)) return null;
|
|
30117
30363
|
const chunks = chunkSourceFile(source, relPath);
|
|
30118
30364
|
const imports = chunks.filter((chunk) => chunk.kind === "import").flatMap((chunk) => parseImportPaths2(chunk.text));
|
|
30119
30365
|
const resolvedImports = imports.map((importPath) => resolveImport(relPath, importPath, allFiles)).filter((file) => Boolean(file));
|
|
30120
|
-
const symbols = chunks.filter((chunk) => chunk.
|
|
30366
|
+
const symbols = chunks.filter((chunk) => chunk.name !== "(unknown)").map((chunk) => ({
|
|
30121
30367
|
id: symbolId(relPath, chunk),
|
|
30122
30368
|
name: chunk.name,
|
|
30123
30369
|
kind: chunk.kind,
|
|
30124
30370
|
filePath: relPath,
|
|
30371
|
+
language,
|
|
30125
30372
|
startLine: chunk.startLine,
|
|
30126
30373
|
endLine: chunk.endLine,
|
|
30127
30374
|
summary: summarizeChunk(chunk, relPath),
|
|
30128
|
-
text: chunk.text.slice(0,
|
|
30375
|
+
text: chunk.text.slice(0, 8e3),
|
|
30129
30376
|
comment: chunk.comment
|
|
30130
30377
|
}));
|
|
30131
|
-
return {
|
|
30132
|
-
path: relPath,
|
|
30133
|
-
absPath,
|
|
30134
|
-
hash,
|
|
30135
|
-
mtimeMs: stat.mtimeMs,
|
|
30136
|
-
size: stat.size,
|
|
30137
|
-
imports,
|
|
30138
|
-
resolvedImports,
|
|
30139
|
-
symbols
|
|
30140
|
-
};
|
|
30378
|
+
return { record: { path: relPath, absPath, language, hash, mtimeMs: stat.mtimeMs, size: stat.size, imports, resolvedImports, symbols }, reused: false };
|
|
30141
30379
|
}
|
|
30142
30380
|
function buildCodeContextIndex(options = {}) {
|
|
30143
30381
|
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
@@ -30147,17 +30385,27 @@ function buildCodeContextIndex(options = {}) {
|
|
|
30147
30385
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
30148
30386
|
const allFiles = new Set(files.map((file) => file.replaceAll(path54.sep, "/")));
|
|
30149
30387
|
const fileRecords = {};
|
|
30388
|
+
let rebuiltFiles = 0;
|
|
30389
|
+
let reusedFiles = 0;
|
|
30390
|
+
let skippedFiles = 0;
|
|
30150
30391
|
for (const rel of files) {
|
|
30151
30392
|
const normalized = rel.replaceAll(path54.sep, "/");
|
|
30152
|
-
const record = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
30153
|
-
if (record)
|
|
30154
|
-
|
|
30393
|
+
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
30394
|
+
if (record) {
|
|
30395
|
+
fileRecords[normalized] = record;
|
|
30396
|
+
if (reused) reusedFiles++;
|
|
30397
|
+
else rebuiltFiles++;
|
|
30398
|
+
} else skippedFiles++;
|
|
30399
|
+
}
|
|
30400
|
+
const languageBreakdown = {};
|
|
30401
|
+
for (const file of Object.values(fileRecords)) languageBreakdown[file.language] = (languageBreakdown[file.language] ?? 0) + 1;
|
|
30155
30402
|
const index = {
|
|
30156
30403
|
version: INDEX_VERSION,
|
|
30157
30404
|
projectRoot,
|
|
30158
30405
|
rootHash: hashText(projectRoot).slice(0, 16),
|
|
30159
30406
|
branch,
|
|
30160
30407
|
indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30408
|
+
stats: { filesSeen: files.length, filesIndexed: Object.keys(fileRecords).length, rebuiltFiles, reusedFiles, skippedFiles, languageBreakdown },
|
|
30161
30409
|
files: fileRecords
|
|
30162
30410
|
};
|
|
30163
30411
|
saveIndex(index);
|
|
@@ -30185,13 +30433,60 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
30185
30433
|
}
|
|
30186
30434
|
return buildCodeContextIndex(options);
|
|
30187
30435
|
}
|
|
30436
|
+
function normalizeLanguage(language) {
|
|
30437
|
+
return language.toLowerCase().replace(/^c\+\+$/, "cpp").replace(/^c#$/, "csharp").replace(/^js$/, "javascript").replace(/^ts$/, "typescript");
|
|
30438
|
+
}
|
|
30188
30439
|
function tokenize(query) {
|
|
30189
|
-
|
|
30440
|
+
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);
|
|
30441
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
30442
|
+
for (const term of raw) {
|
|
30443
|
+
expanded.add(term);
|
|
30444
|
+
for (const part of term.split(/[_.$/-]+/)) if (part.length >= 2) expanded.add(part);
|
|
30445
|
+
const dashed = term.replace(/[_.$/]+/g, "-");
|
|
30446
|
+
if (dashed.length >= 2) expanded.add(dashed);
|
|
30447
|
+
if (!term.includes("-")) expanded.add(`${term}s`);
|
|
30448
|
+
if (term.endsWith("s") && term.length > 3) expanded.add(term.slice(0, -1));
|
|
30449
|
+
}
|
|
30450
|
+
return [...expanded];
|
|
30451
|
+
}
|
|
30452
|
+
function ngrams(terms) {
|
|
30453
|
+
const grams = [...terms];
|
|
30454
|
+
for (let i = 0; i < terms.length - 1; i++) grams.push(`${terms[i]} ${terms[i + 1]}`);
|
|
30455
|
+
return grams;
|
|
30456
|
+
}
|
|
30457
|
+
function globToRegex(pattern) {
|
|
30458
|
+
let out = "";
|
|
30459
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
30460
|
+
const ch = pattern[i];
|
|
30461
|
+
const next = pattern[i + 1];
|
|
30462
|
+
if (ch === "*" && next === "*") {
|
|
30463
|
+
out += ".*";
|
|
30464
|
+
i++;
|
|
30465
|
+
continue;
|
|
30466
|
+
}
|
|
30467
|
+
if (ch === "*") {
|
|
30468
|
+
out += "[^/]*";
|
|
30469
|
+
continue;
|
|
30470
|
+
}
|
|
30471
|
+
if (".+^${}()|[]\\".includes(ch)) out += `\\${ch}`;
|
|
30472
|
+
else out += ch;
|
|
30473
|
+
}
|
|
30474
|
+
return new RegExp(`^${out}$`);
|
|
30475
|
+
}
|
|
30476
|
+
function matchesPath(filePath, patterns) {
|
|
30477
|
+
if (!patterns || patterns.length === 0) return true;
|
|
30478
|
+
const normalized = filePath.replaceAll(path54.sep, "/");
|
|
30479
|
+
return patterns.some((pattern) => {
|
|
30480
|
+
const p = pattern.replaceAll(path54.sep, "/").replace(/^\.\//, "");
|
|
30481
|
+
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
30482
|
+
});
|
|
30190
30483
|
}
|
|
30191
30484
|
function scoreSymbol(symbol, terms) {
|
|
30485
|
+
const grams = ngrams(terms);
|
|
30192
30486
|
const haystacks = {
|
|
30193
30487
|
name: symbol.name.toLowerCase(),
|
|
30194
30488
|
path: symbol.filePath.toLowerCase(),
|
|
30489
|
+
language: symbol.language.toLowerCase(),
|
|
30195
30490
|
summary: symbol.summary.toLowerCase(),
|
|
30196
30491
|
text: symbol.text.toLowerCase()
|
|
30197
30492
|
};
|
|
@@ -30199,41 +30494,73 @@ function scoreSymbol(symbol, terms) {
|
|
|
30199
30494
|
const matches = [];
|
|
30200
30495
|
for (const term of terms) {
|
|
30201
30496
|
if (haystacks.name === term) {
|
|
30202
|
-
score +=
|
|
30497
|
+
score += 100;
|
|
30203
30498
|
matches.push(`name=${term}`);
|
|
30204
30499
|
continue;
|
|
30205
30500
|
}
|
|
30206
30501
|
if (haystacks.name.includes(term)) {
|
|
30207
|
-
score +=
|
|
30502
|
+
score += 45;
|
|
30208
30503
|
matches.push(`name~${term}`);
|
|
30209
30504
|
}
|
|
30210
30505
|
if (haystacks.path.includes(term)) {
|
|
30211
|
-
score +=
|
|
30506
|
+
score += 18;
|
|
30212
30507
|
matches.push(`path~${term}`);
|
|
30213
30508
|
}
|
|
30509
|
+
if (haystacks.language === term) {
|
|
30510
|
+
score += 18;
|
|
30511
|
+
matches.push(`language=${term}`);
|
|
30512
|
+
}
|
|
30214
30513
|
if (haystacks.summary.includes(term)) {
|
|
30215
|
-
score +=
|
|
30514
|
+
score += 16;
|
|
30216
30515
|
matches.push(`summary~${term}`);
|
|
30217
30516
|
}
|
|
30218
30517
|
if (haystacks.text.includes(term)) {
|
|
30219
|
-
score +=
|
|
30518
|
+
score += 5;
|
|
30220
30519
|
matches.push(`text~${term}`);
|
|
30221
30520
|
}
|
|
30222
30521
|
}
|
|
30522
|
+
for (const gram of grams.filter((g) => g.includes(" "))) {
|
|
30523
|
+
if (haystacks.text.includes(gram) || haystacks.summary.includes(gram)) {
|
|
30524
|
+
score += 20;
|
|
30525
|
+
matches.push(`phrase~${gram}`);
|
|
30526
|
+
}
|
|
30527
|
+
}
|
|
30528
|
+
const uniqueMatches = new Set(matches.map((m) => m.replace(/^[^=~]+[=~]/, ""))).size;
|
|
30529
|
+
score += uniqueMatches * 3;
|
|
30223
30530
|
return { score, matches };
|
|
30224
30531
|
}
|
|
30532
|
+
function filteredFiles(index, options = {}) {
|
|
30533
|
+
const languages = options.languages?.map(normalizeLanguage).filter(Boolean);
|
|
30534
|
+
return Object.values(index.files).filter((file) => {
|
|
30535
|
+
if (languages && languages.length > 0 && !languages.includes(normalizeLanguage(file.language))) return false;
|
|
30536
|
+
return matchesPath(file.path, options.paths);
|
|
30537
|
+
});
|
|
30538
|
+
}
|
|
30225
30539
|
function searchCodeContext(query, options = {}) {
|
|
30226
30540
|
const terms = tokenize(query);
|
|
30227
30541
|
if (terms.length === 0) return [];
|
|
30228
|
-
const index = loadOrBuildCodeContextIndex(options);
|
|
30542
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
30229
30543
|
const results = [];
|
|
30230
|
-
for (const file of
|
|
30544
|
+
for (const file of filteredFiles(index, options)) {
|
|
30231
30545
|
for (const symbol of file.symbols) {
|
|
30232
30546
|
const scored = scoreSymbol(symbol, terms);
|
|
30233
|
-
if (scored.score > 0)
|
|
30547
|
+
if (scored.score > 0) {
|
|
30548
|
+
results.push({
|
|
30549
|
+
symbol,
|
|
30550
|
+
score: scored.score,
|
|
30551
|
+
matches: scored.matches,
|
|
30552
|
+
filePath: symbol.filePath,
|
|
30553
|
+
language: symbol.language,
|
|
30554
|
+
content: symbol.text,
|
|
30555
|
+
startLine: symbol.startLine,
|
|
30556
|
+
endLine: symbol.endLine
|
|
30557
|
+
});
|
|
30558
|
+
}
|
|
30234
30559
|
}
|
|
30235
30560
|
}
|
|
30236
|
-
|
|
30561
|
+
const offset = Math.max(0, options.offset ?? 0);
|
|
30562
|
+
const limit = options.limit ?? 20;
|
|
30563
|
+
return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
30237
30564
|
}
|
|
30238
30565
|
function dependentsMap(index) {
|
|
30239
30566
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -30261,19 +30588,10 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
30261
30588
|
const index = loadOrBuildCodeContextIndex(options);
|
|
30262
30589
|
const matches = findSymbols(index, symbolName, options.limit ?? 20);
|
|
30263
30590
|
const dependents = dependentsMap(index);
|
|
30264
|
-
return {
|
|
30265
|
-
|
|
30266
|
-
|
|
30267
|
-
|
|
30268
|
-
const file = index.files[symbol.filePath];
|
|
30269
|
-
return {
|
|
30270
|
-
symbol,
|
|
30271
|
-
imports: file.resolvedImports,
|
|
30272
|
-
dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(),
|
|
30273
|
-
relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20)
|
|
30274
|
-
};
|
|
30275
|
-
})
|
|
30276
|
-
};
|
|
30591
|
+
return { query: symbolName, matches, definitions: matches.map((symbol) => {
|
|
30592
|
+
const file = index.files[symbol.filePath];
|
|
30593
|
+
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) };
|
|
30594
|
+
}) };
|
|
30277
30595
|
}
|
|
30278
30596
|
function resolveTargetFile(index, input) {
|
|
30279
30597
|
if (input.filePath) {
|
|
@@ -30313,20 +30631,10 @@ function analyzeBlastRadius(input) {
|
|
|
30313
30631
|
const lower = file.toLowerCase();
|
|
30314
30632
|
return (lower.includes("test") || lower.includes("spec")) && (lower.includes(targetBase) || (symbolLower ? index.files[file].symbols.some((s) => s.text.toLowerCase().includes(symbolLower)) : false));
|
|
30315
30633
|
});
|
|
30316
|
-
for (const test of tests) {
|
|
30317
|
-
if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
|
|
30318
|
-
}
|
|
30634
|
+
for (const test of tests) if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
|
|
30319
30635
|
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));
|
|
30320
30636
|
const nonTestImpacted = impactedFiles.filter((f) => !f.filePath.match(/(test|spec)\./i)).length;
|
|
30321
|
-
|
|
30322
|
-
return {
|
|
30323
|
-
target: target.target,
|
|
30324
|
-
targetFile: target.filePath,
|
|
30325
|
-
impactedFiles,
|
|
30326
|
-
tests,
|
|
30327
|
-
symbolsInTarget: index.files[target.filePath]?.symbols ?? [],
|
|
30328
|
-
riskLevel
|
|
30329
|
-
};
|
|
30637
|
+
return { target: target.target, targetFile: target.filePath, impactedFiles, tests, symbolsInTarget: index.files[target.filePath]?.symbols ?? [], riskLevel: nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low" };
|
|
30330
30638
|
}
|
|
30331
30639
|
function getCodeContextStats(options = {}) {
|
|
30332
30640
|
const index = loadOrBuildCodeContextIndex(options);
|
|
@@ -30334,27 +30642,31 @@ function getCodeContextStats(options = {}) {
|
|
|
30334
30642
|
projectRoot: index.projectRoot,
|
|
30335
30643
|
branch: index.branch,
|
|
30336
30644
|
indexedAt: index.indexedAt,
|
|
30645
|
+
indexAgeMs: Math.max(0, Date.now() - Date.parse(index.indexedAt)),
|
|
30337
30646
|
files: Object.keys(index.files).length,
|
|
30338
30647
|
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
30339
30648
|
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
30649
|
+
languageBreakdown: index.stats.languageBreakdown,
|
|
30650
|
+
rebuiltFiles: index.stats.rebuiltFiles,
|
|
30651
|
+
reusedFiles: index.stats.reusedFiles,
|
|
30652
|
+
skippedFiles: index.stats.skippedFiles,
|
|
30340
30653
|
indexPath: getCodeContextIndexPath(index.projectRoot)
|
|
30341
30654
|
};
|
|
30342
30655
|
}
|
|
30343
|
-
var INDEX_VERSION, DEFAULT_MAX_FILES,
|
|
30656
|
+
var INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS;
|
|
30344
30657
|
var init_code_context_index = __esm({
|
|
30345
30658
|
"src/lib/code-context-index.ts"() {
|
|
30346
30659
|
"use strict";
|
|
30347
30660
|
init_config();
|
|
30348
30661
|
init_code_chunker();
|
|
30349
|
-
INDEX_VERSION =
|
|
30350
|
-
DEFAULT_MAX_FILES =
|
|
30351
|
-
|
|
30352
|
-
IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build"]);
|
|
30662
|
+
INDEX_VERSION = 2;
|
|
30663
|
+
DEFAULT_MAX_FILES = 5e3;
|
|
30664
|
+
IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
30353
30665
|
}
|
|
30354
30666
|
});
|
|
30355
30667
|
|
|
30356
30668
|
// src/mcp/tools/code-context.ts
|
|
30357
|
-
import { z as
|
|
30669
|
+
import { z as z89 } from "zod";
|
|
30358
30670
|
function errorResult10(text3) {
|
|
30359
30671
|
return { content: [{ type: "text", text: text3 }], isError: true };
|
|
30360
30672
|
}
|
|
@@ -30366,17 +30678,21 @@ function registerCodeContext(server) {
|
|
|
30366
30678
|
title: "Code Context",
|
|
30367
30679
|
description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
|
|
30368
30680
|
inputSchema: {
|
|
30369
|
-
action:
|
|
30370
|
-
project_root:
|
|
30371
|
-
query:
|
|
30372
|
-
symbol:
|
|
30373
|
-
file_path:
|
|
30374
|
-
force:
|
|
30375
|
-
limit:
|
|
30376
|
-
|
|
30377
|
-
|
|
30378
|
-
|
|
30379
|
-
|
|
30681
|
+
action: z89.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
|
|
30682
|
+
project_root: z89.string().optional().describe("Repository root. Defaults to current working directory."),
|
|
30683
|
+
query: z89.string().optional().describe("Search query for action=search"),
|
|
30684
|
+
symbol: z89.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
|
|
30685
|
+
file_path: z89.string().optional().describe("File path for blast_radius"),
|
|
30686
|
+
force: z89.boolean().optional().describe("Force rebuild before answering"),
|
|
30687
|
+
limit: z89.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
30688
|
+
offset: z89.coerce.number().int().min(0).optional().describe("Search pagination offset"),
|
|
30689
|
+
refresh_index: z89.boolean().optional().describe("Refresh/rebuild index before searching"),
|
|
30690
|
+
languages: z89.array(z89.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
|
|
30691
|
+
paths: z89.array(z89.string()).optional().describe("Path/glob filters"),
|
|
30692
|
+
depth: z89.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
30693
|
+
max_files: z89.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
|
|
30694
|
+
}
|
|
30695
|
+
}, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files }) => {
|
|
30380
30696
|
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
30381
30697
|
if (action === "index") {
|
|
30382
30698
|
const index = buildCodeContextIndex(opts);
|
|
@@ -30394,7 +30710,15 @@ function registerCodeContext(server) {
|
|
|
30394
30710
|
}
|
|
30395
30711
|
if (action === "search") {
|
|
30396
30712
|
if (!query) return errorResult10('code_context action "search" requires query');
|
|
30397
|
-
return jsonResult({
|
|
30713
|
+
return jsonResult({
|
|
30714
|
+
query,
|
|
30715
|
+
limit: limit ?? 20,
|
|
30716
|
+
offset: offset ?? 0,
|
|
30717
|
+
refresh_index: refresh_index ?? false,
|
|
30718
|
+
languages: languages ?? [],
|
|
30719
|
+
paths: paths ?? [],
|
|
30720
|
+
results: searchCodeContext(query, { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths })
|
|
30721
|
+
});
|
|
30398
30722
|
}
|
|
30399
30723
|
if (action === "trace") {
|
|
30400
30724
|
if (!symbol) return errorResult10('code_context action "trace" requires symbol');
|
|
@@ -30417,7 +30741,7 @@ var init_code_context = __esm({
|
|
|
30417
30741
|
});
|
|
30418
30742
|
|
|
30419
30743
|
// src/mcp/tools/support-inbox.ts
|
|
30420
|
-
import { z as
|
|
30744
|
+
import { z as z90 } from "zod";
|
|
30421
30745
|
function adminToken() {
|
|
30422
30746
|
return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
30423
30747
|
}
|
|
@@ -30456,9 +30780,9 @@ function registerListBugReports(server) {
|
|
|
30456
30780
|
title: "List Bug Reports",
|
|
30457
30781
|
description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
|
|
30458
30782
|
inputSchema: {
|
|
30459
|
-
status:
|
|
30460
|
-
severity:
|
|
30461
|
-
limit:
|
|
30783
|
+
status: z90.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
|
|
30784
|
+
severity: z90.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
30785
|
+
limit: z90.number().int().min(1).max(100).default(25)
|
|
30462
30786
|
}
|
|
30463
30787
|
},
|
|
30464
30788
|
async ({ status, severity, limit }) => {
|
|
@@ -30477,7 +30801,7 @@ function registerGetBugReport(server) {
|
|
|
30477
30801
|
{
|
|
30478
30802
|
title: "Get Bug Report",
|
|
30479
30803
|
description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
|
|
30480
|
-
inputSchema: { id:
|
|
30804
|
+
inputSchema: { id: z90.string().min(8) }
|
|
30481
30805
|
},
|
|
30482
30806
|
async ({ id }) => {
|
|
30483
30807
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
|
|
@@ -30492,12 +30816,12 @@ function registerTriageBugReport(server) {
|
|
|
30492
30816
|
title: "Triage Bug Report",
|
|
30493
30817
|
description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
|
|
30494
30818
|
inputSchema: {
|
|
30495
|
-
id:
|
|
30819
|
+
id: z90.string().min(8),
|
|
30496
30820
|
status: STATUS.optional(),
|
|
30497
|
-
triage_notes:
|
|
30498
|
-
linked_task_id:
|
|
30499
|
-
linked_commit:
|
|
30500
|
-
fixed_version:
|
|
30821
|
+
triage_notes: z90.string().optional(),
|
|
30822
|
+
linked_task_id: z90.string().optional(),
|
|
30823
|
+
linked_commit: z90.string().optional(),
|
|
30824
|
+
fixed_version: z90.string().optional()
|
|
30501
30825
|
}
|
|
30502
30826
|
},
|
|
30503
30827
|
async ({ id, status, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
@@ -30514,7 +30838,7 @@ var init_support_inbox = __esm({
|
|
|
30514
30838
|
"src/mcp/tools/support-inbox.ts"() {
|
|
30515
30839
|
"use strict";
|
|
30516
30840
|
DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
30517
|
-
STATUS =
|
|
30841
|
+
STATUS = z90.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
|
|
30518
30842
|
}
|
|
30519
30843
|
});
|
|
30520
30844
|
|
|
@@ -30588,6 +30912,8 @@ var init_tool_gates = __esm({
|
|
|
30588
30912
|
registerStoreDecision: "core",
|
|
30589
30913
|
registerGetDecision: "core",
|
|
30590
30914
|
registerCreateBugReport: "core",
|
|
30915
|
+
registerGetSessionEvents: "core",
|
|
30916
|
+
registerGetLastAssistantResponse: "core",
|
|
30591
30917
|
registerCodeContext: "graph-read",
|
|
30592
30918
|
registerListBugReports: "admin",
|
|
30593
30919
|
registerGetBugReport: "admin",
|
|
@@ -30843,6 +31169,8 @@ function registerAllTools(server) {
|
|
|
30843
31169
|
gate("registerStoreDecision", registerStoreDecision);
|
|
30844
31170
|
gate("registerGetDecision", registerGetDecision);
|
|
30845
31171
|
gate("registerCreateBugReport", registerCreateBugReport);
|
|
31172
|
+
gate("registerGetSessionEvents", registerGetSessionEvents);
|
|
31173
|
+
gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
|
|
30846
31174
|
gate("registerCodeContext", registerCodeContext);
|
|
30847
31175
|
if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
|
|
30848
31176
|
gate("registerListBugReports", registerListBugReports);
|
|
@@ -30988,6 +31316,7 @@ var init_register_tools = __esm({
|
|
|
30988
31316
|
init_activate_license();
|
|
30989
31317
|
init_query_company_brain();
|
|
30990
31318
|
init_create_bug_report();
|
|
31319
|
+
init_get_session_events();
|
|
30991
31320
|
init_code_context();
|
|
30992
31321
|
init_support_inbox();
|
|
30993
31322
|
init_tool_gates();
|
|
@@ -32055,7 +32384,7 @@ init_daemon_auth();
|
|
|
32055
32384
|
import os24 from "os";
|
|
32056
32385
|
import net2 from "net";
|
|
32057
32386
|
import { createServer as createHttpServer } from "http";
|
|
32058
|
-
import { randomUUID as
|
|
32387
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
32059
32388
|
import { writeFileSync as writeFileSync28, unlinkSync as unlinkSync14, mkdirSync as mkdirSync24, existsSync as existsSync46, readFileSync as readFileSync39, chmodSync as chmodSync2 } from "fs";
|
|
32060
32389
|
import path59 from "path";
|
|
32061
32390
|
|
|
@@ -32139,6 +32468,7 @@ var DAEMON_TOKEN_ENV2 = "EXE_DAEMON_TOKEN";
|
|
|
32139
32468
|
var _context = null;
|
|
32140
32469
|
var _model = null;
|
|
32141
32470
|
var _llama = null;
|
|
32471
|
+
var _shuttingDown = false;
|
|
32142
32472
|
var _daemonToken = "";
|
|
32143
32473
|
var MAX_QUEUE_SIZE = 1e3;
|
|
32144
32474
|
var highQueue = [];
|
|
@@ -32240,6 +32570,8 @@ function checkIdle() {
|
|
|
32240
32570
|
}
|
|
32241
32571
|
}
|
|
32242
32572
|
async function shutdown() {
|
|
32573
|
+
if (_shuttingDown) return;
|
|
32574
|
+
_shuttingDown = true;
|
|
32243
32575
|
resetIdleTimer2();
|
|
32244
32576
|
flushToDisk();
|
|
32245
32577
|
try {
|
|
@@ -32252,6 +32584,19 @@ async function shutdown() {
|
|
|
32252
32584
|
disposeShards2();
|
|
32253
32585
|
} catch {
|
|
32254
32586
|
}
|
|
32587
|
+
if (process.platform === "darwin" && _llama) {
|
|
32588
|
+
try {
|
|
32589
|
+
unlinkSync14(SOCKET_PATH2);
|
|
32590
|
+
} catch {
|
|
32591
|
+
}
|
|
32592
|
+
try {
|
|
32593
|
+
unlinkSync14(PID_PATH4);
|
|
32594
|
+
} catch {
|
|
32595
|
+
}
|
|
32596
|
+
process.stderr.write("[exed] Shutdown complete (darwin fast native exit).\n");
|
|
32597
|
+
process.kill(process.pid, "SIGKILL");
|
|
32598
|
+
process.exit(0);
|
|
32599
|
+
}
|
|
32255
32600
|
if (_context) {
|
|
32256
32601
|
try {
|
|
32257
32602
|
await _context.dispose();
|
|
@@ -32266,7 +32611,13 @@ async function shutdown() {
|
|
|
32266
32611
|
}
|
|
32267
32612
|
_model = null;
|
|
32268
32613
|
}
|
|
32269
|
-
_llama
|
|
32614
|
+
if (_llama) {
|
|
32615
|
+
try {
|
|
32616
|
+
await _llama.dispose();
|
|
32617
|
+
} catch {
|
|
32618
|
+
}
|
|
32619
|
+
_llama = null;
|
|
32620
|
+
}
|
|
32270
32621
|
try {
|
|
32271
32622
|
unlinkSync14(SOCKET_PATH2);
|
|
32272
32623
|
} catch {
|
|
@@ -32423,7 +32774,7 @@ async function handleBatchWriteMemory(socket, requestId, entries) {
|
|
|
32423
32774
|
}
|
|
32424
32775
|
}
|
|
32425
32776
|
async function writeMemoryRecord(entry) {
|
|
32426
|
-
const id =
|
|
32777
|
+
const id = randomUUID10();
|
|
32427
32778
|
const now2 = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
32428
32779
|
const governed = governMemoryRecord({
|
|
32429
32780
|
id,
|
|
@@ -32745,16 +33096,22 @@ async function startMcpHttpServer() {
|
|
|
32745
33096
|
for (const [sid, lastSeen] of sessionLastSeen.entries()) {
|
|
32746
33097
|
if (now2 - lastSeen > MCP_SESSION_TTL_MS) closeMcpSession2(sid, "session_expired");
|
|
32747
33098
|
}
|
|
32748
|
-
},
|
|
33099
|
+
}, getJsonRpcId2 = function(body) {
|
|
33100
|
+
if (body && typeof body === "object" && !Array.isArray(body) && "id" in body) {
|
|
33101
|
+
const id = body.id;
|
|
33102
|
+
if (typeof id === "string" || typeof id === "number") return id;
|
|
33103
|
+
}
|
|
33104
|
+
return "unknown";
|
|
33105
|
+
}, sendJsonRpcError2 = function(res, status, message, id, extra) {
|
|
32749
33106
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
32750
33107
|
res.end(JSON.stringify({
|
|
32751
33108
|
jsonrpc: "2.0",
|
|
32752
33109
|
error: { code: -32e3, message },
|
|
32753
|
-
id
|
|
33110
|
+
id
|
|
32754
33111
|
}));
|
|
32755
33112
|
recordMcpHttpEvent({ level: "warn", message, status, ...extra });
|
|
32756
33113
|
};
|
|
32757
|
-
var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, sweepStaleMcpSessions = sweepStaleMcpSessions2, sendJsonRpcError = sendJsonRpcError2;
|
|
33114
|
+
var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, sweepStaleMcpSessions = sweepStaleMcpSessions2, getJsonRpcId = getJsonRpcId2, sendJsonRpcError = sendJsonRpcError2;
|
|
32758
33115
|
const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
|
|
32759
33116
|
const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
32760
33117
|
const { isInitializeRequest } = await import("@modelcontextprotocol/sdk/types.js");
|
|
@@ -32783,14 +33140,14 @@ async function startMcpHttpServer() {
|
|
|
32783
33140
|
const url = new URL(req.url || "/", `http://127.0.0.1:${MCP_HTTP_PORT}`);
|
|
32784
33141
|
const authHeader = req.headers.authorization;
|
|
32785
33142
|
if (!authHeader || authHeader !== `Bearer ${_daemonToken}`) {
|
|
32786
|
-
sendJsonRpcError2(res, 401, "Unauthorized: invalid or missing daemon token", {
|
|
33143
|
+
sendJsonRpcError2(res, 401, "Unauthorized: invalid or missing daemon token", "unknown", {
|
|
32787
33144
|
method: req.method,
|
|
32788
33145
|
path: url.pathname
|
|
32789
33146
|
});
|
|
32790
33147
|
return;
|
|
32791
33148
|
}
|
|
32792
33149
|
if (url.pathname !== "/mcp") {
|
|
32793
|
-
sendJsonRpcError2(res, 404, "Not Found: MCP endpoint is /mcp", {
|
|
33150
|
+
sendJsonRpcError2(res, 404, "Not Found: MCP endpoint is /mcp", "unknown", {
|
|
32794
33151
|
method: req.method,
|
|
32795
33152
|
path: url.pathname
|
|
32796
33153
|
});
|
|
@@ -32814,7 +33171,7 @@ async function startMcpHttpServer() {
|
|
|
32814
33171
|
});
|
|
32815
33172
|
parsedBody = body ? JSON.parse(body) : void 0;
|
|
32816
33173
|
} catch (err) {
|
|
32817
|
-
sendJsonRpcError2(res, 400, "Bad Request: invalid JSON body", {
|
|
33174
|
+
sendJsonRpcError2(res, 400, "Bad Request: invalid JSON body", "unknown", {
|
|
32818
33175
|
method: req.method,
|
|
32819
33176
|
agentId,
|
|
32820
33177
|
agentRole,
|
|
@@ -32830,7 +33187,7 @@ async function startMcpHttpServer() {
|
|
|
32830
33187
|
sessionLastSeen.set(sessionId, Date.now());
|
|
32831
33188
|
} else if (!sessionId && req.method === "POST" && isInitializeRequest(parsedBody)) {
|
|
32832
33189
|
transport = new StreamableHTTPServerTransport({
|
|
32833
|
-
sessionIdGenerator: () =>
|
|
33190
|
+
sessionIdGenerator: () => randomUUID10(),
|
|
32834
33191
|
onsessioninitialized: (sid) => {
|
|
32835
33192
|
transports.set(sid, transport);
|
|
32836
33193
|
sessionLastSeen.set(sid, Date.now());
|
|
@@ -32855,7 +33212,7 @@ async function startMcpHttpServer() {
|
|
|
32855
33212
|
await sessionMcp.connect(transport);
|
|
32856
33213
|
} else {
|
|
32857
33214
|
const message = sessionId ? "Bad Request: MCP session is stale or unknown; reconnect MCP client" : "Bad Request: missing MCP session; initialize before tool calls";
|
|
32858
|
-
sendJsonRpcError2(res, 400, message, {
|
|
33215
|
+
sendJsonRpcError2(res, 400, message, getJsonRpcId2(parsedBody), {
|
|
32859
33216
|
method: req.method,
|
|
32860
33217
|
hasSessionId: Boolean(sessionId),
|
|
32861
33218
|
sessionId,
|
|
@@ -32914,7 +33271,7 @@ async function startMcpHttpServer() {
|
|
|
32914
33271
|
error: err instanceof Error ? err.message : String(err)
|
|
32915
33272
|
});
|
|
32916
33273
|
if (!res.headersSent) {
|
|
32917
|
-
sendJsonRpcError2(res, 500, "Internal Server Error: MCP request failed", {
|
|
33274
|
+
sendJsonRpcError2(res, 500, "Internal Server Error: MCP request failed", getJsonRpcId2(parsedBody), {
|
|
32918
33275
|
method: req.method,
|
|
32919
33276
|
sessionId,
|
|
32920
33277
|
agentId,
|
|
@@ -33582,8 +33939,31 @@ function startBackgroundJobGuardrails() {
|
|
|
33582
33939
|
timer.unref();
|
|
33583
33940
|
process.stderr.write("[exed] Background job guardrails started (every 60s)\n");
|
|
33584
33941
|
}
|
|
33585
|
-
|
|
33586
|
-
process.
|
|
33942
|
+
function handleSignalShutdown() {
|
|
33943
|
+
if (process.platform === "darwin" && _llama) {
|
|
33944
|
+
if (_shuttingDown) return;
|
|
33945
|
+
_shuttingDown = true;
|
|
33946
|
+
resetIdleTimer2();
|
|
33947
|
+
try {
|
|
33948
|
+
flushToDisk();
|
|
33949
|
+
} catch {
|
|
33950
|
+
}
|
|
33951
|
+
try {
|
|
33952
|
+
unlinkSync14(SOCKET_PATH2);
|
|
33953
|
+
} catch {
|
|
33954
|
+
}
|
|
33955
|
+
try {
|
|
33956
|
+
unlinkSync14(PID_PATH4);
|
|
33957
|
+
} catch {
|
|
33958
|
+
}
|
|
33959
|
+
process.stderr.write("[exed] Shutdown complete (darwin signal fast native exit).\n");
|
|
33960
|
+
process.kill(process.pid, "SIGKILL");
|
|
33961
|
+
process.exit(0);
|
|
33962
|
+
}
|
|
33963
|
+
void shutdown();
|
|
33964
|
+
}
|
|
33965
|
+
process.on("SIGINT", handleSignalShutdown);
|
|
33966
|
+
process.on("SIGTERM", handleSignalShutdown);
|
|
33587
33967
|
function checkExistingDaemon() {
|
|
33588
33968
|
try {
|
|
33589
33969
|
if (!existsSync46(PID_PATH4)) return false;
|