@stackmemoryai/stackmemory 1.10.4 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +104 -23
  2. package/dist/src/cli/claude-sm.js +266 -84
  3. package/dist/src/cli/codex-sm.js +185 -33
  4. package/dist/src/cli/commands/bench.js +209 -2
  5. package/dist/src/cli/commands/cache.js +126 -0
  6. package/dist/src/cli/commands/daemon.js +41 -0
  7. package/dist/src/cli/commands/handoff.js +40 -9
  8. package/dist/src/cli/commands/onboard.js +70 -3
  9. package/dist/src/cli/commands/optimize.js +117 -0
  10. package/dist/src/cli/commands/orchestrate.js +230 -5
  11. package/dist/src/cli/commands/orchestrator.js +312 -24
  12. package/dist/src/cli/commands/pack.js +322 -0
  13. package/dist/src/cli/commands/search.js +40 -1
  14. package/dist/src/cli/commands/setup.js +177 -7
  15. package/dist/src/cli/commands/skills.js +10 -1
  16. package/dist/src/cli/commands/state.js +265 -0
  17. package/dist/src/cli/commands/wiki.js +33 -0
  18. package/dist/src/cli/gemini-sm.js +19 -29
  19. package/dist/src/cli/index.js +90 -29
  20. package/dist/src/cli/opencode-sm.js +38 -21
  21. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  22. package/dist/src/cli/utils/real-cli-bin.js +44 -0
  23. package/dist/src/core/cache/content-cache.js +238 -0
  24. package/dist/src/core/cache/index.js +11 -0
  25. package/dist/src/core/cache/token-estimator.js +16 -0
  26. package/dist/src/core/context/frame-database.js +38 -30
  27. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  28. package/dist/src/core/{merge → cross-search}/index.js +6 -4
  29. package/dist/src/core/database/sqlite-adapter.js +0 -83
  30. package/dist/src/core/extensions/provider-adapter.js +5 -0
  31. package/dist/src/core/models/model-router.js +22 -2
  32. package/dist/src/core/monitoring/logger.js +2 -1
  33. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  34. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  35. package/dist/src/core/provenance/index.js +40 -0
  36. package/dist/src/core/provenance/provenance-store.js +194 -0
  37. package/dist/src/core/provenance/types.js +82 -0
  38. package/dist/src/core/session/project-handoff.js +64 -0
  39. package/dist/src/core/session/session-manager.js +28 -0
  40. package/dist/src/core/shared-state/canonical-store.js +564 -0
  41. package/dist/src/core/skill-packs/index.js +18 -0
  42. package/dist/src/core/skill-packs/parser.js +42 -0
  43. package/dist/src/core/skill-packs/registry.js +224 -0
  44. package/dist/src/core/skill-packs/types.js +66 -0
  45. package/dist/src/core/trace/trace-event-store.js +282 -0
  46. package/dist/src/core/trace/trace-event.js +4 -0
  47. package/dist/src/core/wiki/wiki-compiler.js +219 -0
  48. package/dist/src/daemon/daemon-config.js +7 -0
  49. package/dist/src/daemon/services/github-service.js +126 -0
  50. package/dist/src/daemon/unified-daemon.js +30 -0
  51. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  52. package/dist/src/hooks/schemas.js +2 -0
  53. package/dist/src/integrations/claude-code/subagent-client.js +89 -0
  54. package/dist/src/integrations/github/pr-state.js +158 -0
  55. package/dist/src/integrations/linear/client.js +4 -1
  56. package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
  57. package/dist/src/integrations/mcp/handlers/index.js +40 -59
  58. package/dist/src/integrations/mcp/server.js +425 -311
  59. package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
  60. package/dist/src/integrations/mcp/tool-definitions.js +98 -229
  61. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
  62. package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
  63. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
  64. package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
  65. package/dist/src/orchestrators/multimodal/determinism.js +243 -0
  66. package/dist/src/orchestrators/multimodal/harness.js +147 -77
  67. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  68. package/dist/src/utils/hook-installer.js +8 -8
  69. package/package.json +10 -1
  70. package/packs/coding/python-fastapi/instructions.md +60 -0
  71. package/packs/coding/python-fastapi/pack.yaml +28 -0
  72. package/packs/coding/typescript-react/instructions.md +47 -0
  73. package/packs/coding/typescript-react/pack.yaml +28 -0
  74. package/packs/core/commands/capture.md +32 -0
  75. package/packs/core/commands/learn.md +73 -0
  76. package/packs/core/commands/next.md +36 -0
  77. package/packs/core/commands/restart.md +58 -0
  78. package/packs/core/commands/restore.md +29 -0
  79. package/packs/core/commands/start.md +57 -0
  80. package/packs/core/commands/stop.md +65 -0
  81. package/packs/core/commands/summary.md +40 -0
  82. package/packs/core/manifest.json +24 -0
  83. package/packs/ops/decision-recovery/instructions.md +65 -0
  84. package/packs/ops/decision-recovery/pack.yaml +89 -0
  85. package/templates/claude-hooks/doc-ingest.js +76 -0
  86. package/dist/src/cli/commands/team.js +0 -168
  87. package/dist/src/core/context/shared-context-layer.js +0 -620
  88. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  89. package/dist/src/core/merge/conflict-detector.js +0 -430
  90. package/dist/src/core/merge/resolution-engine.js +0 -557
  91. package/dist/src/core/merge/stack-diff.js +0 -531
  92. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  93. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  94. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  95. /package/dist/src/core/{merge → cache}/types.js +0 -0
