@polymorphism-tech/morph-spec 4.2.0 → 4.3.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 (132) hide show
  1. package/bin/morph-spec.js +283 -8
  2. package/bin/validate.js +4 -4
  3. package/docs/{v3.0 → next-generation}/AGENTS.md +1 -1
  4. package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
  5. package/docs/next-generation/EXECUTION-FLOW.md +274 -0
  6. package/docs/next-generation/META-PROMPTS.md +235 -0
  7. package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
  8. package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
  9. package/package.json +5 -5
  10. package/src/commands/agents/agents-fuse.js +96 -0
  11. package/src/commands/agents/micro-agent.js +112 -0
  12. package/src/commands/agents/spawn-team.js +69 -4
  13. package/src/commands/agents/squad-template.js +146 -0
  14. package/src/commands/analytics/analytics.js +176 -0
  15. package/src/commands/context/context-prime.js +63 -0
  16. package/src/commands/context/core-four.js +54 -0
  17. package/src/commands/mcp/mcp.js +102 -0
  18. package/src/commands/project/detect-agents.js +1 -1
  19. package/src/commands/project/doctor.js +573 -356
  20. package/src/commands/project/init.js +1 -1
  21. package/src/commands/project/update.js +1 -1
  22. package/src/commands/state/advance-phase.js +433 -416
  23. package/src/commands/templates/template-render.js +80 -1
  24. package/src/commands/threads/thread-template.js +103 -0
  25. package/src/commands/threads/threads.js +261 -0
  26. package/src/commands/trust/trust.js +205 -0
  27. package/src/{orchestrator.js → core/orchestrator.js} +8 -8
  28. package/src/core/state/state-manager.js +18 -2
  29. package/src/core/workflows/workflow-detector.js +100 -2
  30. package/src/lib/agents/micro-agent-factory.js +161 -0
  31. package/src/lib/analytics/analytics-engine.js +345 -0
  32. package/src/lib/checkpoints/checkpoint-hooks.js +293 -258
  33. package/src/lib/context/context-bundler.js +240 -0
  34. package/src/lib/context/context-optimizer.js +212 -0
  35. package/src/lib/context/context-tracker.js +273 -0
  36. package/src/lib/context/core-four-tracker.js +201 -0
  37. package/src/lib/context/mcp-optimizer.js +200 -0
  38. package/src/lib/execution/fusion-executor.js +304 -0
  39. package/src/lib/execution/parallel-executor.js +270 -0
  40. package/src/lib/generators/context-generator.js +3 -3
  41. package/src/lib/generators/recap-generator.js +2 -2
  42. package/src/lib/hooks/hook-executor.js +169 -0
  43. package/src/lib/hooks/stop-hook-executor.js +286 -0
  44. package/src/lib/hops/hop-composer.js +221 -0
  45. package/src/lib/threads/thread-coordinator.js +238 -0
  46. package/src/lib/threads/thread-manager.js +317 -0
  47. package/src/lib/tracking/artifact-trail.js +202 -0
  48. package/src/lib/trust/trust-manager.js +269 -0
  49. package/src/lib/validators/design-system/design-system-validator.js +2 -2
  50. package/src/lib/validators/validation-runner.js +6 -6
  51. package/stacks/blazor-azure/.morph/config/agents.json +72 -3
  52. package/stacks/nextjs-supabase/.morph/config/agents.json +3 -3
  53. package/CLAUDE.md +0 -993
  54. package/docs/llm-interaction-config.md +0 -735
  55. package/docs/v3.0/EXECUTION-FLOW.md +0 -1304
  56. package/src/commands/utils/migrate-state.js +0 -158
  57. package/src/commands/utils/upgrade.js +0 -346
  58. package/src/lib/validators/architecture-validator.js +0 -60
  59. package/src/lib/validators/content-validator.js +0 -164
  60. package/src/lib/validators/package-validator.js +0 -61
  61. package/src/lib/validators/ui-contrast-validator.js +0 -44
  62. package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
  63. package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
  64. package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
  65. package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
  66. package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
  67. package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
  68. package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
  69. package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
  70. package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
  71. package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
  72. package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
  73. package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
  74. package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
  75. package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
  76. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
  77. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
  78. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
  79. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
  80. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
  81. package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
  82. package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
  83. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
  84. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
  85. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
  86. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
  87. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +0 -59
  88. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -77
  89. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -58
  90. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
  91. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -45
  92. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
  93. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
  94. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
  95. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
  96. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
  97. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
  98. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
  99. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
  100. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
  101. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
  102. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
  103. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +0 -64
  104. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
  105. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
  106. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
  107. package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
  108. package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
  109. package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
  110. package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
  111. package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
  112. package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
  113. package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
  114. package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
  115. package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +0 -221
  116. package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +0 -79
  117. package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +0 -529
  118. package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +0 -209
  119. package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +0 -227
  120. package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +0 -122
  121. package/stacks/nextjs-supabase/.claude/commands/morph-status.md +0 -86
  122. package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +0 -122
  123. package/stacks/nextjs-supabase/.claude/settings.local.json +0 -6
  124. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
  125. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
  126. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
  127. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -50
  128. /package/docs/{v3.0 → next-generation}/ANALYSIS.md +0 -0
  129. /package/docs/{v3.0 → next-generation}/ARCHITECTURE.md +0 -0
  130. /package/docs/{v3.0 → next-generation}/FEATURES.md +0 -0
  131. /package/docs/{v3.0 → next-generation}/README.md +0 -0
  132. /package/docs/{v3.0 → next-generation}/ROADMAP.md +0 -0
