@polymorphism-tech/morph-spec 4.22.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.
- 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
|
@@ -35,13 +35,9 @@ const OUTPUT_SECTIONS = {
|
|
|
35
35
|
'Performance', 'Edge Cases', 'Clarifications'
|
|
36
36
|
]
|
|
37
37
|
},
|
|
38
|
-
'
|
|
39
|
-
required: ['
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
'schema-analysis.md': {
|
|
43
|
-
required: ['Tables Analyzed', 'Columns', 'Relationships'],
|
|
44
|
-
optional: ['CRITICAL FINDINGS', 'Recommendations', 'Indexes']
|
|
38
|
+
'mandate.md': {
|
|
39
|
+
required: ['Stack', 'Standards', 'Done'],
|
|
40
|
+
optional: ['Patterns proibidos', 'Riscos']
|
|
45
41
|
},
|
|
46
42
|
'decisions.md': {
|
|
47
43
|
required: ['ADR', 'Status', 'Context', 'Decision'],
|
|
@@ -51,29 +47,32 @@ const OUTPUT_SECTIONS = {
|
|
|
51
47
|
required: ['T001', 'category', 'dependencies'],
|
|
52
48
|
optional: ['CHECKPOINT', 'estimatedMinutes']
|
|
53
49
|
},
|
|
50
|
+
'review-report.md': {
|
|
51
|
+
required: ['Scores', 'Recommendation'],
|
|
52
|
+
optional: ['Blockers', 'Improvements']
|
|
53
|
+
},
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Phase-to-output mapping
|
|
57
|
+
* Phase-to-output mapping (v5 NLH)
|
|
58
58
|
*/
|
|
59
59
|
const PHASE_OUTPUTS = {
|
|
60
60
|
proposal: ['proposal.md'],
|
|
61
61
|
uiux: ['design-system.md', 'mockups.md', 'components.md', 'flows.md'],
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
plan: ['spec.md', 'mandate.md', 'tasks.md'],
|
|
63
|
+
implement: ['recap.md'],
|
|
64
|
+
review: ['review-report.md'],
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Phase-to-folder mapping (filesystem convention)
|
|
68
|
+
* Phase-to-folder mapping (v5 filesystem convention)
|
|
69
69
|
*/
|
|
70
70
|
const PHASE_FOLDERS = {
|
|
71
71
|
proposal: '0-proposal',
|
|
72
72
|
uiux: '1-ui',
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
implement: '5-implement',
|
|
73
|
+
plan: '2-plan',
|
|
74
|
+
implement: '3-implement',
|
|
75
|
+
review: '4-review',
|
|
77
76
|
};
|
|
78
77
|
|
|
79
78
|
/**
|
|
@@ -105,13 +104,8 @@ function validateOutput(featurePath, outputFile) {
|
|
|
105
104
|
return result;
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
validateCSharpContent(content, result);
|
|
111
|
-
} else {
|
|
112
|
-
// Markdown section validation
|
|
113
|
-
validateMarkdownSections(content, sections, result);
|
|
114
|
-
}
|
|
107
|
+
// v5: only markdown outputs — contracts moved into spec.md
|
|
108
|
+
validateMarkdownSections(content, sections, result);
|
|
115
109
|
|
|
116
110
|
// Size warnings
|
|
117
111
|
if (result.lineCount < 10) {
|
|
@@ -143,94 +137,6 @@ function validateMarkdownSections(content, sections, result) {
|
|
|
143
137
|
}
|
|
144
138
|
}
|
|
145
139
|
|
|
146
|
-
/**
|
|
147
|
-
* Validate C# contracts content
|
|
148
|
-
*/
|
|
149
|
-
function validateCSharpContent(content, result) {
|
|
150
|
-
// Check for namespace
|
|
151
|
-
if (/namespace\s+\S+/.test(content)) {
|
|
152
|
-
result.sections.found.push('namespace');
|
|
153
|
-
} else {
|
|
154
|
-
result.sections.missing.push('namespace');
|
|
155
|
-
result.errors.push('Missing namespace declaration');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Check for records (DTOs)
|
|
159
|
-
const records = content.match(/public\s+record\s+\w+/g) || [];
|
|
160
|
-
if (records.length > 0) {
|
|
161
|
-
result.sections.found.push(`${records.length} record(s)`);
|
|
162
|
-
} else {
|
|
163
|
-
result.warnings.push('No record types found — DTOs expected');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Check for interfaces
|
|
167
|
-
const interfaces = content.match(/public\s+interface\s+I\w+/g) || [];
|
|
168
|
-
if (interfaces.length > 0) {
|
|
169
|
-
result.sections.found.push(`${interfaces.length} interface(s)`);
|
|
170
|
-
} else {
|
|
171
|
-
result.warnings.push('No interfaces found — service contracts expected');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Check for CancellationToken in async methods
|
|
175
|
-
const asyncMethods = content.match(/Task<.*>\s+\w+\(/g) || [];
|
|
176
|
-
const methodsWithCt = content.match(/CancellationToken\s+ct/g) || [];
|
|
177
|
-
if (asyncMethods.length > 0 && methodsWithCt.length < asyncMethods.length) {
|
|
178
|
-
result.warnings.push(
|
|
179
|
-
`${asyncMethods.length - methodsWithCt.length} async method(s) may be missing CancellationToken`
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Check for schema-analysis reference
|
|
184
|
-
if (content.includes('schema-analysis')) {
|
|
185
|
-
result.sections.found.push('schema reference');
|
|
186
|
-
} else {
|
|
187
|
-
result.warnings.push('No reference to schema-analysis.md — fields may not match database');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Cross-validate contracts against schema-analysis
|
|
193
|
-
*/
|
|
194
|
-
function crossValidateSchema(featurePath) {
|
|
195
|
-
const schemaPath = path.join(featurePath, 'schema-analysis.md');
|
|
196
|
-
const contractsPath = path.join(featurePath, 'contracts.cs');
|
|
197
|
-
const warnings = [];
|
|
198
|
-
|
|
199
|
-
if (!fs.existsSync(schemaPath) || !fs.existsSync(contractsPath)) {
|
|
200
|
-
return warnings;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const schema = fs.readFileSync(schemaPath, 'utf8');
|
|
204
|
-
const contracts = fs.readFileSync(contractsPath, 'utf8');
|
|
205
|
-
|
|
206
|
-
// Extract field names from schema (look for column names in tables)
|
|
207
|
-
const schemaFields = new Set();
|
|
208
|
-
const columnRegex = /\|\s*(\w+)\s*\|/g;
|
|
209
|
-
let match;
|
|
210
|
-
while ((match = columnRegex.exec(schema)) !== null) {
|
|
211
|
-
const field = match[1].toLowerCase();
|
|
212
|
-
if (!['column', 'name', 'type', 'nullable', 'default', 'notes', 'table', '---'].includes(field)) {
|
|
213
|
-
schemaFields.add(field);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Extract field names from contracts
|
|
218
|
-
const contractFields = new Set();
|
|
219
|
-
const fieldRegex = /(?:string|int|Guid|DateTime|bool|decimal|JsonObject)\???\s+(\w+)/g;
|
|
220
|
-
while ((match = fieldRegex.exec(contracts)) !== null) {
|
|
221
|
-
contractFields.add(match[1].toLowerCase());
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Check for contract fields not in schema
|
|
225
|
-
for (const field of contractFields) {
|
|
226
|
-
if (schemaFields.size > 0 && !schemaFields.has(field) && field !== 'ct') {
|
|
227
|
-
warnings.push(`Contract field "${field}" not found in schema-analysis.md — verify correctness`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return warnings;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
140
|
/**
|
|
235
141
|
* Main validate command
|
|
236
142
|
*/
|
|
@@ -282,7 +188,7 @@ export async function validateFeatureCommand(featureName, options = {}) {
|
|
|
282
188
|
const results = phase
|
|
283
189
|
? outputFiles.map(file => validateOutput(searchPath, file))
|
|
284
190
|
: outputFiles.map(({ file, dir }) => validateOutput(dir, file));
|
|
285
|
-
const schemaWarnings =
|
|
191
|
+
const schemaWarnings = []; // v5: cross-validation removed (contracts in spec.md)
|
|
286
192
|
|
|
287
193
|
// Aggregate results
|
|
288
194
|
const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
|
|
@@ -1,152 +1,133 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Output Schema — Single Source of Truth
|
|
3
|
-
*
|
|
4
|
-
* ALL output type definitions live here. Edit ONLY this file to add, rename,
|
|
5
|
-
* or relocate output types.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
proposal:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
*
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
export const PROTECTED_SPEC_FILES = Object.fromEntries(
|
|
135
|
-
Object.entries(OUTPUT_SCHEMA)
|
|
136
|
-
.filter(([, entry]) => entry.protected && entry.approvalGate)
|
|
137
|
-
.map(([, entry]) => [entry.filename, entry.approvalGate])
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
// ============================================================================
|
|
141
|
-
// Stack-aware Helpers
|
|
142
|
-
// ============================================================================
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Resolve the correct contracts output type key for a given stack.
|
|
146
|
-
* @param {string|null} stack
|
|
147
|
-
* @returns {'contracts'|'contractsTs'}
|
|
148
|
-
*/
|
|
149
|
-
export function resolveContractsOutputType(stack) {
|
|
150
|
-
const NEXTJS_STACKS = new Set(['nextjs', 'next', 'next.js']);
|
|
151
|
-
return NEXTJS_STACKS.has(stack?.toLowerCase?.()) ? 'contractsTs' : 'contracts';
|
|
152
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Output Schema — Single Source of Truth (v5)
|
|
3
|
+
*
|
|
4
|
+
* ALL output type definitions live here. Edit ONLY this file to add, rename,
|
|
5
|
+
* or relocate output types.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Schema Definition
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Schema for output types produced by morph-spec NLH phases.
|
|
16
|
+
*
|
|
17
|
+
* Fields:
|
|
18
|
+
* filename - The file's basename (e.g. 'spec.md')
|
|
19
|
+
* phaseDir - Subdirectory within .morph/features/{feature}/
|
|
20
|
+
* phase - The morph-spec phase that produces this output
|
|
21
|
+
* protected - Whether the file requires an approval gate before editing
|
|
22
|
+
* approvalGate - (optional) Which gate protects this file
|
|
23
|
+
*/
|
|
24
|
+
export const OUTPUT_SCHEMA = {
|
|
25
|
+
// 0-proposal/
|
|
26
|
+
proposal: { filename: 'proposal.md', phaseDir: '0-proposal', phase: 'proposal', protected: false },
|
|
27
|
+
stackScan: { filename: 'stack-scan.md', phaseDir: '0-proposal', phase: 'proposal', protected: false },
|
|
28
|
+
migrationPlan: { filename: 'migration-plan.md', phaseDir: '0-proposal', phase: 'proposal', protected: false },
|
|
29
|
+
|
|
30
|
+
// 1-ui/ (optional)
|
|
31
|
+
uiDesignSystem: { filename: 'design-system.md', phaseDir: '1-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
|
|
32
|
+
uiMockups: { filename: 'mockups.md', phaseDir: '1-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
|
|
33
|
+
uiComponents: { filename: 'components.md', phaseDir: '1-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
|
|
34
|
+
uiFlows: { filename: 'flows.md', phaseDir: '1-ui', phase: 'uiux', protected: true, approvalGate: 'uiux' },
|
|
35
|
+
|
|
36
|
+
// 2-plan/
|
|
37
|
+
spec: { filename: 'spec.md', phaseDir: '2-plan', phase: 'plan', protected: true, approvalGate: 'plan' },
|
|
38
|
+
mandate: { filename: 'mandate.md', phaseDir: '2-plan', phase: 'plan', protected: true, approvalGate: 'plan' },
|
|
39
|
+
tasks: { filename: 'tasks.md', phaseDir: '2-plan', phase: 'plan', protected: true, approvalGate: 'plan' },
|
|
40
|
+
decisions: { filename: 'decisions.md', phaseDir: '2-plan', phase: 'plan', protected: false },
|
|
41
|
+
|
|
42
|
+
// 3-implement/
|
|
43
|
+
recap: { filename: 'recap.md', phaseDir: '3-implement', phase: 'implement', protected: false },
|
|
44
|
+
|
|
45
|
+
// 4-review/
|
|
46
|
+
reviewReport: { filename: 'review-report.md', phaseDir: '4-review', phase: 'review', protected: false },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Builder Functions (Windows-safe via path.join)
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the morph-relative path for a specific output type of a feature.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} featureName
|
|
57
|
+
* @param {string} outputType - camelCase output type key from OUTPUT_SCHEMA
|
|
58
|
+
* @returns {string} e.g. ".morph/features/my-feature/2-plan/spec.md"
|
|
59
|
+
*/
|
|
60
|
+
export function getOutputPath(featureName, outputType) {
|
|
61
|
+
const entry = OUTPUT_SCHEMA[outputType];
|
|
62
|
+
if (!entry) {
|
|
63
|
+
throw new Error(`Unknown output type: "${outputType}". Valid types: ${Object.keys(OUTPUT_SCHEMA).join(', ')}`);
|
|
64
|
+
}
|
|
65
|
+
return join('.morph', 'features', featureName, entry.phaseDir, entry.filename)
|
|
66
|
+
.replace(/\\/g, '/');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the absolute filesystem path for a specific output type.
|
|
71
|
+
*/
|
|
72
|
+
export function getAbsoluteOutputPath(projectRoot, featureName, outputType) {
|
|
73
|
+
const entry = OUTPUT_SCHEMA[outputType];
|
|
74
|
+
if (!entry) {
|
|
75
|
+
throw new Error(`Unknown output type: "${outputType}". Valid types: ${Object.keys(OUTPUT_SCHEMA).join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
return join(projectRoot, '.morph', 'features', featureName, entry.phaseDir, entry.filename);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the feature base directory (morph-relative, forward slashes).
|
|
82
|
+
*/
|
|
83
|
+
export function getFeatureDir(featureName) {
|
|
84
|
+
return join('.morph', 'features', featureName).replace(/\\/g, '/');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get all output paths for a feature, ready for state.json `outputs` block.
|
|
89
|
+
*/
|
|
90
|
+
export function getAllOutputPaths(featureName) {
|
|
91
|
+
const outputs = {};
|
|
92
|
+
for (const [key] of Object.entries(OUTPUT_SCHEMA)) {
|
|
93
|
+
outputs[key] = {
|
|
94
|
+
created: false,
|
|
95
|
+
path: getOutputPath(featureName, key),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return outputs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Derived Constants
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
export const FILENAME_TO_OUTPUT = Object.fromEntries(
|
|
106
|
+
Object.entries(OUTPUT_SCHEMA).map(([key, entry]) => [entry.filename, key])
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export const OUTPUT_PHASE_MAP = Object.fromEntries(
|
|
110
|
+
Object.entries(OUTPUT_SCHEMA).map(([key, entry]) => [key, entry.phase])
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Protected spec files and the approval gate that locks them.
|
|
115
|
+
*/
|
|
116
|
+
export const PROTECTED_SPEC_FILES = Object.fromEntries(
|
|
117
|
+
Object.entries(OUTPUT_SCHEMA)
|
|
118
|
+
.filter(([, entry]) => entry.protected && entry.approvalGate)
|
|
119
|
+
.map(([, entry]) => [entry.filename, entry.approvalGate])
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Backwards-compat shim (will be removed in v5.1)
|
|
124
|
+
// ============================================================================
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* v4 had separate contracts.cs / contracts.ts / contracts-vsa.cs output types.
|
|
128
|
+
* In v5 contracts live inside spec.md (or in source files for VSA). This
|
|
129
|
+
* function keeps callers happy until they're updated.
|
|
130
|
+
*/
|
|
131
|
+
export function resolveContractsOutputType() {
|
|
132
|
+
return 'spec';
|
|
133
|
+
}
|
|
@@ -10,13 +10,11 @@
|
|
|
10
10
|
* Each phase can only transition to specific next phases
|
|
11
11
|
*/
|
|
12
12
|
const VALID_TRANSITIONS = {
|
|
13
|
-
'proposal': ['uiux', '
|
|
14
|
-
'uiux': ['
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'implement': ['sync', 'archived'], // Can skip sync
|
|
19
|
-
'sync': ['archived']
|
|
13
|
+
'proposal': ['uiux', 'plan'],
|
|
14
|
+
'uiux': ['plan'],
|
|
15
|
+
'plan': ['implement'],
|
|
16
|
+
'implement': ['review'],
|
|
17
|
+
'review': []
|
|
20
18
|
};
|
|
21
19
|
|
|
22
20
|
/**
|
|
@@ -25,18 +23,15 @@ const VALID_TRANSITIONS = {
|
|
|
25
23
|
const PHASE_NAMES = {
|
|
26
24
|
'proposal': 'Proposal (Phase 0)',
|
|
27
25
|
'uiux': 'UI/UX Design (Phase 1)',
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'implement': 'Implement (Phase 5)',
|
|
32
|
-
'sync': 'Sync (Phase 6)',
|
|
33
|
-
'archived': 'Archived'
|
|
26
|
+
'plan': 'Plan (Phase 2)',
|
|
27
|
+
'implement': 'Implement (Phase 3)',
|
|
28
|
+
'review': 'Quality Review (Phase 4)'
|
|
34
29
|
};
|
|
35
30
|
|
|
36
31
|
/**
|
|
37
32
|
* Optional phases that can be skipped
|
|
38
33
|
*/
|
|
39
|
-
const OPTIONAL_PHASES = ['uiux'
|
|
34
|
+
const OPTIONAL_PHASES = ['uiux'];
|
|
40
35
|
|
|
41
36
|
/**
|
|
42
37
|
* Validate if a phase transition is allowed
|
|
@@ -116,16 +111,7 @@ export function validateTransition(fromPhase, toPhase) {
|
|
|
116
111
|
* @returns {string[]} Ordered list of phases
|
|
117
112
|
*/
|
|
118
113
|
export function getPhaseSequence() {
|
|
119
|
-
return [
|
|
120
|
-
'proposal',
|
|
121
|
-
'uiux', // optional
|
|
122
|
-
'design',
|
|
123
|
-
'plan',
|
|
124
|
-
'tasks',
|
|
125
|
-
'implement',
|
|
126
|
-
'sync', // optional
|
|
127
|
-
'archived'
|
|
128
|
-
];
|
|
114
|
+
return ['proposal', 'uiux', 'plan', 'implement', 'review'];
|
|
129
115
|
}
|
|
130
116
|
|
|
131
117
|
/**
|
|
@@ -27,14 +27,13 @@ export function getStatePath() {
|
|
|
27
27
|
* Returns the phase corresponding to the highest-numbered folder present.
|
|
28
28
|
*
|
|
29
29
|
* @param {string} featurePath - Absolute path to .morph/features/{feature}/
|
|
30
|
-
* @returns {string} Phase name: '
|
|
30
|
+
* @returns {string} Phase name: 'review' | 'implement' | 'plan' | 'uiux' | 'proposal'
|
|
31
31
|
*/
|
|
32
32
|
export function derivePhase(featurePath) {
|
|
33
33
|
const phaseMap = [
|
|
34
|
-
['
|
|
35
|
-
['
|
|
36
|
-
['
|
|
37
|
-
['2-design', 'design'],
|
|
34
|
+
['4-review', 'review'],
|
|
35
|
+
['3-implement', 'implement'],
|
|
36
|
+
['2-plan', 'plan'],
|
|
38
37
|
['1-ui', 'uiux'],
|
|
39
38
|
['0-proposal', 'proposal'],
|
|
40
39
|
];
|
|
@@ -79,7 +78,7 @@ export function loadState(throwOnError = true) {
|
|
|
79
78
|
|
|
80
79
|
if (!existsSync(statePath)) {
|
|
81
80
|
if (throwOnError) {
|
|
82
|
-
throw new Error(`State file not found: ${statePath}\nRun 'morph-spec
|
|
81
|
+
throw new Error(`State file not found: ${statePath}\nRun 'morph-spec init' to initialize MORPH in this project.`);
|
|
83
82
|
}
|
|
84
83
|
return null;
|
|
85
84
|
}
|
|
@@ -297,6 +296,7 @@ async function ensureFeature(featureName, options = {}) {
|
|
|
297
296
|
workflowDetection.matchedKeywords = detection.matchedKeywords;
|
|
298
297
|
workflowDetection.estimatedComplexity = detection.estimatedComplexity;
|
|
299
298
|
workflowDetection.reasoning = detection.reasoning;
|
|
299
|
+
workflowDetection.warnings = detection.warnings || [];
|
|
300
300
|
} catch (err) {
|
|
301
301
|
// If detection fails, fall back to auto
|
|
302
302
|
console.warn('Workflow detection failed:', err.message);
|
|
@@ -313,8 +313,8 @@ async function ensureFeature(featureName, options = {}) {
|
|
|
313
313
|
approvalGates: {
|
|
314
314
|
proposal: { approved: false, timestamp: null, approvedBy: null },
|
|
315
315
|
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
316
|
-
|
|
317
|
-
|
|
316
|
+
plan: { approved: false, timestamp: null, approvedBy: null },
|
|
317
|
+
review: { approved: false, timestamp: null, approvedBy: null }
|
|
318
318
|
},
|
|
319
319
|
tasks: {
|
|
320
320
|
total: 0,
|
|
@@ -491,7 +491,7 @@ export function logDispatch(featureName, entry) {
|
|
|
491
491
|
/**
|
|
492
492
|
* Normalize output type from kebab-case to camelCase (BUG-002 fix - Enhanced)
|
|
493
493
|
* Supports both kebab-case and camelCase input formats for all output types
|
|
494
|
-
* @param {string} type - Output type (e.g., 'ui-design-system', '
|
|
494
|
+
* @param {string} type - Output type (e.g., 'ui-design-system', 'stack-scan', or 'uiDesignSystem')
|
|
495
495
|
* @returns {string} Normalized type in camelCase
|
|
496
496
|
*/
|
|
497
497
|
function normalizeOutputType(type) {
|
|
@@ -502,7 +502,7 @@ function normalizeOutputType(type) {
|
|
|
502
502
|
|
|
503
503
|
// Convert kebab-case to camelCase automatically
|
|
504
504
|
// Example: 'ui-design-system' → 'uiDesignSystem'
|
|
505
|
-
// Example: '
|
|
505
|
+
// Example: 'stack-scan' → 'stackScan'
|
|
506
506
|
const camelCase = type.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
507
507
|
|
|
508
508
|
return camelCase;
|
|
@@ -582,7 +582,7 @@ function findClosestMatch(inputType, validTypes) {
|
|
|
582
582
|
/**
|
|
583
583
|
* Mark output as created
|
|
584
584
|
* @param {string} featureName - Feature name
|
|
585
|
-
* @param {string} outputType - Output type (proposal, spec,
|
|
585
|
+
* @param {string} outputType - Output type (proposal, spec, mandate, etc.)
|
|
586
586
|
*/
|
|
587
587
|
export async function markOutput(featureName, outputType) {
|
|
588
588
|
await ensureFeature(featureName);
|
|
@@ -618,7 +618,7 @@ export async function markOutput(featureName, outputType) {
|
|
|
618
618
|
|
|
619
619
|
errorMsg += `\n\nValid types:\n`;
|
|
620
620
|
errorMsg += ` ${validCamelCaseTypes.join(', ')}\n`;
|
|
621
|
-
errorMsg += `\nNote:
|
|
621
|
+
errorMsg += `\nNote: kebab-case is also accepted (e.g., 'ui-design-system', 'stack-scan')`;
|
|
622
622
|
|
|
623
623
|
throw new Error(errorMsg);
|
|
624
624
|
}
|
|
@@ -769,7 +769,7 @@ export function getSummary() {
|
|
|
769
769
|
/**
|
|
770
770
|
* Set approval gate status
|
|
771
771
|
* @param {string} featureName - Feature name
|
|
772
|
-
* @param {string} gate - Gate name (proposal, uiux,
|
|
772
|
+
* @param {string} gate - Gate name (proposal, uiux, plan, review)
|
|
773
773
|
* @param {boolean} approved - Approval status
|
|
774
774
|
* @param {Object} metadata - Additional metadata (approvedBy, reason, etc.)
|
|
775
775
|
*/
|
|
@@ -781,8 +781,8 @@ export async function setApprovalGate(featureName, gate, approved, metadata = {}
|
|
|
781
781
|
feature.approvalGates = {
|
|
782
782
|
proposal: { approved: false, timestamp: null, approvedBy: null },
|
|
783
783
|
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
784
|
-
|
|
785
|
-
|
|
784
|
+
plan: { approved: false, timestamp: null, approvedBy: null },
|
|
785
|
+
review: { approved: false, timestamp: null, approvedBy: null }
|
|
786
786
|
};
|
|
787
787
|
}
|
|
788
788
|
|
|
@@ -831,8 +831,8 @@ export function isPendingApproval(featureName) {
|
|
|
831
831
|
|
|
832
832
|
// Check if current phase has an approval gate
|
|
833
833
|
const phaseGateMap = {
|
|
834
|
-
'
|
|
835
|
-
'
|
|
834
|
+
'plan': 'plan',
|
|
835
|
+
'review': 'review',
|
|
836
836
|
'uiux': 'uiux'
|
|
837
837
|
};
|
|
838
838
|
|