@polymorphism-tech/morph-spec 9.0.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 (304) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +4 -62
  4. package/bin/validate.js +1 -39
  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 +1 -1
  9. package/framework/MORPH.md +1 -1
  10. package/framework/agents.json +12 -4
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +6 -17
  14. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  15. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  16. package/framework/hooks/claude-code/core/chain.js +4 -65
  17. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  18. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  19. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  20. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  21. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  22. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  23. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  24. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  25. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  26. package/framework/hooks/claude-code/core/state-sync.js +11 -67
  27. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  29. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  30. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  31. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  32. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  33. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  34. package/framework/hooks/shared/activity-logger.js +9 -89
  35. package/framework/hooks/shared/agent-reap.js +2 -17
  36. package/framework/hooks/shared/build-check.js +6 -75
  37. package/framework/hooks/shared/claim-registry.js +9 -119
  38. package/framework/hooks/shared/command-tokens.js +6 -51
  39. package/framework/hooks/shared/comment-rules.js +5 -20
  40. package/framework/hooks/shared/comment-scanner.js +6 -25
  41. package/framework/hooks/shared/comment-stripper.js +11 -85
  42. package/framework/hooks/shared/e2e-check.js +0 -0
  43. package/framework/hooks/shared/errata.js +2 -15
  44. package/framework/hooks/shared/feature-locator.js +11 -95
  45. package/framework/hooks/shared/frontend-locations.js +1 -3
  46. package/framework/hooks/shared/gate-question.js +1 -7
  47. package/framework/hooks/shared/git-exclude.js +3 -20
  48. package/framework/hooks/shared/git-paths.js +8 -104
  49. package/framework/hooks/shared/graph-freshness.js +22 -166
  50. package/framework/hooks/shared/graph-refresh.js +21 -149
  51. package/framework/hooks/shared/heavy-command.js +4 -12
  52. package/framework/hooks/shared/hook-response.js +8 -83
  53. package/framework/hooks/shared/morph-cli.js +8 -52
  54. package/framework/hooks/shared/payload-utils.js +10 -132
  55. package/framework/hooks/shared/phase-utils.js +1 -42
  56. package/framework/hooks/shared/redact-secrets.js +4 -15
  57. package/framework/hooks/shared/reparse-scan.js +6 -66
  58. package/framework/hooks/shared/slice-progress.js +4 -23
  59. package/framework/hooks/shared/source-files.js +4 -17
  60. package/framework/hooks/shared/state-reader.js +13 -183
  61. package/framework/hooks/shared/state-writer.js +8 -52
  62. package/framework/hooks/shared/stdin-reader.js +1 -11
  63. package/framework/hooks/shared/telemetry-logger.js +7 -67
  64. package/framework/hooks/shared/transcript-usage.js +7 -29
  65. package/framework/hooks/shared/worktree-root.js +7 -29
  66. package/framework/rules/csharp-standards.md +1 -1
  67. package/framework/rules/frontend-standards.md +1 -0
  68. package/framework/rules/hangfire-standards.md +1 -1
  69. package/framework/rules/neon-standards.md +1 -1
  70. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  71. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  72. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  73. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  74. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  75. package/framework/skills/core/morph-media/SKILL.md +1 -1
  76. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  77. package/framework/skills/core/morph-review/SKILL.md +1 -1
  78. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  79. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  80. package/framework/standards/STANDARDS.json +34 -12
  81. package/framework/standards/STANDARDS.meta.json +16 -2
  82. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  83. package/framework/standards/ai-agents/agent-session.md +1 -1
  84. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  85. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  86. package/framework/standards/ai-agents/guardrails.md +1 -1
  87. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  88. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  89. package/framework/standards/ai-agents/production.md +1 -1
  90. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  91. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  92. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  93. package/framework/standards/ai-agents/setup.md +2 -2
  94. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  95. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  96. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  97. package/framework/standards/ai-agents/workflows.md +1 -1
  98. package/framework/standards/architecture/code-comments.md +1 -1
  99. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  100. package/framework/standards/backend/api/validation.md +1 -1
  101. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  102. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  103. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  104. package/framework/standards/data/nosql/cache/redis.md +1 -1
  105. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  106. package/framework/standards/frontend/admin-app/forms.md +1 -1
  107. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  108. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  109. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  110. package/framework/templates/CLAUDE.md.core.template +3 -3
  111. package/framework/templates/REGISTRY.json +9 -9
  112. package/framework/templates/docs/ui-mockups.md +4 -0
  113. package/package.json +1 -1
  114. package/src/commands/context/context-refresh.js +0 -16
  115. package/src/commands/dashboard/dashboard.js +2 -8
  116. package/src/commands/e2e/e2e.js +17 -104
  117. package/src/commands/graph/affected.js +0 -14
  118. package/src/commands/graph/build.js +11 -50
  119. package/src/commands/graph/explain.js +0 -16
  120. package/src/commands/graph/status.js +1 -12
  121. package/src/commands/mcp/mcp-setup.js +4 -62
  122. package/src/commands/observability/cost.js +5 -57
  123. package/src/commands/observability/telemetry.js +1 -14
  124. package/src/commands/project/branch.js +4 -45
  125. package/src/commands/project/claim.js +4 -48
  126. package/src/commands/project/doctor.js +48 -302
  127. package/src/commands/project/env.js +1 -27
  128. package/src/commands/project/fleet.js +3 -32
  129. package/src/commands/project/init.js +4 -68
  130. package/src/commands/project/install-plugin-cmd.js +0 -1
  131. package/src/commands/project/standards.js +1 -62
  132. package/src/commands/project/status.js +0 -53
  133. package/src/commands/project/update.js +31 -239
  134. package/src/commands/project/worktree.js +108 -882
  135. package/src/commands/scaffold/scaffold.js +0 -25
  136. package/src/commands/state/advance.js +14 -85
  137. package/src/commands/state/approve.js +14 -96
  138. package/src/commands/state/archive.js +12 -64
  139. package/src/commands/state/auto-approval.js +2 -5
  140. package/src/commands/state/create.js +22 -109
  141. package/src/commands/state/delete.js +11 -63
  142. package/src/commands/state/finish.js +98 -475
  143. package/src/commands/state/gate-check.js +22 -132
  144. package/src/commands/state/gate-decision.js +3 -39
  145. package/src/commands/state/handoff.js +3 -30
  146. package/src/commands/state/list.js +14 -84
  147. package/src/commands/state/report-check.js +8 -58
  148. package/src/commands/state/retype.js +4 -26
  149. package/src/commands/validation/index.js +0 -3
  150. package/src/commands/validation/mutate.js +2 -5
  151. package/src/commands/validation/validate-feature.js +5 -94
  152. package/src/commands/validation/verify.js +15 -98
  153. package/src/core/paths/output-schema.js +2 -101
  154. package/src/core/state/feature-identity.js +1 -25
  155. package/src/core/state/gate-order.js +0 -27
  156. package/src/core/state/lifecycle-audit.js +8 -100
  157. package/src/core/state/state-manager.js +22 -304
  158. package/src/core/templates/template-registry.js +2 -97
  159. package/src/core/templates/template-renderer.js +13 -93
  160. package/src/core/templates/template-seed.js +0 -1
  161. package/src/core/work-types.js +1 -36
  162. package/src/dashboard/app/App.jsx +1 -8
  163. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  164. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  165. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  166. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  167. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  168. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  169. package/src/dashboard/app/components/Markdown.jsx +0 -9
  170. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  171. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  172. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  173. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  174. package/src/dashboard/app/hooks/usePref.js +1 -5
  175. package/src/dashboard/app/lib/format.js +0 -2
  176. package/src/dashboard/app/lib/phases.js +1 -5
  177. package/src/dashboard/app/lib/project.js +0 -12
  178. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  179. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  180. package/src/dashboard/app/pages/Features.jsx +1 -8
  181. package/src/dashboard/app/pages/Guide.jsx +0 -1
  182. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  183. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  184. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  185. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  186. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  187. package/src/dashboard/app/pages/Standards.jsx +2 -17
  188. package/src/dashboard/scanner/activity-scanner.js +10 -69
  189. package/src/dashboard/scanner/agents-scanner.js +1 -23
  190. package/src/dashboard/scanner/docs-scanner.js +0 -6
  191. package/src/dashboard/scanner/feature-status.js +2 -5
  192. package/src/dashboard/scanner/features-scanner.js +10 -66
  193. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  194. package/src/dashboard/scanner/index.js +0 -4
  195. package/src/dashboard/scanner/portfolio.js +5 -29
  196. package/src/dashboard/scanner/project-api.js +8 -54
  197. package/src/dashboard/scanner/rules-scanner.js +1 -2
  198. package/src/dashboard/scanner/skills-scanner.js +2 -12
  199. package/src/dashboard/scanner/standards-scanner.js +2 -6
  200. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  201. package/src/lib/ai/ai-checks.js +7 -52
  202. package/src/lib/ai/ai-project-scan.js +14 -108
  203. package/src/lib/ai/code-audit.js +7 -63
  204. package/src/lib/ai/pin-drift.js +8 -51
  205. package/src/lib/ai/registry-audit.js +11 -90
  206. package/src/lib/ci/ci-verify.js +4 -19
  207. package/src/lib/ci/github-ci.js +9 -37
  208. package/src/lib/ci/workflow-scaffold.js +2 -10
  209. package/src/lib/detectors/claude-config-detector.js +13 -74
  210. package/src/lib/detectors/index.js +4 -50
  211. package/src/lib/e2e/compose-env.js +5 -66
  212. package/src/lib/e2e/stack-teardown.js +6 -40
  213. package/src/lib/generators/architecture-scanner.js +18 -156
  214. package/src/lib/generators/handoff-generator.js +9 -53
  215. package/src/lib/generators/scaffold-generator.js +5 -101
  216. package/src/lib/generators/settings-generator.js +4 -37
  217. package/src/lib/git/branch-classifier.js +11 -84
  218. package/src/lib/git/default-branch.js +14 -169
  219. package/src/lib/git/evidence-staleness.js +1 -0
  220. package/src/lib/git/gh.js +2 -17
  221. package/src/lib/git/pending-prs.js +6 -37
  222. package/src/lib/git/pr-files.js +3 -28
  223. package/src/lib/git/pr-index.js +3 -34
  224. package/src/lib/git/tree-fingerprint.js +3 -15
  225. package/src/lib/git/tree-state.js +6 -33
  226. package/src/lib/graph/god-nodes.js +2 -22
  227. package/src/lib/graph/graph-health.js +3 -24
  228. package/src/lib/graph/graph-loader.js +5 -56
  229. package/src/lib/graph/graphify-runtime.js +7 -37
  230. package/src/lib/graph/graphifyignore.js +8 -74
  231. package/src/lib/graph/sanitize.js +16 -136
  232. package/src/lib/heavy/cleanup.js +1 -1
  233. package/src/lib/installers/effects.js +8 -93
  234. package/src/lib/installers/mcp-installer.js +9 -169
  235. package/src/lib/installers/memory-installer.js +4 -27
  236. package/src/lib/installers/templates-reconcile.js +5 -60
  237. package/src/lib/mutation/dotnet-introspection.js +20 -106
  238. package/src/lib/mutation/subset.js +1 -4
  239. package/src/lib/plan/artifact-vocabulary.js +5 -25
  240. package/src/lib/plan/checks.js +5 -31
  241. package/src/lib/plan/plan-parser.js +1 -6
  242. package/src/lib/projects/registry.js +7 -44
  243. package/src/lib/review-reports.js +5 -23
  244. package/src/lib/risk-detector.js +39 -173
  245. package/src/lib/specs/adr-index.js +6 -34
  246. package/src/lib/specs/frontmatter.js +2 -15
  247. package/src/lib/specs/gfm-table.js +1 -7
  248. package/src/lib/specs/revocations.js +1 -14
  249. package/src/lib/specs/screen-checklist.js +47 -124
  250. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  251. package/src/lib/stack/stack-profile.js +1 -28
  252. package/src/lib/stack/stack-scan.js +1 -8
  253. package/src/lib/stack-filter.js +4 -27
  254. package/src/lib/standards/registry.js +1 -7
  255. package/src/lib/standards/sections.js +2 -13
  256. package/src/lib/standards/verified-against.js +19 -191
  257. package/src/lib/tasks/eval-node.js +21 -169
  258. package/src/lib/tasks/linux-runner.js +18 -142
  259. package/src/lib/tasks/test-nodes.js +43 -240
  260. package/src/lib/tasks/test-runner.js +43 -341
  261. package/src/lib/telemetry/metrics.js +7 -36
  262. package/src/lib/telemetry/telemetry-reader.js +13 -125
  263. package/src/lib/telemetry/verify-event.js +2 -30
  264. package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
  265. package/src/lib/validators/code-budget/index.js +0 -7
  266. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  267. package/src/lib/validators/contracts/index.js +0 -6
  268. package/src/lib/validators/nextjs/index.js +0 -5
  269. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  270. package/src/lib/validators/packages/index.js +0 -6
  271. package/src/lib/validators/packages/package-validator.js +6 -56
  272. package/src/lib/validators/reuse/index.js +0 -8
  273. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  274. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  275. package/src/lib/validators/traceability-check.js +5 -63
  276. package/src/lib/validators/validation-runner.js +20 -125
  277. package/src/lib/validators/verify-runner.js +52 -358
  278. package/src/lib/work-type-detector.js +5 -67
  279. package/src/lib/worktree/docker-audit.js +17 -136
  280. package/src/lib/worktree/env-allocator.js +10 -98
  281. package/src/lib/worktree/feature-truth.js +12 -115
  282. package/src/lib/worktree/finish-backup.js +32 -242
  283. package/src/lib/worktree/fleet-scanner.js +13 -95
  284. package/src/lib/worktree/ignored-triage.js +15 -91
  285. package/src/lib/worktree/vscode-settings.js +2 -3
  286. package/src/lib/worktree/worktree-audit.js +21 -213
  287. package/src/lib/worktree/worktree-include.js +3 -5
  288. package/src/scripts/global-install.js +3 -9
  289. package/src/scripts/install-plugin.js +1 -13
  290. package/src/scripts/setup-infra.js +17 -147
  291. package/src/utils/agents-installer.js +28 -275
  292. package/src/utils/banner.js +4 -25
  293. package/src/utils/claude-md-injector.js +1 -37
  294. package/src/utils/claude-settings-manager.js +1 -52
  295. package/src/utils/file-copier.js +11 -95
  296. package/src/utils/hooks-installer.js +21 -298
  297. package/src/utils/skills-installer.js +4 -101
  298. package/src/utils/version-checker.js +4 -83
  299. package/framework/commands/morph-proposal.md +0 -9
  300. package/src/commands/utils/index.js +0 -3
  301. package/src/lib/detectors/design-system-detector.js +0 -188
  302. package/src/lib/generators/design-system-generator.js +0 -508
  303. package/src/lib/specs/old-format.js +0 -68
  304. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,22 +1,8 @@
