@polymorphism-tech/morph-spec 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CLAUDE.md +446 -1730
  2. package/README.md +515 -516
  3. package/bin/morph-spec.js +366 -294
  4. package/bin/task-manager.js +429 -368
  5. package/bin/validate.js +369 -268
  6. package/content/.claude/commands/morph-apply.md +221 -158
  7. package/content/.claude/commands/morph-deploy.md +529 -0
  8. package/content/.claude/commands/morph-preflight.md +227 -0
  9. package/content/.claude/commands/morph-proposal.md +122 -101
  10. package/content/.claude/commands/morph-status.md +86 -86
  11. package/content/.claude/commands/morph-troubleshoot.md +122 -0
  12. package/content/.claude/skills/infra/azure-deploy-specialist.md +699 -0
  13. package/content/.claude/skills/level-0-meta/README.md +7 -0
  14. package/content/.claude/skills/level-0-meta/code-review.md +226 -0
  15. package/content/.claude/skills/level-0-meta/morph-checklist.md +117 -0
  16. package/content/.claude/skills/level-0-meta/simulation-checklist.md +77 -0
  17. package/content/.claude/skills/level-1-workflows/README.md +7 -0
  18. package/content/.claude/skills/level-1-workflows/morph-replicate.md +213 -0
  19. package/content/.claude/{commands/morph-clarify.md → skills/level-1-workflows/phase-clarify.md} +131 -184
  20. package/content/.claude/{commands/morph-design.md → skills/level-1-workflows/phase-design.md} +213 -275
  21. package/content/.claude/skills/level-1-workflows/phase-setup.md +106 -0
  22. package/content/.claude/skills/level-1-workflows/phase-tasks.md +164 -0
  23. package/content/.claude/{commands/morph-uiux.md → skills/level-1-workflows/phase-uiux.md} +169 -211
  24. package/content/.claude/skills/level-2-domains/README.md +14 -0
  25. package/content/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +192 -0
  26. package/content/.claude/skills/{specialists → level-2-domains/architecture}/po-pm-advisor.md +197 -197
  27. package/content/.claude/skills/level-2-domains/architecture/standards-architect.md +156 -0
  28. package/content/.claude/skills/level-2-domains/backend/dotnet-senior.md +287 -0
  29. package/content/.claude/skills/level-2-domains/backend/ef-modeler.md +113 -0
  30. package/content/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +126 -0
  31. package/content/.claude/skills/level-2-domains/backend/ms-agent-expert.md +109 -0
  32. package/content/.claude/skills/level-2-domains/frontend/blazor-builder.md +210 -0
  33. package/content/.claude/skills/level-2-domains/frontend/nextjs-expert.md +154 -0
  34. package/content/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +191 -0
  35. package/content/.claude/skills/{specialists → level-2-domains/infrastructure}/azure-architect.md +142 -142
  36. package/content/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +126 -0
  37. package/content/.claude/skills/level-2-domains/infrastructure/container-specialist.md +131 -0
  38. package/content/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +119 -0
  39. package/content/.claude/skills/level-2-domains/integrations/asaas-financial.md +130 -0
  40. package/content/.claude/skills/level-2-domains/integrations/azure-identity.md +142 -0
  41. package/content/.claude/skills/level-2-domains/integrations/clerk-auth.md +108 -0
  42. package/content/.claude/skills/level-2-domains/integrations/resend-email.md +119 -0
  43. package/content/.claude/skills/level-2-domains/quality/code-analyzer.md +235 -0
  44. package/content/.claude/skills/level-2-domains/quality/testing-specialist.md +126 -0
  45. package/content/.claude/skills/level-3-technologies/README.md +7 -0
  46. package/content/.claude/skills/level-4-patterns/README.md +7 -0
  47. package/content/.claude/skills/specialists/prompt-engineer.md +189 -0
  48. package/content/.claude/skills/specialists/seo-growth-hacker.md +320 -0
  49. package/content/.morph/config/agents.json +762 -242
  50. package/content/.morph/config/config.template.json +122 -108
  51. package/content/.morph/docs/workflows/design-impl.md +37 -0
  52. package/content/.morph/docs/workflows/enforcement-pipeline.md +668 -0
  53. package/content/.morph/docs/workflows/fast-track.md +29 -0
  54. package/content/.morph/docs/workflows/full-morph.md +76 -0
  55. package/content/.morph/docs/workflows/standard.md +44 -0
  56. package/content/.morph/docs/workflows/ui-refresh.md +39 -0
  57. package/content/.morph/examples/scheduled-reports/decisions.md +158 -0
  58. package/content/.morph/examples/scheduled-reports/proposal.md +95 -0
  59. package/content/.morph/examples/scheduled-reports/spec.md +267 -0
  60. package/content/.morph/hooks/README.md +348 -239
  61. package/content/.morph/hooks/pre-commit-agents.sh +24 -24
  62. package/content/.morph/hooks/task-completed.js +73 -0
  63. package/content/.morph/hooks/teammate-idle.js +68 -0
  64. package/content/.morph/schemas/tasks.schema.json +220 -0
  65. package/content/.morph/standards/agent-framework-blazor-ui.md +359 -0
  66. package/content/.morph/standards/agent-framework-production.md +410 -0
  67. package/content/.morph/standards/agent-framework-setup.md +413 -453
  68. package/content/.morph/standards/agent-framework-workflows.md +349 -0
  69. package/content/.morph/standards/agent-teams-workflow.md +474 -0
  70. package/content/.morph/standards/architecture.md +325 -325
  71. package/content/.morph/standards/azure.md +605 -379
  72. package/content/.morph/standards/dotnet10-migration.md +520 -494
  73. package/content/.morph/templates/CONTEXT-FEATURE.md +276 -0
  74. package/content/.morph/templates/CONTEXT.md +170 -0
  75. package/content/.morph/templates/agent.cs +163 -172
  76. package/content/.morph/templates/clarify-questions.md +159 -0
  77. package/content/.morph/templates/contracts/Commands.cs +74 -0
  78. package/content/.morph/templates/contracts/Entities.cs +25 -0
  79. package/content/.morph/templates/contracts/Queries.cs +74 -0
  80. package/content/.morph/templates/contracts/README.md +74 -0
  81. package/content/.morph/templates/decisions.md +123 -106
  82. package/content/.morph/templates/infra/azure-pipelines-deploy.yml +480 -0
  83. package/content/.morph/templates/infra/deploy-checklist.md +426 -0
  84. package/content/.morph/templates/proposal.md +141 -155
  85. package/content/.morph/templates/recap.md +94 -105
  86. package/content/.morph/templates/simulation.md +353 -0
  87. package/content/.morph/templates/spec.md +149 -148
  88. package/content/.morph/templates/state.template.json +222 -222
  89. package/content/.morph/templates/tasks.md +257 -235
  90. package/content/.morph/templates/ui-components.md +362 -276
  91. package/content/CLAUDE.md +150 -442
  92. package/detectors/structure-detector.js +245 -250
  93. package/docs/README.md +144 -149
  94. package/docs/getting-started.md +301 -302
  95. package/docs/installation.md +361 -361
  96. package/docs/validation-checklist.md +265 -266
  97. package/package.json +80 -80
  98. package/src/commands/advance-phase.js +266 -0
  99. package/src/commands/analyze-blazor-concurrency.js +193 -0
  100. package/src/commands/deploy.js +780 -0
  101. package/src/commands/detect-agents.js +167 -0
  102. package/src/commands/doctor.js +356 -280
  103. package/src/commands/generate-context.js +40 -0
  104. package/src/commands/init.js +258 -245
  105. package/src/commands/lint-fluent.js +352 -0
  106. package/src/commands/rollback-phase.js +185 -0
  107. package/src/commands/session-summary.js +291 -0
  108. package/src/commands/task.js +78 -75
  109. package/src/commands/troubleshoot.js +222 -0
  110. package/src/commands/update.js +192 -159
  111. package/src/commands/validate-blazor-state.js +210 -0
  112. package/src/commands/validate-blazor.js +156 -0
  113. package/src/commands/validate-css.js +84 -0
  114. package/src/commands/validate-phase.js +221 -0
  115. package/src/lib/blazor-concurrency-analyzer.js +288 -0
  116. package/src/lib/blazor-state-validator.js +291 -0
  117. package/src/lib/blazor-validator.js +374 -0
  118. package/src/lib/complexity-analyzer.js +441 -292
  119. package/src/lib/context-generator.js +513 -0
  120. package/src/lib/continuous-validator.js +421 -440
  121. package/src/lib/css-validator.js +352 -0
  122. package/src/lib/decision-constraint-loader.js +109 -0
  123. package/src/lib/design-system-detector.js +187 -0
  124. package/src/lib/design-system-scaffolder.js +299 -0
  125. package/src/lib/hook-executor.js +256 -0
  126. package/src/lib/recap-generator.js +205 -0
  127. package/src/lib/spec-validator.js +258 -0
  128. package/src/lib/standards-context-injector.js +287 -0
  129. package/src/lib/state-manager.js +397 -340
  130. package/src/lib/team-orchestrator.js +322 -0
  131. package/src/lib/troubleshoot-grep.js +194 -0
  132. package/src/lib/troubleshoot-index.js +144 -0
  133. package/src/lib/validation-runner.js +283 -0
  134. package/src/lib/validators/contract-compliance-validator.js +273 -0
  135. package/src/lib/validators/design-system-validator.js +231 -0
  136. package/src/utils/file-copier.js +187 -139
  137. package/content/.claude/commands/morph-costs.md +0 -206
  138. package/content/.claude/commands/morph-setup.md +0 -100
  139. package/content/.claude/commands/morph-tasks.md +0 -319
  140. package/content/.claude/skills/infra/bicep-architect.md +0 -419
  141. package/content/.claude/skills/infra/container-specialist.md +0 -437
  142. package/content/.claude/skills/infra/devops-engineer.md +0 -405
  143. package/content/.claude/skills/integrations/asaas-financial.md +0 -333
  144. package/content/.claude/skills/integrations/azure-identity.md +0 -309
  145. package/content/.claude/skills/integrations/clerk-auth.md +0 -290
  146. package/content/.claude/skills/specialists/ai-system-architect.md +0 -604
  147. package/content/.claude/skills/specialists/cost-guardian.md +0 -110
  148. package/content/.claude/skills/specialists/ef-modeler.md +0 -211
  149. package/content/.claude/skills/specialists/hangfire-orchestrator.md +0 -255
  150. package/content/.claude/skills/specialists/ms-agent-expert.md +0 -263
  151. package/content/.claude/skills/specialists/standards-architect.md +0 -78
  152. package/content/.claude/skills/specialists/ui-ux-designer.md +0 -1100
  153. package/content/.claude/skills/stacks/dotnet-blazor.md +0 -606
  154. package/content/.claude/skills/stacks/dotnet-nextjs.md +0 -402
  155. package/content/.claude/skills/stacks/shopify.md +0 -445
  156. package/content/.morph/config/azure-pricing.json +0 -70
  157. package/content/.morph/config/azure-pricing.schema.json +0 -50
  158. package/content/.morph/hooks/pre-commit-costs.sh +0 -91
  159. package/docs/api/cost-calculator.js.html +0 -513
  160. package/docs/api/design-system-generator.js.html +0 -382
  161. package/docs/api/global.html +0 -5263
  162. package/docs/api/index.html +0 -96
  163. package/docs/api/state-manager.js.html +0 -423
  164. package/src/commands/cost.js +0 -181
  165. package/src/commands/update-pricing.js +0 -206
  166. package/src/lib/cost-calculator.js +0 -429
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview Generate Context Command
3
+ *
4
+ * CLI: morph-spec generate context [feature]
5
+ *
6
+ * Generates CONTEXT.md (project-level) or CONTEXT-{feature}.md (feature-level)
7
+ * with all relevant project data for Claude's consumption.
8
+ *
9
+ * @module generate-context
10
+ */
11
+
12
+ import { generateContext } from '../lib/context-generator.js';
13
+
14
+ /**
15
+ * Generate context files
16
+ * @param {string} projectPath - Root path of the project
17
+ * @param {string} featureName - Optional feature name
18
+ * @returns {Promise<void>}
19
+ */
20
+ export async function generateContextCommand(projectPath, featureName = null) {
21
+ console.log('📄 Generating CONTEXT files...\n');
22
+
23
+ try {
24
+ const results = await generateContext(projectPath, featureName);
25
+
26
+ console.log('✅ Generated:');
27
+ console.log(` - ${results.projectContext}`);
28
+
29
+ if (results.featureContext) {
30
+ console.log(` - ${results.featureContext}`);
31
+ }
32
+
33
+ console.log('\n💡 Use these files to provide full context to Claude Code.');
34
+
35
+ } catch (err) {
36
+ console.error('❌ Error generating context:');
37
+ console.error(` ${err.message}`);
38
+ process.exit(1);
39
+ }
40
+ }
@@ -1,245 +1,258 @@
1
- import { join } from 'path';
2
- import fs from 'fs-extra';
3
- import ora from 'ora';
4
- import chalk from 'chalk';
5
- import { logger } from '../utils/logger.js';
6
- import {
7
- getContentDir,
8
- copyDirectory,
9
- copyFile,
10
- pathExists,
11
- readJson,
12
- writeJson,
13
- ensureDir,
14
- writeFile,
15
- readFile,
16
- updateGitignore,
17
- createSymlink
18
- } from '../utils/file-copier.js';
19
- import { saveProjectMorphVersion, getInstalledCLIVersion } from '../utils/version-checker.js';
20
-
21
- export async function initCommand(options) {
22
- const targetPath = options.path || process.cwd();
23
- const claudeMdPath = join(import.meta.dirname, '..', '..', 'CLAUDE.md');
24
-
25
- logger.header('MORPH-SPEC Framework Installation');
26
- logger.dim(`Target: ${targetPath}`);
27
- logger.blank();
28
-
29
- // Check if already initialized
30
- const morphPath = join(targetPath, '.morph');
31
- if (await pathExists(morphPath)) {
32
- if (!options.force) {
33
- logger.warn('MORPH already initialized in this directory.');
34
- logger.dim('Use --force to overwrite existing installation.');
35
- process.exit(1);
36
- }
37
- logger.warn('Overwriting existing MORPH installation...');
38
- }
39
-
40
- const spinner = ora('Installing MORPH-SPEC...').start();
41
-
42
- try {
43
- // 1. Copy CLAUDE.md
44
- spinner.text = 'Copying CLAUDE.md...';
45
- const claudeMdDest = join(targetPath, 'CLAUDE.md');
46
-
47
- // Backup existing CLAUDE.md if not from MORPH
48
- if (await pathExists(claudeMdDest)) {
49
- const existingContent = await readFile(claudeMdDest);
50
- if (!existingContent.includes('MORPH-SPEC')) {
51
- await copyFile(claudeMdDest, `${claudeMdDest}.backup`);
52
- logger.dim('Backed up existing CLAUDE.md');
53
- }
54
- }
55
- await copyFile(claudeMdPath, claudeMdDest);
56
-
57
- // 2. Create .morph directory structure
58
- spinner.text = 'Creating .morph structure...';
59
- const projectPath = join(morphPath, 'project');
60
- const configDir = join(morphPath, 'config');
61
- await ensureDir(join(projectPath, 'context'));
62
- await ensureDir(join(projectPath, 'standards'));
63
- await ensureDir(join(projectPath, 'outputs'));
64
- await ensureDir(configDir);
65
-
66
- // 3. Create config.json
67
- spinner.text = 'Generating config.json...';
68
- const configPath = join(configDir, 'config.json');
69
- const dirName = targetPath.split(/[\\/]/).pop();
70
-
71
- const config = {
72
- framework: 'global',
73
- frameworkVersion: getInstalledCLIVersion(),
74
- project: {
75
- name: dirName,
76
- stack: 'unknown',
77
- architecture: 'unknown'
78
- }
79
- };
80
-
81
- await writeJson(configPath, config);
82
-
83
- // 4. Create context/README.md
84
- spinner.text = 'Creating context README...';
85
- const contextReadme = join(projectPath, 'context', 'README.md');
86
- const readmeContent = `# ${dirName} - Project Context
87
-
88
- ## Overview
89
-
90
- This file will be auto-updated by MORPH-SPEC detection system.
91
-
92
- ## Stack
93
-
94
- Run \`morph-spec detect\` to analyze your project.
95
-
96
- ## Technologies
97
-
98
- (To be detected)
99
-
100
- ## Architecture
101
-
102
- (To be detected)
103
- `;
104
- await writeFile(contextReadme, readmeContent);
105
-
106
- // 5. Copy templates from content
107
- spinner.text = 'Copying templates...';
108
- const contentDir = getContentDir();
109
- const templatesSrc = join(contentDir, '.morph', 'templates');
110
- const templatesDest = join(morphPath, 'templates');
111
- if (await pathExists(templatesSrc)) {
112
- await copyDirectory(templatesSrc, templatesDest);
113
- }
114
-
115
- // 6. Copy standards from content
116
- spinner.text = 'Copying standards...';
117
- const standardsSrc = join(contentDir, '.morph', 'standards');
118
- const standardsDest = join(morphPath, 'standards');
119
- if (await pathExists(standardsSrc)) {
120
- await copyDirectory(standardsSrc, standardsDest);
121
- }
122
-
123
- // 7. Copy agents.json
124
- spinner.text = 'Copying agents configuration...';
125
- const agentsSrc = join(contentDir, '.morph', 'config', 'agents.json');
126
- const agentsDest = join(configDir, 'agents.json');
127
- if (await pathExists(agentsSrc)) {
128
- await copyFile(agentsSrc, agentsDest);
129
- }
130
-
131
- // 8. Copy azure-pricing files
132
- spinner.text = 'Copying Azure pricing data...';
133
- const pricingSrc = join(contentDir, '.morph', 'config', 'azure-pricing.json');
134
- const pricingDest = join(configDir, 'azure-pricing.json');
135
- if (await pathExists(pricingSrc)) {
136
- await copyFile(pricingSrc, pricingDest);
137
- }
138
- const pricingSchemaSrc = join(contentDir, '.morph', 'config', 'azure-pricing.schema.json');
139
- const pricingSchemaDest = join(configDir, 'azure-pricing.schema.json');
140
- if (await pathExists(pricingSchemaSrc)) {
141
- await copyFile(pricingSchemaSrc, pricingSchemaDest);
142
- }
143
-
144
- // 9. Copy .claude commands and create symlinks for skills
145
- spinner.text = 'Setting up Claude Code integration...';
146
- const claudeSrc = join(contentDir, '.claude');
147
- const claudeDest = join(targetPath, '.claude');
148
-
149
- let symlinkCount = 0;
150
- let copyCount = 0;
151
-
152
- if (await pathExists(claudeSrc)) {
153
- // Copy commands directory (slash commands)
154
- const commandsSrc = join(claudeSrc, 'commands');
155
- const commandsDest = join(claudeDest, 'commands');
156
- if (await pathExists(commandsSrc)) {
157
- await copyDirectory(commandsSrc, commandsDest);
158
- }
159
-
160
- // Create symlinks for skills (or copy if symlink fails)
161
- const skillsSrc = join(claudeSrc, 'skills');
162
- const skillsDest = join(claudeDest, 'skills');
163
-
164
- if (await pathExists(skillsSrc)) {
165
- // Ensure skills destination directory exists
166
- await ensureDir(skillsDest);
167
-
168
- // Recursively walk through skills directory
169
- const walkDir = async (dir, basePath) => {
170
- const entries = await fs.readdir(dir, { withFileTypes: true });
171
-
172
- for (const entry of entries) {
173
- const srcPath = join(dir, entry.name);
174
- const relPath = join(basePath, entry.name);
175
- const destPath = join(skillsDest, relPath);
176
-
177
- if (entry.isDirectory()) {
178
- await ensureDir(destPath);
179
- await walkDir(srcPath, relPath);
180
- } else if (entry.isFile() && entry.name.endsWith('.md')) {
181
- // Create symlink for skill files
182
- const result = await createSymlink(srcPath, destPath, 'file');
183
- if (result === 'symlink') {
184
- symlinkCount++;
185
- } else {
186
- copyCount++;
187
- }
188
- }
189
- }
190
- };
191
-
192
- await walkDir(skillsSrc, '');
193
- }
194
- }
195
-
196
- // 10. Save version info
197
- spinner.text = 'Saving version info...';
198
- const cliVersion = getInstalledCLIVersion();
199
- await saveProjectMorphVersion(targetPath, cliVersion);
200
-
201
- // 11. Update .gitignore
202
- spinner.text = 'Updating .gitignore...';
203
- await updateGitignore(targetPath);
204
-
205
- spinner.succeed('MORPH-SPEC installed successfully!');
206
-
207
- // Show next steps
208
- logger.blank();
209
- logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
210
- logger.blank();
211
- logger.header('Next Steps');
212
-
213
- logger.step(1, 'Run detection: morph-spec detect');
214
- logger.step(2, 'Review .morph/config.json and .morph/project/standards/inferred.md');
215
- logger.step(3, 'Open project in VS Code with Claude Code');
216
- logger.step(4, 'Start your first feature:');
217
- logger.blank();
218
- logger.box([
219
- 'Ask Claude Code to implement a feature'
220
- ]);
221
- logger.blank();
222
-
223
- logger.info('Files installed:');
224
- logger.dim(` ✓ CLAUDE.md`);
225
- logger.dim(` ✓ .morph/config/ (config.json, agents.json, azure-pricing.json)`);
226
- logger.dim(` ✓ .morph/standards/ (coding.md, architecture.md, azure.md, ...)`);
227
- logger.dim(` ✓ .morph/templates/ (Bicep, integrations, saas, ...)`);
228
- logger.dim(` ✓ .morph/project/ (context, standards, outputs)`);
229
- logger.dim(` ✓ .claude/commands/ (slash commands)`);
230
-
231
- if (symlinkCount > 0) {
232
- logger.dim(` ✓ .claude/skills/ (${symlinkCount} symlinked)`);
233
- } else if (copyCount > 0) {
234
- logger.dim(` ✓ .claude/skills/ (${copyCount} copied)`);
235
- logger.warn(` ⚠ Symlinks not supported (copied instead). Skills won't auto-update.`);
236
- }
237
-
238
- logger.blank();
239
-
240
- } catch (error) {
241
- spinner.fail('Installation failed');
242
- logger.error(error.message);
243
- process.exit(1);
244
- }
245
- }
1
+ import { join } from 'path';
2
+ import fs from 'fs-extra';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { logger } from '../utils/logger.js';
6
+ import {
7
+ getContentDir,
8
+ copyDirectory,
9
+ copyFile,
10
+ pathExists,
11
+ readJson,
12
+ writeJson,
13
+ ensureDir,
14
+ writeFile,
15
+ readFile,
16
+ updateGitignore,
17
+ createSymlink,
18
+ createDirectoryLink
19
+ } from '../utils/file-copier.js';
20
+ import { saveProjectMorphVersion, getInstalledCLIVersion } from '../utils/version-checker.js';
21
+
22
+ export async function initCommand(options) {
23
+ const targetPath = options.path || process.cwd();
24
+ const claudeMdPath = join(import.meta.dirname, '..', '..', 'CLAUDE.md');
25
+
26
+ logger.header('MORPH-SPEC Framework Installation');
27
+ logger.dim(`Target: ${targetPath}`);
28
+ logger.blank();
29
+
30
+ // Check if already initialized
31
+ const morphPath = join(targetPath, '.morph');
32
+ if (await pathExists(morphPath)) {
33
+ if (!options.force) {
34
+ logger.warn('MORPH already initialized in this directory.');
35
+ logger.dim('Use --force to overwrite existing installation.');
36
+ process.exit(1);
37
+ }
38
+ logger.warn('Overwriting existing MORPH installation...');
39
+ }
40
+
41
+ const spinner = ora('Installing MORPH-SPEC...').start();
42
+
43
+ try {
44
+ // 1. Copy CLAUDE.md
45
+ spinner.text = 'Copying CLAUDE.md...';
46
+ const claudeMdDest = join(targetPath, 'CLAUDE.md');
47
+
48
+ // Backup existing CLAUDE.md if not from MORPH
49
+ if (await pathExists(claudeMdDest)) {
50
+ const existingContent = await readFile(claudeMdDest);
51
+ if (!existingContent.includes('MORPH-SPEC')) {
52
+ await copyFile(claudeMdDest, `${claudeMdDest}.backup`);
53
+ logger.dim('Backed up existing CLAUDE.md');
54
+ }
55
+ }
56
+ await copyFile(claudeMdPath, claudeMdDest);
57
+
58
+ // 2. Create .morph directory structure
59
+ spinner.text = 'Creating .morph structure...';
60
+ const projectPath = join(morphPath, 'project');
61
+ const configDir = join(morphPath, 'config');
62
+ await ensureDir(join(projectPath, 'context'));
63
+ await ensureDir(join(projectPath, 'standards'));
64
+ await ensureDir(join(projectPath, 'outputs'));
65
+ await ensureDir(configDir);
66
+
67
+ // 3. Create config.json
68
+ spinner.text = 'Generating config.json...';
69
+ const configPath = join(configDir, 'config.json');
70
+ const dirName = targetPath.split(/[\\/]/).pop();
71
+
72
+ const config = {
73
+ framework: 'global',
74
+ frameworkVersion: getInstalledCLIVersion(),
75
+ project: {
76
+ name: dirName,
77
+ stack: 'unknown',
78
+ architecture: 'unknown'
79
+ }
80
+ };
81
+
82
+ await writeJson(configPath, config);
83
+
84
+ // 4. Create context/README.md
85
+ spinner.text = 'Creating context README...';
86
+ const contextReadme = join(projectPath, 'context', 'README.md');
87
+ const readmeContent = `# ${dirName} - Project Context
88
+
89
+ ## Overview
90
+
91
+ This file will be auto-updated by MORPH-SPEC detection system.
92
+
93
+ ## Stack
94
+
95
+ Run \`morph-spec detect\` to analyze your project.
96
+
97
+ ## Technologies
98
+
99
+ (To be detected)
100
+
101
+ ## Architecture
102
+
103
+ (To be detected)
104
+ `;
105
+ await writeFile(contextReadme, readmeContent);
106
+
107
+ // 5. Copy templates from content
108
+ spinner.text = 'Copying templates...';
109
+ const contentDir = getContentDir();
110
+ const templatesSrc = join(contentDir, '.morph', 'templates');
111
+ const templatesDest = join(morphPath, 'templates');
112
+ if (await pathExists(templatesSrc)) {
113
+ await copyDirectory(templatesSrc, templatesDest);
114
+ }
115
+
116
+ // 6. Copy standards from content
117
+ spinner.text = 'Copying standards...';
118
+ const standardsSrc = join(contentDir, '.morph', 'standards');
119
+ const standardsDest = join(morphPath, 'standards');
120
+ if (await pathExists(standardsSrc)) {
121
+ await copyDirectory(standardsSrc, standardsDest);
122
+ }
123
+
124
+ // 7. Copy agents.json
125
+ spinner.text = 'Copying agents configuration...';
126
+ const agentsSrc = join(contentDir, '.morph', 'config', 'agents.json');
127
+ const agentsDest = join(configDir, 'agents.json');
128
+ if (await pathExists(agentsSrc)) {
129
+ await copyFile(agentsSrc, agentsDest);
130
+ }
131
+
132
+ // 8. Copy azure-pricing files
133
+ spinner.text = 'Copying Azure pricing data...';
134
+ const pricingSrc = join(contentDir, '.morph', 'config', 'azure-pricing.json');
135
+ const pricingDest = join(configDir, 'azure-pricing.json');
136
+ if (await pathExists(pricingSrc)) {
137
+ await copyFile(pricingSrc, pricingDest);
138
+ }
139
+ const pricingSchemaSrc = join(contentDir, '.morph', 'config', 'azure-pricing.schema.json');
140
+ const pricingSchemaDest = join(configDir, 'azure-pricing.schema.json');
141
+ if (await pathExists(pricingSchemaSrc)) {
142
+ await copyFile(pricingSchemaSrc, pricingSchemaDest);
143
+ }
144
+
145
+ // 9. Copy .claude commands and create symlinks for skills
146
+ spinner.text = 'Setting up Claude Code integration...';
147
+ const claudeSrc = join(contentDir, '.claude');
148
+ const claudeDest = join(targetPath, '.claude');
149
+
150
+ let symlinkCount = 0;
151
+ let copyCount = 0;
152
+
153
+ if (await pathExists(claudeSrc)) {
154
+ // Copy commands directory (slash commands)
155
+ const commandsSrc = join(claudeSrc, 'commands');
156
+ const commandsDest = join(claudeDest, 'commands');
157
+ if (await pathExists(commandsSrc)) {
158
+ await copyDirectory(commandsSrc, commandsDest);
159
+ }
160
+
161
+ // Create directory links for skill categories (or copy if linking fails)
162
+ const skillsSrc = join(claudeSrc, 'skills');
163
+ const skillsDest = join(claudeDest, 'skills');
164
+
165
+ if (await pathExists(skillsSrc)) {
166
+ await ensureDir(skillsDest);
167
+
168
+ const entries = await fs.readdir(skillsSrc, { withFileTypes: true });
169
+
170
+ let linkedCategories = 0;
171
+ let copiedCategories = 0;
172
+ let totalSkillFiles = 0;
173
+
174
+ // Link category directories (specialists/, infra/, checklists/, etc.)
175
+ for (const entry of entries) {
176
+ if (entry.isDirectory()) {
177
+ const categorySrc = join(skillsSrc, entry.name);
178
+ const categoryDest = join(skillsDest, entry.name);
179
+
180
+ // Count .md files for reporting
181
+ const categoryEntries = await fs.readdir(categorySrc, { withFileTypes: true });
182
+ const mdFiles = categoryEntries.filter(e => e.isFile() && e.name.endsWith('.md'));
183
+ totalSkillFiles += mdFiles.length;
184
+
185
+ const result = await createDirectoryLink(categorySrc, categoryDest);
186
+ if (result === 'copy') {
187
+ copiedCategories++;
188
+ } else {
189
+ linkedCategories++;
190
+ }
191
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
192
+ // Handle .md files in skills root
193
+ totalSkillFiles++;
194
+ const result = await createSymlink(join(skillsSrc, entry.name), join(skillsDest, entry.name), 'file');
195
+ if (result === 'symlink') {
196
+ linkedCategories++;
197
+ } else {
198
+ copiedCategories++;
199
+ }
200
+ }
201
+ }
202
+
203
+ symlinkCount = linkedCategories > 0 ? totalSkillFiles : 0;
204
+ copyCount = linkedCategories === 0 ? totalSkillFiles : 0;
205
+ }
206
+ }
207
+
208
+ // 10. Save version info
209
+ spinner.text = 'Saving version info...';
210
+ const cliVersion = getInstalledCLIVersion();
211
+ await saveProjectMorphVersion(targetPath, cliVersion);
212
+
213
+ // 11. Update .gitignore
214
+ spinner.text = 'Updating .gitignore...';
215
+ await updateGitignore(targetPath);
216
+
217
+ spinner.succeed('MORPH-SPEC installed successfully!');
218
+
219
+ // Show next steps
220
+ logger.blank();
221
+ logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
222
+ logger.blank();
223
+ logger.header('Next Steps');
224
+
225
+ logger.step(1, 'Run detection: morph-spec detect');
226
+ logger.step(2, 'Review .morph/config.json and .morph/project/standards/inferred.md');
227
+ logger.step(3, 'Open project in VS Code with Claude Code');
228
+ logger.step(4, 'Start your first feature:');
229
+ logger.blank();
230
+ logger.box([
231
+ 'Ask Claude Code to implement a feature'
232
+ ]);
233
+ logger.blank();
234
+
235
+ logger.info('Files installed:');
236
+ logger.dim(` ✓ CLAUDE.md`);
237
+ logger.dim(` ✓ .morph/config/ (config.json, agents.json, azure-pricing.json)`);
238
+ logger.dim(` ✓ .morph/standards/ (coding.md, architecture.md, azure.md, ...)`);
239
+ logger.dim(` ✓ .morph/templates/ (Bicep, integrations, saas, ...)`);
240
+ logger.dim(` .morph/project/ (context, standards, outputs)`);
241
+ logger.dim(` ✓ .claude/commands/ (slash commands)`);
242
+
243
+ if (symlinkCount > 0) {
244
+ const linkType = process.platform === 'win32' ? 'junction-linked' : 'symlinked';
245
+ logger.dim(` ✓ .claude/skills/ (${symlinkCount} ${linkType})`);
246
+ } else if (copyCount > 0) {
247
+ logger.dim(` ✓ .claude/skills/ (${copyCount} copied)`);
248
+ logger.warn(` ⚠ Directory links not supported (copied instead). Skills won't auto-update.`);
249
+ }
250
+
251
+ logger.blank();
252
+
253
+ } catch (error) {
254
+ spinner.fail('Installation failed');
255
+ logger.error(error.message);
256
+ process.exit(1);
257
+ }
258
+ }