@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,43 +1,18 @@
1
- /**
2
- * O checklist de tela do `1-design/mockups.md` — as decisões que uma tela de
3
- * dados precisa (volume, paginação, ordenação, filtro, os três estados,
4
- * mobile, abas) escritas por tela, na fase uiux.
5
- *
6
- * Existe porque o que o design não pergunta não vira `doneCriteria`, e o que
7
- * não é critério ninguém entrega: telas de admin saíam listando tudo, sem
8
- * paginar nem ordenar, com o design aprovado. Os leitores do MESMO parser:
9
- *
10
- * - `gate-check uiux` pausa com os problemas nomeados;
11
- * - `approve`/`advance` com assinatura `auto` recusam a mesma pausa sem uma
12
- * decisão gravada (`uiuxAutoApprovalBlock`) — a regra é a do risk-detector;
13
- * - `gate-check review` exige que o avaliador registre a conferência de cada
14
- * item decidido contra a tela entregue (`checklistCoverage`).
15
- *
16
- * Mockup no formato ANTERIOR ao checklist (telas `## S001: …`, sem nenhuma
17
- * `## Tela:`) é legado e isento, com a isenção gravada no sinal.
18
- *
19
- * O formato é o que a `morph-uiux` ensina: cada tela é um `## Tela: <nome>`, e
20
- * dentro dela um `### Checklist de tela` com uma linha por item — tabela
21
- * (`| Paginação | servidor, 25/página |`) ou lista (`- **Paginação:** …`).
22
- * "Não se aplica" é resposta válida; vazio, "?" ou "a definir" não são.
23
- */
1
+ // Cada tela é `## Tela: <nome>` com um `### Checklist de tela`, uma linha por item (tabela ou lista).
2
+ // "Não se aplica" é resposta válida; vazio, "?" ou "a definir" não são. Telas `## S001: …` sem
3
+ // nenhuma `## Tela:` são o formato legado, isento.
24
4
 
25
5
  import { createHash } from 'crypto';
26
6
  import { existsSync, readFileSync } from 'fs';
27
- import { join } from 'path';
7
+ import { dirname, join } from 'path';
28
8
  import { fileURLToPath } from 'url';
29
9
  import { getAbsoluteOutputPath } from '../../core/paths/output-schema.js';
30
- import { evaluatorScreenChecklist } from '../evaluator-report.js';
10
+ import { verifierScreenChecklist } from '../review-reports.js';
31
11
  import { LEGACY_MOCKUP_EXEMPTION, uiuxChecklistPause } from '../risk-detector.js';
32
12
 
33
13
  const PACKAGE_AGENTS = fileURLToPath(new URL('../../../framework/agents.json', import.meta.url));
34
14
 
