@polymorphism-tech/morph-spec 8.47.0 → 9.1.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 (377) hide show
  1. package/CLAUDE.md +51 -43
  2. package/README.md +39 -38
  3. package/bin/morph-spec.js +41 -113
  4. package/bin/validate.js +1 -147
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +86 -21
  11. package/framework/commands/morph-apply.md +5 -28
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +8 -8
  14. package/framework/commands/morph-infra.md +1 -1
  15. package/framework/commands/morph-preflight.md +2 -3
  16. package/framework/commands/morph-status.md +4 -4
  17. package/framework/docs/CLI.md +574 -286
  18. package/framework/evals/review-checklist.md +82 -41
  19. package/framework/hooks/README.md +10 -11
  20. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  21. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  22. package/framework/hooks/claude-code/core/chain.js +4 -65
  23. package/framework/hooks/claude-code/core/comment-guard.js +3 -17
  24. package/framework/hooks/claude-code/core/denylist.json +33 -3
  25. package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
  26. package/framework/hooks/claude-code/core/gate-guard.js +9 -78
  27. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  28. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  29. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  30. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  31. package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
  32. package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
  33. package/framework/hooks/claude-code/core/state-sync.js +15 -85
  34. package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
  35. package/framework/hooks/claude-code/core/validate-completion.js +37 -147
  36. package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
  37. package/framework/hooks/claude-code/core/worktree-context.js +15 -114
  38. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  39. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  40. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  41. package/framework/hooks/claude-code/statusline.py +55 -41
  42. package/framework/hooks/package.json +3 -0
  43. package/framework/hooks/shared/activity-logger.js +9 -89
  44. package/framework/hooks/shared/agent-reap.js +2 -17
  45. package/framework/hooks/shared/build-check.js +11 -76
  46. package/framework/hooks/shared/claim-registry.js +9 -119
  47. package/framework/hooks/shared/command-tokens.js +168 -44
  48. package/framework/hooks/shared/comment-rules.js +5 -20
  49. package/framework/hooks/shared/comment-scanner.js +11 -43
  50. package/framework/hooks/shared/comment-stripper.js +11 -85
  51. package/framework/hooks/shared/e2e-check.js +0 -0
  52. package/framework/hooks/shared/errata.js +59 -0
  53. package/framework/hooks/shared/feature-locator.js +11 -95
  54. package/framework/hooks/shared/frontend-locations.js +4 -0
  55. package/framework/hooks/shared/gate-question.js +12 -0
  56. package/framework/hooks/shared/git-exclude.js +3 -20
  57. package/framework/hooks/shared/git-paths.js +8 -104
  58. package/framework/hooks/shared/graph-freshness.js +22 -166
  59. package/framework/hooks/shared/graph-refresh.js +21 -149
  60. package/framework/hooks/shared/heavy-command.js +4 -12
  61. package/framework/hooks/shared/hook-response.js +8 -83
  62. package/framework/hooks/shared/morph-cli.js +8 -52
  63. package/framework/hooks/shared/payload-utils.js +10 -132
  64. package/framework/hooks/shared/phase-utils.js +14 -72
  65. package/framework/hooks/shared/redact-secrets.js +4 -15
  66. package/framework/hooks/shared/reparse-scan.js +6 -66
  67. package/framework/hooks/shared/slice-progress.js +99 -0
  68. package/framework/hooks/shared/source-files.js +4 -17
  69. package/framework/hooks/shared/spawn-command.js +23 -0
  70. package/framework/hooks/shared/state-reader.js +51 -247
  71. package/framework/hooks/shared/state-writer.js +9 -54
  72. package/framework/hooks/shared/stdin-reader.js +1 -11
  73. package/framework/hooks/shared/telemetry-logger.js +7 -67
  74. package/framework/hooks/shared/transcript-usage.js +7 -29
  75. package/framework/hooks/shared/worktree-root.js +7 -29
  76. package/framework/mcp/registry.json +3 -4
  77. package/framework/phases.json +23 -39
  78. package/framework/rules/csharp-standards.md +1 -1
  79. package/framework/rules/frontend-standards.md +1 -0
  80. package/framework/rules/hangfire-standards.md +1 -1
  81. package/framework/rules/neon-standards.md +1 -1
  82. package/framework/schemas/config.schema.json +14 -9
  83. package/framework/schemas/feature.schema.json +15 -50
  84. package/framework/skills/README.md +3 -4
  85. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  86. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  87. package/framework/skills/core/morph-apply/SKILL.md +149 -222
  88. package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
  89. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  90. package/framework/skills/core/morph-media/SKILL.md +1 -1
  91. package/framework/skills/core/morph-plan/SKILL.md +137 -299
  92. package/framework/skills/core/morph-review/SKILL.md +111 -136
  93. package/framework/skills/core/morph-standards/SKILL.md +27 -39
  94. package/framework/skills/core/morph-uiux/SKILL.md +94 -152
  95. package/framework/standards/STANDARDS.json +109 -1219
  96. package/framework/standards/STANDARDS.meta.json +1319 -0
  97. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  98. package/framework/standards/ai-agents/agent-session.md +1 -1
  99. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  100. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  101. package/framework/standards/ai-agents/evals-with-cache.md +1 -2
  102. package/framework/standards/ai-agents/guardrails.md +1 -1
  103. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  104. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  105. package/framework/standards/ai-agents/production.md +1 -1
  106. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  107. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  108. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  109. package/framework/standards/ai-agents/setup.md +3 -3
  110. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  111. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  112. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  113. package/framework/standards/ai-agents/workflows.md +1 -1
  114. package/framework/standards/architecture/code-comments.md +7 -7
  115. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  116. package/framework/standards/backend/api/validation.md +1 -1
  117. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  118. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  119. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  120. package/framework/standards/data/nosql/cache/redis.md +1 -1
  121. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  122. package/framework/standards/frontend/admin-app/forms.md +1 -1
  123. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  124. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  125. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  126. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  127. package/framework/templates/CLAUDE.md.core.template +3 -3
  128. package/framework/templates/README.md +2 -3
  129. package/framework/templates/REGISTRY.json +15 -179
  130. package/framework/templates/docs/plan.md +72 -0
  131. package/framework/templates/docs/ui-mockups.md +4 -0
  132. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  134. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  135. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  136. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  137. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  138. package/package.json +1 -1
  139. package/src/commands/context/context-refresh.js +0 -16
  140. package/src/commands/dashboard/dashboard.js +2 -8
  141. package/src/commands/e2e/e2e.js +18 -105
  142. package/src/commands/graph/affected.js +0 -14
  143. package/src/commands/graph/build.js +11 -50
  144. package/src/commands/graph/explain.js +0 -16
  145. package/src/commands/graph/status.js +1 -12
  146. package/src/commands/mcp/mcp-setup.js +4 -62
  147. package/src/commands/observability/cost.js +17 -111
  148. package/src/commands/observability/telemetry.js +1 -14
  149. package/src/commands/project/branch.js +4 -45
  150. package/src/commands/project/claim.js +4 -48
  151. package/src/commands/project/doctor-procs.js +1 -1
  152. package/src/commands/project/doctor.js +53 -307
  153. package/src/commands/project/env.js +1 -27
  154. package/src/commands/project/fleet.js +6 -35
  155. package/src/commands/project/heavy.js +3 -3
  156. package/src/commands/project/init.js +8 -68
  157. package/src/commands/project/install-plugin-cmd.js +0 -1
  158. package/src/commands/project/setup-infra-cmd.js +2 -1
  159. package/src/commands/project/stack-scan.js +58 -0
  160. package/src/commands/project/standards.js +65 -72
  161. package/src/commands/project/status.js +28 -121
  162. package/src/commands/project/update.js +31 -223
  163. package/src/commands/project/worktree.js +109 -883
  164. package/src/commands/scaffold/scaffold.js +0 -25
  165. package/src/commands/state/advance.js +65 -120
  166. package/src/commands/state/approve.js +87 -229
  167. package/src/commands/state/archive.js +23 -139
  168. package/src/commands/state/auto-approval.js +65 -0
  169. package/src/commands/state/create.js +49 -109
  170. package/src/commands/state/delete.js +11 -63
  171. package/src/commands/state/finish.js +105 -494
  172. package/src/commands/state/gate-check.js +74 -183
  173. package/src/commands/state/gate-decision.js +8 -42
  174. package/src/commands/state/handoff.js +4 -31
  175. package/src/commands/state/list.js +21 -91
  176. package/src/commands/state/report-check.js +134 -338
  177. package/src/commands/state/retype.js +12 -28
  178. package/src/commands/validation/index.js +0 -3
  179. package/src/commands/validation/mutate.js +79 -19
  180. package/src/commands/validation/plan-check.js +81 -0
  181. package/src/commands/validation/validate-feature.js +52 -216
  182. package/src/commands/validation/verify.js +45 -160
  183. package/src/core/paths/output-schema.js +9 -155
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +2 -30
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +32 -395
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +21 -0
  191. package/src/core/work-types.js +10 -45
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
  194. package/src/dashboard/app/components/CommandPalette.jsx +4 -11
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +3 -9
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/pages/Activity.jsx +1 -1
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
  211. package/src/dashboard/app/pages/Features.jsx +9 -16
  212. package/src/dashboard/app/pages/Guide.jsx +2 -3
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +14 -83
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +1 -7
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +92 -113
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +4 -7
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/lib/ai/ai-checks.js +7 -52
  233. package/src/lib/ai/ai-project-scan.js +14 -108
  234. package/src/lib/ai/code-audit.js +7 -63
  235. package/src/lib/ai/pin-drift.js +8 -51
  236. package/src/lib/ai/registry-audit.js +11 -90
  237. package/src/lib/ci/ci-verify.js +14 -13
  238. package/src/lib/ci/github-ci.js +9 -37
  239. package/src/lib/ci/workflow-scaffold.js +2 -10
  240. package/src/lib/detectors/claude-config-detector.js +13 -74
  241. package/src/lib/detectors/index.js +8 -59
  242. package/src/lib/e2e/compose-env.js +5 -66
  243. package/src/lib/e2e/stack-teardown.js +6 -40
  244. package/src/lib/generators/architecture-scanner.js +18 -156
  245. package/src/lib/generators/handoff-generator.js +37 -112
  246. package/src/lib/generators/scaffold-generator.js +5 -101
  247. package/src/lib/generators/settings-generator.js +11 -122
  248. package/src/lib/git/branch-classifier.js +11 -84
  249. package/src/lib/git/default-branch.js +14 -169
  250. package/src/lib/git/evidence-staleness.js +2 -1
  251. package/src/lib/git/gh.js +2 -17
  252. package/src/lib/git/pending-prs.js +6 -37
  253. package/src/lib/git/pr-files.js +3 -28
  254. package/src/lib/git/pr-index.js +3 -34
  255. package/src/lib/git/tree-fingerprint.js +17 -17
  256. package/src/lib/git/tree-state.js +6 -33
  257. package/src/lib/graph/god-nodes.js +2 -22
  258. package/src/lib/graph/graph-health.js +3 -24
  259. package/src/lib/graph/graph-loader.js +5 -56
  260. package/src/lib/graph/graphify-runtime.js +7 -37
  261. package/src/lib/graph/graphifyignore.js +8 -74
  262. package/src/lib/graph/sanitize.js +16 -136
  263. package/src/lib/heavy/cleanup.js +2 -6
  264. package/src/lib/installers/effects.js +8 -93
  265. package/src/lib/installers/mcp-installer.js +9 -169
  266. package/src/lib/installers/memory-installer.js +8 -33
  267. package/src/lib/installers/templates-reconcile.js +5 -60
  268. package/src/lib/mutation/commands.js +3 -2
  269. package/src/lib/mutation/dotnet-introspection.js +281 -0
  270. package/src/lib/mutation/plan.js +14 -6
  271. package/src/lib/mutation/runner.js +1 -1
  272. package/src/lib/mutation/subset.js +47 -13
  273. package/src/lib/plan/artifact-vocabulary.js +40 -0
  274. package/src/lib/plan/checks.js +208 -0
  275. package/src/lib/plan/plan-parser.js +204 -0
  276. package/src/lib/plan/plan-template.js +18 -0
  277. package/src/lib/projects/registry.js +7 -44
  278. package/src/lib/review-reports.js +204 -0
  279. package/src/lib/risk-detector.js +131 -274
  280. package/src/lib/specs/adr-index.js +38 -85
  281. package/src/lib/specs/design-seed.js +18 -0
  282. package/src/lib/specs/frontmatter.js +31 -0
  283. package/src/lib/specs/gfm-table.js +11 -0
  284. package/src/lib/specs/revocations.js +31 -102
  285. package/src/lib/specs/screen-checklist.js +56 -132
  286. package/src/lib/stack/dotnet-build-ignores.js +43 -0
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +260 -0
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +37 -0
  291. package/src/lib/standards/sections.js +47 -0
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +23 -179
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -313
  296. package/src/lib/tasks/test-runner.js +107 -447
  297. package/src/lib/telemetry/metrics.js +13 -103
  298. package/src/lib/telemetry/telemetry-reader.js +13 -238
  299. package/src/lib/telemetry/verify-event.js +4 -34
  300. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  301. package/src/lib/validators/code-budget/index.js +0 -7
  302. package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
  303. package/src/lib/validators/contracts/index.js +0 -6
  304. package/src/lib/validators/nextjs/index.js +0 -5
  305. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  306. package/src/lib/validators/packages/index.js +0 -6
  307. package/src/lib/validators/packages/package-validator.js +6 -56
  308. package/src/lib/validators/reuse/index.js +0 -8
  309. package/src/lib/validators/reuse/reuse-validator.js +24 -137
  310. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  311. package/src/lib/validators/traceability-check.js +54 -156
  312. package/src/lib/validators/validation-runner.js +20 -125
  313. package/src/lib/validators/verify-runner.js +152 -575
  314. package/src/lib/work-type-detector.js +5 -67
  315. package/src/lib/worktree/docker-audit.js +17 -137
  316. package/src/lib/worktree/env-allocator.js +10 -98
  317. package/src/lib/worktree/feature-truth.js +12 -115
  318. package/src/lib/worktree/finish-backup.js +32 -242
  319. package/src/lib/worktree/fleet-scanner.js +31 -126
  320. package/src/lib/worktree/ignored-triage.js +15 -91
  321. package/src/lib/worktree/vscode-settings.js +2 -3
  322. package/src/lib/worktree/worktree-audit.js +21 -213
  323. package/src/lib/worktree/worktree-include.js +3 -5
  324. package/src/scripts/global-install.js +3 -9
  325. package/src/scripts/install-plugin.js +1 -13
  326. package/src/scripts/setup-infra.js +17 -138
  327. package/src/utils/agents-installer.js +92 -272
  328. package/src/utils/banner.js +4 -25
  329. package/src/utils/claude-md-injector.js +1 -37
  330. package/src/utils/claude-settings-manager.js +1 -87
  331. package/src/utils/file-copier.js +32 -90
  332. package/src/utils/hooks-installer.js +26 -296
  333. package/src/utils/skills-installer.js +4 -102
  334. package/src/utils/version-checker.js +4 -83
  335. package/framework/commands/morph-proposal.md +0 -57
  336. package/framework/evals/trace-schema.md +0 -67
  337. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  338. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  339. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  340. package/framework/hooks/shared/tasks-validator.js +0 -111
  341. package/framework/rules/morph-task-tracking.md +0 -54
  342. package/framework/rules/morph-workflow.md +0 -244
  343. package/framework/schemas/tasks.schema.json +0 -79
  344. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  345. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  346. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  347. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  348. package/framework/templates/docs/proposal.md +0 -192
  349. package/framework/templates/docs/spec.md +0 -57
  350. package/framework/templates/docs/ui-components.md +0 -52
  351. package/framework/templates/docs/ui-design-system.md +0 -76
  352. package/framework/templates/docs/ui-flows.md +0 -167
  353. package/framework/templates/examples/spec-examples.md +0 -91
  354. package/framework/templates/feature/decisions.md +0 -124
  355. package/framework/templates/feature/recap.md +0 -145
  356. package/framework/templates/tasks.template.json +0 -17
  357. package/src/commands/state/usecase.js +0 -143
  358. package/src/commands/tasks/dag.js +0 -181
  359. package/src/commands/utils/index.js +0 -3
  360. package/src/lib/agents/output-collector.js +0 -76
  361. package/src/lib/detectors/design-system-detector.js +0 -188
  362. package/src/lib/evaluator-report.js +0 -104
  363. package/src/lib/generators/design-system-generator.js +0 -508
  364. package/src/lib/specs/acervo-format.js +0 -70
  365. package/src/lib/specs/acervo-lifecycle.js +0 -264
  366. package/src/lib/specs/done-criteria-check.js +0 -200
  367. package/src/lib/specs/usecase-check.js +0 -301
  368. package/src/lib/specs/usecase-promoter.js +0 -301
  369. package/src/lib/specs/usecase-registry.js +0 -237
  370. package/src/lib/tasks/dispatch-planner.js +0 -319
  371. package/src/lib/tasks/dotnet-test-introspection.js +0 -632
  372. package/src/lib/tasks/mermaid-renderer.js +0 -132
  373. package/src/lib/tasks/task-classifier.js +0 -30
  374. package/src/lib/tasks/task-parser.js +0 -209
  375. package/src/lib/tasks/tasks-json-parser.js +0 -296
  376. package/src/lib/tasks/test-scope.js +0 -597
  377. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,47 +1,18 @@
