@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.
Files changed (214) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +44 -55
  4. package/bin/task-manager.js +133 -20
  5. package/bin/validate.js +67 -33
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +201 -203
  8. package/docs/QUICKSTART.md +2 -2
  9. package/framework/CLAUDE.md +99 -77
  10. package/framework/agents.json +734 -182
  11. package/framework/commands/commit.md +166 -0
  12. package/framework/commands/morph-apply.md +13 -2
  13. package/framework/commands/morph-archive.md +8 -2
  14. package/framework/commands/morph-infra.md +6 -0
  15. package/framework/commands/morph-preflight.md +6 -0
  16. package/framework/commands/morph-proposal.md +56 -7
  17. package/framework/commands/morph-status.md +6 -0
  18. package/framework/commands/morph-troubleshoot.md +6 -0
  19. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  20. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  21. package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
  22. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
  23. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  24. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  25. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  26. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
  27. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  28. package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
  29. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  30. package/framework/hooks/claude-code/statusline.py +76 -30
  31. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  32. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  33. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  34. package/framework/hooks/shared/activity-logger.js +0 -24
  35. package/framework/hooks/shared/compact-restore.js +100 -0
  36. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  37. package/framework/hooks/shared/phase-utils.js +12 -5
  38. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  39. package/framework/hooks/shared/stale-task-reset.js +57 -0
  40. package/framework/hooks/shared/state-reader.js +29 -5
  41. package/framework/hooks/shared/worktree-helpers.js +53 -0
  42. package/framework/phases.json +69 -14
  43. package/framework/rules/morph-workflow.md +88 -86
  44. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  45. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
  46. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  47. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
  48. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
  49. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
  50. package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
  51. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
  52. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
  53. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  54. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
  55. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
  56. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
  57. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
  58. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
  59. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  60. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  61. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
  62. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  63. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
  64. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  65. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  66. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  67. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  68. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
  69. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
  70. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  71. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
  72. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  73. package/framework/standards/STANDARDS.json +640 -88
  74. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  76. package/framework/templates/REGISTRY.json +1825 -1909
  77. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  78. package/framework/templates/docs/onboarding.md +3 -7
  79. package/package.json +2 -7
  80. package/src/commands/agents/dispatch-agents.js +104 -6
  81. package/src/commands/mcp/mcp-setup.js +39 -2
  82. package/src/commands/phase/phase-reset.js +74 -0
  83. package/src/commands/project/doctor.js +34 -51
  84. package/src/commands/project/init.js +1 -1
  85. package/src/commands/project/status.js +2 -2
  86. package/src/commands/project/update.js +381 -365
  87. package/src/commands/project/worktree.js +154 -0
  88. package/src/commands/scope/escalate.js +215 -0
  89. package/src/commands/state/advance-phase.js +132 -68
  90. package/src/commands/state/approve.js +2 -2
  91. package/src/commands/state/index.js +7 -8
  92. package/src/commands/state/phase-runner.js +1 -1
  93. package/src/commands/state/state.js +61 -6
  94. package/src/commands/task/expand.js +100 -0
  95. package/src/commands/tasks/task.js +78 -99
  96. package/src/commands/templates/template-render.js +93 -173
  97. package/src/commands/trust/trust.js +26 -21
  98. package/src/core/paths/output-schema.js +19 -3
  99. package/src/core/state/phase-state-machine.js +7 -4
  100. package/src/core/state/state-manager.js +32 -57
  101. package/src/core/workflows/workflow-detector.js +9 -87
  102. package/src/lib/detectors/claude-config-detector.js +93 -347
  103. package/src/lib/detectors/design-system-detector.js +189 -189
  104. package/src/lib/detectors/index.js +155 -57
  105. package/src/lib/generators/context-generator.js +2 -2
  106. package/src/lib/installers/mcp-installer.js +37 -5
  107. package/src/lib/phase-chain/phase-validator.js +336 -0
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack/stack-profile.js +88 -0
  110. package/src/lib/tasks/task-classifier.js +16 -0
  111. package/src/lib/tasks/task-parser.js +1 -1
  112. package/src/lib/tasks/test-runner.js +77 -0
  113. package/src/lib/trust/trust-manager.js +32 -144
  114. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  115. package/src/lib/validators/spec-validator.js +58 -4
  116. package/src/lib/validators/validation-runner.js +23 -11
  117. package/src/scripts/setup-infra.js +255 -224
  118. package/src/utils/agents-installer.js +34 -14
  119. package/src/utils/banner.js +1 -1
  120. package/src/utils/claude-settings-manager.js +1 -1
  121. package/src/utils/file-copier.js +1 -1
  122. package/src/utils/hooks-installer.js +272 -8
  123. package/framework/hooks/dev/check-sync-health.js +0 -117
  124. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  125. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  126. package/framework/hooks/dev/sync-template-registry.js +0 -60
  127. package/framework/hooks/dev/validate-skill-format.js +0 -70
  128. package/framework/hooks/dev/validate-standard-format.js +0 -73
  129. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
  130. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
  131. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  132. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  133. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  134. package/framework/workflows/configs/design-impl.json +0 -49
  135. package/framework/workflows/configs/express.json +0 -45
  136. package/framework/workflows/configs/fast-track.json +0 -42
  137. package/framework/workflows/configs/full-morph.json +0 -79
  138. package/framework/workflows/configs/fusion.json +0 -39
  139. package/framework/workflows/configs/long-running.json +0 -33
  140. package/framework/workflows/configs/spec-only.json +0 -43
  141. package/framework/workflows/configs/ui-refresh.json +0 -49
  142. package/framework/workflows/configs/zero-touch.json +0 -82
  143. package/src/commands/project/index.js +0 -8
  144. package/src/commands/project/monitor.js +0 -295
  145. package/src/commands/project/tutorial.js +0 -115
  146. package/src/commands/state/validate-phase.js +0 -238
  147. package/src/commands/templates/generate-contracts.js +0 -445
  148. package/src/core/index.js +0 -10
  149. package/src/core/orchestrator.js +0 -171
  150. package/src/core/registry/command-registry.js +0 -28
  151. package/src/core/registry/index.js +0 -8
  152. package/src/core/registry/validator-registry.js +0 -204
  153. package/src/core/state/index.js +0 -8
  154. package/src/core/templates/index.js +0 -9
  155. package/src/core/templates/template-data-sources.js +0 -325
  156. package/src/core/templates/template-validator.js +0 -296
  157. package/src/core/workflows/index.js +0 -7
  158. package/src/generator/config-generator.js +0 -206
  159. package/src/generator/templates/config.json.template +0 -40
  160. package/src/generator/templates/project.md.template +0 -67
  161. package/src/lib/agents/micro-agent-factory.js +0 -161
  162. package/src/lib/analysis/complexity-analyzer.js +0 -441
  163. package/src/lib/analysis/index.js +0 -7
  164. package/src/lib/analytics/analytics-engine.js +0 -345
  165. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  166. package/src/lib/checkpoints/index.js +0 -7
  167. package/src/lib/context/context-bundler.js +0 -241
  168. package/src/lib/context/context-optimizer.js +0 -212
  169. package/src/lib/context/context-tracker.js +0 -273
  170. package/src/lib/context/core-four-tracker.js +0 -201
  171. package/src/lib/context/mcp-optimizer.js +0 -200
  172. package/src/lib/detectors/config-detector.js +0 -223
  173. package/src/lib/detectors/standards-generator.js +0 -335
  174. package/src/lib/detectors/structure-detector.js +0 -275
  175. package/src/lib/execution/fusion-executor.js +0 -304
  176. package/src/lib/execution/parallel-executor.js +0 -270
  177. package/src/lib/hooks/stop-hook-executor.js +0 -286
  178. package/src/lib/hops/hop-composer.js +0 -221
  179. package/src/lib/monitor/agent-resolver.js +0 -144
  180. package/src/lib/monitor/renderer.js +0 -230
  181. package/src/lib/orchestration/index.js +0 -7
  182. package/src/lib/orchestration/team-orchestrator.js +0 -404
  183. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  184. package/src/lib/threads/thread-coordinator.js +0 -238
  185. package/src/lib/threads/thread-manager.js +0 -317
  186. package/src/lib/tracking/artifact-trail.js +0 -202
  187. package/src/sanitizer/context-sanitizer.js +0 -221
  188. package/src/sanitizer/patterns.js +0 -163
  189. package/src/scanner/project-scanner.js +0 -242
  190. package/src/ui/diff-display.js +0 -91
  191. package/src/ui/interactive-wizard.js +0 -96
  192. package/src/ui/user-review.js +0 -211
  193. package/src/ui/wizard-questions.js +0 -188
  194. package/src/utils/color-utils.js +0 -70
  195. package/src/utils/process-handler.js +0 -97
  196. package/src/writer/file-writer.js +0 -86
  197. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  198. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  199. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  200. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  201. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  202. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  203. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  204. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  205. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  206. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  207. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  208. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  209. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  210. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  211. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  212. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  213. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  214. /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*[—:\-]\s*(.+)$/gm;
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 fullTaskPrompt = briefing
302
- ? `${rawPrompt}\n\nConstraints:\n${briefing}`
303
- : rawPrompt;
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}/3-tasks/tasks.md`);
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
- // Needs credentials
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/templates/generate-contracts.js'
103
- ];
104
-
105
- // HOP templates (meta-prompts)
106
- const HOP_TEMPLATES = [
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. HOP Templates ────────────────────────────────────────────────────
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. Zero-Touch Workflow Config ───────────────────────────────────────
237
- console.log(chalk.cyan('\n framework/workflows/ — Zero-Touch Config'));
238
- const ztPath = join(frameworkRoot, 'framework/workflows/configs/zero-touch.json');
239
- check('zero-touch.json workflow config', await pathExists(ztPath));
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
- // ── 1. Binary check ────────────────────────────────────────────────────
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.cmd ? chalk.gray(` [${c.cmd}]`) : '';
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 `morph-spec tutorial` to get started.');
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 '../state/validate-phase.js';
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 (v5.0.0+)
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) {