@polymorphism-tech/morph-spec 4.22.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/CLAUDE.md +57 -71
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +224 -491
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +55 -38
  8. package/framework/MORPH.md +244 -0
  9. package/framework/agents.json +127 -1198
  10. package/framework/commands/morph-apply.md +56 -240
  11. package/framework/commands/morph-archive.md +48 -85
  12. package/framework/commands/morph-preflight.md +7 -7
  13. package/framework/commands/morph-proposal.md +35 -191
  14. package/framework/commands/morph-status.md +19 -50
  15. package/framework/commands/morph-troubleshoot.md +2 -2
  16. package/framework/evals/rubrics/README.md +44 -0
  17. package/framework/evals/rubrics/architecture.md +46 -0
  18. package/framework/evals/rubrics/code-quality.md +50 -0
  19. package/framework/evals/rubrics/contracts.md +47 -0
  20. package/framework/evals/rubrics/test-coverage.md +50 -0
  21. package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
  22. package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
  23. package/framework/evals/suites/standard.yaml +5 -0
  24. package/framework/evals/trace-schema.md +67 -0
  25. package/framework/hooks/claude-code/core/README.md +3 -0
  26. package/framework/hooks/claude-code/core/denylist.json +14 -0
  27. package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
  28. package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
  30. package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
  31. package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
  32. package/framework/hooks/shared/phase-utils.js +20 -31
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
  35. package/framework/phases.json +41 -115
  36. package/framework/rubrics/eval-rubric.json +66 -0
  37. package/framework/skills/README.md +39 -64
  38. package/framework/skills/core/README.md +3 -0
  39. package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
  40. package/framework/skills/core/morph-eval/SKILL.md +57 -0
  41. package/framework/skills/core/morph-implement/SKILL.md +83 -0
  42. package/framework/skills/core/morph-plan/SKILL.md +64 -0
  43. package/framework/skills/core/morph-review/SKILL.md +62 -0
  44. package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
  45. package/framework/skills/core/morph-standards/SKILL.md +49 -0
  46. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  47. package/framework/templates/CLAUDE.md.core.template +22 -0
  48. package/framework/templates/REGISTRY.json +4 -4
  49. package/framework/workflows/configs/fast-track.json +5 -10
  50. package/framework/workflows/configs/full.json +4 -8
  51. package/framework/workflows/configs/quick.json +32 -0
  52. package/framework/workflows/configs/standard.json +57 -64
  53. package/package.json +7 -6
  54. package/src/commands/mcp/mcp-setup.js +58 -31
  55. package/src/commands/project/doctor.js +69 -70
  56. package/src/commands/project/init.js +26 -5
  57. package/src/commands/project/status.js +18 -19
  58. package/src/commands/state/approve.js +1 -1
  59. package/src/commands/validation/validate-feature.js +18 -112
  60. package/src/core/paths/output-schema.js +133 -152
  61. package/src/core/state/phase-state-machine.js +10 -24
  62. package/src/core/state/state-manager.js +17 -17
  63. package/src/core/workflows/workflow-detector.js +214 -31
  64. package/src/core/workflows/workflow-router.js +204 -0
  65. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  66. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  67. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  68. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  69. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  70. package/src/dashboard/scanner/agents-scanner.js +30 -35
  71. package/src/lib/agents/metrics-aggregator.js +1 -1
  72. package/src/lib/agents/output-collector.js +7 -9
  73. package/src/lib/context/context-builder.js +289 -0
  74. package/src/lib/detectors/claude-config-detector.js +19 -3
  75. package/src/lib/generators/context-generator.js +20 -42
  76. package/src/lib/generators/index.js +1 -0
  77. package/src/lib/generators/mandate-generator.js +512 -0
  78. package/src/lib/generators/metadata-extractor.js +1 -1
  79. package/src/lib/generators/recap-generator.js +3 -4
  80. package/src/lib/installers/mcp-installer.js +300 -92
  81. package/src/lib/phase-chain/phase-validator.js +36 -40
  82. package/src/lib/stack/stack-profile.js +74 -91
  83. package/src/lib/tasks/task-parser.js +1 -1
  84. package/src/lib/tasks/wave-resolver.js +353 -0
  85. package/src/lib/validators/checkpoint-persistence.js +2 -2
  86. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  87. package/src/lib/validators/content/index.js +1 -0
  88. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  89. package/src/scripts/global-install.js +2 -27
  90. package/src/utils/agents-installer.js +224 -447
  91. package/src/utils/claude-settings-manager.js +57 -15
  92. package/src/utils/file-copier.js +1 -0
  93. package/src/utils/hooks-installer.js +15 -117
  94. package/src/utils/skills-installer.js +12 -18
  95. package/bin/detect-agents.js +0 -226
  96. package/bin/task-manager.js +0 -1036
  97. package/framework/agents/README.md +0 -13
  98. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  99. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  100. package/framework/agents/architecture/architecture-expert.md +0 -98
  101. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  102. package/framework/agents/architecture/packages-validator.md +0 -89
  103. package/framework/agents/architecture/security-expert.md +0 -79
  104. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  105. package/framework/agents/architecture/standards-architect.md +0 -153
  106. package/framework/agents/backend/dotnet-senior.md +0 -163
  107. package/framework/agents/backend/ef-modeler.md +0 -119
  108. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  109. package/framework/agents/backend/ms-agent-expert.md +0 -55
  110. package/framework/agents/backend/neon-expert.md +0 -379
  111. package/framework/agents/frontend/css-specialist.md +0 -89
  112. package/framework/agents/frontend/design-system-validator.md +0 -100
  113. package/framework/agents/frontend/gsap-animator.md +0 -131
  114. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  115. package/framework/agents/frontend/nextjs-expert.md +0 -201
  116. package/framework/agents/frontend/ui-designer.md +0 -259
  117. package/framework/agents/infrastructure/infra-architect.md +0 -215
  118. package/framework/agents/integrations/asaas-financial.md +0 -138
  119. package/framework/agents/integrations/neon-auth.md +0 -151
  120. package/framework/agents/integrations/resend-email.md +0 -127
  121. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  122. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  123. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  124. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  125. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  126. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  127. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  128. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  129. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  130. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  131. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  132. package/framework/hooks/shared/compact-restore.js +0 -123
  133. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  134. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  135. package/framework/hooks/shared/stale-task-reset.js +0 -57
  136. package/framework/hooks/shared/worktree-helpers.js +0 -75
  137. package/framework/skills/level-0-meta/README.md +0 -7
  138. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  139. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  140. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  141. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  142. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  143. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  144. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  145. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  146. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  147. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  148. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  149. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  150. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  151. package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
  152. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  153. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  154. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
  155. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  156. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  157. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  158. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  159. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  160. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  161. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  162. package/framework/skills/level-1-workflows/README.md +0 -7
  163. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  164. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  165. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  166. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  168. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  169. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
  170. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  171. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  173. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  174. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  175. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  176. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  177. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  181. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
  182. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  183. package/framework/squad-templates/backend-only.json +0 -47
  184. package/framework/squad-templates/frontend-only.json +0 -56
  185. package/framework/squad-templates/full-stack.json +0 -74
  186. package/src/commands/agents/agent-metrics.js +0 -174
  187. package/src/commands/agents/audit-dispatch.js +0 -82
  188. package/src/commands/agents/detect-agents.js +0 -275
  189. package/src/commands/agents/dispatch-agents.js +0 -461
  190. package/src/commands/agents/index.js +0 -13
  191. package/src/commands/agents/manage-agents.js +0 -352
  192. package/src/commands/agents/project-metrics.js +0 -74
  193. package/src/commands/phase/phase-reset.js +0 -74
  194. package/src/commands/scope/escalate.js +0 -217
  195. package/src/commands/state/advance-phase.js +0 -600
  196. package/src/commands/state/phase-runner.js +0 -215
  197. package/src/commands/state/state.js +0 -444
  198. package/src/commands/task/expand.js +0 -100
  199. package/src/commands/tasks/index.js +0 -4
  200. package/src/commands/tasks/task.js +0 -105
  201. package/src/commands/templates/index.js +0 -5
  202. package/src/commands/templates/template-render.js +0 -93
  203. package/src/commands/trust/trust.js +0 -118
  204. package/src/lib/agents/agents-manager.js +0 -349
  205. package/src/lib/build/build-runner.js +0 -99
  206. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  207. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  208. package/src/lib/scope/impact-analyzer.js +0 -106
  209. package/src/lib/trust/trust-manager.js +0 -157
  210. package/src/utils/tier-helpers.js +0 -24
  211. /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
