@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
@@ -0,0 +1,154 @@
1
+ /**
2
+ * MORPH-SPEC Worktree Setup Command
3
+ *
4
+ * Creates a git worktree at .worktrees/{feature}/ on branch morph/{feature}.
5
+ * Used by SessionStart hook to isolate feature work from the main branch.
6
+ *
7
+ * Usage:
8
+ * morph-spec worktree setup <feature>
9
+ * morph-spec worktree setup <feature> --fresh
10
+ *
11
+ * Exit codes:
12
+ * 0 — success (created) or fail-open (not a git repo, git error)
13
+ * 2 — worktree already exists (signals resume scenario to hook caller)
14
+ */
15
+
16
+ import { execSync } from 'child_process';
17
+ import { existsSync, mkdirSync } from 'fs';
18
+ import { join } from 'path';
19
+
20
+ // ── Pure helpers (exported for tests) ──────────────────────────────────────
21
+
22
+ /**
23
+ * Check if a directory is inside a git repository.
24
+ * @param {string} cwd
25
+ * @returns {boolean}
26
+ */
27
+ export function isGitRepo(cwd = process.cwd()) {
28
+ try {
29
+ execSync('git rev-parse --git-dir', { cwd, stdio: 'pipe' });
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Check if a branch exists locally.
38
+ * @param {string} branch
39
+ * @param {string} cwd
40
+ * @returns {boolean}
41
+ */
42
+ export function branchExists(branch, cwd = process.cwd()) {
43
+ try {
44
+ execSync(`git show-ref --verify --quiet refs/heads/${branch}`, { cwd, stdio: 'pipe' });
45
+ return true;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Check if a worktree path is already registered with git.
53
+ * @param {string} worktreePath Relative or absolute path
54
+ * @param {string} cwd
55
+ * @returns {boolean}
56
+ */
57
+ export function worktreeExists(worktreePath, cwd = process.cwd()) {
58
+ try {
59
+ const output = execSync('git worktree list --porcelain', { cwd, stdio: 'pipe' }).toString();
60
+ const normalizedPath = worktreePath.replace(/\\/g, '/');
61
+ const absPath = normalizedPath.startsWith('/') ? normalizedPath : `${cwd.replace(/\\/g, '/')}/${normalizedPath}`;
62
+ return output.replace(/\\/g, '/').includes(absPath);
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Build the worktree directory path (always forward slashes).
70
+ * @param {string} feature
71
+ * @param {string} cwd
72
+ * @param {{ fresh?: boolean }} options
73
+ * @returns {string}
74
+ */
75
+ export function buildWorktreePath(feature, cwd = process.cwd(), options = {}) {
76
+ const suffix = options.fresh ? `-${getDateSuffix()}` : '';
77
+ const normalizedCwd = cwd.replace(/\\/g, '/');
78
+ return `${normalizedCwd}/.worktrees/${feature}${suffix}`;
79
+ }
80
+
81
+ /**
82
+ * Build the branch name for a feature worktree.
83
+ * @param {string} feature
84
+ * @param {{ fresh?: boolean }} options
85
+ * @returns {string}
86
+ */
87
+ export function buildBranchName(feature, options = {}) {
88
+ const suffix = options.fresh ? `-${getDateSuffix()}` : '';
89
+ return `morph/${feature}${suffix}`;
90
+ }
91
+
92
+ function getDateSuffix() {
93
+ return new Date().toISOString().slice(0, 10).replace(/-/g, '');
94
+ }
95
+
96
+ // ── Command handler ─────────────────────────────────────────────────────────
97
+
98
+ /**
99
+ * Main command handler for `morph-spec worktree setup <feature>`
100
+ * @param {string} feature
101
+ * @param {{ fresh?: boolean, cwd?: string }} options
102
+ */
103
+ export async function worktreeSetupCommand(feature, options = {}) {
104
+ const cwd = options.cwd || process.cwd();
105
+
106
+ // Gate 1: Must be a git repo
107
+ if (!isGitRepo(cwd)) {
108
+ console.log(JSON.stringify({ created: false, error: 'not-a-git-repo', feature }));
109
+ process.exit(0); // Fail-open
110
+ }
111
+
112
+ const branch = buildBranchName(feature, options);
113
+ const worktreePath = buildWorktreePath(feature, cwd, options);
114
+
115
+ // Gate 2: If worktree already registered and not --fresh, signal resume
116
+ if (!options.fresh && worktreeExists(worktreePath, cwd)) {
117
+ console.log(JSON.stringify({ created: false, alreadyExists: true, path: worktreePath, branch, feature }));
118
+ process.exit(2); // Convention: 2 = already exists
119
+ }
120
+
121
+ try {
122
+ // Create branch if needed (based off current HEAD)
123
+ if (!branchExists(branch, cwd)) {
124
+ execSync(`git branch ${branch}`, { cwd, stdio: 'pipe' });
125
+ }
126
+
127
+ // Create worktree directory parent if needed
128
+ const parentDir = join(cwd, '.worktrees');
129
+ if (!existsSync(parentDir)) {
130
+ mkdirSync(parentDir, { recursive: true });
131
+ }
132
+
133
+ // Add worktree
134
+ execSync(`git worktree add "${worktreePath}" ${branch}`, { cwd, stdio: 'pipe' });
135
+
136
+ // Persist in state (non-blocking — feature may not be in state yet)
137
+ try {
138
+ const { loadState, saveState } = await import('../../lib/state/state-manager.js');
139
+ const state = loadState(cwd);
140
+ if (state?.features?.[feature]) {
141
+ state.features[feature].worktree = { path: worktreePath, branch, createdAt: new Date().toISOString() };
142
+ saveState(state, cwd);
143
+ }
144
+ } catch {
145
+ // Non-blocking
146
+ }
147
+
148
+ console.log(JSON.stringify({ created: true, path: worktreePath, branch, feature }));
149
+ process.exit(0);
150
+ } catch (err) {
151
+ console.log(JSON.stringify({ created: false, error: (err.message || 'unknown').slice(0, 200), feature }));
152
+ process.exit(0); // Fail-open
153
+ }
154
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * MORPH-SPEC Scope Escalate Command
3
+ *
4
+ * Handles mid-implementation scope discovery by analyzing impact,
5
+ * regressing phase or expanding tasks, and creating an audit trail.
6
+ *
7
+ * Usage:
8
+ * morph-spec scope escalate <feature> --task <id> --reason "..."
9
+ * morph-spec scope escalate <feature> --task <id> --reason "..." --dry-run
10
+ * morph-spec scope escalate <feature> --task <id> --reason "..." --target design
11
+ */
12
+
13
+ import chalk from 'chalk';
14
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
15
+ import { join } from 'path';
16
+ import { loadState, saveState } from '../../core/state/state-manager.js';
17
+ import { analyzeImpact } from '../../lib/scope/impact-analyzer.js';
18
+
19
+ /**
20
+ * Core escalation logic — exported for testing.
21
+ *
22
+ * @param {string} featureName
23
+ * @param {Object} options
24
+ * @param {string} options.task - Trigger task ID
25
+ * @param {string} options.reason - Reason for escalation
26
+ * @param {string} [options.target] - Override target phase
27
+ * @param {boolean} [options.dryRun] - Don't execute, just recommend
28
+ * @returns {Promise<{ success: boolean, error?: string, recommendation?: Object }>}
29
+ */
30
+ export async function escalateScope(featureName, options) {
31
+ const state = loadState();
32
+ const feature = state.features[featureName];
33
+
34
+ if (!feature) {
35
+ return { success: false, error: `Feature "${featureName}" not found in state.json` };
36
+ }
37
+
38
+ if (feature.phase !== 'implement') {
39
+ return { success: false, error: `Scope escalation only available during implement phase (current: ${feature.phase})` };
40
+ }
41
+
42
+ // Read tasks.md
43
+ const tasksPath = join(process.cwd(), '.morph', 'features', featureName, '4-tasks', 'tasks.md');
44
+ let tasksMd = '';
45
+ try {
46
+ tasksMd = readFileSync(tasksPath, 'utf-8');
47
+ } catch {
48
+ return { success: false, error: 'tasks.md not found' };
49
+ }
50
+
51
+ // Read spec.md (optional — not all features have it)
52
+ const specPath = join(process.cwd(), '.morph', 'features', featureName, '1-design', 'spec.md');
53
+ let specMd = '';
54
+ try { specMd = readFileSync(specPath, 'utf-8'); } catch { /* ok */ }
55
+
56
+ // Analyze impact
57
+ const analysis = analyzeImpact({
58
+ triggerTaskId: options.task,
59
+ tasksMd,
60
+ specMd,
61
+ reason: options.reason
62
+ });
63
+
64
+ // Apply target override
65
+ if (options.target) {
66
+ analysis.recommendation = `regress-${options.target}`;
67
+ analysis.targetPhase = options.target;
68
+ if (analysis.targetPhase === 'design') analysis.impact = 'high';
69
+ else if (analysis.targetPhase === 'tasks') analysis.impact = 'medium';
70
+ }
71
+
72
+ // Dry-run: return recommendation without modifying state
73
+ if (options.dryRun) {
74
+ return { success: true, recommendation: analysis };
75
+ }
76
+
77
+ // Build escalation record
78
+ const escId = `ESC-${String((feature.escalations?.length || 0) + 1).padStart(3, '0')}`;
79
+ const record = {
80
+ id: escId,
81
+ triggerTask: options.task,
82
+ reason: options.reason,
83
+ impact: analysis.impact,
84
+ action: analysis.recommendation,
85
+ affectedTasks: analysis.affectedTasks,
86
+ reviewedTasks: [],
87
+ targetPhase: analysis.targetPhase || 'tasks',
88
+ previousPhase: 'implement',
89
+ createdAt: new Date().toISOString(),
90
+ resolvedAt: null
91
+ };
92
+
93
+ // Execute based on recommendation
94
+ if (analysis.recommendation.startsWith('regress')) {
95
+ const targetPhase = analysis.targetPhase || 'tasks';
96
+
97
+ // Flag completed tasks as needsReview (supports both v2 'done' and v3 'completed')
98
+ if (feature.taskList) {
99
+ for (const task of feature.taskList) {
100
+ if (task.status === 'done' || task.status === 'completed') {
101
+ task.needsReview = true;
102
+ }
103
+ }
104
+ }
105
+
106
+ // Reset phase
107
+ feature.phase = targetPhase;
108
+
109
+ // Reset relevant approval gates
110
+ if (feature.approvalGates) {
111
+ if (targetPhase === 'tasks' && feature.approvalGates.tasks) {
112
+ feature.approvalGates.tasks = { approved: false, timestamp: null, approvedBy: null };
113
+ }
114
+ if (targetPhase === 'design') {
115
+ if (feature.approvalGates.design) {
116
+ feature.approvalGates.design = { approved: false, timestamp: null, approvedBy: null };
117
+ }
118
+ if (feature.approvalGates.tasks) {
119
+ feature.approvalGates.tasks = { approved: false, timestamp: null, approvedBy: null };
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ // Write escalation record to state
126
+ if (!feature.escalations) feature.escalations = [];
127
+ feature.escalations.push(record);
128
+ feature.updatedAt = new Date().toISOString();
129
+
130
+ saveState(state);
131
+
132
+ // Create/append escalation-log.md
133
+ writeEscalationLog(featureName, record);
134
+
135
+ return { success: true, recommendation: analysis, escalationId: escId };
136
+ }
137
+
138
+ /**
139
+ * Write escalation log entry to 4-tasks/escalation-log.md
140
+ */
141
+ function writeEscalationLog(featureName, record) {
142
+ const logDir = join(process.cwd(), '.morph', 'features', featureName, '4-tasks');
143
+ mkdirSync(logDir, { recursive: true });
144
+ const logPath = join(logDir, 'escalation-log.md');
145
+
146
+ const doneTasks = record.reviewedTasks?.length > 0
147
+ ? record.reviewedTasks.join(', ')
148
+ : '(all completed tasks flagged)';
149
+
150
+ const entry = `
151
+ ## ${record.id} — ${record.createdAt.split('T')[0]}
152
+ - **Trigger:** ${record.triggerTask}
153
+ - **Discovery:** ${record.reason}
154
+ - **Impact:** ${record.impact} (${record.affectedTasks.length} tasks affected: ${record.affectedTasks.join(', ')})
155
+ - **Action:** ${record.action} → ${record.targetPhase} phase
156
+ - **Affected completed tasks flagged for review:** ${doneTasks}
157
+ `;
158
+
159
+ let existing = '';
160
+ try { existing = readFileSync(logPath, 'utf-8'); } catch { /* new file */ }
161
+
162
+ if (!existing) {
163
+ writeFileSync(logPath, `# Scope Escalation Log\n${entry}`);
164
+ } else {
165
+ writeFileSync(logPath, existing + entry);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * CLI command handler
171
+ */
172
+ export async function scopeEscalateCommand(feature, options) {
173
+ console.log(chalk.cyan('\n========================================'));
174
+ console.log(chalk.cyan(' MORPH-SPEC SCOPE ESCALATION'));
175
+ console.log(chalk.cyan('========================================\n'));
176
+
177
+ if (!options.task) {
178
+ console.error(chalk.red('--task <id> is required'));
179
+ process.exit(1);
180
+ }
181
+ if (!options.reason) {
182
+ console.error(chalk.red('--reason "..." is required'));
183
+ process.exit(1);
184
+ }
185
+
186
+ const result = await escalateScope(feature, options);
187
+
188
+ if (!result.success) {
189
+ console.error(chalk.red(`\n ${result.error}`));
190
+ process.exit(1);
191
+ }
192
+
193
+ const rec = result.recommendation;
194
+ console.log(chalk.gray('Trigger task:'), rec.triggerTask);
195
+ console.log(chalk.gray('Impact:'), rec.impact);
196
+ console.log(chalk.gray('Affected tasks:'), rec.affectedTasks.join(', '));
197
+ console.log(chalk.gray('Recommendation:'), rec.recommendation);
198
+ if (rec.targetPhase) console.log(chalk.gray('Target phase:'), rec.targetPhase);
199
+
200
+ if (options.dryRun) {
201
+ console.log(chalk.cyan('\n Dry-run — no changes made'));
202
+ process.exit(2);
203
+ }
204
+
205
+ if (rec.recommendation === 'expand') {
206
+ console.log(chalk.green(`\n Escalation ${result.escalationId} recorded (low impact)`));
207
+ console.log(chalk.yellow(` Action: Use task expand to split the task:`));
208
+ console.log(chalk.gray(` morph-spec task expand ${feature} ${options.task} --into "ID: Title" ...`));
209
+ } else {
210
+ console.log(chalk.green(`\n Escalation ${result.escalationId} executed`));
211
+ console.log(chalk.yellow(` Phase regressed to: ${rec.targetPhase || 'tasks'}`));
212
+ }
213
+ console.log(chalk.gray(` Escalation log: .morph/features/${feature}/4-tasks/escalation-log.md`));
214
+ console.log('');
215
+ }