@stackmemoryai/stackmemory 1.2.7 → 1.2.8

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.
@@ -31,6 +31,12 @@ import { LLMContextRetrieval } from "../../core/retrieval/index.js";
31
31
  import { DiscoveryHandlers } from "./handlers/discovery-handlers.js";
32
32
  import { DiffMemHandlers } from "./handlers/diffmem-handlers.js";
33
33
  import { GreptileHandlers } from "./handlers/greptile-handlers.js";
34
+ import { CordHandlers } from "./handlers/cord-handlers.js";
35
+ import { TeamHandlers } from "./handlers/team-handlers.js";
36
+ import { SQLiteAdapter } from "../../core/database/sqlite-adapter.js";
37
+ import {
38
+ generateChronologicalDigest
39
+ } from "../../core/digest/chronological-digest.js";
34
40
  import { fuzzyEdit } from "../../utils/fuzzy-edit.js";
35
41
  import { v4 as uuidv4 } from "uuid";
36
42
  import {
@@ -66,6 +72,8 @@ class LocalStackMemoryMCP {
66
72
  diffMemHandlers;
67
73
  greptileHandlers;
68
74
  providerHandlers = null;
75
+ cordHandlers = null;
76
+ teamHandlers = null;
69
77
  pendingPlans = /* @__PURE__ */ new Map();
70
78
  constructor() {
71
79
  this.projectRoot = this.findProjectRoot();
@@ -139,6 +147,7 @@ class LocalStackMemoryMCP {
139
147
  });
140
148
  this.diffMemHandlers = new DiffMemHandlers();
141
149
  this.greptileHandlers = new GreptileHandlers();
150
+ this.initCordTeamHandlers();
142
151
  this.initProviderHandlers();
143
152
  this.setupHandlers();
144
153
  this.loadInitialContext();
@@ -261,6 +270,27 @@ ${summary}...`, 0.8);
261
270
  this.contexts.set(ctx.id, ctx);
262
271
  });
263
272
  }
273
+ async initCordTeamHandlers() {
274
+ try {
275
+ const dbPath = join(this.projectRoot, ".stackmemory", "context.db");
276
+ const adapter = new SQLiteAdapter(this.projectId, {
277
+ dbPath,
278
+ walMode: true
279
+ });
280
+ await adapter.connect();
281
+ this.cordHandlers = new CordHandlers({
282
+ frameManager: this.frameManager,
283
+ dbAdapter: adapter
284
+ });
285
+ this.teamHandlers = new TeamHandlers({
286
+ frameManager: this.frameManager,
287
+ dbAdapter: adapter
288
+ });
289
+ logger.info("Cord and Team handlers initialized");
290
+ } catch (error) {
291
+ logger.warn("Failed to initialize Cord/Team handlers", { error });
292
+ }
293
+ }
264
294
  async initProviderHandlers() {
265
295
  if (!isFeatureEnabled("multiProvider")) return;
266
296
  try {
@@ -296,199 +326,180 @@ ${summary}...`, 0.8);
296
326
  }
297
327
  }
