@polymorphism-tech/morph-spec 4.21.0 → 5.0.1

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 (213) hide show
  1. package/CLAUDE.md +57 -71
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +224 -491
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +55 -38
  8. package/framework/MORPH.md +244 -0
  9. package/framework/agents.json +127 -1183
  10. package/framework/commands/morph-apply.md +56 -240
  11. package/framework/commands/morph-archive.md +48 -85
  12. package/framework/commands/morph-preflight.md +7 -7
  13. package/framework/commands/morph-proposal.md +35 -191
  14. package/framework/commands/morph-status.md +19 -50
  15. package/framework/commands/morph-troubleshoot.md +2 -2
  16. package/framework/evals/rubrics/README.md +44 -0
  17. package/framework/evals/rubrics/architecture.md +46 -0
  18. package/framework/evals/rubrics/code-quality.md +50 -0
  19. package/framework/evals/rubrics/contracts.md +47 -0
  20. package/framework/evals/rubrics/test-coverage.md +50 -0
  21. package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
  22. package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
  23. package/framework/evals/suites/standard.yaml +5 -0
  24. package/framework/evals/trace-schema.md +67 -0
  25. package/framework/hooks/claude-code/core/README.md +3 -0
  26. package/framework/hooks/claude-code/core/denylist.json +14 -0
  27. package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
  28. package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
  30. package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
  31. package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
  32. package/framework/hooks/shared/phase-utils.js +20 -28
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -210
  35. package/framework/phases.json +40 -114
  36. package/framework/rubrics/eval-rubric.json +66 -0
  37. package/framework/skills/README.md +39 -64
  38. package/framework/skills/core/README.md +3 -0
  39. package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
  40. package/framework/skills/core/morph-eval/SKILL.md +57 -0
  41. package/framework/skills/core/morph-implement/SKILL.md +83 -0
  42. package/framework/skills/core/morph-plan/SKILL.md +64 -0
  43. package/framework/skills/core/morph-review/SKILL.md +62 -0
  44. package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
  45. package/framework/skills/core/morph-standards/SKILL.md +49 -0
  46. package/framework/standards/STANDARDS.json +32 -0
  47. package/framework/standards/frontend/design-system/ai-image-generation.md +244 -0
  48. package/framework/standards/integration/mcp/mcp-tools.md +48 -0
  49. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  50. package/framework/templates/CLAUDE.md.core.template +22 -0
  51. package/framework/templates/REGISTRY.json +4 -4
  52. package/framework/workflows/configs/fast-track.json +5 -10
  53. package/framework/workflows/configs/full.json +4 -8
  54. package/framework/workflows/configs/quick.json +32 -0
  55. package/framework/workflows/configs/standard.json +57 -64
  56. package/package.json +7 -6
  57. package/src/commands/mcp/mcp-setup.js +58 -31
  58. package/src/commands/project/doctor.js +69 -70
  59. package/src/commands/project/init.js +26 -5
  60. package/src/commands/project/status.js +18 -19
  61. package/src/commands/state/approve.js +1 -1
  62. package/src/commands/validation/validate-feature.js +18 -112
  63. package/src/core/paths/output-schema.js +133 -151
  64. package/src/core/state/phase-state-machine.js +10 -24
  65. package/src/core/state/state-manager.js +17 -17
  66. package/src/core/workflows/workflow-detector.js +214 -31
  67. package/src/core/workflows/workflow-router.js +204 -0
  68. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  69. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  70. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  71. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  72. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  73. package/src/dashboard/scanner/agents-scanner.js +30 -35
  74. package/src/lib/agents/metrics-aggregator.js +1 -1
  75. package/src/lib/agents/output-collector.js +7 -8
  76. package/src/lib/context/context-builder.js +289 -0
  77. package/src/lib/detectors/claude-config-detector.js +19 -3
  78. package/src/lib/generators/context-generator.js +20 -42
  79. package/src/lib/generators/index.js +1 -0
  80. package/src/lib/generators/mandate-generator.js +512 -0
  81. package/src/lib/generators/metadata-extractor.js +1 -1
  82. package/src/lib/generators/recap-generator.js +3 -4
  83. package/src/lib/installers/mcp-installer.js +300 -92
  84. package/src/lib/phase-chain/phase-validator.js +36 -40
  85. package/src/lib/stack/stack-profile.js +74 -91
  86. package/src/lib/tasks/task-parser.js +1 -1
  87. package/src/lib/tasks/wave-resolver.js +353 -0
  88. package/src/lib/validators/checkpoint-persistence.js +2 -2
  89. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  90. package/src/lib/validators/content/index.js +1 -0
  91. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  92. package/src/scripts/global-install.js +2 -27
  93. package/src/utils/agents-installer.js +224 -447
  94. package/src/utils/claude-settings-manager.js +57 -15
  95. package/src/utils/file-copier.js +1 -0
  96. package/src/utils/hooks-installer.js +15 -117
  97. package/src/utils/skills-installer.js +12 -18
  98. package/bin/detect-agents.js +0 -226
  99. package/bin/task-manager.js +0 -1036
  100. package/framework/agents/README.md +0 -13
  101. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  102. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  103. package/framework/agents/architecture/architecture-expert.md +0 -98
  104. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  105. package/framework/agents/architecture/packages-validator.md +0 -89
  106. package/framework/agents/architecture/security-expert.md +0 -79
  107. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  108. package/framework/agents/architecture/standards-architect.md +0 -153
  109. package/framework/agents/backend/dotnet-senior.md +0 -163
  110. package/framework/agents/backend/ef-modeler.md +0 -119
  111. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  112. package/framework/agents/backend/ms-agent-expert.md +0 -55
  113. package/framework/agents/backend/neon-expert.md +0 -379
  114. package/framework/agents/frontend/css-specialist.md +0 -89
  115. package/framework/agents/frontend/design-system-validator.md +0 -100
  116. package/framework/agents/frontend/gsap-animator.md +0 -131
  117. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  118. package/framework/agents/frontend/nextjs-expert.md +0 -201
  119. package/framework/agents/frontend/ui-designer.md +0 -259
  120. package/framework/agents/infrastructure/infra-architect.md +0 -215
  121. package/framework/agents/integrations/asaas-financial.md +0 -138
  122. package/framework/agents/integrations/neon-auth.md +0 -151
  123. package/framework/agents/integrations/resend-email.md +0 -127
  124. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  125. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  126. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  127. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  128. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  129. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  130. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  131. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  132. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  133. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  134. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  135. package/framework/hooks/shared/compact-restore.js +0 -123
  136. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  137. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  138. package/framework/hooks/shared/stale-task-reset.js +0 -57
  139. package/framework/hooks/shared/worktree-helpers.js +0 -75
  140. package/framework/skills/level-0-meta/README.md +0 -7
  141. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  142. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  143. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  144. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  145. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  146. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  147. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  148. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  149. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  150. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  151. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  152. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  153. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  154. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  155. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  156. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -382
  157. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  158. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  159. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  160. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  161. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  162. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  163. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  164. package/framework/skills/level-1-workflows/README.md +0 -7
  165. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  166. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  168. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  169. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  170. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  171. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -493
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  173. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -46
  174. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  175. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  176. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  177. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  181. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  182. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  183. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -788
  184. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  185. package/framework/squad-templates/backend-only.json +0 -47
  186. package/framework/squad-templates/frontend-only.json +0 -56
  187. package/framework/squad-templates/full-stack.json +0 -74
  188. package/src/commands/agents/agent-metrics.js +0 -174
  189. package/src/commands/agents/audit-dispatch.js +0 -82
  190. package/src/commands/agents/detect-agents.js +0 -275
  191. package/src/commands/agents/dispatch-agents.js +0 -461
  192. package/src/commands/agents/index.js +0 -13
  193. package/src/commands/agents/manage-agents.js +0 -352
  194. package/src/commands/agents/project-metrics.js +0 -74
  195. package/src/commands/phase/phase-reset.js +0 -74
  196. package/src/commands/scope/escalate.js +0 -217
  197. package/src/commands/state/advance-phase.js +0 -600
  198. package/src/commands/state/phase-runner.js +0 -215
  199. package/src/commands/state/state.js +0 -444
  200. package/src/commands/task/expand.js +0 -100
  201. package/src/commands/tasks/index.js +0 -4
  202. package/src/commands/tasks/task.js +0 -105
  203. package/src/commands/templates/index.js +0 -5
  204. package/src/commands/templates/template-render.js +0 -93
  205. package/src/commands/trust/trust.js +0 -118
  206. package/src/lib/agents/agents-manager.js +0 -349
  207. package/src/lib/build/build-runner.js +0 -99
  208. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  209. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  210. package/src/lib/scope/impact-analyzer.js +0 -106
  211. package/src/lib/trust/trust-manager.js +0 -157
  212. package/src/utils/tier-helpers.js +0 -24
  213. /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
