@stackmemoryai/stackmemory 1.10.4 → 1.12.0
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/README.md +104 -23
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +185 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +230 -5
- package/dist/src/cli/commands/orchestrator.js +312 -24
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +177 -7
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/wiki.js +33 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/index.js +90 -29
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +44 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/core/cache/index.js +11 -0
- package/dist/src/core/cache/token-estimator.js +16 -0
- package/dist/src/core/context/frame-database.js +38 -30
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/{merge → cross-search}/index.js +6 -4
- package/dist/src/core/database/sqlite-adapter.js +0 -83
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +22 -2
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +66 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/trace/trace-event.js +4 -0
- package/dist/src/core/wiki/wiki-compiler.js +219 -0
- package/dist/src/daemon/daemon-config.js +7 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/unified-daemon.js +30 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/hooks/schemas.js +2 -0
- package/dist/src/integrations/claude-code/subagent-client.js +89 -0
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -59
- package/dist/src/integrations/mcp/server.js +425 -311
- package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
- package/dist/src/integrations/mcp/tool-definitions.js +98 -229
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
- package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
- package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
- package/dist/src/orchestrators/multimodal/determinism.js +243 -0
- package/dist/src/orchestrators/multimodal/harness.js +147 -77
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/utils/hook-installer.js +8 -8
- package/package.json +10 -1
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/templates/claude-hooks/doc-ingest.js +76 -0
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- /package/dist/src/core/{merge → cache}/types.js +0 -0
|
@@ -29,20 +29,24 @@ import { execSync } from "child_process";
|
|
|
29
29
|
import { FrameManager } from "../../core/context/index.js";
|
|
30
30
|
import { logger } from "../../core/monitoring/logger.js";
|
|
31
31
|
import { isFeatureEnabled } from "../../core/config/feature-flags.js";
|
|
32
|
+
import { ContentCache } from "../../core/cache/index.js";
|
|
33
|
+
import { TraceEventStore } from "../../core/trace/trace-event-store.js";
|
|
32
34
|
import { BrowserMCPIntegration } from "../../features/browser/browser-mcp.js";
|
|
33
35
|
import { TraceDetector } from "../../core/trace/trace-detector.js";
|
|
34
36
|
import { LLMContextRetrieval } from "../../core/retrieval/index.js";
|
|
35
37
|
import { DiscoveryHandlers } from "./handlers/discovery-handlers.js";
|
|
36
38
|
import { DiffMemHandlers } from "./handlers/diffmem-handlers.js";
|
|
37
39
|
import { GreptileHandlers } from "./handlers/greptile-handlers.js";
|
|
38
|
-
import {
|
|
39
|
-
import { TeamHandlers } from "./handlers/team-handlers.js";
|
|
40
|
-
import { SQLiteAdapter } from "../../core/database/sqlite-adapter.js";
|
|
40
|
+
import { CrossSearchHandlers } from "./handlers/cross-search-handlers.js";
|
|
41
41
|
import {
|
|
42
42
|
generateChronologicalDigest
|
|
43
43
|
} from "../../core/digest/chronological-digest.js";
|
|
44
44
|
import { fuzzyEdit } from "../../utils/fuzzy-edit.js";
|
|
45
45
|
import { v4 as uuidv4 } from "uuid";
|
|
46
|
+
import {
|
|
47
|
+
resolveToolAlias,
|
|
48
|
+
resolveParamAliases
|
|
49
|
+
} from "./tool-alias-registry.js";
|
|
46
50
|
import {
|
|
47
51
|
DEFAULT_PLANNER_MODEL,
|
|
48
52
|
DEFAULT_IMPLEMENTER,
|
|
@@ -59,6 +63,35 @@ function _getEnv(key, defaultValue) {
|
|
|
59
63
|
function _getOptionalEnv(key) {
|
|
60
64
|
return process.env[key];
|
|
61
65
|
}
|
|
66
|
+
const CACHEABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
67
|
+
"get_context",
|
|
68
|
+
"get_hot_stack",
|
|
69
|
+
"get_active_tasks",
|
|
70
|
+
"get_task_metrics",
|
|
71
|
+
"get_traces",
|
|
72
|
+
"get_trace_statistics",
|
|
73
|
+
"smart_context",
|
|
74
|
+
"get_summary",
|
|
75
|
+
"sm_discover",
|
|
76
|
+
"sm_related_files",
|
|
77
|
+
"sm_session_summary",
|
|
78
|
+
"sm_search",
|
|
79
|
+
"sm_cross_search",
|
|
80
|
+
"sm_cross_discover",
|
|
81
|
+
"sm_cross_list",
|
|
82
|
+
"diffmem_get_user_context",
|
|
83
|
+
"diffmem_search",
|
|
84
|
+
"diffmem_status",
|
|
85
|
+
"greptile_list_prs",
|
|
86
|
+
"greptile_pr_details",
|
|
87
|
+
"greptile_pr_comments",
|
|
88
|
+
"greptile_status",
|
|
89
|
+
"linear_get_tasks",
|
|
90
|
+
"linear_status",
|
|
91
|
+
"provenant_search",
|
|
92
|
+
"provenant_status",
|
|
93
|
+
"provenant_contradictions"
|
|
94
|
+
]);
|
|
62
95
|
class LocalStackMemoryMCP {
|
|
63
96
|
server;
|
|
64
97
|
db;
|
|
@@ -76,9 +109,14 @@ class LocalStackMemoryMCP {
|
|
|
76
109
|
diffMemHandlers;
|
|
77
110
|
greptileHandlers;
|
|
78
111
|
providerHandlers = null;
|
|
79
|
-
|
|
80
|
-
teamHandlers = null;
|
|
112
|
+
crossSearchHandlers;
|
|
81
113
|
pendingPlans = /* @__PURE__ */ new Map();
|
|
114
|
+
contentCache;
|
|
115
|
+
traceEventStore;
|
|
116
|
+
sessionId;
|
|
117
|
+
sessionTokensSaved = 0;
|
|
118
|
+
sessionCacheHits = 0;
|
|
119
|
+
sessionCacheMisses = 0;
|
|
82
120
|
constructor() {
|
|
83
121
|
this.projectRoot = this.findProjectRoot();
|
|
84
122
|
this.projectId = this.getProjectId();
|
|
@@ -120,6 +158,9 @@ class LocalStackMemoryMCP {
|
|
|
120
158
|
);
|
|
121
159
|
CREATE INDEX IF NOT EXISTS idx_edit_telemetry_ts ON edit_telemetry(timestamp);
|
|
122
160
|
`);
|
|
161
|
+
this.contentCache = new ContentCache(this.db);
|
|
162
|
+
this.traceEventStore = new TraceEventStore(this.db);
|
|
163
|
+
this.sessionId = uuidv4();
|
|
123
164
|
this.frameManager = new FrameManager(this.db, this.projectId);
|
|
124
165
|
this.initLinearIfEnabled();
|
|
125
166
|
this.server = new Server(
|
|
@@ -151,7 +192,7 @@ class LocalStackMemoryMCP {
|
|
|
151
192
|
});
|
|
152
193
|
this.diffMemHandlers = new DiffMemHandlers();
|
|
153
194
|
this.greptileHandlers = new GreptileHandlers();
|
|
154
|
-
this.
|
|
195
|
+
this.crossSearchHandlers = new CrossSearchHandlers({});
|
|
155
196
|
this.initProviderHandlers();
|
|
156
197
|
this.setupHandlers();
|
|
157
198
|
this.loadInitialContext();
|
|
@@ -166,6 +207,105 @@ class LocalStackMemoryMCP {
|
|
|
166
207
|
projectId: this.projectId
|
|
167
208
|
});
|
|
168
209
|
}
|
|
210
|
+
// ------------------------------------------------------------------
|
|
211
|
+
// Content-hash cache helpers
|
|
212
|
+
// ------------------------------------------------------------------
|
|
213
|
+
/**
|
|
214
|
+
* Build a deterministic cache key from tool name + sorted args.
|
|
215
|
+
* Returns null for non-cacheable tools or empty args.
|
|
216
|
+
*/
|
|
217
|
+
buildCacheKey(tool, args) {
|
|
218
|
+
if (!CACHEABLE_TOOLS.has(tool)) return null;
|
|
219
|
+
const sorted = JSON.stringify(args, Object.keys(args).sort());
|
|
220
|
+
return `${tool}:${sorted}`;
|
|
221
|
+
}
|
|
222
|
+
handleCacheStats() {
|
|
223
|
+
const stats = this.contentCache.getStats();
|
|
224
|
+
return {
|
|
225
|
+
content: [
|
|
226
|
+
{
|
|
227
|
+
type: "text",
|
|
228
|
+
text: JSON.stringify({
|
|
229
|
+
session: {
|
|
230
|
+
cacheHits: this.sessionCacheHits,
|
|
231
|
+
cacheMisses: this.sessionCacheMisses,
|
|
232
|
+
tokensSaved: this.sessionTokensSaved,
|
|
233
|
+
hitRate: this.sessionCacheHits + this.sessionCacheMisses > 0 ? this.sessionCacheHits / (this.sessionCacheHits + this.sessionCacheMisses) : 0
|
|
234
|
+
},
|
|
235
|
+
lifetime: stats
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
isError: false
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
handleCacheLookup(args) {
|
|
243
|
+
const content = String(args.content ?? "");
|
|
244
|
+
if (!content) {
|
|
245
|
+
return {
|
|
246
|
+
content: [
|
|
247
|
+
{
|
|
248
|
+
type: "text",
|
|
249
|
+
text: JSON.stringify({ error: "content is required" })
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
isError: true
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const result = this.contentCache.lookup(content, String(args.source ?? ""));
|
|
256
|
+
if (result.hit) {
|
|
257
|
+
this.sessionCacheHits++;
|
|
258
|
+
this.sessionTokensSaved += result.tokensSaved;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
262
|
+
isError: false
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// ------------------------------------------------------------------
|
|
266
|
+
// Trace event handlers
|
|
267
|
+
// ------------------------------------------------------------------
|
|
268
|
+
handleTraceEvents(args) {
|
|
269
|
+
const events = this.traceEventStore.query({
|
|
270
|
+
session_id: args.session_id,
|
|
271
|
+
operation: args.operation,
|
|
272
|
+
min_score: args.min_score,
|
|
273
|
+
has_feedback: args.has_feedback,
|
|
274
|
+
limit: args.limit ?? 50
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
278
|
+
isError: false
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
handleTraceEventStats(args) {
|
|
282
|
+
const stats = this.traceEventStore.getStats({
|
|
283
|
+
session_id: args.session_id
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
content: [{ type: "text", text: JSON.stringify(stats) }],
|
|
287
|
+
isError: false
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
handleTraceEventAnnotate(args) {
|
|
291
|
+
const id = String(args.id ?? "");
|
|
292
|
+
if (!id) {
|
|
293
|
+
return {
|
|
294
|
+
content: [
|
|
295
|
+
{ type: "text", text: JSON.stringify({ error: "id is required" }) }
|
|
296
|
+
],
|
|
297
|
+
isError: true
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const ok = this.traceEventStore.annotate(id, {
|
|
301
|
+
score: args.score,
|
|
302
|
+
feedback: args.feedback
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
content: [{ type: "text", text: JSON.stringify({ ok, id }) }],
|
|
306
|
+
isError: false
|
|
307
|
+
};
|
|
308
|
+
}
|
|
169
309
|
findProjectRoot() {
|
|
170
310
|
let dir = process.cwd();
|
|
171
311
|
while (dir !== "/") {
|
|
@@ -276,27 +416,6 @@ ${summary}...`, 0.8);
|
|
|
276
416
|
this.contexts.set(ctx.id, ctx);
|
|
277
417
|
});
|
|
278
418
|
}
|
|
279
|
-
async initCordTeamHandlers() {
|
|
280
|
-
try {
|
|
281
|
-
const dbPath = join(this.projectRoot, ".stackmemory", "context.db");
|
|
282
|
-
const adapter = new SQLiteAdapter(this.projectId, {
|
|
283
|
-
dbPath,
|
|
284
|
-
walMode: true
|
|
285
|
-
});
|
|
286
|
-
await adapter.connect();
|
|
287
|
-
this.cordHandlers = new CordHandlers({
|
|
288
|
-
frameManager: this.frameManager,
|
|
289
|
-
dbAdapter: adapter
|
|
290
|
-
});
|
|
291
|
-
this.teamHandlers = new TeamHandlers({
|
|
292
|
-
frameManager: this.frameManager,
|
|
293
|
-
dbAdapter: adapter
|
|
294
|
-
});
|
|
295
|
-
logger.info("Cord and Team handlers initialized");
|
|
296
|
-
} catch (error) {
|
|
297
|
-
logger.warn("Failed to initialize Cord/Team handlers", { error });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
419
|
async initProviderHandlers() {
|
|
301
420
|
if (!isFeatureEnabled("multiProvider")) return;
|
|
302
421
|
try {
|
|
@@ -369,6 +488,11 @@ ${summary}...`, 0.8);
|
|
|
369
488
|
description: "Which agent implements code"
|
|
370
489
|
},
|
|
371
490
|
maxIters: { type: "number", default: 2 },
|
|
491
|
+
verificationCommands: {
|
|
492
|
+
type: "array",
|
|
493
|
+
items: { type: "string" },
|
|
494
|
+
description: "Optional repro/test commands that must pass after implementation"
|
|
495
|
+
},
|
|
372
496
|
recordFrame: { type: "boolean", default: true },
|
|
373
497
|
execute: { type: "boolean", default: true }
|
|
374
498
|
},
|
|
@@ -972,190 +1096,6 @@ ${summary}...`, 0.8);
|
|
|
972
1096
|
},
|
|
973
1097
|
// DiffMem tools (only when DIFFMEM_ENDPOINT or DIFFMEM_ENABLED is set)
|
|
974
1098
|
...process.env.DIFFMEM_ENDPOINT || process.env.DIFFMEM_ENABLED === "true" ? this.diffMemHandlers.getToolDefinitions() : [],
|
|
975
|
-
// Cord task orchestration tools
|
|
976
|
-
{
|
|
977
|
-
name: "cord_spawn",
|
|
978
|
-
description: "Create a subtask with clean context (spawn). Child sees only its prompt and completed blocker results.",
|
|
979
|
-
inputSchema: {
|
|
980
|
-
type: "object",
|
|
981
|
-
properties: {
|
|
982
|
-
goal: {
|
|
983
|
-
type: "string",
|
|
984
|
-
description: "What this task should accomplish"
|
|
985
|
-
},
|
|
986
|
-
prompt: {
|
|
987
|
-
type: "string",
|
|
988
|
-
description: "Detailed instructions for the task"
|
|
989
|
-
},
|
|
990
|
-
blocked_by: {
|
|
991
|
-
type: "array",
|
|
992
|
-
items: { type: "string" },
|
|
993
|
-
description: "Task IDs that must complete first"
|
|
994
|
-
},
|
|
995
|
-
parent_id: { type: "string", description: "Parent task ID" }
|
|
996
|
-
},
|
|
997
|
-
required: ["goal"]
|
|
998
|
-
}
|
|
999
|
-
},
|
|
1000
|
-
{
|
|
1001
|
-
name: "cord_fork",
|
|
1002
|
-
description: "Create a subtask with full sibling context (fork). Child sees prompt, blocker results, AND completed sibling results.",
|
|
1003
|
-
inputSchema: {
|
|
1004
|
-
type: "object",
|
|
1005
|
-
properties: {
|
|
1006
|
-
goal: {
|
|
1007
|
-
type: "string",
|
|
1008
|
-
description: "What this task should accomplish"
|
|
1009
|
-
},
|
|
1010
|
-
prompt: {
|
|
1011
|
-
type: "string",
|
|
1012
|
-
description: "Detailed instructions for the task"
|
|
1013
|
-
},
|
|
1014
|
-
blocked_by: {
|
|
1015
|
-
type: "array",
|
|
1016
|
-
items: { type: "string" },
|
|
1017
|
-
description: "Task IDs that must complete first"
|
|
1018
|
-
},
|
|
1019
|
-
parent_id: { type: "string", description: "Parent task ID" }
|
|
1020
|
-
},
|
|
1021
|
-
required: ["goal"]
|
|
1022
|
-
}
|
|
1023
|
-
},
|
|
1024
|
-
{
|
|
1025
|
-
name: "cord_complete",
|
|
1026
|
-
description: "Mark a cord task as completed with a result. Automatically unblocks dependent tasks.",
|
|
1027
|
-
inputSchema: {
|
|
1028
|
-
type: "object",
|
|
1029
|
-
properties: {
|
|
1030
|
-
task_id: {
|
|
1031
|
-
type: "string",
|
|
1032
|
-
description: "Task ID to complete"
|
|
1033
|
-
},
|
|
1034
|
-
result: {
|
|
1035
|
-
type: "string",
|
|
1036
|
-
description: "The result/output of this task"
|
|
1037
|
-
}
|
|
1038
|
-
},
|
|
1039
|
-
required: ["task_id", "result"]
|
|
1040
|
-
}
|
|
1041
|
-
},
|
|
1042
|
-
{
|
|
1043
|
-
name: "cord_ask",
|
|
1044
|
-
description: "Create an ask task \u2014 a question that needs an answer before dependent tasks can proceed.",
|
|
1045
|
-
inputSchema: {
|
|
1046
|
-
type: "object",
|
|
1047
|
-
properties: {
|
|
1048
|
-
question: {
|
|
1049
|
-
type: "string",
|
|
1050
|
-
description: "The question to ask"
|
|
1051
|
-
},
|
|
1052
|
-
options: {
|
|
1053
|
-
type: "array",
|
|
1054
|
-
items: { type: "string" },
|
|
1055
|
-
description: "Optional list of answer choices"
|
|
1056
|
-
},
|
|
1057
|
-
parent_id: { type: "string", description: "Parent task ID" }
|
|
1058
|
-
},
|
|
1059
|
-
required: ["question"]
|
|
1060
|
-
}
|
|
1061
|
-
},
|
|
1062
|
-
{
|
|
1063
|
-
name: "cord_tree",
|
|
1064
|
-
description: "View the cord task tree with context scoping. Shows active, blocked, or completed tasks.",
|
|
1065
|
-
inputSchema: {
|
|
1066
|
-
type: "object",
|
|
1067
|
-
properties: {
|
|
1068
|
-
task_id: {
|
|
1069
|
-
type: "string",
|
|
1070
|
-
description: "Root task ID to show subtree (omit for full tree)"
|
|
1071
|
-
},
|
|
1072
|
-
include_results: {
|
|
1073
|
-
type: "boolean",
|
|
1074
|
-
default: false,
|
|
1075
|
-
description: "Include task results in output"
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
},
|
|
1080
|
-
// Team collaboration tools
|
|
1081
|
-
{
|
|
1082
|
-
name: "team_context_get",
|
|
1083
|
-
description: "Get context from other agents working on the same project. Returns recent frames and shared anchors.",
|
|
1084
|
-
inputSchema: {
|
|
1085
|
-
type: "object",
|
|
1086
|
-
properties: {
|
|
1087
|
-
limit: {
|
|
1088
|
-
type: "number",
|
|
1089
|
-
default: 10,
|
|
1090
|
-
description: "Max frames to return"
|
|
1091
|
-
},
|
|
1092
|
-
types: {
|
|
1093
|
-
type: "array",
|
|
1094
|
-
items: { type: "string" },
|
|
1095
|
-
description: "Filter by frame types"
|
|
1096
|
-
},
|
|
1097
|
-
since: {
|
|
1098
|
-
type: "number",
|
|
1099
|
-
description: "Only frames created after this timestamp (epoch ms)"
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
},
|
|
1104
|
-
{
|
|
1105
|
-
name: "team_context_share",
|
|
1106
|
-
description: "Share a piece of context with other agents. Creates a high-priority anchor visible to team_context_get.",
|
|
1107
|
-
inputSchema: {
|
|
1108
|
-
type: "object",
|
|
1109
|
-
properties: {
|
|
1110
|
-
content: {
|
|
1111
|
-
type: "string",
|
|
1112
|
-
description: "The context to share"
|
|
1113
|
-
},
|
|
1114
|
-
type: {
|
|
1115
|
-
type: "string",
|
|
1116
|
-
enum: [
|
|
1117
|
-
"FACT",
|
|
1118
|
-
"DECISION",
|
|
1119
|
-
"CONSTRAINT",
|
|
1120
|
-
"INTERFACE_CONTRACT",
|
|
1121
|
-
"TODO",
|
|
1122
|
-
"RISK"
|
|
1123
|
-
],
|
|
1124
|
-
default: "FACT",
|
|
1125
|
-
description: "Type of context"
|
|
1126
|
-
},
|
|
1127
|
-
priority: {
|
|
1128
|
-
type: "number",
|
|
1129
|
-
minimum: 1,
|
|
1130
|
-
maximum: 10,
|
|
1131
|
-
default: 8,
|
|
1132
|
-
description: "Priority level (1-10)"
|
|
1133
|
-
}
|
|
1134
|
-
},
|
|
1135
|
-
required: ["content"]
|
|
1136
|
-
}
|
|
1137
|
-
},
|
|
1138
|
-
{
|
|
1139
|
-
name: "team_search",
|
|
1140
|
-
description: "Search across all agents' context in the project. Uses full-text search across all sessions.",
|
|
1141
|
-
inputSchema: {
|
|
1142
|
-
type: "object",
|
|
1143
|
-
properties: {
|
|
1144
|
-
query: { type: "string", description: "Search query" },
|
|
1145
|
-
limit: {
|
|
1146
|
-
type: "number",
|
|
1147
|
-
default: 20,
|
|
1148
|
-
description: "Maximum results to return"
|
|
1149
|
-
},
|
|
1150
|
-
include_events: {
|
|
1151
|
-
type: "boolean",
|
|
1152
|
-
default: false,
|
|
1153
|
-
description: "Include events in results"
|
|
1154
|
-
}
|
|
1155
|
-
},
|
|
1156
|
-
required: ["query"]
|
|
1157
|
-
}
|
|
1158
|
-
},
|
|
1159
1099
|
// Greptile tools (only active when GREPTILE_API_KEY is set)
|
|
1160
1100
|
...process.env.GREPTILE_API_KEY ? this.greptileHandlers.getToolDefinitions() : [],
|
|
1161
1101
|
// Provider tools (only active when STACKMEMORY_MULTI_PROVIDER=true)
|
|
@@ -1274,6 +1214,143 @@ ${summary}...`, 0.8);
|
|
|
1274
1214
|
},
|
|
1275
1215
|
required: ["period"]
|
|
1276
1216
|
}
|
|
1217
|
+
},
|
|
1218
|
+
// Cross-project search tools
|
|
1219
|
+
{
|
|
1220
|
+
name: "sm_cross_search",
|
|
1221
|
+
description: "Search frames across all registered project databases using FTS5/BM25. Returns results ranked by relevance with source project attribution.",
|
|
1222
|
+
inputSchema: {
|
|
1223
|
+
type: "object",
|
|
1224
|
+
properties: {
|
|
1225
|
+
query: {
|
|
1226
|
+
type: "string",
|
|
1227
|
+
description: "Search query (natural language or keywords)"
|
|
1228
|
+
},
|
|
1229
|
+
limit: {
|
|
1230
|
+
type: "number",
|
|
1231
|
+
default: 20,
|
|
1232
|
+
description: "Maximum results to return"
|
|
1233
|
+
},
|
|
1234
|
+
exclude_current: {
|
|
1235
|
+
type: "boolean",
|
|
1236
|
+
default: false,
|
|
1237
|
+
description: "Exclude the current project from results"
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
required: ["query"]
|
|
1241
|
+
}
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
name: "sm_cross_discover",
|
|
1245
|
+
description: "Auto-discover project databases by scanning common directories for .stackmemory/context.db files.",
|
|
1246
|
+
inputSchema: {
|
|
1247
|
+
type: "object",
|
|
1248
|
+
properties: {
|
|
1249
|
+
paths: {
|
|
1250
|
+
type: "array",
|
|
1251
|
+
items: { type: "string" },
|
|
1252
|
+
description: "Custom directory paths to scan"
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
name: "sm_cross_register",
|
|
1259
|
+
description: "Manually register a project database for cross-project search.",
|
|
1260
|
+
inputSchema: {
|
|
1261
|
+
type: "object",
|
|
1262
|
+
properties: {
|
|
1263
|
+
name: {
|
|
1264
|
+
type: "string",
|
|
1265
|
+
description: "Project display name"
|
|
1266
|
+
},
|
|
1267
|
+
path: {
|
|
1268
|
+
type: "string",
|
|
1269
|
+
description: "Project root directory path"
|
|
1270
|
+
},
|
|
1271
|
+
db_path: {
|
|
1272
|
+
type: "string",
|
|
1273
|
+
description: "Path to the SQLite context.db file"
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
required: ["name", "path", "db_path"]
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
name: "sm_cross_list",
|
|
1281
|
+
description: "List all project databases registered for cross-project search.",
|
|
1282
|
+
inputSchema: {
|
|
1283
|
+
type: "object",
|
|
1284
|
+
properties: {}
|
|
1285
|
+
}
|
|
1286
|
+
},
|
|
1287
|
+
// Cache tools
|
|
1288
|
+
{
|
|
1289
|
+
name: "cache_stats",
|
|
1290
|
+
description: "Get content-hash cache statistics: session token savings, hit rate, and lifetime totals. Call this to see how many tokens have been saved by deduplication.",
|
|
1291
|
+
inputSchema: {
|
|
1292
|
+
type: "object",
|
|
1293
|
+
properties: {}
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
name: "cache_lookup",
|
|
1298
|
+
description: "Check if content is already cached. Returns hit/miss and token savings. Use for explicit deduplication before sending large content.",
|
|
1299
|
+
inputSchema: {
|
|
1300
|
+
type: "object",
|
|
1301
|
+
properties: {
|
|
1302
|
+
content: {
|
|
1303
|
+
type: "string",
|
|
1304
|
+
description: "The content to check against the cache."
|
|
1305
|
+
},
|
|
1306
|
+
source: {
|
|
1307
|
+
type: "string",
|
|
1308
|
+
description: 'Optional source label (e.g., "file:src/index.ts").'
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
required: ["content"]
|
|
1312
|
+
}
|
|
1313
|
+
},
|
|
1314
|
+
// Trace event tools
|
|
1315
|
+
{
|
|
1316
|
+
name: "trace_events",
|
|
1317
|
+
description: "Query ASI-shaped trace events. Filter by session, operation, min score, or feedback presence. Returns events with provenance, cost, and token data.",
|
|
1318
|
+
inputSchema: {
|
|
1319
|
+
type: "object",
|
|
1320
|
+
properties: {
|
|
1321
|
+
session_id: { type: "string" },
|
|
1322
|
+
operation: { type: "string" },
|
|
1323
|
+
min_score: { type: "number" },
|
|
1324
|
+
has_feedback: { type: "boolean" },
|
|
1325
|
+
limit: { type: "number" }
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
name: "trace_event_stats",
|
|
1331
|
+
description: "Get aggregate trace event statistics: total tokens, cost, operation counts, host distribution.",
|
|
1332
|
+
inputSchema: {
|
|
1333
|
+
type: "object",
|
|
1334
|
+
properties: {
|
|
1335
|
+
session_id: { type: "string" }
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
{
|
|
1340
|
+
name: "trace_event_annotate",
|
|
1341
|
+
description: "Add a numeric score and/or textual feedback to a trace event. Used by GEPA-class optimizers.",
|
|
1342
|
+
inputSchema: {
|
|
1343
|
+
type: "object",
|
|
1344
|
+
properties: {
|
|
1345
|
+
id: { type: "string", description: "Trace event ID" },
|
|
1346
|
+
score: { type: "number", description: "Numeric score (0-1)" },
|
|
1347
|
+
feedback: {
|
|
1348
|
+
type: "string",
|
|
1349
|
+
description: "Textual ASI feedback"
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
required: ["id"]
|
|
1353
|
+
}
|
|
1277
1354
|
}
|
|
1278
1355
|
]
|
|
1279
1356
|
};
|
|
@@ -1288,7 +1365,18 @@ ${summary}...`, 0.8);
|
|
|
1288
1365
|
})
|
|
1289
1366
|
}),
|
|
1290
1367
|
async (request) => {
|
|
1291
|
-
const { name, arguments:
|
|
1368
|
+
const { name: rawName, arguments: rawArgs } = request.params;
|
|
1369
|
+
const aliasResolution = resolveToolAlias(rawName);
|
|
1370
|
+
const name = aliasResolution.canonicalName;
|
|
1371
|
+
const paramResolution = resolveParamAliases(name, rawArgs);
|
|
1372
|
+
const args = paramResolution.resolvedParams;
|
|
1373
|
+
if (aliasResolution.wasAlias || Object.keys(paramResolution.renames).length > 0) {
|
|
1374
|
+
logger.debug("Tool alias resolved", {
|
|
1375
|
+
originalTool: rawName,
|
|
1376
|
+
canonicalTool: name,
|
|
1377
|
+
paramRenames: paramResolution.renames
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1292
1380
|
const callId = uuidv4();
|
|
1293
1381
|
const startTime = Date.now();
|
|
1294
1382
|
const currentFrameId = this.frameManager.getCurrentFrameId();
|
|
@@ -1296,7 +1384,8 @@ ${summary}...`, 0.8);
|
|
|
1296
1384
|
this.frameManager.addEvent("tool_call", {
|
|
1297
1385
|
tool_name: name,
|
|
1298
1386
|
arguments: args,
|
|
1299
|
-
timestamp: startTime
|
|
1387
|
+
timestamp: startTime,
|
|
1388
|
+
...aliasResolution.wasAlias ? { alias_from: rawName } : {}
|
|
1300
1389
|
});
|
|
1301
1390
|
}
|
|
1302
1391
|
const toolCall = {
|
|
@@ -1305,6 +1394,28 @@ ${summary}...`, 0.8);
|
|
|
1305
1394
|
arguments: args,
|
|
1306
1395
|
timestamp: startTime
|
|
1307
1396
|
};
|
|
1397
|
+
const cacheKey = this.buildCacheKey(name, args);
|
|
1398
|
+
if (cacheKey) {
|
|
1399
|
+
const cached = this.contentCache.lookupByKey(
|
|
1400
|
+
cacheKey,
|
|
1401
|
+
`tool:${name}`
|
|
1402
|
+
);
|
|
1403
|
+
if (cached.hit && cached.entry) {
|
|
1404
|
+
this.sessionCacheHits++;
|
|
1405
|
+
this.sessionTokensSaved += cached.tokensSaved;
|
|
1406
|
+
const total = this.sessionCacheHits + this.sessionCacheMisses;
|
|
1407
|
+
const rate = (this.sessionCacheHits / total * 100).toFixed(0);
|
|
1408
|
+
console.error(
|
|
1409
|
+
`[cache] HIT ${name} \u2014 saved ~${cached.tokensSaved} tokens (session: ${this.sessionTokensSaved.toLocaleString()} total, ${rate}% hit rate)`
|
|
1410
|
+
);
|
|
1411
|
+
const cachedResult = JSON.parse(cached.entry.content);
|
|
1412
|
+
toolCall.result = cachedResult;
|
|
1413
|
+
toolCall.duration = Date.now() - startTime;
|
|
1414
|
+
this.traceDetector.addToolCall(toolCall);
|
|
1415
|
+
return cachedResult;
|
|
1416
|
+
}
|
|
1417
|
+
this.sessionCacheMisses++;
|
|
1418
|
+
}
|
|
1308
1419
|
let result;
|
|
1309
1420
|
let error;
|
|
1310
1421
|
try {
|
|
@@ -1453,104 +1564,6 @@ ${summary}...`, 0.8);
|
|
|
1453
1564
|
case "sm_edit":
|
|
1454
1565
|
result = await this.handleSmEdit(args);
|
|
1455
1566
|
break;
|
|
1456
|
-
// Cord task orchestration
|
|
1457
|
-
case "cord_spawn":
|
|
1458
|
-
if (!this.cordHandlers) {
|
|
1459
|
-
result = {
|
|
1460
|
-
content: [
|
|
1461
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1462
|
-
],
|
|
1463
|
-
is_error: true
|
|
1464
|
-
};
|
|
1465
|
-
} else {
|
|
1466
|
-
result = await this.cordHandlers.handleCordSpawn(args);
|
|
1467
|
-
}
|
|
1468
|
-
break;
|
|
1469
|
-
case "cord_fork":
|
|
1470
|
-
if (!this.cordHandlers) {
|
|
1471
|
-
result = {
|
|
1472
|
-
content: [
|
|
1473
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1474
|
-
],
|
|
1475
|
-
is_error: true
|
|
1476
|
-
};
|
|
1477
|
-
} else {
|
|
1478
|
-
result = await this.cordHandlers.handleCordFork(args);
|
|
1479
|
-
}
|
|
1480
|
-
break;
|
|
1481
|
-
case "cord_complete":
|
|
1482
|
-
if (!this.cordHandlers) {
|
|
1483
|
-
result = {
|
|
1484
|
-
content: [
|
|
1485
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1486
|
-
],
|
|
1487
|
-
is_error: true
|
|
1488
|
-
};
|
|
1489
|
-
} else {
|
|
1490
|
-
result = await this.cordHandlers.handleCordComplete(args);
|
|
1491
|
-
}
|
|
1492
|
-
break;
|
|
1493
|
-
case "cord_ask":
|
|
1494
|
-
if (!this.cordHandlers) {
|
|
1495
|
-
result = {
|
|
1496
|
-
content: [
|
|
1497
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1498
|
-
],
|
|
1499
|
-
is_error: true
|
|
1500
|
-
};
|
|
1501
|
-
} else {
|
|
1502
|
-
result = await this.cordHandlers.handleCordAsk(args);
|
|
1503
|
-
}
|
|
1504
|
-
break;
|
|
1505
|
-
case "cord_tree":
|
|
1506
|
-
if (!this.cordHandlers) {
|
|
1507
|
-
result = {
|
|
1508
|
-
content: [
|
|
1509
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1510
|
-
],
|
|
1511
|
-
is_error: true
|
|
1512
|
-
};
|
|
1513
|
-
} else {
|
|
1514
|
-
result = await this.cordHandlers.handleCordTree(args);
|
|
1515
|
-
}
|
|
1516
|
-
break;
|
|
1517
|
-
// Team collaboration
|
|
1518
|
-
case "team_context_get":
|
|
1519
|
-
if (!this.teamHandlers) {
|
|
1520
|
-
result = {
|
|
1521
|
-
content: [
|
|
1522
|
-
{ type: "text", text: "Team handlers not initialized." }
|
|
1523
|
-
],
|
|
1524
|
-
is_error: true
|
|
1525
|
-
};
|
|
1526
|
-
} else {
|
|
1527
|
-
result = await this.teamHandlers.handleTeamContextGet(args);
|
|
1528
|
-
}
|
|
1529
|
-
break;
|
|
1530
|
-
case "team_context_share":
|
|
1531
|
-
if (!this.teamHandlers) {
|
|
1532
|
-
result = {
|
|
1533
|
-
content: [
|
|
1534
|
-
{ type: "text", text: "Team handlers not initialized." }
|
|
1535
|
-
],
|
|
1536
|
-
is_error: true
|
|
1537
|
-
};
|
|
1538
|
-
} else {
|
|
1539
|
-
result = await this.teamHandlers.handleTeamContextShare(args);
|
|
1540
|
-
}
|
|
1541
|
-
break;
|
|
1542
|
-
case "team_search":
|
|
1543
|
-
if (!this.teamHandlers) {
|
|
1544
|
-
result = {
|
|
1545
|
-
content: [
|
|
1546
|
-
{ type: "text", text: "Team handlers not initialized." }
|
|
1547
|
-
],
|
|
1548
|
-
is_error: true
|
|
1549
|
-
};
|
|
1550
|
-
} else {
|
|
1551
|
-
result = await this.teamHandlers.handleTeamSearch(args);
|
|
1552
|
-
}
|
|
1553
|
-
break;
|
|
1554
1567
|
// Provider tools
|
|
1555
1568
|
case "delegate_to_model":
|
|
1556
1569
|
if (!this.providerHandlers) {
|
|
@@ -1609,6 +1622,35 @@ ${summary}...`, 0.8);
|
|
|
1609
1622
|
case "sm_desire_paths":
|
|
1610
1623
|
result = this.handleDesirePaths(args);
|
|
1611
1624
|
break;
|
|
1625
|
+
case "sm_cross_search":
|
|
1626
|
+
result = await this.crossSearchHandlers.handleCrossSearch(args);
|
|
1627
|
+
break;
|
|
1628
|
+
case "sm_cross_discover":
|
|
1629
|
+
result = await this.crossSearchHandlers.handleCrossDiscover(args);
|
|
1630
|
+
break;
|
|
1631
|
+
case "sm_cross_register":
|
|
1632
|
+
result = await this.crossSearchHandlers.handleCrossRegister(args);
|
|
1633
|
+
break;
|
|
1634
|
+
case "sm_cross_list":
|
|
1635
|
+
result = await this.crossSearchHandlers.handleCrossList();
|
|
1636
|
+
break;
|
|
1637
|
+
// Cache tools
|
|
1638
|
+
case "cache_stats":
|
|
1639
|
+
result = this.handleCacheStats();
|
|
1640
|
+
break;
|
|
1641
|
+
case "cache_lookup":
|
|
1642
|
+
result = this.handleCacheLookup(args);
|
|
1643
|
+
break;
|
|
1644
|
+
// Trace event tools
|
|
1645
|
+
case "trace_events":
|
|
1646
|
+
result = this.handleTraceEvents(args);
|
|
1647
|
+
break;
|
|
1648
|
+
case "trace_event_stats":
|
|
1649
|
+
result = this.handleTraceEventStats(args);
|
|
1650
|
+
break;
|
|
1651
|
+
case "trace_event_annotate":
|
|
1652
|
+
result = this.handleTraceEventAnnotate(args);
|
|
1653
|
+
break;
|
|
1612
1654
|
default:
|
|
1613
1655
|
throw new Error(`Unknown tool: ${name}`);
|
|
1614
1656
|
}
|
|
@@ -1619,6 +1661,22 @@ ${summary}...`, 0.8);
|
|
|
1619
1661
|
throw err;
|
|
1620
1662
|
} finally {
|
|
1621
1663
|
const endTime = Date.now();
|
|
1664
|
+
if (!error && result && cacheKey) {
|
|
1665
|
+
try {
|
|
1666
|
+
const serialized = JSON.stringify(result);
|
|
1667
|
+
const entry = this.contentCache.putByKey(
|
|
1668
|
+
cacheKey,
|
|
1669
|
+
serialized,
|
|
1670
|
+
`tool:${name}`
|
|
1671
|
+
);
|
|
1672
|
+
if (entry.hitCount === 0) {
|
|
1673
|
+
console.error(
|
|
1674
|
+
`[cache] STORE ${name} \u2014 ~${entry.tokenCount} tokens cached for future dedup`
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
} catch {
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1622
1680
|
if (currentFrameId && name !== "close_frame") {
|
|
1623
1681
|
try {
|
|
1624
1682
|
this.frameManager.addEvent("tool_result", {
|
|
@@ -1641,11 +1699,50 @@ ${summary}...`, 0.8);
|
|
|
1641
1699
|
toolCall.filesAffected = Array.isArray(files) ? files : [files];
|
|
1642
1700
|
}
|
|
1643
1701
|
this.traceDetector.addToolCall(toolCall);
|
|
1702
|
+
try {
|
|
1703
|
+
const traceEvent = {
|
|
1704
|
+
timestamp: new Date(startTime).toISOString(),
|
|
1705
|
+
session_id: this.sessionId,
|
|
1706
|
+
trace_id: callId,
|
|
1707
|
+
tenant_id: "local",
|
|
1708
|
+
actor: {
|
|
1709
|
+
host: process.env["STACKMEMORY_HOST"] || "claude-code",
|
|
1710
|
+
agent: "stackmemory-mcp",
|
|
1711
|
+
user: process.env["USER"] || "anonymous"
|
|
1712
|
+
},
|
|
1713
|
+
operation: name,
|
|
1714
|
+
inputs: args,
|
|
1715
|
+
outputs: error ? { error: error.message } : result ?? {},
|
|
1716
|
+
tokens_in: 0,
|
|
1717
|
+
tokens_out: 0,
|
|
1718
|
+
cost_usd: 0,
|
|
1719
|
+
duration_ms: endTime - startTime,
|
|
1720
|
+
error: error?.message,
|
|
1721
|
+
provenance: {
|
|
1722
|
+
sources: [{ type: "tool", id: name }],
|
|
1723
|
+
derivation: ["mcp-call"],
|
|
1724
|
+
confidence: 1
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
this.traceEventStore.record(traceEvent);
|
|
1728
|
+
} catch {
|
|
1729
|
+
}
|
|
1644
1730
|
}
|
|
1645
1731
|
return result;
|
|
1646
1732
|
}
|
|
1647
1733
|
);
|
|
1648
1734
|
}
|
|
1735
|
+
getVerificationCommands(args) {
|
|
1736
|
+
const commands = args.verificationCommands ?? args.verifyCommands;
|
|
1737
|
+
if (Array.isArray(commands)) {
|
|
1738
|
+
return commands.map((command) => String(command).trim()).filter(Boolean);
|
|
1739
|
+
}
|
|
1740
|
+
const single = args.verificationCommand ?? args.verifyCommand;
|
|
1741
|
+
if (typeof single === "string" && single.trim()) {
|
|
1742
|
+
return [single.trim()];
|
|
1743
|
+
}
|
|
1744
|
+
return [];
|
|
1745
|
+
}
|
|
1649
1746
|
// Handle plan_and_code tool by invoking the mm harness
|
|
1650
1747
|
async handlePlanAndCode(args) {
|
|
1651
1748
|
const { runSpike } = await import("../../orchestrators/multimodal/harness.js");
|
|
@@ -1660,6 +1757,7 @@ ${summary}...`, 0.8);
|
|
|
1660
1757
|
const record = Boolean(args.record);
|
|
1661
1758
|
const recordFrame = Boolean(args.recordFrame);
|
|
1662
1759
|
const compact = Boolean(args.compact);
|
|
1760
|
+
const verificationCommands = this.getVerificationCommands(args);
|
|
1663
1761
|
const task = String(args.task || "Plan and implement change");
|
|
1664
1762
|
const result = await runSpike(
|
|
1665
1763
|
{
|
|
@@ -1673,6 +1771,7 @@ ${summary}...`, 0.8);
|
|
|
1673
1771
|
maxIters: isFinite(maxIters) ? Math.max(1, maxIters) : 2,
|
|
1674
1772
|
dryRun: !execute,
|
|
1675
1773
|
auditDir: void 0,
|
|
1774
|
+
verificationCommands,
|
|
1676
1775
|
recordFrame
|
|
1677
1776
|
}
|
|
1678
1777
|
);
|
|
@@ -1833,6 +1932,7 @@ ${summary}...`, 0.8);
|
|
|
1833
1932
|
);
|
|
1834
1933
|
const recordFrame = args.recordFrame !== false;
|
|
1835
1934
|
const execute = args.execute !== false;
|
|
1935
|
+
const verificationCommands = this.getVerificationCommands(args);
|
|
1836
1936
|
const result = await runSpike(
|
|
1837
1937
|
{ task: pending.task, repoPath: this.projectRoot },
|
|
1838
1938
|
{
|
|
@@ -1841,6 +1941,7 @@ ${summary}...`, 0.8);
|
|
|
1841
1941
|
implementer: implementer === "claude" ? "claude" : "codex",
|
|
1842
1942
|
maxIters: isFinite(maxIters) ? Math.max(1, maxIters) : 2,
|
|
1843
1943
|
dryRun: !execute,
|
|
1944
|
+
verificationCommands,
|
|
1844
1945
|
recordFrame
|
|
1845
1946
|
}
|
|
1846
1947
|
);
|
|
@@ -3341,6 +3442,19 @@ ${catLines.join("\n")}`
|
|
|
3341
3442
|
const transport = new StdioServerTransport();
|
|
3342
3443
|
await this.server.connect(transport);
|
|
3343
3444
|
console.error("StackMemory MCP Server started");
|
|
3445
|
+
const printCacheSummary = () => {
|
|
3446
|
+
if (this.sessionCacheHits > 0 || this.sessionCacheMisses > 0) {
|
|
3447
|
+
const total = this.sessionCacheHits + this.sessionCacheMisses;
|
|
3448
|
+
const rate = (this.sessionCacheHits / total * 100).toFixed(1);
|
|
3449
|
+
console.error(
|
|
3450
|
+
`
|
|
3451
|
+
\u{1F4CA} Cache: ${this.sessionCacheHits}/${total} hits (${rate}%), ~${this.sessionTokensSaved.toLocaleString()} tokens saved`
|
|
3452
|
+
);
|
|
3453
|
+
}
|
|
3454
|
+
};
|
|
3455
|
+
process.on("SIGINT", printCacheSummary);
|
|
3456
|
+
process.on("SIGTERM", printCacheSummary);
|
|
3457
|
+
process.on("exit", printCacheSummary);
|
|
3344
3458
|
}
|
|
3345
3459
|
}
|
|
3346
3460
|
var server_default = LocalStackMemoryMCP;
|