35
- /**
36
- * Os itens, na ordem do template. `match` roda sobre o rótulo NORMALIZADO
37
- * (minúsculas, sem acento): "Estado de erro", "Erro" e "**Erro:**" são o mesmo
38
- * item. Um rótulo que casa com vários ("Estados (vazio/carregando/erro)")
39
- * responde a todos eles.
40
- */
15
+ /** `match` roda sobre o rótulo normalizado; um rótulo que casa com vários itens responde a todos. */
41
16
  export const SCREEN_CHECKLIST_ITEMS = [
42
17
  { id: 'superficie', label: 'Superfície', match: /\bsuperficie\b/ },
43
18
  { id: 'volume', label: 'Volume esperado', match: /\bvolume\b/ },
@@ -51,26 +26,24 @@ export const SCREEN_CHECKLIST_ITEMS = [
51
26
  { id: 'abas', label: 'Abas', match: /\babas\b/ },
52
27
  ];
53
28
 
54
- /**
55
- * Itens que o avaliador não confere contra a tela: a superfície decide QUAIS
56
- * standards a tela lê, não um comportamento que a tela entregue exiba.
57
- */
29
+ // A superfície decide quais standards a tela lê, não um comportamento que a tela entregue exiba.
58
30
  const NOT_VERIFIED_AT_REVIEW = new Set(['superficie']);
59
31
 
60
- /**
61
- * Respostas que ocupam a célula sem decidir nada: as exatas, e as que COMEÇAM
62
- * adiando a decisão ("a definir depois", "TBD.", "? talvez").
63
- */
32
+ // As exatas, e as que começam adiando a decisão ("a definir depois", "TBD.").
64
33
  const PLACEHOLDERS = new Set(['', '-', '--', '—', '–', '...', 'x', 'todo', 'definir', 'pendente']);
65
34
  const PLACEHOLDER_PREFIX_RE = /^(a definir|tbd|\?|…)/;
66
35
 
67
- /** O formato de mockup anterior ao checklist: telas `## S001: <nome>`. */
68
36
  const LEGACY_SCREEN_HEADING_RE = /^##\s+S\d+\s*:/i;
69
37
 
70
38
  const NOT_APPLICABLE_RE = /^(nao se aplica|n\/a)\b/;
71
39
 
72
40
  const SCREEN_HEADING_RE = /^##\s+tela\s*[:—–-]\s*(.+?)\s*#*\s*$/i;
73
41
  const CHECKLIST_HEADING_RE = /^(#{3,6})\s+checklist\b/i;
42
+ const PROTOTYPE_LINE_RE = /prot[oó]tipo/i;
43
+ const PROTOTYPE_PATH_RE = /(?:1-design\/)?(prototipos\/[^\s`|)\]]+\.html)/i;
44
+
45
+ /** As larguras dos prints que o laço de autocrítica deixa ao lado do protótipo. */
46
+ export const PROTOTYPE_WIDTHS = [1280, 390];
74
47
 
75
48
  /** Minúsculas, sem acento, sem marcação inline, espaços colapsados. */
76
49
  export function normalizeLabel(text) {
@@ -83,21 +56,15 @@ export function normalizeLabel(text) {
83
56
  .trim();
84
57
  }
85
58
 
86
- /** Nome de tela comparável entre o mockup e o relatório do avaliador. */
87
59
  function screenKey(name) {
88
60
  return normalizeLabel(name).replace(/^tela\s*[:—–-]\s*/, '').replace(/[.:;,]+$/, '');
89
61
  }
90
62
 
91
- /** Os itens que um rótulo responde. */
92
63
  function itemsOf(label) {
93
64
  const norm = normalizeLabel(label);
94
65
  return SCREEN_CHECKLIST_ITEMS.filter((item) => item.match.test(norm)).map((item) => item.id);
95
66
  }
96
67
 
97
- /**
98
- * Uma linha de checklist como `[rótulo, resposta]`, ou null: linha de tabela
99
- * (separador `|---|` fora) ou item de lista `- Rótulo: resposta`.
100
- */
101
68
  function checklistRow(line) {
102
69
  const trimmed = line.trim();
103
70
  if (trimmed.startsWith('|')) {
@@ -112,13 +79,12 @@ function checklistRow(line) {
112
79
  return [bullet[1].slice(0, colon), bullet[1].slice(colon + 1).trim()];
113
80
  }
114
81
 
115
- /** As células de uma linha de tabela markdown, sem as bordas. */
116
82
  export function tableCells(line) {
117
83
  const inner = line.trim().replace(/^\|/, '').replace(/\|$/, '');
118
84
  return inner.split(/(?<!\\)\|/).map((cell) => cell.trim());
119
85
  }
120
86
 
121
- /** Linhas fora de blocos cercados: um exemplo dentro de ``` não é uma tela. */
87
+ // Um exemplo dentro de ``` não é uma tela.
122
88
  function proseLines(markdown) {
123
89
  const out = [];
124
90
  let fenced = false;
@@ -129,12 +95,6 @@ function proseLines(markdown) {
129
95
  return out;
130
96
  }
131
97
 
132
- /**
133
- * As telas do mockup e o que o checklist de cada uma responde.
134
- *
135
- * @param {string} markdown - conteúdo do `1-design/mockups.md`
136
- * @returns {Array<{ name: string, hasChecklist: boolean, answers: Map<string, string> }>}
137
- */
138
98
  export function parseScreens(markdown) {
139
99
  const screens = [];
140
100
  let screen = null;
@@ -146,7 +106,7 @@ export function parseScreens(markdown) {
146
106
  const level = heading[1].length;
147
107
  const named = SCREEN_HEADING_RE.exec(line);
148
108
  if (level <= 2) {
149
- screen = named && named[1].trim() ? { name: named[1].trim(), hasChecklist: false, answers: new Map() } : null;
109
+ screen = named && named[1].trim() ? { name: named[1].trim(), hasChecklist: false, answers: new Map(), prototype: null } : null;
150
110
  if (screen) screens.push(screen);
151
111
  checklistLevel = 0;
152
112
  continue;
@@ -160,6 +120,9 @@ export function parseScreens(markdown) {
160
120
  }
161
121
  continue;
162
122
  }
123
+ if (screen && !screen.prototype && PROTOTYPE_LINE_RE.test(line)) {
124
+ screen.prototype = PROTOTYPE_PATH_RE.exec(line)?.[1] ?? null;
125
+ }
163
126
  if (!screen || !checklistLevel) continue;
164
127
  const row = checklistRow(line);
165
128
  if (!row) continue;
@@ -178,47 +141,29 @@ const isNotApplicable = (answer) => NOT_APPLICABLE_RE.test(normalizeLabel(answer
178
141
 
179
142
  const labelOf = (id) => SCREEN_CHECKLIST_ITEMS.find((item) => item.id === id)?.label ?? id;
180
143
 
181
- /**
182
- * As superfícies que a persona `ui-designer` declara no `agents.json` — o
183
- * vocabulário válido do item `Superfície`, que o `morph-plan` §5a usa para
184
- * somar os standards da tela em `tasks[].standards`. Lê a cópia instalada do
185
- * projeto; sem ela (este repositório), a do pacote. Persona sem `surfaces`
186
- * (instalação anterior ao campo) devolve null: o valor não é validado.
187
- *
188
- * @param {string} cwd
189
- * @returns {string[]|null}
190
- */
144
+ // O vocabulário válido de `Superfície`: as `surfaces` da `ui-designer`, da cópia instalada ou do pacote.
145
+ // null quando a persona não declara o campo, e então o valor não é validado.
191
146
  export function knownSurfaces(cwd) {
147
+ const surfaces = readAgentsJson(cwd)?.agents?.['ui-designer']?.surfaces;
148
+ return surfaces && typeof surfaces === 'object' && !Array.isArray(surfaces) ? Object.keys(surfaces) : null;
149
+ }
150
+
151
+ /** The project's installed `agents.json`, else the package's; null when unreadable. */
152
+ export function readAgentsJson(cwd) {
192
153
  for (const path of [join(cwd, '.morph', 'framework', 'agents.json'), PACKAGE_AGENTS]) {
193
154
  if (!existsSync(path)) continue;
194
- try {
195
- const surfaces = JSON.parse(readFileSync(path, 'utf8'))?.agents?.['ui-designer']?.surfaces;
196
- return surfaces && typeof surfaces === 'object' && !Array.isArray(surfaces) ? Object.keys(surfaces) : null;
197
- } catch {
198
- return null;
199
- }
155
+ try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
200
156
  }
201
157
  return null;
202
158
  }
203
159
 
204
- /** A resposta de `Superfície` nomeia uma das superfícies conhecidas? */
205
160
  function namesSurface(answer, surfaces) {
206
161
  const norm = normalizeLabel(answer);
207
162
  return surfaces.some((key) => new RegExp(`(^|[^a-z0-9-])${key}([^a-z0-9-]|$)`).test(norm));
208
163
  }
209
164
 
210
- /**
211
- * Audita o checklist do mockup: toda tela tem o bloco, e todo item tem uma
212
- * resposta escrita — e, quando `surfaces` é dado, a `Superfície` é uma delas.
213
- *
214
- * Sem nenhuma `## Tela:`, o mockup é `legacy` (isento) só quando declara telas
215
- * no formato antigo; qualquer outro formato é o problema "nenhuma tela
216
- * declarada" — senão trocar o heading seria o atalho para pular o checklist.
217
- *
218
- * @param {string} markdown
219
- * @returns {{ legacy: boolean, screens: Array<{ name: string, hasChecklist: boolean, missing: string[], unanswered: string[] }>,
220
- * problems: string[] }} `problems` vazio ⇔ checklist completo (ou isento)
221
- */
165
+ // Sem nenhuma `## Tela:`, só o formato antigo é legado; qualquer outro heading é "nenhuma tela
166
+ // declarada", senão trocar o heading pularia o checklist.
222
167
  export function auditScreenChecklist(markdown, { surfaces = null } = {}) {
223
168
  const parsed = parseScreens(markdown);
224
169
  const problems = [];
@@ -246,69 +191,55 @@ export function auditScreenChecklist(markdown, { surfaces = null } = {}) {
246
191
  problems.push(`tela "${screen.name}": Superfície "${surface}" não é uma das superfícies da ui-designer (${surfaces.join(', ')})`);
247
192
  }
248
193
  }
249
- return { name: screen.name, hasChecklist: screen.hasChecklist, missing, unanswered };
194
+ return { name: screen.name, hasChecklist: screen.hasChecklist, missing, unanswered, prototype: screen.prototype };
250
195
  });
251
196
  return { legacy: false, screens, problems };
252
197
  }
253
198
 
254
- /**
255
- * O sinal `screenChecklist` de um gate, lido do disco. COLETA, nunca decide:
256
- * quem pausa é o risk-detector.
257
- *
258
- * `mockups: false` é "a feature não tem mockup" — nada a cobrar. No review, o
259
- * que se cobra é a conferência (`uncovered`: itens decididos sem linha na
260
- * seção do avaliador), não o design, que já passou pelo Gate uiux.
261
- *
262
- * @param {string} cwd
263
- * @param {string} featureName
264
- * @param {string} gateName
265
- * @returns {{ mockups: boolean, legacy: boolean, screens: string[], problems: string[],
266
- * exempt?: string, section?: boolean, uncovered?: string[] }}
267
- */
199
+ // Linha **Protótipo:** da tela + o HTML + os prints do laço de autocrítica ao lado dele.
200
+ export function auditPrototypes(screens, designDir) {
201
+ const problems = [];
202
+ for (const screen of screens) {
203
+ if (!screen.prototype) {
204
+ problems.push(`tela "${screen.name}" sem a linha \`**Protótipo:** 1-design/prototipos/<tela>.html\``);
205
+ continue;
206
+ }
207
+ const base = screen.prototype.replace(/\.html$/i, '');
208
+ const expected = [screen.prototype, ...PROTOTYPE_WIDTHS.map((width) => `${base}-${width}.png`)];
209
+ const missing = expected.filter((rel) => !existsSync(join(designDir, rel)));
210
+ if (missing.length) problems.push(`tela "${screen.name}": falta ${missing.map((rel) => `1-design/${rel}`).join(', ')}`);
211
+ }
212
+ return problems;
213
+ }
214
+
215
+ // Coleta, nunca decide: quem pausa é o risk-detector. No review cobra-se a conferência (`uncovered`), não o design.
268
216
  export function collectScreenChecklist(cwd, featureName, gateName) {
269
217
  const mockups = readMockups(cwd, featureName);
270
218
  if (mockups === null) return { mockups: false, legacy: false, screens: [], problems: [] };
271
219
  const audit = auditScreenChecklist(mockups, { surfaces: knownSurfaces(cwd) });
272
220
  const signal = { mockups: true, legacy: audit.legacy, screens: audit.screens.map((s) => s.name), problems: audit.problems };
273
221
  if (audit.legacy) signal.exempt = LEGACY_MOCKUP_EXEMPTION;
222
+ if (gateName === 'uiux' && !audit.legacy) {
223
+ const designDir = dirname(getAbsoluteOutputPath(cwd, featureName, 'uiMockups'));
224
+ signal.prototypes = auditPrototypes(audit.screens, designDir);
225
+ }
274
226
  if (gateName === 'review') {
275
- const report = evaluatorScreenChecklist(cwd, featureName);
227
+ const report = verifierScreenChecklist(cwd, featureName);
276
228
  signal.section = report.section;
277
229
  signal.uncovered = checklistCoverage(mockups, report.section ? report.body : '');
278
230
  }
279
231
  return signal;
280
232
  }
281
233
 
282
- /**
283
- * Por que o Gate uiux NÃO pode ser aprovado com assinatura `auto` — a pausa
284
- * que o `gate-check uiux` daria pelo checklist —, ou null. Lido por `approve`
285
- * e `advance`, para que uma aprovação automática nunca passe onde o gate-check
286
- * pausaria. Uma decisão gravada com `pause: false` (o `gate-decision` manual,
287
- * ou um gate-check que não pausou) é a resposta a essa pausa e destrava — só
288
- * para o mockup que ela viu: a decisão guarda `mockupsHash`, e um mockup
289
- * escrito ou mudado depois dela volta a ser conferido.
290
- *
291
- * @param {string} cwd
292
- * @param {string} featureName
293
- * @param {object} feature - o feature.json (lê `gateDecisions.uiux`)
294
- * @returns {string|null}
295
- */
234
+ // Lido por approve e advance para que o `auto` nunca passe onde o gate-check pausaria. Uma decisão com
235
+ // `pause: false` destrava só o design que ela viu: guarda `mockupsHash`, e mudar o arquivo volta a conferir.
296
236
  export function uiuxAutoApprovalBlock(cwd, featureName, feature) {
297
237
  const decision = feature?.gateDecisions?.uiux;
298
238
  if (decision?.pause === false && decision.mockupsHash === mockupsHash(cwd, featureName)) return null;
299
239
  return uiuxChecklistPause(collectScreenChecklist(cwd, featureName, 'uiux'));
300
240
  }
301
241
 
302
- /**
303
- * O que o avaliador deixou de conferir: por tela do mockup, os itens com
304
- * decisão (nem "não se aplica", nem a superfície) sem linha na seção
305
- * `## Checklist de tela` do relatório. Uma linha que o avaliador marca como
306
- * não verificável conta como conferida — o que pausa é a AUSÊNCIA da linha.
307
- *
308
- * @param {string} designMarkdown - o `1-design/mockups.md`
309
- * @param {string} reportSection - o corpo da seção do `evaluator-report.md`
310
- * @returns {string[]} `"<tela>: <item>, <item>"` por tela com lacuna
311
- */
242
+ // Linha marcada como não verificável conta como conferida: o que pausa é a ausência da linha.
312
243
  export function checklistCoverage(designMarkdown, reportSection) {
313
244
  const covered = new Map();
314
245
  for (const line of proseLines(reportSection)) {
@@ -332,14 +263,7 @@ export function checklistCoverage(designMarkdown, reportSection) {
332
263
  return gaps;
333
264
  }
334
265
 
335
- /**
336
- * O `1-design/mockups.md` por conteúdo: BOM e CRLF não contam, para que o
337
- * checkout de outra máquina seja o mesmo mockup.
338
- *
339
- * @param {string} cwd
340
- * @param {string} featureName
341
- * @returns {string|null} null sem mockup
342
- */
266
+ // BOM e CRLF não contam: o checkout de outra máquina é o mesmo arquivo.
343
267
  export function mockupsHash(cwd, featureName) {
344
268
  const text = readMockups(cwd, featureName);
345
269
  if (text === null) return null;
@@ -0,0 +1,43 @@
1
+ import { readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { ensureDotnetBuildIgnores } from '../../utils/file-copier.js';
4
+ import { getProjectTags } from '../stack-filter.js';
5
+ import { readMorphConfig } from '../tasks/test-runner.js';
6
+ import { readPlanChecks } from '../plan/checks.js';
7
+ import { WORKTREE_DIR_NAMES } from '../worktree/dirs.js';
8
+
9
+ export const DOTNET_IMPLEMENTERS = ['dotnet-senior', 'ef-modeler', 'maf-expert'];
10
+
11
+ const SKIPPED_DIRS = new Set([...WORKTREE_DIR_NAMES, 'node_modules', 'bin', 'obj']);
12
+ const DOTNET_PROJECT_FILE = /\.(?:csproj|slnx?)$/i;
13
+ const MAX_DEPTH = 6;
14
+
15
+ export function hasDotnetProjectFile(root, depth = 0) {
16
+ let entries;
17
+ try { entries = readdirSync(root, { withFileTypes: true }); } catch { return false; }
18
+ if (entries.some((e) => e.isFile() && DOTNET_PROJECT_FILE.test(e.name))) return true;
19
+ if (depth >= MAX_DEPTH) return false;
20
+ return entries.some((e) => e.isDirectory() && !e.name.startsWith('.') && !SKIPPED_DIRS.has(e.name)
21
+ && hasDotnetProjectFile(join(root, e.name), depth + 1));
22
+ }
23
+
24
+ function planHasDotnetSlice(tree, featureName) {
25
+ if (!featureName) return false;
26
+ const { checks } = readPlanChecks(tree, featureName);
27
+ return (checks?.slices || []).some((slice) => DOTNET_IMPLEMENTERS.includes(String(slice.persona || '').replace(/^morph-/, '')));
28
+ }
29
+
30
+ // The plan's slices are checked because a greenfield tree has no stack or .csproj yet.
31
+ export function isDotnetProject(tree, { featureName, configRoot = tree } = {}) {
32
+ if (getProjectTags(readMorphConfig(configRoot)).includes('dotnet')) return true;
33
+ return planHasDotnetSlice(tree, featureName) || hasDotnetProjectFile(tree);
34
+ }
35
+
36
+ /** Returns the `.gitignore` entries added; `[]` when not .NET or on any error. */
37
+ export function seedDotnetBuildIgnores(tree, options = {}) {
38
+ try {
39
+ return isDotnetProject(tree, options) ? ensureDotnetBuildIgnores(tree) : [];
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
@@ -1,17 +1,6 @@
1
1
  import { readFileSync, existsSync } from 'fs';
2
2
  import { join } from 'path';
3
3
 
4
- /** @typedef {'nextjs'|'dotnet'|'nodejs-cli'|'unknown'} StackName */
5
-
6
- /**
7
- * @typedef {Object} StackProfile
8
- * @property {StackName} stack
9
- * @property {boolean} isDotnet
10
- * @property {boolean} isNextjs
11
- * @property {string[]} defaultValidators
12
- * @property {string|null} buildCommand
13
- */
14
-
15
4
  const DOTNET_STACKS = new Set(['dotnet', 'aspnet']);
16
5
  const NEXTJS_STACKS = new Set(['nextjs', 'next', 'next.js']);
17
6
  const NO_VALIDATOR_STACKS = new Set(['nodejs-cli', 'node-cli', 'cli']);
@@ -34,9 +23,6 @@ const PROFILES = {
34
23
  },
35
24
  };
36
25
 
37
- /**
38
- * Read project stack from .morph/config/config.json.
39
- */
40
26
  function readStack(cwd) {
41
27
  const configPath = join(cwd, '.morph', 'config', 'config.json');
42
28
  if (!existsSync(configPath)) return null;
@@ -47,7 +33,6 @@ function readStack(cwd) {
47
33
  }
48
34
  }
49
35
 
50
- /** Merge two or more profiles' validator lists (deduped) and boolean flags. */
51
36
  function mergeProfiles(profiles) {
52
37
  return {
53
38
  isDotnet: profiles.some((p) => p.isDotnet),
@@ -57,16 +42,7 @@ function mergeProfiles(profiles) {
57
42
  };
58
43
  }
59
44
 
60
- /**
61
- * Get the StackProfile for the current project.
62
- *
63
- * `stack` may be a compound value (e.g. "dotnet-nextjs", "dotnet+nextjs") —
64
- * the framework's own convention for monorepos (see stack-filter.js's
65
- * getProjectTags, which splits project.stack the same way). Each segment is
66
- * resolved independently and matching profiles are merged, instead of the
67
- * whole compound string failing every exact-match check and silently
68
- * collapsing to the empty profile (0 default validators).
69
- */
45
+ /** A compound stack ("dotnet-nextjs", "dotnet+nextjs") merges the profile of each segment. */
70
46
  export function getStackProfile(cwd, stackOverride) {
71
47
  const root = cwd ?? process.cwd();
72
48
  const raw = stackOverride ?? readStack(root);
@@ -74,8 +50,6 @@ export function getStackProfile(cwd, stackOverride) {
74
50
 
75
51
  const segments = stack.split(/[-+]/).filter(Boolean);
76
52
 
77
- // Single-segment stack (the overwhelming common case): resolve exactly as
78
- // before, byte-for-byte, including the 'unknown' → dotnet fallback.
79
53
  if (segments.length <= 1) {
80
54
  let profile;
81
55
  if (NEXTJS_STACKS.has(stack)) {
@@ -90,7 +64,6 @@ export function getStackProfile(cwd, stackOverride) {
90
64
  return { stack, ...profile };
91
65
  }
92
66
 
93
- // Compound stack: resolve each recognized segment and merge.
94
67
  const matched = [];
95
68
  if (segments.some((s) => DOTNET_STACKS.has(s))) matched.push(PROFILES.dotnet);
96
69
  if (segments.some((s) => NEXTJS_STACKS.has(s))) matched.push(PROFILES.nextjs);