@@ -25,11 +25,11 @@ import {
25
25
  loadMcpRegistry,
26
26
  isRemoteMcp,
27
27
  generateStitchProxy,
28
- patchConfigForPlatform
28
+ patchConfigForPlatform,
29
+ setupStitch
29
30
  } from '../../lib/installers/mcp-installer.js';
30
31
  import { detectClaudeConfig } from '../../lib/detectors/claude-config-detector.js';
31
32
  import { detectProject } from '../../lib/detectors/index.js';
32
- import { installMcpServers } from '../../utils/claude-settings-manager.js';
33
33
 
34
34
  /**
35
35
  * Detect the current project's stack.
@@ -207,35 +207,58 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
207
207
 
208
208
  const credentials = mcpEntry.install.credentials || [];
209
209
 
210
- // Stitch MCP — custom OAuth proxy setup
210
+ // Stitch MCP — automated setup via setupStitch()
211
211
  if (name === 'stitch' && !mcpEntry.install.autoInstall) {
212
- console.log(chalk.cyan(`\n Setting up Stitch MCP (OAuth proxy)\n`));
212
+ console.log(chalk.cyan(`\n Setting up Stitch MCP (automated)\n`));
213
213
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
214
214
  console.log('');
215
215
 
216
+ // Allow user to provide GCP project if auto-detection might fail
217
+ let gcpProject;
216
218
  const gcpCred = credentials.find(c => c.envVar === 'STITCH_GCP_PROJECT');
217
- const { gcpProject } = await inquirer.prompt([{
219
+ const { gcpProjectInput } = await inquirer.prompt([{
218
220
  type: 'input',
219
- name: 'gcpProject',
220
- message: gcpCred ? `${gcpCred.name}:` : 'GCP Project ID:',
221
+ name: 'gcpProjectInput',
222
+ message: gcpCred ? `${gcpCred.name} (leave blank for auto-detect):` : 'GCP Project ID (blank for auto-detect):',
221
223
  }]);
222
-
223
- if (!gcpProject || !gcpProject.trim()) {
224
- console.log(chalk.red(' GCP Project ID is required.'));
225
- console.log('');
226
- return;
224
+ if (gcpProjectInput?.trim()) gcpProject = gcpProjectInput.trim();
225
+
226
+ const spinner = ora('Setting up Stitch MCP...').start();
227
+ const result = await setupStitch(targetPath, { gcpProject });
228
+
229
+ // Report step-by-step results
230
+ for (const step of result.steps) {
231
+ if (step.success) {
232
+ switch (step.step) {
233
+ case 'find-gcloud': spinner.text = `gcloud found (${step.source})`; break;
234
+ case 'verify-adc': spinner.text = 'ADC credentials verified'; break;
235
+ case 'detect-project': spinner.text = `GCP project: ${step.gcpProject}`; break;
236
+ case 'enable-api': spinner.text = 'Stitch API enabled'; break;
237
+ case 'generate-proxy': spinner.text = `Proxy generated: ${step.proxyPath}`; break;
238
+ case 'install-mcp': spinner.text = 'MCP config installed in .mcp.json'; break;
239
+ case 'health-check': spinner.text = `Health-check passed (protocol ${step.protocolVersion})`; break;
240
+ }
241
+ }
227
242
  }
228
243
 
229
- const spinner = ora('Generating Stitch proxy...').start();
230
- const { proxyPath } = generateStitchProxy(targetPath, gcpProject.trim());
231
- spinner.succeed(`Proxy script generated: ${proxyPath}`);
232
-
233
- const config = { ...mcpEntry.install.config };
234
- const result = await installMcpServers(targetPath, { stitch: patchConfigForPlatform(config) });
235
- if (result.added.length > 0) {
236
- console.log(chalk.green(` ✓ Stitch MCP configured (using OAuth proxy)`));
244
+ if (result.success) {
245
+ spinner.succeed('Stitch MCP setup complete');
246
+ for (const step of result.steps) {
247
+ if (step.step === 'health-check' && step.success) {
248
+ console.log(chalk.green(` ✓ Health-check passed (protocol ${step.protocolVersion})`));
249
+ }
250
+ }
237
251
  } else {
238
- console.log(chalk.yellow(` ⚠ Stitch MCP already configured`));
252
+ const failedStep = result.steps.find(s => !s.success && s.step !== 'enable-api' && s.step !== 'health-check');
253
+ spinner.fail(`Stitch setup failed at: ${failedStep?.step || 'unknown'}`);
254
+ if (failedStep?.error) {
255
+ console.log(chalk.red(` ${failedStep.error}`));
256
+ }
257
+ // Show health-check warning if it failed but setup otherwise succeeded
258
+ const healthStep = result.steps.find(s => s.step === 'health-check');
259
+ if (healthStep && !healthStep.success) {
260
+ console.log(chalk.yellow(` ⚠ Health-check: ${healthStep.error}`));
261
+ }
239
262
  }
240
263
 
241
264
  if (mcpEntry.companionSkills?.installCommand) {
@@ -292,7 +315,7 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
292
315
  const { installNow } = await inquirer.prompt([{
293
316
  type: 'confirm',
294
317
  name: 'installNow',
295
- message: 'Add to .claude/settings.local.json now?',
318
+ message: 'Add to .mcp.json now?',
296
319
  default: true
297
320
  }]);
298
321
 
@@ -345,7 +368,7 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
345
368
  } else {
346
369
  // Show config snippet
347
370
  const instructions = generateSetupInstructions(name, mcpEntry);
348
- console.log(chalk.gray('\n Add to .claude/settings.local.json → mcpServers:'));
371
+ console.log(chalk.gray('\n Add to .mcp.json → mcpServers:'));
349
372
  console.log(chalk.gray(' ' + '─'.repeat(55)));
350
373
  for (const line of instructions.configSnippet.split('\n')) {
351
374
  console.log(chalk.gray(` ${line}`));
@@ -424,20 +447,24 @@ async function interactiveWizard(targetPath, stack, existingMcps) {
424
447
  }]);
425
448
 
426
449
  if (setupNow) {
427
- // Stitch — custom OAuth proxy flow
450
+ // Stitch — automated setup via setupStitch()
428
451
  if (name === 'stitch' && !entry.install.autoInstall) {
429
452
  const gcpCred = entry.install.credentials?.find(c => c.envVar === 'STITCH_GCP_PROJECT');
430
453
  const { gcpProject } = await inquirer.prompt([{
431
454
  type: 'input',
432
455
  name: 'gcpProject',
433
- message: gcpCred ? `${gcpCred.name}:` : 'GCP Project ID:',
456
+ message: gcpCred ? `${gcpCred.name} (blank for auto-detect):` : 'GCP Project ID (blank for auto-detect):',
434
457
  }]);
435
- if (gcpProject?.trim()) {
436
- const { proxyPath } = generateStitchProxy(targetPath, gcpProject.trim());
437
- console.log(chalk.green(` ✓ Proxy generated: ${proxyPath}`));
438
- const config = { ...entry.install.config };
439
- await installMcpServers(targetPath, { stitch: patchConfigForPlatform(config) });
440
- console.log(chalk.green(` ✓ Stitch configured (OAuth proxy)`));
458
+
459
+ const stitchSpinner = ora('Setting up Stitch MCP...').start();
460
+ const stitchResult = await setupStitch(targetPath, { gcpProject: gcpProject?.trim() || undefined });
461
+ if (stitchResult.success) {
462
+ stitchSpinner.succeed('Stitch MCP setup complete');
463
+ } else {
464
+ const failedStep = stitchResult.steps.find(s => !s.success && s.step !== 'enable-api' && s.step !== 'health-check');
465
+ stitchSpinner.fail(`Stitch setup failed: ${failedStep?.error || 'unknown'}`);
466
+ }
467
+ if (entry.companionSkills?.installCommand) {
441
468
  const skillResults = installCompanionSkills({ stitch: entry });
442
469
  for (const r of skillResults) {
443
470
  if (r.success) console.log(chalk.green(` ✓ Companion skills: ${r.repo}`));
@@ -68,30 +68,23 @@ function getNpmGlobalPrefix() {
68
68
  }
69
69
  }
70
70
 
71
- // lib files
71
+ // lib files (v5 — minimal surface)
72
72
  const REQUIRED_LIB_FILES = [
73
- 'src/lib/trust/trust-manager.js'
73
+ 'src/lib/stack-filter.js'
74
74
  ];
75
75
 
76
- // command files (current CLI surface after radical simplification)
76
+ // command files (v5 CLI surface — installer-only)
77
77
  const REQUIRED_COMMAND_FILES = [
78
78
  'src/commands/project/init.js',
79
79
  'src/commands/project/update.js',
80
80
  'src/commands/project/doctor.js',
81
81
  'src/commands/project/status.js',
82
- 'src/commands/state/state.js',
83
- 'src/commands/state/advance-phase.js',
82
+ 'src/commands/project/worktree.js',
84
83
  'src/commands/state/approve.js',
85
- 'src/commands/tasks/task.js',
86
- 'src/commands/trust/trust.js',
87
84
  'src/commands/validation/validate-feature.js',
88
- 'src/commands/templates/template-render.js',
89
- 'src/commands/agents/dispatch-agents.js',
90
- 'src/commands/project/worktree.js',
91
- 'src/commands/phase/phase-reset.js',
92
- 'src/commands/scope/escalate.js',
93
- 'src/commands/task/expand.js',
94
- 'src/lib/scope/impact-analyzer.js'
85
+ 'src/commands/scaffold/scaffold.js',
86
+ 'src/commands/mcp/mcp-setup.js',
87
+ 'src/commands/context/context-refresh.js',
95
88
  ];
96
89
 
97
90
  // framework standards (stack-relevant only)
@@ -105,9 +98,9 @@ const FRAMEWORK_STANDARDS = [
105
98
  'framework/standards/workflows/parallel-execution.md'
106
99
  ];
107
100
 
108
- // required agents expected in agents.json
101
+ // required personas expected in agents.json (v5 — at least the universal evaluator)
109
102
  const REQUIRED_AGENTS = [
110
- 'vector-search-expert'
103
+ 'evaluator'
111
104
  ];
112
105
 
113
106
  /**
@@ -211,7 +204,7 @@ async function doctorFullCommand(frameworkRoot) {
211
204
  check('state.json parse', false, false, 'invalid JSON');
212
205
  }
213
206
  } else {
214
- check('state.json', false, true, 'not found (run: morph-spec state init)');
207
+ check('state.json', false, true, 'not found (run: morph-spec init)');
215
208
  }
216
209
 
217
210
  // ── Display ──────────────────────────────────────────────────────────────
@@ -242,8 +235,8 @@ async function doctorFullCommand(frameworkRoot) {
242
235
  }
243
236
 
244
237
  /**
245
- * MCP health check: reads settings.json / settings.local.json and validates
246
- * each configured MCP server's command binary and env var requirements.
238
+ * MCP health check: reads .mcp.json, settings.json, and settings.local.json
239
+ * and validates each configured MCP server's command binary and env var requirements.
247
240
  *
248
241
  * Strategy:
249
242
  * - "ok" : command binary exists (or is a known runtime like npx/node) AND
@@ -258,23 +251,43 @@ async function doctorMcpCommand(targetPath) {
258
251
  logger.header('MCP Server Health Check');
259
252
  logger.blank();
260
253
 
254
+ const allMcpServers = {};
255
+ const sourcesFound = [];
256
+
257
+ // Primary: .mcp.json (Claude Code reads MCPs from here)
258
+ const mcpJsonPath = join(targetPath, '.mcp.json');
259
+ if (await pathExists(mcpJsonPath)) {
260
+ try {
261
+ const mcpJson = JSON.parse(await fs.readFile(mcpJsonPath, 'utf8'));
262
+ const mcps = mcpJson.mcpServers || {};
263
+ const count = Object.keys(mcps).length;
264
+ if (count > 0) {
265
+ Object.assign(allMcpServers, mcps);
266
+ sourcesFound.push(`.mcp.json (${count})`);
267
+ }
268
+ } catch { /* invalid JSON — ignore */ }
269
+ }
270
+
271
+ // Legacy: settings files (for backward compat)
261
272
  const settingsPaths = [
262
273
  join(targetPath, '.claude', 'settings.json'),
263
274
  join(targetPath, '.claude', 'settings.local.json'),
264
275
  ];