@@ -1,416 +1,433 @@
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 } from '../../core/state/state-manager.js';
14
- import { PHASES, validatePhase } from './validate-phase.js';
15
- import { detectDesignSystem, hasUIAgentsActive } from '../../lib/detectors/design-system-detector.js';
16
- import { validateSpec } from '../../lib/validators/spec-validator.js';
17
- import { validateTransition, getPhaseDisplayName } from '../../core/state/phase-state-machine.js';
18
- import { validateSpecContent, validateTasksContent, validateFeatureOutputs } from '../../lib/validators/content-validator.js';
19
- import { getWorkflowConfig } from '../../core/workflows/workflow-detector.js';
20
- import { readFileSync, existsSync } from 'fs';
21
- import { join, dirname } from 'path';
22
- import { fileURLToPath } from 'url';
23
-
24
- const __dirname = dirname(fileURLToPath(import.meta.url));
25
-
26
- // Phase order for advancing (skips optional phases unless active)
27
- const PHASE_ORDER = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'tasks', 'implement', 'sync'];
28
-
29
- /**
30
- * Get the next phase after the current one
31
- */
32
- function getNextPhase(currentPhase, feature, skipOptional) {
33
- // Load workflow config if workflow is set
34
- let workflowConfig = null;
35
- if (feature.workflow && feature.workflow !== 'auto') {
36
- workflowConfig = getWorkflowConfig(feature.workflow);
37
- }
38
-
39
- const currentIndex = PHASE_ORDER.indexOf(currentPhase);
40
- if (currentIndex === -1 || currentIndex >= PHASE_ORDER.length - 1) {
41
- return null;
42
- }
43
-
44
- for (let i = currentIndex + 1; i < PHASE_ORDER.length; i++) {
45
- const candidate = PHASE_ORDER[i];
46
- const phaseDef = PHASES[candidate];
47
-
48
- // === Workflow-Based Phase Skipping ===
49
- if (workflowConfig && workflowConfig.phases) {
50
- // Check if workflow explicitly skips this phase
51
- if (workflowConfig.phases.skip && workflowConfig.phases.skip.includes(candidate)) {
52
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: workflow ${feature.workflow} skips this phase`));
53
- continue;
54
- }
55
-
56
- // Check if this phase is part of a combined phase
57
- if (workflowConfig.phases.combined) {
58
- for (const [combinedName, phases] of Object.entries(workflowConfig.phases.combined)) {
59
- if (phases.includes(candidate) && candidate !== combinedName) {
60
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: combined into ${combinedName} phase`));
61
- continue;
62
- }
63
- }
64
- }
65
-
66
- // Check if workflow only runs specific phases
67
- if (workflowConfig.phases.run && !workflowConfig.phases.run.includes(candidate)) {
68
- // Also check if it's not a combined phase
69
- const isCombinedPhase = workflowConfig.phases.combined &&
70
- Object.keys(workflowConfig.phases.combined).includes(candidate);
71
- if (!isCombinedPhase) {
72
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: not in workflow run list`));
73
- continue;
74
- }
75
- }
76
-
77
- // Check conditional skips
78
- if (workflowConfig.phases.skipIfCondition) {
79
- const condition = workflowConfig.phases.skipIfCondition[candidate];
80
- if (condition) {
81
- // Evaluate condition (simple conditions for now)
82
- if (condition === '!hasUIAgents' && !hasUIAgentsActive(feature)) {
83
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
84
- continue;
85
- }
86
- }
87
- }
88
- }
89
-
90
- // === Legacy Phase Skipping (fallback when no workflow config) ===
91
- if (!workflowConfig) {
92
- // Skip optional phases if flag set or no relevant agents/outputs
93
- if (phaseDef?.optional && skipOptional) {
94
- continue;
95
- }
96
-
97
- // Skip uiux if no UI agents are active
98
- if (candidate === 'uiux') {
99
- const uiAgents = ['blazor-builder', 'uiux-designer', 'nextjs-expert', 'ui-ux-designer'];
100
- const hasUiAgent = feature.activeAgents?.some(a => uiAgents.includes(a));
101
- if (!hasUiAgent && skipOptional !== false) {
102
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
103
- continue;
104
- }
105
- }
106
-
107
- // Skip sync for simple workflows
108
- if (candidate === 'sync') {
109
- const isSimple = feature.workflow === 'fast-track';
110
- if (isSimple && skipOptional !== false) {
111
- console.log(chalk.gray(` ⏩ Skipping ${candidate}: fast-track workflow`));
112
- continue;
113
- }
114
- }
115
- }
116
-
117
- return candidate;
118
- }
119
-
120
- return null;
121
- }
122
-
123
- /**
124
- * Main command handler
125
- */
126
- export async function advancePhaseCommand(feature, options = {}) {
127
- console.log(chalk.cyan('\n╔════════════════════════════════════════════════╗'));
128
- console.log(chalk.cyan('║ MORPH-SPEC PHASE ADVANCE ║'));
129
- console.log(chalk.cyan('╚════════════════════════════════════════════════╝\n'));
130
-
131
- // Get current feature state
132
- const featureData = getFeature(feature);
133
- if (!featureData) {
134
- console.log(chalk.red(`✗ Feature not found: ${feature}`));
135
- console.log(chalk.yellow(` Run: morph-spec state set ${feature} phase proposal`));
136
- process.exit(1);
137
- }
138
-
139
- const currentPhase = featureData.phase;
140
- const currentPhaseDef = PHASES[currentPhase];
141
-
142
- console.log(chalk.gray('Feature:'), feature);
143
- console.log(chalk.gray('Current Phase:'), currentPhaseDef?.name || currentPhase);
144
- console.log(chalk.gray('Workflow:'), featureData.workflow || 'auto');
145
-
146
- // Determine next phase
147
- const nextPhase = getNextPhase(currentPhase, featureData, options.skipOptional);
148
-
149
- if (!nextPhase) {
150
- console.log(chalk.green('\n✓ Feature is at the final phase!'));
151
- if (currentPhase === 'implement' || currentPhase === 'sync') {
152
- console.log(chalk.green(' Consider running: morph-spec generate recap ' + feature));
153
- }
154
- return;
155
- }
156
-
157
- const nextPhaseDef = PHASES[nextPhase];
158
- console.log(chalk.gray('Next Phase:'), nextPhaseDef.name);
159
-
160
- // === GATE 1: State Machine Validation ===
161
- // Ensure phase transition is valid (no skipping required phases)
162
- if (!options.force) {
163
- try {
164
- validateTransition(currentPhase, nextPhase);
165
- } catch (error) {
166
- console.log(chalk.red('\n✗ Invalid phase transition'));
167
- console.log(chalk.yellow(error.message));
168
- console.log(chalk.gray('\nUse --force to override (not recommended)\n'));
169
- process.exit(1);
170
- }
171
- }
172
-
173
- // === GATE 2: Approval Gate Check ===
174
- // Check if current phase requires approval before advancing
175
- const approvalGateMap = {
176
- 'design': 'design',
177
- 'tasks': 'tasks',
178
- 'uiux': 'uiux'
179
- };
180
-
181
- const requiredGate = approvalGateMap[currentPhase];
182
- if (requiredGate && !options.skipApproval) {
183
- const gateStatus = getApprovalGate(feature, requiredGate);
184
-
185
- if (!gateStatus || !gateStatus.approved) {
186
- console.log(chalk.red(`\n✗ Phase "${currentPhase}" requires approval before advancing`));
187
- console.log(chalk.yellow(`\nRun: morph-spec approve ${feature} ${requiredGate}`));
188
- console.log(chalk.gray('Or use --skip-approval to bypass (not recommended)\n'));
189
- process.exit(1);
190
- }
191
-
192
- console.log(chalk.green(`✓ Approval gate "${requiredGate}" passed`));
193
- }
194
-
195
- // === GATE 3: Output Requirements ===
196
- // Validate that current phase requirements are met before advancing
197
- const validation = validatePhase(feature, nextPhase);
198
-
199
- if (!validation.valid) {
200
- console.log(chalk.red('\n✗ Cannot advance — missing requirements:'));
201
- validation.missingOutputs.forEach(output => {
202
- console.log(chalk.red(` - ${output}`));
203
- });
204
- console.log(chalk.yellow(`\n Complete these before advancing to ${nextPhaseDef.name}`));
205
- process.exit(1);
206
- }
207
-
208
- if (validation.stateWarning) {
209
- console.log(chalk.yellow(`\n⚠️ ${validation.stateWarning}`));
210
- }
211
-
212
- // === GATE 4: Content Validation ===
213
- // Validate spec.md and contracts.cs when advancing from design phase
214
- if (currentPhase === 'design' && (nextPhase === 'clarify' || nextPhase === 'tasks')) {
215
- // Check spec.md structure and content
216
- if (featureData.outputs?.spec?.created) {
217
- const specContentValidation = validateSpecContent(featureData.outputs.spec.path);
218
-
219
- if (!specContentValidation.valid) {
220
- console.log(chalk.red('\n✗ Spec content validation failed:'));
221
- specContentValidation.missing.forEach(section => {
222
- console.log(chalk.red(` - Missing section: ${section}`));
223
- });
224
- specContentValidation.errors.forEach(error => {
225
- console.log(chalk.red(` - ${error}`));
226
- });
227
- console.log('');
228
- process.exit(1);
229
- }
230
-
231
- if (specContentValidation.warnings?.length > 0 && !options.skipWarnings) {
232
- console.log(chalk.yellow('\n⚠️ Spec content warnings:'));
233
- specContentValidation.warnings.forEach(warning => {
234
- console.log(chalk.yellow(` - ${warning}`));
235
- });
236
- }
237
- }
238
-
239
- // Run existing spec validator (anti-patterns, IaC checks)
240
- const specValidation = await validateSpec('.', feature);
241
-
242
- if (specValidation.errors > 0) {
243
- console.log(chalk.red('\n✗ Spec validation failed — fix errors before advancing:'));
244
- specValidation.issues.filter(i => i.level === 'error').forEach(issue => {
245
- console.log(chalk.red(` - ${issue.message}`));
246
- console.log(chalk.yellow(` → ${issue.solution}`));
247
- });
248
- console.log('');
249
- process.exit(1);
250
- }
251
-
252
- if (specValidation.warnings > 0 && !options.skipWarnings) {
253
- console.log(chalk.yellow('\n⚠️ Spec validation warnings:'));
254
- specValidation.issues.filter(i => i.level === 'warning').forEach(issue => {
255
- console.log(chalk.yellow(` - ${issue.message}`));
256
- });
257
- console.log(chalk.gray('\n (Use --skip-warnings to ignore warnings)\n'));
258
- }
259
- }
260
-
261
- // === GATE 5: Tasks Content Validation ===
262
- // Validate tasks.json structure when advancing to implement
263
- if (currentPhase === 'tasks' && nextPhase === 'implement') {
264
- if (featureData.outputs?.tasks?.created) {
265
- const tasksContentValidation = validateTasksContent(featureData.outputs.tasks.path);
266
-
267
- if (!tasksContentValidation.valid) {
268
- console.log(chalk.red('\n✗ Tasks content validation failed:'));
269
- tasksContentValidation.errors.forEach(error => {
270
- console.log(chalk.red(` - ${error}`));
271
- });
272
- console.log('');
273
- process.exit(1);
274
- }
275
-
276
- if (tasksContentValidation.warnings?.length > 0 && !options.skipWarnings) {
277
- console.log(chalk.yellow('\n⚠️ Tasks content warnings:'));
278
- tasksContentValidation.warnings.forEach(warning => {
279
- console.log(chalk.yellow(` - ${warning}`));
280
- });
281
- }
282
-
283
- // Show tasks stats
284
- console.log(chalk.green(`\n✓ Tasks validated: ${tasksContentValidation.stats.totalTasks} total (${tasksContentValidation.stats.regularTasks} tasks + ${tasksContentValidation.stats.checkpoints} checkpoints)`));
285
- }
286
- }
287
-
288
- // Gate: Check design system when advancing to implement with UI agents
289
- if (nextPhase === 'implement') {
290
- const gateResult = designSystemGate(feature);
291
-
292
- if (gateResult.blocked) {
293
- console.log(chalk.red(`\n✗ ${gateResult.message}`));
294
- console.log('');
295
- gateResult.solution.forEach(line => {
296
- if (line === '') {
297
- console.log('');
298
- } else {
299
- console.log(chalk.yellow(` ${line}`));
300
- }
301
- });
302
- console.log('');
303
- process.exit(1);
304
- }
305
- }
306
-
307
- // Advance the phase
308
- const state = loadState();
309
- state.features[feature].phase = nextPhase;
310
- state.features[feature].updatedAt = new Date().toISOString();
311
- saveState(state);
312
-
313
- console.log(chalk.green(`\n✓ Advanced to ${nextPhaseDef.name}`));
314
-
315
- // Show what's needed in the new phase
316
- console.log(chalk.cyan('\n📋 Phase requirements:'));
317
- console.log(chalk.gray(` ${nextPhaseDef.description}`));
318
-
319
- if (nextPhaseDef.requiredOutputs?.length > 0) {
320
- console.log(chalk.cyan('\n Required outputs:'));
321
- nextPhaseDef.requiredOutputs.forEach(output => {
322
- const exists = validation.phase ? true : false; // Already validated
323
- console.log(chalk.gray(` ✓ ${output} (exists)`));
324
- });
325
- }
326
-
327
- // Show phase-specific guidance
328
- const guidance = getPhaseGuidance(nextPhase, feature);
329
- if (guidance) {
330
- console.log(chalk.cyan('\n Next steps:'));
331
- guidance.forEach(step => console.log(chalk.white(` → ${step}`)));
332
- }
333
-
334
- console.log('');
335
- }
336
-
337
- /**
338
- * Get guidance for what to do in a phase
339
- */
340
- function getPhaseGuidance(phase, feature) {
341
- const guides = {
342
- 'setup': [
343
- `Resume spec pipeline: /morph-proposal ${feature}`,
344
- 'Auto-continues from setup phase'
345
- ],
346
- 'uiux': [
347
- `Resume spec pipeline: /morph-proposal ${feature}`,
348
- 'Provide layout references and preferences',
349
- 'Review wireframes before proceeding'
350
- ],
351
- 'design': [
352
- `Resume spec pipeline: /morph-proposal ${feature}`,
353
- 'Review DECISION POINTS carefully',
354
- 'Approve spec.md and contracts.cs'
355
- ],
356
- 'clarify': [
357
- `Resume spec pipeline: /morph-proposal ${feature}`,
358
- 'Resolve edge cases and unknowns'
359
- ],
360
- 'tasks': [
361
- `Resume spec pipeline: /morph-proposal ${feature}`,
362
- 'Review task order and dependencies',
363
- 'Approve task list before implementing'
364
- ],
365
- 'implement': [
366
- `Start implementing: /morph-apply ${feature}`,
367
- 'Complete tasks one by one with: morph-spec task done',
368
- 'Validators run automatically on task completion'
369
- ],
370
- 'sync': [
371
- 'Review decisions.md for standards to promote',
372
- `Sync: morph-spec sync --path .morph/project/outputs/${feature}/decisions.md`
373
- ]
374
- };
375
-
376
- return guides[phase] || null;
377
- }
378
-
379
- /**
380
- * Gate: Check design system exists when advancing to implement with UI agents
381
- * @param {string} feature - Feature name
382
- * @param {string} projectPath - Project root path
383
- * @returns {Object} { blocked: boolean, message?: string, solution?: string[] }
384
- */
385
- function designSystemGate(feature, projectPath = '.') {
386
- // Check if UI agents are active
387
- const hasUIAgents = hasUIAgentsActive(projectPath, feature);
388
-
389
- if (!hasUIAgents) {
390
- return { blocked: false }; // No UI agents, gate doesn't apply
391
- }
392
-
393
- // Check if design system exists
394
- const dsDetection = detectDesignSystem(projectPath, feature);
395
-
396
- if (dsDetection.hasDesignSystem) {
397
- return { blocked: false }; // Design system exists, gate passed
398
- }
399
-
400
- // Block advancement - no design system found with UI agents active
401
- return {
402
- blocked: true,
403
- message: 'Cannot advance to implementation — UI agents are active but no design system found',
404
- solution: [
405
- 'Design system is required when UI agents (blazor-builder, ui-designer, css-specialist) are active',
406
- '',
407
- 'Create a design system with one of these options:',
408
- ' 1. Project-level: .morph/project/design-system.md (shared across features)',
409
- ` 2. Feature-level: .morph/project/outputs/${feature}/ui-design-system.md (feature-specific)`,
410
- ' 3. Auto-generate: morph-spec generate design-system (scans existing CSS)',
411
- '',
412
- 'Or remove UI agents if they are not needed:',
413
- ` morph-spec state remove-agent ${feature} blazor-builder`
414
- ]
415
- };
416
- }
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 } from '../../core/state/state-manager.js';
14
+ import { PHASES, validatePhase } from './validate-phase.js';
15
+ import { detectDesignSystem, hasUIAgentsActive } from '../../lib/detectors/design-system-detector.js';
16
+ import { shouldAutoApprove, autoCalculateTrust } from '../../lib/trust/trust-manager.js';
17
+ import { validateSpec } from '../../lib/validators/spec-validator.js';
18
+ import { validateTransition, getPhaseDisplayName } from '../../core/state/phase-state-machine.js';
19
+ import { validateSpecContent, validateTasksContent, validateFeatureOutputs } from '../../lib/validators/content/content-validator.js';
20
+ import { getWorkflowConfig } from '../../core/workflows/workflow-detector.js';
21
+ import { readFileSync, existsSync } from 'fs';
22
+ import { join, dirname } from 'path';
23
+ import { fileURLToPath } from 'url';
24
+
25
+ const __dirname = dirname(fileURLToPath(import.meta.url));
26
+
27
+ // Phase order for advancing (skips optional phases unless active)
28
+ const PHASE_ORDER = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'tasks', 'implement', 'sync'];
29
+
30
+ /**
31
+ * Get the next phase after the current one
32
+ */
33
+ function getNextPhase(currentPhase, feature, skipOptional) {
34
+ // Load workflow config if workflow is set
35
+ let workflowConfig = null;
36
+ if (feature.workflow && feature.workflow !== 'auto') {
37
+ workflowConfig = getWorkflowConfig(feature.workflow);
38
+ }
39
+
40
+ const currentIndex = PHASE_ORDER.indexOf(currentPhase);
41
+ if (currentIndex === -1 || currentIndex >= PHASE_ORDER.length - 1) {
42
+ return null;
43
+ }
44
+
45
+ for (let i = currentIndex + 1; i < PHASE_ORDER.length; i++) {
46
+ const candidate = PHASE_ORDER[i];
47
+ const phaseDef = PHASES[candidate];
48
+
49
+ // === Workflow-Based Phase Skipping ===
50
+ if (workflowConfig && workflowConfig.phases) {
51
+ // Check if workflow explicitly skips this phase
52
+ if (workflowConfig.phases.skip && workflowConfig.phases.skip.includes(candidate)) {
53
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: workflow ${feature.workflow} skips this phase`));
54
+ continue;
55
+ }
56
+
57
+ // Check if this phase is part of a combined phase
58
+ if (workflowConfig.phases.combined) {
59
+ for (const [combinedName, phases] of Object.entries(workflowConfig.phases.combined)) {
60
+ if (phases.includes(candidate) && candidate !== combinedName) {
61
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: combined into ${combinedName} phase`));
62
+ continue;
63
+ }
64
+ }
65
+ }
66
+
67
+ // Check if workflow only runs specific phases
68
+ if (workflowConfig.phases.run && !workflowConfig.phases.run.includes(candidate)) {
69
+ // Also check if it's not a combined phase
70
+ const isCombinedPhase = workflowConfig.phases.combined &&
71
+ Object.keys(workflowConfig.phases.combined).includes(candidate);
72
+ if (!isCombinedPhase) {
73
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: not in workflow run list`));
74
+ continue;
75
+ }
76
+ }
77
+
78
+ // Check conditional skips
79
+ if (workflowConfig.phases.skipIfCondition) {
80
+ const condition = workflowConfig.phases.skipIfCondition[candidate];
81
+ if (condition) {
82
+ // Evaluate condition (simple conditions for now)
83
+ if (condition === '!hasUIAgents' && !hasUIAgentsActive(feature)) {
84
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
85
+ continue;
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ // === Legacy Phase Skipping (fallback when no workflow config) ===
92
+ if (!workflowConfig) {
93
+ // Skip optional phases if flag set or no relevant agents/outputs
94
+ if (phaseDef?.optional && skipOptional) {
95
+ continue;
96
+ }
97
+
98
+ // Skip uiux if no UI agents are active
99
+ if (candidate === 'uiux') {
100
+ const uiAgents = ['blazor-builder', 'uiux-designer', 'nextjs-expert', 'ui-ux-designer'];
101
+ const hasUiAgent = feature.activeAgents?.some(a => uiAgents.includes(a));
102
+ if (!hasUiAgent && skipOptional !== false) {
103
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: no UI agents active`));
104
+ continue;
105
+ }
106
+ }
107
+
108
+ // Skip sync for simple workflows
109
+ if (candidate === 'sync') {
110
+ const isSimple = feature.workflow === 'fast-track';
111
+ if (isSimple && skipOptional !== false) {
112
+ console.log(chalk.gray(` ⏩ Skipping ${candidate}: fast-track workflow`));
113
+ continue;
114
+ }
115
+ }
116
+ }
117
+
118
+ return candidate;
119
+ }
120
+
121
+ return null;
122
+ }
123
+
124
+ /**
125
+ * Main command handler
126
+ */
127
+ export async function advancePhaseCommand(feature, options = {}) {
128
+ console.log(chalk.cyan('\n╔════════════════════════════════════════════════╗'));
129
+ console.log(chalk.cyan('║ MORPH-SPEC PHASE ADVANCE ║'));
130
+ console.log(chalk.cyan('╚════════════════════════════════════════════════╝\n'));
131
+
132
+ // Get current feature state
133
+ const featureData = getFeature(feature);
134
+ if (!featureData) {
135
+ console.log(chalk.red(`✗ Feature not found: ${feature}`));
136
+ console.log(chalk.yellow(` Run: morph-spec state set ${feature} phase proposal`));
137
+ process.exit(1);
138
+ }
139
+
140
+ const currentPhase = featureData.phase;
141
+ const currentPhaseDef = PHASES[currentPhase];
142
+
143
+ console.log(chalk.gray('Feature:'), feature);
144
+ console.log(chalk.gray('Current Phase:'), currentPhaseDef?.name || currentPhase);
145
+ console.log(chalk.gray('Workflow:'), featureData.workflow || 'auto');
146
+
147
+ // Determine next phase
148
+ const nextPhase = getNextPhase(currentPhase, featureData, options.skipOptional);
149
+
150
+ if (!nextPhase) {
151
+ console.log(chalk.green('\n✓ Feature is at the final phase!'));
152
+ if (currentPhase === 'implement' || currentPhase === 'sync') {
153
+ console.log(chalk.green(' Consider running: morph-spec generate recap ' + feature));
154
+ }
155
+ return;
156
+ }
157
+
158
+ const nextPhaseDef = PHASES[nextPhase];
159
+ console.log(chalk.gray('Next Phase:'), nextPhaseDef.name);
160
+
161
+ // === GATE 1: State Machine Validation ===
162
+ // Ensure phase transition is valid (no skipping required phases)
163
+ if (!options.force) {
164
+ try {
165
+ validateTransition(currentPhase, nextPhase);
166
+ } catch (error) {
167
+ console.log(chalk.red('\n✗ Invalid phase transition'));
168
+ console.log(chalk.yellow(error.message));
169
+ console.log(chalk.gray('\nUse --force to override (not recommended)\n'));
170
+ process.exit(1);
171
+ }
172
+ }
173
+
174
+ // === GATE 2: Approval Gate Check ===
175
+ // Check if current phase requires approval before advancing
176
+ const approvalGateMap = {
177
+ 'design': 'design',
178
+ 'tasks': 'tasks',
179
+ 'uiux': 'uiux'
180
+ };
181
+
182
+ const requiredGate = approvalGateMap[currentPhase];
183
+ if (requiredGate && !options.skipApproval) {
184
+ const gateStatus = getApprovalGate(feature, requiredGate);
185
+
186
+ // Check trust-based auto-approval before blocking
187
+ if (!gateStatus || !gateStatus.approved) {
188
+ try {
189
+ const trustResult = shouldAutoApprove(feature, requiredGate);
190
+ if (trustResult.autoApprove) {
191
+ console.log(chalk.green(`\n✓ Auto-approved gate "${requiredGate}" (${trustResult.reason})`));
192
+ // Proceed — trust bypasses manual gate requirement
193
+ } else {
194
+ console.log(chalk.red(`\n✗ Phase "${currentPhase}" requires approval before advancing`));
195
+ console.log(chalk.gray(` Trust level: ${trustResult.level} (${trustResult.reason})`));
196
+ console.log(chalk.yellow(`\nRun: morph-spec approve ${feature} ${requiredGate}`));
197
+ console.log(chalk.gray('Or use --skip-approval to bypass (not recommended)\n'));
198
+ process.exit(1);
199
+ }
200
+ } catch {
201
+ // Trust manager unavailable — fall through to manual gate check
202
+ console.log(chalk.red(`\n✗ Phase "${currentPhase}" requires approval before advancing`));
203
+ console.log(chalk.yellow(`\nRun: morph-spec approve ${feature} ${requiredGate}`));
204
+ console.log(chalk.gray('Or use --skip-approval to bypass (not recommended)\n'));
205
+ process.exit(1);
206
+ }
207
+ } else {
208
+ console.log(chalk.green(`✓ Approval gate "${requiredGate}" passed`));
209
+ }
210
+ }
211
+
212
+ // === GATE 3: Output Requirements ===
213
+ // Validate that current phase requirements are met before advancing
214
+ const validation = validatePhase(feature, nextPhase);
215
+
216
+ if (!validation.valid) {
217
+ console.log(chalk.red('\n✗ Cannot advance — missing requirements:'));
218
+ validation.missingOutputs.forEach(output => {
219
+ console.log(chalk.red(` - ${output}`));
220
+ });
221
+ console.log(chalk.yellow(`\n Complete these before advancing to ${nextPhaseDef.name}`));
222
+ process.exit(1);
223
+ }
224
+
225
+ if (validation.stateWarning) {
226
+ console.log(chalk.yellow(`\n⚠️ ${validation.stateWarning}`));
227
+ }
228
+
229
+ // === GATE 4: Content Validation ===
230
+ // Validate spec.md and contracts.cs when advancing from design phase
231
+ if (currentPhase === 'design' && (nextPhase === 'clarify' || nextPhase === 'tasks')) {
232
+ // Check spec.md structure and content
233
+ if (featureData.outputs?.spec?.created) {
234
+ const specContentValidation = validateSpecContent(featureData.outputs.spec.path);
235
+
236
+ if (!specContentValidation.valid) {
237
+ console.log(chalk.red('\n✗ Spec content validation failed:'));
238
+ specContentValidation.missing.forEach(section => {
239
+ console.log(chalk.red(` - Missing section: ${section}`));
240
+ });
241
+ specContentValidation.errors.forEach(error => {
242
+ console.log(chalk.red(` - ${error}`));
243
+ });
244
+ console.log('');
245
+ process.exit(1);
246
+ }
247
+
248
+ if (specContentValidation.warnings?.length > 0 && !options.skipWarnings) {
249
+ console.log(chalk.yellow('\n⚠️ Spec content warnings:'));
250
+ specContentValidation.warnings.forEach(warning => {
251
+ console.log(chalk.yellow(` - ${warning}`));
252
+ });
253
+ }
254
+ }
255
+
256
+ // Run existing spec validator (anti-patterns, IaC checks)
257
+ const specValidation = await validateSpec('.', feature);
258
+
259
+ if (specValidation.errors > 0) {
260
+ console.log(chalk.red('\n✗ Spec validation failed — fix errors before advancing:'));
261
+ specValidation.issues.filter(i => i.level === 'error').forEach(issue => {
262
+ console.log(chalk.red(` - ${issue.message}`));
263
+ console.log(chalk.yellow(` → ${issue.solution}`));
264
+ });
265
+ console.log('');
266
+ process.exit(1);
267
+ }
268
+
269
+ if (specValidation.warnings > 0 && !options.skipWarnings) {
270
+ console.log(chalk.yellow('\n⚠️ Spec validation warnings:'));
271
+ specValidation.issues.filter(i => i.level === 'warning').forEach(issue => {
272
+ console.log(chalk.yellow(` - ${issue.message}`));
273
+ });
274
+ console.log(chalk.gray('\n (Use --skip-warnings to ignore warnings)\n'));
275
+ }
276
+ }
277
+
278
+ // === GATE 5: Tasks Content Validation ===
279
+ // Validate tasks.json structure when advancing to implement
280
+ if (currentPhase === 'tasks' && nextPhase === 'implement') {
281
+ if (featureData.outputs?.tasks?.created) {
282
+ const tasksContentValidation = validateTasksContent(featureData.outputs.tasks.path);
283
+
284
+ if (!tasksContentValidation.valid) {
285
+ console.log(chalk.red('\n✗ Tasks content validation failed:'));
286
+ tasksContentValidation.errors.forEach(error => {
287
+ console.log(chalk.red(` - ${error}`));
288
+ });
289
+ console.log('');
290
+ process.exit(1);
291
+ }
292
+
293
+ if (tasksContentValidation.warnings?.length > 0 && !options.skipWarnings) {
294
+ console.log(chalk.yellow('\n⚠️ Tasks content warnings:'));
295
+ tasksContentValidation.warnings.forEach(warning => {
296
+ console.log(chalk.yellow(` - ${warning}`));
297
+ });
298
+ }
299
+
300
+ // Show tasks stats
301
+ console.log(chalk.green(`\n✓ Tasks validated: ${tasksContentValidation.stats.totalTasks} total (${tasksContentValidation.stats.regularTasks} tasks + ${tasksContentValidation.stats.checkpoints} checkpoints)`));
302
+ }
303
+ }
304
+
305
+ // Gate: Check design system when advancing to implement with UI agents
306
+ if (nextPhase === 'implement') {
307
+ const gateResult = designSystemGate(feature);
308
+
309
+ if (gateResult.blocked) {
310
+ console.log(chalk.red(`\n✗ ${gateResult.message}`));
311
+ console.log('');
312
+ gateResult.solution.forEach(line => {
313
+ if (line === '') {
314
+ console.log('');
315
+ } else {
316
+ console.log(chalk.yellow(` ${line}`));
317
+ }
318
+ });
319
+ console.log('');
320
+ process.exit(1);
321
+ }
322
+ }
323
+
324
+ // Advance the phase
325
+ const state = loadState();
326
+ state.features[feature].phase = nextPhase;
327
+ state.features[feature].updatedAt = new Date().toISOString();
328
+ saveState(state);
329
+
330
+ console.log(chalk.green(`\n Advanced to ${nextPhaseDef.name}`));
331
+
332
+ // Show what's needed in the new phase
333
+ console.log(chalk.cyan('\n📋 Phase requirements:'));
334
+ console.log(chalk.gray(` ${nextPhaseDef.description}`));
335
+
336
+ if (nextPhaseDef.requiredOutputs?.length > 0) {
337
+ console.log(chalk.cyan('\n Required outputs:'));
338
+ nextPhaseDef.requiredOutputs.forEach(output => {
339
+ const exists = validation.phase ? true : false; // Already validated
340
+ console.log(chalk.gray(` ✓ ${output} (exists)`));
341
+ });
342
+ }
343
+
344
+ // Show phase-specific guidance
345
+ const guidance = getPhaseGuidance(nextPhase, feature);
346
+ if (guidance) {
347
+ console.log(chalk.cyan('\n Next steps:'));
348
+ guidance.forEach(step => console.log(chalk.white(` → ${step}`)));
349
+ }
350
+
351
+ console.log('');
352
+ }
353
+
354
+ /**
355
+ * Get guidance for what to do in a phase
356
+ */
357
+ function getPhaseGuidance(phase, feature) {
358
+ const guides = {
359
+ 'setup': [
360
+ `Resume spec pipeline: /morph-proposal ${feature}`,
361
+ 'Auto-continues from setup phase'
362
+ ],
363
+ 'uiux': [
364
+ `Resume spec pipeline: /morph-proposal ${feature}`,
365
+ 'Provide layout references and preferences',
366
+ 'Review wireframes before proceeding'
367
+ ],
368
+ 'design': [
369
+ `Resume spec pipeline: /morph-proposal ${feature}`,
370
+ 'Review DECISION POINTS carefully',
371
+ 'Approve spec.md and contracts.cs'
372
+ ],
373
+ 'clarify': [
374
+ `Resume spec pipeline: /morph-proposal ${feature}`,
375
+ 'Resolve edge cases and unknowns'
376
+ ],
377
+ 'tasks': [
378
+ `Resume spec pipeline: /morph-proposal ${feature}`,
379
+ 'Review task order and dependencies',
380
+ 'Approve task list before implementing'
381
+ ],
382
+ 'implement': [
383
+ `Start implementing: /morph-apply ${feature}`,
384
+ 'Complete tasks one by one with: morph-spec task done',
385
+ 'Validators run automatically on task completion'
386
+ ],
387
+ 'sync': [
388
+ 'Review decisions.md for standards to promote',
389
+ `Sync: morph-spec sync --path .morph/project/outputs/${feature}/decisions.md`
390
+ ]
391
+ };
392
+
393
+ return guides[phase] || null;
394
+ }
395
+
396
+ /**
397
+ * Gate: Check design system exists when advancing to implement with UI agents
398
+ * @param {string} feature - Feature name
399
+ * @param {string} projectPath - Project root path
400
+ * @returns {Object} { blocked: boolean, message?: string, solution?: string[] }
401
+ */
402
+ function designSystemGate(feature, projectPath = '.') {
403
+ // Check if UI agents are active
404
+ const hasUIAgents = hasUIAgentsActive(projectPath, feature);
405
+
406
+ if (!hasUIAgents) {
407
+ return { blocked: false }; // No UI agents, gate doesn't apply
408
+ }
409
+
410
+ // Check if design system exists
411
+ const dsDetection = detectDesignSystem(projectPath, feature);
412
+
413
+ if (dsDetection.hasDesignSystem) {
414
+ return { blocked: false }; // Design system exists, gate passed
415
+ }
416
+
417
+ // Block advancement - no design system found with UI agents active
418
+ return {
419
+ blocked: true,
420
+ message: 'Cannot advance to implementation — UI agents are active but no design system found',
421
+ solution: [
422
+ 'Design system is required when UI agents (blazor-builder, ui-designer, css-specialist) are active',
423
+ '',
424
+ 'Create a design system with one of these options:',
425
+ ' 1. Project-level: .morph/project/design-system.md (shared across features)',
426
+ ` 2. Feature-level: .morph/project/outputs/${feature}/ui-design-system.md (feature-specific)`,
427
+ ' 3. Auto-generate: morph-spec generate design-system (scans existing CSS)',
428
+ '',
429
+ 'Or remove UI agents if they are not needed:',
430
+ ` morph-spec state remove-agent ${feature} blazor-builder`
431
+ ]
432
+ };
433
+ }