298
328
  },
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)"
329
+ // Planning tools (only when ANTHROPIC_API_KEY is set)
330
+ ...process.env.ANTHROPIC_API_KEY ? [
331
+ {
332
+ name: "plan_gate",
333
+ description: "Phase 1: Generate a plan and return an approvalId for later execution",
334
+ inputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ task: {
338
+ type: "string",
339
+ description: "Task description"
340
+ },
341
+ plannerModel: {
342
+ type: "string",
343
+ description: "Claude model (optional)"
344
+ }
321
345
  },
322
- record: {
323
- type: "boolean",
324
- default: false,
325
- description: "Record plan & critique into StackMemory context"
346
+ required: ["task"]
347
+ }
348
+ },
349
+ {
350
+ name: "approve_plan",
351
+ description: "Phase 2: Execute a previously generated plan by approvalId",
352
+ inputSchema: {
353
+ type: "object",
354
+ properties: {
355
+ approvalId: {
356
+ type: "string",
357
+ description: "Id from plan_gate"
358
+ },
359
+ implementer: {
360
+ type: "string",
361
+ enum: ["codex", "claude"],
362
+ default: "codex",
363
+ description: "Which agent implements code"
364
+ },
365
+ maxIters: { type: "number", default: 2 },
366
+ recordFrame: { type: "boolean", default: true },
367
+ execute: { type: "boolean", default: true }
326
368
  },
327
- recordFrame: {
328
- type: "boolean",
329
- default: false,
330
- description: "Record as real frame with anchors"
369
+ required: ["approvalId"]
370
+ }
371
+ },
372
+ {
373
+ name: "pending_list",
374
+ description: "List pending approval-gated plans (supports filters)",
375
+ inputSchema: {
376
+ type: "object",
377
+ properties: {
378
+ taskContains: {
379
+ type: "string",
380
+ description: "Filter tasks containing this substring"
381
+ },
382
+ olderThanMs: {
383
+ type: "number",
384
+ description: "Only items older than this age (ms)"
385
+ },
386
+ newerThanMs: {
387
+ type: "number",
388
+ description: "Only items newer than this age (ms)"
389
+ },
390
+ sort: {
391
+ type: "string",
392
+ enum: ["asc", "desc"],
393
+ description: "Sort by createdAt"
394
+ },
395
+ limit: {
396
+ type: "number",
397
+ description: "Max items to return"
398
+ }
331
399
  }
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)"
400
+ }
401
+ },
402
+ {
403
+ name: "pending_clear",
404
+ description: "Clear pending approval-gated plans (by id, all, or olderThanMs)",
405
+ inputSchema: {
406
+ type: "object",
407
+ properties: {
408
+ approvalId: {
409
+ type: "string",
410
+ description: "Clear a single approval by id"
411
+ },
412
+ all: {
413
+ type: "boolean",
414
+ description: "Clear all pending approvals",
415
+ default: false
416
+ },
417
+ olderThanMs: {
418
+ type: "number",
419
+ description: "Clear approvals older than this age (ms)"
420
+ }
346
421
  }
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
422
  }
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"
423
+ },
424
+ {
425
+ name: "pending_show",
426
+ description: "Show a pending plan by approvalId",
427
+ inputSchema: {
428
+ type: "object",
429
+ properties: {
430
+ approvalId: {
431
+ type: "string",
432
+ description: "Approval id from plan_gate"
433
+ }
410
434
  },
411
- all: {
412
- type: "boolean",
413
- description: "Clear all pending approvals",
414
- default: false
435
+ required: ["approvalId"]
436
+ }
437
+ },
438
+ {
439
+ name: "plan_only",
440
+ description: "Generate an implementation plan (Claude) and return JSON only",
441
+ inputSchema: {
442
+ type: "object",
443
+ properties: {
444
+ task: {
445
+ type: "string",
446
+ description: "Task description"
447
+ },
448
+ plannerModel: {
449
+ type: "string",
450
+ description: "Claude model for planning (optional)"
451
+ }
415
452
  },
416
- olderThanMs: {
417
- type: "number",
418
- description: "Clear approvals older than this age (ms)"
419
- }
453
+ required: ["task"]
420
454
  }
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"
455
+ },
456
+ {
457
+ name: "call_codex",
458
+ description: "Invoke Codex via codex-sm with a prompt and args; dry-run by default",
459
+ inputSchema: {
460
+ type: "object",
461
+ properties: {
462
+ prompt: {
463
+ type: "string",
464
+ description: "Prompt for Codex"
465
+ },
466
+ args: {
467
+ type: "array",
468
+ items: { type: "string" },
469
+ description: "Additional CLI args for codex-sm"
470
+ },
471
+ execute: {
472
+ type: "boolean",
473
+ default: false,
474
+ description: "Actually run codex-sm (otherwise dry-run)"
475
+ }
463
476
  },
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)"
477
+ required: ["prompt"]
478
+ }
479
+ },
480
+ {
481
+ name: "call_claude",
482
+ description: "Invoke Claude with a prompt (Anthropic SDK)",
483
+ inputSchema: {
484
+ type: "object",
485
+ properties: {
486
+ prompt: {
487
+ type: "string",
488
+ description: "Prompt for Claude"
489
+ },
490
+ model: {
491
+ type: "string",
492
+ description: "Claude model (optional)"
493
+ },
494
+ system: {
495
+ type: "string",
496
+ description: "System prompt (optional)"
497
+ }
483
498
  },
484
- system: {
485
- type: "string",
486
- description: "System prompt (optional)"
487
- }
488
- },
489
- required: ["prompt"]
499
+ required: ["prompt"]
500
+ }
490
501
  }
