@stackmemoryai/stackmemory 0.5.64 → 0.5.67

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 (66) hide show
  1. package/README.md +69 -346
  2. package/bin/claude-sm +1 -1
  3. package/bin/claude-smd +1 -1
  4. package/bin/codex-sm +6 -0
  5. package/bin/codex-smd +1 -1
  6. package/bin/opencode-sm +1 -1
  7. package/dist/src/cli/claude-sm.js +162 -25
  8. package/dist/src/cli/claude-sm.js.map +2 -2
  9. package/dist/src/cli/commands/ping.js +14 -0
  10. package/dist/src/cli/commands/ping.js.map +7 -0
  11. package/dist/src/cli/commands/ralph.js +103 -1
  12. package/dist/src/cli/commands/ralph.js.map +2 -2
  13. package/dist/src/cli/commands/retrieval.js +1 -1
  14. package/dist/src/cli/commands/retrieval.js.map +2 -2
  15. package/dist/src/cli/commands/skills.js +300 -1
  16. package/dist/src/cli/commands/skills.js.map +2 -2
  17. package/dist/src/cli/index.js +362 -20
  18. package/dist/src/cli/index.js.map +2 -2
  19. package/dist/src/core/digest/types.js +1 -1
  20. package/dist/src/core/digest/types.js.map +1 -1
  21. package/dist/src/core/extensions/provider-adapter.js +2 -5
  22. package/dist/src/core/extensions/provider-adapter.js.map +2 -2
  23. package/dist/src/core/retrieval/llm-provider.js +2 -2
  24. package/dist/src/core/retrieval/llm-provider.js.map +1 -1
  25. package/dist/src/core/retrieval/types.js +1 -1
  26. package/dist/src/core/retrieval/types.js.map +1 -1
  27. package/dist/src/features/sweep/pty-wrapper.js +15 -5
  28. package/dist/src/features/sweep/pty-wrapper.js.map +2 -2
  29. package/dist/src/features/workers/tmux-manager.js +71 -0
  30. package/dist/src/features/workers/tmux-manager.js.map +7 -0
  31. package/dist/src/features/workers/worker-registry.js +52 -0
  32. package/dist/src/features/workers/worker-registry.js.map +7 -0
  33. package/dist/src/integrations/linear/webhook-handler.js +82 -0
  34. package/dist/src/integrations/linear/webhook-handler.js.map +2 -2
  35. package/dist/src/integrations/mcp/pending-utils.js +33 -0
  36. package/dist/src/integrations/mcp/pending-utils.js.map +7 -0
  37. package/dist/src/integrations/mcp/server.js +571 -1
  38. package/dist/src/integrations/mcp/server.js.map +2 -2
  39. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +2 -2
  40. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +2 -2
  41. package/dist/src/orchestrators/multimodal/constants.js +17 -0
  42. package/dist/src/orchestrators/multimodal/constants.js.map +7 -0
  43. package/dist/src/orchestrators/multimodal/harness.js +292 -0
  44. package/dist/src/orchestrators/multimodal/harness.js.map +7 -0
  45. package/dist/src/orchestrators/multimodal/providers.js +98 -0
  46. package/dist/src/orchestrators/multimodal/providers.js.map +7 -0
  47. package/dist/src/orchestrators/multimodal/types.js +5 -0
  48. package/dist/src/orchestrators/multimodal/types.js.map +7 -0
  49. package/dist/src/orchestrators/multimodal/utils.js +25 -0
  50. package/dist/src/orchestrators/multimodal/utils.js.map +7 -0
  51. package/dist/src/skills/claude-skills.js +116 -1
  52. package/dist/src/skills/claude-skills.js.map +2 -2
  53. package/dist/src/skills/linear-task-runner.js +262 -0
  54. package/dist/src/skills/linear-task-runner.js.map +7 -0
  55. package/dist/src/skills/recursive-agent-orchestrator.js +114 -85
  56. package/dist/src/skills/recursive-agent-orchestrator.js.map +2 -2
  57. package/dist/src/skills/spec-generator-skill.js +441 -0
  58. package/dist/src/skills/spec-generator-skill.js.map +7 -0
  59. package/package.json +14 -9
  60. package/scripts/claude-code-wrapper.sh +18 -30
  61. package/scripts/demos/ralph-integration-demo.ts +14 -13
  62. package/scripts/demos/trace-demo.ts +7 -21
  63. package/scripts/demos/trace-test.ts +20 -8
  64. package/scripts/install-claude-hooks.sh +2 -2
  65. package/scripts/verify-dist.cjs +83 -0
  66. package/templates/claude-hooks/post-edit-sweep.js +7 -10
