@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.
Files changed (27) hide show
  1. package/README.md +3 -3
  2. package/dist/src/cli/commands/daemon.js +306 -2
  3. package/dist/src/cli/commands/desires.js +117 -0
  4. package/dist/src/cli/commands/digest.js +73 -0
  5. package/dist/src/cli/commands/setup.js +369 -39
  6. package/dist/src/cli/commands/team.js +168 -0
  7. package/dist/src/cli/index.js +6 -0
  8. package/dist/src/core/digest/chronological-digest.js +143 -0
  9. package/dist/src/core/digest/index.js +1 -0
  10. package/dist/src/integrations/mcp/server.js +622 -251
  11. package/dist/src/integrations/mcp/tool-definitions.js +607 -50
  12. package/dist/src/utils/hook-installer.js +35 -0
  13. package/package.json +2 -2
  14. package/scripts/install-claude-hooks-auto.js +35 -0
  15. package/templates/claude-hooks/daemon-auto-start.js +125 -0
  16. package/templates/claude-hooks/desire-path-trace.js +118 -0
  17. package/templates/claude-hooks/session-rescue.sh +3 -0
  18. package/templates/claude-hooks/team-subagent-stop.js +77 -0
  19. package/templates/claude-hooks/team-task-complete.js +83 -0
  20. package/templates/claude-hooks/team-teammate-idle.js +96 -0
  21. /package/templates/claude-hooks/{auto-background-hook.js → archive/auto-background-hook.js} +0 -0
  22. /package/templates/claude-hooks/{hook-config.json → archive/hook-config.json} +0 -0
  23. /package/templates/claude-hooks/{hooks.json → archive/hooks.json} +0 -0
  24. /package/templates/claude-hooks/{on-compact-detected → archive/on-compact-detected} +0 -0
  25. /package/templates/claude-hooks/{on-exit → archive/on-exit} +0 -0
  26. /package/templates/claude-hooks/{post-edit-sweep.js → archive/post-edit-sweep.js} +0 -0
  27. /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 { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
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
- name: "plan_and_code",
301
- description: "Generate a plan (Claude), attempt implementation (Codex/Claude), and return JSON result. Quiet by default.",
302
- inputSchema: {
303
- type: "object",
304
- properties: {
305
- task: { type: "string", description: "Task description" },
306
- implementer: {
307
- type: "string",
308
- enum: ["codex", "claude"],
309
- default: "codex",
310
- description: "Which agent implements code"
311
- },
312
- maxIters: {
313
- type: "number",
314
- default: 2,
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
- record: {
323
- type: "boolean",
324
- default: false,
325
- description: "Record plan & critique into StackMemory context"
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
- recordFrame: {
328
- type: "boolean",
329
- default: false,
330
- description: "Record as real frame with anchors"
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
- required: ["task"]
334
- }
335
- },
336
- {
337
- name: "plan_gate",
338
- description: "Phase 1: Generate a plan and return an approvalId for later execution",
339
- inputSchema: {
340
- type: "object",
341
- properties: {
342
- task: { type: "string", description: "Task description" },
343
- plannerModel: {
344
- type: "string",
345
- description: "Claude model (optional)"
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
- name: "pending_clear",
403
- description: "Clear pending approval-gated plans (by id, all, or olderThanMs)",
404
- inputSchema: {
405
- type: "object",
406
- properties: {
407
- approvalId: {
408
- type: "string",
409
- description: "Clear a single approval by id"
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
- all: {
412
- type: "boolean",
413
- description: "Clear all pending approvals",
414
- default: false
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
- olderThanMs: {
417
- type: "number",
418
- description: "Clear approvals older than this age (ms)"
419
- }
461
+ required: ["task"]
420
462
  }
421
- }
422
- },
423
- {
424
- name: "pending_show",
425
- description: "Show a pending plan by approvalId",
426
- inputSchema: {
427
- type: "object",
428
- properties: {
429
- approvalId: {
430
- type: "string",
431
- description: "Approval id from plan_gate"
432
- }
433
- },
434
- required: ["approvalId"]
435
- }
436
- },
437
- {
438
- name: "plan_only",
439
- description: "Generate an implementation plan (Claude) and return JSON only",
440
- inputSchema: {
441
- type: "object",
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
- execute: {
465
- type: "boolean",
466
- default: false,
467
- description: "Actually run codex-sm (otherwise dry-run)"
468
- }
469
- },
470
- required: ["prompt"]
471
- }
472
- },
473
- {
474
- name: "call_claude",
475
- description: "Invoke Claude with a prompt (Anthropic SDK)",
476
- inputSchema: {
477
- type: "object",
478
- properties: {
479
- prompt: { type: "string", description: "Prompt for Claude" },
480
- model: {
481
- type: "string",
482
- description: "Claude model (optional)"
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
- system: {
485
- type: "string",
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 for user memory management
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: "diffmem_get_user_context",
959
- description: "Fetch user knowledge and preferences from memory. Use to personalize responses based on learned user patterns.",
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
- categories: {
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
- type: "string",
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
- limit: {
978
- type: "number",
979
- default: 10,
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: "diffmem_store_learning",
987
- description: "Store a new insight about the user (preference, expertise, pattern, or correction)",
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
- content: {
1008
+ goal: {
992
1009
  type: "string",
993
- description: "The insight to store"
1010
+ description: "What this task should accomplish"
994
1011
  },
995
- category: {
1012
+ prompt: {
996
1013
  type: "string",
997
- enum: [
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
- confidence: {
1007
- type: "number",
1008
- minimum: 0,
1009
- maximum: 1,
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
- context: {
1014
- type: "object",
1015
- description: "Additional context for the insight"
1016
- }
1021
+ parent_id: { type: "string", description: "Parent task ID" }
1017
1022
  },
1018
- required: ["content", "category"]
1023
+ required: ["goal"]
1019
1024
  }
1020
1025
  },
1021
1026
  {
1022
- name: "diffmem_search",
1023
- description: "Semantic search across user memories. Find relevant past insights and preferences.",
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
- query: {
1032
+ task_id: {
1028
1033
  type: "string",
1029
- description: "Search query"
1034
+ description: "Task ID to complete"
1030
1035
  },
1031
- timeRange: {
1036
+ result: {
1032
1037
  type: "string",
1033
- enum: ["day", "week", "month", "all"],
1034
- default: "all",
1035
- description: "Time range filter"
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
- minConfidence: {
1038
- type: "number",
1039
- minimum: 0,
1040
- maximum: 1,
1041
- default: 0.5,
1042
- description: "Minimum confidence threshold"
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: "Maximum results"
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: ["query"]
1137
+ required: ["content"]
1051
1138
  }
1052
1139
  },
1053
1140
  {
1054
- name: "diffmem_status",
1055
- description: "Check DiffMem connection status and memory statistics",
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);