491
- },
502
+ ] : [],
492
503
  {
493
504
  name: "add_decision",
494
505
  description: "Record a decision or important information",
@@ -953,109 +964,190 @@ ${summary}...`, 0.8);
953
964
  required: ["query"]
954
965
  }
955
966
  },
956
- // DiffMem tools for user memory management
967
+ // DiffMem tools (only when DIFFMEM_ENDPOINT or DIFFMEM_ENABLED is set)
968
+ ...process.env.DIFFMEM_ENDPOINT || process.env.DIFFMEM_ENABLED === "true" ? this.diffMemHandlers.getToolDefinitions() : [],
969
+ // Cord task orchestration tools
957
970
  {
958
- name: "diffmem_get_user_context",
959
- description: "Fetch user knowledge and preferences from memory. Use to personalize responses based on learned user patterns.",
971
+ name: "cord_spawn",
972
+ description: "Create a subtask with clean context (spawn). Child sees only its prompt and completed blocker results.",
960
973
  inputSchema: {
961
974
  type: "object",
962
975
  properties: {
963
- categories: {
976
+ goal: {
977
+ type: "string",
978
+ description: "What this task should accomplish"
979
+ },
980
+ prompt: {
981
+ type: "string",
982
+ description: "Detailed instructions for the task"
983
+ },
984
+ blocked_by: {
964
985
  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"
986
+ items: { type: "string" },
987
+ description: "Task IDs that must complete first"
976
988
  },
977
- limit: {
978
- type: "number",
979
- default: 10,
980
- description: "Maximum memories to return"
981
- }
982
- }
989
+ parent_id: { type: "string", description: "Parent task ID" }
990
+ },
991
+ required: ["goal"]
983
992
  }
984
993
  },
985
994
  {
986
- name: "diffmem_store_learning",
987
- description: "Store a new insight about the user (preference, expertise, pattern, or correction)",
995
+ name: "cord_fork",
996
+ description: "Create a subtask with full sibling context (fork). Child sees prompt, blocker results, AND completed sibling results.",
988
997
  inputSchema: {
989
998
  type: "object",
990
999
  properties: {
991
- content: {
1000
+ goal: {
992
1001
  type: "string",
993
- description: "The insight to store"
1002
+ description: "What this task should accomplish"
994
1003
  },
995
- category: {
1004
+ prompt: {
996
1005
  type: "string",
997
- enum: [
998
- "preference",
999
- "expertise",
1000
- "project_knowledge",
1001
- "pattern",
1002
- "correction"
1003
- ],
1004
- description: "Category of the insight"
1006
+ description: "Detailed instructions for the task"
1005
1007
  },
1006
- confidence: {
1007
- type: "number",
1008
- minimum: 0,
1009
- maximum: 1,
1010
- default: 0.7,
1011
- description: "Confidence level (0-1)"
1008
+ blocked_by: {
1009
+ type: "array",
1010
+ items: { type: "string" },
1011
+ description: "Task IDs that must complete first"
1012
1012
  },
1013
- context: {
1014
- type: "object",
1015
- description: "Additional context for the insight"
1016
- }
1013
+ parent_id: { type: "string", description: "Parent task ID" }
1017
1014
  },
1018
- required: ["content", "category"]
1015
+ required: ["goal"]
1019
1016
  }
1020
1017
  },
1021
1018
  {
1022
- name: "diffmem_search",
1023
- description: "Semantic search across user memories. Find relevant past insights and preferences.",
1019
+ name: "cord_complete",
1020
+ description: "Mark a cord task as completed with a result. Automatically unblocks dependent tasks.",
1024
1021
  inputSchema: {
1025
1022
  type: "object",
1026
1023
  properties: {
1027
- query: {
1024
+ task_id: {
1028
1025
  type: "string",
1029
- description: "Search query"
1026
+ description: "Task ID to complete"
1030
1027
  },
1031
- timeRange: {
1028
+ result: {
1032
1029
  type: "string",
1033
- enum: ["day", "week", "month", "all"],
1034
- default: "all",
1035
- description: "Time range filter"
1030
+ description: "The result/output of this task"
1031
+ }
1032
+ },
1033
+ required: ["task_id", "result"]
1034
+ }
1035
+ },
1036
+ {
1037
+ name: "cord_ask",
1038
+ description: "Create an ask task \u2014 a question that needs an answer before dependent tasks can proceed.",
1039
+ inputSchema: {
1040
+ type: "object",
1041
+ properties: {
1042
+ question: {
1043
+ type: "string",
1044
+ description: "The question to ask"
1036
1045
  },
1037
- minConfidence: {
1038
- type: "number",
1039
- minimum: 0,
1040
- maximum: 1,
1041
- default: 0.5,
1042
- description: "Minimum confidence threshold"
1046
+ options: {
1047
+ type: "array",
1048
+ items: { type: "string" },
1049
+ description: "Optional list of answer choices"
1050
+ },
1051
+ parent_id: { type: "string", description: "Parent task ID" }
1052
+ },
1053
+ required: ["question"]
1054
+ }
1055
+ },
1056
+ {
1057
+ name: "cord_tree",
1058
+ description: "View the cord task tree with context scoping. Shows active, blocked, or completed tasks.",
1059
+ inputSchema: {
1060
+ type: "object",
1061
+ properties: {
1062
+ task_id: {
1063
+ type: "string",
1064
+ description: "Root task ID to show subtree (omit for full tree)"
1043
1065
  },
1066
+ include_results: {
1067
+ type: "boolean",
1068
+ default: false,
1069
+ description: "Include task results in output"
1070
+ }
1071
+ }
1072
+ }
1073
+ },
1074
+ // Team collaboration tools
1075
+ {
1076
+ name: "team_context_get",
1077
+ description: "Get context from other agents working on the same project. Returns recent frames and shared anchors.",
1078
+ inputSchema: {
1079
+ type: "object",
1080
+ properties: {
1044
1081
  limit: {
1045
1082
  type: "number",
1046
1083
  default: 10,
1047
- description: "Maximum results"
1084
+ description: "Max frames to return"
1085
+ },
1086
+ types: {
1087
+ type: "array",
1088
+ items: { type: "string" },
1089
+ description: "Filter by frame types"
1090
+ },
1091
+ since: {
1092
+ type: "number",
1093
+ description: "Only frames created after this timestamp (epoch ms)"
1094
+ }
1095
+ }
1096
+ }
1097
+ },
1098
+ {
1099
+ name: "team_context_share",
1100
+ description: "Share a piece of context with other agents. Creates a high-priority anchor visible to team_context_get.",
1101
+ inputSchema: {
1102
+ type: "object",
1103
+ properties: {
1104
+ content: {
1105
+ type: "string",
1106
+ description: "The context to share"
1107
+ },
1108
+ type: {
1109
+ type: "string",
1110
+ enum: [
1111
+ "FACT",
1112
+ "DECISION",
1113
+ "CONSTRAINT",
1114
+ "INTERFACE_CONTRACT",
1115
+ "TODO",
1116
+ "RISK"
1117
+ ],
1118
+ default: "FACT",
1119
+ description: "Type of context"
1120
+ },
1121
+ priority: {
1122
+ type: "number",
1123
+ minimum: 1,
1124
+ maximum: 10,
1125
+ default: 8,
1126
+ description: "Priority level (1-10)"
1048
1127
  }
1049
1128
  },
1050
- required: ["query"]
1129
+ required: ["content"]
1051
1130
  }
1052
1131
  },
1053
1132
  {
1054
- name: "diffmem_status",
1055
- description: "Check DiffMem connection status and memory statistics",
1133
+ name: "team_search",
1134
+ description: "Search across all agents' context in the project. Uses full-text search across all sessions.",
1056
1135
  inputSchema: {
1057
1136
  type: "object",
1058
- properties: {}
1137
+ properties: {
1138
+ query: { type: "string", description: "Search query" },
1139
+ limit: {
1140
+ type: "number",
1141
+ default: 20,
1142
+ description: "Maximum results to return"
1143
+ },
1144
+ include_events: {
1145
+ type: "boolean",
1146
+ default: false,
1147
+ description: "Include events in results"
1148
+ }
1149
+ },
1150
+ required: ["query"]
1059
1151
  }
1060
1152
  },
1061
1153
  // Greptile tools (only active when GREPTILE_API_KEY is set)
@@ -1160,7 +1252,23 @@ ${summary}...`, 0.8);
1160
1252
  required: ["batchId"]
