@stackmemoryai/stackmemory 1.2.2 → 1.2.6

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 (77) hide show
  1. package/README.md +10 -6
  2. package/dist/src/cli/claude-sm.js +33 -4
  3. package/dist/src/cli/codex-sm-danger.js +4 -1
  4. package/dist/src/cli/codex-sm.js +6 -8
  5. package/dist/src/cli/commands/config.js +0 -81
  6. package/dist/src/cli/commands/context-rehydrate.js +133 -47
  7. package/dist/src/cli/commands/db.js +35 -8
  8. package/dist/src/cli/commands/handoff.js +1 -1
  9. package/dist/src/cli/commands/linear.js +9 -0
  10. package/dist/src/cli/commands/ralph.js +2 -2
  11. package/dist/src/cli/commands/setup.js +2 -2
  12. package/dist/src/cli/commands/signup.js +3 -1
  13. package/dist/src/cli/commands/skills.js +108 -1
  14. package/dist/src/cli/commands/storage-tier.js +26 -8
  15. package/dist/src/cli/index.js +1 -57
  16. package/dist/src/core/config/feature-flags.js +0 -4
  17. package/dist/src/core/context/dual-stack-manager.js +10 -3
  18. package/dist/src/core/context/frame-database.js +32 -0
  19. package/dist/src/core/context/frame-handoff-manager.js +2 -2
  20. package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
  21. package/dist/src/core/context/index.js +2 -2
  22. package/dist/src/core/database/sqlite-adapter.js +161 -1
  23. package/dist/src/core/digest/frame-digest-integration.js +1 -1
  24. package/dist/src/core/digest/index.js +1 -1
  25. package/dist/src/core/execution/parallel-executor.js +5 -1
  26. package/dist/src/core/projects/project-isolation.js +18 -4
  27. package/dist/src/core/security/index.js +2 -0
  28. package/dist/src/core/security/input-sanitizer.js +23 -0
  29. package/dist/src/core/utils/update-checker.js +10 -6
  30. package/dist/src/daemon/daemon-config.js +2 -1
  31. package/dist/src/daemon/services/auto-save-service.js +121 -0
  32. package/dist/src/daemon/services/maintenance-service.js +76 -1
  33. package/dist/src/features/sweep/prompt-builder.js +2 -2
  34. package/dist/src/hooks/daemon.js +0 -8
  35. package/dist/src/integrations/linear/config.js +3 -1
  36. package/dist/src/integrations/linear/sync.js +18 -5
  37. package/dist/src/integrations/linear/webhook.js +0 -16
  38. package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
  39. package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
  40. package/dist/src/integrations/mcp/handlers/index.js +55 -9
  41. package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
  42. package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
  43. package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
  44. package/dist/src/integrations/mcp/index.js +2 -2
  45. package/dist/src/integrations/mcp/refactored-server.js +31 -10
  46. package/dist/src/integrations/mcp/server.js +0 -130
  47. package/dist/src/integrations/mcp/tool-definitions.js +196 -67
  48. package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
  49. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
  50. package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
  51. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
  52. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
  53. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
  54. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
  55. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
  56. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
  57. package/dist/src/skills/claude-skills.js +37 -103
  58. package/dist/src/skills/theory-skill.js +191 -0
  59. package/dist/src/utils/hook-installer.js +16 -0
  60. package/package.json +6 -6
  61. package/scripts/install-claude-hooks-auto.js +8 -0
  62. package/templates/claude-hooks/cord-trace.js +225 -0
  63. package/templates/claude-hooks/theory-capture.js +100 -0
  64. package/dist/src/core/config/storage-config.js +0 -114
  65. package/dist/src/core/storage/chromadb-adapter.js +0 -379
  66. package/dist/src/hooks/graphiti-hooks.js +0 -253
  67. package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
  68. package/dist/src/integrations/graphiti/client.js +0 -115
  69. package/dist/src/integrations/graphiti/config.js +0 -17
  70. package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
  71. package/dist/src/integrations/graphiti/types.js +0 -4
  72. package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
  73. package/dist/src/skills/repo-ingestion-skill.js +0 -631
  74. package/templates/claude-hooks/chromadb-wrapper +0 -21
  75. /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
  76. /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
  77. /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