@@ -1,302 +0,0 @@
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 { v4 as uuidv4 } from "uuid";
6
- import { ConflictDetector } from "./conflict-detector.js";
7
- import { StackDiffVisualizer } from "./stack-diff.js";
8
- import { ResolutionEngine } from "./resolution-engine.js";
9
- import { logger } from "../monitoring/logger.js";
10
- class UnifiedMergeResolver {
11
- conflictDetector;
12
- diffVisualizer;
13
- resolutionEngine;
14
- activeSessions = /* @__PURE__ */ new Map();
15
- rollbackSnapshots = /* @__PURE__ */ new Map();
16
- statistics = {
17
- totalConflicts: 0,
18
- resolvedConflicts: 0,
19
- averageResolutionTime: 0,
20
- successRate: 0,
21
- rollbackCount: 0
22
- };
23
- constructor() {
24
- this.conflictDetector = new ConflictDetector();
25
- this.diffVisualizer = new StackDiffVisualizer();
26
- this.resolutionEngine = new ResolutionEngine();
27
- logger.debug("UnifiedMergeResolver initialized");
28
- }
29
- /**
30
- * Start a new merge session with automatic conflict detection
31
- */
32
- async startMergeSession(stack1, stack2, options) {
33
- const sessionId = `unified-merge-${Date.now()}-${uuidv4().substring(0, 8)}`;
34
- const conflicts = this.conflictDetector.detectConflicts(stack1, stack2);
35
- let rollbackPoint;
36
- if (options?.preserveRollback !== false) {
37
- rollbackPoint = this.createRollbackSnapshot(sessionId, stack1, stack2);
38
- }
39
- const session = {
40
- sessionId,
41
- stack1,
42
- stack2,
43
- conflicts,
44
- status: "analyzing",
45
- rollbackPoint,
46
- startedAt: Date.now(),
47
- metadata: {
48
- totalFrames: stack1.frames.length + stack2.frames.length,
49
- conflictCount: conflicts.length,
50
- resolvedCount: 0
51
- }
52
- };
53
- this.activeSessions.set(sessionId, session);
54
- this.statistics.totalConflicts += conflicts.length;
55
- logger.info(`Merge session started: ${sessionId}`, {
56
- stack1Id: stack1.id,
57
- stack2Id: stack2.id,
58
- conflictCount: conflicts.length
59
- });
60
- if (options?.autoResolve && conflicts.length > 0 && options.context) {
61
- const defaultStrategy = options.strategy || "ai_suggest";
62
- await this.resolveConflicts(sessionId, defaultStrategy, options.context);
63
- }
64
- return sessionId;
65
- }
66
- /**
67
- * Generate a preview of the merge result
68
- */
69
- async generatePreview(sessionId, strategy) {
70
- const session = this.activeSessions.get(sessionId);
71
- if (!session) {
72
- throw new Error(`Session not found: ${sessionId}`);
73
- }
74
- const preview = this.diffVisualizer.generateMergePreview(
75
- session.stack1,
76
- session.stack2,
77
- strategy
78
- );
79
- session.preview = preview;
80
- session.status = "preview";
81
- session.metadata.strategyUsed = strategy;
82
- this.activeSessions.set(sessionId, session);
83
- logger.info(`Preview generated for session: ${sessionId}`, {
84
- mergedFrameCount: preview.mergedFrames.length,
85
- estimatedSuccess: preview.estimatedSuccess
86
- });
87
- return preview;
88
- }
89
- /**
90
- * Resolve conflicts using the specified strategy
91
- */
92
- async resolveConflicts(sessionId, strategy, context) {
93
- const session = this.activeSessions.get(sessionId);
94
- if (!session) {
95
- throw new Error(`Session not found: ${sessionId}`);
96
- }
97
- session.status = "resolving";
98
- try {
99
- const result = await this.resolutionEngine.resolveConflicts(
100
- session.stack1,
101
- session.stack2,
102
- strategy,
103
- context
104
- );
105
- session.resolution = result.resolution;
106
- session.status = result.success ? "completed" : "failed";
107
- session.completedAt = Date.now();
108
- session.metadata.resolvedCount = session.conflicts.filter(
109
- (c) => c.resolution !== void 0
110
- ).length;
111
- session.metadata.strategyUsed = strategy;
112
- this.activeSessions.set(sessionId, session);
113
- if (result.success) {
114
- this.statistics.resolvedConflicts += session.metadata.resolvedCount;
115
- this.updateSuccessRate();
116
- this.updateAverageResolutionTime(session);
117
- }
118
- logger.info(`Conflicts resolved for session: ${sessionId}`, {
119
- success: result.success,
120
- strategy,
121
- resolvedCount: session.metadata.resolvedCount
122
- });
123
- return result;
124
- } catch (error) {
125
- session.status = "failed";
126
- this.activeSessions.set(sessionId, session);
127
- logger.error(
128
- `Failed to resolve conflicts for session: ${sessionId}`,
129
- error
130
- );
131
- throw error;
132
- }
133
- }
134
- /**
135
- * Rollback a merge to its original state
136
- */
137
- async rollback(sessionId) {
138
- const session = this.activeSessions.get(sessionId);
139
- if (!session || !session.rollbackPoint) {
140
- logger.warn(`Cannot rollback session: ${sessionId} - no rollback point`);
141
- return false;
142
- }
143
- const snapshot = this.rollbackSnapshots.get(session.rollbackPoint);
144
- if (!snapshot) {
145
- logger.error(`Rollback snapshot not found: ${session.rollbackPoint}`);
146
- return false;
147
- }
148
- session.stack1 = snapshot.stack1;
149
- session.stack2 = snapshot.stack2;
150
- session.status = "rolled_back";
151
- session.resolution = void 0;
152
- session.conflicts = this.conflictDetector.detectConflicts(
153
- snapshot.stack1,
154
- snapshot.stack2
155
- );
156
- session.metadata.resolvedCount = 0;
157
- this.activeSessions.set(sessionId, session);
158
- this.statistics.rollbackCount++;
159
- logger.info(`Session rolled back: ${sessionId}`);
160
- return true;
161
- }
162
- /**
163
- * Get merge session details
164
- */
165
- getSession(sessionId) {
166
- return this.activeSessions.get(sessionId);
167
- }
168
- /**
169
- * List all active merge sessions
170
- */
171
- listActiveSessions() {
172
- return Array.from(this.activeSessions.values()).filter(
173
- (s) => s.status !== "completed" && s.status !== "rolled_back" && s.status !== "failed"
174
- );
175
- }
176
- /**
177
- * Get merge statistics
178
- */
179
- getStatistics() {
180
- return { ...this.statistics };
181
- }
182
- /**
183
- * Analyze parallel solutions across stacks
184
- */
185
- analyzeParallelSolutions(frames) {
186
- const solutions = this.conflictDetector.analyzeParallelSolutions(frames);
187
- const recommendations = [];
188
- if (solutions.length > 1) {
189
- const grouped = /* @__PURE__ */ new Map();
190
- for (const sol of solutions) {
191
- const key = sol.approach.toLowerCase();
192
- if (!grouped.has(key)) {
193
- grouped.set(key, []);
194
- }
195
- grouped.get(key).push(sol);
196
- }
197
- for (const [approach, group] of grouped) {
198
- if (group.length > 1) {
199
- const avgEffectiveness = group.reduce((sum, s) => sum + (s.effectiveness || 0), 0) / group.length;
200
- recommendations.push(
201
- `${group.length} parallel solutions using "${approach}" approach (avg effectiveness: ${(avgEffectiveness * 100).toFixed(1)}%)`
202
- );
203
- }
204
- }
205
- const best = solutions.reduce(
206
- (a, b) => (a.effectiveness || 0) > (b.effectiveness || 0) ? a : b,
207
- solutions[0]
208
- );
209
- if (best && best.effectiveness && best.effectiveness > 0.7) {
210
- recommendations.push(
211
- `Recommended: Use solution from frame "${best.frameId}" (${(best.effectiveness * 100).toFixed(1)}% effectiveness)`
212
- );
213
- }
214
- }
215
- return {
216
- solutions: solutions.map((s) => ({
217
- frameId: s.frameId,
218
- approach: s.approach,
219
- effectiveness: s.effectiveness || 0
220
- })),
221
- recommendations
222
- };
223
- }
224
- /**
225
- * Create a visual diff between two stacks
226
- */
227
- createVisualDiff(baseFrame, stack1, stack2) {
228
- const diff = this.diffVisualizer.visualizeDivergence(
229
- baseFrame,
230
- stack1,
231
- stack2
232
- );
233
- const conflicts = this.conflictDetector.detectConflicts(stack1, stack2);
234
- return {
235
- nodes: diff.nodes.map((n) => ({
236
- id: n.id,
237
- type: n.type,
238
- depth: n.frame?.depth
239
- })),
240
- edges: diff.edges.map((e) => ({
241
- source: e.source,
242
- target: e.target,
243
- type: e.type
244
- })),
245
- conflicts: conflicts.map((c) => ({
246
- frameId1: c.frameId1,
247
- frameId2: c.frameId2,
248
- severity: c.severity
249
- }))
250
- };
251
- }
252
- /**
253
- * Close a merge session and clean up resources
254
- */
255
- closeSession(sessionId) {
256
- const session = this.activeSessions.get(sessionId);
257
- if (session) {
258
- if (session.rollbackPoint) {
259
- this.rollbackSnapshots.delete(session.rollbackPoint);
260
- }
261
- this.activeSessions.delete(sessionId);
262
- logger.debug(`Session closed: ${sessionId}`);
263
- }
264
- }
265
- // Private helpers
266
- createRollbackSnapshot(sessionId, stack1, stack2) {
267
- const snapshotId = `rollback-${sessionId}`;
268
- this.rollbackSnapshots.set(snapshotId, {
269
- stack1: this.deepCloneStack(stack1),
270
- stack2: this.deepCloneStack(stack2)
271
- });
272
- return snapshotId;
273
- }
274
- deepCloneStack(stack) {
275
- return {
276
- ...stack,
277
- frames: stack.frames.map((f) => ({ ...f })),
278
- events: stack.events.map((e) => ({ ...e }))
279
- };
280
- }
281
- updateSuccessRate() {
282
- if (this.statistics.totalConflicts > 0) {
283
- this.statistics.successRate = this.statistics.resolvedConflicts / this.statistics.totalConflicts;
284
- }
285
- }
286
- updateAverageResolutionTime(session) {
287
- if (session.completedAt && session.startedAt) {
288
- const duration = session.completedAt - session.startedAt;
289
- const completedSessions = Array.from(this.activeSessions.values()).filter(
290
- (s) => s.completedAt
291
- ).length;
292
- if (completedSessions === 1) {
293
- this.statistics.averageResolutionTime = duration;
294
- } else {
295
- this.statistics.averageResolutionTime = (this.statistics.averageResolutionTime * (completedSessions - 1) + duration) / completedSessions;
296
- }
297
- }
298
- }
299
- }
300
- export {
301
- UnifiedMergeResolver
302
- };
@@ -1,397 +0,0 @@
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
- };