@polymorphism-tech/morph-spec 4.22.0 → 6.0.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.
- package/CLAUDE.md +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /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
|
-
}
|