1161
1253
  }
1162
1254
  }
1163
- ] : []
1255
+ ] : [],
1256
+ // Digest tool
1257
+ {
1258
+ name: "sm_digest",
1259
+ description: "Generate a chronological activity digest for a time period (today/yesterday/week)",
1260
+ inputSchema: {
1261
+ type: "object",
1262
+ properties: {
1263
+ period: {
1264
+ type: "string",
1265
+ enum: ["today", "yesterday", "week"],
1266
+ description: "Time period for the digest"
1267
+ }
1268
+ },
1269
+ required: ["period"]
1270
+ }
1271
+ }
1164
1272
  ]
1165
1273
  };
1166
1274
  }
@@ -1330,6 +1438,96 @@ ${summary}...`, 0.8);
1330
1438
  case "sm_edit":
1331
1439
  result = await this.handleSmEdit(args);
1332
1440
  break;
1441
+ // Cord task orchestration
1442
+ case "cord_spawn":
1443
+ if (!this.cordHandlers) {
1444
+ result = {
1445
+ content: [
1446
+ { type: "text", text: "Cord handlers not initialized." }
1447
+ ]
1448
+ };
1449
+ } else {
1450
+ result = await this.cordHandlers.handleCordSpawn(args);
1451
+ }
1452
+ break;
1453
+ case "cord_fork":
1454
+ if (!this.cordHandlers) {
1455
+ result = {
1456
+ content: [
1457
+ { type: "text", text: "Cord handlers not initialized." }
1458
+ ]
1459
+ };
1460
+ } else {
1461
+ result = await this.cordHandlers.handleCordFork(args);
1462
+ }
1463
+ break;
1464
+ case "cord_complete":
1465
+ if (!this.cordHandlers) {
1466
+ result = {
1467
+ content: [
1468
+ { type: "text", text: "Cord handlers not initialized." }
1469
+ ]
1470
+ };
1471
+ } else {
1472
+ result = await this.cordHandlers.handleCordComplete(args);
1473
+ }
1474
+ break;
1475
+ case "cord_ask":
1476
+ if (!this.cordHandlers) {
1477
+ result = {
1478
+ content: [
1479
+ { type: "text", text: "Cord handlers not initialized." }
1480
+ ]
1481
+ };
1482
+ } else {
1483
+ result = await this.cordHandlers.handleCordAsk(args);
1484
+ }
1485
+ break;
1486
+ case "cord_tree":
1487
+ if (!this.cordHandlers) {
1488
+ result = {
1489
+ content: [
1490
+ { type: "text", text: "Cord handlers not initialized." }
1491
+ ]
1492
+ };
1493
+ } else {
1494
+ result = await this.cordHandlers.handleCordTree(args);
1495
+ }
1496
+ break;
1497
+ // Team collaboration
1498
+ case "team_context_get":
1499
+ if (!this.teamHandlers) {
1500
+ result = {
1501
+ content: [
1502
+ { type: "text", text: "Team handlers not initialized." }
1503
+ ]
1504
+ };
1505
+ } else {
1506
+ result = await this.teamHandlers.handleTeamContextGet(args);
1507
+ }
1508
+ break;
1509
+ case "team_context_share":
1510
+ if (!this.teamHandlers) {
1511
+ result = {
1512
+ content: [
1513
+ { type: "text", text: "Team handlers not initialized." }
1514
+ ]
1515
+ };
1516
+ } else {
1517
+ result = await this.teamHandlers.handleTeamContextShare(args);
1518
+ }
1519
+ break;
1520
+ case "team_search":
1521
+ if (!this.teamHandlers) {
1522
+ result = {
1523
+ content: [
1524
+ { type: "text", text: "Team handlers not initialized." }
1525
+ ]
1526
+ };
1527
+ } else {
1528
+ result = await this.teamHandlers.handleTeamSearch(args);
1529
+ }
1530
+ break;
1333
1531
  // Provider tools
1334
1532
  case "delegate_to_model":
1335
1533
  if (!this.providerHandlers) {
@@ -1379,6 +1577,9 @@ ${summary}...`, 0.8);
1379
1577
  );