@@ -0,0 +1,262 @@
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 { logger } from "../core/monitoring/logger.js";
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ const PROMPT_PLAN_PATH = "docs/specs/PROMPT_PLAN.md";
9
+ class LinearTaskRunner {
10
+ constructor(taskManager, rlmOrchestrator, context, specSkill) {
11
+ this.taskManager = taskManager;
12
+ this.rlmOrchestrator = rlmOrchestrator;
13
+ this.context = context;
14
+ this.specSkill = specSkill;
15
+ }
16
+ /** Pull next task from Linear, execute via RLM, update status */
17
+ async runNext(opts) {
18
+ const tasks = this.getFilteredTasks(opts);
19
+ if (tasks.length === 0) {
20
+ return {
21
+ success: true,
22
+ message: "No pending tasks found",
23
+ data: { tasksAvailable: 0 }
24
+ };
25
+ }
26
+ const task = tasks[0];
27
+ return this.runTask(task.id, opts);
28
+ }
29
+ /** Run all active tasks iteratively */
30
+ async runAll(opts) {
31
+ const startTime = Date.now();
32
+ const tasks = this.getFilteredTasks(opts);
33
+ if (tasks.length === 0) {
34
+ return {
35
+ success: true,
36
+ message: "No pending tasks to execute",
37
+ data: { tasksAvailable: 0 }
38
+ };
39
+ }
40
+ if (opts?.dryRun) {
41
+ return this.preview();
42
+ }
43
+ const summary = {
44
+ completed: [],
45
+ failed: [],
46
+ skipped: [],
47
+ totalTokens: 0,
48
+ totalCost: 0,
49
+ duration: 0
50
+ };
51
+ for (const task of tasks) {
52
+ try {
53
+ const result = await this.executeTask(task);
54
+ if (result.success) {
55
+ summary.completed.push(task.id);
56
+ const data = result.data;
57
+ summary.totalTokens += data?.totalTokens || 0;
58
+ summary.totalCost += data?.totalCost || 0;
59
+ await this.autoUpdatePromptPlan(task);
60
+ } else {
61
+ summary.failed.push({
62
+ taskId: task.id,
63
+ error: result.message
64
+ });
65
+ }
66
+ await this.syncSafe();
67
+ } catch (error) {
68
+ const msg = error instanceof Error ? error.message : String(error);
69
+ summary.failed.push({ taskId: task.id, error: msg });
70
+ logger.error("Task execution failed", { taskId: task.id, error: msg });
71
+ }
72
+ }
73
+ summary.duration = Date.now() - startTime;
74
+ return {
75
+ success: summary.failed.length === 0,
76
+ message: `Completed ${summary.completed.length}/${tasks.length} tasks`,
77
+ data: summary,
78
+ action: `Executed ${summary.completed.length} tasks, ${summary.failed.length} failures`
79
+ };
80
+ }
81
+ /** Execute a specific Linear task by ID */
82
+ async runTask(taskId, opts) {
83
+ const task = this.taskManager.getTask(taskId);
84
+ if (!task) {
85
+ return { success: false, message: `Task not found: ${taskId}` };
86
+ }
87
+ if (opts?.dryRun) {
88
+ return this.previewTask(task);
89
+ }
90
+ const result = await this.executeTask(task);
91
+ if (result.success) {
92
+ await this.autoUpdatePromptPlan(task);
93
+ await this.syncSafe();
94
+ }
95
+ return result;
96
+ }
97
+ /** Show execution plan without running */
98
+ async preview(taskId) {
99
+ if (taskId) {
100
+ const task = this.taskManager.getTask(taskId);
101
+ if (!task) {
102
+ return { success: false, message: `Task not found: ${taskId}` };
103
+ }
104
+ return this.previewTask(task);
105
+ }
106
+ const tasks = this.getFilteredTasks();
107
+ const plan = tasks.map((t, i) => ({
108
+ order: i + 1,
109
+ id: t.id,
110
+ identifier: t.externalIdentifier || t.id,
111
+ title: t.title,
112
+ priority: t.priority || "medium",
113
+ status: t.status,
114
+ tags: t.tags
115
+ }));
116
+ return {
117
+ success: true,
118
+ message: `${plan.length} tasks in execution queue`,
119
+ data: { plan, totalTasks: plan.length }
120
+ };
121
+ }
122
+ // --- Private helpers ---
123
+ async executeTask(task) {
124
+ const taskLabel = task.externalIdentifier || task.id;
125
+ logger.info("Starting task execution", {
126
+ taskId: task.id,
127
+ title: task.title
128
+ });
129
+ try {
130
+ this.taskManager.updateTaskStatus(
131
+ task.id,
132
+ "in_progress",
133
+ "LinearTaskRunner: starting execution"
134
+ );
135
+ } catch {
136
+ }
137
+ try {
138
+ const result = await this.rlmOrchestrator.execute(
139
+ task.description || task.title,
140
+ {
141
+ linearTaskId: task.id,
142
+ linearIdentifier: task.externalIdentifier,
143
+ title: task.title,
144
+ tags: task.tags
145
+ }
146
+ );
147
+ if (result.success) {
148
+ this.taskManager.updateTaskStatus(
149
+ task.id,
150
+ "done",
151
+ `Completed via RLM: ${result.improvements.length} improvements, ${result.testsGenerated} tests`
152
+ );
153
+ return {
154
+ success: true,
155
+ message: `${taskLabel}: completed`,
156
+ data: {
157
+ taskId: task.id,
158
+ duration: result.duration,
159
+ totalTokens: result.totalTokens,
160
+ totalCost: result.totalCost,
161
+ testsGenerated: result.testsGenerated,
162
+ improvements: result.improvements.length,
163
+ issuesFound: result.issuesFound,
164
+ issuesFixed: result.issuesFixed
165
+ },
166
+ action: `Executed ${taskLabel} via RLM`
167
+ };
168
+ } else {
169
+ logger.warn("Task execution failed", {
170
+ taskId: task.id,
171
+ rootNode: result.rootNode
172
+ });
173
+ return {
174
+ success: false,
175
+ message: `${taskLabel}: execution failed`,
176
+ data: {
177
+ taskId: task.id,
178
+ duration: result.duration,
179
+ totalTokens: result.totalTokens
180
+ }
181
+ };
182
+ }
183
+ } catch (error) {
184
+ const msg = error instanceof Error ? error.message : String(error);
185
+ logger.error("Task execution threw", { taskId: task.id, error: msg });
186
+ return {
187
+ success: false,
188
+ message: `${taskLabel}: ${msg}`,
189
+ data: { taskId: task.id }
190
+ };
191
+ }
192
+ }
193
+ getFilteredTasks(opts) {
194
+ const tasks = this.taskManager.getTasksByStatus("todo");
195
+ let filtered = tasks;
196
+ if (opts?.priority) {
197
+ filtered = filtered.filter((t) => t.priority === opts.priority);
198
+ }
199
+ if (opts?.tag) {
200
+ const tag = opts.tag;
201
+ filtered = filtered.filter((t) => t.tags.includes(tag));
202
+ }
203
+ const priorityOrder = {
204
+ urgent: 0,
205
+ high: 1,
206
+ medium: 2,
207
+ low: 3
208
+ };
209
+ return filtered.sort(
210
+ (a, b) => (priorityOrder[a.priority || "medium"] || 2) - (priorityOrder[b.priority || "medium"] || 2)
211
+ );
212
+ }
213
+ previewTask(task) {
214
+ return {
215
+ success: true,
216
+ message: `Preview: ${task.externalIdentifier || task.id}`,
217
+ data: {
218
+ id: task.id,
219
+ identifier: task.externalIdentifier,
220
+ title: task.title,
221
+ description: task.description?.slice(0, 200),
222
+ priority: task.priority,
223
+ status: task.status,
224
+ tags: task.tags,
225
+ willExecuteVia: "RLM Orchestrator",
226
+ estimatedSteps: [
227
+ "Planning agent decomposes task",
228
+ "Code/Test/Review subagents execute",
229
+ "Multi-stage review",
230
+ "Update Linear status to done"
231
+ ]
232
+ }
233
+ };
234
+ }
235
+ /** Auto-update PROMPT_PLAN checkboxes when a task completes */
236
+ async autoUpdatePromptPlan(task) {
237
+ if (!this.specSkill) return;
238
+ const promptPlanPath = path.join(process.cwd(), PROMPT_PLAN_PATH);
239
+ if (!fs.existsSync(promptPlanPath)) return;
240
+ try {
241
+ await this.specSkill.update(PROMPT_PLAN_PATH, task.title);
242
+ logger.info("Auto-updated PROMPT_PLAN checkbox", {
243
+ taskId: task.id,
244
+ title: task.title
245
+ });
246
+ } catch {
247
+ }
248
+ }
249
+ /** Safe Linear sync — log errors but don't throw */
250
+ async syncSafe() {
251
+ try {
252
+ await this.taskManager.syncWithLinear();
253
+ } catch (error) {
254
+ const msg = error instanceof Error ? error.message : String(error);
255
+ logger.warn("Linear sync failed (non-fatal)", { error: msg });
256
+ }
257
+ }
258
+ }
259
+ export {
260
+ LinearTaskRunner
261
+ };
262
+ //# sourceMappingURL=linear-task-runner.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/skills/linear-task-runner.ts"],
4
+ "sourcesContent": ["/**\n * Linear Task Runner\n * Bridges LinearTaskManager \u2192 RLM Orchestrator \u2192 Linear status updates.\n * Stateless: pull \u2192 execute \u2192 update. Ralph loop handles retries/learning.\n */\n\nimport type { SkillContext, SkillResult } from './claude-skills.js';\nimport type { RecursiveAgentOrchestrator } from './recursive-agent-orchestrator.js';\nimport type { SpecGeneratorSkill } from './spec-generator-skill.js';\nimport type { Task } from '../types/task.js';\nimport { logger } from '../core/monitoring/logger.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n// Dynamic import type for LinearTaskManager (loaded lazily)\ntype LinearTaskManagerType = {\n getActiveTasks(): Task[];\n getTask(id: string): Task | undefined;\n updateTaskStatus(id: string, status: string, reason?: string): void;\n syncWithLinear(): Promise<{ synced: number; errors: string[] }>;\n getTasksByStatus(status: string): Task[];\n};\n\ninterface RunOptions {\n priority?: string;\n tag?: string;\n dryRun?: boolean;\n maxConcurrent?: number;\n}\n\ninterface RunSummary {\n completed: string[];\n failed: Array<{ taskId: string; error: string }>;\n skipped: string[];\n totalTokens: number;\n totalCost: number;\n duration: number;\n}\n\nconst PROMPT_PLAN_PATH = 'docs/specs/PROMPT_PLAN.md';\n\nexport class LinearTaskRunner {\n constructor(\n private taskManager: LinearTaskManagerType,\n private rlmOrchestrator: RecursiveAgentOrchestrator,\n private context: SkillContext,\n private specSkill?: SpecGeneratorSkill\n ) {}\n\n /** Pull next task from Linear, execute via RLM, update status */\n async runNext(opts?: RunOptions): Promise<SkillResult> {\n const tasks = this.getFilteredTasks(opts);\n\n if (tasks.length === 0) {\n return {\n success: true,\n message: 'No pending tasks found',\n data: { tasksAvailable: 0 },\n };\n }\n\n const task = tasks[0];\n return this.runTask(task.id, opts);\n }\n\n /** Run all active tasks iteratively */\n async runAll(opts?: RunOptions): Promise<SkillResult> {\n const startTime = Date.now();\n const tasks = this.getFilteredTasks(opts);\n\n if (tasks.length === 0) {\n return {\n success: true,\n message: 'No pending tasks to execute',\n data: { tasksAvailable: 0 },\n };\n }\n\n if (opts?.dryRun) {\n return this.preview();\n }\n\n const summary: RunSummary = {\n completed: [],\n failed: [],\n skipped: [],\n totalTokens: 0,\n totalCost: 0,\n duration: 0,\n };\n\n // Sequential by default\n for (const task of tasks) {\n try {\n const result = await this.executeTask(task);\n\n if (result.success) {\n summary.completed.push(task.id);\n const data = result.data as Record<string, unknown> | undefined;\n summary.totalTokens += (data?.totalTokens as number) || 0;\n summary.totalCost += (data?.totalCost as number) || 0;\n\n // Auto-update PROMPT_PLAN checkboxes if spec skill available\n await this.autoUpdatePromptPlan(task);\n } else {\n summary.failed.push({\n taskId: task.id,\n error: result.message,\n });\n }\n\n // Sync to Linear after each task\n await this.syncSafe();\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n summary.failed.push({ taskId: task.id, error: msg });\n logger.error('Task execution failed', { taskId: task.id, error: msg });\n }\n }\n\n summary.duration = Date.now() - startTime;\n\n return {\n success: summary.failed.length === 0,\n message: `Completed ${summary.completed.length}/${tasks.length} tasks`,\n data: summary,\n action: `Executed ${summary.completed.length} tasks, ${summary.failed.length} failures`,\n };\n }\n\n /** Execute a specific Linear task by ID */\n async runTask(taskId: string, opts?: RunOptions): Promise<SkillResult> {\n const task = this.taskManager.getTask(taskId);\n\n if (!task) {\n return { success: false, message: `Task not found: ${taskId}` };\n }\n\n if (opts?.dryRun) {\n return this.previewTask(task);\n }\n\n const result = await this.executeTask(task);\n\n // Auto-update PROMPT_PLAN if successful\n if (result.success) {\n await this.autoUpdatePromptPlan(task);\n await this.syncSafe();\n }\n\n return result;\n }\n\n /** Show execution plan without running */\n async preview(taskId?: string): Promise<SkillResult> {\n if (taskId) {\n const task = this.taskManager.getTask(taskId);\n if (!task) {\n return { success: false, message: `Task not found: ${taskId}` };\n }\n return this.previewTask(task);\n }\n\n const tasks = this.getFilteredTasks();\n const plan = tasks.map((t, i) => ({\n order: i + 1,\n id: t.id,\n identifier: t.externalIdentifier || t.id,\n title: t.title,\n priority: t.priority || 'medium',\n status: t.status,\n tags: t.tags,\n }));\n\n return {\n success: true,\n message: `${plan.length} tasks in execution queue`,\n data: { plan, totalTasks: plan.length },\n };\n }\n\n // --- Private helpers ---\n\n private async executeTask(task: Task): Promise<SkillResult> {\n const taskLabel = task.externalIdentifier || task.id;\n\n logger.info('Starting task execution', {\n taskId: task.id,\n title: task.title,\n });\n\n // 1. Mark as in_progress\n try {\n this.taskManager.updateTaskStatus(\n task.id,\n 'in_progress',\n 'LinearTaskRunner: starting execution'\n );\n } catch {\n // Non-fatal \u2014 task may already be in_progress\n }\n\n // 2. Execute via RLM orchestrator\n try {\n const result = await this.rlmOrchestrator.execute(\n task.description || task.title,\n {\n linearTaskId: task.id,\n linearIdentifier: task.externalIdentifier,\n title: task.title,\n tags: task.tags,\n }\n );\n\n if (result.success) {\n // 3. Mark as done\n this.taskManager.updateTaskStatus(\n task.id,\n 'done',\n `Completed via RLM: ${result.improvements.length} improvements, ${result.testsGenerated} tests`\n );\n\n return {\n success: true,\n message: `${taskLabel}: completed`,\n data: {\n taskId: task.id,\n duration: result.duration,\n totalTokens: result.totalTokens,\n totalCost: result.totalCost,\n testsGenerated: result.testsGenerated,\n improvements: result.improvements.length,\n issuesFound: result.issuesFound,\n issuesFixed: result.issuesFixed,\n },\n action: `Executed ${taskLabel} via RLM`,\n };\n } else {\n // Leave as in_progress on failure \u2014 don't regress to todo\n logger.warn('Task execution failed', {\n taskId: task.id,\n rootNode: result.rootNode,\n });\n\n return {\n success: false,\n message: `${taskLabel}: execution failed`,\n data: {\n taskId: task.id,\n duration: result.duration,\n totalTokens: result.totalTokens,\n },\n };\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.error('Task execution threw', { taskId: task.id, error: msg });\n\n return {\n success: false,\n message: `${taskLabel}: ${msg}`,\n data: { taskId: task.id },\n };\n }\n }\n\n private getFilteredTasks(opts?: RunOptions): Task[] {\n const tasks = this.taskManager.getTasksByStatus('todo');\n\n let filtered = tasks;\n\n // Filter by priority\n if (opts?.priority) {\n filtered = filtered.filter((t) => t.priority === opts.priority);\n }\n\n // Filter by tag\n if (opts?.tag) {\n const tag = opts.tag;\n filtered = filtered.filter((t) => t.tags.includes(tag));\n }\n\n // Sort: urgent > high > medium > low\n const priorityOrder: Record<string, number> = {\n urgent: 0,\n high: 1,\n medium: 2,\n low: 3,\n };\n\n return filtered.sort(\n (a, b) =>\n (priorityOrder[a.priority || 'medium'] || 2) -\n (priorityOrder[b.priority || 'medium'] || 2)\n );\n }\n\n private previewTask(task: Task): SkillResult {\n return {\n success: true,\n message: `Preview: ${task.externalIdentifier || task.id}`,\n data: {\n id: task.id,\n identifier: task.externalIdentifier,\n title: task.title,\n description: task.description?.slice(0, 200),\n priority: task.priority,\n status: task.status,\n tags: task.tags,\n willExecuteVia: 'RLM Orchestrator',\n estimatedSteps: [\n 'Planning agent decomposes task',\n 'Code/Test/Review subagents execute',\n 'Multi-stage review',\n 'Update Linear status to done',\n ],\n },\n };\n }\n\n /** Auto-update PROMPT_PLAN checkboxes when a task completes */\n private async autoUpdatePromptPlan(task: Task): Promise<void> {\n if (!this.specSkill) return;\n\n const promptPlanPath = path.join(process.cwd(), PROMPT_PLAN_PATH);\n if (!fs.existsSync(promptPlanPath)) return;\n\n try {\n // Try to match task title to a checkbox in PROMPT_PLAN\n await this.specSkill.update(PROMPT_PLAN_PATH, task.title);\n logger.info('Auto-updated PROMPT_PLAN checkbox', {\n taskId: task.id,\n title: task.title,\n });\n } catch {\n // Non-fatal \u2014 task title may not match any checkbox\n }\n }\n\n /** Safe Linear sync \u2014 log errors but don't throw */\n private async syncSafe(): Promise<void> {\n try {\n await this.taskManager.syncWithLinear();\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.warn('Linear sync failed (non-fatal)', { error: msg });\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -2,6 +2,8 @@ import { fileURLToPath as __fileURLToPath } from 'url';
2
2
  import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
+ import * as fs from "fs";
6
+ import * as path from "path";
5
7
  import { logger } from "../core/monitoring/logger.js";
6
8
  import { ParallelExecutor } from "../core/execution/parallel-executor.js";
7
9
  import { RecursiveContextManager } from "../core/context/recursive-context-manager.js";
@@ -61,42 +63,46 @@ class RecursiveAgentOrchestrator {
61
63
  const configs = /* @__PURE__ */ new Map();
62
64
  configs.set("planning", {
63
65
  type: "planning",
64
- model: "claude-3-5-sonnet-latest",
66
+ model: "claude-sonnet-4-5-20250929",
65
67
  maxTokens: 2e4,
66
68
  temperature: 0.3,
67
- systemPrompt: `You are a Planning Agent specializing in task decomposition.
68
- Analyze complex tasks and break them into parallel and sequential subtasks.
69
- Create detailed execution plans with clear dependencies.
70
- Consider edge cases and potential failures.
71
- Output structured task trees with agent assignments.`,
69
+ systemPrompt: `You decompose tasks into parallel/sequential subtask trees.
70
+ Output JSON: { subtasks: [{ id, description, agent, dependencies[], parallel: bool }] }
71
+ Rules:
72
+ - Maximize parallelism \u2014 independent tasks run concurrently
73
+ - Each subtask names its agent type: planning, code, testing, linting, review, improve, context, publish
74
+ - Include failure modes and rollback steps for risky operations
75
+ - Keep subtask descriptions actionable (verb + object + constraint)`,
72
76
  capabilities: ["decompose", "analyze", "strategize", "prioritize"]
73
77
  });
74
78
  configs.set("code", {
75
79
  type: "code",
76
- model: "claude-3-5-sonnet-latest",
80
+ model: "claude-sonnet-4-5-20250929",
77
81
  maxTokens: 3e4,
78
82
  temperature: 0.2,
79
- systemPrompt: `You are a Code Agent specializing in implementation.
80
- Write clean, maintainable, production-ready code.
81
- Follow project conventions and best practices.
82
- Include comprehensive error handling.
83
- Document complex logic with clear comments.`,
83
+ systemPrompt: `You implement code changes. Read existing code before modifying.
84
+ Output JSON: { success: bool, filesChanged: string[], changes: string[], notes: string[] }
85
+ Rules:
86
+ - Follow existing project conventions (naming, imports, patterns)
87
+ - Add .js extensions to relative TypeScript imports (ESM)
88
+ - Return undefined over throwing; log+continue over crash
89
+ - No emojis, no unnecessary comments, functions under 20 lines
90
+ - Validate inputs at system boundaries only`,
84
91
  capabilities: ["implement", "refactor", "optimize", "document"]
85
92
  });
86
93
  configs.set("testing", {
87
94
  type: "testing",
88
- model: "claude-3-5-sonnet-latest",
89
- // High quality for test generation
95
+ model: "claude-sonnet-4-5-20250929",
90
96
  maxTokens: 25e3,
91
97
  temperature: 0.1,
92
- systemPrompt: `You are a Testing Agent specializing in test generation and validation.
93
- Generate comprehensive test suites including:
94
- - Unit tests for all functions/methods
95
- - Integration tests for API endpoints
96
- - E2E tests for critical user flows
97
- - Edge cases and error scenarios
98
- Ensure 100% code coverage where possible.
99
- Validate that all tests pass and are meaningful.`,
98
+ systemPrompt: `You generate and run tests using the project's test framework.
99
+ Output JSON: { success: bool, tests: [{ name, type, file }], coverage: string, notes: string[] }
100
+ Rules:
101
+ - Use vitest (describe/it/expect) \u2014 check existing tests for patterns
102
+ - Prioritize: critical paths > edge cases > happy paths
103
+ - Each test should assert meaningful behavior, not implementation details
104
+ - Use parameterized tests (it.each) to consolidate similar cases
105
+ - Run tests after writing: npm run test:run`,
100
106
  capabilities: [
101
107
  "generate-tests",
102
108
  "validate",
@@ -106,33 +112,31 @@ class RecursiveAgentOrchestrator {
106
112
  });
107
113
  configs.set("linting", {
108
114
  type: "linting",
109
- model: "claude-3-5-haiku-latest",
115
+ model: "claude-haiku-4-5-20251001",
110
116
  maxTokens: 15e3,
111
117
  temperature: 0,
112
- systemPrompt: `You are a Linting Agent specializing in code quality.
113
- Check for:
114
- - Syntax errors and type issues
115
- - Code formatting and style violations
116
- - Security vulnerabilities
117
- - Performance anti-patterns
118
- - Unused imports and dead code
119
- Provide actionable fixes for all issues found.`,
118
+ systemPrompt: `You run lint checks and fix issues.
119
+ Output JSON: { success: bool, issuesFound: number, issuesFixed: number, remaining: string[] }
120
+ Rules:
121
+ - Run: npm run lint (ESLint + Prettier)
122
+ - Auto-fix: npm run lint:fix
123
+ - ESM imports require .js extension on relative paths
124
+ - Report unfixable issues with file:line format`,
120
125
  capabilities: ["lint", "format", "type-check", "security-scan"]
121
126
  });
122
127
  configs.set("review", {
123
128
  type: "review",
124
- model: "claude-3-5-sonnet-latest",
129
+ model: "claude-sonnet-4-5-20250929",
125
130
  maxTokens: 25e3,
126
131
  temperature: 0.2,
127
- systemPrompt: `You are a Review Agent specializing in multi-stage code review.
128
- Perform thorough reviews focusing on:
129
- - Architecture and design patterns
130
- - Code quality and maintainability
131
- - Performance implications
132
- - Security considerations
133
- - Test coverage adequacy
134
- Suggest specific improvements with examples.
135
- Rate quality on a 0-1 scale.`,
132
+ systemPrompt: `You review code changes for quality, security, and correctness.
133
+ Output JSON: { qualityScore: 0-1, issues: [{ severity, file, line, description, suggestion }], approved: bool }
134
+ Rules:
135
+ - Score 0.85+ = approved, below = needs improvement
136
+ - Flag: SQL injection, XSS, secret exposure, command injection
137
+ - Flag: functions > 20 lines, cyclomatic complexity > 5
138
+ - Flag: missing error handling at system boundaries
139
+ - Suggest specific fixes, not vague improvements`,
136
140
  capabilities: [
137
141
  "review",
138
142
  "critique",
@@ -142,46 +146,44 @@ class RecursiveAgentOrchestrator {
142
146
  });
143
147
  configs.set("improve", {
144
148
  type: "improve",
145
- model: "claude-3-5-sonnet-latest",
149
+ model: "claude-sonnet-4-5-20250929",
146
150
  maxTokens: 3e4,
147
151
  temperature: 0.3,
148
- systemPrompt: `You are an Improvement Agent specializing in code enhancement.
149
- Take reviewed code and implement suggested improvements:
150
- - Refactor for better architecture
151
- - Optimize performance bottlenecks
152
- - Enhance error handling
153
- - Improve code clarity and documentation
154
- - Add missing test cases
155
- Ensure all improvements maintain backward compatibility.`,
152
+ systemPrompt: `You implement review feedback and improve code quality.
153
+ Output JSON: { success: bool, improvements: string[], filesChanged: string[] }
154
+ Rules:
155
+ - Apply only the specific improvements requested \u2014 no scope creep
156
+ - Maintain backward compatibility unless explicitly breaking
157
+ - Run lint + tests after changes to verify nothing regressed
158
+ - Keep changes minimal and focused`,
156
159
  capabilities: ["enhance", "refactor", "optimize", "polish"]
157
160
  });
158
161
  configs.set("context", {
159
162
  type: "context",
160
- model: "claude-3-5-haiku-latest",
163
+ model: "claude-haiku-4-5-20251001",
161
164
  maxTokens: 1e4,
162
165
  temperature: 0,
163
- systemPrompt: `You are a Context Agent specializing in information retrieval.
164
- Search and retrieve relevant context from:
165
- - Project codebase and documentation
166
- - Previous frame history
167
- - Similar implementations
168
- - Best practices and patterns
169
- Provide concise, relevant context for other agents.`,
166
+ systemPrompt: `You retrieve relevant context from the codebase and specs.
167
+ Output JSON: { context: string, sources: string[], relevanceScore: 0-1 }
168
+ Rules:
169
+ - Check docs/specs/ for ONE_PAGER.md, DEV_SPEC.md, PROMPT_PLAN.md
170
+ - Check CLAUDE.md and AGENTS.md for project conventions
171
+ - Search src/ for relevant implementations
172
+ - Return concise summaries, not full file contents`,
170
173
  capabilities: ["search", "retrieve", "summarize", "contextualize"]
171
174
  });
172
175
  configs.set("publish", {
173
176
  type: "publish",
174
- model: "claude-3-5-haiku-latest",
177
+ model: "claude-haiku-4-5-20251001",
175
178
  maxTokens: 15e3,
176
179
  temperature: 0,
177
- systemPrompt: `You are a Publish Agent specializing in release management.
178
- Handle:
179
- - NPM package publishing
180
- - GitHub releases and tagging
181
- - Documentation updates
182
- - Changelog generation
183
- - Deployment automation
184
- Ensure all release steps are properly sequenced.`,
180
+ systemPrompt: `You handle releases and publishing.
181
+ Output JSON: { success: bool, version: string, actions: string[] }
182
+ Rules:
183
+ - Verify lint + tests + build pass before any publish
184
+ - Follow semver: breaking=major, feature=minor, fix=patch
185
+ - Generate changelog from git log since last tag
186
+ - Never force-push or skip pre-publish hooks`,
185
187
  capabilities: ["publish-npm", "github-release", "deploy", "document"]
186
188
  });
187
189
  return configs;
@@ -479,23 +481,50 @@ class RecursiveAgentOrchestrator {
479
481
  node.children.push(testNode);
480
482
  }
481
483
  injectReviewStages(_node, _stages) {
484
+ }
485
+ loadSpecContext() {
486
+ const specDir = path.join(process.cwd(), "docs", "specs");
487
+ if (!fs.existsSync(specDir)) return "";
488
+ const specFiles = ["ONE_PAGER.md", "DEV_SPEC.md", "PROMPT_PLAN.md"];
489
+ const sections = [];
490
+ for (const file of specFiles) {
491
+ const filePath = path.join(specDir, file);
492
+ if (fs.existsSync(filePath)) {
493
+ const content = fs.readFileSync(filePath, "utf-8");
494
+ const truncated = content.length > 2e3 ? content.slice(0, 2e3) + "\n...[truncated]" : content;
495
+ sections.push(`### ${file}
496
+ ${truncated}`);
497
+ }
498
+ }
499
+ return sections.length > 0 ? `
500
+ ## Project Specs
501
+ ${sections.join("\n\n")}` : "";
482
502
  }
483
503
  buildAgentPrompt(node, context) {
484
- return `
485
- Task: ${node.description}
486
-
487
- Context:
488
- ${JSON.stringify(context, null, 2)}
489
-
490
- Previous Results:
491
- ${JSON.stringify(
492
- node.dependencies.map((id) => this.activeExecutions.get(id)?.result),
493
- null,
494
- 2
495
- )}
496
-
497
- Please complete this task following your specialized role.
498
- `;
504
+ const depResults = node.dependencies.map((id) => {
505
+ const dep = this.activeExecutions.get(id);
506
+ if (!dep?.result) return null;
507
+ return { id, agent: dep.agent, result: dep.result };
508
+ }).filter(Boolean);
509
+ const specContext = node.agent === "planning" || node.agent === "code" ? this.loadSpecContext() : "";
510
+ return [
511
+ `## Task`,
512
+ node.description,
513
+ "",
514
+ `## Agent Role: ${node.agent}`,
515
+ `Config: ${JSON.stringify(this.subagentConfigs.get(node.agent)?.capabilities || [])}`,
516
+ "",
517
+ `## Context`,
518
+ JSON.stringify(context, null, 2),
519
+ "",
520
+ ...depResults.length > 0 ? [`## Dependency Results`, JSON.stringify(depResults, null, 2), ""] : [],
521
+ ...specContext ? [specContext, ""] : [],
522
+ `## Constraints`,
523
+ `- ESM imports: use .js extensions on relative imports`,
524
+ `- Testing: vitest (not jest)`,
525
+ `- Lint: npm run lint (eslint + prettier)`,
526
+ `- Output structured JSON when possible`
527
+ ].join("\n");
499
528
  }
500
529
  estimateTokens(text) {
501
530
  return Math.ceil(text.length / 4);
@@ -526,9 +555,9 @@ class RecursiveAgentOrchestrator {
526
555
  }
527
556
  calculateNodeCost(tokens, model) {
528
557
  const pricing = {
529
- "claude-3-5-sonnet-latest": 15,
530
- "claude-3-5-haiku-latest": 1,
531
- "claude-3-opus-latest": 75
558
+ "claude-sonnet-4-5-20250929": 15,
559
+ "claude-haiku-4-5-20251001": 1,
560
+ "claude-opus-4-6": 75
532
561
  };
533
562
  return tokens / 1e6 * (pricing[model] || 10);
534
563
  }