@@ -5,7 +5,6 @@ const __dirname = __pathDirname(__filename);
5
5
  import { readFileSync, writeFileSync, existsSync } from "fs";
6
6
  import { join } from "path";
7
7
  import { logger } from "../../core/monitoring/logger.js";
8
- import { IntegrationError, ErrorCode } from "../../core/errors/index.js";
9
8
  import { LinearClient } from "./client.js";
10
9
  class LinearSyncEngine {
11
10
  taskStore;
@@ -15,6 +14,7 @@ class LinearSyncEngine {
15
14
  mappings = /* @__PURE__ */ new Map();
16
15
  projectRoot;
17
16
  mappingsPath;
17
+ _isConfigured = true;
18
18
  constructor(taskStore, authManager, config, projectRoot) {
19
19
  this.taskStore = taskStore;
20
20
  this.authManager = authManager;
@@ -33,10 +33,9 @@ class LinearSyncEngine {
33
33
  } else {
34
34
  const tokens = this.authManager.loadTokens();
35
35
  if (!tokens) {
36
- throw new IntegrationError(
37
- 'Linear API key or authentication tokens not found. Set LINEAR_API_KEY environment variable or run "stackmemory linear setup" first.',
38
- ErrorCode.LINEAR_SYNC_FAILED
39
- );
36
+ logger.info("Linear API key not configured \u2014 skipping Linear sync");
37
+ this._isConfigured = false;
38
+ return;
40
39
  }
41
40
  this.linearClient = new LinearClient({
42
41
  apiKey: tokens.accessToken,
@@ -49,6 +48,12 @@ class LinearSyncEngine {
49
48
  }
50
49
  this.loadMappings();
51
50
  }
51
+ /**
52
+ * Check if Linear credentials are available
53
+ */
54
+ get isConfigured() {
55
+ return this._isConfigured;
56
+ }
52
57
  /**
53
58
  * Update sync configuration
54
59
  */
@@ -59,6 +64,14 @@ class LinearSyncEngine {
59
64
  * Perform bi-directional sync
60
65
  */
61
66
  async sync() {
67
+ if (!this._isConfigured) {
68
+ return {
69
+ success: true,
70
+ synced: { toLinear: 0, fromLinear: 0, updated: 0 },
71
+ conflicts: [],
72
+ errors: []
73
+ };
74
+ }
62
75
  if (!this.config.enabled) {
63
76
  return {
64
77
  success: false,
@@ -22,7 +22,6 @@ function getOptionalEnv(key) {
22
22
  class LinearWebhookHandler {
23
23
  syncEngine;
24
24
  taskStore;
25
- graphitiBridge;
26
25
  webhookSecret;
27
26
  constructor(webhookSecret) {
28
27
  this.webhookSecret = webhookSecret || process.env["LINEAR_WEBHOOK_SECRET"];
@@ -39,12 +38,6 @@ class LinearWebhookHandler {
39
38
  setTaskStore(taskStore) {
40
39
  this.taskStore = taskStore;
41
40
  }
42
- /**
43
- * Set the Graphiti bridge for knowledge graph sync
44
- */
45
- setGraphitiBridge(bridge) {
46
- this.graphitiBridge = bridge;
47
- }
48
41
  /**
49
42
  * Verify webhook signature
50
43
  */
@@ -111,15 +104,6 @@ class LinearWebhookHandler {
111
104
  default:
112
105
  logger.warn(`Unknown webhook action: ${payload.action}`);
113
106
  }
114
- if (this.graphitiBridge) {
115
- this.graphitiBridge.processWebhook(payload).catch((err) => {
116
- logger.debug("Linear-Graphiti bridge error", {
117
- action: payload.action,
118
- identifier: payload.data.identifier,
119
- error: err instanceof Error ? err.message : String(err)
120
- });
121
- });
122
- }
123
107
  }
124
108
  /**
125
109
  * Handle issue created in Linear
@@ -27,7 +27,12 @@ class CodeExecutionHandler {
27
27
  * Execute code in a controlled environment
28
28
  */
29
29
  async executeCode(params) {
30
- const { language, code, workingDirectory, timeout = EXECUTION_TIMEOUT } = params;
30
+ const {
31
+ language,
32
+ code,
33
+ workingDirectory,
34
+ timeout = EXECUTION_TIMEOUT
35
+ } = params;
31
36
  if (!this.allowedLanguages.includes(language.toLowerCase())) {
32
37
  return {
33
38
  success: false,
@@ -43,7 +48,12 @@ class CodeExecutionHandler {
43
48
  );
44
49
  try {
45
50
  await fs.writeFile(tempFile, code, "utf-8");
46
- const result = await this.runCode(language, tempFile, workingDirectory || this.sandboxDir, timeout);
51
+ const result = await this.runCode(
52
+ language,
53
+ tempFile,
54
+ workingDirectory || this.sandboxDir,
55
+ timeout
56
+ );
47
57
  await fs.unlink(tempFile).catch(() => {
48
58
  });
49
59
  return result;
@@ -98,8 +108,15 @@ class CodeExecutionHandler {
98
108
  clearTimeout(timeoutId);
99
109
  let outputFile;
100
110
  if (truncated) {
101
- outputFile = join(this.sandboxDir, `output_${randomBytes(8).toString("hex")}.txt`);
102
- await fs.writeFile(outputFile, stdout + "\n---STDERR---\n" + stderr, "utf-8").catch(() => {
111
+ outputFile = join(
112
+ this.sandboxDir,
113
+ `output_${randomBytes(8).toString("hex")}.txt`
114
+ );
115
+ await fs.writeFile(
116
+ outputFile,
117
+ stdout + "\n---STDERR---\n" + stderr,
118
+ "utf-8"
119
+ ).catch(() => {
103
120
  });
104
121
  }
105
122
  resolve({
@@ -175,12 +192,21 @@ class CodeExecutionHandler {
175
192
  const warnings = [];
176
193
  const dangerousPatterns = [
177
194
  { pattern: /import\s+os/i, message: "Importing os module detected" },
178
- { pattern: /import\s+subprocess/i, message: "Subprocess module detected" },
195
+ {
196
+ pattern: /import\s+subprocess/i,
197
+ message: "Subprocess module detected"
198
+ },
179
199
  { pattern: /exec\s*\(/i, message: "exec() function detected" },
180
200
  { pattern: /eval\s*\(/i, message: "eval() function detected" },
181
201
  { pattern: /__import__/i, message: "__import__ detected" },
182
- { pattern: /open\s*\([^)]*['"]\//i, message: "Absolute path file access detected" },
183
- { pattern: /require\s*\([^)]*child_process/i, message: "child_process module detected" },
202
+ {
203
+ pattern: /open\s*\([^)]*['"]\//i,
204
+ message: "Absolute path file access detected"
205
+ },
206
+ {
207
+ pattern: /require\s*\([^)]*child_process/i,
208
+ message: "child_process module detected"
209
+ },
184
210
  { pattern: /require\s*\([^)]*fs/i, message: "fs module access detected" }
185
211
  ];
186
212
  for (const { pattern, message } of dangerousPatterns) {
@@ -0,0 +1,397 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { randomUUID } from "crypto";
6
+ import { logger } from "../../../core/monitoring/logger.js";
7
+ class CordHandlers {
8
+ constructor(deps) {
9
+ this.deps = deps;
10
+ }
11
+ MAX_DEPTH = 10;
12
+ MAX_TASKS = 50;
13
+ /**
14
+ * cord_spawn — create a child task with clean context (only blocker results visible)
15
+ */
16
+ async handleCordSpawn(args) {
17
+ return this.createTask(args, "spawn");
18
+ }
19
+ /**
20
+ * cord_fork — create a child task with full sibling context
21
+ */
22
+ async handleCordFork(args) {
23
+ return this.createTask(args, "fork");
24
+ }
25
+ /**
26
+ * cord_complete — mark a task as completed and unblock dependents
27
+ */
28
+ async handleCordComplete(args) {
29
+ try {
30
+ const { task_id, result } = args;
31
+ if (!task_id) throw new Error("task_id is required");
32
+ if (result === void 0 || result === null) {
33
+ throw new Error("result is required");
34
+ }
35
+ const db = this.deps.dbAdapter.getRawDatabase();
36
+ if (!db) throw new Error("Database not available");
37
+ const task = db.prepare("SELECT * FROM cord_tasks WHERE task_id = ?").get(task_id);
38
+ if (!task) throw new Error(`Task not found: ${task_id}`);
39
+ if (task.status === "completed") {
40
+ throw new Error(`Task already completed: ${task_id}`);
41
+ }
42
+ const now = Math.floor(Date.now() / 1e3);
43
+ db.prepare(
44
+ "UPDATE cord_tasks SET status = ?, result = ?, completed_at = ? WHERE task_id = ?"
45
+ ).run("completed", String(result), now, task_id);
46
+ const unblocked = this.checkAndUnblockDependents(db, task_id);
47
+ logger.info("Cord task completed", { task_id, unblocked });
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: `Task ${task_id} completed.${unblocked.length > 0 ? ` Unblocked: ${unblocked.join(", ")}` : ""}`
53
+ }
54
+ ],
55
+ metadata: { task_id, status: "completed", unblocked }
56
+ };
57
+ } catch (error) {
58
+ logger.error(
59
+ "Error completing cord task",
60
+ error instanceof Error ? error : new Error(String(error))
61
+ );
62
+ throw error;
63
+ }
64
+ }
65
+ /**
66
+ * cord_ask — create an "ask" task (question with optional options)
67
+ */
68
+ async handleCordAsk(args) {
69
+ try {
70
+ const { question, options, parent_id } = args;
71
+ if (!question) throw new Error("question is required");
72
+ const db = this.deps.dbAdapter.getRawDatabase();
73
+ if (!db) throw new Error("Database not available");
74
+ const projectId = this.getProjectId();
75
+ const runId = this.getRunId();
76
+ const taskId = randomUUID();
77
+ this.checkTaskLimit(db, projectId);
78
+ let depth = 0;
79
+ if (parent_id) {
80
+ depth = this.computeDepth(db, parent_id);
81
+ }
82
+ const prompt = options ? JSON.stringify({ question, options }) : JSON.stringify({ question });
83
+ db.prepare(
84
+ `INSERT INTO cord_tasks (task_id, parent_id, project_id, run_id, goal, prompt, status, context_mode, depth)
85
+ VALUES (?, ?, ?, ?, ?, ?, 'asked', 'ask', ?)`
86
+ ).run(
87
+ taskId,
88
+ parent_id || null,
89
+ projectId,
90
+ runId,
91
+ question,
92
+ prompt,
93
+ depth
94
+ );
95
+ logger.info("Cord ask created", { task_id: taskId });
96
+ return {
97
+ content: [
98
+ {
99
+ type: "text",
100
+ text: `Ask created: ${taskId} \u2014 "${question}"`
101
+ }
102
+ ],
103
+ metadata: {
104
+ task_id: taskId,
105
+ status: "asked",
106
+ context_mode: "ask",
107
+ question,
108
+ options: options || null
109
+ }
110
+ };
111
+ } catch (error) {
112
+ logger.error(
113
+ "Error creating cord ask",
114
+ error instanceof Error ? error : new Error(String(error))
115
+ );
116
+ throw error;
117
+ }
118
+ }
119
+ /**
120
+ * cord_tree — view the task tree with context scoping
121
+ */
122
+ async handleCordTree(args) {
123
+ try {
124
+ const { task_id, include_results = false } = args;
125
+ const db = this.deps.dbAdapter.getRawDatabase();
126
+ if (!db) throw new Error("Database not available");
127
+ const projectId = this.getProjectId();
128
+ let tasks;
129
+ if (task_id) {
130
+ tasks = this.getSubtree(db, task_id);
131
+ } else {
132
+ tasks = db.prepare(
133
+ "SELECT * FROM cord_tasks WHERE project_id = ? ORDER BY depth ASC, created_at ASC"
134
+ ).all(projectId);
135
+ }
136
+ if (tasks.length === 0) {
137
+ return {
138
+ content: [{ type: "text", text: "No cord tasks found." }],
139
+ metadata: { tasks: [] }
140
+ };
141
+ }
142
+ const taskMap = /* @__PURE__ */ new Map();
143
+ for (const t of tasks) taskMap.set(t.task_id, t);
144
+ const allTasks = db.prepare(
145
+ "SELECT * FROM cord_tasks WHERE project_id = ? ORDER BY depth ASC, created_at ASC"
146
+ ).all(projectId);
147
+ const allTaskMap = /* @__PURE__ */ new Map();
148
+ for (const t of allTasks) allTaskMap.set(t.task_id, t);
149
+ const treeNodes = tasks.map((t) => {
150
+ const blockedBy = JSON.parse(t.blocked_by);
151
+ const node = {
152
+ task_id: t.task_id,
153
+ goal: t.goal,
154
+ status: t.status,
155
+ context_mode: t.context_mode,
156
+ depth: t.depth,
157
+ blocked_by: blockedBy,
158
+ parent_id: t.parent_id
159
+ };
160
+ if (include_results && t.result !== null) {
161
+ node.result = t.result;
162
+ }
163
+ node.visible_context = this.computeVisibleContext(
164
+ t,
165
+ allTaskMap,
166
+ include_results
167
+ );
168
+ return node;
169
+ });
170
+ const summary = tasks.map(
171
+ (t) => `${" ".repeat(t.depth)}[${t.status}] ${t.goal}${t.context_mode === "ask" ? " (ask)" : ""}`
172
+ ).join("\n");
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: `Cord Tree (${tasks.length} tasks):
178
+ ${summary}`
179
+ }
180
+ ],
181
+ metadata: { tasks: treeNodes }
182
+ };
183
+ } catch (error) {
184
+ logger.error(
185
+ "Error getting cord tree",
186
+ error instanceof Error ? error : new Error(String(error))
187
+ );
188
+ throw error;
189
+ }
190
+ }
191
+ // --- Private helpers ---
192
+ async createTask(args, contextMode) {
193
+ try {
194
+ const { goal, prompt = "", blocked_by = [], parent_id } = args;
195
+ if (!goal) throw new Error("goal is required");
196
+ const db = this.deps.dbAdapter.getRawDatabase();
197
+ if (!db) throw new Error("Database not available");
198
+ const projectId = this.getProjectId();
199
+ const runId = this.getRunId();
200
+ const taskId = randomUUID();
201
+ this.checkTaskLimit(db, projectId);
202
+ let depth = 0;
203
+ if (parent_id) {
204
+ depth = this.computeDepth(db, parent_id);
205
+ }
206
+ const blockerIds = Array.isArray(blocked_by) ? blocked_by : [];
207
+ if (blockerIds.length > 0) {
208
+ this.validateBlockers(db, blockerIds);
209
+ this.detectCircularDeps(db, taskId, blockerIds);
210
+ }
211
+ const status = this.initialStatus(db, blockerIds);
212
+ db.prepare(
213
+ `INSERT INTO cord_tasks (task_id, parent_id, project_id, run_id, goal, prompt, status, context_mode, blocked_by, depth)
214
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
215
+ ).run(
216
+ taskId,
217
+ parent_id || null,
218
+ projectId,
219
+ runId,
220
+ goal,
221
+ prompt,
222
+ status,
223
+ contextMode,
224
+ JSON.stringify(blockerIds),
225
+ depth
226
+ );
227
+ logger.info("Cord task created", {
228
+ task_id: taskId,
229
+ context_mode: contextMode,
230
+ status
231
+ });
232
+ return {
233
+ content: [
234
+ {
235
+ type: "text",
236
+ text: `Task ${taskId} created (${contextMode}, ${status}): ${goal}`
237
+ }
238
+ ],
239
+ metadata: {
240
+ task_id: taskId,
241
+ status,
242
+ context_mode: contextMode,
243
+ depth,
244
+ blocked_by: blockerIds
245
+ }
246
+ };
247
+ } catch (error) {
248
+ logger.error(
249
+ "Error creating cord task",
250
+ error instanceof Error ? error : new Error(String(error))
251
+ );
252
+ throw error;
253
+ }
254
+ }
255
+ checkTaskLimit(db, projectId) {
256
+ const row = db.prepare("SELECT COUNT(*) as count FROM cord_tasks WHERE project_id = ?").get(projectId);
257
+ if (row.count >= this.MAX_TASKS) {
258
+ throw new Error(
259
+ `Task limit reached: ${this.MAX_TASKS} tasks per project`
260
+ );
261
+ }
262
+ }
263
+ computeDepth(db, parentId) {
264
+ const parent = db.prepare("SELECT depth FROM cord_tasks WHERE task_id = ?").get(parentId);
265
+ if (!parent) throw new Error(`Parent task not found: ${parentId}`);
266
+ const depth = parent.depth + 1;
267
+ if (depth >= this.MAX_DEPTH) {
268
+ throw new Error(`Max depth exceeded: ${this.MAX_DEPTH}`);
269
+ }
270
+ return depth;
271
+ }
272
+ validateBlockers(db, blockerIds) {
273
+ for (const id of blockerIds) {
274
+ const exists = db.prepare("SELECT 1 FROM cord_tasks WHERE task_id = ?").get(id);
275
+ if (!exists) throw new Error(`Blocker task not found: ${id}`);
276
+ }
277
+ }
278
+ detectCircularDeps(db, newTaskId, blockerIds) {
279
+ const visited = /* @__PURE__ */ new Set();
280
+ const queue = [...blockerIds];
281
+ while (queue.length > 0) {
282
+ const current = queue.shift();
283
+ if (current === newTaskId) {
284
+ throw new Error("Circular dependency detected");
285
+ }
286
+ if (visited.has(current)) continue;
287
+ visited.add(current);
288
+ const task = db.prepare("SELECT blocked_by FROM cord_tasks WHERE task_id = ?").get(current);
289
+ if (task) {
290
+ const deps = JSON.parse(task.blocked_by);
291
+ for (const dep of deps) {
292
+ if (!visited.has(dep)) queue.push(dep);
293
+ }
294
+ }
295
+ }
296
+ }
297
+ initialStatus(db, blockerIds) {
298
+ if (blockerIds.length === 0) return "active";
299
+ for (const id of blockerIds) {
300
+ const task = db.prepare("SELECT status FROM cord_tasks WHERE task_id = ?").get(id);
301
+ if (!task || task.status !== "completed") return "blocked";
302
+ }
303
+ return "active";
304
+ }
305
+ checkAndUnblockDependents(db, completedTaskId) {
306
+ const allBlocked = db.prepare("SELECT * FROM cord_tasks WHERE status = 'blocked'").all();
307
+ const unblocked = [];
308
+ for (const task of allBlocked) {
309
+ const blockers = JSON.parse(task.blocked_by);
310
+ if (!blockers.includes(completedTaskId)) continue;
311
+ const allDone = blockers.every((bid) => {
312
+ if (bid === completedTaskId) return true;
313
+ const blocker = db.prepare("SELECT status FROM cord_tasks WHERE task_id = ?").get(bid);
314
+ return blocker?.status === "completed";
315
+ });
316
+ if (allDone) {
317
+ db.prepare(
318
+ "UPDATE cord_tasks SET status = 'active' WHERE task_id = ?"
319
+ ).run(task.task_id);
320
+ unblocked.push(task.task_id);
321
+ }
322
+ }
323
+ return unblocked;
324
+ }
325
+ getSubtree(db, rootId) {
326
+ const result = [];
327
+ const queue = [rootId];
328
+ while (queue.length > 0) {
329
+ const current = queue.shift();
330
+ const task = db.prepare("SELECT * FROM cord_tasks WHERE task_id = ?").get(current);
331
+ if (task) {
332
+ result.push(task);
333
+ const children = db.prepare(
334
+ "SELECT task_id FROM cord_tasks WHERE parent_id = ? ORDER BY created_at ASC"
335
+ ).all(current);
336
+ for (const c of children) queue.push(c.task_id);
337
+ }
338
+ }
339
+ return result;
340
+ }
341
+ computeVisibleContext(task, allTaskMap, includeResults) {
342
+ const ctx = { prompt: task.prompt };
343
+ if (task.context_mode === "ask") {
344
+ try {
345
+ const parsed = JSON.parse(task.prompt);
346
+ ctx.question = parsed.question;
347
+ ctx.options = parsed.options || null;
348
+ } catch {
349
+ ctx.question = task.goal;
350
+ }
351
+ if (task.status === "completed" && task.result !== null) {
352
+ ctx.answer = task.result;
353
+ }
354
+ return ctx;
355
+ }
356
+ const blockerIds = JSON.parse(task.blocked_by);
357
+ const blockerResults = [];
358
+ for (const bid of blockerIds) {
359
+ const blocker = allTaskMap.get(bid);
360
+ if (blocker?.status === "completed" && blocker.result !== null) {
361
+ blockerResults.push({
362
+ task_id: bid,
363
+ goal: blocker.goal,
364
+ result: includeResults ? blocker.result : "[completed]"
365
+ });
366
+ }
367
+ }
368
+ if (blockerResults.length > 0) {
369
+ ctx.blocker_results = blockerResults;
370
+ }
371
+ if (task.context_mode === "fork" && task.parent_id) {
372
+ const siblingResults = [];
373
+ for (const [, t] of allTaskMap) {
374
+ if (t.parent_id === task.parent_id && t.task_id !== task.task_id && t.status === "completed" && t.result !== null) {
375
+ siblingResults.push({
376
+ task_id: t.task_id,
377
+ goal: t.goal,
378
+ result: includeResults ? t.result : "[completed]"
379
+ });
380
+ }
381
+ }
382
+ if (siblingResults.length > 0) {
383
+ ctx.sibling_results = siblingResults;
384
+ }
385
+ }
386
+ return ctx;
387
+ }
388
+ getProjectId() {
389
+ return this.deps.dbAdapter.projectId;
390
+ }
391
+ getRunId() {
392
+ return this.deps.frameManager.currentRunId;
393
+ }
394
+ }
395
+ export {
396
+ CordHandlers
397
+ };
@@ -18,6 +18,8 @@ import {
18
18
  import {
19
19
  ProviderHandlers
20
20
  } from "./provider-handlers.js";
21
+ import { TeamHandlers } from "./team-handlers.js";
22
+ import { CordHandlers } from "./cord-handlers.js";
21
23
  import {
22
24
  ContextHandlers as ContextHandlers2
23
25
  } from "./context-handlers.js";
@@ -27,12 +29,16 @@ import {
27
29
  } from "./linear-handlers.js";
28
30
  import { TraceHandlers as TraceHandlers2 } from "./trace-handlers.js";
29
31
  import { ProviderHandlers as ProviderHandlers2 } from "./provider-handlers.js";
32
+ import { TeamHandlers as TeamHandlers2 } from "./team-handlers.js";
33
+ import { CordHandlers as CordHandlers2 } from "./cord-handlers.js";
30
34
  class MCPHandlerFactory {
31
35
  contextHandlers;
32
36
  taskHandlers;
33
37
  linearHandlers;
34
38
  traceHandlers;
35
39
  providerHandlers;
40
+ teamHandlers;
41
+ cordHandlers;
36
42
  constructor(deps) {
37
43
  this.contextHandlers = new ContextHandlers2({
38
44
  frameManager: deps.frameManager
@@ -51,6 +57,16 @@ class MCPHandlerFactory {
51
57
  browserMCP: deps.browserMCP
52
58
  });
53
59
  this.providerHandlers = new ProviderHandlers2();
60
+ if (deps.dbAdapter) {
61
+ this.teamHandlers = new TeamHandlers2({
62
+ frameManager: deps.frameManager,
63
+ dbAdapter: deps.dbAdapter
64
+ });
65
+ this.cordHandlers = new CordHandlers2({
66
+ frameManager: deps.frameManager,
67
+ dbAdapter: deps.dbAdapter
68
+ });
69
+ }
54
70
  }
55
71
  /**
56
72
  * Get handler for a specific tool
@@ -83,10 +99,6 @@ class MCPHandlerFactory {
83
99
  return this.taskHandlers.handleGetActiveTasks.bind(this.taskHandlers);
84
100
  case "get_task_metrics":
85
101
  return this.taskHandlers.handleGetTaskMetrics.bind(this.taskHandlers);
86
- case "add_task_dependency":
87
- return this.taskHandlers.handleAddTaskDependency.bind(
88
- this.taskHandlers
89
- );
90
102
  // Linear handlers
91
103
  case "linear_sync":
92
104
  return this.linearHandlers.handleLinearSync.bind(this.linearHandlers);
@@ -103,8 +115,6 @@ class MCPHandlerFactory {
103
115
  // Trace handlers
104
116
  case "get_traces":
105
117
  return this.traceHandlers.handleGetTraces.bind(this.traceHandlers);
106
- case "analyze_traces":
107
- return this.traceHandlers.handleAnalyzeTraces.bind(this.traceHandlers);
108
118
  case "start_browser_debug":
109
119
  return this.traceHandlers.handleStartBrowserDebug.bind(
110
120
  this.traceHandlers
@@ -130,6 +140,32 @@ class MCPHandlerFactory {
130
140
  return this.providerHandlers.handleBatchCheck.bind(
131
141
  this.providerHandlers
132
142
  );
143
+ // Team handlers
144
+ case "team_context_get":
145
+ if (!this.teamHandlers) throw new Error("Team tools require dbAdapter");
146
+ return this.teamHandlers.handleTeamContextGet.bind(this.teamHandlers);
147
+ case "team_context_share":
148
+ if (!this.teamHandlers) throw new Error("Team tools require dbAdapter");
149
+ return this.teamHandlers.handleTeamContextShare.bind(this.teamHandlers);
150
+ case "team_search":
151
+ if (!this.teamHandlers) throw new Error("Team tools require dbAdapter");
152
+ return this.teamHandlers.handleTeamSearch.bind(this.teamHandlers);
153
+ // Cord orchestration handlers
154
+ case "cord_spawn":
155
+ if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
156
+ return this.cordHandlers.handleCordSpawn.bind(this.cordHandlers);
157
+ case "cord_fork":
158
+ if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
159
+ return this.cordHandlers.handleCordFork.bind(this.cordHandlers);
160
+ case "cord_complete":
161
+ if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
162
+ return this.cordHandlers.handleCordComplete.bind(this.cordHandlers);
163
+ case "cord_ask":
164
+ if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
165
+ return this.cordHandlers.handleCordAsk.bind(this.cordHandlers);
166
+ case "cord_tree":
167
+ if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
168
+ return this.cordHandlers.handleCordTree.bind(this.cordHandlers);
133
169
  default:
134
170
  throw new Error(`Unknown tool: ${toolName}`);
135
171
  }
@@ -151,7 +187,6 @@ class MCPHandlerFactory {
151
187
  "update_task_status",
152
188
  "get_active_tasks",
153
189
  "get_task_metrics",
154
- "add_task_dependency",
155
190
  // Linear tools
156
191
  "linear_sync",
157
192
  "linear_update_task",
@@ -159,7 +194,6 @@ class MCPHandlerFactory {
159
194
  "linear_status",
160
195
  // Trace tools
161
196
  "get_traces",
162
- "analyze_traces",
163
197
  "start_browser_debug",
164
198
  "take_screenshot",
165
199
  "execute_script",
@@ -167,7 +201,17 @@ class MCPHandlerFactory {
167
201
  // Provider tools (conditionally active)
168
202
  "delegate_to_model",
169
203
  "batch_submit",
170
- "batch_check"
204
+ "batch_check",
205
+ // Team tools
206
+ "team_context_get",
207
+ "team_context_share",
208
+ "team_search",
209
+ // Cord orchestration tools
210
+ "cord_spawn",
211
+ "cord_fork",
212
+ "cord_complete",
213
+ "cord_ask",
214
+ "cord_tree"
171
215
  ];
172
216
  }
173
217
  /**
@@ -179,10 +223,12 @@ class MCPHandlerFactory {
179
223
  }
180
224
  export {
181
225
  ContextHandlers,
226
+ CordHandlers,
182
227
  DiscoveryHandlers,
183
228
  LinearHandlers,
184
229
  MCPHandlerFactory,
185
230
  ProviderHandlers,
186
231
  TaskHandlers,
232
+ TeamHandlers,
187
233
  TraceHandlers
188
234
  };