@polymorphism-tech/morph-spec 4.8.19 → 4.10.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 +21 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +44 -55
- package/bin/task-manager.js +133 -20
- package/bin/validate.js +67 -33
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +201 -203
- package/docs/QUICKSTART.md +2 -2
- package/framework/CLAUDE.md +99 -77
- package/framework/agents.json +734 -182
- package/framework/commands/commit.md +166 -0
- package/framework/commands/morph-apply.md +13 -2
- package/framework/commands/morph-archive.md +8 -2
- package/framework/commands/morph-infra.md +6 -0
- package/framework/commands/morph-preflight.md +6 -0
- package/framework/commands/morph-proposal.md +56 -7
- package/framework/commands/morph-status.md +6 -0
- package/framework/commands/morph-troubleshoot.md +6 -0
- package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
- package/framework/hooks/claude-code/statusline.py +76 -30
- package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
- package/framework/hooks/shared/activity-logger.js +0 -24
- package/framework/hooks/shared/compact-restore.js +100 -0
- package/framework/hooks/shared/dispatch-helpers.js +116 -0
- package/framework/hooks/shared/phase-utils.js +12 -5
- package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
- package/framework/hooks/shared/stale-task-reset.js +57 -0
- package/framework/hooks/shared/state-reader.js +29 -5
- package/framework/hooks/shared/worktree-helpers.js +53 -0
- package/framework/phases.json +69 -14
- package/framework/rules/morph-workflow.md +88 -86
- package/framework/skills/level-0-meta/mcp-registry.json +86 -51
- package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
- package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
- package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
- package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
- package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
- package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
- package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
- package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
- package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
- package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
- package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
- package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
- package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
- package/framework/standards/STANDARDS.json +640 -88
- package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
- package/framework/standards/integration/mcp/mcp-tools.md +25 -7
- package/framework/templates/REGISTRY.json +1825 -1909
- package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
- package/framework/templates/docs/onboarding.md +3 -7
- package/package.json +2 -7
- package/src/commands/agents/dispatch-agents.js +104 -6
- package/src/commands/mcp/mcp-setup.js +39 -2
- package/src/commands/phase/phase-reset.js +74 -0
- package/src/commands/project/doctor.js +34 -51
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/status.js +2 -2
- package/src/commands/project/update.js +381 -365
- package/src/commands/project/worktree.js +154 -0
- package/src/commands/scope/escalate.js +215 -0
- package/src/commands/state/advance-phase.js +132 -68
- package/src/commands/state/approve.js +2 -2
- package/src/commands/state/index.js +7 -8
- package/src/commands/state/phase-runner.js +1 -1
- package/src/commands/state/state.js +61 -6
- package/src/commands/task/expand.js +100 -0
- package/src/commands/tasks/task.js +78 -99
- package/src/commands/templates/template-render.js +93 -173
- package/src/commands/trust/trust.js +26 -21
- package/src/core/paths/output-schema.js +19 -3
- package/src/core/state/phase-state-machine.js +7 -4
- package/src/core/state/state-manager.js +32 -57
- package/src/core/workflows/workflow-detector.js +9 -87
- package/src/lib/detectors/claude-config-detector.js +93 -347
- package/src/lib/detectors/design-system-detector.js +189 -189
- package/src/lib/detectors/index.js +155 -57
- package/src/lib/generators/context-generator.js +2 -2
- package/src/lib/installers/mcp-installer.js +37 -5
- package/src/lib/phase-chain/phase-validator.js +336 -0
- package/src/lib/scope/impact-analyzer.js +106 -0
- package/src/lib/stack/stack-profile.js +88 -0
- package/src/lib/tasks/task-classifier.js +16 -0
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/test-runner.js +77 -0
- package/src/lib/trust/trust-manager.js +32 -144
- package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
- package/src/lib/validators/spec-validator.js +58 -4
- package/src/lib/validators/validation-runner.js +23 -11
- package/src/scripts/setup-infra.js +255 -224
- package/src/utils/agents-installer.js +34 -14
- package/src/utils/banner.js +1 -1
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +1 -1
- package/src/utils/hooks-installer.js +272 -8
- package/framework/hooks/dev/check-sync-health.js +0 -117
- package/framework/hooks/dev/guard-version-numbers.js +0 -57
- package/framework/hooks/dev/sync-standards-registry.js +0 -60
- package/framework/hooks/dev/sync-template-registry.js +0 -60
- package/framework/hooks/dev/validate-skill-format.js +0 -70
- package/framework/hooks/dev/validate-standard-format.js +0 -73
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
- package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/framework/workflows/configs/design-impl.json +0 -49
- package/framework/workflows/configs/express.json +0 -45
- package/framework/workflows/configs/fast-track.json +0 -42
- package/framework/workflows/configs/full-morph.json +0 -79
- package/framework/workflows/configs/fusion.json +0 -39
- package/framework/workflows/configs/long-running.json +0 -33
- package/framework/workflows/configs/spec-only.json +0 -43
- package/framework/workflows/configs/ui-refresh.json +0 -49
- package/framework/workflows/configs/zero-touch.json +0 -82
- package/src/commands/project/index.js +0 -8
- package/src/commands/project/monitor.js +0 -295
- package/src/commands/project/tutorial.js +0 -115
- package/src/commands/state/validate-phase.js +0 -238
- package/src/commands/templates/generate-contracts.js +0 -445
- package/src/core/index.js +0 -10
- package/src/core/orchestrator.js +0 -171
- package/src/core/registry/command-registry.js +0 -28
- package/src/core/registry/index.js +0 -8
- package/src/core/registry/validator-registry.js +0 -204
- package/src/core/state/index.js +0 -8
- package/src/core/templates/index.js +0 -9
- package/src/core/templates/template-data-sources.js +0 -325
- package/src/core/templates/template-validator.js +0 -296
- package/src/core/workflows/index.js +0 -7
- package/src/generator/config-generator.js +0 -206
- package/src/generator/templates/config.json.template +0 -40
- package/src/generator/templates/project.md.template +0 -67
- package/src/lib/agents/micro-agent-factory.js +0 -161
- package/src/lib/analysis/complexity-analyzer.js +0 -441
- package/src/lib/analysis/index.js +0 -7
- package/src/lib/analytics/analytics-engine.js +0 -345
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
- package/src/lib/checkpoints/index.js +0 -7
- package/src/lib/context/context-bundler.js +0 -241
- package/src/lib/context/context-optimizer.js +0 -212
- package/src/lib/context/context-tracker.js +0 -273
- package/src/lib/context/core-four-tracker.js +0 -201
- package/src/lib/context/mcp-optimizer.js +0 -200
- package/src/lib/detectors/config-detector.js +0 -223
- package/src/lib/detectors/standards-generator.js +0 -335
- package/src/lib/detectors/structure-detector.js +0 -275
- package/src/lib/execution/fusion-executor.js +0 -304
- package/src/lib/execution/parallel-executor.js +0 -270
- package/src/lib/hooks/stop-hook-executor.js +0 -286
- package/src/lib/hops/hop-composer.js +0 -221
- package/src/lib/monitor/agent-resolver.js +0 -144
- package/src/lib/monitor/renderer.js +0 -230
- package/src/lib/orchestration/index.js +0 -7
- package/src/lib/orchestration/team-orchestrator.js +0 -404
- package/src/lib/phase-chain/eligibility-checker.js +0 -243
- package/src/lib/threads/thread-coordinator.js +0 -238
- package/src/lib/threads/thread-manager.js +0 -317
- package/src/lib/tracking/artifact-trail.js +0 -202
- package/src/sanitizer/context-sanitizer.js +0 -221
- package/src/sanitizer/patterns.js +0 -163
- package/src/scanner/project-scanner.js +0 -242
- package/src/ui/diff-display.js +0 -91
- package/src/ui/interactive-wizard.js +0 -96
- package/src/ui/user-review.js +0 -211
- package/src/ui/wizard-questions.js +0 -188
- package/src/utils/color-utils.js +0 -70
- package/src/utils/process-handler.js +0 -97
- package/src/writer/file-writer.js +0 -86
- /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
- /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
- /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
- /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
- /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
- /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
- /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
- /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
|
@@ -28,6 +28,9 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
28
28
|
/** Path to agents.json in the installed package */
|
|
29
29
|
const AGENTS_JSON_PATH = join(__dirname, '../../../framework/agents.json');
|
|
30
30
|
|
|
31
|
+
/** Path to phases.json in the installed package */
|
|
32
|
+
const PHASES_JSON_PATH = join(__dirname, '../../../framework/phases.json');
|
|
33
|
+
|
|
31
34
|
/** Phases where agent dispatch is meaningful */
|
|
32
35
|
const DISPATCHABLE_PHASES = ['design', 'tasks', 'implement'];
|
|
33
36
|
|
|
@@ -87,6 +90,85 @@ const TASK_CATEGORY_TO_GROUP = {
|
|
|
87
90
|
'docs': 'docs',
|
|
88
91
|
};
|
|
89
92
|
|
|
93
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
94
|
+
// Required Skills Injection
|
|
95
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build a mandatory skills block for the given phase.
|
|
99
|
+
* Reads requiredSkills from phases.json data and formats them as instructions for subagents.
|
|
100
|
+
* Returns null if no required skills for this phase.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} phase - Phase id (e.g. 'implement')
|
|
103
|
+
* @param {Object|null} phasesData - Parsed phases.json content
|
|
104
|
+
* @returns {string|null}
|
|
105
|
+
*/
|
|
106
|
+
export function buildSkillsBlock(phase, phasesData) {
|
|
107
|
+
try {
|
|
108
|
+
const skills = phasesData?.phases?.[phase]?.requiredSkills;
|
|
109
|
+
if (!skills || skills.length === 0) return null;
|
|
110
|
+
|
|
111
|
+
const triggerLabel = trigger =>
|
|
112
|
+
trigger.replace(/([A-Z])/g, ' $1').toUpperCase().trim();
|
|
113
|
+
|
|
114
|
+
const lines = skills.map(s => `- ${triggerLabel(s.trigger)}: Invoke Skill(${s.skill})`);
|
|
115
|
+
|
|
116
|
+
return [
|
|
117
|
+
'Mandatory Skills (non-negotiable — invoke at the specified trigger points):',
|
|
118
|
+
...lines,
|
|
119
|
+
'',
|
|
120
|
+
'Do NOT skip these skills. They are required by MORPH-SPEC for this phase.',
|
|
121
|
+
].join('\n');
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
128
|
+
// MCP Awareness Injection
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build an MCP availability block for the given phase.
|
|
133
|
+
* Reads recommendedMCPs from phases.json and MCP usage info from mcp-registry.json.
|
|
134
|
+
* Returns null if no MCPs recommended for this phase.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} phase - Phase id (e.g. 'implement')
|
|
137
|
+
* @param {Object|null} phasesData - Parsed phases.json content
|
|
138
|
+
* @param {Object} [opts] - Options
|
|
139
|
+
* @param {string} [opts.registryPath] - Override path to mcp-registry.json
|
|
140
|
+
* @returns {string|null}
|
|
141
|
+
*/
|
|
142
|
+
export function buildMcpBlock(phase, phasesData, opts = {}) {
|
|
143
|
+
try {
|
|
144
|
+
const mcps = phasesData?.phases?.[phase]?.recommendedMCPs;
|
|
145
|
+
if (!mcps || mcps.length === 0) return null;
|
|
146
|
+
|
|
147
|
+
// Try to load registry for usage descriptions
|
|
148
|
+
let registry = null;
|
|
149
|
+
const registryPath = opts.registryPath || join(__dirname, '../../../framework/skills/level-0-meta/mcp-registry.json');
|
|
150
|
+
try {
|
|
151
|
+
if (existsSync(registryPath)) {
|
|
152
|
+
registry = JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
153
|
+
}
|
|
154
|
+
} catch { /* non-blocking */ }
|
|
155
|
+
|
|
156
|
+
const lines = mcps.map(name => {
|
|
157
|
+
const usage = registry?.mcps?.[name]?.usage || '';
|
|
158
|
+
return usage ? `- ${name}: ${usage}` : `- ${name}`;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return [
|
|
162
|
+
`Available MCPs for ${phase} phase:`,
|
|
163
|
+
...lines,
|
|
164
|
+
'',
|
|
165
|
+
'Use MCP tools when available; fall back to native tools (Bash, Read, Grep) when not.',
|
|
166
|
+
].join('\n');
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
90
172
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
173
|
// Helpers
|
|
92
174
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -164,7 +246,7 @@ export function parseAndGroupTasks(tasksPath) {
|
|
|
164
246
|
const groups = {};
|
|
165
247
|
|
|
166
248
|
// Match task headings: ### T001 — Title or ### T001: Title
|
|
167
|
-
const taskHeadingRe = /^###\s+(T\d{3,})\s*[
|
|
249
|
+
const taskHeadingRe = /^###\s+(T\d{3,})\s*[—–:\-]\s*(.+)$/gm;
|
|
168
250
|
// Match category line within a task block: **category**: `domain` or - category: application
|
|
169
251
|
const categoryRe = /(?:\*\*category\*\*\s*:\s*`?([a-z-]+)`?|category:\s*([a-z-]+))/i;
|
|
170
252
|
|
|
@@ -248,6 +330,17 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
248
330
|
const agentsData = JSON.parse(readFileSync(agentsPath, 'utf8'));
|
|
249
331
|
const allAgents = agentsData.agents || {};
|
|
250
332
|
|
|
333
|
+
// Load phases.json for required skills injection (opts.phasesJsonPath allows override for testing)
|
|
334
|
+
const phasesPath = opts.phasesJsonPath || PHASES_JSON_PATH;
|
|
335
|
+
let phasesData = null;
|
|
336
|
+
try {
|
|
337
|
+
if (existsSync(phasesPath)) {
|
|
338
|
+
phasesData = JSON.parse(readFileSync(phasesPath, 'utf8'));
|
|
339
|
+
}
|
|
340
|
+
} catch {
|
|
341
|
+
// Non-blocking — fail silent, skills block will be omitted
|
|
342
|
+
}
|
|
343
|
+
|
|
251
344
|
// Active agents from state (keyword-detected during setup phase)
|
|
252
345
|
const activeAgentIds = new Set(feature.activeAgents || []);
|
|
253
346
|
|
|
@@ -296,11 +389,16 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
296
389
|
rawPrompt = agentData.teammate.spawn_prompt;
|
|
297
390
|
}
|
|
298
391
|
|
|
299
|
-
// Append standards digest as Constraints block
|
|
392
|
+
// Append standards digest as Constraints block, then skills block, then MCP block
|
|
300
393
|
const briefing = buildAgentBriefing(agentId, phase);
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
394
|
+
const skillsBlock = buildSkillsBlock(phase, phasesData);
|
|
395
|
+
const mcpBlock = buildMcpBlock(phase, phasesData);
|
|
396
|
+
const fullTaskPrompt = [
|
|
397
|
+
rawPrompt,
|
|
398
|
+
briefing ? `\n\nConstraints:\n${briefing}` : '',
|
|
399
|
+
skillsBlock ? `\n\n${skillsBlock}` : '',
|
|
400
|
+
mcpBlock ? `\n\n${mcpBlock}` : '',
|
|
401
|
+
].join('');
|
|
304
402
|
|
|
305
403
|
dispatchableAgents.push({
|
|
306
404
|
id: agentId,
|
|
@@ -347,7 +445,7 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
347
445
|
|
|
348
446
|
if (phase === 'implement') {
|
|
349
447
|
// Group tasks from tasks.md by domain category
|
|
350
|
-
const tasksPath = join(projectPath, `.morph/features/${featureName}/
|
|
448
|
+
const tasksPath = join(projectPath, `.morph/features/${featureName}/4-tasks/tasks.md`);
|
|
351
449
|
const tasksByGroup = parseAndGroupTasks(tasksPath);
|
|
352
450
|
|
|
353
451
|
for (const [group, taskIds] of Object.entries(tasksByGroup)) {
|
|
@@ -19,7 +19,8 @@ import {
|
|
|
19
19
|
installMcpWithCredentials,
|
|
20
20
|
generateSetupInstructions,
|
|
21
21
|
formatMcpStatusTable,
|
|
22
|
-
loadMcpRegistry
|
|
22
|
+
loadMcpRegistry,
|
|
23
|
+
isRemoteMcp
|
|
23
24
|
} from '../../lib/installers/mcp-installer.js';
|
|
24
25
|
import { detectClaudeConfig } from '../../lib/detectors/claude-config-detector.js';
|
|
25
26
|
import { detectProject } from '../../lib/detectors/index.js';
|
|
@@ -187,7 +188,43 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
|
|
|
187
188
|
return;
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
//
|
|
191
|
+
// Remote HTTP MCP — show setup command and optionally auto-configure
|
|
192
|
+
if (isRemoteMcp(mcpEntry.install.config)) {
|
|
193
|
+
console.log(chalk.cyan(`\n Setting up ${name} MCP (remote)\n`));
|
|
194
|
+
console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
|
|
195
|
+
console.log('');
|
|
196
|
+
|
|
197
|
+
const instructions = generateSetupInstructions(name, mcpEntry);
|
|
198
|
+
console.log(chalk.white(` Run this command to add the MCP:`));
|
|
199
|
+
console.log(chalk.cyan(` ${instructions.cliCommand}`));
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
for (const warning of mcpEntry.install.warnings || []) {
|
|
203
|
+
console.log(chalk.yellow(` ⚠ ${warning}`));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { installNow } = await inquirer.prompt([{
|
|
207
|
+
type: 'confirm',
|
|
208
|
+
name: 'installNow',
|
|
209
|
+
message: 'Add to .claude/settings.local.json now?',
|
|
210
|
+
default: true
|
|
211
|
+
}]);
|
|
212
|
+
|
|
213
|
+
if (installNow) {
|
|
214
|
+
const spinner = ora(`Configuring ${name}...`).start();
|
|
215
|
+
const result = await installAutoMcps(targetPath, { [name]: mcpEntry });
|
|
216
|
+
if (result.added.length > 0) {
|
|
217
|
+
spinner.succeed(`${name} MCP configured (authenticate via /mcp in Claude Code)`);
|
|
218
|
+
} else {
|
|
219
|
+
spinner.info(`${name} MCP already configured`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Needs credentials (stdio MCPs)
|
|
191
228
|
console.log(chalk.cyan(`\n Setting up ${name} MCP\n`));
|
|
192
229
|
console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
|
|
193
230
|
console.log('');
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MORPH-SPEC Phase Reset Command
|
|
3
|
+
*
|
|
4
|
+
* Recovery tool for corrupted phase state.
|
|
5
|
+
* Sets the phase directly without approval gate checks.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* morph-spec phase reset <feature> <phase>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { loadState, saveState, derivePhase } from '../../core/state/state-manager.js';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
|
|
15
|
+
const VALID_PHASES = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'plan', 'tasks', 'implement', 'sync'];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Core reset logic — exported for testing.
|
|
19
|
+
* @param {string} featureName
|
|
20
|
+
* @param {string} targetPhase
|
|
21
|
+
* @returns {{ success: boolean, error?: string, derivedPhase?: string }}
|
|
22
|
+
*/
|
|
23
|
+
export function resetPhase(featureName, targetPhase) {
|
|
24
|
+
if (!VALID_PHASES.includes(targetPhase)) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: `Unknown phase: "${targetPhase}". Valid phases: ${VALID_PHASES.join(', ')}`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const state = loadState();
|
|
32
|
+
if (!state?.features?.[featureName]) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: `Feature "${featureName}" not found in state.json`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get filesystem-derived phase for comparison
|
|
40
|
+
const featureFolderPath = join(process.cwd(), '.morph', 'features', featureName);
|
|
41
|
+
const derivedPhase = derivePhase(featureFolderPath);
|
|
42
|
+
|
|
43
|
+
// Set phase directly — no approval gate checks
|
|
44
|
+
state.features[featureName].phase = targetPhase;
|
|
45
|
+
state.features[featureName].updatedAt = new Date().toISOString();
|
|
46
|
+
saveState(state);
|
|
47
|
+
|
|
48
|
+
return { success: true, derivedPhase };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* CLI command handler
|
|
53
|
+
*/
|
|
54
|
+
export async function phaseResetCommand(feature, phase) {
|
|
55
|
+
console.log(chalk.cyan('\n╔════════════════════════════════════════════════╗'));
|
|
56
|
+
console.log(chalk.cyan('║ MORPH-SPEC PHASE RESET ║'));
|
|
57
|
+
console.log(chalk.cyan('╚════════════════════════════════════════════════╝\n'));
|
|
58
|
+
|
|
59
|
+
const result = resetPhase(feature, phase);
|
|
60
|
+
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
console.log(chalk.red(`\n✗ ${result.error}`));
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(chalk.green(`✓ Phase reset to "${phase}" for feature "${feature}"`));
|
|
67
|
+
|
|
68
|
+
if (result.derivedPhase !== phase) {
|
|
69
|
+
console.log(chalk.yellow(`\n⚠ Filesystem-derived phase is "${result.derivedPhase}" (folders on disk differ from state)`));
|
|
70
|
+
console.log(chalk.gray(` This is expected during recovery. The state.json value takes priority.`));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log('');
|
|
74
|
+
}
|
|
@@ -67,20 +67,6 @@ function getNpmGlobalPrefix() {
|
|
|
67
67
|
|
|
68
68
|
// lib files
|
|
69
69
|
const REQUIRED_LIB_FILES = [
|
|
70
|
-
'src/lib/analytics/analytics-engine.js',
|
|
71
|
-
'src/lib/tracking/artifact-trail.js',
|
|
72
|
-
'src/lib/context/context-bundler.js',
|
|
73
|
-
'src/lib/context/context-optimizer.js',
|
|
74
|
-
'src/lib/context/context-tracker.js',
|
|
75
|
-
'src/lib/context/core-four-tracker.js',
|
|
76
|
-
'src/lib/execution/fusion-executor.js',
|
|
77
|
-
'src/lib/hops/hop-composer.js',
|
|
78
|
-
'src/lib/context/mcp-optimizer.js',
|
|
79
|
-
'src/lib/agents/micro-agent-factory.js',
|
|
80
|
-
'src/lib/execution/parallel-executor.js',
|
|
81
|
-
'src/lib/hooks/stop-hook-executor.js', // Legacy — retained for backward compat
|
|
82
|
-
'src/lib/threads/thread-coordinator.js',
|
|
83
|
-
'src/lib/threads/thread-manager.js',
|
|
84
70
|
'src/lib/trust/trust-manager.js'
|
|
85
71
|
];
|
|
86
72
|
|
|
@@ -90,7 +76,6 @@ const REQUIRED_COMMAND_FILES = [
|
|
|
90
76
|
'src/commands/project/update.js',
|
|
91
77
|
'src/commands/project/doctor.js',
|
|
92
78
|
'src/commands/project/status.js',
|
|
93
|
-
'src/commands/project/tutorial.js',
|
|
94
79
|
'src/commands/state/state.js',
|
|
95
80
|
'src/commands/state/advance-phase.js',
|
|
96
81
|
'src/commands/state/approve.js',
|
|
@@ -99,23 +84,11 @@ const REQUIRED_COMMAND_FILES = [
|
|
|
99
84
|
'src/commands/validation/validate-feature.js',
|
|
100
85
|
'src/commands/templates/template-render.js',
|
|
101
86
|
'src/commands/agents/dispatch-agents.js',
|
|
102
|
-
'src/commands/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
'framework/templates/meta-prompts/squad-leaders/backend-squad.md',
|
|
108
|
-
'framework/templates/meta-prompts/squad-leaders/frontend-squad.md',
|
|
109
|
-
'framework/templates/meta-prompts/parallel-workers/parallel-worker.md',
|
|
110
|
-
'framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md',
|
|
111
|
-
'framework/templates/meta-prompts/hops/hop-wrapper.md',
|
|
112
|
-
'framework/templates/meta-prompts/hops/hop-retry.md',
|
|
113
|
-
'framework/templates/meta-prompts/hops/hop-validation.md',
|
|
114
|
-
'framework/templates/meta-prompts/validators/checkpoint-validator.md',
|
|
115
|
-
'framework/templates/meta-prompts/validators/pre-commit-validator.md',
|
|
116
|
-
'framework/templates/meta-prompts/fusion/fusion-agent.md',
|
|
117
|
-
'framework/templates/meta-prompts/fusion/fusion-aggregator.md',
|
|
118
|
-
'framework/templates/REGISTRY.json'
|
|
87
|
+
'src/commands/project/worktree.js',
|
|
88
|
+
'src/commands/phase/phase-reset.js',
|
|
89
|
+
'src/commands/scope/escalate.js',
|
|
90
|
+
'src/commands/task/expand.js',
|
|
91
|
+
'src/lib/scope/impact-analyzer.js'
|
|
119
92
|
];
|
|
120
93
|
|
|
121
94
|
// framework standards
|
|
@@ -195,17 +168,7 @@ async function doctorFullCommand(frameworkRoot) {
|
|
|
195
168
|
missingCmds.length === 0, false,
|
|
196
169
|
missingCmds.length > 0 ? `missing: ${missingCmds.join(', ')}` : '');
|
|
197
170
|
|
|
198
|
-
// ── 3.
|
|
199
|
-
console.log(chalk.cyan(`\n framework/templates/meta-prompts/ — HOP Templates (${HOP_TEMPLATES.length})`));
|
|
200
|
-
const missingHOPs = [];
|
|
201
|
-
for (const f of HOP_TEMPLATES) {
|
|
202
|
-
if (!(await pathExists(join(frameworkRoot, f)))) missingHOPs.push(f.split('/').pop());
|
|
203
|
-
}
|
|
204
|
-
check(`HOP Templates (${HOP_TEMPLATES.length - missingHOPs.length}/${HOP_TEMPLATES.length})`,
|
|
205
|
-
missingHOPs.length === 0, false,
|
|
206
|
-
missingHOPs.length > 0 ? `missing: ${missingHOPs.join(', ')}` : '');
|
|
207
|
-
|
|
208
|
-
// ── 4. Framework Standards ───────────────────────────────────────────────
|
|
171
|
+
// ── 3. Framework Standards ───────────────────────────────────────────────
|
|
209
172
|
console.log(chalk.cyan(`\n framework/standards/ — Framework Standards (${FRAMEWORK_STANDARDS.length} files)`));
|
|
210
173
|
const missingStds = [];
|
|
211
174
|
for (const f of FRAMEWORK_STANDARDS) {
|
|
@@ -233,10 +196,10 @@ async function doctorFullCommand(frameworkRoot) {
|
|
|
233
196
|
check('agents.json', false, false, 'file not found');
|
|
234
197
|
}
|
|
235
198
|
|
|
236
|
-
// ── 7.
|
|
237
|
-
console.log(chalk.cyan('\n framework/workflows/ —
|
|
238
|
-
const
|
|
239
|
-
check('
|
|
199
|
+
// ── 7. Workflow Configs ──────────────────────────────────────────────────
|
|
200
|
+
console.log(chalk.cyan('\n framework/workflows/ — Workflow Configs'));
|
|
201
|
+
const stdPath = join(frameworkRoot, 'framework/workflows/configs/standard.json');
|
|
202
|
+
check('standard.json workflow config', await pathExists(stdPath));
|
|
240
203
|
|
|
241
204
|
// ── 8. state.json schema version ────────────────────────────────────────
|
|
242
205
|
console.log(chalk.cyan('\n .morph/state.json — Schema Version'));
|
|
@@ -337,10 +300,18 @@ async function doctorMcpCommand(targetPath) {
|
|
|
337
300
|
for (const [name, config] of Object.entries(allMcpServers)) {
|
|
338
301
|
const issues = [];
|
|
339
302
|
let status = 'ok';
|
|
303
|
+
let isRemote = false;
|
|
340
304
|
|
|
341
|
-
// ──
|
|
305
|
+
// ── 0. Remote MCP detection ────────────────────────────────────────────
|
|
306
|
+
// Remote MCPs use HTTP transport (type: "http" or url property) — no binary to check
|
|
307
|
+
if (config.type === 'http' || (config.url && !config.command)) {
|
|
308
|
+
isRemote = true;
|
|
309
|
+
// Remote MCPs are always "ok" from a config perspective — auth happens at runtime
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ── 1. Binary check (stdio MCPs only) ──────────────────────────────────
|
|
342
313
|
const cmd = config.command || '';
|
|
343
|
-
if (cmd && !KNOWN_RUNTIMES.has(cmd)) {
|
|
314
|
+
if (!isRemote && cmd && !KNOWN_RUNTIMES.has(cmd)) {
|
|
344
315
|
try {
|
|
345
316
|
execSync(isWindows ? `where "${cmd}"` : `which "${cmd}"`, { stdio: 'ignore' });
|
|
346
317
|
} catch {
|
|
@@ -370,12 +341,14 @@ async function doctorMcpCommand(targetPath) {
|
|
|
370
341
|
}
|
|
371
342
|
}
|
|
372
343
|
|
|
373
|
-
checks.push({ name, status, issues, cmd });
|
|
344
|
+
checks.push({ name, status, issues, cmd, isRemote });
|
|
374
345
|
}
|
|
375
346
|
|
|
376
347
|
// ── Display ────────────────────────────────────────────────────────────────
|
|
377
348
|
for (const c of checks) {
|
|
378
|
-
const typeLabel = c.
|
|
349
|
+
const typeLabel = c.isRemote
|
|
350
|
+
? chalk.gray(` [remote: ${allMcpServers[c.name]?.url || 'http'}]`)
|
|
351
|
+
: c.cmd ? chalk.gray(` [${c.cmd}]`) : '';
|
|
379
352
|
|
|
380
353
|
if (c.status === 'ok') {
|
|
381
354
|
console.log(chalk.green(` ✓ ${c.name}`) + typeLabel);
|
|
@@ -454,6 +427,16 @@ export async function doctorCommand(options = {}) {
|
|
|
454
427
|
let hasErrors = false;
|
|
455
428
|
let hasWarnings = false;
|
|
456
429
|
|
|
430
|
+
const check = (name, passed, warnOnly = false, msg = '') => {
|
|
431
|
+
if (passed) {
|
|
432
|
+
checks.push({ name, status: 'ok' });
|
|
433
|
+
} else {
|
|
434
|
+
checks.push({ name, status: warnOnly ? 'warn' : 'missing', msg });
|
|
435
|
+
if (warnOnly) hasWarnings = true;
|
|
436
|
+
else hasErrors = true;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
457
440
|
// Check versions first
|
|
458
441
|
const cliCheck = await checkCLIOutdated();
|
|
459
442
|
const projectCheck = await checkProjectOutdated(targetPath);
|
|
@@ -385,7 +385,7 @@ export async function initCommand(options) {
|
|
|
385
385
|
// Suggest tutorial for first-time users
|
|
386
386
|
if (integrationsCreated) {
|
|
387
387
|
logger.blank();
|
|
388
|
-
logger.info('First time using morph-spec? Run
|
|
388
|
+
logger.info('First time using morph-spec? Run `/morph-proposal <feature>` to start a new feature.');
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
logger.blank();
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import chalk from 'chalk';
|
|
16
16
|
import { loadState, getFeature, derivePhase, deriveOutputs } from '../../core/state/state-manager.js';
|
|
17
|
-
import { PHASES } from '
|
|
17
|
+
import { PHASES } from '../../lib/phase-chain/phase-validator.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Phase symbols for visual representation
|
|
@@ -243,7 +243,7 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
243
243
|
process.exit(1);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
// Derive phase and outputs from filesystem if not stored in state
|
|
246
|
+
// Derive phase and outputs from filesystem if not stored in state
|
|
247
247
|
const { join } = await import('path');
|
|
248
248
|
const featurePath = join(process.cwd(), '.morph', 'features', featureName);
|
|
249
249
|
if (!feature.phase) {
|