1380
1578
  }
1381
1579
  break;
1580
+ case "sm_digest":
1581
+ result = await this.handleSmDigest(args);
1582
+ break;
1382
1583
  default:
1383
1584
  throw new Error(`Unknown tool: ${name}`);
1384
1585
  }
@@ -2851,6 +3052,31 @@ ${typeBreakdown}`
2851
3052
  matchedText: editResult.match.matchedText.length > 200 ? editResult.match.matchedText.slice(0, 200) + "..." : editResult.match.matchedText
2852
3053
  };
2853
3054
  }
3055
+ async handleSmDigest(args) {
3056
+ const period = String(args.period || "today");
3057
+ if (!["today", "yesterday", "week"].includes(period)) {
3058
+ return {
3059
+ content: [
3060
+ {
3061
+ type: "text",
3062
+ text: `Invalid period "${period}". Use: today, yesterday, week`
3063
+ }
3064
+ ],
3065
+ isError: true
3066
+ };
3067
+ }
3068
+ const markdown = generateChronologicalDigest(
3069
+ this.db,
3070
+ period,
3071
+ this.projectId
3072
+ );
3073
+ const outputPath = join(this.projectRoot, ".stackmemory", `${period}.md`);
3074
+ writeFileSync(outputPath, markdown);
3075
+ return {
3076
+ content: [{ type: "text", text: markdown }],
3077
+ isError: false
3078
+ };
3079
+ }
2854
3080
  async start() {
2855
3081
  const transport = new StdioServerTransport();
2856
3082
  await this.server.connect(transport);