1
- /**
2
- * Shared hook response builders for Claude Code hooks.
3
- *
4
- * Claude Code hooks communicate via JSON to stdout.
5
- * - PreToolUse: { "decision": "block"|"approve", "reason": "..." }
6
- * - SessionStart/Stop/PostToolUse/etc (non-blocking context injection):
7
- * { "hookSpecificOutput": { "hookEventName": "...", "additionalContext": "..." } }
8
- * A top-level `additionalContext` is NOT honored by Claude Code for any
9
- * event — it must be nested under `hookSpecificOutput` alongside the
10
- * event name (see https://code.claude.com/docs/en/hooks.md).
11
- */
1
+ // A top-level `additionalContext` is NOT honored for any event: it must be nested
2
+ // under `hookSpecificOutput` (https://code.claude.com/docs/en/hooks.md).
12
3
 
13
- /**
14
- * Block a tool use with a reason.
15
- * @param {string} reason - Human-readable reason for blocking
16
- */
17
4
  export function block(reason) {
18
5
  console.log(JSON.stringify({ decision: 'block', reason }));
19
6
  process.exit(0);
20
7
  }
21
8
 
22
- /**
23
- * Approve a tool use, optionally injecting context.
24
- * @param {string} [context] - Additional context to inject
25
- */
26
9
  export function approve(context) {
27
10
  if (context) {
28
11
  console.log(JSON.stringify({ decision: 'approve', additionalContext: context }));
29
12
  }
30
- // No output = implicit approve
31
13
  process.exit(0);
32
14
  }