265
276
 
266
- const allMcpServers = {};
267
- const sourcesFound = [];
268
-
269
277
  for (const sp of settingsPaths) {
270
278
  if (await pathExists(sp)) {
271
279
  try {
272
280
  const settings = JSON.parse(await fs.readFile(sp, 'utf8'));
273
281
  const mcps = settings.mcpServers || {};
274
- const count = Object.keys(mcps).length;
275
- if (count > 0) {
276
- Object.assign(allMcpServers, mcps);
277
- sourcesFound.push(`${relative(targetPath, sp)} (${count})`);
282
+ let addedCount = 0;
283
+ for (const [name, config] of Object.entries(mcps)) {
284
+ if (!allMcpServers[name]) {
285
+ allMcpServers[name] = config;
286
+ addedCount++;
287
+ }
288
+ }
289
+ if (addedCount > 0) {
290
+ sourcesFound.push(`${relative(targetPath, sp)} (${addedCount})`);
278
291
  }
279
292
  } catch { /* invalid JSON — ignore */ }
280
293
  }
@@ -283,7 +296,7 @@ async function doctorMcpCommand(targetPath) {
283
296
  if (Object.keys(allMcpServers).length === 0) {
284
297
  logger.warn('No MCP servers configured');
285
298
  logger.blank();
286
- logger.dim('Configure MCPs in: .claude/settings.json → mcpServers: { ... }');
299
+ logger.dim('Configure MCPs in: .mcp.json → mcpServers: { ... }');
287
300
  logger.dim('Or use: morph-spec mcp setup');
288
301
  return;
289
302
  }
@@ -564,77 +577,63 @@ export async function doctorCommand(options = {}) {
564
577
  checks.push({ name: 'agents.json count', status: 'ok', msg: `${actualCount} agents` });
565
578
  }
566
579
 
567
- // Check 2: required tier-2 agents present
568
- const requiredTier2 = ['standards-architect', 'dotnet-senior', 'infra-architect'];
569
- const missingTier2 = requiredTier2.filter(id => !agentsData.agents[id]);
570
- if (missingTier2.length > 0) {
580
+ // Check 2: required v5 personas present
581
+ const requiredPersonas = REQUIRED_AGENTS;
582
+ const missingPersonas = requiredPersonas.filter(id => !agentsData.agents[id]);
583
+ if (missingPersonas.length > 0) {
571
584
  checks.push({
572
- name: 'agents.json required agents',
585
+ name: 'agents.json required personas',
573
586
  status: 'error',
574
- msg: `Missing required agents: ${missingTier2.join(', ')}`
587
+ msg: `Missing required personas: ${missingPersonas.join(', ')}`
575
588
  });
576
589
  hasErrors = true;
577
590
  } else {
578
- checks.push({ name: 'agents.json required agents', status: 'ok' });
579
- }
580
-
581
- // Check 3: tier counts sum matches total
582
- const tierCounts = [1, 2, 3, 4].map(t =>
583
- agentEntries.filter(([, v]) => v.tier === t).length
584
- );
585
- const tierSum = tierCounts.reduce((a, b) => a + b, 0);
586
- if (tierSum !== actualCount) {
587
- checks.push({
588
- name: 'agents.json tier counts',
589
- status: 'warn',
590
- msg: `Tier counts sum to ${tierSum} but total is ${actualCount}`
591
- });
592
- hasWarnings = true;
593
- } else {
594
- checks.push({ name: 'agents.json tier counts', status: 'ok' });
591
+ checks.push({ name: 'agents.json required personas', status: 'ok' });
595
592
  }
596
593
 
597
- // Check 4: v4 schema validation
594
+ // Check 3: v5 schema validation (flat personas, no tiers)
598
595
  const version = agentsData.version || 'unknown';
599
- const isV4 = version.startsWith('4.');
600
- const v3Fields = [];
601
- const missingV4Fields = [];
596
+ const isV5 = version.startsWith('5.');
597
+ const v4Fields = [];
598
+ const missingV5Fields = [];
602
599
 
603
600
  for (const [id, data] of agentEntries) {
604
- if (data.role !== undefined) v3Fields.push(`${id}.role`);
605
- if (data.always_active !== undefined) v3Fields.push(`${id}.always_active`);
606
- if (data.relationships !== undefined) v3Fields.push(`${id}.relationships`);
607
- if (!data.group) missingV4Fields.push(`${id}.group`);
608
- if (!Array.isArray(data.active_phases)) missingV4Fields.push(`${id}.active_phases`);
601
+ if (data.tier !== undefined) v4Fields.push(`${id}.tier`);
602
+ if (data.active_phases !== undefined) v4Fields.push(`${id}.active_phases`);
603
+ if (data.reports_to !== undefined) v4Fields.push(`${id}.reports_to`);
604
+ if (data.teammate !== undefined) v4Fields.push(`${id}.teammate`);
605
+ if (!data.title) missingV5Fields.push(`${id}.title`);
606
+ if (!Array.isArray(data.domains)) missingV5Fields.push(`${id}.domains`);
607
+ if (typeof data.persona !== 'string' || !data.persona) missingV5Fields.push(`${id}.persona`);
609
608
  }
610
609
 
611
- if (isV4) {
610
+ if (isV5) {
612
611
  checks.push({ name: `agents.json schema (v${version})`, status: 'ok' });
613
612
  } else {
614
613
  checks.push({
615
614
  name: `agents.json schema (v${version})`,
616
615
  status: 'warn',
617
- msg: 'expected v4.x — run agents migration'
616
+ msg: 'expected v5.x — run agents migration'
618
617
  });
619
618
  hasWarnings = true;
620
619
  }
621
620
 
622
- if (v3Fields.length > 0) {
623
- const sample = v3Fields.slice(0, 3).join(', ');
621
+ if (v4Fields.length > 0) {
622
+ const sample = v4Fields.slice(0, 3).join(', ');
624
623
  checks.push({
625
- name: 'agents.json deprecated fields',
624
+ name: 'agents.json deprecated v4 fields',
626
625
  status: 'warn',
627
- msg: `${v3Fields.length} v3 field(s) found: ${sample}${v3Fields.length > 3 ? '...' : ''}`
626
+ msg: `${v4Fields.length} v4 field(s) found: ${sample}${v4Fields.length > 3 ? '...' : ''}`
628
627
  });
629
628
  hasWarnings = true;
630
629
  }
631
630
 
632
- if (missingV4Fields.length > 0 && isV4) {
633
- const sample = missingV4Fields.slice(0, 3).join(', ');
631
+ if (missingV5Fields.length > 0 && isV5) {
632
+ const sample = missingV5Fields.slice(0, 3).join(', ');
634
633
  checks.push({
635
- name: 'agents.json v4 required fields',
634
+ name: 'agents.json v5 required fields',
636
635
  status: 'warn',
637
- msg: `${missingV4Fields.length} missing: ${sample}${missingV4Fields.length > 3 ? '...' : ''}`
636
+ msg: `${missingV5Fields.length} missing: ${sample}${missingV5Fields.length > 3 ? '...' : ''}`
638
637
  });
639
638
  hasWarnings = true;
640
639
  }
@@ -853,7 +852,7 @@ export async function doctorCommand(options = {}) {
853
852
  hasErrors = true;
854
853
  }
855
854
  } else {
856
- checks.push({ name: 'state.json', status: 'warn', msg: 'not initialized (run: morph-spec state init)' });
855
+ checks.push({ name: 'state.json', status: 'warn', msg: 'not initialized (run: morph-spec init)' });
857
856
  hasWarnings = true;
858
857
  }
859
858
 
@@ -24,7 +24,8 @@ import {
24
24
  installMcpWithCredentials,
25
25
  generateSetupInstructions,
26
26
  formatMcpStatusTable,
27
- isRemoteMcp
27
+ isRemoteMcp,
28
+ setupStitch
28
29
  } from '../../lib/installers/mcp-installer.js';
29
30
  import { setupInfra, installRulesForStack } from '../../scripts/setup-infra.js';
30
31
  import { installSkills } from '../../utils/skills-installer.js';
@@ -209,7 +210,7 @@ export async function initCommand(options) {
209
210
  // Skills
210
211
  await installSkills(targetPath, { projectTags: stackTags });
211
212
 
212
- // Agents (tier 1/2/4 from agents.json + domain agents)
213
+ // Agents (domain personas from agents.json)
213
214
  await installAgents(targetPath, fwSrcDir, { projectTags: stackTags });
214
215
  await installDomainAgents(targetPath, fwSrcDir, { projectTags: stackTags });
215
216
 
@@ -290,6 +291,26 @@ export async function initCommand(options) {
290
291
  }]);
291
292
 
292
293
  if (setupNow) {
294
+ // Stitch — automated setup via setupStitch()
295
+ if (name === 'stitch' && !entry.install.autoInstall) {
296
+ spinner.start('Setting up Stitch MCP...');
297
+ const stitchResult = await setupStitch(targetPath);
298
+ if (stitchResult.success) {
299
+ spinner.succeed('Stitch MCP setup complete');
300
+ manualInstalled[name] = entry;
301
+ } else {
302
+ const failedStep = stitchResult.steps.find(s => !s.success && s.step !== 'enable-api' && s.step !== 'health-check');
303
+ spinner.fail(`Stitch setup failed: ${failedStep?.error || 'unknown'}`);
304
+ }
305
+ if (entry.companionSkills?.installCommand) {
306
+ const skillResults = installCompanionSkills({ stitch: entry });
307
+ for (const r of skillResults) {
308
+ if (r.success) logger.success(` Companion skills installed: ${r.repo}`);
309
+ }
310
+ }
311
+ continue;
312
+ }
313
+
293
314
  // Remote MCPs — launch OAuth via claude mcp add
294
315
  if (isRemoteMcp(entry.install.config)) {
295
316
  const url = entry.install.config.url;
@@ -327,7 +348,7 @@ export async function initCommand(options) {
327
348
  // Show setup instructions
328
349
  const instructions = generateSetupInstructions(name, entry);
329
350
  logger.blank();
330
- logger.dim(' Add to .claude/settings.local.json → mcpServers:');
351
+ logger.dim(' Add to .mcp.json → mcpServers:');
331
352
  logger.dim(' ' + '─'.repeat(55));
332
353
  for (const line of instructions.configSnippet.split('\n')) {
333
354
  logger.dim(` ${line}`);
@@ -434,8 +455,8 @@ export async function initCommand(options) {
434
455
  // Show agent team breakdown from setup-infra result
435
456
  const agents = infraResult?.agents;
436
457
  if (agents) {
437
- const total = agents.tier1 + agents.tier2 + agents.specialists;
438
- logger.dim(` ✓ .claude/agents/ (${total} agents: ${agents.tier1} orchestrators, ${agents.tier2} domain leaders, ${agents.specialists} specialists)`);
458
+ const total = (agents.tier1 || 0) + (agents.tier2 || 0) + (agents.specialists || 0) + (agents.personas || 0);
459
+ logger.dim(` ✓ .claude/agents/ (${total} domain personas installed)`);
439
460
  } else {
440
461
  logger.dim(' ✓ .claude/agents/ (native subagents installed)');
441
462
  }
@@ -32,11 +32,9 @@ const PHASE_SYMBOLS = {
32
32
  const PHASE_ORDER = [
33
33
  { key: 'proposal', name: 'FASE 0: PROPOSAL', order: 0 },
34
34
  { key: 'uiux', name: 'FASE 1: UI/UX', order: 1, optional: true },
35
- { key: 'design', name: 'FASE 2: DESIGN', order: 2 },
36
- { key: 'plan', name: 'FASE 3: PLAN', order: 3 },
37
- { key: 'tasks', name: 'FASE 4: TASKS', order: 4 },
38
- { key: 'implement', name: 'FASE 5: IMPLEMENT', order: 5 },
39
- { key: 'sync', name: 'FASE 6: SYNC', order: 6, optional: true }
35
+ { key: 'plan', name: 'FASE 2: PLAN', order: 2 },
36
+ { key: 'implement', name: 'FASE 3: IMPLEMENT', order: 3 },
37
+ { key: 'review', name: 'FASE 4: REVIEW', order: 4 }
40
38
  ];
41
39
 
42
40
  /**
@@ -65,10 +63,11 @@ function getPhaseStatus(phaseKey, currentPhase, feature) {
65
63
  */
66
64
  function checkPhaseOutputs(phaseKey, feature) {
67
65
  const phaseOutputMap = {
68
- proposal: ['proposal'],
66
+ proposal: ['proposal', 'stackScan', 'migrationPlan'],
69
67
  uiux: ['uiDesignSystem', 'uiMockups', 'uiComponents', 'uiFlows'],
70
- design: ['spec', 'contracts', 'schemaAnalysis', 'decisions'],
71
- tasks: ['tasks']
68
+ plan: ['spec', 'mandate', 'tasks', 'decisions'],
69
+ implement: ['recap'],
70
+ review: ['reviewReport']
72
71
  };
73
72
 
74
73
  const outputs = phaseOutputMap[phaseKey] || [];
@@ -102,11 +101,11 @@ function formatPhase(phaseKey, currentPhase, feature, options = {}) {
102
101
  // Add output files if verbose
103
102
  if (options.verbose && status !== 'pending' && status !== 'skipped') {
104
103
  const phaseOutputMap = {
105
- proposal: ['proposal'],
104
+ proposal: ['proposal', 'stackScan', 'migrationPlan'],
106
105
  uiux: ['uiDesignSystem', 'uiMockups', 'uiComponents', 'uiFlows'],
107
- design: ['spec', 'contracts', 'schemaAnalysis', 'decisions'],
108
- tasks: ['tasks'],
109
- implement: ['recap']
106
+ plan: ['spec', 'mandate', 'tasks', 'decisions'],
107
+ implement: ['recap'],
108
+ review: ['reviewReport']
110
109
  };
111
110
 
112
111
  const outputs = phaseOutputMap[phaseKey] || [];
@@ -143,9 +142,9 @@ function getNextSteps(feature) {
143
142
  const phaseApprovalMap = {
144
143
  proposal: 'proposal',
145
144
  uiux: 'uiux',
146
- design: 'design',
147
- tasks: 'tasks',
148
- implement: 'tasks'
145
+ plan: 'plan',
146
+ implement: 'plan',
147
+ review: 'review'
149
148
  };
150
149
 
151
150
  const relevantGate = phaseApprovalMap[feature.phase];
@@ -162,9 +161,9 @@ function getNextSteps(feature) {
162
161
  // Check for missing outputs in current phase
163
162
  const phaseOutputMap = {
164
163
  proposal: ['proposal'],
165
- design: ['spec', 'contracts'],
166
- tasks: ['tasks'],
167
- implement: ['recap']
164
+ plan: ['spec', 'mandate', 'tasks'],
165
+ implement: ['recap'],
166
+ review: ['reviewReport']
168
167
  };
169
168
 
170
169
  const requiredOutputs = phaseOutputMap[feature.phase] || [];
@@ -240,7 +239,7 @@ export async function statusCommand(featureName, options = {}) {
240
239
  }
241
240
  if (!feature) {
242
241
  console.error(chalk.red(`✗ Error: Feature '${featureName}' not found`));
243
- console.error(chalk.gray(`Run 'morph-spec state get ${featureName}' to check if feature exists`));
242
+ console.error(chalk.gray(`Run 'morph-spec status' to list known features`));
244
243
  process.exit(1);
245
244
  }
246
245
 
@@ -54,7 +54,7 @@ const program = new Command();
54
54
  /**
55
55
  * Valid approval gate names
56
56
  */
57
- const VALID_GATES = ['proposal', 'uiux', 'design', 'plan', 'tasks'];
57
+ const VALID_GATES = ['proposal', 'uiux', 'plan', 'review'];
58
58
 
59
59
  /**
60
60
  * Approve action function - Mark a gate as approved