@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,555 +1,398 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { logger } from '../../utils/logger.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from '../../
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
|
|
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
|
-
const
|
|
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
|
-
await
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
message: `${cred.name}:`,
|
|
400
|
-
mask: cred.secret ? '*' : undefined
|
|
401
|
-
}]);
|
|
402
|
-
credentialValues[cred.envVar] = value;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
spinner.start(`Configuring ${name}...`);
|
|
406
|
-
await installMcpWithCredentials(targetPath, name, entry, credentialValues);
|
|
407
|
-
spinner.succeed(`${capitalizedName} MCP configured`);
|
|
408
|
-
manualInstalled[name] = entry;
|
|
409
|
-
} else {
|
|
410
|
-
// Show setup instructions
|
|
411
|
-
const instructions = generateSetupInstructions(name, entry);
|
|
412
|
-
logger.blank();
|
|
413
|
-
logger.dim(' Add to .claude/settings.local.json → mcpServers:');
|
|
414
|
-
logger.dim(' ' + '─'.repeat(55));
|
|
415
|
-
for (const line of instructions.configSnippet.split('\n')) {
|
|
416
|
-
logger.dim(` ${line}`);
|
|
417
|
-
}
|
|
418
|
-
logger.dim(' ' + '─'.repeat(55));
|
|
419
|
-
|
|
420
|
-
for (const cred of instructions.credentialUrls) {
|
|
421
|
-
logger.dim(` Get ${cred.name}: ${cred.helpUrl}`);
|
|
422
|
-
}
|
|
423
|
-
logger.dim(` Or run later: ${instructions.cliCommand}`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Summary table
|
|
429
|
-
const justInstalled = { ...autoInstalled, ...manualInstalled };
|
|
430
|
-
const statusRows = formatMcpStatusTable(orchestration, justInstalled);
|
|
431
|
-
|
|
432
|
-
if (statusRows.length > 0) {
|
|
433
|
-
logger.blank();
|
|
434
|
-
logger.info('MCP Servers');
|
|
435
|
-
logger.dim('─'.repeat(60));
|
|
436
|
-
|
|
437
|
-
const STATUS_ICONS = {
|
|
438
|
-
installed: '✓',
|
|
439
|
-
available: '○',
|
|
440
|
-
already_configured: '✓',
|
|
441
|
-
needs_setup: '⚠',
|
|
442
|
-
prereq_missing: '✗',
|
|
443
|
-
not_relevant: '─'
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
for (const row of statusRows) {
|
|
447
|
-
const icon = STATUS_ICONS[row.status] || '?';
|
|
448
|
-
const statusLabel = row.status.replace(/_/g, ' ');
|
|
449
|
-
logger.dim(` ${icon} ${row.name.padEnd(15)} ${statusLabel.padEnd(20)} ${row.detail}`);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
mcpSetupResult = { autoInstalled, manualInstalled, orchestration };
|
|
454
|
-
}
|
|
455
|
-
} catch (error) {
|
|
456
|
-
logger.dim(` MCP setup skipped: ${error.message}`);
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
logger.dim('\nSkipped MCP setup (--skip-mcp flag)');
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
spinner.succeed('MORPH-SPEC installed successfully!');
|
|
463
|
-
|
|
464
|
-
// Show next steps
|
|
465
|
-
logger.blank();
|
|
466
|
-
logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
|
|
467
|
-
logger.blank();
|
|
468
|
-
logger.header('Next Steps');
|
|
469
|
-
|
|
470
|
-
logger.step(1, 'Run detection: morph-spec detect');
|
|
471
|
-
logger.step(2, 'Review .morph/config/config.json and .morph/context/standards.md');
|
|
472
|
-
logger.step(3, 'Open project in VS Code with Claude Code');
|
|
473
|
-
logger.step(4, 'Start your first feature:');
|
|
474
|
-
logger.blank();
|
|
475
|
-
logger.box([
|
|
476
|
-
'Ask Claude Code to implement a feature'
|
|
477
|
-
]);
|
|
478
|
-
logger.blank();
|
|
479
|
-
|
|
480
|
-
logger.info('Files installed:');
|
|
481
|
-
logger.dim(` ✓ CLAUDE.md`);
|
|
482
|
-
logger.dim(` ✓ .morph/config/ (config.json)`);
|
|
483
|
-
logger.dim(` ✓ .morph/framework/ (agents.json, standards/, templates/)`);
|
|
484
|
-
logger.dim(` ✓ .morph/context/ (project context)`);
|
|
485
|
-
logger.dim(` ✓ .morph/features/ (feature outputs)`);
|
|
486
|
-
if (commandsCopied) {
|
|
487
|
-
logger.dim(` ✓ .claude/commands/ (slash commands)`);
|
|
488
|
-
}
|
|
489
|
-
if (rulesCopied) {
|
|
490
|
-
logger.dim(` ✓ .claude/rules/ (5 path-scoped rules)`);
|
|
491
|
-
}
|
|
492
|
-
logger.dim(` ✓ .claude/settings.local.json (${hooksResult.installed} hooks installed)`);
|
|
493
|
-
if (integrationsCreated) {
|
|
494
|
-
logger.dim(` ✓ .morph/config/integrations.json (detected plugins & MCPs)`);
|
|
495
|
-
}
|
|
496
|
-
if (mcpSetupResult) {
|
|
497
|
-
const autoCount = Object.keys(mcpSetupResult.autoInstalled).length;
|
|
498
|
-
const manualCount = Object.keys(mcpSetupResult.manualInstalled).length;
|
|
499
|
-
if (autoCount + manualCount > 0) {
|
|
500
|
-
logger.dim(` ✓ MCP servers (${autoCount + manualCount} configured)`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
logger.dim(' ✓ .claude/skills/ (installed as skill directories)');
|
|
505
|
-
logger.dim(' ✓ .claude/agents/ (native subagents installed)');
|
|
506
|
-
|
|
507
|
-
logger.blank();
|
|
508
|
-
|
|
509
|
-
// 13. LLM-based auto-detect project context (if Claude Code is available and not --skip-detection)
|
|
510
|
-
if (!options.skipDetection && detectClaudeCode()) {
|
|
511
|
-
logger.blank();
|
|
512
|
-
logger.header('Auto-Detecting Project Context');
|
|
513
|
-
logger.dim('Using Claude Code LLM to analyze your project...');
|
|
514
|
-
logger.blank();
|
|
515
|
-
|
|
516
|
-
try {
|
|
517
|
-
const orchestrator = new AutoContextOrchestrator();
|
|
518
|
-
const result = await orchestrator.execute(targetPath, {
|
|
519
|
-
skipReview: false,
|
|
520
|
-
fallbackOnError: true,
|
|
521
|
-
wizardMode: options.wizard || false
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
if (result.success) {
|
|
525
|
-
logger.success('Project context detected and saved to .morph/project.md');
|
|
526
|
-
} else {
|
|
527
|
-
logger.warn('Auto-detection incomplete. Run "morph-spec update" to retry.');
|
|
528
|
-
}
|
|
529
|
-
} catch (error) {
|
|
530
|
-
logger.warn(`Auto-detection failed: ${error.message}`);
|
|
531
|
-
logger.dim('You can run "morph-spec update" later to re-analyze your project.');
|
|
532
|
-
}
|
|
533
|
-
} else if (options.skipDetection) {
|
|
534
|
-
logger.dim('\nSkipped auto-detection (--skip-detection flag)');
|
|
535
|
-
logger.dim('Run "morph-spec update" later to analyze your project.');
|
|
536
|
-
} else {
|
|
537
|
-
logger.warn('\n⚠️ Claude Code not detected');
|
|
538
|
-
logger.dim('Auto-detection requires Claude Code CLI.');
|
|
539
|
-
logger.dim('Run "morph-spec update --wizard" to configure manually.');
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Suggest tutorial for first-time users
|
|
543
|
-
if (integrationsCreated) {
|
|
544
|
-
logger.blank();
|
|
545
|
-
logger.info('First time using morph-spec? Run `morph-spec tutorial` to get started.');
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
logger.blank();
|
|
549
|
-
|
|
550
|
-
} catch (error) {
|
|
551
|
-
spinner.fail('Installation failed');
|
|
552
|
-
logger.error(error.message);
|
|
553
|
-
process.exit(1);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { logger } from '../../utils/logger.js';
|
|
6
|
+
import {
|
|
7
|
+
pathExists,
|
|
8
|
+
readJson,
|
|
9
|
+
writeJson,
|
|
10
|
+
writeFile
|
|
11
|
+
} from '../../utils/file-copier.js';
|
|
12
|
+
import { getInstalledCLIVersion } from '../../utils/version-checker.js';
|
|
13
|
+
import { installClaudeHooks } from '../../utils/claude-settings-manager.js';
|
|
14
|
+
import inquirer from 'inquirer';
|
|
15
|
+
import { detectProject } from '../../lib/detectors/index.js';
|
|
16
|
+
import { detectClaudeConfig, mapMcpsToPhases, formatConfigSummary } from '../../lib/detectors/claude-config-detector.js';
|
|
17
|
+
import { generateSettings, saveIntegrations } from '../../lib/generators/settings-generator.js';
|
|
18
|
+
import {
|
|
19
|
+
orchestrateMcpSetup,
|
|
20
|
+
installAutoMcps,
|
|
21
|
+
installMcpWithCredentials,
|
|
22
|
+
generateSetupInstructions,
|
|
23
|
+
formatMcpStatusTable
|
|
24
|
+
} from '../../lib/installers/mcp-installer.js';
|
|
25
|
+
import { setupInfra } from '../../scripts/setup-infra.js';
|
|
26
|
+
|
|
27
|
+
export async function initCommand(options) {
|
|
28
|
+
const targetPath = options.path || process.cwd();
|
|
29
|
+
|
|
30
|
+
logger.header('MORPH-SPEC Framework Installation');
|
|
31
|
+
logger.dim(`Target: ${targetPath}`);
|
|
32
|
+
logger.blank();
|
|
33
|
+
|
|
34
|
+
// Check if already initialized
|
|
35
|
+
const morphPath = join(targetPath, '.morph');
|
|
36
|
+
if (await pathExists(morphPath)) {
|
|
37
|
+
if (!options.force) {
|
|
38
|
+
logger.warn('MORPH already initialized in this directory.');
|
|
39
|
+
logger.dim('Use --force to overwrite existing installation.');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
logger.warn('Overwriting existing MORPH installation...');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const spinner = ora('Installing MORPH-SPEC...').start();
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
|
|
49
|
+
// Steps 1-10: Infrastructure (headless, no prompts)
|
|
50
|
+
spinner.stop();
|
|
51
|
+
const infraResult = await setupInfra(targetPath);
|
|
52
|
+
spinner.start('Continuing...');
|
|
53
|
+
|
|
54
|
+
// Derive display variables from what setupInfra installed
|
|
55
|
+
const cliVersion = getInstalledCLIVersion();
|
|
56
|
+
const commandsCopied = await pathExists(join(targetPath, '.claude', 'commands'));
|
|
57
|
+
const rulesCopied = await pathExists(join(targetPath, '.claude', 'rules'));
|
|
58
|
+
// Re-run installClaudeHooks to get the result object (idempotent — hooks already installed)
|
|
59
|
+
const hooksResult = await installClaudeHooks(targetPath);
|
|
60
|
+
|
|
61
|
+
// Re-derive config path for stack detection updates
|
|
62
|
+
const configDir = join(morphPath, 'config');
|
|
63
|
+
const configPath = join(configDir, 'config.json');
|
|
64
|
+
const contextDir = join(morphPath, 'context');
|
|
65
|
+
|
|
66
|
+
// Read config written by setupInfra so stack detection can update it
|
|
67
|
+
let config = (await pathExists(configPath)) ? await readJson(configPath) : { project: {} };
|
|
68
|
+
if (!config.project) config.project = {};
|
|
69
|
+
|
|
70
|
+
// 11b. Detect Claude Code environment (plugins, MCPs, skills)
|
|
71
|
+
spinner.text = 'Detecting Claude Code environment...';
|
|
72
|
+
let claudeConfigDetected = null;
|
|
73
|
+
let integrationsCreated = false;
|
|
74
|
+
try {
|
|
75
|
+
claudeConfigDetected = await detectClaudeConfig(targetPath);
|
|
76
|
+
const hasIntegrations = claudeConfigDetected.plugins.length > 0
|
|
77
|
+
|| claudeConfigDetected.mcpServers.length > 0
|
|
78
|
+
|| claudeConfigDetected.superpowers.installed;
|
|
79
|
+
|
|
80
|
+
if (hasIntegrations) {
|
|
81
|
+
spinner.stop();
|
|
82
|
+
logger.blank();
|
|
83
|
+
logger.header('Claude Code Environment Detected');
|
|
84
|
+
|
|
85
|
+
const summary = formatConfigSummary(claudeConfigDetected);
|
|
86
|
+
if (summary) {
|
|
87
|
+
logger.info(summary);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Show MCP-to-phase mapping
|
|
91
|
+
if (claudeConfigDetected.mcpServers.length > 0) {
|
|
92
|
+
const phaseMap = mapMcpsToPhases(claudeConfigDetected.mcpServers);
|
|
93
|
+
logger.blank();
|
|
94
|
+
logger.dim('MCP usage by phase:');
|
|
95
|
+
for (const [phase, mcps] of Object.entries(phaseMap)) {
|
|
96
|
+
if (mcps.length > 0) {
|
|
97
|
+
logger.dim(` ${phase}: ${mcps.map(m => m.name).join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
logger.blank();
|
|
103
|
+
const { saveIntegrationsChoice } = await inquirer.prompt([{
|
|
104
|
+
type: 'confirm',
|
|
105
|
+
name: 'saveIntegrationsChoice',
|
|
106
|
+
message: 'Save detected integrations and generate optimized settings?',
|
|
107
|
+
default: true
|
|
108
|
+
}]);
|
|
109
|
+
|
|
110
|
+
if (saveIntegrationsChoice) {
|
|
111
|
+
spinner.start('Saving integrations...');
|
|
112
|
+
await saveIntegrations(targetPath, claudeConfigDetected);
|
|
113
|
+
await generateSettings(targetPath, claudeConfigDetected, config);
|
|
114
|
+
integrationsCreated = true;
|
|
115
|
+
spinner.succeed('Integrations saved to .morph/config/integrations.json');
|
|
116
|
+
} else {
|
|
117
|
+
spinner.start('Continuing...');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// Non-fatal: continue without integrations
|
|
122
|
+
logger.dim(` Claude config detection skipped: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 11c. Stack detection (moved before MCP setup so we know the stack)
|
|
126
|
+
spinner.text = 'Detecting project stack...';
|
|
127
|
+
let detectedStack = 'unknown';
|
|
128
|
+
const hasProjectFiles = await pathExists(join(targetPath, 'package.json'))
|
|
129
|
+
|| await pathExists(join(targetPath, 'src'))
|
|
130
|
+
|| (await fs.readdir(targetPath)).some(f => f.endsWith('.csproj'));
|
|
131
|
+
|
|
132
|
+
if (hasProjectFiles) {
|
|
133
|
+
try {
|
|
134
|
+
const quickResults = await detectProject(targetPath, { conversation: false, generateStandards: false });
|
|
135
|
+
const structure = quickResults.structure;
|
|
136
|
+
|
|
137
|
+
if (structure?.stack && structure.stack !== 'unknown') {
|
|
138
|
+
detectedStack = structure.stack;
|
|
139
|
+
|
|
140
|
+
// Persist detected stack to config.json
|
|
141
|
+
if (structure?.stack && structure.stack !== 'unknown') {
|
|
142
|
+
config.project.stack = structure.stack;
|
|
143
|
+
}
|
|
144
|
+
if (structure?.architecture && structure.architecture !== 'unknown') {
|
|
145
|
+
config.project.architecture = structure.architecture;
|
|
146
|
+
}
|
|
147
|
+
if (structure?.uiLibrary) {
|
|
148
|
+
config.project.uiLibrary = structure.uiLibrary;
|
|
149
|
+
}
|
|
150
|
+
await writeJson(configPath, config);
|
|
151
|
+
|
|
152
|
+
spinner.stop();
|
|
153
|
+
logger.blank();
|
|
154
|
+
logger.header('Existing Project Detected');
|
|
155
|
+
logger.info(`Stack detected: ${structure.stack}`);
|
|
156
|
+
if (structure?.architecture) logger.dim(`Architecture: ${structure.architecture}`);
|
|
157
|
+
if (structure?.uiLibrary) logger.dim(`UI Library: ${structure.uiLibrary}`);
|
|
158
|
+
|
|
159
|
+
const { standardsChoice } = await inquirer.prompt([{
|
|
160
|
+
type: 'list',
|
|
161
|
+
name: 'standardsChoice',
|
|
162
|
+
message: 'How should morph-spec handle your project standards?',
|
|
163
|
+
choices: [
|
|
164
|
+
{ name: 'Keep morph-spec defaults (recommended)', value: 'morph-spec' },
|
|
165
|
+
{ name: 'Detect project-specific standards', value: 'detect' }
|
|
166
|
+
]
|
|
167
|
+
}]);
|
|
168
|
+
|
|
169
|
+
if (standardsChoice === 'detect') {
|
|
170
|
+
spinner.start('Generating project-specific standards...');
|
|
171
|
+
const fullResults = await detectProject(targetPath, { conversation: false });
|
|
172
|
+
const inferredPath = join(contextDir, 'standards.md');
|
|
173
|
+
await writeFile(inferredPath, fullResults.inferred.markdown);
|
|
174
|
+
spinner.succeed('Generated .morph/context/standards.md');
|
|
175
|
+
logger.dim('Review and edit .morph/context/standards.md as needed.');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
spinner.start('Continuing...');
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
logger.dim(` Stack detection skipped: ${error.message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 11d. MCP Auto-Setup
|
|
186
|
+
let mcpSetupResult = null;
|
|
187
|
+
if (!options.skipMcp) {
|
|
188
|
+
try {
|
|
189
|
+
spinner.stop();
|
|
190
|
+
const existingMcps = claudeConfigDetected?.mcpServers || [];
|
|
191
|
+
const orchestration = orchestrateMcpSetup(targetPath, detectedStack, existingMcps);
|
|
192
|
+
|
|
193
|
+
const autoNames = Object.keys(orchestration.autoInstallable);
|
|
194
|
+
const manualNames = Object.keys(orchestration.needsManualSetup);
|
|
195
|
+
const hasAutoMcps = autoNames.length > 0;
|
|
196
|
+
const hasManualMcps = manualNames.length > 0;
|
|
197
|
+
|
|
198
|
+
if (hasAutoMcps || hasManualMcps) {
|
|
199
|
+
logger.blank();
|
|
200
|
+
logger.header('MCP Server Setup');
|
|
201
|
+
|
|
202
|
+
// Auto-install prompt
|
|
203
|
+
let autoInstalled = {};
|
|
204
|
+
if (hasAutoMcps) {
|
|
205
|
+
const { installAuto } = await inquirer.prompt([{
|
|
206
|
+
type: 'confirm',
|
|
207
|
+
name: 'installAuto',
|
|
208
|
+
message: `Install recommended MCP servers? (${autoNames.join(', ')})`,
|
|
209
|
+
default: true
|
|
210
|
+
}]);
|
|
211
|
+
|
|
212
|
+
if (installAuto) {
|
|
213
|
+
spinner.start('Installing MCP servers...');
|
|
214
|
+
const result = await installAutoMcps(targetPath, orchestration.autoInstallable);
|
|
215
|
+
spinner.succeed(`Installed ${result.added.length} MCP server(s)`);
|
|
216
|
+
for (const name of result.added) {
|
|
217
|
+
autoInstalled[name] = orchestration.autoInstallable[name];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Credential MCPs — inline setup or instructions
|
|
223
|
+
const manualInstalled = {};
|
|
224
|
+
if (hasManualMcps) {
|
|
225
|
+
for (const [name, entry] of Object.entries(orchestration.needsManualSetup)) {
|
|
226
|
+
logger.blank();
|
|
227
|
+
|
|
228
|
+
// Show warnings
|
|
229
|
+
for (const warning of entry.install.warnings || []) {
|
|
230
|
+
logger.warn(` ${warning}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
234
|
+
const { setupNow } = await inquirer.prompt([{
|
|
235
|
+
type: 'confirm',
|
|
236
|
+
name: 'setupNow',
|
|
237
|
+
message: `Set up ${capitalizedName} MCP now? (${entry.usage})`,
|
|
238
|
+
default: false
|
|
239
|
+
}]);
|
|
240
|
+
|
|
241
|
+
if (setupNow) {
|
|
242
|
+
// Collect credentials
|
|
243
|
+
const credentialValues = {};
|
|
244
|
+
for (const cred of entry.install.credentials) {
|
|
245
|
+
const { value } = await inquirer.prompt([{
|
|
246
|
+
type: cred.secret ? 'password' : 'input',
|
|
247
|
+
name: 'value',
|
|
248
|
+
message: `${cred.name}:`,
|
|
249
|
+
mask: cred.secret ? '*' : undefined
|
|
250
|
+
}]);
|
|
251
|
+
credentialValues[cred.envVar] = value;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
spinner.start(`Configuring ${name}...`);
|
|
255
|
+
await installMcpWithCredentials(targetPath, name, entry, credentialValues);
|
|
256
|
+
spinner.succeed(`${capitalizedName} MCP configured`);
|
|
257
|
+
manualInstalled[name] = entry;
|
|
258
|
+
} else {
|
|
259
|
+
// Show setup instructions
|
|
260
|
+
const instructions = generateSetupInstructions(name, entry);
|
|
261
|
+
logger.blank();
|
|
262
|
+
logger.dim(' Add to .claude/settings.local.json → mcpServers:');
|
|
263
|
+
logger.dim(' ' + '─'.repeat(55));
|
|
264
|
+
for (const line of instructions.configSnippet.split('\n')) {
|
|
265
|
+
logger.dim(` ${line}`);
|
|
266
|
+
}
|
|
267
|
+
logger.dim(' ' + '─'.repeat(55));
|
|
268
|
+
|
|
269
|
+
for (const cred of instructions.credentialUrls) {
|
|
270
|
+
logger.dim(` Get ${cred.name}: ${cred.helpUrl}`);
|
|
271
|
+
}
|
|
272
|
+
logger.dim(` Or run later: ${instructions.cliCommand}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Summary table
|
|
278
|
+
const justInstalled = { ...autoInstalled, ...manualInstalled };
|
|
279
|
+
const statusRows = formatMcpStatusTable(orchestration, justInstalled);
|
|
280
|
+
|
|
281
|
+
if (statusRows.length > 0) {
|
|
282
|
+
logger.blank();
|
|
283
|
+
logger.info('MCP Servers');
|
|
284
|
+
logger.dim('─'.repeat(60));
|
|
285
|
+
|
|
286
|
+
const STATUS_ICONS = {
|
|
287
|
+
installed: '✓',
|
|
288
|
+
available: '○',
|
|
289
|
+
already_configured: '✓',
|
|
290
|
+
needs_setup: '⚠',
|
|
291
|
+
prereq_missing: '✗',
|
|
292
|
+
not_relevant: '─'
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
for (const row of statusRows) {
|
|
296
|
+
const icon = STATUS_ICONS[row.status] || '?';
|
|
297
|
+
const statusLabel = row.status.replace(/_/g, ' ');
|
|
298
|
+
logger.dim(` ${icon} ${row.name.padEnd(15)} ${statusLabel.padEnd(20)} ${row.detail}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
mcpSetupResult = { autoInstalled, manualInstalled, orchestration };
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
logger.dim(` MCP setup skipped: ${error.message}`);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
logger.dim('\nSkipped MCP setup (--skip-mcp flag)');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
spinner.succeed('MORPH-SPEC installed successfully!');
|
|
312
|
+
|
|
313
|
+
// Show next steps
|
|
314
|
+
logger.blank();
|
|
315
|
+
logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
|
|
316
|
+
logger.blank();
|
|
317
|
+
logger.header('Next Steps');
|
|
318
|
+
|
|
319
|
+
logger.step(1, 'Review .morph/config/config.json');
|
|
320
|
+
logger.step(2, 'Open project in VS Code with Claude Code');
|
|
321
|
+
logger.step(3, 'Start your first feature:');
|
|
322
|
+
logger.blank();
|
|
323
|
+
logger.box([
|
|
324
|
+
'Ask Claude Code to implement a feature'
|
|
325
|
+
]);
|
|
326
|
+
logger.blank();
|
|
327
|
+
|
|
328
|
+
logger.info('Files installed:');
|
|
329
|
+
logger.dim(` ✓ CLAUDE.md`);
|
|
330
|
+
logger.dim(` ✓ .morph/config/ (config.json)`);
|
|
331
|
+
logger.dim(` ✓ .morph/framework/ (agents.json, standards/, templates/, hooks/)`);
|
|
332
|
+
logger.dim(` ✓ .morph/context/ (project context)`);
|
|
333
|
+
logger.dim(` ✓ .morph/features/ (feature outputs)`);
|
|
334
|
+
if (commandsCopied) {
|
|
335
|
+
logger.dim(` ✓ .claude/commands/ (slash commands)`);
|
|
336
|
+
}
|
|
337
|
+
if (rulesCopied) {
|
|
338
|
+
logger.dim(` ✓ .claude/rules/ (5 path-scoped rules)`);
|
|
339
|
+
}
|
|
340
|
+
logger.dim(` ✓ .claude/settings.local.json (${hooksResult.installed} hooks installed)`);
|
|
341
|
+
if (integrationsCreated) {
|
|
342
|
+
logger.dim(` ✓ .morph/config/integrations.json (detected plugins & MCPs)`);
|
|
343
|
+
}
|
|
344
|
+
if (mcpSetupResult) {
|
|
345
|
+
const autoCount = Object.keys(mcpSetupResult.autoInstalled).length;
|
|
346
|
+
const manualCount = Object.keys(mcpSetupResult.manualInstalled).length;
|
|
347
|
+
if (autoCount + manualCount > 0) {
|
|
348
|
+
logger.dim(` ✓ MCP servers (${autoCount + manualCount} configured)`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
logger.dim(' ✓ .claude/skills/ (installed as skill directories)');
|
|
353
|
+
|
|
354
|
+
// Show agent team breakdown from setup-infra result
|
|
355
|
+
const agents = infraResult?.agents;
|
|
356
|
+
if (agents) {
|
|
357
|
+
const total = agents.tier1 + agents.tier2 + agents.specialists;
|
|
358
|
+
logger.dim(` ✓ .claude/agents/ (${total} agents: ${agents.tier1} orchestrators, ${agents.tier2} domain leaders, ${agents.specialists} specialists)`);
|
|
359
|
+
} else {
|
|
360
|
+
logger.dim(' ✓ .claude/agents/ (native subagents installed)');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
logger.dim(' ✓ ~/.claude/statusline.sh (global statusline installed)');
|
|
364
|
+
|
|
365
|
+
// Check CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS in ~/.claude/settings.local.json
|
|
366
|
+
logger.blank();
|
|
367
|
+
const globalSettingsPath = join(homedir(), '.claude', 'settings.local.json');
|
|
368
|
+
let agentTeamsEnabled = false;
|
|
369
|
+
try {
|
|
370
|
+
const globalSettings = await readJson(globalSettingsPath);
|
|
371
|
+
agentTeamsEnabled = globalSettings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === '1';
|
|
372
|
+
} catch {
|
|
373
|
+
// File missing or unreadable — treat as not enabled
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (agentTeamsEnabled) {
|
|
377
|
+
logger.success('Agent Teams: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS enabled ✓');
|
|
378
|
+
} else {
|
|
379
|
+
logger.warn('Agent Teams not enabled. To activate multi-agent workflows, add to ~/.claude/settings.local.json:');
|
|
380
|
+
logger.dim(' {"env": {"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"}}');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
logger.blank();
|
|
384
|
+
|
|
385
|
+
// Suggest tutorial for first-time users
|
|
386
|
+
if (integrationsCreated) {
|
|
387
|
+
logger.blank();
|
|
388
|
+
logger.info('First time using morph-spec? Run `morph-spec tutorial` to get started.');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
logger.blank();
|
|
392
|
+
|
|
393
|
+
} catch (error) {
|
|
394
|
+
spinner.fail('Installation failed');
|
|
395
|
+
logger.error(error.message);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
}
|