33
15
 
34
- /**
35
- * Inject additional (non-blocking) context for the current hook event.
36
- *
37
- * Must be nested under `hookSpecificOutput` — Claude Code does not honor a
38
- * top-level `additionalContext` for any event.
39
- *
40
- * @param {string} text - Context text to inject
41
- * @param {string} eventName - The hook event this is responding to (e.g.
42
- * 'SessionStart', 'Stop', 'PostToolUse') — required, echoed back as
43
- * `hookSpecificOutput.hookEventName`.
44
- */
45
16
  export function injectContext(text, eventName) {
46
17
  console.log(JSON.stringify({
47
18
  hookSpecificOutput: { hookEventName: eventName, additionalContext: text },
@@ -49,63 +20,26 @@ export function injectContext(text, eventName) {
49
20
  process.exit(0);
50
21
  }
51
22
 
52
- /**
53
- * Silent exit — no output, no blocking.
54
- */
55
23
  export function pass() {
56
24
  process.exit(0);
57
25
  }
58
26
 
59
- // ── Pure (non-exiting) result builders ──────────────────────────────────────
60
- //
61
- // Every core hook used to run inline at module scope and call block()/pass()/
62
- // injectContext() above to print+exit(0) the moment it knew its verdict. That
63
- // stops being safe once a hook's logic moves into an exported `run(payload)`
64
- // that can ALSO be called by framework/hooks/claude-code/core/chain.js — which
65
- // imports several hooks' run() into ONE process and cannot have any of them
66
- // call process.exit() mid-chain (it would kill the other links). run() must
67
- // instead RETURN the verdict object; chain.js (or this file's own
68
- // runStandalone(), below) is what actually prints it and exits.
69
- //
70
- // These builders produce the exact same JSON shapes as block()/approve()/
71
- // injectContext() above — keep them in sync if that shape ever changes.
72
-
73
- /** Pure equivalent of block(reason) — returns the verdict instead of printing+exiting. */
27
+ // `run()` returns these instead of exiting: chain.js runs several hooks in one
28
+ // process. Same JSON shapes as block()/approve()/injectContext() above.
74
29
  export function blockResult(reason) {
75
30
  return { decision: 'block', reason };
76
31
  }
77
32
 
78
- /** Pure equivalent of approve(context) — null means "pass" (nothing to say). */
79
33
  export function approveResult(context) {
80
34
  return context ? { decision: 'approve', additionalContext: context } : null;
81
35
  }
82
36
 
83
- /** Pure equivalent of injectContext(text, eventName). */
84
37
  export function contextResult(text, eventName) {
85
38
  return { hookSpecificOutput: { hookEventName: eventName, additionalContext: text } };
86
39
  }
87
40
 
88
- /**
89
- * Standalone tail every core hook calls as its last line:
90
- * `await runStandalone(run);`
91
- *
92
- * Reads stdin once, calls the hook's `run(payload)`, prints the JSON verdict
93
- * (if any) and exits 0 — reproducing exactly what block()/injectContext()/
94
- * pass() used to do when a hook's logic lived inline at module scope.
95
- *
96
- * No-ops when MORPH_HOOK_CHAINED is set: that means core/chain.js already
97
- * imported this module and will call run() itself with a payload it read
98
- * once for the whole chain — this file must not ALSO read stdin or exit here
99
- * (doing so would race chain.js's own stdin read and kill the chain process
100
- * after the first link). chain.js sets the flag BEFORE its first dynamic
101
- * import, so every hook it loads sees it already set at call time.
102
- *
103
- * Fail-open: any error (including a rejected run()) prints nothing and exits
104
- * 0 — the same silent-pass behavior every hook's own outer try/catch used to
105
- * produce on an unexpected error.
106
- *
107
- * @param {(payload: object) => Promise<object|null>} run
108
- */
41
+ // No-op under MORPH_HOOK_CHAINED: chain.js reads stdin once for the whole chain,
42
+ // and reading or exiting here would kill it after the first link.
109
43
  export async function runStandalone(run) {
110
44
  if (process.env.MORPH_HOOK_CHAINED) return;
111
45
 
@@ -123,17 +57,8 @@ export async function runStandalone(run) {
123
57
  process.exit(0);
124
58
  }
125
59
 
126
- /**
127
- * Pure builder for a PreToolUse rewrite: Claude Code runs the tool with
128
- * `updatedInput` in place of the original `tool_input`, and the decision is
129
- * an explicit `allow` (a rewrite without a decision is ignored). Use it ONLY
130
- * when the hook actually changed something — an unconditional `allow` would
131
- * skip the permission prompt for every call it touches.
132
- *
133
- * @param {object} updatedInput - the full replacement tool_input
134
- * @param {string} eventName - 'PreToolUse'
135
- * @param {string} [context] - optional additionalContext (what was changed)
136
- */
60
+ // A rewrite without a decision is ignored, so it carries `allow`: use it ONLY when
61
+ // the input changed, or every call it touches skips the permission prompt.
137
62
  export function updatedInputResult(updatedInput, eventName, context) {
138
63
  const out = { hookEventName: eventName, permissionDecision: 'allow', updatedInput };
139
64
  if (context) out.additionalContext = context;
@@ -1,36 +1,10 @@
1
- /**
2
- * morph-cli.js — run the morph-spec CLI from inside a hook.
3
- *
4
- * Hooks are installed STANDALONE into `.morph/framework/hooks/` and may never
5
- * import from `src/`; the only way for a hook to reuse a CLI command (the
6
- * WorktreeCreate hook needs `worktree setup`/`provision`, WorktreeRemove needs
7
- * `worktree remove --if-clean`) is to spawn the CLI as a subprocess. This
8
- * module is the ONE place that knows how to find it.
9
- *
10
- * Resolution order:
11
- * 1. `MORPH_SPEC_BIN` — an explicit command, possibly with leading args and
12
- * quoted paths (`node "R:/my repo/bin/morph-spec.js"`). What tests and a
13
- * framework checkout use.
14
- * 2. `morph-spec` on PATH — the npm-installed bin. On win32 the shim is
15
- * `morph-spec.cmd`, which Node refuses to spawn without a shell, so it is
16
- * located by scanning PATH ourselves and then run through the shell with
17
- * each arg quoted; on POSIX a plain `shell:false` spawn.
18
- * 3. `npx --no-install morph-spec` — a project-local dependency.
19
- *
20
- * ENOENT (or the shell's "not recognized"/"could not determine executable")
21
- * everywhere → `{ unavailable: true }`, never a throw: the caller decides what
22
- * degraded behavior looks like.
23
- *
24
- * `json` is the LAST line of stdout that parses as a JSON object (the CLI's
25
- * commands print exactly one JSON line; anything a stray console.log adds
26
- * before it is skipped), or null.
27
- */
28
-
1
+ // A hook cannot import `src/`, so it reuses a CLI command by spawning the CLI; this is the one
2
+ // place that finds it. On win32 `morph-spec.cmd` is located on PATH by hand and run through the
3
+ // shell, since Node refuses to spawn a `.cmd` without one.
29
4
  import { spawnSync } from 'child_process';
30
5
  import { existsSync } from 'fs';
31
6
  import { delimiter, join } from 'path';
32
7
 
33
- /** Split a command string into argv, honoring single and double quotes. */
34
8
  function splitCommand(command) {
35
9
  const tokens = [];
36
10
  const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
@@ -39,22 +13,13 @@ function splitCommand(command) {
39
13
  return tokens;
40
14
  }
41
15
 
42
- /** Quote one argument for cmd.exe (the only shell path this module uses). */
43
16
  export function quoteForCmd(arg) {
44
17
  if (arg === '') return '""';
45
18
  if (!/[\s"&|<>^()]/.test(arg)) return arg;
46
19
  return `"${arg.replace(/"/g, '""')}"`;
47
20
  }
48
21
 
49
- /**
50
- * First PATH entry holding any of `files`, or null. The entries are walked in
51
- * PATH order and every name is tried inside each one, the way cmd.exe applies
52
- * PATHEXT: a `graphify.cmd` early in PATH wins over a `graphify.exe` later.
53
- *
54
- * @param {string[]} files
55
- * @param {NodeJS.ProcessEnv} env
56
- * @returns {string|null}
57
- */
22
+ /** Walks PATH in order and tries every name in each entry, the way cmd.exe applies PATHEXT. */
58
23
  export function findFirstOnPath(files, env) {
59
24
  const raw = env.PATH || env.Path || env.path || '';
60
25
  for (const dir of raw.split(delimiter)) {
@@ -69,7 +34,6 @@ export function findFirstOnPath(files, env) {
69
34
 
70
35
  const findOnPath = (file, env) => findFirstOnPath([file], env);
71
36
 
72
- /** Last stdout line that parses as a JSON object, or null. */
73
37
  export function lastJsonLine(stdout) {
74
38
  const lines = String(stdout || '').split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
75
39
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -82,10 +46,6 @@ export function lastJsonLine(stdout) {
82
46
  return null;
83
47
  }
84
48
 
85
- /**
86
- * The candidates to try, in order. Each is `{ file, args, shell }`.
87
- * @param {NodeJS.ProcessEnv} env
88
- */
89
49
  function candidates(env) {
90
50
  const list = [];
91
51
  const explicit = env.MORPH_SPEC_BIN && splitCommand(env.MORPH_SPEC_BIN);
@@ -107,10 +67,8 @@ function candidates(env) {
107
67
  const NOT_FOUND_RE = /not recognized as an internal or external command|could not determine executable to run|command not found|ENOENT/i;
108
68
 
109
69
  /**
110
- * Run `morph-spec <args>` and return its result.
111
- *
112
- * @param {string[]} args - CLI argv after the program name
113
- * @param {{ cwd?: string, timeoutMs?: number, env?: NodeJS.ProcessEnv }} [opts]
70
+ * A missing CLI is `{ unavailable: true }`, never a throw. `json` is the last stdout line that
71
+ * parses as an object.
114
72
  * @returns {{ ok: boolean, code: number|null, stdout: string, stderr: string, json: object|null, unavailable: boolean }}
115
73
  */
116
74
  export function runMorphSpec(args, { cwd, timeoutMs = 120000, env } = {}) {
@@ -134,10 +92,8 @@ export function runMorphSpec(args, { cwd, timeoutMs = 120000, env } = {}) {
134
92
  }
135
93
  const stdout = r.stdout || '';
136
94
  const stderr = r.stderr || '';
137
- // The npx/shell paths cannot surface ENOENT: the shell (or npx itself)
138
- // reports "not found" as a non-zero exit with a message. Only those two
139
- // kinds are read that way — a real morph-spec failure is never mistaken
140
- // for absence.
95
+ // Shell and npx report "not found" as a non-zero exit, never ENOENT; the explicit
96
+ // MORPH_SPEC_BIN is excluded so a real morph-spec failure is never read as absence.
141
97
  if (cand.kind !== 'env' && r.status !== 0 && !stdout.trim() && NOT_FOUND_RE.test(stderr)) continue;
142
98
  return { ok: r.status === 0, code: r.status, stdout, stderr, json: lastJsonLine(stdout), unavailable: false };
143
99
  }
@@ -1,32 +1,9 @@
1
- /**
2
- * Shared payload utilities for Claude Code Write/Edit hooks.
3
- *
4
- * Extracts file path and content from tool_input payloads,
5
- * normalizing between Write (content, file_path) and Edit (new_string, old_string, file_path).
6
- */
7
-
8
1
  import { createHash } from 'crypto';
9
2
  import { existsSync } from 'fs';
10
3
  import { dirname, join, isAbsolute } from 'path';
11
4
 
12
- /**
13
- * Resolve the project root that OWNS a given file: walk up from the file's
14
- * directory to the nearest ancestor containing one of `markers` (default
15
- * `package.json` — the tsc/build root). Falls back to `fallbackCwd` when
16
- * nothing matches or on any error.
17
- *
18
- * This exists so a PostToolUse hook typechecks/builds in the CORRECT root when
19
- * a subagent edits a file inside its own git worktree (`isolation: "worktree"`,
20
- * morph-apply §3b-w) instead of always assuming the main working tree. In the
21
- * normal (non-worktree) case it resolves to the same root as `process.cwd()`.
22
- *
23
- * Fail-open: never throws — returns `fallbackCwd` on any error.
24
- *
25
- * @param {string} filePath - edited file path (absolute or relative)
26
- * @param {string} fallbackCwd - root to use if resolution fails (usually process.cwd())
27
- * @param {string[]} [markers] - ancestor marker filenames to look for
28
- * @returns {string} the resolved root directory
29
- */
5
+ // A sub-agent may edit inside its own worktree: the build root comes from the
6
+ // file, not from the session's cwd.
30
7
  export function resolveRootForFile(filePath, fallbackCwd, markers = ['package.json']) {
31
8
  try {
32
9
  if (!filePath) return fallbackCwd;
@@ -48,33 +25,9 @@ export function resolveRootForFile(filePath, fallbackCwd, markers = ['package.js
48
25
  }
49
26
  }
50
27
 
51
- /**
52
- * Resolve the project root that OWNS a `.morph/features/...` path — the tree
53
- * whose `.morph/` the hook must read and write for that file.
54
- *
55
- * A hook process knows only where the SESSION lives (`process.cwd()` /
56
- * `CLAUDE_PROJECT_DIR`). That is not the same thing as where the FILE lives: a
57
- * session rooted in the primary repo can legitimately write to a feature that
58
- * lives in a secondary git worktree. Resolving state from the process instead
59
- * of the path is what made `enforce-phase-writes` block a legal `2-plan/` write
60
- * (the root still had the feature at `proposal`) while `protect-spec-files`
61
- * simultaneously went DARK there (the root had no approved gate to enforce) —
62
- * one defect, opposite directions.
63
- *
64
- * The anchor is exact, so no marker walk is needed: everything before the
65
- * file's own `.morph/features/` segment IS the owning root. `lastIndexOf` picks
66
- * the CLOSEST enclosing `.morph/` — the worktree's, not the root it nests under.
67
- *
68
- * Note this cannot be solved by locating the hook file itself: `worktree setup`
69
- * junctions `.morph/framework/` back to the primary root, so every worktree
70
- * executes the very same hook file. Only the target path identifies the tree.
71
- *
72
- * Fail-open: never throws — returns `fallbackCwd` for any non-feature path.
73
- *
74
- * @param {string} filePath - target file path (absolute or relative)
75
- * @param {string} fallbackCwd - root to use when the path owns no `.morph/`
76
- * @returns {string} the resolved project root
77
- */
28
+ // State is resolved from the target PATH, never the session: a session in the
29
+ // primary root may write a feature that lives in a worktree. `lastIndexOf` picks
30
+ // the closest enclosing `.morph/`.
78
31
  export function resolveMorphRoot(filePath, fallbackCwd) {
79
32
  try {
80
33
  if (!filePath) return fallbackCwd;
@@ -88,111 +41,41 @@ export function resolveMorphRoot(filePath, fallbackCwd) {
88
41
  }
89
42
  }
90
43
 
91
- /**
92
- * Is this an already-absolute path, on EITHER convention?
93
- *
94
- * `path.isAbsolute` is platform-bound: `C:/x` is absolute on win32 and relative
95
- * on posix. That asymmetry is a real defect here, not a theoretical one — a
96
- * Windows-shaped path reaching a posix `join()` produces `/cwd/C:/x`, silently
97
- * resolving state against a directory that cannot exist. Recognizing the drive
98
- * letter explicitly makes the answer the same everywhere (and is a no-op on
99
- * win32, where `isAbsolute` already accepts it).
100
- */
44
+ // `path.isAbsolute` is platform-bound: on posix `C:/x` would join into `/cwd/C:/x`.
101
45
  const isAbsolutePath = (p) => isAbsolute(p) || /^[a-zA-Z]:\//.test(p);
102
46
 
103
- /** Normalize to forward slashes and make absolute against `fallbackCwd`. */
104
47
  function toAbsolutePosix(filePath, fallbackCwd) {
105
48
  const p = String(filePath).replace(/\\/g, '/');
106
49
  return isAbsolutePath(p) ? p : join(fallbackCwd, p).replace(/\\/g, '/');
107
50
  }
108
51
 
109
- /**
110
- * Absolute path of a hook's target file, resolved against the root that owns
111
- * it. Pairs with `resolveMorphRoot` so a hook never mixes "the tree I read
112
- * state from" with "the file I read content from".
113
- *
114
- * @param {string} filePath
115
- * @param {string} fallbackCwd
116
- * @returns {string}
117
- */
118
52
  export function resolveTargetPath(filePath, fallbackCwd) {
119
53
  return toAbsolutePosix(filePath || '', fallbackCwd);
120
54
  }
121
55
 
122
- /**
123
- * Get the file path from a hook payload, normalizing backslashes.
124
- * @param {Object} payload - Hook payload from stdin
125
- * @returns {string} Normalized file path or empty string
126
- */
127
56
  export function getFilePath(payload) {
128
57
  const raw = payload?.tool_input?.file_path || payload?.tool_input?.path || '';
129
58
  return raw.replace(/\\/g, '/');
130
59
  }
131
60
 
132
- /**
133
- * Get the content being written or edited.
134
- * For Write: returns `content` (full file content).
135
- * For Edit: returns `new_string` (replacement text).
136
- * @param {Object} payload - Hook payload from stdin
137
- * @returns {string} Content string or empty string
138
- */
139
61
  export function getContentToValidate(payload) {
140
62
  const input = payload?.tool_input;
141
63
  if (!input) return '';
142
- // Write tool uses 'content', Edit tool uses 'new_string'
143
64
  return input.content || input.new_string || '';
144
65
  }
145
66
 
146
- /**
147
- * Check if this is a Write operation (full file content available).
148
- * @param {Object} payload - Hook payload from stdin
149
- * @returns {boolean} True if Write (content exists), false if Edit or unknown
150
- */
151
67
  export function isWriteOperation(payload) {
152
68
  return typeof payload?.tool_input?.content === 'string';
153
69
  }
154
70
 
155
- /**
156
- * Get the tool name from a hook payload.
157
- * @param {Object} payload - Hook payload from stdin
158
- * @returns {string} Tool name or 'unknown'
159
- */
160
71
  export function getToolName(payload) {
161
72
  return payload?.tool_name || 'unknown';
162
73
  }
163
74
 
164
- /** Assinatura de Bash: comando normalizado, truncado. Ver getCommandSignature. */
165
75
  const BASH_SIGNATURE_MAX = 120;
166
76
 
167
- /**
168
- * Build a normalized signature for loop detection.
169
- * - Edit/Write: "Edit:normalized/file/path#<sha1[0:8] do conteúdo>" ("Edit:path" sem conteúdo)
170
- * - Bash: "Bash:<normalized command, truncated>"
171
- * - Every other tool WITH input: "Tool:<truncated JSON of tool_input>"
172
- * - No tool_input at all: just the tool name
173
- *
174
- * The general (non-Edit/Write/Bash) case matters: a burst of `Read`/`Skill`/
175
- * `Grep`/`Agent` calls against DIFFERENT targets (phase-context loading,
176
- * post-task evaluation reads, parallel subagent dispatch) is normal work, not
177
- * a loop. Collapsing every one of them to the bare tool name is what made
178
- * loop-detect fire on exactly that legitimate traffic — a real, reported
179
- * false positive. Folding in the actual input (rather than hand-listing a
180
- * target field per tool) fixes it uniformly for every tool at once.
181
- *
182
- * **Bash ficou de fora daquele fix (2026-07-22) e continuou colapsando em DUAS
183
- * PALAVRAS**, o que é quase sempre o nome da ferramenta e nada mais:
184
- * `npm test -- a`, `npm test -- b` e `npm run build` viravam `Bash:npm test` /
185
- * `Bash:npm run`. Um ciclo legítimo de mutação — rodar o mesmo runner sobre
186
- * alvos diferentes, que é exatamente como se testa se um teste morde — disparava
187
- * o aviso de loop três vezes seguidas. Aviso que toca à toa é aviso que se
188
- * aprende a ignorar, e aí ele não serve nem quando está certo.
189
- *
190
- * O discriminador agora é o comando inteiro (normalizado e truncado), mesma
191
- * forma dos demais tools. Repetir o comando IDÊNTICO continua sendo loop.
192
- *
193
- * @param {Object} payload - Hook payload from stdin
194
- * @returns {string} Signature string
195
- */
77
+ // The input is part of the signature: the same tool on DIFFERENT targets is
78
+ // work, not a loop. Only an identical call repeats the signature.
196
79
  export function getCommandSignature(payload) {
197
80
  const tool = getToolName(payload);
198
81
  if (tool === 'unknown') return 'unknown';
@@ -203,11 +86,8 @@ export function getCommandSignature(payload) {
203
86
  if (tool === 'Edit' || tool === 'Write') {
204
87
  const path = (input.file_path || input.path || '').replace(/\\/g, '/');
205
88
  if (!path) return tool;
206
- // O caminho sozinho confundia TRABALHO com LOOP: editar regiões diferentes
207
- // de dois arquivos (a forma normal de uma correção que cruza teste e
208
- // fonte) dava só duas assinaturas e disparava o aviso. O conteúdo da
209
- // edição entra na assinatura; repetir a MESMA edição continua casando, e o
210
- // ping-pong A→B / B→A continua dando duas assinaturas — loop de verdade.
89
+ // O caminho sozinho confundia editar regiões diferentes com loop; o
90
+ // ping-pong A→B / B→A continua dando duas assinaturas.
211
91
  const body = tool === 'Write'
212
92
  ? input.content
213
93
  : (input.old_string !== undefined || input.new_string !== undefined)
@@ -218,8 +98,6 @@ export function getCommandSignature(payload) {
218
98
  }
219
99
 
220
100
  if (tool === 'Bash') {
221
- // Espaços colapsados para que a mesma invocação escrita com espaçamento
222
- // diferente continue casando — o alvo é o comando, não a digitação.
223
101
  const cmd = (input.command || '').replace(/\s+/g, ' ').trim();
224
102
  return cmd ? `Bash:${cmd.slice(0, BASH_SIGNATURE_MAX)}` : 'Bash';
225
103
  }
@@ -1,136 +1,78 @@
1
1
  // GENERATED by scripts/generate-refs.js — DO NOT EDIT manually
2
2
  // Source of truth: src/core/paths/output-schema.js
3
3
  // Regenerate with: node scripts/generate-refs.js
4
- /**
5
- * Shared phase utilities for Claude Code hooks.
6
- *
7
- * Maps phases to directories and output types.
8
- */
9
4
 
10
- /** Phase order */
11
- export const PHASE_ORDER = ["proposal","uiux","plan","implement","review","sync"];
5
+ export const PHASE_ORDER = ["plan","uiux","implement","review"];
12
6
 
13
- /** Map phase → allowed output subdirectory */
14
7
  export const PHASE_DIRS = {
15
- proposal: '0-proposal',
16
- uiux: '1-design',
17
8
  plan: '2-plan',
9
+ uiux: '1-design',
18
10
  implement: '3-implement',
19
11
  review: '4-review',
20
- sync: '3-implement',
21
12
  };
22
13
 
23
- /** Map output type (camelCase) → phase that produces it */
24
14
  export const OUTPUT_PHASE_MAP = {
25
- proposal: 'proposal',
26
- stackScan: 'proposal',
15
+ plan: 'plan',
16
+ checks: 'plan',
27
17
  uiDesignSystem: 'uiux',
28
18
  uiMockups: 'uiux',
29
19
  uiComponents: 'uiux',
30
20
  uiFlows: 'uiux',
31
- spec: 'plan',
32
- tasks: 'plan',
33
- decisions: 'plan',
34
- usecases: 'plan',
35
- recap: 'implement',
36
21
  evidencias: 'implement',
37
- reviewReport: 'review',
38
- evaluatorReport: 'review',
22
+ verifierReport: 'review',
23
+ reviewerReport: 'review',
24
+ securityReport: 'review',
39
25
  };
40
26
 
41
- /** Map filename → output type (camelCase) */
42
27
  export const FILENAME_TO_OUTPUT = {
43
- 'proposal.md': 'proposal',
44
- 'stack-scan.md': 'stackScan',
28
+ 'plan.md': 'plan',
29
+ 'checks.json': 'checks',
45
30
  'design-system.md': 'uiDesignSystem',
46
31
  'mockups.md': 'uiMockups',
47
32
  'components.md': 'uiComponents',
48
33
  'flows.md': 'uiFlows',
49
- 'spec.md': 'spec',
50
- 'tasks.json': 'tasks',
51
- 'decisions.md': 'decisions',
52
- 'recap.md': 'recap',
53
- 'review-report.md': 'reviewReport',
54
- 'evaluator-report.md': 'evaluatorReport',
34
+ 'verifier.md': 'verifierReport',
35
+ 'reviewer.md': 'reviewerReport',
36
+ 'security.md': 'securityReport',
55
37
  };
56
38
 
57
- /** Protected spec files and the approval gate that locks them */
58
39
  export const PROTECTED_SPEC_FILES = {
40
+ 'plan.md': 'plan',
41
+ 'checks.json': 'plan',
59
42
  'design-system.md': 'uiux',
60
43
  'mockups.md': 'uiux',
61
44
  'components.md': 'uiux',
62
45
  'flows.md': 'uiux',
63
- 'spec.md': 'plan',
64
- 'tasks.json': 'plan',
65
46
  };
66
47
 
67
- /**
68
- * For protected files that carry mutable progress fields: filename → the
69
- * top-level array-item fields that may change after the gate is approved.
70
- * When a protected file is listed here, the protect-spec-files hook does a
71
- * semantic diff (allowing only these fields to change) instead of a blanket block.
72
- */
73
- export const MUTABLE_SPEC_FIELDS = {
74
- 'tasks.json': ['status', 'outputs', 'notes'],
75
- };
76
48
 
77
- /**
78
- * Extract feature name from a .morph/features/{feature}/... path
79
- * @param {string} filePath - File path to analyze
80
- * @returns {string|null} Feature name or null
81
- */
82
49
  export function extractFeatureName(filePath) {
83
50
  const normalized = filePath.replace(/\\/g, '/');
84
51
  const match = normalized.match(/\.morph\/features\/([^/]+)\//);
85
52
  return match ? match[1] : null;
86
53
  }
87
54
 
88
- /**
89
- * Extract phase subdirectory from a .morph/features/{feature}/{phaseDir}/... path
90
- * @param {string} filePath - File path to analyze
91
- * @returns {string|null} Phase directory (e.g., '0-proposal', '2-design') or null
92
- */
93
55
  export function extractPhaseDir(filePath) {
94
56
  const normalized = filePath.replace(/\\/g, '/');
95
57
  const match = normalized.match(/\.morph\/features\/[^/]+\/(\d+-[^/]+)\//);
96
58
  return match ? match[1] : null;
97
59
  }
98
60
 
99
- /**
100
- * Check if a file path is inside .morph/features/
101
- * @param {string} filePath
102
- * @returns {boolean}
103
- */
104
61
  export function isFeaturePath(filePath) {
105
62
  const normalized = filePath.replace(/\\/g, '/');
106
63
  return normalized.includes('.morph/features/');
107
64
  }
108
65
 
109
- /**
110
- * Check if a file path is inside .morph/framework/ (readonly)
111
- * @param {string} filePath
112
- * @returns {boolean}
113
- */
114
66
  export function isFrameworkPath(filePath) {
115
67
  const normalized = filePath.replace(/\\/g, '/');
116
68
  return normalized.includes('.morph/framework/');
117
69
  }
118
70
 
119
- /**
120
- * Check if a file path is state.json
121
- * @param {string} filePath
122
- * @returns {boolean}
123
- */
124
71
  export function isStatePath(filePath) {
125
72
  const normalized = filePath.replace(/\\/g, '/');
126
73
  return normalized.endsWith('.morph/state.json');
127
74
  }
128
75
 
129
- /**
130
- * Get the basename of a file path
131
- * @param {string} filePath
132
- * @returns {string}
133
- */
134
76
  export function getBasename(filePath) {
135
77
  const normalized = filePath.replace(/\\/g, '/');
136
78
  return normalized.split('/').pop();
@@ -1,14 +1,6 @@
1
- /**
2
- * Redaction of obvious secrets in free text that telemetry persists (Bash
3
- * commands, dispatch prompts). events.jsonl is copied into finish backups
4
- * under `.git/`, so whatever lands there outlives the session.
5
- *
6
- * Pattern-based by design: it catches a secret written NEXT TO its name
7
- * (`KEY=…`, `Authorization: …`, `user:pass@`) or in a well-known token shape.
8
- * A bare value with no name beside it is invisible to any pattern — which is
9
- * why tool OUTPUT (stdout/stderr, a failure's `error` body) is never persisted
10
- * at all instead of being redacted.
11
- */
1
+ // Catches a secret written next to its name or in a known token shape; a bare
2
+ // value is invisible to any pattern, which is why tool OUTPUT is never
3
+ // persisted at all instead of being redacted.
12
4
 
13
5
  export const REDACTED = '[REDACTED]';
14
6
 
@@ -43,10 +35,7 @@ const RULES = [
43
35
  [/\beyJ[A-Za-z0-9_-]{5,}\.eyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]+/g, REDACTED],
44
36
  ];
45
37
 
46
- /**
47
- * @param {*} text
48
- * @returns {*} the text with secrets replaced by `[REDACTED]`; non-strings unchanged
49
- */
38
+ /** Non-strings pass through unchanged. */
50
39
  export function redactSecrets(text) {
51
40
  if (typeof text !== 'string' || text === '') return text;
52
41
  let out = text;