@stackmemoryai/stackmemory 1.2.7 → 1.2.9
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 +3 -3
- package/dist/src/cli/commands/daemon.js +306 -2
- package/dist/src/cli/commands/desires.js +117 -0
- package/dist/src/cli/commands/digest.js +73 -0
- package/dist/src/cli/commands/setup.js +369 -39
- package/dist/src/cli/commands/team.js +168 -0
- package/dist/src/cli/index.js +6 -0
- package/dist/src/core/digest/chronological-digest.js +143 -0
- package/dist/src/core/digest/index.js +1 -0
- package/dist/src/integrations/mcp/server.js +622 -251
- package/dist/src/integrations/mcp/tool-definitions.js +607 -50
- package/dist/src/utils/hook-installer.js +35 -0
- package/package.json +2 -2
- package/scripts/install-claude-hooks-auto.js +35 -0
- package/templates/claude-hooks/daemon-auto-start.js +125 -0
- package/templates/claude-hooks/desire-path-trace.js +118 -0
- package/templates/claude-hooks/session-rescue.sh +3 -0
- package/templates/claude-hooks/team-subagent-stop.js +77 -0
- package/templates/claude-hooks/team-task-complete.js +83 -0
- package/templates/claude-hooks/team-teammate-idle.js +96 -0
- /package/templates/claude-hooks/{auto-background-hook.js → archive/auto-background-hook.js} +0 -0
- /package/templates/claude-hooks/{hook-config.json → archive/hook-config.json} +0 -0
- /package/templates/claude-hooks/{hooks.json → archive/hooks.json} +0 -0
- /package/templates/claude-hooks/{on-compact-detected → archive/on-compact-detected} +0 -0
- /package/templates/claude-hooks/{on-exit → archive/on-exit} +0 -0
- /package/templates/claude-hooks/{post-edit-sweep.js → archive/post-edit-sweep.js} +0 -0
- /package/templates/claude-hooks/{pre-tool-use → archive/pre-tool-use} +0 -0
|
@@ -13,7 +13,15 @@ import {
|
|
|
13
13
|
AddAnchorSchema,
|
|
14
14
|
CreateTaskSchema
|
|
15
15
|
} from "./schemas.js";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
readFileSync,
|
|
18
|
+
readdirSync,
|
|
19
|
+
existsSync,
|
|
20
|
+
mkdirSync,
|
|
21
|
+
writeFileSync,
|
|
22
|
+
appendFileSync
|
|
23
|
+
} from "fs";
|
|
24
|
+
import { homedir } from "os";
|
|
17
25
|
import { compactPlan } from "../../orchestrators/multimodal/utils.js";
|
|
18
26
|
import { filterPending } from "./pending-utils.js";
|
|
19
27
|
import { join, dirname } from "path";
|
|
@@ -31,6 +39,12 @@ import { LLMContextRetrieval } from "../../core/retrieval/index.js";
|
|
|
31
39
|
import { DiscoveryHandlers } from "./handlers/discovery-handlers.js";
|
|
32
40
|
import { DiffMemHandlers } from "./handlers/diffmem-handlers.js";
|
|
33
41
|
import { GreptileHandlers } from "./handlers/greptile-handlers.js";
|
|
42
|
+
import { CordHandlers } from "./handlers/cord-handlers.js";
|
|
43
|
+
import { TeamHandlers } from "./handlers/team-handlers.js";
|
|
44
|
+
import { SQLiteAdapter } from "../../core/database/sqlite-adapter.js";
|
|
45
|
+
import {
|
|
46
|
+
generateChronologicalDigest
|
|
47
|
+
} from "../../core/digest/chronological-digest.js";
|
|
34
48
|
import { fuzzyEdit } from "../../utils/fuzzy-edit.js";
|
|
35
49
|
import { v4 as uuidv4 } from "uuid";
|
|
36
50
|
import {
|
|
@@ -66,6 +80,8 @@ class LocalStackMemoryMCP {
|
|
|
66
80
|
diffMemHandlers;
|
|
67
81
|
greptileHandlers;
|
|
68
82
|
providerHandlers = null;
|
|
83
|
+
cordHandlers = null;
|
|
84
|
+
teamHandlers = null;
|
|
69
85
|
pendingPlans = /* @__PURE__ */ new Map();
|
|
70
86
|
constructor() {
|
|
71
87
|
this.projectRoot = this.findProjectRoot();
|
|
@@ -139,6 +155,7 @@ class LocalStackMemoryMCP {
|
|
|
139
155
|
});
|
|
140
156
|
this.diffMemHandlers = new DiffMemHandlers();
|
|
141
157
|
this.greptileHandlers = new GreptileHandlers();
|
|
158
|
+
this.initCordTeamHandlers();
|
|
142
159
|
this.initProviderHandlers();
|
|
143
160
|
this.setupHandlers();
|
|
144
161
|
this.loadInitialContext();
|
|
@@ -261,6 +278,27 @@ ${summary}...`, 0.8);
|
|
|
261
278
|
this.contexts.set(ctx.id, ctx);
|
|
262
279
|
});
|
|
263
280
|
}
|
|
281
|
+
async initCordTeamHandlers() {
|
|
282
|
+
try {
|
|
283
|
+
const dbPath = join(this.projectRoot, ".stackmemory", "context.db");
|
|
284
|
+
const adapter = new SQLiteAdapter(this.projectId, {
|
|
285
|
+
dbPath,
|
|
286
|
+
walMode: true
|
|
287
|
+
});
|
|
288
|
+
await adapter.connect();
|
|
289
|
+
this.cordHandlers = new CordHandlers({
|
|
290
|
+
frameManager: this.frameManager,
|
|
291
|
+
dbAdapter: adapter
|
|
292
|
+
});
|
|
293
|
+
this.teamHandlers = new TeamHandlers({
|
|
294
|
+
frameManager: this.frameManager,
|
|
295
|
+
dbAdapter: adapter
|
|
296
|
+
});
|
|
297
|
+
logger.info("Cord and Team handlers initialized");
|
|
298
|
+
} catch (error) {
|
|
299
|
+
logger.warn("Failed to initialize Cord/Team handlers", { error });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
264
302
|
async initProviderHandlers() {
|
|
265
303
|
if (!isFeatureEnabled("multiProvider")) return;
|
|
266
304
|
try {
|
|
@@ -296,199 +334,180 @@ ${summary}...`, 0.8);
|
|
|
296
334
|
}
|
|
297
335
|
}
|
|
298
336
|
},
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
description: "Retry loop iterations"
|
|
316
|
-
},
|
|
317
|
-
execute: {
|
|
318
|
-
type: "boolean",
|
|
319
|
-
default: false,
|
|
320
|
-
description: "Actually call implementer (otherwise dry-run)"
|
|
337
|
+
// Planning tools (only when ANTHROPIC_API_KEY is set)
|
|
338
|
+
...process.env.ANTHROPIC_API_KEY ? [
|
|
339
|
+
{
|
|
340
|
+
name: "plan_gate",
|
|
341
|
+
description: "Phase 1: Generate a plan and return an approvalId for later execution",
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: "object",
|
|
344
|
+
properties: {
|
|
345
|
+
task: {
|
|
346
|
+
type: "string",
|
|
347
|
+
description: "Task description"
|
|
348
|
+
},
|
|
349
|
+
plannerModel: {
|
|
350
|
+
type: "string",
|
|
351
|
+
description: "Claude model (optional)"
|
|
352
|
+
}
|
|
321
353
|
},
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
required: ["task"]
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: "approve_plan",
|
|
359
|
+
description: "Phase 2: Execute a previously generated plan by approvalId",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
type: "object",
|
|
362
|
+
properties: {
|
|
363
|
+
approvalId: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "Id from plan_gate"
|
|
366
|
+
},
|
|
367
|
+
implementer: {
|
|
368
|
+
type: "string",
|
|
369
|
+
enum: ["codex", "claude"],
|
|
370
|
+
default: "codex",
|
|
371
|
+
description: "Which agent implements code"
|
|
372
|
+
},
|
|
373
|
+
maxIters: { type: "number", default: 2 },
|
|
374
|
+
recordFrame: { type: "boolean", default: true },
|
|
375
|
+
execute: { type: "boolean", default: true }
|
|
326
376
|
},
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
377
|
+
required: ["approvalId"]
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "pending_list",
|
|
382
|
+
description: "List pending approval-gated plans (supports filters)",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
type: "object",
|
|
385
|
+
properties: {
|
|
386
|
+
taskContains: {
|
|
387
|
+
type: "string",
|
|
388
|
+
description: "Filter tasks containing this substring"
|
|
389
|
+
},
|
|
390
|
+
olderThanMs: {
|
|
391
|
+
type: "number",
|
|
392
|
+
description: "Only items older than this age (ms)"
|
|
393
|
+
},
|
|
394
|
+
newerThanMs: {
|
|
395
|
+
type: "number",
|
|
396
|
+
description: "Only items newer than this age (ms)"
|
|
397
|
+
},
|
|
398
|
+
sort: {
|
|
399
|
+
type: "string",
|
|
400
|
+
enum: ["asc", "desc"],
|
|
401
|
+
description: "Sort by createdAt"
|
|
402
|
+
},
|
|
403
|
+
limit: {
|
|
404
|
+
type: "number",
|
|
405
|
+
description: "Max items to return"
|
|
406
|
+
}
|
|
331
407
|
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "pending_clear",
|
|
412
|
+
description: "Clear pending approval-gated plans (by id, all, or olderThanMs)",
|
|
413
|
+
inputSchema: {
|
|
414
|
+
type: "object",
|
|
415
|
+
properties: {
|
|
416
|
+
approvalId: {
|
|
417
|
+
type: "string",
|
|
418
|
+
description: "Clear a single approval by id"
|
|
419
|
+
},
|
|
420
|
+
all: {
|
|
421
|
+
type: "boolean",
|
|
422
|
+
description: "Clear all pending approvals",
|
|
423
|
+
default: false
|
|
424
|
+
},
|
|
425
|
+
olderThanMs: {
|
|
426
|
+
type: "number",
|
|
427
|
+
description: "Clear approvals older than this age (ms)"
|
|
428
|
+
}
|
|
346
429
|
}
|
|
347
|
-
},
|
|
348
|
-
required: ["task"]
|
|
349
|
-
}
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
name: "approve_plan",
|
|
353
|
-
description: "Phase 2: Execute a previously generated plan by approvalId (runs implement + critique)",
|
|
354
|
-
inputSchema: {
|
|
355
|
-
type: "object",
|
|
356
|
-
properties: {
|
|
357
|
-
approvalId: {
|
|
358
|
-
type: "string",
|
|
359
|
-
description: "Id from plan_gate"
|
|
360
|
-
},
|
|
361
|
-
implementer: {
|
|
362
|
-
type: "string",
|
|
363
|
-
enum: ["codex", "claude"],
|
|
364
|
-
default: "codex",
|
|
365
|
-
description: "Which agent implements code"
|
|
366
|
-
},
|
|
367
|
-
maxIters: { type: "number", default: 2 },
|
|
368
|
-
recordFrame: { type: "boolean", default: true },
|
|
369
|
-
execute: { type: "boolean", default: true }
|
|
370
|
-
},
|
|
371
|
-
required: ["approvalId"]
|
|
372
|
-
}
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
name: "pending_list",
|
|
376
|
-
description: "List pending approval-gated plans (supports filters)",
|
|
377
|
-
inputSchema: {
|
|
378
|
-
type: "object",
|
|
379
|
-
properties: {
|
|
380
|
-
taskContains: {
|
|
381
|
-
type: "string",
|
|
382
|
-
description: "Filter tasks containing this substring"
|
|
383
|
-
},
|
|
384
|
-
olderThanMs: {
|
|
385
|
-
type: "number",
|
|
386
|
-
description: "Only items older than this age (ms)"
|
|
387
|
-
},
|
|
388
|
-
newerThanMs: {
|
|
389
|
-
type: "number",
|
|
390
|
-
description: "Only items newer than this age (ms)"
|
|
391
|
-
},
|
|
392
|
-
sort: {
|
|
393
|
-
type: "string",
|
|
394
|
-
enum: ["asc", "desc"],
|
|
395
|
-
description: "Sort by createdAt"
|
|
396
|
-
},
|
|
397
|
-
limit: { type: "number", description: "Max items to return" }
|
|
398
430
|
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: "pending_show",
|
|
434
|
+
description: "Show a pending plan by approvalId",
|
|
435
|
+
inputSchema: {
|
|
436
|
+
type: "object",
|
|
437
|
+
properties: {
|
|
438
|
+
approvalId: {
|
|
439
|
+
type: "string",
|
|
440
|
+
description: "Approval id from plan_gate"
|
|
441
|
+
}
|
|
410
442
|
},
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
443
|
+
required: ["approvalId"]
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "plan_only",
|
|
448
|
+
description: "Generate an implementation plan (Claude) and return JSON only",
|
|
449
|
+
inputSchema: {
|
|
450
|
+
type: "object",
|
|
451
|
+
properties: {
|
|
452
|
+
task: {
|
|
453
|
+
type: "string",
|
|
454
|
+
description: "Task description"
|
|
455
|
+
},
|
|
456
|
+
plannerModel: {
|
|
457
|
+
type: "string",
|
|
458
|
+
description: "Claude model for planning (optional)"
|
|
459
|
+
}
|
|
415
460
|
},
|
|
416
|
-
|
|
417
|
-
type: "number",
|
|
418
|
-
description: "Clear approvals older than this age (ms)"
|
|
419
|
-
}
|
|
461
|
+
required: ["task"]
|
|
420
462
|
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
properties: {
|
|
443
|
-
task: { type: "string", description: "Task description" },
|
|
444
|
-
plannerModel: {
|
|
445
|
-
type: "string",
|
|
446
|
-
description: "Claude model for planning (optional)"
|
|
447
|
-
}
|
|
448
|
-
},
|
|
449
|
-
required: ["task"]
|
|
450
|
-
}
|
|
451
|
-
},
|
|
452
|
-
{
|
|
453
|
-
name: "call_codex",
|
|
454
|
-
description: "Invoke Codex via codex-sm with a prompt and args; dry-run by default",
|
|
455
|
-
inputSchema: {
|
|
456
|
-
type: "object",
|
|
457
|
-
properties: {
|
|
458
|
-
prompt: { type: "string", description: "Prompt for Codex" },
|
|
459
|
-
args: {
|
|
460
|
-
type: "array",
|
|
461
|
-
items: { type: "string" },
|
|
462
|
-
description: "Additional CLI args for codex-sm"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "call_codex",
|
|
466
|
+
description: "Invoke Codex via codex-sm with a prompt and args; dry-run by default",
|
|
467
|
+
inputSchema: {
|
|
468
|
+
type: "object",
|
|
469
|
+
properties: {
|
|
470
|
+
prompt: {
|
|
471
|
+
type: "string",
|
|
472
|
+
description: "Prompt for Codex"
|
|
473
|
+
},
|
|
474
|
+
args: {
|
|
475
|
+
type: "array",
|
|
476
|
+
items: { type: "string" },
|
|
477
|
+
description: "Additional CLI args for codex-sm"
|
|
478
|
+
},
|
|
479
|
+
execute: {
|
|
480
|
+
type: "boolean",
|
|
481
|
+
default: false,
|
|
482
|
+
description: "Actually run codex-sm (otherwise dry-run)"
|
|
483
|
+
}
|
|
463
484
|
},
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
485
|
+
required: ["prompt"]
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: "call_claude",
|
|
490
|
+
description: "Invoke Claude with a prompt (Anthropic SDK)",
|
|
491
|
+
inputSchema: {
|
|
492
|
+
type: "object",
|
|
493
|
+
properties: {
|
|
494
|
+
prompt: {
|
|
495
|
+
type: "string",
|
|
496
|
+
description: "Prompt for Claude"
|
|
497
|
+
},
|
|
498
|
+
model: {
|
|
499
|
+
type: "string",
|
|
500
|
+
description: "Claude model (optional)"
|
|
501
|
+
},
|
|
502
|
+
system: {
|
|
503
|
+
type: "string",
|
|
504
|
+
description: "System prompt (optional)"
|
|
505
|
+
}
|
|
483
506
|
},
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
description: "System prompt (optional)"
|
|
487
|
-
}
|
|
488
|
-
},
|
|
489
|
-
required: ["prompt"]
|
|
507
|
+
required: ["prompt"]
|
|
508
|
+
}
|
|
490
509
|
}
|
|
491
|
-
|
|
510
|
+
] : [],
|
|
492
511
|
{
|
|
493
512
|
name: "add_decision",
|
|
494
513
|
description: "Record a decision or important information",
|
|
@@ -953,109 +972,190 @@ ${summary}...`, 0.8);
|
|
|
953
972
|
required: ["query"]
|
|
954
973
|
}
|
|
955
974
|
},
|
|
956
|
-
// DiffMem tools
|
|
975
|
+
// DiffMem tools (only when DIFFMEM_ENDPOINT or DIFFMEM_ENABLED is set)
|
|
976
|
+
...process.env.DIFFMEM_ENDPOINT || process.env.DIFFMEM_ENABLED === "true" ? this.diffMemHandlers.getToolDefinitions() : [],
|
|
977
|
+
// Cord task orchestration tools
|
|
957
978
|
{
|
|
958
|
-
name: "
|
|
959
|
-
description: "
|
|
979
|
+
name: "cord_spawn",
|
|
980
|
+
description: "Create a subtask with clean context (spawn). Child sees only its prompt and completed blocker results.",
|
|
960
981
|
inputSchema: {
|
|
961
982
|
type: "object",
|
|
962
983
|
properties: {
|
|
963
|
-
|
|
984
|
+
goal: {
|
|
985
|
+
type: "string",
|
|
986
|
+
description: "What this task should accomplish"
|
|
987
|
+
},
|
|
988
|
+
prompt: {
|
|
989
|
+
type: "string",
|
|
990
|
+
description: "Detailed instructions for the task"
|
|
991
|
+
},
|
|
992
|
+
blocked_by: {
|
|
964
993
|
type: "array",
|
|
965
|
-
items: {
|
|
966
|
-
|
|
967
|
-
enum: [
|
|
968
|
-
"preference",
|
|
969
|
-
"expertise",
|
|
970
|
-
"project_knowledge",
|
|
971
|
-
"pattern",
|
|
972
|
-
"correction"
|
|
973
|
-
]
|
|
974
|
-
},
|
|
975
|
-
description: "Filter by memory categories"
|
|
994
|
+
items: { type: "string" },
|
|
995
|
+
description: "Task IDs that must complete first"
|
|
976
996
|
},
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
description: "Maximum memories to return"
|
|
981
|
-
}
|
|
982
|
-
}
|
|
997
|
+
parent_id: { type: "string", description: "Parent task ID" }
|
|
998
|
+
},
|
|
999
|
+
required: ["goal"]
|
|
983
1000
|
}
|
|
984
1001
|
},
|
|
985
1002
|
{
|
|
986
|
-
name: "
|
|
987
|
-
description: "
|
|
1003
|
+
name: "cord_fork",
|
|
1004
|
+
description: "Create a subtask with full sibling context (fork). Child sees prompt, blocker results, AND completed sibling results.",
|
|
988
1005
|
inputSchema: {
|
|
989
1006
|
type: "object",
|
|
990
1007
|
properties: {
|
|
991
|
-
|
|
1008
|
+
goal: {
|
|
992
1009
|
type: "string",
|
|
993
|
-
description: "
|
|
1010
|
+
description: "What this task should accomplish"
|
|
994
1011
|
},
|
|
995
|
-
|
|
1012
|
+
prompt: {
|
|
996
1013
|
type: "string",
|
|
997
|
-
|
|
998
|
-
"preference",
|
|
999
|
-
"expertise",
|
|
1000
|
-
"project_knowledge",
|
|
1001
|
-
"pattern",
|
|
1002
|
-
"correction"
|
|
1003
|
-
],
|
|
1004
|
-
description: "Category of the insight"
|
|
1014
|
+
description: "Detailed instructions for the task"
|
|
1005
1015
|
},
|
|
1006
|
-
|
|
1007
|
-
type: "
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
default: 0.7,
|
|
1011
|
-
description: "Confidence level (0-1)"
|
|
1016
|
+
blocked_by: {
|
|
1017
|
+
type: "array",
|
|
1018
|
+
items: { type: "string" },
|
|
1019
|
+
description: "Task IDs that must complete first"
|
|
1012
1020
|
},
|
|
1013
|
-
|
|
1014
|
-
type: "object",
|
|
1015
|
-
description: "Additional context for the insight"
|
|
1016
|
-
}
|
|
1021
|
+
parent_id: { type: "string", description: "Parent task ID" }
|
|
1017
1022
|
},
|
|
1018
|
-
required: ["
|
|
1023
|
+
required: ["goal"]
|
|
1019
1024
|
}
|
|
1020
1025
|
},
|
|
1021
1026
|
{
|
|
1022
|
-
name: "
|
|
1023
|
-
description: "
|
|
1027
|
+
name: "cord_complete",
|
|
1028
|
+
description: "Mark a cord task as completed with a result. Automatically unblocks dependent tasks.",
|
|
1024
1029
|
inputSchema: {
|
|
1025
1030
|
type: "object",
|
|
1026
1031
|
properties: {
|
|
1027
|
-
|
|
1032
|
+
task_id: {
|
|
1028
1033
|
type: "string",
|
|
1029
|
-
description: "
|
|
1034
|
+
description: "Task ID to complete"
|
|
1030
1035
|
},
|
|
1031
|
-
|
|
1036
|
+
result: {
|
|
1032
1037
|
type: "string",
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1038
|
+
description: "The result/output of this task"
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
required: ["task_id", "result"]
|
|
1042
|
+
}
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
name: "cord_ask",
|
|
1046
|
+
description: "Create an ask task \u2014 a question that needs an answer before dependent tasks can proceed.",
|
|
1047
|
+
inputSchema: {
|
|
1048
|
+
type: "object",
|
|
1049
|
+
properties: {
|
|
1050
|
+
question: {
|
|
1051
|
+
type: "string",
|
|
1052
|
+
description: "The question to ask"
|
|
1036
1053
|
},
|
|
1037
|
-
|
|
1038
|
-
type: "
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1054
|
+
options: {
|
|
1055
|
+
type: "array",
|
|
1056
|
+
items: { type: "string" },
|
|
1057
|
+
description: "Optional list of answer choices"
|
|
1058
|
+
},
|
|
1059
|
+
parent_id: { type: "string", description: "Parent task ID" }
|
|
1060
|
+
},
|
|
1061
|
+
required: ["question"]
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
name: "cord_tree",
|
|
1066
|
+
description: "View the cord task tree with context scoping. Shows active, blocked, or completed tasks.",
|
|
1067
|
+
inputSchema: {
|
|
1068
|
+
type: "object",
|
|
1069
|
+
properties: {
|
|
1070
|
+
task_id: {
|
|
1071
|
+
type: "string",
|
|
1072
|
+
description: "Root task ID to show subtree (omit for full tree)"
|
|
1043
1073
|
},
|
|
1074
|
+
include_results: {
|
|
1075
|
+
type: "boolean",
|
|
1076
|
+
default: false,
|
|
1077
|
+
description: "Include task results in output"
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
// Team collaboration tools
|
|
1083
|
+
{
|
|
1084
|
+
name: "team_context_get",
|
|
1085
|
+
description: "Get context from other agents working on the same project. Returns recent frames and shared anchors.",
|
|
1086
|
+
inputSchema: {
|
|
1087
|
+
type: "object",
|
|
1088
|
+
properties: {
|
|
1044
1089
|
limit: {
|
|
1045
1090
|
type: "number",
|
|
1046
1091
|
default: 10,
|
|
1047
|
-
description: "
|
|
1092
|
+
description: "Max frames to return"
|
|
1093
|
+
},
|
|
1094
|
+
types: {
|
|
1095
|
+
type: "array",
|
|
1096
|
+
items: { type: "string" },
|
|
1097
|
+
description: "Filter by frame types"
|
|
1098
|
+
},
|
|
1099
|
+
since: {
|
|
1100
|
+
type: "number",
|
|
1101
|
+
description: "Only frames created after this timestamp (epoch ms)"
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
name: "team_context_share",
|
|
1108
|
+
description: "Share a piece of context with other agents. Creates a high-priority anchor visible to team_context_get.",
|
|
1109
|
+
inputSchema: {
|
|
1110
|
+
type: "object",
|
|
1111
|
+
properties: {
|
|
1112
|
+
content: {
|
|
1113
|
+
type: "string",
|
|
1114
|
+
description: "The context to share"
|
|
1115
|
+
},
|
|
1116
|
+
type: {
|
|
1117
|
+
type: "string",
|
|
1118
|
+
enum: [
|
|
1119
|
+
"FACT",
|
|
1120
|
+
"DECISION",
|
|
1121
|
+
"CONSTRAINT",
|
|
1122
|
+
"INTERFACE_CONTRACT",
|
|
1123
|
+
"TODO",
|
|
1124
|
+
"RISK"
|
|
1125
|
+
],
|
|
1126
|
+
default: "FACT",
|
|
1127
|
+
description: "Type of context"
|
|
1128
|
+
},
|
|
1129
|
+
priority: {
|
|
1130
|
+
type: "number",
|
|
1131
|
+
minimum: 1,
|
|
1132
|
+
maximum: 10,
|
|
1133
|
+
default: 8,
|
|
1134
|
+
description: "Priority level (1-10)"
|
|
1048
1135
|
}
|
|
1049
1136
|
},
|
|
1050
|
-
required: ["
|
|
1137
|
+
required: ["content"]
|
|
1051
1138
|
}
|
|
1052
1139
|
},
|
|
1053
1140
|
{
|
|
1054
|
-
name: "
|
|
1055
|
-
description: "
|
|
1141
|
+
name: "team_search",
|
|
1142
|
+
description: "Search across all agents' context in the project. Uses full-text search across all sessions.",
|
|
1056
1143
|
inputSchema: {
|
|
1057
1144
|
type: "object",
|
|
1058
|
-
properties: {
|
|
1145
|
+
properties: {
|
|
1146
|
+
query: { type: "string", description: "Search query" },
|
|
1147
|
+
limit: {
|
|
1148
|
+
type: "number",
|
|
1149
|
+
default: 20,
|
|
1150
|
+
description: "Maximum results to return"
|
|
1151
|
+
},
|
|
1152
|
+
include_events: {
|
|
1153
|
+
type: "boolean",
|
|
1154
|
+
default: false,
|
|
1155
|
+
description: "Include events in results"
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
required: ["query"]
|
|
1059
1159
|
}
|
|
1060
1160
|
},
|
|
1061
1161
|
// Greptile tools (only active when GREPTILE_API_KEY is set)
|
|
@@ -1160,7 +1260,23 @@ ${summary}...`, 0.8);
|
|
|
1160
1260
|
required: ["batchId"]
|
|
1161
1261
|
}
|
|
1162
1262
|
}
|
|
1163
|
-
] : []
|
|
1263
|
+
] : [],
|
|
1264
|
+
// Digest tool
|
|
1265
|
+
{
|
|
1266
|
+
name: "sm_digest",
|
|
1267
|
+
description: "Generate a chronological activity digest for a time period (today/yesterday/week)",
|
|
1268
|
+
inputSchema: {
|
|
1269
|
+
type: "object",
|
|
1270
|
+
properties: {
|
|
1271
|
+
period: {
|
|
1272
|
+
type: "string",
|
|
1273
|
+
enum: ["today", "yesterday", "week"],
|
|
1274
|
+
description: "Time period for the digest"
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
required: ["period"]
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1164
1280
|
]
|
|
1165
1281
|
};
|
|
1166
1282
|
}
|
|
@@ -1330,6 +1446,104 @@ ${summary}...`, 0.8);
|
|
|
1330
1446
|
case "sm_edit":
|
|
1331
1447
|
result = await this.handleSmEdit(args);
|
|
1332
1448
|
break;
|
|
1449
|
+
// Cord task orchestration
|
|
1450
|
+
case "cord_spawn":
|
|
1451
|
+
if (!this.cordHandlers) {
|
|
1452
|
+
result = {
|
|
1453
|
+
content: [
|
|
1454
|
+
{ type: "text", text: "Cord handlers not initialized." }
|
|
1455
|
+
],
|
|
1456
|
+
is_error: true
|
|
1457
|
+
};
|
|
1458
|
+
} else {
|
|
1459
|
+
result = await this.cordHandlers.handleCordSpawn(args);
|
|
1460
|
+
}
|
|
1461
|
+
break;
|
|
1462
|
+
case "cord_fork":
|
|
1463
|
+
if (!this.cordHandlers) {
|
|
1464
|
+
result = {
|
|
1465
|
+
content: [
|
|
1466
|
+
{ type: "text", text: "Cord handlers not initialized." }
|
|
1467
|
+
],
|
|
1468
|
+
is_error: true
|
|
1469
|
+
};
|
|
1470
|
+
} else {
|
|
1471
|
+
result = await this.cordHandlers.handleCordFork(args);
|
|
1472
|
+
}
|
|
1473
|
+
break;
|
|
1474
|
+
case "cord_complete":
|
|
1475
|
+
if (!this.cordHandlers) {
|
|
1476
|
+
result = {
|
|
1477
|
+
content: [
|
|
1478
|
+
{ type: "text", text: "Cord handlers not initialized." }
|
|
1479
|
+
],
|
|
1480
|
+
is_error: true
|
|
1481
|
+
};
|
|
1482
|
+
} else {
|
|
1483
|
+
result = await this.cordHandlers.handleCordComplete(args);
|
|
1484
|
+
}
|
|
1485
|
+
break;
|
|
1486
|
+
case "cord_ask":
|
|
1487
|
+
if (!this.cordHandlers) {
|
|
1488
|
+
result = {
|
|
1489
|
+
content: [
|
|
1490
|
+
{ type: "text", text: "Cord handlers not initialized." }
|
|
1491
|
+
],
|
|
1492
|
+
is_error: true
|
|
1493
|
+
};
|
|
1494
|
+
} else {
|
|
1495
|
+
result = await this.cordHandlers.handleCordAsk(args);
|
|
1496
|
+
}
|
|
1497
|
+
break;
|
|
1498
|
+
case "cord_tree":
|
|
1499
|
+
if (!this.cordHandlers) {
|
|
1500
|
+
result = {
|
|
1501
|
+
content: [
|
|
1502
|
+
{ type: "text", text: "Cord handlers not initialized." }
|
|
1503
|
+
],
|
|
1504
|
+
is_error: true
|
|
1505
|
+
};
|
|
1506
|
+
} else {
|
|
1507
|
+
result = await this.cordHandlers.handleCordTree(args);
|
|
1508
|
+
}
|
|
1509
|
+
break;
|
|
1510
|
+
// Team collaboration
|
|
1511
|
+
case "team_context_get":
|
|
1512
|
+
if (!this.teamHandlers) {
|
|
1513
|
+
result = {
|
|
1514
|
+
content: [
|
|
1515
|
+
{ type: "text", text: "Team handlers not initialized." }
|
|
1516
|
+
],
|
|
1517
|
+
is_error: true
|
|
1518
|
+
};
|
|
1519
|
+
} else {
|
|
1520
|
+
result = await this.teamHandlers.handleTeamContextGet(args);
|
|
1521
|
+
}
|
|
1522
|
+
break;
|
|
1523
|
+
case "team_context_share":
|
|
1524
|
+
if (!this.teamHandlers) {
|
|
1525
|
+
result = {
|
|
1526
|
+
content: [
|
|
1527
|
+
{ type: "text", text: "Team handlers not initialized." }
|
|
1528
|
+
],
|
|
1529
|
+
is_error: true
|
|
1530
|
+
};
|
|
1531
|
+
} else {
|
|
1532
|
+
result = await this.teamHandlers.handleTeamContextShare(args);
|
|
1533
|
+
}
|
|
1534
|
+
break;
|
|
1535
|
+
case "team_search":
|
|
1536
|
+
if (!this.teamHandlers) {
|
|
1537
|
+
result = {
|
|
1538
|
+
content: [
|
|
1539
|
+
{ type: "text", text: "Team handlers not initialized." }
|
|
1540
|
+
],
|
|
1541
|
+
is_error: true
|
|
1542
|
+
};
|
|
1543
|
+
} else {
|
|
1544
|
+
result = await this.teamHandlers.handleTeamSearch(args);
|
|
1545
|
+
}
|
|
1546
|
+
break;
|
|
1333
1547
|
// Provider tools
|
|
1334
1548
|
case "delegate_to_model":
|
|
1335
1549
|
if (!this.providerHandlers) {
|
|
@@ -1339,7 +1553,8 @@ ${summary}...`, 0.8);
|
|
|
1339
1553
|
type: "text",
|
|
1340
1554
|
text: "Multi-provider routing is disabled."
|
|
1341
1555
|
}
|
|
1342
|
-
]
|
|
1556
|
+
],
|
|
1557
|
+
is_error: true
|
|
1343
1558
|
};
|
|
1344
1559
|
} else {
|
|
1345
1560
|
result = await this.providerHandlers.handleDelegateToModel(
|
|
@@ -1355,7 +1570,8 @@ ${summary}...`, 0.8);
|
|
|
1355
1570
|
type: "text",
|
|
1356
1571
|
text: "Multi-provider routing is disabled."
|
|
1357
1572
|
}
|
|
1358
|
-
]
|
|
1573
|
+
],
|
|
1574
|
+
is_error: true
|
|
1359
1575
|
};
|
|
1360
1576
|
} else {
|
|
1361
1577
|
result = await this.providerHandlers.handleBatchSubmit(
|
|
@@ -1371,7 +1587,8 @@ ${summary}...`, 0.8);
|
|
|
1371
1587
|
type: "text",
|
|
1372
1588
|
text: "Multi-provider routing is disabled."
|
|
1373
1589
|
}
|
|
1374
|
-
]
|
|
1590
|
+
],
|
|
1591
|
+
is_error: true
|
|
1375
1592
|
};
|
|
1376
1593
|
} else {
|
|
1377
1594
|
result = await this.providerHandlers.handleBatchCheck(
|
|
@@ -1379,12 +1596,19 @@ ${summary}...`, 0.8);
|
|
|
1379
1596
|
);
|
|
1380
1597
|
}
|
|
1381
1598
|
break;
|
|
1599
|
+
case "sm_digest":
|
|
1600
|
+
result = await this.handleSmDigest(args);
|
|
1601
|
+
break;
|
|
1602
|
+
case "sm_desire_paths":
|
|
1603
|
+
result = this.handleDesirePaths(args);
|
|
1604
|
+
break;
|
|
1382
1605
|
default:
|
|
1383
1606
|
throw new Error(`Unknown tool: ${name}`);
|
|
1384
1607
|
}
|
|
1385
1608
|
} catch (err) {
|
|
1386
1609
|
error = err instanceof Error ? err : new Error(String(err));
|
|
1387
1610
|
toolCall.error = error.message;
|
|
1611
|
+
this.logDesirePath(name, args, error.message);
|
|
1388
1612
|
throw err;
|
|
1389
1613
|
} finally {
|
|
1390
1614
|
const endTime = Date.now();
|
|
@@ -2851,6 +3075,153 @@ ${typeBreakdown}`
|
|
|
2851
3075
|
matchedText: editResult.match.matchedText.length > 200 ? editResult.match.matchedText.slice(0, 200) + "..." : editResult.match.matchedText
|
|
2852
3076
|
};
|
|
2853
3077
|
}
|
|
3078
|
+
/** Log failed tool calls to desire path JSONL for product prioritization */
|
|
3079
|
+
logDesirePath(tool, args, errorMsg) {
|
|
3080
|
+
try {
|
|
3081
|
+
const home = process.env["HOME"] || "/tmp";
|
|
3082
|
+
const dir = join(home, ".stackmemory", "desire-paths");
|
|
3083
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
3084
|
+
const category = /unknown tool/i.test(errorMsg) ? "unknown_tool" : /invalid param|missing.*param/i.test(errorMsg) ? "invalid_params" : "handler_error";
|
|
3085
|
+
const sanitized = {};
|
|
3086
|
+
for (const [k, v] of Object.entries(args)) {
|
|
3087
|
+
sanitized[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "...[truncated]" : v;
|
|
3088
|
+
}
|
|
3089
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3090
|
+
const entry = {
|
|
3091
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3092
|
+
tool,
|
|
3093
|
+
input: sanitized,
|
|
3094
|
+
error: errorMsg.slice(0, 500),
|
|
3095
|
+
category,
|
|
3096
|
+
source: "mcp-server"
|
|
3097
|
+
};
|
|
3098
|
+
appendFileSync(
|
|
3099
|
+
join(dir, `desire-server-${date}.jsonl`),
|
|
3100
|
+
JSON.stringify(entry) + "\n"
|
|
3101
|
+
);
|
|
3102
|
+
} catch {
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
/** Handle sm_desire_paths tool — read and aggregate desire path logs */
|
|
3106
|
+
handleDesirePaths(args) {
|
|
3107
|
+
const mode = String(args.mode || "summary");
|
|
3108
|
+
const days = Number(args.days || 7);
|
|
3109
|
+
const limit = Number(args.limit || 20);
|
|
3110
|
+
const categoryFilter = args.category ? String(args.category) : void 0;
|
|
3111
|
+
const desireDir = join(homedir(), ".stackmemory", "desire-paths");
|
|
3112
|
+
if (!existsSync(desireDir)) {
|
|
3113
|
+
return {
|
|
3114
|
+
content: [{ type: "text", text: "No desire path data found." }]
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
3118
|
+
const files = readdirSync(desireDir).filter(
|
|
3119
|
+
(f) => f.startsWith("desire-") && f.endsWith(".jsonl")
|
|
3120
|
+
);
|
|
3121
|
+
const entries = [];
|
|
3122
|
+
for (const file of files) {
|
|
3123
|
+
const lines = readFileSync(join(desireDir, file), "utf-8").split("\n").filter(Boolean);
|
|
3124
|
+
for (const line of lines) {
|
|
3125
|
+
try {
|
|
3126
|
+
const entry = JSON.parse(line);
|
|
3127
|
+
if (new Date(entry.ts).getTime() < cutoff) continue;
|
|
3128
|
+
if (categoryFilter && entry.category !== categoryFilter) continue;
|
|
3129
|
+
entries.push(entry);
|
|
3130
|
+
} catch {
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
if (entries.length === 0) {
|
|
3135
|
+
return {
|
|
3136
|
+
content: [
|
|
3137
|
+
{
|
|
3138
|
+
type: "text",
|
|
3139
|
+
text: `No desire path data in the last ${days} day(s).`
|
|
3140
|
+
}
|
|
3141
|
+
]
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
3144
|
+
if (mode === "list") {
|
|
3145
|
+
const recent = entries.sort((a, b) => b.ts.localeCompare(a.ts)).slice(0, limit);
|
|
3146
|
+
const lines = recent.map(
|
|
3147
|
+
(e) => `${e.ts.slice(0, 19)} ${e.tool} [${e.category}]
|
|
3148
|
+
${e.error.slice(0, 120)}`
|
|
3149
|
+
);
|
|
3150
|
+
return {
|
|
3151
|
+
content: [
|
|
3152
|
+
{
|
|
3153
|
+
type: "text",
|
|
3154
|
+
text: `Recent desire paths (${recent.length}/${entries.length}):
|
|
3155
|
+
|
|
3156
|
+
${lines.join("\n\n")}`
|
|
3157
|
+
}
|
|
3158
|
+
]
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
3162
|
+
for (const e of entries) {
|
|
3163
|
+
const existing = byTool.get(e.tool);
|
|
3164
|
+
if (!existing || e.ts > existing.lastSeen) {
|
|
3165
|
+
byTool.set(e.tool, {
|
|
3166
|
+
count: (existing?.count || 0) + 1,
|
|
3167
|
+
category: e.category,
|
|
3168
|
+
lastSeen: e.ts
|
|
3169
|
+
});
|
|
3170
|
+
} else {
|
|
3171
|
+
existing.count++;
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
const sorted = [...byTool.entries()].sort(
|
|
3175
|
+
(a, b) => b[1].count - a[1].count
|
|
3176
|
+
);
|
|
3177
|
+
const byCat = /* @__PURE__ */ new Map();
|
|
3178
|
+
for (const e of entries) {
|
|
3179
|
+
byCat.set(e.category, (byCat.get(e.category) || 0) + 1);
|
|
3180
|
+
}
|
|
3181
|
+
const toolLines = sorted.map(
|
|
3182
|
+
([tool, data]) => `${tool}: ${data.count}x [${data.category}] (last: ${data.lastSeen.slice(0, 10)})`
|
|
3183
|
+
);
|
|
3184
|
+
const catLines = [...byCat.entries()].sort((a, b) => b[1] - a[1]).map(([cat, count]) => ` ${cat}: ${count}`);
|
|
3185
|
+
return {
|
|
3186
|
+
content: [
|
|
3187
|
+
{
|
|
3188
|
+
type: "text",
|
|
3189
|
+
text: `Desire Path Summary (last ${days}d, ${entries.length} total failures)
|
|
3190
|
+
|
|
3191
|
+
By Tool:
|
|
3192
|
+
${toolLines.join("\n")}
|
|
3193
|
+
|
|
3194
|
+
By Category:
|
|
3195
|
+
${catLines.join("\n")}`
|
|
3196
|
+
}
|
|
3197
|
+
]
|
|
3198
|
+
};
|
|
3199
|
+
}
|
|
3200
|
+
async handleSmDigest(args) {
|
|
3201
|
+
const period = String(args.period || "today");
|
|
3202
|
+
if (!["today", "yesterday", "week"].includes(period)) {
|
|
3203
|
+
return {
|
|
3204
|
+
content: [
|
|
3205
|
+
{
|
|
3206
|
+
type: "text",
|
|
3207
|
+
text: `Invalid period "${period}". Use: today, yesterday, week`
|
|
3208
|
+
}
|
|
3209
|
+
],
|
|
3210
|
+
isError: true
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
const markdown = generateChronologicalDigest(
|
|
3214
|
+
this.db,
|
|
3215
|
+
period,
|
|
3216
|
+
this.projectId
|
|
3217
|
+
);
|
|
3218
|
+
const outputPath = join(this.projectRoot, ".stackmemory", `${period}.md`);
|
|
3219
|
+
writeFileSync(outputPath, markdown);
|
|
3220
|
+
return {
|
|
3221
|
+
content: [{ type: "text", text: markdown }],
|
|
3222
|
+
isError: false
|
|
3223
|
+
};
|
|
3224
|
+
}
|
|
2854
3225
|
async start() {
|
|
2855
3226
|
const transport = new StdioServerTransport();
|
|
2856
3227
|
await this.server.connect(transport);
|