@polymorphism-tech/morph-spec 4.7.1 → 4.8.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/.morph/analytics/threads-log.jsonl +54 -0
- package/.morph/state.json +198 -0
- package/LICENSE +1 -2
- package/README.md +379 -414
- package/bin/morph-spec.js +57 -403
- package/bin/validate.js +2 -26
- package/claude-plugin.json +2 -2
- package/docs/ARCHITECTURE.md +43 -46
- package/docs/CHEATSHEET.md +203 -221
- package/docs/COMMAND-FLOWS.md +319 -289
- package/docs/QUICKSTART.md +2 -8
- package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +2 -0
- package/docs/plans/2026-02-22-claude-settings.md +2 -0
- package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +2 -0
- package/docs/plans/2026-02-22-morph-spec-next.md +2 -0
- package/docs/plans/2026-02-22-native-alignment-design.md +2 -0
- package/docs/plans/2026-02-22-native-alignment-impl.md +2 -0
- package/docs/plans/2026-02-22-native-enrichment-design.md +2 -0
- package/docs/plans/2026-02-22-native-enrichment.md +2 -0
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +2 -0
- package/docs/plans/2026-02-23-ddd-nextsteps.md +2 -0
- package/docs/plans/2026-02-23-infra-architect-refactor.md +2 -0
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +2 -1
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +2 -0
- package/docs/plans/2026-02-23-nextjs-standards-design.md +2 -1
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +2 -0
- package/docs/plans/2026-02-24-cli-radical-simplification.md +592 -0
- package/docs/plans/2026-02-24-framework-failure-points.md +125 -0
- package/docs/plans/2026-02-24-morph-init-design.md +337 -0
- package/docs/plans/2026-02-24-morph-init-impl.md +1269 -0
- package/docs/plans/2026-02-24-tutorial-command-design.md +71 -0
- package/docs/plans/2026-02-24-tutorial-command.md +298 -0
- package/framework/CLAUDE.md +2 -2
- package/framework/commands/morph-proposal.md +3 -3
- package/framework/hooks/README.md +11 -10
- package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
- package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +4 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +20 -5
- package/framework/hooks/claude-code/statusline.py +6 -1
- package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
- package/framework/hooks/dev/check-sync-health.js +117 -0
- package/framework/hooks/dev/guard-version-numbers.js +57 -0
- package/framework/hooks/dev/sync-standards-registry.js +60 -0
- package/framework/hooks/dev/sync-template-registry.js +60 -0
- package/framework/hooks/dev/validate-skill-format.js +70 -0
- package/framework/hooks/dev/validate-standard-format.js +73 -0
- package/framework/hooks/shared/payload-utils.js +39 -0
- package/framework/hooks/shared/state-reader.js +25 -1
- package/framework/rules/morph-workflow.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +216 -0
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +4 -4
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +192 -191
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +181 -180
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +339 -338
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +254 -253
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +168 -170
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +284 -283
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +246 -245
- package/framework/templates/examples/design-system-examples.md +1 -1
- package/framework/templates/ui/FluentDesignTheme.cs +1 -1
- package/framework/templates/ui/MudTheme.cs +1 -1
- package/framework/templates/ui/design-system.css +1 -1
- package/package.json +4 -2
- package/scripts/bump-version.js +248 -0
- package/scripts/install-dev-hooks.js +138 -0
- package/src/commands/agents/index.js +1 -2
- package/src/commands/index.js +13 -16
- package/src/commands/project/doctor.js +100 -14
- package/src/commands/project/index.js +7 -10
- package/src/commands/project/init.js +398 -555
- package/src/commands/project/install-plugin-cmd.js +28 -0
- package/src/commands/project/setup-infra-cmd.js +12 -0
- package/src/commands/project/tutorial.js +115 -0
- package/src/commands/project/update.js +22 -37
- package/src/commands/state/approve.js +213 -221
- package/src/commands/state/index.js +0 -1
- package/src/commands/state/state.js +337 -365
- package/src/commands/templates/index.js +0 -4
- package/src/commands/trust/trust.js +1 -93
- package/src/commands/utils/index.js +1 -5
- package/src/commands/validation/index.js +1 -5
- package/src/core/registry/command-registry.js +11 -285
- package/src/core/state/state-manager.js +5 -2
- package/src/lib/detectors/index.js +81 -87
- package/src/lib/detectors/structure-detector.js +275 -273
- package/src/lib/generators/recap-generator.js +232 -225
- package/src/lib/installers/mcp-installer.js +18 -3
- package/src/scripts/global-install.js +34 -0
- package/src/scripts/install-plugin.js +126 -0
- package/src/scripts/setup-infra.js +203 -0
- package/src/utils/agents-installer.js +10 -1
- package/src/utils/hooks-installer.js +70 -17
- package/CLAUDE.md +0 -77
- package/docs/claude-alignment-report.md +0 -137
- package/docs/examples/order-management/contracts.cs +0 -84
- package/docs/examples/order-management/proposal.md +0 -24
- package/docs/examples/order-management/spec.md +0 -162
- package/src/commands/feature/create-story.js +0 -362
- package/src/commands/feature/index.js +0 -6
- package/src/commands/feature/shard-spec.js +0 -225
- package/src/commands/feature/sprint-status.js +0 -250
- package/src/commands/generation/generate-onboarding.js +0 -169
- package/src/commands/generation/generate.js +0 -276
- package/src/commands/generation/index.js +0 -5
- package/src/commands/learning/capture-pattern.js +0 -121
- package/src/commands/learning/index.js +0 -5
- package/src/commands/learning/search-patterns.js +0 -126
- package/src/commands/mcp/mcp.js +0 -102
- package/src/commands/project/changes.js +0 -66
- package/src/commands/project/cost.js +0 -179
- package/src/commands/project/detect.js +0 -114
- package/src/commands/project/diff.js +0 -278
- package/src/commands/project/revert.js +0 -173
- package/src/commands/project/standards.js +0 -80
- package/src/commands/project/sync.js +0 -167
- package/src/commands/project/update-agents.js +0 -23
- package/src/commands/state/rollback-phase.js +0 -185
- package/src/commands/templates/template-customize.js +0 -87
- package/src/commands/templates/template-list.js +0 -114
- package/src/commands/templates/template-show.js +0 -129
- package/src/commands/templates/template-validate.js +0 -91
- package/src/commands/utils/troubleshoot.js +0 -222
- package/src/commands/validation/analyze-blazor-concurrency.js +0 -193
- package/src/commands/validation/lint-fluent.js +0 -352
- package/src/commands/validation/validate-blazor-state.js +0 -210
- package/src/commands/validation/validate-blazor.js +0 -156
- package/src/commands/validation/validate-css.js +0 -84
- package/src/lib/detectors/conversation-analyzer.js +0 -163
- package/src/lib/learning/index.js +0 -7
- package/src/lib/learning/learning-system.js +0 -520
- package/src/lib/troubleshooting/index.js +0 -8
- package/src/lib/troubleshooting/troubleshoot-grep.js +0 -198
- package/src/lib/troubleshooting/troubleshoot-index.js +0 -144
- package/src/llm/environment-detector.js +0 -43
|
@@ -1,225 +1,232 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recap Generator
|
|
3
|
-
*
|
|
4
|
-
* Auto-generates recap.md from real project data: state.json, contract compliance,
|
|
5
|
-
* validation results, and decisions. Replaces manual template filling.
|
|
6
|
-
*
|
|
7
|
-
* MORPH-SPEC 3.0 - Phase B: Spec-Driven Pipeline
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
11
|
-
import { join, dirname } from 'path';
|
|
12
|
-
import chalk from 'chalk';
|
|
13
|
-
import { loadState } from '../../core/state/state-manager.js';
|
|
14
|
-
import { runValidation } from '../validators/validation-runner.js';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generate recap.md for a feature
|
|
18
|
-
*
|
|
19
|
-
* @param {string} projectPath - Project root path
|
|
20
|
-
* @param {string} featureName - Feature name
|
|
21
|
-
* @param {Object} options - Options
|
|
22
|
-
* @returns {string} Generated recap content
|
|
23
|
-
*/
|
|
24
|
-
export async function generateRecap(projectPath, featureName, options = {}) {
|
|
25
|
-
const state = loadState(false);
|
|
26
|
-
if (!state || !state.features[featureName]) {
|
|
27
|
-
throw new Error(`Feature '${featureName}' not found in state.json`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const feature = state.features[featureName];
|
|
31
|
-
const outputsPath = join(projectPath, '.morph/features', featureName);
|
|
32
|
-
|
|
33
|
-
// Gather data
|
|
34
|
-
const tasksSummary = getTasksSummary(feature);
|
|
35
|
-
const agentsSummary = getAgentsSummary(feature);
|
|
36
|
-
const checkpointsSummary = getCheckpointsSummary(feature);
|
|
37
|
-
// Decision constraints support (stub - decision-constraint-loader removed as orphaned code)
|
|
38
|
-
const decisions = { decisions: [] };
|
|
39
|
-
const validationResult = await runValidation(projectPath, featureName, { verbose: false });
|
|
40
|
-
|
|
41
|
-
// Contract coverage
|
|
42
|
-
let contractCoverage = null;
|
|
43
|
-
if (validationResult.issues) {
|
|
44
|
-
const contractIssues = validationResult.issues.filter(i => i.message?.includes('Interface') || i.message?.includes('contracts'));
|
|
45
|
-
if (contractIssues.length > 0 || existsSync(join(outputsPath, 'contracts.cs'))) {
|
|
46
|
-
// Extract coverage from validation result
|
|
47
|
-
contractCoverage = extractContractCoverage(validationResult);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Generate markdown
|
|
52
|
-
const recap = buildRecapMarkdown({
|
|
53
|
-
featureName,
|
|
54
|
-
feature,
|
|
55
|
-
tasksSummary,
|
|
56
|
-
agentsSummary,
|
|
57
|
-
checkpointsSummary,
|
|
58
|
-
decisions: decisions.decisions,
|
|
59
|
-
validationResult,
|
|
60
|
-
contractCoverage
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Write to file
|
|
64
|
-
const recapPath = join(outputsPath, 'recap.md');
|
|
65
|
-
if (!existsSync(dirname(recapPath))) {
|
|
66
|
-
mkdirSync(dirname(recapPath), { recursive: true });
|
|
67
|
-
}
|
|
68
|
-
writeFileSync(recapPath, recap, 'utf-8');
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
md
|
|
153
|
-
md +=
|
|
154
|
-
md +=
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
md +=
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
md += `\n
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
//
|
|
203
|
-
if (
|
|
204
|
-
md += `##
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Recap Generator
|
|
3
|
+
*
|
|
4
|
+
* Auto-generates recap.md from real project data: state.json, contract compliance,
|
|
5
|
+
* validation results, and decisions. Replaces manual template filling.
|
|
6
|
+
*
|
|
7
|
+
* MORPH-SPEC 3.0 - Phase B: Spec-Driven Pipeline
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { loadState, markOutput } from '../../core/state/state-manager.js';
|
|
14
|
+
import { runValidation } from '../validators/validation-runner.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate recap.md for a feature
|
|
18
|
+
*
|
|
19
|
+
* @param {string} projectPath - Project root path
|
|
20
|
+
* @param {string} featureName - Feature name
|
|
21
|
+
* @param {Object} options - Options
|
|
22
|
+
* @returns {string} Generated recap content
|
|
23
|
+
*/
|
|
24
|
+
export async function generateRecap(projectPath, featureName, options = {}) {
|
|
25
|
+
const state = loadState(false);
|
|
26
|
+
if (!state || !state.features[featureName]) {
|
|
27
|
+
throw new Error(`Feature '${featureName}' not found in state.json`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const feature = state.features[featureName];
|
|
31
|
+
const outputsPath = join(projectPath, '.morph/features', featureName);
|
|
32
|
+
|
|
33
|
+
// Gather data
|
|
34
|
+
const tasksSummary = getTasksSummary(feature);
|
|
35
|
+
const agentsSummary = getAgentsSummary(feature);
|
|
36
|
+
const checkpointsSummary = getCheckpointsSummary(feature);
|
|
37
|
+
// Decision constraints support (stub - decision-constraint-loader removed as orphaned code)
|
|
38
|
+
const decisions = { decisions: [] };
|
|
39
|
+
const validationResult = await runValidation(projectPath, featureName, { verbose: false });
|
|
40
|
+
|
|
41
|
+
// Contract coverage
|
|
42
|
+
let contractCoverage = null;
|
|
43
|
+
if (validationResult.issues) {
|
|
44
|
+
const contractIssues = validationResult.issues.filter(i => i.message?.includes('Interface') || i.message?.includes('contracts'));
|
|
45
|
+
if (contractIssues.length > 0 || existsSync(join(outputsPath, 'contracts.cs'))) {
|
|
46
|
+
// Extract coverage from validation result
|
|
47
|
+
contractCoverage = extractContractCoverage(validationResult);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Generate markdown
|
|
52
|
+
const recap = buildRecapMarkdown({
|
|
53
|
+
featureName,
|
|
54
|
+
feature,
|
|
55
|
+
tasksSummary,
|
|
56
|
+
agentsSummary,
|
|
57
|
+
checkpointsSummary,
|
|
58
|
+
decisions: decisions.decisions,
|
|
59
|
+
validationResult,
|
|
60
|
+
contractCoverage
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Write to file
|
|
64
|
+
const recapPath = join(outputsPath, 'recap.md');
|
|
65
|
+
if (!existsSync(dirname(recapPath))) {
|
|
66
|
+
mkdirSync(dirname(recapPath), { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
writeFileSync(recapPath, recap, 'utf-8');
|
|
69
|
+
|
|
70
|
+
// Auto-mark recap output as created in state
|
|
71
|
+
try {
|
|
72
|
+
await markOutput(featureName, 'recap');
|
|
73
|
+
} catch {
|
|
74
|
+
// Non-fatal: state may not exist in all contexts
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!options.quiet) {
|
|
78
|
+
console.log(chalk.green(`\n✅ Recap generated: ${recapPath}`));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return recap;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getTasksSummary(feature) {
|
|
85
|
+
// v2: feature.tasks is an array of task objects
|
|
86
|
+
if (Array.isArray(feature.tasks)) {
|
|
87
|
+
const tasks = feature.tasks;
|
|
88
|
+
const completed = tasks.filter(t => t.status === 'completed');
|
|
89
|
+
const pending = tasks.filter(t => t.status === 'pending');
|
|
90
|
+
const inProgress = tasks.filter(t => t.status === 'in_progress');
|
|
91
|
+
return {
|
|
92
|
+
total: tasks.length,
|
|
93
|
+
completed: completed.length,
|
|
94
|
+
pending: pending.length,
|
|
95
|
+
inProgress: inProgress.length,
|
|
96
|
+
percentage: tasks.length > 0 ? Math.round((completed.length / tasks.length) * 100) : 0,
|
|
97
|
+
items: tasks
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// v3: feature.tasks is a counter object {total, completed, inProgress, pending}
|
|
102
|
+
// Use feature.taskList for individual items if available
|
|
103
|
+
const counters = feature.tasks || {};
|
|
104
|
+
const items = feature.taskList || [];
|
|
105
|
+
const total = counters.total || items.length || 0;
|
|
106
|
+
const completedCount = counters.completed ?? items.filter(t => t.status === 'completed').length;
|
|
107
|
+
const pendingCount = counters.pending ?? items.filter(t => t.status === 'pending').length;
|
|
108
|
+
const inProgressCount = counters.inProgress ?? items.filter(t => t.status === 'in_progress').length;
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
total,
|
|
112
|
+
completed: completedCount,
|
|
113
|
+
pending: pendingCount,
|
|
114
|
+
inProgress: inProgressCount,
|
|
115
|
+
percentage: total > 0 ? Math.round((completedCount / total) * 100) : 0,
|
|
116
|
+
items
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getAgentsSummary(feature) {
|
|
121
|
+
return feature.activeAgents || [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getCheckpointsSummary(feature) {
|
|
125
|
+
return (feature.checkpoints || []).map(cp => ({
|
|
126
|
+
timestamp: cp.timestamp,
|
|
127
|
+
note: cp.note,
|
|
128
|
+
tasks: cp.tasksCompleted || []
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function extractContractCoverage(validationResult) {
|
|
133
|
+
// Look for contract compliance data in validation issues
|
|
134
|
+
const interfaceErrors = validationResult.issues.filter(i =>
|
|
135
|
+
i.message?.includes('Interface') && i.level === 'error'
|
|
136
|
+
);
|
|
137
|
+
const methodErrors = validationResult.issues.filter(i =>
|
|
138
|
+
i.message?.includes('Method') && i.message?.includes('missing') && i.level === 'error'
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
missingInterfaces: interfaceErrors.length,
|
|
143
|
+
missingMethods: methodErrors.length,
|
|
144
|
+
hasIssues: interfaceErrors.length > 0 || methodErrors.length > 0
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildRecapMarkdown(data) {
|
|
149
|
+
const { featureName, feature, tasksSummary, agentsSummary, checkpointsSummary, decisions, validationResult, contractCoverage } = data;
|
|
150
|
+
const now = new Date().toISOString().split('T')[0];
|
|
151
|
+
|
|
152
|
+
let md = `# Recap: ${featureName}\n\n`;
|
|
153
|
+
md += `> Generated: ${now} | Phase: ${feature.phase} | Status: ${feature.status}\n\n`;
|
|
154
|
+
md += `---\n\n`;
|
|
155
|
+
|
|
156
|
+
// Progress
|
|
157
|
+
md += `## Progress\n\n`;
|
|
158
|
+
md += `| Metric | Value |\n|--------|-------|\n`;
|
|
159
|
+
md += `| Tasks completed | ${tasksSummary.completed}/${tasksSummary.total} (${tasksSummary.percentage}%) |\n`;
|
|
160
|
+
md += `| Tasks pending | ${tasksSummary.pending} |\n`;
|
|
161
|
+
md += `| Tasks in progress | ${tasksSummary.inProgress} |\n`;
|
|
162
|
+
md += `| Active agents | ${agentsSummary.length} |\n`;
|
|
163
|
+
md += `| Checkpoints | ${checkpointsSummary.length} |\n\n`;
|
|
164
|
+
|
|
165
|
+
// Validation Results
|
|
166
|
+
md += `## Validation Results\n\n`;
|
|
167
|
+
if (validationResult.passed) {
|
|
168
|
+
md += `**Status: PASSED** (${validationResult.warnings} warnings)\n\n`;
|
|
169
|
+
} else {
|
|
170
|
+
md += `**Status: FAILED** (${validationResult.errors} errors, ${validationResult.warnings} warnings)\n\n`;
|
|
171
|
+
if (validationResult.issues.length > 0) {
|
|
172
|
+
md += `| Level | Issue |\n|-------|-------|\n`;
|
|
173
|
+
for (const issue of validationResult.issues.slice(0, 15)) {
|
|
174
|
+
md += `| ${issue.level} | ${issue.message} |\n`;
|
|
175
|
+
}
|
|
176
|
+
md += `\n`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Contract Coverage
|
|
181
|
+
if (contractCoverage) {
|
|
182
|
+
md += `## Contract Compliance\n\n`;
|
|
183
|
+
if (contractCoverage.hasIssues) {
|
|
184
|
+
md += `- Missing interfaces: ${contractCoverage.missingInterfaces}\n`;
|
|
185
|
+
md += `- Missing methods: ${contractCoverage.missingMethods}\n\n`;
|
|
186
|
+
} else {
|
|
187
|
+
md += `All contracts implemented.\n\n`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Decisions
|
|
192
|
+
if (decisions.length > 0) {
|
|
193
|
+
md += `## Decisions Applied\n\n`;
|
|
194
|
+
for (const d of decisions) {
|
|
195
|
+
if (d.title) {
|
|
196
|
+
md += `- **${d.title}**: ${d.decision.slice(0, 100)}${d.decision.length > 100 ? '...' : ''}\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
md += `\n`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Agents
|
|
203
|
+
if (agentsSummary.length > 0) {
|
|
204
|
+
md += `## Active Agents\n\n`;
|
|
205
|
+
md += agentsSummary.map(a => `- ${a}`).join('\n');
|
|
206
|
+
md += `\n\n`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Checkpoints
|
|
210
|
+
if (checkpointsSummary.length > 0) {
|
|
211
|
+
md += `## Checkpoints\n\n`;
|
|
212
|
+
for (const cp of checkpointsSummary) {
|
|
213
|
+
md += `- ${cp.timestamp}: ${cp.note}\n`;
|
|
214
|
+
}
|
|
215
|
+
md += `\n`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Tasks Detail
|
|
219
|
+
if (tasksSummary.items.length > 0) {
|
|
220
|
+
md += `## Tasks\n\n`;
|
|
221
|
+
md += `| ID | Title | Status |\n|----|-------|--------|\n`;
|
|
222
|
+
for (const task of tasksSummary.items) {
|
|
223
|
+
const statusIcon = task.status === 'completed' ? '✅' : task.status === 'in_progress' ? '🔄' : '⏳';
|
|
224
|
+
md += `| ${task.id} | ${task.title || '-'} | ${statusIcon} ${task.status} |\n`;
|
|
225
|
+
}
|
|
226
|
+
md += `\n`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
md += `---\n\n*Generated by MORPH-SPEC Recap Generator*\n`;
|
|
230
|
+
|
|
231
|
+
return md;
|
|
232
|
+
}
|
|
@@ -96,6 +96,21 @@ export function checkPrerequisites(mcpEntry) {
|
|
|
96
96
|
return results;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Patch MCP config for the current platform.
|
|
101
|
+
* On Windows, npx must be invoked via `cmd /C` to avoid ENOENT errors.
|
|
102
|
+
* @param {Object} config - MCP server config { command, args, env? }
|
|
103
|
+
* @returns {Object} Patched config (new object, original unmodified)
|
|
104
|
+
*/
|
|
105
|
+
export function patchConfigForPlatform(config) {
|
|
106
|
+
if (process.platform !== 'win32' || config.command !== 'npx') return config;
|
|
107
|
+
return {
|
|
108
|
+
...config,
|
|
109
|
+
command: 'cmd',
|
|
110
|
+
args: ['/C', 'npx', ...(config.args || [])]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
99
114
|
/**
|
|
100
115
|
* Install auto-installable MCPs (no credentials, no prerequisites)
|
|
101
116
|
* @param {string} targetPath - Project root directory
|
|
@@ -106,12 +121,12 @@ export async function installAutoMcps(targetPath, mcpsToInstall) {
|
|
|
106
121
|
const servers = {};
|
|
107
122
|
|
|
108
123
|
for (const [name, entry] of Object.entries(mcpsToInstall)) {
|
|
109
|
-
|
|
124
|
+
let config = { ...entry.install.config };
|
|
110
125
|
// Only include env if it has actual values
|
|
111
126
|
if (config.env && Object.values(config.env).every(v => !v)) {
|
|
112
127
|
delete config.env;
|
|
113
128
|
}
|
|
114
|
-
servers[name] = config;
|
|
129
|
+
servers[name] = patchConfigForPlatform(config);
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
return installMcpServers(targetPath, servers);
|
|
@@ -126,7 +141,7 @@ export async function installAutoMcps(targetPath, mcpsToInstall) {
|
|
|
126
141
|
* @returns {Promise<Object>} Result from installMcpServers
|
|
127
142
|
*/
|
|
128
143
|
export async function installMcpWithCredentials(targetPath, name, mcpEntry, credentialValues) {
|
|
129
|
-
const config = { ...mcpEntry.install.config };
|
|
144
|
+
const config = patchConfigForPlatform({ ...mcpEntry.install.config });
|
|
130
145
|
config.env = { ...config.env, ...credentialValues };
|
|
131
146
|
return installMcpServers(targetPath, { [name]: config });
|
|
132
147
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/scripts/global-install.js
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { mkdir, copyFile } from 'fs/promises';
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const FRAMEWORK_DIR = join(__dirname, '..', '..', 'framework');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Install morph-init skill to a Claude global skills directory.
|
|
13
|
+
* @param {string} claudeDir - target ~/.claude dir (defaults to real ~/.claude)
|
|
14
|
+
*/
|
|
15
|
+
export async function installGlobalSkill(claudeDir = join(homedir(), '.claude')) {
|
|
16
|
+
const src = join(FRAMEWORK_DIR, 'skills', 'level-0-meta', 'morph-init', 'SKILL.md');
|
|
17
|
+
const destDir = join(claudeDir, 'skills', 'morph-init');
|
|
18
|
+
const dest = join(destDir, 'SKILL.md');
|
|
19
|
+
|
|
20
|
+
if (!existsSync(src)) {
|
|
21
|
+
console.warn(`morph-init skill not found at ${src} — skipping global install`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await mkdir(destDir, { recursive: true });
|
|
26
|
+
await copyFile(src, dest);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Run as postinstall script
|
|
30
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
31
|
+
installGlobalSkill()
|
|
32
|
+
.then(() => console.log('✓ morph-init skill installed to ~/.claude/skills/'))
|
|
33
|
+
.catch(e => console.warn('Could not install morph-init skill globally:', e.message));
|
|
34
|
+
}
|