@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.
Files changed (138) hide show
  1. package/.morph/analytics/threads-log.jsonl +54 -0
  2. package/.morph/state.json +198 -0
  3. package/LICENSE +1 -2
  4. package/README.md +379 -414
  5. package/bin/morph-spec.js +57 -403
  6. package/bin/validate.js +2 -26
  7. package/claude-plugin.json +2 -2
  8. package/docs/ARCHITECTURE.md +43 -46
  9. package/docs/CHEATSHEET.md +203 -221
  10. package/docs/COMMAND-FLOWS.md +319 -289
  11. package/docs/QUICKSTART.md +2 -8
  12. package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +2 -0
  13. package/docs/plans/2026-02-22-claude-settings.md +2 -0
  14. package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +2 -0
  15. package/docs/plans/2026-02-22-morph-spec-next.md +2 -0
  16. package/docs/plans/2026-02-22-native-alignment-design.md +2 -0
  17. package/docs/plans/2026-02-22-native-alignment-impl.md +2 -0
  18. package/docs/plans/2026-02-22-native-enrichment-design.md +2 -0
  19. package/docs/plans/2026-02-22-native-enrichment.md +2 -0
  20. package/docs/plans/2026-02-23-ddd-architecture-refactor.md +2 -0
  21. package/docs/plans/2026-02-23-ddd-nextsteps.md +2 -0
  22. package/docs/plans/2026-02-23-infra-architect-refactor.md +2 -0
  23. package/docs/plans/2026-02-23-nextjs-code-review-design.md +2 -1
  24. package/docs/plans/2026-02-23-nextjs-code-review-impl.md +2 -0
  25. package/docs/plans/2026-02-23-nextjs-standards-design.md +2 -1
  26. package/docs/plans/2026-02-23-nextjs-standards-impl.md +2 -0
  27. package/docs/plans/2026-02-24-cli-radical-simplification.md +592 -0
  28. package/docs/plans/2026-02-24-framework-failure-points.md +125 -0
  29. package/docs/plans/2026-02-24-morph-init-design.md +337 -0
  30. package/docs/plans/2026-02-24-morph-init-impl.md +1269 -0
  31. package/docs/plans/2026-02-24-tutorial-command-design.md +71 -0
  32. package/docs/plans/2026-02-24-tutorial-command.md +298 -0
  33. package/framework/CLAUDE.md +2 -2
  34. package/framework/commands/morph-proposal.md +3 -3
  35. package/framework/hooks/README.md +11 -10
  36. package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
  37. package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
  38. package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +4 -55
  39. package/framework/hooks/claude-code/session-start/inject-morph-context.js +20 -5
  40. package/framework/hooks/claude-code/statusline.py +6 -1
  41. package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
  42. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
  43. package/framework/hooks/dev/check-sync-health.js +117 -0
  44. package/framework/hooks/dev/guard-version-numbers.js +57 -0
  45. package/framework/hooks/dev/sync-standards-registry.js +60 -0
  46. package/framework/hooks/dev/sync-template-registry.js +60 -0
  47. package/framework/hooks/dev/validate-skill-format.js +70 -0
  48. package/framework/hooks/dev/validate-standard-format.js +73 -0
  49. package/framework/hooks/shared/payload-utils.js +39 -0
  50. package/framework/hooks/shared/state-reader.js +25 -1
  51. package/framework/rules/morph-workflow.md +1 -1
  52. package/framework/skills/level-0-meta/morph-init/SKILL.md +216 -0
  53. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
  54. package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +4 -4
  55. package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
  56. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +192 -191
  57. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +181 -180
  58. package/framework/skills/level-1-workflows/phase-design/SKILL.md +339 -338
  59. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +254 -253
  60. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +168 -170
  61. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +284 -283
  62. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +246 -245
  63. package/framework/templates/examples/design-system-examples.md +1 -1
  64. package/framework/templates/ui/FluentDesignTheme.cs +1 -1
  65. package/framework/templates/ui/MudTheme.cs +1 -1
  66. package/framework/templates/ui/design-system.css +1 -1
  67. package/package.json +4 -2
  68. package/scripts/bump-version.js +248 -0
  69. package/scripts/install-dev-hooks.js +138 -0
  70. package/src/commands/agents/index.js +1 -2
  71. package/src/commands/index.js +13 -16
  72. package/src/commands/project/doctor.js +100 -14
  73. package/src/commands/project/index.js +7 -10
  74. package/src/commands/project/init.js +398 -555
  75. package/src/commands/project/install-plugin-cmd.js +28 -0
  76. package/src/commands/project/setup-infra-cmd.js +12 -0
  77. package/src/commands/project/tutorial.js +115 -0
  78. package/src/commands/project/update.js +22 -37
  79. package/src/commands/state/approve.js +213 -221
  80. package/src/commands/state/index.js +0 -1
  81. package/src/commands/state/state.js +337 -365
  82. package/src/commands/templates/index.js +0 -4
  83. package/src/commands/trust/trust.js +1 -93
  84. package/src/commands/utils/index.js +1 -5
  85. package/src/commands/validation/index.js +1 -5
  86. package/src/core/registry/command-registry.js +11 -285
  87. package/src/core/state/state-manager.js +5 -2
  88. package/src/lib/detectors/index.js +81 -87
  89. package/src/lib/detectors/structure-detector.js +275 -273
  90. package/src/lib/generators/recap-generator.js +232 -225
  91. package/src/lib/installers/mcp-installer.js +18 -3
  92. package/src/scripts/global-install.js +34 -0
  93. package/src/scripts/install-plugin.js +126 -0
  94. package/src/scripts/setup-infra.js +203 -0
  95. package/src/utils/agents-installer.js +10 -1
  96. package/src/utils/hooks-installer.js +70 -17
  97. package/CLAUDE.md +0 -77
  98. package/docs/claude-alignment-report.md +0 -137
  99. package/docs/examples/order-management/contracts.cs +0 -84
  100. package/docs/examples/order-management/proposal.md +0 -24
  101. package/docs/examples/order-management/spec.md +0 -162
  102. package/src/commands/feature/create-story.js +0 -362
  103. package/src/commands/feature/index.js +0 -6
  104. package/src/commands/feature/shard-spec.js +0 -225
  105. package/src/commands/feature/sprint-status.js +0 -250
  106. package/src/commands/generation/generate-onboarding.js +0 -169
  107. package/src/commands/generation/generate.js +0 -276
  108. package/src/commands/generation/index.js +0 -5
  109. package/src/commands/learning/capture-pattern.js +0 -121
  110. package/src/commands/learning/index.js +0 -5
  111. package/src/commands/learning/search-patterns.js +0 -126
  112. package/src/commands/mcp/mcp.js +0 -102
  113. package/src/commands/project/changes.js +0 -66
  114. package/src/commands/project/cost.js +0 -179
  115. package/src/commands/project/detect.js +0 -114
  116. package/src/commands/project/diff.js +0 -278
  117. package/src/commands/project/revert.js +0 -173
  118. package/src/commands/project/standards.js +0 -80
  119. package/src/commands/project/sync.js +0 -167
  120. package/src/commands/project/update-agents.js +0 -23
  121. package/src/commands/state/rollback-phase.js +0 -185
  122. package/src/commands/templates/template-customize.js +0 -87
  123. package/src/commands/templates/template-list.js +0 -114
  124. package/src/commands/templates/template-show.js +0 -129
  125. package/src/commands/templates/template-validate.js +0 -91
  126. package/src/commands/utils/troubleshoot.js +0 -222
  127. package/src/commands/validation/analyze-blazor-concurrency.js +0 -193
  128. package/src/commands/validation/lint-fluent.js +0 -352
  129. package/src/commands/validation/validate-blazor-state.js +0 -210
  130. package/src/commands/validation/validate-blazor.js +0 -156
  131. package/src/commands/validation/validate-css.js +0 -84
  132. package/src/lib/detectors/conversation-analyzer.js +0 -163
  133. package/src/lib/learning/index.js +0 -7
  134. package/src/lib/learning/learning-system.js +0 -520
  135. package/src/lib/troubleshooting/index.js +0 -8
  136. package/src/lib/troubleshooting/troubleshoot-grep.js +0 -198
  137. package/src/lib/troubleshooting/troubleshoot-index.js +0 -144
  138. package/src/llm/environment-detector.js +0 -43
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Version Bump Script for MORPH-SPEC Framework
5
+ *
6
+ * Updates the version number across all files that contain it.
7
+ * Single source of truth: package.json
8
+ *
9
+ * Usage:
10
+ * node scripts/bump-version.js patch # 4.8.0 → 4.8.1
11
+ * node scripts/bump-version.js minor # 4.8.0 → 4.9.0
12
+ * node scripts/bump-version.js major # 4.8.0 → 5.0.0
13
+ * node scripts/bump-version.js 4.9.0 # explicit version
14
+ * node scripts/bump-version.js --dry-run patch # preview changes
15
+ *
16
+ * npm script:
17
+ * npm run version:bump -- patch
18
+ * npm run version:bump -- --dry-run minor
19
+ */
20
+
21
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
22
+ import { join, dirname } from 'path';
23
+ import { fileURLToPath } from 'url';
24
+ import { glob } from 'glob';
25
+
26
+ // ── Exported helpers (used by tests) ─────────────────────────────────
27
+
28
+ /**
29
+ * Compute a new version from a current version and bump type.
30
+ * @param {string} current - Current semver (e.g. "4.8.0")
31
+ * @param {string} type - "patch", "minor", "major", or explicit "X.Y.Z"
32
+ * @returns {string} New version string
33
+ */
34
+ export function bumpVersion(current, type) {
35
+ const [major, minor, patch] = current.split('.').map(Number);
36
+ switch (type) {
37
+ case 'major': return `${major + 1}.0.0`;
38
+ case 'minor': return `${major}.${minor + 1}.0`;
39
+ case 'patch': return `${major}.${minor}.${patch + 1}`;
40
+ default: {
41
+ if (/^\d+\.\d+\.\d+$/.test(type)) return type;
42
+ throw new Error(`Invalid version or bump type: "${type}". Expected: patch, minor, major, or X.Y.Z`);
43
+ }
44
+ }
45
+ }
46
+
47
+ function escapeRegex(str) {
48
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
49
+ }
50
+
51
+ /**
52
+ * Build the list of file updates for a version bump.
53
+ * @param {string} rootDir - Project root directory
54
+ * @param {string} currentVersion - Current version string
55
+ * @param {string} newVersion - Target version string
56
+ * @returns {Array<{file: string, replacements: Array<{pattern: RegExp, replacement: string}>}>}
57
+ */
58
+ export function buildFileUpdates(rootDir, currentVersion, newVersion) {
59
+ const cv = escapeRegex(currentVersion);
60
+
61
+ const updates = [
62
+ {
63
+ file: 'package.json',
64
+ replacements: [
65
+ { pattern: new RegExp(`"version":\\s*"${cv}"`), replacement: `"version": "${newVersion}"` },
66
+ ],
67
+ },
68
+ {
69
+ file: 'claude-plugin.json',
70
+ replacements: [
71
+ { pattern: new RegExp(`"version":\\s*"${cv}"`), replacement: `"version": "${newVersion}"` },
72
+ ],
73
+ },
74
+ {
75
+ file: 'README.md',
76
+ replacements: [
77
+ { pattern: new RegExp(`\\*\\*Version:\\*\\*\\s*${cv}`), replacement: `**Version:** ${newVersion}` },
78
+ { pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
79
+ ],
80
+ },
81
+ {
82
+ file: 'docs/ARCHITECTURE.md',
83
+ replacements: [
84
+ { pattern: new RegExp(`morph-spec v${cv}`, 'g'), replacement: `morph-spec v${newVersion}` },
85
+ ],
86
+ },
87
+ {
88
+ file: 'docs/CHEATSHEET.md',
89
+ replacements: [
90
+ { pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
91
+ ],
92
+ },
93
+ {
94
+ file: 'docs/COMMAND-FLOWS.md',
95
+ replacements: [
96
+ { pattern: new RegExp(`Version:\\s*${cv}`), replacement: `Version: ${newVersion}` },
97
+ { pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
98
+ ],
99
+ },
100
+ {
101
+ file: 'docs/QUICKSTART.md',
102
+ replacements: [
103
+ { pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
104
+ ],
105
+ },
106
+ {
107
+ file: 'framework/hooks/dev/guard-version-numbers.js',
108
+ replacements: [
109
+ { pattern: new RegExp(`MORPH-SPEC v${cv}`), replacement: `MORPH-SPEC v${newVersion}` },
110
+ ],
111
+ },
112
+ ];
113
+
114
+ // Skill files (cliVersion in YAML frontmatter)
115
+ const skillFiles = glob.sync('framework/skills/level-1-workflows/*/SKILL.md', { cwd: rootDir });
116
+ for (const sf of skillFiles) {
117
+ updates.push({
118
+ file: sf,
119
+ replacements: [
120
+ { pattern: new RegExp(`cliVersion:\\s*"${cv}"`), replacement: `cliVersion: "${newVersion}"` },
121
+ ],
122
+ });
123
+ }
124
+
125
+ return updates;
126
+ }
127
+
128
+ /**
129
+ * Apply version updates to files on disk.
130
+ * @param {string} rootDir - Project root directory
131
+ * @param {Array} fileUpdates - From buildFileUpdates()
132
+ * @param {{ dryRun?: boolean }} options
133
+ * @returns {{ updatedCount: number, replacementCount: number, warnings: string[] }}
134
+ */
135
+ export function applyUpdates(rootDir, fileUpdates, { dryRun = false } = {}) {
136
+ let updatedCount = 0;
137
+ let replacementCount = 0;
138
+ const warnings = [];
139
+
140
+ for (const { file, replacements } of fileUpdates) {
141
+ const fullPath = join(rootDir, file);
142
+
143
+ if (!existsSync(fullPath)) {
144
+ warnings.push(`SKIP ${file} (file not found)`);
145
+ continue;
146
+ }
147
+
148
+ let content = readFileSync(fullPath, 'utf-8');
149
+ let fileChanged = false;
150
+
151
+ for (const { pattern, replacement } of replacements) {
152
+ if (pattern.test(content)) {
153
+ content = content.replace(pattern, replacement);
154
+ fileChanged = true;
155
+ replacementCount++;
156
+ }
157
+ }
158
+
159
+ if (fileChanged) {
160
+ if (!dryRun) {
161
+ writeFileSync(fullPath, content, 'utf-8');
162
+ }
163
+ updatedCount++;
164
+ } else {
165
+ warnings.push(`${file} (no matches — pattern may have changed)`);
166
+ }
167
+ }
168
+
169
+ return { updatedCount, replacementCount, warnings };
170
+ }
171
+
172
+ // ── CLI entry point ──────────────────────────────────────────────────
173
+
174
+ const __dirname = dirname(fileURLToPath(import.meta.url));
175
+ const isMainModule = process.argv[1]?.replace(/\\/g, '/').endsWith('scripts/bump-version.js');
176
+
177
+ if (isMainModule) {
178
+ const ROOT = join(__dirname, '..');
179
+ const args = process.argv.slice(2);
180
+ const dryRun = args.includes('--dry-run');
181
+ const filtered = args.filter(a => a !== '--dry-run');
182
+
183
+ if (filtered.length === 0) {
184
+ console.error(
185
+ 'Usage: node scripts/bump-version.js [--dry-run] <patch|minor|major|X.Y.Z>'
186
+ );
187
+ process.exit(1);
188
+ }
189
+
190
+ const input = filtered[0];
191
+ const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
192
+ const currentVersion = pkg.version;
193
+
194
+ let newVersion;
195
+ try {
196
+ newVersion = bumpVersion(currentVersion, input);
197
+ } catch (err) {
198
+ console.error(err.message);
199
+ process.exit(1);
200
+ }
201
+
202
+ if (newVersion === currentVersion) {
203
+ console.log(`Version is already ${currentVersion}. Nothing to do.`);
204
+ process.exit(0);
205
+ }
206
+
207
+ console.log(`\nBumping version: ${currentVersion} → ${newVersion}`);
208
+ if (dryRun) console.log('(dry run — no files will be modified)\n');
209
+ else console.log('');
210
+
211
+ const fileUpdates = buildFileUpdates(ROOT, currentVersion, newVersion);
212
+ const result = applyUpdates(ROOT, fileUpdates, { dryRun });
213
+
214
+ for (const file of fileUpdates) {
215
+ const fullPath = join(ROOT, file.file);
216
+ if (existsSync(fullPath)) {
217
+ const content = readFileSync(fullPath, 'utf-8');
218
+ const hasMatch = file.replacements.some(r =>
219
+ dryRun
220
+ ? new RegExp(escapeRegex(currentVersion)).test(content)
221
+ : new RegExp(escapeRegex(newVersion)).test(content)
222
+ );
223
+ if (hasMatch) console.log(` ✓ ${file.file}`);
224
+ }
225
+ }
226
+
227
+ console.log('');
228
+ if (result.warnings.length > 0) {
229
+ console.log('Warnings:');
230
+ for (const w of result.warnings) console.log(` ⚠ ${w}`);
231
+ console.log('');
232
+ }
233
+
234
+ console.log(
235
+ `${dryRun ? 'Would update' : 'Updated'} ${result.updatedCount} files (${result.replacementCount} replacements).`
236
+ );
237
+
238
+ if (dryRun) {
239
+ console.log('\nRe-run without --dry-run to apply changes.');
240
+ } else {
241
+ console.log(`\nVersion is now ${newVersion}.`);
242
+ console.log('Next steps:');
243
+ console.log(' 1. Review changes: git diff');
244
+ console.log(` 2. Commit: git commit -am "chore: bump version to ${newVersion}"`);
245
+ console.log(` 3. Tag: git tag v${newVersion}`);
246
+ console.log(' 4. Publish: npm publish');
247
+ }
248
+ }
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Dev Hooks Installer for MORPH-SPEC Framework Codebase
5
+ *
6
+ * Installs development-only hooks into .claude/settings.local.json.
7
+ * These hooks enforce quality standards when developing the framework itself:
8
+ * - Version number guard (no hardcoded versions in .md files)
9
+ * - Standard format validator (required metadata headers)
10
+ * - Skill frontmatter validator (required YAML fields)
11
+ * - Template registry sync advisory
12
+ * - Standards registry sync advisory
13
+ * - Sync health check advisory on Stop
14
+ *
15
+ * Usage: node scripts/install-dev-hooks.js
16
+ *
17
+ * Dev hooks use _morph_dev:true marker (not _morph:true) so they survive
18
+ * `morph-spec init/update` reinstalls, which only remove _morph:true entries.
19
+ */
20
+
21
+ import { readFile, writeFile, mkdir } from 'fs/promises';
22
+ import { existsSync } from 'fs';
23
+ import { join } from 'path';
24
+
25
+ /**
26
+ * Dev hook definitions.
27
+ * Commands reference framework/hooks/dev/ directly (not .morph/framework/)
28
+ * since they only run in the framework repo where this path exists.
29
+ */
30
+ const DEV_HOOKS = [
31
+ {
32
+ event: 'PreToolUse',
33
+ matcher: 'Write|Edit',
34
+ hooks: [
35
+ { type: 'command', command: 'node framework/hooks/dev/guard-version-numbers.js' },
36
+ { type: 'command', command: 'node framework/hooks/dev/validate-standard-format.js' },
37
+ { type: 'command', command: 'node framework/hooks/dev/validate-skill-format.js' },
38
+ ]
39
+ },
40
+ {
41
+ event: 'PostToolUse',
42
+ matcher: 'Write',
43
+ hooks: [
44
+ { type: 'command', command: 'node framework/hooks/dev/sync-template-registry.js' },
45
+ { type: 'command', command: 'node framework/hooks/dev/sync-standards-registry.js' },
46
+ ]
47
+ },
48
+ {
49
+ event: 'Stop',
50
+ matcher: null,
51
+ hooks: [
52
+ { type: 'command', command: 'node framework/hooks/dev/check-sync-health.js' },
53
+ ]
54
+ },
55
+ ];
56
+
57
+ /**
58
+ * Install dev hooks into .claude/settings.local.json.
59
+ * Preserves _morph:true entries and user hooks.
60
+ * Removes old _morph_dev:true entries before installing (clean slate).
61
+ *
62
+ * @param {string} [projectPath] - Project root (defaults to cwd)
63
+ * @returns {Promise<{ installed: number }>}
64
+ */
65
+ export async function installDevHooks(projectPath) {
66
+ const root = projectPath || process.cwd();
67
+ const claudeDir = join(root, '.claude');
68
+ const settingsPath = join(claudeDir, 'settings.local.json');
69
+
70
+ // Read existing settings
71
+ let settings = {};
72
+ if (existsSync(settingsPath)) {
73
+ try {
74
+ settings = JSON.parse(await readFile(settingsPath, 'utf-8'));
75
+ } catch {
76
+ settings = {};
77
+ }
78
+ }
79
+
80
+ settings.hooks = settings.hooks || {};
81
+
82
+ // Remove old dev hooks (clean slate)
83
+ for (const [event, entries] of Object.entries(settings.hooks)) {
84
+ if (!Array.isArray(entries)) continue;
85
+ settings.hooks[event] = entries.filter(entry => entry._morph_dev !== true);
86
+ if (settings.hooks[event].length === 0) {
87
+ delete settings.hooks[event];
88
+ }
89
+ }
90
+
91
+ // Install dev hooks
92
+ let installed = 0;
93
+ for (const hookDef of DEV_HOOKS) {
94
+ const { event, matcher, hooks } = hookDef;
95
+
96
+ if (!settings.hooks[event]) {
97
+ settings.hooks[event] = [];
98
+ }
99
+
100
+ const entry = { _morph_dev: true };
101
+ if (matcher) {
102
+ entry.matcher = matcher;
103
+ }
104
+ entry.hooks = hooks.map(h => ({ type: h.type, command: h.command }));
105
+
106
+ settings.hooks[event].push(entry);
107
+ installed++;
108
+ }
109
+
110
+ // Ensure .claude directory exists
111
+ if (!existsSync(claudeDir)) {
112
+ await mkdir(claudeDir, { recursive: true });
113
+ }
114
+
115
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
116
+ return { installed };
117
+ }
118
+
119
+ /**
120
+ * Get the dev hooks definitions (for testing/inspection).
121
+ * @returns {Array}
122
+ */
123
+ export function getDevHookDefinitions() {
124
+ return DEV_HOOKS;
125
+ }
126
+
127
+ // CLI entry point
128
+ const isMainModule = process.argv[1]?.replace(/\\/g, '/').endsWith('scripts/install-dev-hooks.js');
129
+ if (isMainModule) {
130
+ try {
131
+ const result = await installDevHooks();
132
+ console.log(`Installed ${result.installed} dev hooks into .claude/settings.local.json`);
133
+ console.log('Dev hooks are active for framework development only (_morph_dev: true).');
134
+ } catch (err) {
135
+ console.error('Failed to install dev hooks:', err.message);
136
+ process.exit(1);
137
+ }
138
+ }
@@ -1,4 +1,3 @@
1
1
  /**
2
- * Agent Team Commands
2
+ * Agent Commands — no active commands after CLI simplification
3
3
  */
4
- export { spawnTeamCommand } from './spawn-team.js';
@@ -1,16 +1,13 @@
1
- /**
2
- * All Commands - Category Exports
3
- *
4
- * Commands organized by functional category.
5
- */
6
-
7
- export * from './feature/index.js';
8
- export * from './state/index.js';
9
- export * from './generation/index.js';
10
- export * from './validation/index.js';
11
- export * from './templates/index.js';
12
- export * from './project/index.js';
13
- export * from './learning/index.js';
14
- export * from './tasks/index.js';
15
- export * from './agents/index.js';
16
- export * from './utils/index.js';
1
+ /**
2
+ * All Commands - Category Exports
3
+ *
4
+ * Commands organized by functional category.
5
+ */
6
+
7
+ export * from './state/index.js';
8
+ export * from './validation/index.js';
9
+ export * from './templates/index.js';
10
+ export * from './project/index.js';
11
+ export * from './tasks/index.js';
12
+ export * from './agents/index.js';
13
+ export * from './utils/index.js';
@@ -1,6 +1,6 @@
1
1
  import { join } from 'path';
2
2
  import { execSync } from 'child_process';
3
- import { platform } from 'os';
3
+ import { platform, homedir } from 'os';
4
4
  import fs from 'fs-extra';
5
5
  import chalk from 'chalk';
6
6
  import { logger } from '../../utils/logger.js';
@@ -13,6 +13,18 @@ import {
13
13
  import { resetMorphSettings } from '../../utils/claude-settings-manager.js';
14
14
 
15
15
  const isWindows = platform() === 'win32';
16
+ /**
17
+ * Check which required Claude Code plugins are installed.
18
+ * @param {Object} pluginsData - The `plugins` object from installed_plugins.json
19
+ * @returns {{ superpowers: boolean, context7: boolean }}
20
+ */
21
+ export function checkInstalledPlugins(pluginsData) {
22
+ const REQUIRED = ['superpowers', 'context7'];
23
+ return Object.fromEntries(
24
+ REQUIRED.map(name => [name, `${name}@claude-plugins-official` in (pluginsData || {})])
25
+ );
26
+ }
27
+
16
28
 
17
29
  /**
18
30
  * Check if morph-spec command is in PATH
@@ -72,18 +84,20 @@ const REQUIRED_LIB_FILES = [
72
84
  'src/lib/trust/trust-manager.js'
73
85
  ];
74
86
 
75
- // command files
87
+ // command files (current CLI surface after radical simplification)
76
88
  const REQUIRED_COMMAND_FILES = [
77
- 'src/commands/agents/agents-fuse.js',
78
- 'src/commands/analytics/analytics.js',
79
- 'src/commands/context/context-prime.js',
80
- 'src/commands/context/core-four.js',
81
- 'src/commands/mcp/mcp.js',
82
- 'src/commands/agents/micro-agent.js',
83
- 'src/commands/agents/squad-template.js',
84
- 'src/commands/threads/thread-template.js',
85
- 'src/commands/threads/threads.js',
86
- 'src/commands/trust/trust.js'
89
+ 'src/commands/project/init.js',
90
+ 'src/commands/project/update.js',
91
+ 'src/commands/project/doctor.js',
92
+ 'src/commands/project/status.js',
93
+ 'src/commands/project/tutorial.js',
94
+ 'src/commands/state/state.js',
95
+ 'src/commands/state/advance-phase.js',
96
+ 'src/commands/state/approve.js',
97
+ 'src/commands/tasks/task.js',
98
+ 'src/commands/trust/trust.js',
99
+ 'src/commands/validation/validate-feature.js',
100
+ 'src/commands/templates/template-render.js'
87
101
  ];
88
102
 
89
103
  // HOP templates (meta-prompts)
@@ -227,9 +241,9 @@ async function doctorFullCommand(frameworkRoot) {
227
241
  if (await pathExists(statePath)) {
228
242
  try {
229
243
  const state = JSON.parse(await fs.readFile(statePath, 'utf8'));
230
- const isCurrent = state.version === '3.0.0';
244
+ const isCurrent = state.version === '5.0.0';
231
245
  check(`state.json schema (${state.version})`, isCurrent, true,
232
- isCurrent ? '' : 'run: morph-spec state init --force');
246
+ isCurrent ? '' : `expected 5.0.0, found ${state.version} run any CLI command to auto-migrate`);
233
247
  } catch {
234
248
  check('state.json parse', false, false, 'invalid JSON');
235
249
  }
@@ -558,6 +572,78 @@ export async function doctorCommand(options = {}) {
558
572
  checks.push({ name: '.claude/agents/', status: 'warn', msg: 'missing — run morph-spec update' });
559
573
  hasWarnings = true;
560
574
  }
575
+ // ── Hook health: verify hook scripts exist ───────────────────────────────
576
+ const settingsLocalPath = join(claudePath, 'settings.local.json');
577
+ if (await pathExists(settingsLocalPath)) {
578
+ try {
579
+ const settings = JSON.parse(await fs.readFile(settingsLocalPath, 'utf8'));
580
+ const hookCommands = [];
581
+ for (const eventHooks of Object.values(settings.hooks || {})) {
582
+ for (const group of (Array.isArray(eventHooks) ? eventHooks : [])) {
583
+ for (const h of (group.hooks || [])) {
584
+ if (h.type === 'command' && h.command) hookCommands.push(h.command);
585
+ }
586
+ }
587
+ }
588
+ const hookPathRe = /\$CLAUDE_PROJECT_DIR\/([^\s"]+\.js)/;
589
+ const missingHooks = [];
590
+ for (const cmd of hookCommands) {
591
+ const m = cmd.match(hookPathRe);
592
+ if (m && !(await pathExists(join(targetPath, m[1])))) {
593
+ missingHooks.push(m[1].split('/').pop());
594
+ }
595
+ }
596
+ if (missingHooks.length === 0) {
597
+ checks.push({ name: 'hook scripts', status: 'ok', msg: `${hookCommands.length} hooks configured` });
598
+ } else {
599
+ checks.push({ name: 'hook scripts', status: 'warn', msg: `missing: ${missingHooks.join(', ')} — run morph-spec update` });
600
+ hasWarnings = true;
601
+ }
602
+ } catch {
603
+ checks.push({ name: 'hook scripts', status: 'warn', msg: 'could not parse settings.local.json' });
604
+ hasWarnings = true;
605
+ }
606
+ } else {
607
+ checks.push({ name: 'hook scripts', status: 'warn', msg: 'settings.local.json missing — run morph-spec init' });
608
+ hasWarnings = true;
609
+ }
610
+
611
+ // ── Skills cliVersion: warn if installed skills reference older CLI ──────
612
+ try {
613
+ const cliVersion = getInstalledCLIVersion();
614
+ if (cliVersion) {
615
+ const skillDirsForVersion = await fs.readdir(skillsPath);
616
+ const staleSkills = [];
617
+ for (const skillDir of skillDirsForVersion) {
618
+ const skillMdPath = join(skillsPath, skillDir, 'SKILL.md');
619
+ if (!(await pathExists(skillMdPath))) continue;
620
+ const content = await fs.readFile(skillMdPath, 'utf8');
621
+ const m = content.match(/^cliVersion:\s*"?([^"\n]+)"?/m);
622
+ if (m && m[1].trim() !== cliVersion) {
623
+ staleSkills.push(skillDir);
624
+ }
625
+ }
626
+ if (staleSkills.length === 0) {
627
+ checks.push({ name: 'skill cliVersion', status: 'ok' });
628
+ } else {
629
+ checks.push({ name: 'skill cliVersion', status: 'warn', msg: `${staleSkills.length} skill(s) behind CLI v${cliVersion} — run morph-spec update` });
630
+ hasWarnings = true;
631
+ }
632
+ }
633
+ } catch { /* non-fatal */ }
634
+
635
+ // ── Claude Code Plugins ──────────────────────────────────────────────────
636
+ console.log(chalk.cyan('\n Claude Code Plugins (required for /morph-init)'));
637
+ const pluginsFile = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
638
+ let pluginData = { plugins: {} };
639
+ try {
640
+ pluginData = JSON.parse(await fs.readFile(pluginsFile, 'utf8'));
641
+ } catch { /* file may not exist */ }
642
+ const pluginStatus = checkInstalledPlugins(pluginData.plugins || {});
643
+ check('superpowers plugin', pluginStatus.superpowers, false,
644
+ 'Run: morph-spec install-plugin superpowers');
645
+ check('context7 plugin', pluginStatus.context7, false,
646
+ 'Run: morph-spec install-plugin context7');
561
647
  } else {
562
648
  checks.push({ name: '.claude/', status: 'warn', msg: 'incomplete structure' });
563
649
  }
@@ -1,10 +1,7 @@
1
- /**
2
- * Project-Level Commands
3
- */
4
- export { initCommand } from './init.js';
5
- export { doctorCommand } from './doctor.js';
6
- export { detectCommand } from './detect.js';
7
- export { detectWorkflowCommand } from './detect-workflow.js';
8
- export { detectAgentsCommand } from './detect-agents.js';
9
- export { syncCommand } from './sync.js';
10
- export { updateCommand } from './update.js';
1
+ /**
2
+ * Project-Level Commands
3
+ */
4
+ export { initCommand } from './init.js';
5
+ export { doctorCommand } from './doctor.js';
6
+ export { updateCommand } from './update.js';
7
+ export { tutorialCommand } from './tutorial.js';