@@ -1,600 +0,0 @@
1
- /**
2
- * MORPH-SPEC Phase Advance Command
3
- *
4
- * Validates current phase → advances to next → shows requirements.
5
- * Replaces the two-step `state set` + `validate-phase` dance.
6
- *
7
- * Usage:
8
- * morph-spec phase advance <feature>
9
- * morph-spec phase advance <feature> --skip-optional
10
- */
11
-
12
- import chalk from 'chalk';
13
- import { loadState, saveState, getFeature, getApprovalGate, derivePhase } from '../../core/state/state-manager.js';
14
- import { PHASES, validatePhase } from '../../lib/phase-chain/phase-validator.js';
15
- import { detectDesignSystem, hasUIAgentsActive } from '../../lib/detectors/design-system-detector.js';
16
- import { getOutputPath, getAbsoluteOutputPath } from '../../core/paths/output-schema.js';
17
- import { shouldAutoApprove } from '../../lib/trust/trust-manager.js';
18
- import { validateSpec } from '../../lib/validators/spec-validator.js';
19
- import { validateTransition } from '../../core/state/phase-state-machine.js';
20
- import { validateSpecContent, validateTasksContent } from '../../lib/validators/content/content-validator.js';
21
- import { getWorkflowConfig } from '../../core/workflows/workflow-detector.js';
22
- import { getStackProfile } from '../../lib/stack/stack-profile.js';
23
- import { readFileSync, existsSync, mkdirSync } from 'fs';
24
- import { join, dirname } from 'path';
25
- import { fileURLToPath } from 'url';
26
- import { emitValidatorDispatch } from '../../lib/validators/shared/emit-validator-dispatch.js';
27
-
28
- const __dirname = dirname(fileURLToPath(import.meta.url));
29
-
30
- // Phase order for advancing (skips optional phases unless active)
31
- const PHASE_ORDER = ['proposal', 'uiux', 'design', 'plan', 'tasks', 'implement', 'sync'];
32
-
33
- /**
34
- * Phases that require an explicit approval gate before advancing.
35
- * When --skip-approval is passed, these gates are bypassed and a warning is logged.
36
- * Exported for use in tests and in phase-runner warnings.
37
- */
38
- export const APPROVAL_GATE_MAP = {
39
- 'design': 'design',
40
- 'plan': 'plan',
41
- 'tasks': 'tasks',
42
- 'uiux': 'uiux'
43
- };
44
-
45
- /**
46
- * Get the next phase after the current one.
47
- * Returns { nextPhase, skippedPhases } so callers can persist skipped phases in state.
48
- */
49
- function getNextPhase(currentPhase, feature, skipOptional, featureName) {
50
- // Load workflow config if workflow is set
51
- let workflowConfig = null;
52
- if (feature.workflow && feature.workflow !== 'auto') {
53
- workflowConfig = getWorkflowConfig(feature.workflow);
54
- }
55
-
56
- const currentIndex = PHASE_ORDER.indexOf(currentPhase);
57
- if (currentIndex === -1 || currentIndex >= PHASE_ORDER.length - 1) {
58
- return { nextPhase: null, skippedPhases: [] };
59
- }
60
-
61
- // Agents that justify the uiux phase (wireframes, design system).
62
- // Implementation-only agents (nextjs-expert) are NOT included —
63
- // they build components from existing designs but don't create the design artifacts.
64
- const uiDesignAgents = ['uiux-designer', 'ui-ux-designer', 'ui-designer'];
65
-
66
- const skippedPhases = [];
67
-
68
- for (let i = currentIndex + 1; i < PHASE_ORDER.length; i++) {
69
- const candidate = PHASE_ORDER[i];
70
- const phaseDef = PHASES[candidate];
71
-
72
- // === Workflow-Based Phase Skipping ===
73
- if (workflowConfig && workflowConfig.phases) {
74
- // Check if workflow explicitly skips this phase
75
- if (workflowConfig.phases.skip && workflowConfig.phases.skip.includes(candidate)) {
76
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: workflow ${feature.workflow} skips this phase`));
77
- skippedPhases.push(candidate);
78
- continue;
79
- }
80
-
81
- // Check if this phase is part of a combined phase
82
- if (workflowConfig.phases.combined) {
83
- let isCombined = false;
84
- for (const [combinedName, phases] of Object.entries(workflowConfig.phases.combined)) {
85
- if (phases.includes(candidate) && candidate !== combinedName) {
86
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: combined into ${combinedName} phase`));
87
- skippedPhases.push(candidate);
88
- isCombined = true;
89
- break;
90
- }
91
- }
92
- if (isCombined) continue;
93
- }
94
-
95
- // Check if workflow only runs specific phases
96
- if (workflowConfig.phases.run && !workflowConfig.phases.run.includes(candidate)) {
97
- // Also check if it's not a combined phase
98
- const isCombinedPhase = workflowConfig.phases.combined &&
99
- Object.keys(workflowConfig.phases.combined).includes(candidate);
100
- if (!isCombinedPhase) {
101
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: not in workflow run list`));
102
- skippedPhases.push(candidate);
103
- continue;
104
- }
105
- }
106
-
107
- // Check conditional skips
108
- if (workflowConfig.phases.skipIfCondition) {
109
- const condition = workflowConfig.phases.skipIfCondition[candidate];
110
- if (condition) {
111
- // Evaluate condition (simple conditions for now)
112
- if (condition === '!hasUIAgents' && !hasUIAgentsActive(process.cwd(), featureName)) {
113
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
114
- skippedPhases.push(candidate);
115
- continue;
116
- }
117
- }
118
- }
119
- }
120
-
121
- // === Legacy Phase Skipping (fallback when no workflow config) ===
122
- if (!workflowConfig) {
123
- // Skip optional phases if flag set or no relevant agents/outputs
124
- if (phaseDef?.optional && skipOptional) {
125
- skippedPhases.push(candidate);
126
- continue;
127
- }
128
-
129
- // Skip uiux if no UI design agents are active
130
- if (candidate === 'uiux') {
131
- if (feature.workflow === 'migration' || feature.workflow === 'refactor') {
132
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: ${feature.workflow} workflow — no visual changes`));
133
- skippedPhases.push(candidate);
134
- continue;
135
- }
136
- const hasUiDesignAgent = feature.activeAgents?.some(a => uiDesignAgents.includes(a));
137
- if (!hasUiDesignAgent && skipOptional !== false) {
138
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI design agents active`));
139
- skippedPhases.push(candidate);
140
- continue;
141
- }
142
- }
143
-
144
- // sync is optional — only skip if --skip-optional is set
145
- if (candidate === 'sync' && skipOptional) {
146
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: optional phase`));
147
- skippedPhases.push(candidate);
148
- continue;
149
- }
150
- }
151
-
152
- return { nextPhase: candidate, skippedPhases };
153
- }
154
-
155
- return { nextPhase: null, skippedPhases };
156
- }
157
-
158
- /**
159
- * Main command handler
160
- */
161
- export async function advancePhaseCommand(feature, options = {}) {
162
- console.log(chalk.cyan('\n╔════════════════════════════════════════════════╗'));
163
- console.log(chalk.cyan('║ MORPH-SPEC PHASE ADVANCE ║'));
164
- console.log(chalk.cyan('╚════════════════════════════════════════════════╝\n'));
165
-
166
- // Get current feature state
167
- const featureData = getFeature(feature);
168
- if (!featureData) {
169
- console.log(chalk.red(`✗ Feature not found: ${feature}`));
170
- console.log(chalk.yellow(` Run: morph-spec state set ${feature} phase proposal`));
171
- process.exit(1);
172
- }
173
-
174
- const featureFolderPath = join(process.cwd(), '.morph', 'features', feature);
175
- const currentPhase = featureData.phase || derivePhase(featureFolderPath);
176
- const currentPhaseDef = PHASES[currentPhase];
177
-
178
- console.log(chalk.gray('Feature:'), feature);
179
- console.log(chalk.gray('Current Phase:'), currentPhaseDef?.name || currentPhase);
180
- console.log(chalk.gray('Workflow:'), featureData.workflow || 'auto');
181
-
182
- // Determine next phase
183
- const { nextPhase, skippedPhases } = getNextPhase(currentPhase, featureData, options.skipOptional, feature);
184
-
185
- if (!nextPhase) {
186
- console.log(chalk.green('\n✓ Feature is at the final phase!'));
187
- if (currentPhase === 'implement' || currentPhase === 'sync') {
188
- console.log(chalk.green(' Consider running: morph-spec generate recap ' + feature));
189
- }
190
- return;
191
- }
192
-
193
- const nextPhaseDef = PHASES[nextPhase];
194
- console.log(chalk.gray('Next Phase:'), nextPhaseDef.name);
195
-
196
- // === GATE 1: State Machine Validation ===
197
- // When a workflow config defines which phases to run, getNextPhase()
198
- // is authoritative for phase navigation. The state machine's
199
- // adjacent-only transitions would reject legitimate workflow skips
200
- // (e.g., fast-track: proposal → tasks).
201
- const activeWorkflowConfig = featureData.workflow && featureData.workflow !== 'auto'
202
- ? getWorkflowConfig(featureData.workflow)
203
- : null;
204
-
205
- if (!options.force && !activeWorkflowConfig) {
206
- try {
207
- validateTransition(currentPhase, nextPhase);
208
- } catch (error) {
209
- console.log(chalk.red('\n✗ Invalid phase transition'));
210
- console.log(chalk.yellow(error.message));
211
- console.log(chalk.gray('\nUse --force to override (not recommended)\n'));
212
- process.exit(1);
213
- }
214
- }
215
-
216
- // === GATE 2: Approval Gate Check ===
217
- // Check if current phase requires approval before advancing
218
- // Track gates that get approved implicitly so we can persist them after state load.
219
- let gateApprovedImplicitly = null; // { gate, approvedBy, reason }
220
-
221
- const requiredGate = APPROVAL_GATE_MAP[currentPhase];
222
- if (requiredGate && options.skipApproval) {
223
- console.log(chalk.yellow(`\n⚠ --skip-approval: Approval gate "${requiredGate}" bypassed for phase "${currentPhase}"`));
224
- console.log(chalk.gray(' Intended for CI/automation only. Run without --skip-approval for normal workflow.\n'));
225
- gateApprovedImplicitly = { gate: requiredGate, approvedBy: 'skip-approval' };
226
- }
227
- if (requiredGate && !options.skipApproval) {
228
- const gateStatus = getApprovalGate(feature, requiredGate);
229
-
230
- // Check trust-based auto-approval before blocking
231
- if (!gateStatus || !gateStatus.approved) {
232
- try {
233
- const trustResult = shouldAutoApprove(feature, requiredGate);
234
- if (trustResult.autoApprove) {
235
- console.log(chalk.green(`\n✓ Auto-approved gate "${requiredGate}" (${trustResult.reason})`));
236
- // Proceed — trust bypasses manual gate requirement
237
- gateApprovedImplicitly = { gate: requiredGate, approvedBy: `trust:${trustResult.level}` };
238
- } else {
239
- console.log(chalk.red(`\n✗ Phase "${currentPhase}" requires approval before advancing`));
240
- console.log(chalk.gray(` Trust level: ${trustResult.level} (${trustResult.reason})`));
241
- console.log(chalk.yellow(`\nRun: morph-spec approve ${feature} ${requiredGate}`));
242
- console.log(chalk.gray('Or use --skip-approval to bypass (not recommended)\n'));
243
- process.exit(1);
244
- }
245
- } catch {
246
- // Trust manager unavailable — fall through to manual gate check
247
- console.log(chalk.red(`\n✗ Phase "${currentPhase}" requires approval before advancing`));
248
- console.log(chalk.yellow(`\nRun: morph-spec approve ${feature} ${requiredGate}`));
249
- console.log(chalk.gray('Or use --skip-approval to bypass (not recommended)\n'));
250
- process.exit(1);
251
- }
252
- } else {
253
- console.log(chalk.green(`✓ Approval gate "${requiredGate}" passed`));
254
- }
255
- }
256
-
257
- // === GATE 3: Output Requirements ===
258
- // Validate that current phase requirements are met before advancing
259
- const validation = validatePhase(feature, nextPhase);
260
-
261
- if (!validation.valid) {
262
- console.log(chalk.red('\n✗ Cannot advance — missing requirements:'));
263
- validation.missingOutputs.forEach(output => {
264
- console.log(chalk.red(` - ${output}`));
265
- });
266
- console.log(chalk.yellow(`\n Complete these before advancing to ${nextPhaseDef.name}`));
267
- process.exit(1);
268
- }
269
-
270
- if (validation.stateWarning) {
271
- console.log(chalk.yellow(`\n⚠️ ${validation.stateWarning}`));
272
- }
273
-
274
- // === GATE 4: Content Validation ===
275
- // Validate spec.md and contracts.cs when advancing from design phase
276
- if (currentPhase === 'design' && nextPhase === 'plan') {
277
- // Check spec.md structure and content (filesystem-based)
278
- const specAbsPath = getAbsoluteOutputPath(process.cwd(), feature, 'spec');
279
- if (existsSync(specAbsPath)) {
280
- const specContentValidation = validateSpecContent(specAbsPath);
281
-
282
- if (!specContentValidation.valid) {
283
- console.log(chalk.red('\n✗ Spec content validation failed:'));
284
- specContentValidation.errors.forEach(error => {
285
- console.log(chalk.red(` - ${error}`));
286
- });
287
- console.log('');
288
- process.exit(1);
289
- }
290
-
291
- if (specContentValidation.warnings?.length > 0 && !options.skipWarnings) {
292
- console.log(chalk.yellow('\n⚠️ Spec content warnings:'));
293
- specContentValidation.warnings.forEach(warning => {
294
- console.log(chalk.yellow(` - ${warning}`));
295
- });
296
- }
297
- }
298
-
299
- // Run existing spec validator (anti-patterns, IaC checks)
300
- const specValidation = await validateSpec('.', feature);
301
-
302
- if (specValidation.errors > 0) {
303
- console.log(chalk.red('\n✗ Spec validation failed — fix errors before advancing:'));
304
- specValidation.issues.filter(i => i.level === 'error').forEach(issue => {
305
- console.log(chalk.red(` - ${issue.message}`));
306
- console.log(chalk.yellow(` → ${issue.solution}`));
307
- });
308
- console.log('');
309
- process.exit(1);
310
- }
311
-
312
- if (specValidation.warnings > 0 && !options.skipWarnings) {
313
- console.log(chalk.yellow('\n⚠️ Spec validation warnings:'));
314
- specValidation.issues.filter(i => i.level === 'warning').forEach(issue => {
315
- console.log(chalk.yellow(` - ${issue.message}`));
316
- });
317
- console.log(chalk.gray('\n (Use --skip-warnings to ignore warnings)\n'));
318
- }
319
- }
320
-
321
- // === GATE 5: Tasks Content Validation ===
322
- // Validate tasks.md structure when advancing to implement
323
- if (currentPhase === 'tasks' && nextPhase === 'implement') {
324
- const tasksAbsPath = getAbsoluteOutputPath(process.cwd(), feature, 'tasks');
325
- if (existsSync(tasksAbsPath)) {
326
- const tasksContentValidation = validateTasksContent(tasksAbsPath);
327
-
328
- if (!tasksContentValidation.valid) {
329
- console.log(chalk.red('\n✗ Tasks content validation failed:'));
330
- tasksContentValidation.errors.forEach(error => {
331
- console.log(chalk.red(` - ${error}`));
332
- });
333
- console.log('');
334
- process.exit(1);
335
- }
336
-
337
- if (tasksContentValidation.warnings?.length > 0 && !options.skipWarnings) {
338
- console.log(chalk.yellow('\n⚠️ Tasks content warnings:'));
339
- tasksContentValidation.warnings.forEach(warning => {
340
- console.log(chalk.yellow(` - ${warning}`));
341
- });
342
- }
343
-
344
- // Show tasks stats
345
- console.log(chalk.green(`\n✓ Tasks validated: ${tasksContentValidation.stats.totalTasks} total (${tasksContentValidation.stats.regularTasks} tasks + ${tasksContentValidation.stats.checkpoints} checkpoints)`));
346
- }
347
- }
348
-
349
- // Gate: Check design system when advancing to implement with UI agents
350
- if (nextPhase === 'implement') {
351
- const gateResult = designSystemGate(feature, process.cwd());
352
-
353
- if (gateResult.blocked) {
354
- console.log(chalk.red(`\n✗ ${gateResult.message}`));
355
- console.log('');
356
- gateResult.solution.forEach(line => {
357
- if (line === '') {
358
- console.log('');
359
- } else {
360
- console.log(chalk.yellow(` ${line}`));
361
- }
362
- });
363
- console.log('');
364
- process.exit(1);
365
- }
366
- }
367
-
368
- // Advance the phase
369
- const state = loadState();
370
- state.features[feature].phase = nextPhase;
371
- state.features[feature].updatedAt = new Date().toISOString();
372
-
373
- // Create the target phase directory so derivePhase() reflects the new phase
374
- const PHASE_DIR_MAP = {
375
- 'proposal': '0-proposal', 'design': '2-design', 'uiux': '1-ui',
376
- 'plan': '3-plan', 'tasks': '4-tasks', 'implement': '5-implement',
377
- };
378
- const targetDir = PHASE_DIR_MAP[nextPhase];
379
- if (targetDir) {
380
- const dirPath = join(featureFolderPath, targetDir);
381
- if (!existsSync(dirPath)) {
382
- mkdirSync(dirPath, { recursive: true });
383
- }
384
- }
385
- if (skippedPhases.length > 0) {
386
- const existingSkipped = state.features[feature].skippedPhases || [];
387
- state.features[feature].skippedPhases = [...new Set([...existingSkipped, ...skippedPhases])];
388
- }
389
-
390
- // Ensure approvalGates object exists
391
- if (!state.features[feature].approvalGates) {
392
- state.features[feature].approvalGates = {};
393
- }
394
-
395
- const now = new Date().toISOString();
396
- const gates = state.features[feature].approvalGates;
397
-
398
- // Auto-approve proposal gate when advancing past the proposal phase.
399
- // The act of advancing is itself the approval — no explicit gate check needed.
400
- if (currentPhase === 'proposal' && !gates.proposal?.approved) {
401
- gates.proposal = { approved: true, timestamp: now, approvedBy: 'workflow-advance' };
402
- }
403
-
404
- // Persist gates approved implicitly (trust auto-approve or --skip-approval bypass).
405
- if (gateApprovedImplicitly && !gates[gateApprovedImplicitly.gate]?.approved) {
406
- gates[gateApprovedImplicitly.gate] = {
407
- approved: true,
408
- timestamp: now,
409
- approvedBy: gateApprovedImplicitly.approvedBy
410
- };
411
- }
412
-
413
- // Auto-approve gates for phases that were legitimately skipped.
414
- // Skipped phases never run their explicit approve step, so their gate would
415
- // otherwise stay false in state — making approval-status misleading.
416
- for (const skippedPhase of skippedPhases) {
417
- const skippedGate = APPROVAL_GATE_MAP[skippedPhase];
418
- if (skippedGate && !gates[skippedGate]?.approved) {
419
- gates[skippedGate] = { approved: true, timestamp: now, approvedBy: 'phase-skipped' };
420
- }
421
- }
422
-
423
- saveState(state);
424
-
425
- // Run PhaseAdvanced agent-teams hook (non-blocking)
426
- try {
427
- const { executeHook, formatHookResults } = await import('../../lib/hooks/hook-executor.js');
428
- const hookResult = await executeHook(process.cwd(), feature, 'PhaseAdvanced', {
429
- fromPhase: currentPhase,
430
- toPhase: nextPhase
431
- });
432
- if (!hookResult.passed && hookResult.errors.length > 0) {
433
- const output = formatHookResults(hookResult, 'PhaseAdvanced');
434
- console.log(output);
435
- }
436
- } catch {
437
- // Hook executor unavailable — non-blocking
438
- }
439
-
440
- // Agent detection refresh (fail-open)
441
- try {
442
- const { refreshAgents, displayDetectionDiff } = await import('../agents/detect-agents.js');
443
- const refreshResult = await refreshAgents(process.cwd(), feature);
444
- if (refreshResult.added.length > 0) {
445
- displayDetectionDiff(refreshResult, currentPhase, nextPhase);
446
- }
447
- } catch {
448
- // Refresh is diagnostic — fail-open, phase advance already completed
449
- }
450
-
451
- // Emit Tier-4 validator dispatch when advancing into implement or sync phase
452
- const phasesNeedingValidation = ['implement', 'sync'];
453
- if (phasesNeedingValidation.includes(nextPhase) || phasesNeedingValidation.includes(currentPhase)) {
454
- const validationPhase = phasesNeedingValidation.includes(nextPhase) ? nextPhase : currentPhase;
455
- await emitValidatorDispatch(feature, validationPhase, process.cwd());
456
- }
457
-
458
- // Emit SKILL DISPATCH for any phase that has requiredSkills
459
- await emitSkillDispatch(nextPhase, process.cwd());
460
-
461
- console.log(chalk.green(`\n✓ Advanced to ${nextPhaseDef.name}`));
462
-
463
- // Show what's needed in the new phase
464
- console.log(chalk.cyan('\n📋 Phase requirements:'));
465
- console.log(chalk.gray(` ${nextPhaseDef.description}`));
466
-
467
- if (nextPhaseDef.requiredOutputs?.length > 0) {
468
- console.log(chalk.cyan('\n Required outputs:'));
469
- nextPhaseDef.requiredOutputs.forEach(output => {
470
- const exists = validation.phase ? true : false; // Already validated
471
- console.log(chalk.gray(` ✓ ${output} (exists)`));
472
- });
473
- }
474
-
475
- // Show phase-specific guidance
476
- const guidance = getPhaseGuidance(nextPhase, feature);
477
- if (guidance) {
478
- console.log(chalk.cyan('\n Next steps:'));
479
- guidance.forEach(step => console.log(chalk.white(` → ${step}`)));
480
- }
481
-
482
- console.log('');
483
- }
484
-
485
- /**
486
- * Get guidance for what to do in a phase
487
- */
488
- function getPhaseGuidance(phase, feature) {
489
- const guides = {
490
- 'uiux': [
491
- `Resume spec pipeline: /morph-proposal ${feature}`,
492
- 'Provide layout references and preferences',
493
- 'Review wireframes before proceeding'
494
- ],
495
- 'design': (() => {
496
- const { isDotnet } = getStackProfile();
497
- return [
498
- `Resume spec pipeline: /morph-proposal ${feature}`,
499
- 'Review DECISION POINTS carefully',
500
- isDotnet ? 'Approve spec.md and contracts.cs' : 'Approve spec.md and contracts.ts (TypeScript + Zod schemas)'
501
- ];
502
- })(),
503
- 'plan': [
504
- `Resume spec pipeline: /morph-proposal ${feature}`,
505
- 'Review implementation plan for completeness',
506
- 'Approve plan before generating task breakdown'
507
- ],
508
- 'tasks': [
509
- `Resume spec pipeline: /morph-proposal ${feature}`,
510
- 'Review task order and dependencies',
511
- 'Approve task list before implementing'
512
- ],
513
- 'implement': [
514
- `Start implementing: /morph-apply ${feature}`,
515
- 'Complete tasks one by one with: morph-spec task done',
516
- 'Validators run automatically on task completion'
517
- ],
518
- 'sync': [
519
- 'Review decisions.md for standards to promote',
520
- `Decisions file: ${getOutputPath(feature, 'decisions')}`
521
- ]
522
- };
523
-
524
- return guides[phase] || null;
525
- }
526
-
527
- /**
528
- * Gate: Check design system exists when advancing to implement with UI agents
529
- * @param {string} feature - Feature name
530
- * @param {string} projectPath - Project root path
531
- * @returns {Object} { blocked: boolean, message?: string, solution?: string[] }
532
- */
533
- function designSystemGate(feature, projectPath = '.') {
534
- // Check if UI agents are active
535
- const hasUIAgents = hasUIAgentsActive(projectPath, feature);
536
-
537
- if (!hasUIAgents) {
538
- return { blocked: false }; // No UI agents, gate doesn't apply
539
- }
540
-
541
- // Check if design system exists
542
- const dsDetection = detectDesignSystem(projectPath, feature);
543
-
544
- if (dsDetection.hasDesignSystem) {
545
- return { blocked: false }; // Design system exists, gate passed
546
- }
547
-
548
- // Block advancement - no design system found with UI agents active
549
- return {
550
- blocked: true,
551
- message: 'Cannot advance to implementation — UI agents are active but no design system found',
552
- solution: [
553
- 'Design system is required when UI agents (ui-designer, css-specialist) are active',
554
- '',
555
- 'Create a design system with one of these options:',
556
- ' 1. Project-level: .morph/context/design-system.md (shared across features)',
557
- ` 2. Feature-level: ${getOutputPath(feature, 'uiDesignSystem')} (feature-specific)`,
558
- ' 3. Run /morph-phase-uiux skill to auto-generate from existing codebase',
559
- '',
560
- 'Or remove UI agents if they are not needed:',
561
- ` morph-spec state remove-agent ${feature} css-specialist`
562
- ]
563
- };
564
- }
565
-
566
- /**
567
- * Emit a SKILL DISPATCH block when advancing to a phase that has requiredSkills.
568
- * Outputs structured JSON for the LLM to read and follow.
569
- * Non-blocking — fails silently.
570
- *
571
- * @param {string} nextPhase - The phase being advanced into
572
- * @param {string} cwd - Project root path (for locating phases.json)
573
- */
574
- async function emitSkillDispatch(nextPhase, cwd) {
575
- try {
576
- // Try project-local phases.json first, fall back to package-level
577
- const localPath = join(cwd, 'framework', 'phases.json');
578
- const packagePath = join(__dirname, '../../../framework/phases.json');
579
- const resolvedPath = existsSync(localPath) ? localPath : packagePath;
580
-
581
- if (!existsSync(resolvedPath)) return;
582
-
583
- const phasesData = JSON.parse(readFileSync(resolvedPath, 'utf8'));
584
- const skills = phasesData?.phases?.[nextPhase]?.requiredSkills;
585
- if (!skills || skills.length === 0) return;
586
-
587
- const dispatch = {
588
- skillDispatch: true,
589
- phase: nextPhase,
590
- skills,
591
- instruction: `These skills are MANDATORY for phase '${nextPhase}'. Invoke them at the specified trigger points using Skill(). Do NOT skip them.`,
592
- };
593
-
594
- console.log(chalk.cyan('\n--- SKILL DISPATCH ---'));
595
- console.log(JSON.stringify(dispatch, null, 2));
596
- console.log(chalk.cyan('--- END SKILL DISPATCH ---\n'));
597
- } catch {
598
- // Non-blocking — fail silently
599
- }
600
- }