@polymorphism-tech/morph-spec 4.8.19 → 4.10.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 (214) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +44 -55
  4. package/bin/task-manager.js +133 -20
  5. package/bin/validate.js +67 -33
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +201 -203
  8. package/docs/QUICKSTART.md +2 -2
  9. package/framework/CLAUDE.md +99 -77
  10. package/framework/agents.json +734 -182
  11. package/framework/commands/commit.md +166 -0
  12. package/framework/commands/morph-apply.md +13 -2
  13. package/framework/commands/morph-archive.md +8 -2
  14. package/framework/commands/morph-infra.md +6 -0
  15. package/framework/commands/morph-preflight.md +6 -0
  16. package/framework/commands/morph-proposal.md +56 -7
  17. package/framework/commands/morph-status.md +6 -0
  18. package/framework/commands/morph-troubleshoot.md +6 -0
  19. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  20. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  21. package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
  22. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
  23. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  24. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  25. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  26. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
  27. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  28. package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
  29. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  30. package/framework/hooks/claude-code/statusline.py +76 -30
  31. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  32. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  33. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  34. package/framework/hooks/shared/activity-logger.js +0 -24
  35. package/framework/hooks/shared/compact-restore.js +100 -0
  36. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  37. package/framework/hooks/shared/phase-utils.js +12 -5
  38. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  39. package/framework/hooks/shared/stale-task-reset.js +57 -0
  40. package/framework/hooks/shared/state-reader.js +29 -5
  41. package/framework/hooks/shared/worktree-helpers.js +53 -0
  42. package/framework/phases.json +69 -14
  43. package/framework/rules/morph-workflow.md +88 -86
  44. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  45. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
  46. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  47. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
  48. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
  49. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
  50. package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
  51. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
  52. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
  53. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  54. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
  55. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
  56. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
  57. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
  58. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
  59. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  60. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  61. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
  62. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  63. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
  64. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  65. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  66. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  67. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  68. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
  69. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
  70. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  71. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
  72. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  73. package/framework/standards/STANDARDS.json +640 -88
  74. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  76. package/framework/templates/REGISTRY.json +1825 -1909
  77. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  78. package/framework/templates/docs/onboarding.md +3 -7
  79. package/package.json +2 -7
  80. package/src/commands/agents/dispatch-agents.js +104 -6
  81. package/src/commands/mcp/mcp-setup.js +39 -2
  82. package/src/commands/phase/phase-reset.js +74 -0
  83. package/src/commands/project/doctor.js +34 -51
  84. package/src/commands/project/init.js +1 -1
  85. package/src/commands/project/status.js +2 -2
  86. package/src/commands/project/update.js +381 -365
  87. package/src/commands/project/worktree.js +154 -0
  88. package/src/commands/scope/escalate.js +215 -0
  89. package/src/commands/state/advance-phase.js +132 -68
  90. package/src/commands/state/approve.js +2 -2
  91. package/src/commands/state/index.js +7 -8
  92. package/src/commands/state/phase-runner.js +1 -1
  93. package/src/commands/state/state.js +61 -6
  94. package/src/commands/task/expand.js +100 -0
  95. package/src/commands/tasks/task.js +78 -99
  96. package/src/commands/templates/template-render.js +93 -173
  97. package/src/commands/trust/trust.js +26 -21
  98. package/src/core/paths/output-schema.js +19 -3
  99. package/src/core/state/phase-state-machine.js +7 -4
  100. package/src/core/state/state-manager.js +32 -57
  101. package/src/core/workflows/workflow-detector.js +9 -87
  102. package/src/lib/detectors/claude-config-detector.js +93 -347
  103. package/src/lib/detectors/design-system-detector.js +189 -189
  104. package/src/lib/detectors/index.js +155 -57
  105. package/src/lib/generators/context-generator.js +2 -2
  106. package/src/lib/installers/mcp-installer.js +37 -5
  107. package/src/lib/phase-chain/phase-validator.js +336 -0
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack/stack-profile.js +88 -0
  110. package/src/lib/tasks/task-classifier.js +16 -0
  111. package/src/lib/tasks/task-parser.js +1 -1
  112. package/src/lib/tasks/test-runner.js +77 -0
  113. package/src/lib/trust/trust-manager.js +32 -144
  114. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  115. package/src/lib/validators/spec-validator.js +58 -4
  116. package/src/lib/validators/validation-runner.js +23 -11
  117. package/src/scripts/setup-infra.js +255 -224
  118. package/src/utils/agents-installer.js +34 -14
  119. package/src/utils/banner.js +1 -1
  120. package/src/utils/claude-settings-manager.js +1 -1
  121. package/src/utils/file-copier.js +1 -1
  122. package/src/utils/hooks-installer.js +272 -8
  123. package/framework/hooks/dev/check-sync-health.js +0 -117
  124. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  125. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  126. package/framework/hooks/dev/sync-template-registry.js +0 -60
  127. package/framework/hooks/dev/validate-skill-format.js +0 -70
  128. package/framework/hooks/dev/validate-standard-format.js +0 -73
  129. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
  130. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
  131. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  132. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  133. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  134. package/framework/workflows/configs/design-impl.json +0 -49
  135. package/framework/workflows/configs/express.json +0 -45
  136. package/framework/workflows/configs/fast-track.json +0 -42
  137. package/framework/workflows/configs/full-morph.json +0 -79
  138. package/framework/workflows/configs/fusion.json +0 -39
  139. package/framework/workflows/configs/long-running.json +0 -33
  140. package/framework/workflows/configs/spec-only.json +0 -43
  141. package/framework/workflows/configs/ui-refresh.json +0 -49
  142. package/framework/workflows/configs/zero-touch.json +0 -82
  143. package/src/commands/project/index.js +0 -8
  144. package/src/commands/project/monitor.js +0 -295
  145. package/src/commands/project/tutorial.js +0 -115
  146. package/src/commands/state/validate-phase.js +0 -238
  147. package/src/commands/templates/generate-contracts.js +0 -445
  148. package/src/core/index.js +0 -10
  149. package/src/core/orchestrator.js +0 -171
  150. package/src/core/registry/command-registry.js +0 -28
  151. package/src/core/registry/index.js +0 -8
  152. package/src/core/registry/validator-registry.js +0 -204
  153. package/src/core/state/index.js +0 -8
  154. package/src/core/templates/index.js +0 -9
  155. package/src/core/templates/template-data-sources.js +0 -325
  156. package/src/core/templates/template-validator.js +0 -296
  157. package/src/core/workflows/index.js +0 -7
  158. package/src/generator/config-generator.js +0 -206
  159. package/src/generator/templates/config.json.template +0 -40
  160. package/src/generator/templates/project.md.template +0 -67
  161. package/src/lib/agents/micro-agent-factory.js +0 -161
  162. package/src/lib/analysis/complexity-analyzer.js +0 -441
  163. package/src/lib/analysis/index.js +0 -7
  164. package/src/lib/analytics/analytics-engine.js +0 -345
  165. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  166. package/src/lib/checkpoints/index.js +0 -7
  167. package/src/lib/context/context-bundler.js +0 -241
  168. package/src/lib/context/context-optimizer.js +0 -212
  169. package/src/lib/context/context-tracker.js +0 -273
  170. package/src/lib/context/core-four-tracker.js +0 -201
  171. package/src/lib/context/mcp-optimizer.js +0 -200
  172. package/src/lib/detectors/config-detector.js +0 -223
  173. package/src/lib/detectors/standards-generator.js +0 -335
  174. package/src/lib/detectors/structure-detector.js +0 -275
  175. package/src/lib/execution/fusion-executor.js +0 -304
  176. package/src/lib/execution/parallel-executor.js +0 -270
  177. package/src/lib/hooks/stop-hook-executor.js +0 -286
  178. package/src/lib/hops/hop-composer.js +0 -221
  179. package/src/lib/monitor/agent-resolver.js +0 -144
  180. package/src/lib/monitor/renderer.js +0 -230
  181. package/src/lib/orchestration/index.js +0 -7
  182. package/src/lib/orchestration/team-orchestrator.js +0 -404
  183. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  184. package/src/lib/threads/thread-coordinator.js +0 -238
  185. package/src/lib/threads/thread-manager.js +0 -317
  186. package/src/lib/tracking/artifact-trail.js +0 -202
  187. package/src/sanitizer/context-sanitizer.js +0 -221
  188. package/src/sanitizer/patterns.js +0 -163
  189. package/src/scanner/project-scanner.js +0 -242
  190. package/src/ui/diff-display.js +0 -91
  191. package/src/ui/interactive-wizard.js +0 -96
  192. package/src/ui/user-review.js +0 -211
  193. package/src/ui/wizard-questions.js +0 -188
  194. package/src/utils/color-utils.js +0 -70
  195. package/src/utils/process-handler.js +0 -97
  196. package/src/writer/file-writer.js +0 -86
  197. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  198. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  199. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  200. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  201. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  202. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  203. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  204. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  205. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  206. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  207. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  208. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  209. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  210. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  211. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  212. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  213. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  214. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -1,238 +0,0 @@
