@polymorphism-tech/morph-spec 4.22.0 → 6.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.
- package/CLAUDE.md +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /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 —
|
|
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 (
|
|
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 {
|
|
219
|
+
const { gcpProjectInput } = await inquirer.prompt([{
|
|
218
220
|
type: 'input',
|
|
219
|
-
name: '
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
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 .
|
|
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 .
|
|
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 —
|
|
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
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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/
|
|
73
|
+
'src/lib/stack-filter.js'
|
|
74
74
|
];
|
|
75
75
|
|
|
76
|
-
// command files (
|
|
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/
|
|
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/
|
|
89
|
-
'src/commands/
|
|
90
|
-
'src/commands/
|
|
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
|
|
101
|
+
// required personas expected in agents.json (v5 — at least the universal evaluator)
|
|
109
102
|
const REQUIRED_AGENTS = [
|
|
110
|
-
'
|
|
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
|
|
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
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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: .
|
|
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
|
|
568
|
-
const
|
|
569
|
-
const
|
|
570
|
-
if (
|
|
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
|
|
585
|
+
name: 'agents.json required personas',
|
|
573
586
|
status: 'error',
|
|
574
|
-
msg: `Missing required
|
|
587
|
+
msg: `Missing required personas: ${missingPersonas.join(', ')}`
|
|
575
588
|
});
|
|
576
589
|
hasErrors = true;
|
|
577
590
|
} else {
|
|
578
|
-
checks.push({ name: 'agents.json required
|
|
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
|
|
594
|
+
// Check 3: v5 schema validation (flat personas, no tiers)
|
|
598
595
|
const version = agentsData.version || 'unknown';
|
|
599
|
-
const
|
|
600
|
-
const
|
|
601
|
-
const
|
|
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.
|
|
605
|
-
if (data.
|
|
606
|
-
if (data.
|
|
607
|
-
if (
|
|
608
|
-
if (!
|
|
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 (
|
|
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
|
|
616
|
+
msg: 'expected v5.x — run agents migration'
|
|
618
617
|
});
|
|
619
618
|
hasWarnings = true;
|
|
620
619
|
}
|
|
621
620
|
|
|
622
|
-
if (
|
|
623
|
-
const sample =
|
|
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: `${
|
|
626
|
+
msg: `${v4Fields.length} v4 field(s) found: ${sample}${v4Fields.length > 3 ? '...' : ''}`
|
|
628
627
|
});
|
|
629
628
|
hasWarnings = true;
|
|
630
629
|
}
|
|
631
630
|
|
|
632
|
-
if (
|
|
633
|
-
const sample =
|
|
631
|
+
if (missingV5Fields.length > 0 && isV5) {
|
|
632
|
+
const sample = missingV5Fields.slice(0, 3).join(', ');
|
|
634
633
|
checks.push({
|
|
635
|
-
name: 'agents.json
|
|
634
|
+
name: 'agents.json v5 required fields',
|
|
636
635
|
status: 'warn',
|
|
637
|
-
msg: `${
|
|
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
|
|
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 (
|
|
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 .
|
|
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}
|
|
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: '
|
|
36
|
-
{ key: '
|
|
37
|
-
{ key: '
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
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', '
|
|
57
|
+
const VALID_GATES = ['proposal', 'uiux', 'plan', 'review'];
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Approve action function - Mark a gate as approved
|