1
- /**
2
- * Scaffold Generator - Generates VSA feature slice (.NET) and feature page (Next.js) files.
3
- *
4
- * Uses existing Handlebars templates from framework/templates/ to generate
5
- * complete feature structures with one command.
6
- *
7
- * @module scaffold-generator
8
- */
9
-
10
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs';
11
2
  import { join, dirname, basename } from 'path';
12
3
  import { renderTemplate } from '../../core/templates/template-renderer.js';
13
4
  import { resolveFrameworkTemplatePath } from '../../core/templates/template-registry.js';
14
5
 
15
- /**
16
- * Convert kebab-case to PascalCase.
17
- * @param {string} str
18
- * @returns {string}
19
- */
20
6
  function toPascalCase(str) {
21
7
  return str
22
8
  .split('-')
@@ -24,11 +10,6 @@ function toPascalCase(str) {
24
10
  .join('');
25
11
  }
26
12
 
27
- /**
28
- * Convert kebab-case to camelCase.
29
- * @param {string} str
30
- * @returns {string}
31
- */
32
13
  function toCamelCase(str) {
33
14
  return str
34
15
  .split('-')
@@ -36,16 +17,8 @@ function toCamelCase(str) {
36
17
  .join('');
37
18
  }
38
19
 
39
- /**
40
- * Normalize a namespace to kebab-case so Handlebars {{pascalCase}} reconstructs it correctly.
41
- * Handles: PascalCase ("MyBookStore" → "my-book-store"), dot-separated ("My.App" → "my-app"),
42
- * and already kebab-case pass-through.
43
- * @param {string} ns
44
- * @returns {string}
45
- */
20
+ /** Kebab-case so Handlebars {{pascalCase}} reconstructs it: "MyBookStore" → "my-book-store", "My.App" → "my-app". */
46
21
  export function normalizeNamespaceToKebab(ns) {
47
- // Split on dots first (My.BookStore → ["My", "BookStore"])
48
- // Then split PascalCase on uppercase boundaries (BookStore → Book-Store)
49
22
  return ns
50
23
  .split('.')
51
24
  .map(segment =>
@@ -57,15 +30,8 @@ export function normalizeNamespaceToKebab(ns) {
57
30
  .join('-');
58
31
  }
59
32
 
60
- // ─────────────────────────────────────────────────────────────────────────────
61
- // .NET VSA Scaffold
62
- // ─────────────────────────────────────────────────────────────────────────────
63
-
64
- // Each entry pairs a REGISTRY.json template `id` with the generator-specific
65
- // output shaping (class-name suffix, target directory). REGISTRY owns the
66
- // template path; the generator owns where the rendered file lands.
33
+ // REGISTRY.json owns the template path; the generator owns where the rendered file lands.
67
34
 
68
- /** .NET slice-level files (endpoint/handler/request/response/validator). */
69
35
  const DOTNET_SLICE_TEMPLATES = [
70
36
  { templateId: 'dotnet-feature-endpoint', suffix: 'Endpoint' },
71
37
  { templateId: 'dotnet-feature-handler', suffix: 'Handler' },
@@ -74,23 +40,11 @@ const DOTNET_SLICE_TEMPLATES = [
74
40
  { templateId: 'dotnet-feature-validator', suffix: 'Validator' },
75
41
  ];
76
42
 
77
- /** .NET feature-level files (entity, errors). */
78
43
  const DOTNET_FEATURE_TEMPLATES = [
79
44
  { templateId: 'dotnet-feature-entity', suffix: '', dir: 'entities' },
80
45
  { templateId: 'dotnet-feature-errors', suffix: 'Errors', dir: 'feature' },
81
46
  ];
82
47
 
83
- /**
84
- * Scaffold a .NET VSA feature slice.
85
- *
86
- * @param {Object} options
87
- * @param {string} options.feature - Entity/feature name in kebab-case (e.g., "book")
88
- * @param {string} options.sliceName - Slice name in kebab-case (e.g., "create-book")
89
- * @param {string} options.namespace - Root namespace (e.g., "MyApp")
90
- * @param {string} [options.outputDir] - Base output directory (default: cwd)
91
- * @param {boolean} [options.dryRun] - Preview without writing
92
- * @returns {{ files: Array<{path: string, content: string, skipped: boolean}> }}
93
- */
94
48
  export function scaffoldDotnetSlice(options) {
95
49
  const { feature, sliceName, namespace, outputDir = process.cwd(), dryRun = false } = options;
96
50
 
@@ -107,7 +61,6 @@ export function scaffoldDotnetSlice(options) {
107
61
 
108
62
  const files = [];
109
63
 
110
- // 1. Render slice-level templates
111
64
  for (const tmpl of DOTNET_SLICE_TEMPLATES) {
112
65
  const templatePath = resolveFrameworkTemplatePath(tmpl.templateId);
113
66
  const outputPath = join(
@@ -128,7 +81,6 @@ export function scaffoldDotnetSlice(options) {
128
81
  files.push({ path: outputPath, content, skipped: false });
129
82
  }
130
83
 
131
- // 2. Render feature-level templates (skip if already exist)
132
84
  for (const tmpl of DOTNET_FEATURE_TEMPLATES) {
133
85
  const templatePath = resolveFrameworkTemplatePath(tmpl.templateId);
134
86
  let outputPath;
@@ -157,12 +109,6 @@ export function scaffoldDotnetSlice(options) {
157
109
  return { files };
158
110
  }
159
111
 
160
- // ─────────────────────────────────────────────────────────────────────────────
161
- // Next.js Scaffold
162
- // ─────────────────────────────────────────────────────────────────────────────
163
-
164
- // Each entry pairs a REGISTRY.json template `id` with where the rendered file
165
- // lands. REGISTRY owns the template path; the generator owns the output path.
166
112
  const NEXTJS_TEMPLATES = [
167
113
  {
168
114
  templateId: 'nextjs-page',
@@ -182,15 +128,6 @@ const NEXTJS_TEMPLATES = [
182
128
  },
183
129
  ];
184
130
 
185
- /**
186
- * Scaffold a Next.js feature page with components and hooks.
187
- *
188
- * @param {Object} options
189
- * @param {string} options.featureName - Feature name in kebab-case (e.g., "book")
190
- * @param {string} [options.outputDir] - Base output directory (default: cwd)
191
- * @param {boolean} [options.dryRun] - Preview without writing
192
- * @returns {{ files: Array<{path: string, content: string, skipped: boolean}> }}
193
- */
194
131
  export function scaffoldNextjsFeature(options) {
195
132
  const { featureName, outputDir = process.cwd(), dryRun = false } = options;
196
133
 
@@ -226,33 +163,15 @@ export function scaffoldNextjsFeature(options) {
226
163
  return { files };
227
164
  }
228
165
 
229
- // ─────────────────────────────────────────────────────────────────────────────
230
- // Namespace Detection
231
- // ─────────────────────────────────────────────────────────────────────────────
232
-
233
- /**
234
- * Detect the .NET root namespace from .csproj or config.json.
235
- *
236
- * Search order:
237
- * 1. *.csproj in dir (extract <RootNamespace>)
238
- * 2. *.csproj in dir (use filename without extension)
239
- * 3. *.csproj one level deep
240
- * 4. .morph/config.json → project.name
241
- *
242
- * @param {string} dir - Directory to search in
243
- * @returns {string|null} Detected namespace or null
244
- */
166
+ /** <RootNamespace> or the name of a .csproj here or one level deep, else config.json `project.name`. */
245
167
  export function detectNamespace(dir) {
246
- // 1. Search for .csproj in current dir
247
168
  const csprojInDir = findCsproj(dir);
248
169
  if (csprojInDir) {
249
170
  const ns = extractNamespaceFromCsproj(csprojInDir);
250
171
  if (ns) return ns;
251
- // Fallback: use filename
252
172
  return basename(csprojInDir, '.csproj');
253
173
  }
254
174
 
255
- // 2. Search one level deep
256
175
  try {
257
176
  const entries = readdirSync(dir, { withFileTypes: true });
258
177
  for (const entry of entries) {
@@ -265,29 +184,19 @@ export function detectNamespace(dir) {
265
184
  }
266
185
  }
267
186
  }
268
- } catch {
269
- // ignore read errors
270
- }
187
+ } catch {}
271
188
 
272
- // 3. Fall back to config.json
273
189
  const configPath = join(dir, '.morph', 'config.json');
274
190
  if (existsSync(configPath)) {
275
191
  try {
276
192
  const config = JSON.parse(readFileSync(configPath, 'utf-8'));
277
193
  if (config.project?.name) return config.project.name;
278
- } catch {
279
- // ignore parse errors
280
- }
194
+ } catch {}
281
195
  }
282
196
 
283
197
  return null;
284
198
  }
285
199
 
286
- /**
287
- * Find the first .csproj file in a directory.
288
- * @param {string} dir
289
- * @returns {string|null}
290
- */
291
200
  function findCsproj(dir) {
292
201
  try {
293
202
  const entries = readdirSync(dir);
@@ -298,11 +207,6 @@ function findCsproj(dir) {
298
207
  }
299
208
  }
300
209
 
301
- /**
302
- * Extract <RootNamespace> from a .csproj file.
303
- * @param {string} csprojPath
304
- * @returns {string|null}
305
- */
306
210
  function extractNamespaceFromCsproj(csprojPath) {
307
211
  try {
308
212
  const content = readFileSync(csprojPath, 'utf-8');
@@ -1,10 +1,4 @@
1
- /**
2
- * Settings Generator
3
- *
4
- * Generates .claude/settings.local.json for projects based on detected
5
- * stack, MCPs, and morph-spec configuration. Uses _morph_managed marker
6
- * to track managed entries for safe reset.
7
- */
1
+ // `_morph_managed` marks the managed entries, so a reset never touches the user's.
8
2
 
9
3
  import { join } from 'path';
10
4
  import { existsSync } from 'fs';
@@ -12,13 +6,6 @@ import { readFile, writeFile, mkdir } from 'fs/promises';
12
6
 
13
7
  const MORPH_MANAGED_KEY = '_morph_managed';
14
8
 
15
- /**
16
- * Generate and merge settings for a project
17
- * @param {string} targetPath - Project root directory
18
- * @param {Object} detectedConfig - Output from detectClaudeConfig()
19
- * @param {Object} morphConfig - morph-spec config (from .morph/config/config.json)
20
- * @returns {Promise<Object>} Generated settings summary
21
- */
22
9
  export async function generateSettings(targetPath, detectedConfig, morphConfig = {}) {
23
10
  const claudeDir = join(targetPath, '.claude');
24
11
  const settingsPath = join(claudeDir, 'settings.local.json');
@@ -32,11 +19,9 @@ export async function generateSettings(targetPath, detectedConfig, morphConfig =
32
19
  generated_at: new Date().toISOString()
33
20
  };
34
21
 
35
- // Merge without overwriting user config
36
22
  const merged = mergeSettings(existing, generated);
37
23
  merged[MORPH_MANAGED_KEY] = managed;
38
24
 
39
- // Ensure directory exists
40
25
  if (!existsSync(claudeDir)) {
41
26
  await mkdir(claudeDir, { recursive: true });
42
27
  }
@@ -49,12 +34,7 @@ export async function generateSettings(targetPath, detectedConfig, morphConfig =
49
34
  };
50
35
  }
51
36
 
52
- /**
53
- * Deep merge settings without overwriting user entries
54
- * @param {Object} existing - Current settings.local.json
55
- * @param {Object} generated - Generated settings to merge
56
- * @returns {Object} Merged settings
57
- */
37
+ /** Never overwrites user entries: arrays are unioned, objects merged, only new keys added. */
58
38
  export function mergeSettings(existing, generated) {
59
39
  const result = { ...existing };
60
40
 
@@ -62,17 +42,14 @@ export function mergeSettings(existing, generated) {
62
42
  if (key === MORPH_MANAGED_KEY) continue;
63
43
 
64
44
  if (Array.isArray(value) && Array.isArray(result[key])) {
65
- // Merge arrays without duplicates
66
45
  const set = new Set(result[key]);
67
46
  for (const item of value) {
68
47
  set.add(item);
69
48
  }
70
49
  result[key] = [...set];
71
50
  } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
72
- // Recursively merge objects
73
51
  result[key] = mergeSettings(result[key] || {}, value);
74
52
  } else if (!(key in result)) {
75
- // Only add new keys, never overwrite existing
76
53
  result[key] = value;
77
54
  }
78
55
  }
@@ -80,11 +57,6 @@ export function mergeSettings(existing, generated) {
80
57
  return result;
81
58
  }
82
59
 
83
- /**
84
- * Save integrations.json with detected config
85
- * @param {string} targetPath - Project root directory
86
- * @param {Object} detectedConfig - Output from detectClaudeConfig()
87
- */
88
60
  export async function saveIntegrations(targetPath, detectedConfig) {
89
61
  const integrationsPath = join(targetPath, '.morph', 'config', 'integrations.json');
90
62
  const integrationsDir = join(targetPath, '.morph', 'config');
@@ -107,13 +79,8 @@ export async function saveIntegrations(targetPath, detectedConfig) {
107
79
  return integrationsPath;
108
80
  }
109
81
 
110
- // ============================================================================
111
- // Helpers
112
- // ============================================================================
113
-
114
- // Claude Code reads only `permissions: { allow, deny }`; a bare array (older generator) is ignored.
115
- // Versões antigas gravavam regras curinga (node:*, npm:*, npx:*…) como array, forma que o
116
- // Claude Code ignora: nunca valeram. Converter para { allow } as ligaria em silêncio.
82
+ // Claude Code reads only `permissions: { allow, deny }`. Old generators wrote wildcard rules as a bare
83
+ // array that never applied; converting it to { allow } would silently enable them.
117
84
  function withoutLegacyPermissions(settings) {
118
85
  if (!Array.isArray(settings.permissions)) return settings;
119
86
  const { permissions, ...rest } = settings;
@@ -1,36 +1,6 @@
1
- /**
2
- * branch-classifier.js — answer "is this branch's work already in the default
3
- * branch?" for a repository that squash-merges.
4
- *
5
- * WHY THIS EXISTS: a squash-merge puts the WORK in `origin/master` while
6
- * leaving none of the branch's own commits reachable from it. So
7
- * `git branch --merged origin/master` does not list the branch, and
8
- * `git branch -d` refuses it as unmerged. The only remaining command is
9
- * `git branch -D`, which is byte-identical to the command that discards real,
10
- * never-integrated work — the tool gives the user no way to tell the two apart,
11
- * and after enough of these the habit becomes "just -D it", which is how work
12
- * gets lost.
13
- *
14
- * Every branch lands in exactly one class, and only the two MERGED ones are
15
- * ever deletable:
16
- *
17
- * merged-ancestral — git itself proves it: the branch tip is an ancestor of
18
- * the default. A plain merge, or a fast-forward.
19
- * merged-squash — git cannot see it, but the evidence is positive: either
20
- * GitHub reports the PR as MERGED, or the branch's tree is
21
- * IDENTICAL to the default's (every change it carries is
22
- * already there under a different commit).
23
- * open — a PR is OPEN for it. Never deleted: the PR is the whole
24
- * reason the branch must survive.
25
- * in-worktree — checked out by a live worktree. Never deleted.
26
- * unmerged — none of the above could be PROVEN. This is the fail-safe
27
- * default and covers "no `gh`", "no origin", and "genuinely
28
- * unintegrated work" alike, because from here they are the
29
- * same thing: not proven integrated.
30
- *
31
- * Nothing here mutates. `pruneBranches` in commands/project/branch.js does the
32
- * deleting, and it deletes ONLY what this file classified as merged.
33
- */
1
+ // A squash-merge leaves the work in the default branch but none of the branch's commits,
2
+ // so `branch -d` refuses it and `-D` looks like discarding real work. Only the two
3
+ // `merged-*` classes are deletable; anything not PROVEN integrated is `unmerged`.
34
4
 
35
5
  import { execFileSync } from 'child_process';
36
6
 
@@ -38,11 +8,7 @@ function git(args, cwd) {
38
8
  return execFileSync('git', args, { cwd, stdio: 'pipe' }).toString();
39
9
  }
40
10
 
41
- /**
42
- * Local branches with their tips. Excludes nothing — the caller decides.
43
- *
44
- * @returns {Array<{ name: string, sha: string, current: boolean, worktreePath: string|null }>}
45
- */
11
+ /** @returns {Array<{ name: string, sha: string, current: boolean, worktreePath: string|null }>} */
46
12
  export function listLocalBranches(cwd) {
47
13
  try {
48
14
  const out = git(['for-each-ref', '--format=%(refname:short)%09%(objectname)%09%(HEAD)%09%(worktreepath)', 'refs/heads/'], cwd);
@@ -55,7 +21,6 @@ export function listLocalBranches(cwd) {
55
21
  }
56
22
  }
57
23
 
58
- /** Is `ancestor` reachable from `descendant`? False on any git error. */
59
24
  function isAncestor(ancestor, descendant, cwd) {
60
25
  try {
61
26
  git(['merge-base', '--is-ancestor', ancestor, descendant], cwd);
@@ -65,19 +30,8 @@ function isAncestor(ancestor, descendant, cwd) {
65
30
  }
66
31
  }
67
32
 
68
- /**
69
- * Does the branch introduce NOTHING the base does not already have?
70
- *
71
- * `git diff --quiet <base> <branch>` compares the two TREES: an empty diff
72
- * means every file the branch touched already reads exactly as the branch left
73
- * it in the base. That is precisely the state a squash-merge produces, and it
74
- * is a positive proof rather than an inference from history shape.
75
- *
76
- * Deliberately NOT `base...branch` (the three-dot form diffs against the merge
77
- * base, which for a squash-merged branch still shows the branch's own changes).
78
- *
79
- * Fail-CLOSED: any git error reads as "introduces something", never as merged.
80
- */
33
+ // Two-dot tree diff, never `base...branch`: the three-dot form still shows a squash-merged
34
+ // branch's own changes. Fail-closed: a git error never reads as merged.
81
35
  function treeIsIdentical(base, branch, cwd) {
82
36
  try {
83
37
  execFileSync('git', ['diff', '--quiet', base, branch], { cwd, stdio: 'pipe' });
@@ -87,13 +41,7 @@ function treeIsIdentical(base, branch, cwd) {
87
41
  }
88
42
  }
89
43
 
90
- /**
91
- * How many commits the branch carries that the base does not. Used only for
92
- * REPORTING — the classification never turns on it, because a squash-merged
93
- * branch legitimately has commits ahead while carrying no unintegrated work.
94
- *
95
- * @returns {number|null} null when it cannot be computed
96
- */
44
+ /** Reporting only: a squash-merged branch legitimately has commits ahead. @returns {number|null} */
97
45
  export function commitsAhead(base, branch, cwd) {
98
46
  try {
99
47
  const out = git(['rev-list', '--count', `${base}..${branch}`], cwd).trim();
@@ -105,12 +53,8 @@ export function commitsAhead(base, branch, cwd) {
105
53
  }
106
54
 
107
55
  /**
108
- * Resolve the ref to compare against: `origin/<default>` when the remote-tracking
109
- * ref exists (the authority on what has actually landed), else the local branch.
110
- *
111
- * @returns {string|null} null when neither resolves — the caller must then
112
- * classify everything as `unmerged`, because there is no baseline to prove
113
- * integration against.
56
+ * `origin/<default>` when it exists, else the local branch.
57
+ * @returns {string|null} null = no baseline, so everything is `unmerged`.
114
58
  */
115
59
  export function resolveBaseRef(defaultBranch, cwd) {
116
60
  if (!defaultBranch) return null;
@@ -123,19 +67,8 @@ export function resolveBaseRef(defaultBranch, cwd) {
123
67
  return null;
124
68
  }
125
69
 
126
- /**
127
- * Classify one branch. Pure decision logic over already-gathered facts, so the
128
- * table above is testable without a GitHub account.
129
- *
130
- * ORDER IS THE POLICY, and the two protective classes come FIRST: a branch that
131
- * is checked out, or that carries an OPEN PR, is never deleted even if its tree
132
- * happens to match the base — a PR being open means a human still has business
133
- * with it.
134
- *
135
- * @param {object} b - from listLocalBranches
136
- * @param {{ baseRef: string|null, cwd: string, pr: {number:number,state:string}|null|undefined }} ctx
137
- * @returns {{ name: string, kind: string, reason: string, pr: number|null, ahead: number|null, deletable: boolean }}
138
- */
70
+ // Order is the policy: checked-out and open-PR branches are protected before any
71
+ // merged check, even when the tree matches the base.
139
72
  export function classifyBranch(b, { baseRef, cwd, pr }) {
140
73
  const prNum = pr?.number ?? null;
141
74
  const mk = (kind, reason, deletable) => ({
@@ -163,12 +96,6 @@ export function classifyBranch(b, { baseRef, cwd, pr }) {
163
96
  return mk('unmerged', `carrega mudanças que não estão em ${baseRef} e nenhum PR MERGED comprova o contrário`, false);
164
97
  }
165
98
 
166
- /**
167
- * Classify every local branch except the default itself.
168
- *
169
- * @param {{ cwd: string, defaultBranch: string|null, prIndex: Map|null }} ctx
170
- * @returns {Array} one entry per branch, in `for-each-ref` order
171
- */
172
99
  export function classifyBranches({ cwd, defaultBranch, prIndex }) {
173
100
  const baseRef = resolveBaseRef(defaultBranch, cwd);
174
101
  return listLocalBranches(cwd)