1
- /**
2
- * Thread Coordinator — Dependency analysis and wait strategies
3
- *
4
- * Parses tasks.json dependency graphs and identifies:
5
- * - Tasks that can run in parallel (no dependencies on each other)
6
- * - Tasks that must run sequentially
7
- * - Circular dependencies
8
- *
9
- * Returns an execution plan: phases of parallel groups.
10
- *
11
- * Example output:
12
- * Phase 1 (parallel): [T001, T002, T005] ← no dependencies
13
- * Phase 2 (parallel): [T003, T004] ← depend only on phase 1
14
- * Phase 3 (sequential): [T006] ← depends on both T003, T004
15
- */
16
-
17
- import { readFileSync, existsSync } from 'fs';
18
- import { join } from 'path';
19
-
20
- // ============================================================================
21
- // Dependency Analysis
22
- // ============================================================================
23
-
24
- /**
25
- * Build a dependency graph from an in-memory task array
26
- * @param {Array} tasks - Array of { id, dependencies: [] } task objects
27
- * @returns {Object} { nodes: Map<id, task>, edges: Map<id, Set<depId>>, tasks }
28
- */
29
- export function buildGraph(tasks) {
30
- const nodes = new Map();
31
- const edges = new Map();
32
-
33
- for (const task of tasks) {
34
- nodes.set(task.id, task);
35
- edges.set(task.id, new Set(task.dependencies || []));
36
- }
37
-
38
- return { nodes, edges, tasks };
39
- }
40
-
41
- /**
42
- * Parse tasks.json and build a dependency graph
43
- * @param {string} feature - Feature name
44
- * @returns {Object} { nodes: Map<id, task>, edges: Map<id, Set<depId>> }
45
- */
46
- export function analyzeDependencies(feature) {
47
- const tasksPath = join(process.cwd(), `.morph/features/${feature}/tasks.json`);
48
-
49
- if (!existsSync(tasksPath)) {
50
- throw new Error(`tasks.json not found for feature: ${feature}`);
51
- }
52
-
53
- let tasksData;
54
- try {
55
- tasksData = JSON.parse(readFileSync(tasksPath, 'utf8'));
56
- } catch (err) {
57
- throw new Error(`Failed to parse tasks.json: ${err.message}`);
58
- }
59
-
60
- return buildGraph(tasksData.tasks || []);
61
- }
62
-
63
- /**
64
- * Identify tasks that can run in parallel (no dependencies between them)
65
- * @param {Object} graph - { nodes, edges } from buildGraph/analyzeDependencies
66
- * @returns {Array} Groups of task IDs that can run in parallel
67
- */
68
- export function identifyParallelizable(graph) {
69
- const { nodes, edges } = graph;
70
- const plan = planExecution(nodes, edges);
71
- return plan.filter(group => group.length > 1);
72
- }
73
-
74
- /**
75
- * Identify tasks that must run sequentially
76
- * @param {Object} graph - { nodes, edges } from buildGraph/analyzeDependencies
77
- * @returns {Array} Task IDs that must run sequentially
78
- */
79
- export function identifySequential(graph) {
80
- const { nodes, edges } = graph;
81
- const plan = planExecution(nodes, edges);
82
- return plan.filter(group => group.length === 1).flat();
83
- }
84
-
85
- /**
86
- * Detect circular dependencies in the task graph
87
- * @param {Map} nodes - Task nodes
88
- * @param {Map} edges - Dependency edges
89
- * @returns {Array} Array of circular dependency chains, or [] if none
90
- */
91
- export function detectCircular(nodes, edges) {
92
- const cycles = [];
93
- const visited = new Set();
94
- const recursionStack = new Set();
95
-
96
- function dfs(nodeId, path) {
97
- visited.add(nodeId);
98
- recursionStack.add(nodeId);
99
-
100
- for (const dep of (edges.get(nodeId) || [])) {
101
- if (!nodes.has(dep)) continue;
102
-
103
- if (!visited.has(dep)) {
104
- dfs(dep, [...path, nodeId]);
105
- } else if (recursionStack.has(dep)) {
106
- // Found a cycle
107
- const cycleStart = path.indexOf(dep);
108
- const cycle = cycleStart >= 0
109
- ? [...path.slice(cycleStart), nodeId, dep]
110
- : [...path, nodeId, dep];
111
- cycles.push(cycle);
112
- }
113
- }
114
-
115
- recursionStack.delete(nodeId);
116
- }
117
-
118
- for (const nodeId of nodes.keys()) {
119
- if (!visited.has(nodeId)) {
120
- dfs(nodeId, []);
121
- }
122
- }
123
-
124
- return cycles;
125
- }
126
-
127
- /**
128
- * Plan execution: return phases of parallel task groups
129
- * @param {Map} nodes - Task nodes
130
- * @param {Map} edges - Dependency edges
131
- * @returns {Array} Array of phases, each phase is an array of task IDs
132
- *
133
- * Algorithm: Kahn's topological sort, grouping by "wave"
134
- */
135
- export function planExecution(nodes, edges) {
136
- // Check for cycles first
137
- const cycles = detectCircular(nodes, edges);
138
- if (cycles.length > 0) {
139
- throw new Error(
140
- `Circular dependencies detected: ${cycles.map(c => c.join(' → ')).join('; ')}`
141
- );
142
- }
143
-
144
- // Build reverse edges (dependents)
145
- const inDegree = new Map();
146
- const dependents = new Map(); // id → Set of task IDs that depend on it
147
-
148
- for (const [id] of nodes) {
149
- inDegree.set(id, 0);
150
- dependents.set(id, new Set());
151
- }
152
-
153
- for (const [id, deps] of edges) {
154
- for (const dep of deps) {
155
- if (!inDegree.has(dep)) continue;
156
- inDegree.set(id, (inDegree.get(id) || 0) + 1);
157
- dependents.get(dep).add(id);
158
- }
159
- }
160
-
161
- // Correct in-degree: count only deps that exist in nodes
162
- for (const [id, deps] of edges) {
163
- let count = 0;
164
- for (const dep of deps) {
165
- if (nodes.has(dep)) count++;
166
- }
167
- inDegree.set(id, count);
168
- }
169
-
170
- const phases = [];
171
- const remaining = new Set(nodes.keys());
172
-
173
- while (remaining.size > 0) {
174
- // Find all nodes with no unsatisfied dependencies
175
- const ready = [...remaining].filter(id => {
176
- const deps = edges.get(id) || new Set();
177
- return [...deps].every(dep => !remaining.has(dep));
178
- });
179
-
180
- if (ready.length === 0 && remaining.size > 0) {
181
- // Shouldn't happen if no cycles, but safeguard
182
- throw new Error(`Execution plan stalled with ${remaining.size} remaining tasks: ${[...remaining].join(', ')}`);
183
- }
184
-
185
- phases.push(ready);
186
- for (const id of ready) {
187
- remaining.delete(id);
188
- }
189
- }
190
-
191
- return phases;
192
- }
193
-
194
- // ============================================================================
195
- // Full Analysis
196
- // ============================================================================
197
-
198
- /**
199
- * Get full parallel execution plan for a feature
200
- * @param {string} feature - Feature name
201
- * @returns {Object} Execution plan with phases, parallelizable groups, and stats
202
- */
203
- export function getExecutionPlan(feature) {
204
- const { nodes, edges, tasks } = analyzeDependencies(feature);
205
-
206
- const cycles = detectCircular(nodes, edges);
207
- if (cycles.length > 0) {
208
- return {
209
- feature,
210
- valid: false,
211
- cycles,
212
- error: `Circular dependencies found: ${cycles.map(c => c.join(' → ')).join('; ')}`
213
- };
214
- }
215
-
216
- const phases = planExecution(nodes, edges);
217
- const parallelPhases = phases.filter(p => p.length > 1);
218
- const totalParallelizable = parallelPhases.reduce((sum, p) => sum + p.length, 0);
219
-
220
- return {
221
- feature,
222
- valid: true,
223
- totalTasks: tasks.length,
224
- phases: phases.map((group, i) => ({
225
- phase: i + 1,
226
- tasks: group,
227
- canRunParallel: group.length > 1,
228
- count: group.length
229
- })),
230
- stats: {
231
- totalPhases: phases.length,
232
- parallelPhases: parallelPhases.length,
233
- parallelizableTasks: totalParallelizable,
234
- sequentialTasks: tasks.length - totalParallelizable,
235
- parallelizationRatio: Math.round(totalParallelizable / Math.max(tasks.length, 1) * 100)
236
- }
237
- };
238
- }
@@ -1,317 +0,0 @@
1
- /**
2
- * Thread Manager — Core thread lifecycle management
3
- *
4
- * Manages the full lifecycle of MORPH-SPEC threads:
5
- * - Types: base, parallel, fusion, long-running, zero-touch
6
- * - Status transitions: pending → running → completed | failed | killed
7
- * - Persists thread state to state.json threads{} section
8
- */
9
-
10
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
11
- import { join } from 'path';
12
- import { randomUUID } from 'crypto';
13
-
14
- const STATE_PATH = join(process.cwd(), '.morph/state.json');
15
-
16
- // ============================================================================
17
- // Thread Types and Status
18
- // ============================================================================
19
-
20
- export const THREAD_TYPES = {
21
- BASE: 'base',
22
- PARALLEL: 'parallel',
23
- FUSION: 'fusion',
24
- LONG_RUNNING: 'long-running',
25
- ZERO_TOUCH: 'zero-touch'
26
- };
27
-
28
- export const THREAD_STATUS = {
29
- PENDING: 'pending',
30
- RUNNING: 'running',
31
- COMPLETED: 'completed',
32
- FAILED: 'failed',
33
- KILLED: 'killed'
34
- };
35
-
36
- // ============================================================================
37
- // State I/O
38
- // ============================================================================
39
-
40
- function loadState() {
41
- if (!existsSync(STATE_PATH)) {
42
- throw new Error(`State file not found: ${STATE_PATH}`);
43
- }
44
- return JSON.parse(readFileSync(STATE_PATH, 'utf8'));
45
- }
46
-
47
- function saveState(state) {
48
- state.metadata = state.metadata || {};
49
- state.metadata.lastUpdated = new Date().toISOString();
50
- writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), 'utf8');
51
- }
52
-
53
- function ensureThreadsSection(state) {
54
- if (!state.threads) {
55
- state.threads = {};
56
- }
57
- return state;
58
- }
59
-
60
- // ============================================================================
61
- // Thread CRUD
62
- // ============================================================================
63
-
64
- /**
65
- * Create a new thread
66
- * @param {Object} opts
67
- * @param {string} opts.feature - Feature name this thread belongs to
68
- * @param {string} opts.type - Thread type (base|parallel|fusion|long-running|zero-touch)
69
- * @param {string} [opts.agent] - Agent assigned to this thread
70
- * @param {string} [opts.mission] - Thread mission description
71
- * @param {Object} [opts.meta] - Additional metadata
72
- * @returns {Object} Created thread object
73
- */
74
- export function createThread({ feature, type = THREAD_TYPES.BASE, agent = null, mission = '', meta = {} }) {
75
- const state = ensureThreadsSection(loadState());
76
- const id = randomUUID();
77
- const now = new Date().toISOString();
78
-
79
- const thread = {
80
- id,
81
- feature,
82
- type,
83
- agent,
84
- mission,
85
- status: THREAD_STATUS.PENDING,
86
- createdAt: now,
87
- startedAt: null,
88
- completedAt: null,
89
- duration: null,
90
- events: [],
91
- metrics: {
92
- toolCalls: 0,
93
- tokensUsed: 0,
94
- checkpointsPassed: 0,
95
- checkpointsFailed: 0
96
- },
97
- meta
98
- };
99
-
100
- state.threads[id] = thread;
101
- saveState(state);
102
- return thread;
103
- }
104
-
105
- /**
106
- * Update thread fields
107
- * @param {string} id - Thread ID
108
- * @param {Object} updates - Fields to update
109
- * @returns {Object} Updated thread
110
- */
111
- export function updateThread(id, updates) {
112
- const state = ensureThreadsSection(loadState());
113
-
114
- if (!state.threads[id]) {
115
- throw new Error(`Thread not found: ${id}`);
116
- }
117
-
118
- state.threads[id] = { ...state.threads[id], ...updates };
119
- saveState(state);
120
- return state.threads[id];
121
- }
122
-
123
- /**
124
- * Get a thread by ID
125
- * @param {string} id - Thread ID
126
- * @returns {Object|null} Thread or null
127
- */
128
- export function getThread(id) {
129
- const state = ensureThreadsSection(loadState());
130
- return state.threads[id] || null;
131
- }
132
-
133
- /**
134
- * List threads, optionally filtered
135
- * @param {Object} [filter]
136
- * @param {string} [filter.feature] - Filter by feature name
137
- * @param {string} [filter.status] - Filter by status
138
- * @param {string} [filter.type] - Filter by type
139
- * @returns {Array} Array of thread objects
140
- */
141
- export function listThreads({ feature, status, type } = {}) {
142
- const state = ensureThreadsSection(loadState());
143
- let threads = Object.values(state.threads);
144
-
145
- if (feature) threads = threads.filter(t => t.feature === feature);
146
- if (status) threads = threads.filter(t => t.status === status);
147
- if (type) threads = threads.filter(t => t.type === type);
148
-
149
- return threads.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
150
- }
151
-
152
- /**
153
- * Start a thread (pending → running)
154
- * @param {string} id - Thread ID
155
- * @returns {Object} Updated thread
156
- */
157
- export function startThread(id) {
158
- const now = new Date().toISOString();
159
- return updateThread(id, {
160
- status: THREAD_STATUS.RUNNING,
161
- startedAt: now
162
- });
163
- }
164
-
165
- /**
166
- * Complete a thread (running → completed)
167
- * @param {string} id - Thread ID
168
- * @param {Object} [result] - Optional result metadata
169
- * @returns {Object} Updated thread
170
- */
171
- export function completeThread(id, result = {}) {
172
- const thread = getThread(id);
173
- if (!thread) throw new Error(`Thread not found: ${id}`);
174
-
175
- const now = new Date();
176
- const startedAt = thread.startedAt ? new Date(thread.startedAt) : now;
177
- const duration = Math.round((now - startedAt) / 1000); // seconds
178
-
179
- return updateThread(id, {
180
- status: THREAD_STATUS.COMPLETED,
181
- completedAt: now.toISOString(),
182
- duration,
183
- result
184
- });
185
- }
186
-
187
- /**
188
- * Mark a thread as failed
189
- * @param {string} id - Thread ID
190
- * @param {string} reason - Failure reason
191
- * @returns {Object} Updated thread
192
- */
193
- export function failThread(id, reason = '') {
194
- const thread = getThread(id);
195
- if (!thread) throw new Error(`Thread not found: ${id}`);
196
-
197
- const now = new Date();
198
- const startedAt = thread.startedAt ? new Date(thread.startedAt) : now;
199
- const duration = Math.round((now - startedAt) / 1000);
200
-
201
- return updateThread(id, {
202
- status: THREAD_STATUS.FAILED,
203
- completedAt: now.toISOString(),
204
- duration,
205
- failureReason: reason
206
- });
207
- }
208
-
209
- /**
210
- * Kill (terminate) a running thread
211
- * @param {string} id - Thread ID
212
- * @returns {Object} Updated thread
213
- */
214
- export function killThread(id) {
215
- const thread = getThread(id);
216
- if (!thread) throw new Error(`Thread not found: ${id}`);
217
- if (thread.status === THREAD_STATUS.COMPLETED || thread.status === THREAD_STATUS.KILLED) {
218
- throw new Error(`Cannot kill thread in status: ${thread.status}`);
219
- }
220
-
221
- const now = new Date();
222
- const startedAt = thread.startedAt ? new Date(thread.startedAt) : now;
223
- const duration = Math.round((now - startedAt) / 1000);
224
-
225
- return updateThread(id, {
226
- status: THREAD_STATUS.KILLED,
227
- completedAt: now.toISOString(),
228
- duration
229
- });
230
- }
231
-
232
- /**
233
- * Add an event to a thread's event log
234
- * @param {string} id - Thread ID
235
- * @param {string} type - Event type
236
- * @param {Object} [data] - Event data
237
- */
238
- export function addThreadEvent(id, type, data = {}) {
239
- const state = ensureThreadsSection(loadState());
240
- const thread = state.threads[id];
241
- if (!thread) throw new Error(`Thread not found: ${id}`);
242
-
243
- thread.events = thread.events || [];
244
- thread.events.push({
245
- type,
246
- timestamp: new Date().toISOString(),
247
- data
248
- });
249
-
250
- saveState(state);
251
- }
252
-
253
- /**
254
- * Increment thread metrics counter
255
- * @param {string} id - Thread ID
256
- * @param {string} metric - Metric name (toolCalls|tokensUsed|checkpointsPassed|checkpointsFailed)
257
- * @param {number} [amount=1] - Amount to increment
258
- */
259
- export function incrementThreadMetric(id, metric, amount = 1) {
260
- const state = ensureThreadsSection(loadState());
261
- const thread = state.threads[id];
262
- if (!thread) throw new Error(`Thread not found: ${id}`);
263
-
264
- thread.metrics = thread.metrics || {};
265
- thread.metrics[metric] = (thread.metrics[metric] || 0) + amount;
266
- saveState(state);
267
- }
268
-
269
- // ============================================================================
270
- // Analytics
271
- // ============================================================================
272
-
273
- /**
274
- * Get analytics for threads belonging to a feature
275
- * @param {string} feature - Feature name
276
- * @returns {Object} Analytics summary
277
- */
278
- export function getThreadAnalytics(feature) {
279
- const threads = listThreads({ feature });
280
-
281
- const byStatus = threads.reduce((acc, t) => {
282
- acc[t.status] = (acc[t.status] || 0) + 1;
283
- return acc;
284
- }, {});
285
-
286
- const byType = threads.reduce((acc, t) => {
287
- acc[t.type] = (acc[t.type] || 0) + 1;
288
- return acc;
289
- }, {});
290
-
291
- const completed = threads.filter(t => t.status === THREAD_STATUS.COMPLETED);
292
- const avgDuration = completed.length > 0
293
- ? Math.round(completed.reduce((sum, t) => sum + (t.duration || 0), 0) / completed.length)
294
- : 0;
295
-
296
- const totalToolCalls = threads.reduce((sum, t) => sum + (t.metrics?.toolCalls || 0), 0);
297
- const parallelThreads = threads.filter(t => t.type === THREAD_TYPES.PARALLEL);
298
- const avgParallel = parallelThreads.length;
299
-
300
- return {
301
- feature,
302
- total: threads.length,
303
- byStatus,
304
- byType,
305
- avgDuration,
306
- totalToolCalls,
307
- parallelEfficiency: {
308
- avgParallel,
309
- maxParallel: Math.max(...threads.map(() => parallelThreads.length), 0)
310
- },
311
- checkpointPassRate: threads.reduce((sum, t) => {
312
- const p = t.metrics?.checkpointsPassed || 0;
313
- const f = t.metrics?.checkpointsFailed || 0;
314
- return p + f > 0 ? sum + (p / (p + f)) : sum;
315
- }, 0) / Math.max(threads.filter(t => (t.metrics?.checkpointsPassed || 0) + (t.metrics?.checkpointsFailed || 0) > 0).length, 1)
316
- };
317
- }