@polymorphism-tech/morph-spec 8.34.2 → 8.35.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 (325) hide show
  1. package/CLAUDE.md +20 -3
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +18 -4
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +5 -4
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/MORPH.md +16 -17
  9. package/framework/agents.json +197 -44
  10. package/framework/ai-pin.json +22 -0
  11. package/framework/commands/morph-media.md +45 -0
  12. package/framework/docs/CLI.md +234 -9
  13. package/framework/evals/proof-rubrics/README.md +76 -0
  14. package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
  15. package/framework/evals/proof-rubrics/fiacao.md +54 -0
  16. package/framework/evals/proof-rubrics/guard-cego.md +52 -0
  17. package/framework/evals/proof-rubrics/media-claims.md +109 -0
  18. package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
  19. package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
  20. package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
  21. package/framework/evals/rubrics/architecture.md +30 -0
  22. package/framework/evals/rubrics/code-quality.md +26 -0
  23. package/framework/evals/rubrics/test-coverage.md +20 -0
  24. package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
  25. package/framework/hooks/claude-code/statusline.sh +11 -11
  26. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  27. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  28. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  29. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  30. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  31. package/framework/hooks/shared/e2e-check.js +135 -22
  32. package/framework/hooks/shared/stdin-reader.js +26 -26
  33. package/framework/rules/ai-agents-standards.md +14 -2
  34. package/framework/schemas/ai-pin.schema.json +97 -0
  35. package/framework/schemas/config.schema.json +107 -0
  36. package/framework/schemas/feature.schema.json +23 -4
  37. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  38. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  39. package/framework/skills/core/morph-apply/SKILL.md +26 -5
  40. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  41. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  42. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  43. package/framework/skills/core/morph-implement/SKILL.md +18 -2
  44. package/framework/skills/core/morph-media/SKILL.md +215 -0
  45. package/framework/skills/core/morph-plan/SKILL.md +33 -2
  46. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  47. package/framework/skills/core/morph-review/SKILL.md +166 -9
  48. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  49. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  50. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  51. package/framework/standards/PROVENANCE.json +81 -3
  52. package/framework/standards/STANDARDS.json +1483 -839
  53. package/framework/standards/ai-agents/agent-archetypes.md +160 -0
  54. package/framework/standards/ai-agents/agent-session.md +164 -68
  55. package/framework/standards/ai-agents/agent-spec.md +196 -0
  56. package/framework/standards/ai-agents/batching.md +61 -37
  57. package/framework/standards/ai-agents/context-providers.md +97 -24
  58. package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
  59. package/framework/standards/ai-agents/cost-and-budget.md +256 -0
  60. package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
  61. package/framework/standards/ai-agents/evals-with-cache.md +289 -0
  62. package/framework/standards/ai-agents/guardrails.md +222 -0
  63. package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
  64. package/framework/standards/ai-agents/mcp-server.md +119 -18
  65. package/framework/standards/ai-agents/mcp-tools.md +85 -32
  66. package/framework/standards/ai-agents/media-pipeline.md +352 -0
  67. package/framework/standards/ai-agents/media-video.md +267 -0
  68. package/framework/standards/ai-agents/middleware-patterns.md +39 -5
  69. package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
  70. package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
  71. package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
  72. package/framework/standards/ai-agents/modalities-vision.md +6 -2
  73. package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
  74. package/framework/standards/ai-agents/observability-patterns.md +227 -17
  75. package/framework/standards/ai-agents/production.md +38 -9
  76. package/framework/standards/ai-agents/prompt-sources.md +389 -0
  77. package/framework/standards/ai-agents/providers/model-registry.md +174 -39
  78. package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
  79. package/framework/standards/ai-agents/service-tier-flex.md +27 -19
  80. package/framework/standards/ai-agents/setup.md +166 -50
  81. package/framework/standards/ai-agents/structured-output.md +34 -3
  82. package/framework/standards/ai-agents/sweet-spot.md +23 -3
  83. package/framework/standards/ai-agents/testing-ai.md +236 -0
  84. package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
  85. package/framework/standards/ai-agents/workflows.md +27 -7
  86. package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
  87. package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
  88. package/framework/standards/infrastructure/docker/local-compose-isolation.md +3 -1
  89. package/framework/templates/.idea/morph-templates.xml +92 -0
  90. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  91. package/framework/templates/IDE-SNIPPETS.md +262 -262
  92. package/framework/templates/REGISTRY.json +13 -0
  93. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  94. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  95. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  96. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  97. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
  98. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  99. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  100. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  101. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  102. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  103. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  104. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  105. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  106. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  107. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
  108. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  109. package/framework/templates/code/typescript/contracts.ts +167 -167
  110. package/framework/templates/docs/clarifications.md +253 -253
  111. package/framework/templates/docs/schema-analysis.md +119 -119
  112. package/framework/templates/docs/ui-components.md +52 -52
  113. package/framework/templates/docs/ui-design-system.md +76 -76
  114. package/framework/templates/docs/ui-flows.md +167 -167
  115. package/framework/templates/docs/ui-mockups.md +98 -98
  116. package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
  117. package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
  118. package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
  119. package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
  120. package/framework/templates/dotnet/ai-kit/README.md +128 -0
  121. package/framework/templates/dotnet/ai-kit/global.json +9 -0
  122. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
  123. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
  148. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
  149. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
  150. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
  151. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
  152. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
  153. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
  154. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
  155. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
  156. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
  157. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
  158. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
  159. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
  160. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
  161. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
  162. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
  163. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
  164. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
  165. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
  166. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
  167. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
  168. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
  169. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
  170. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
  171. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
  172. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
  173. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
  174. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
  175. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
  176. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
  180. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
  181. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
  182. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
  183. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
  184. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
  185. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
  186. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
  187. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
  188. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
  189. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
  190. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
  191. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
  192. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
  193. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
  194. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
  195. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
  196. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
  197. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
  198. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
  199. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
  200. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
  201. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
  202. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
  203. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
  204. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
  205. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
  206. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
  207. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
  208. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
  209. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
  210. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
  211. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
  212. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
  213. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
  214. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
  215. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
  216. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
  217. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
  218. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
  219. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
  220. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
  221. package/framework/templates/feature/decisions.md +122 -122
  222. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  223. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  224. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  225. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  226. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  227. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  228. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  229. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  230. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  231. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  232. package/framework/templates/infrastructure/github/README.md +56 -56
  233. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  234. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  235. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  236. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  237. package/framework/templates/integrations/asaas-client.cs +387 -387
  238. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  239. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  240. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  241. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  242. package/framework/templates/project/validate.js +120 -120
  243. package/framework/templates/state.template.json +17 -17
  244. package/framework/templates/ui/design-system.css +226 -226
  245. package/package.json +3 -3
  246. package/src/commands/dashboard/dashboard.js +53 -53
  247. package/src/commands/e2e/e2e.js +15 -2
  248. package/src/commands/index.js +13 -13
  249. package/src/commands/project/checkpoint.js +209 -209
  250. package/src/commands/project/doctor.js +140 -5
  251. package/src/commands/project/update.js +469 -153
  252. package/src/commands/project/worktree.js +8 -1
  253. package/src/commands/state/advance.js +24 -2
  254. package/src/commands/state/approve.js +33 -1
  255. package/src/commands/state/create.js +72 -11
  256. package/src/commands/state/finish.js +333 -21
  257. package/src/commands/state/gate-check.js +190 -6
  258. package/src/commands/state/score.js +36 -1
  259. package/src/commands/validation/verify.js +134 -25
  260. package/src/core/state/phase-state-machine.js +197 -197
  261. package/src/core/state/state-manager.js +9 -1
  262. package/src/core/templates/template-renderer.js +477 -477
  263. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  264. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  265. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  266. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  267. package/src/dashboard/app/main.jsx +13 -13
  268. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  269. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  270. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  271. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  272. package/src/dashboard/app/pages/Standards.jsx +332 -332
  273. package/src/dashboard/app/styles/index.css +47 -47
  274. package/src/dashboard/index.html +12 -12
  275. package/src/dashboard/package.json +24 -24
  276. package/src/dashboard/scanner/phases-scanner.js +15 -15
  277. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  278. package/src/dashboard/scanner/rules-scanner.js +37 -37
  279. package/src/dashboard/scanner/skills-scanner.js +43 -43
  280. package/src/dashboard/scanner/squads-scanner.js +13 -13
  281. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  282. package/src/dashboard/vite.config.js +24 -24
  283. package/src/lib/ai/ai-checks.js +182 -0
  284. package/src/lib/ai/ai-project-scan.js +347 -0
  285. package/src/lib/ai/code-audit.js +181 -0
  286. package/src/lib/ai/comment-stripper.js +184 -0
  287. package/src/lib/ai/pin-drift.js +138 -0
  288. package/src/lib/ai/registry-audit.js +285 -0
  289. package/src/lib/detectors/design-system-detector.js +188 -188
  290. package/src/lib/detectors/index.js +217 -217
  291. package/src/lib/e2e/compose-env.js +50 -4
  292. package/src/lib/evaluator-report.js +269 -15
  293. package/src/lib/generators/design-system-generator.js +508 -508
  294. package/src/lib/generators/settings-generator.js +209 -209
  295. package/src/lib/git/default-branch.js +92 -4
  296. package/src/lib/hooks/hook-executor.js +167 -167
  297. package/src/lib/installers/effects.js +193 -0
  298. package/src/lib/installers/mcp-installer.js +719 -719
  299. package/src/lib/installers/templates-reconcile.js +3 -3
  300. package/src/lib/risk-detector.js +284 -0
  301. package/src/lib/standards/verified-against.js +276 -0
  302. package/src/lib/tasks/eval-node.js +485 -0
  303. package/src/lib/tasks/linux-runner.js +297 -0
  304. package/src/lib/tasks/task-classifier.js +30 -30
  305. package/src/lib/tasks/task-parser.js +209 -209
  306. package/src/lib/tasks/test-nodes.js +486 -0
  307. package/src/lib/tasks/test-runner.js +410 -33
  308. package/src/lib/validators/checkpoint-persistence.js +39 -39
  309. package/src/lib/validators/css/css-validator.js +340 -340
  310. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  311. package/src/lib/validators/shared/issue-counter.js +14 -14
  312. package/src/lib/validators/verify-runner.js +583 -28
  313. package/src/lib/worktree/docker-audit.js +287 -0
  314. package/src/lib/worktree/env-allocator.js +24 -0
  315. package/src/lib/worktree/worktree-audit.js +30 -1
  316. package/src/scripts/setup-infra.js +24 -11
  317. package/src/utils/agent-id-mapper.js +11 -11
  318. package/src/utils/agents-installer.js +18 -8
  319. package/src/utils/claude-md-injector.js +10 -5
  320. package/src/utils/claude-settings-manager.js +300 -300
  321. package/src/utils/file-copier.js +11 -4
  322. package/src/utils/hooks-installer.js +29 -26
  323. package/src/utils/skills-installer.js +36 -29
  324. package/src/utils/version-checker.js +68 -3
  325. package/framework/standards/ai-agents/prompts-in-db.md +0 -214
@@ -1,209 +1,209 @@
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
- */
8
-
9
- import { join } from 'path';
10
- import { existsSync } from 'fs';
11
- import { readFile, writeFile, mkdir } from 'fs/promises';
12
-
13
- const MORPH_MANAGED_KEY = '_morph_managed';
14
-
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
- export async function generateSettings(targetPath, detectedConfig, morphConfig = {}) {
23
- const claudeDir = join(targetPath, '.claude');
24
- const settingsPath = join(claudeDir, 'settings.local.json');
25
-
26
- // Read existing settings
27
- const existing = await readSettingsSafe(settingsPath);
28
-
29
- // Build stack-based permissions
30
- const stack = morphConfig?.project?.architecture || 'unknown';
31
- const permissions = buildPermissions(stack, detectedConfig);
32
-
33
- // Build generated settings
34
- const generated = {};
35
-
36
- if (permissions.length > 0) {
37
- generated.permissions = permissions;
38
- }
39
-
40
- // Track what we manage
41
- const managed = {
42
- permissions_added: permissions,
43
- mcps_detected: detectedConfig.mcpServers.map(s => s.name),
44
- plugins_detected: detectedConfig.plugins.map(p => p.name),
45
- generated_at: new Date().toISOString()
46
- };
47
-
48
- // Merge without overwriting user config
49
- const merged = mergeSettings(existing, generated);
50
- merged[MORPH_MANAGED_KEY] = managed;
51
-
52
- // Ensure directory exists
53
- if (!existsSync(claudeDir)) {
54
- await mkdir(claudeDir, { recursive: true });
55
- }
56
-
57
- await writeFile(settingsPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
58
-
59
- return {
60
- settingsPath,
61
- permissionsAdded: permissions.length,
62
- managed
63
- };
64
- }
65
-
66
- /**
67
- * Build permission rules based on detected stack
68
- * @param {string} stack - Detected project stack
69
- * @param {Object} detectedConfig - Detected Claude config
70
- * @returns {Array<string>} Permission patterns
71
- */
72
- export function buildPermissions(stack, detectedConfig = {}) {
73
- const permissions = [];
74
-
75
- // Stack-based permissions
76
- const stackPermissions = {
77
- nodejs: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
78
- typescript: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)', 'Bash(tsc:*)'],
79
- nextjs: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
80
- react: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
81
- vue: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
82
- dotnet: ['Bash(dotnet:*)', 'Bash(nuget:*)'],
83
- python: ['Bash(python:*)', 'Bash(pip:*)', 'Bash(pytest:*)'],
84
- go: ['Bash(go:*)']
85
- };
86
-
87
- if (stackPermissions[stack]) {
88
- permissions.push(...stackPermissions[stack]);
89
- }
90
-
91
- // morph-spec CLI always needs node/npx
92
- if (!permissions.includes('Bash(npx:*)')) {
93
- permissions.push('Bash(npx:*)');
94
- }
95
-
96
- return permissions;
97
- }
98
-
99
- /**
100
- * Deep merge settings without overwriting user entries
101
- * @param {Object} existing - Current settings.local.json
102
- * @param {Object} generated - Generated settings to merge
103
- * @returns {Object} Merged settings
104
- */
105
- export function mergeSettings(existing, generated) {
106
- const result = { ...existing };
107
-
108
- for (const [key, value] of Object.entries(generated)) {
109
- if (key === MORPH_MANAGED_KEY) continue;
110
-
111
- if (Array.isArray(value) && Array.isArray(result[key])) {
112
- // Merge arrays without duplicates
113
- const set = new Set(result[key]);
114
- for (const item of value) {
115
- set.add(item);
116
- }
117
- result[key] = [...set];
118
- } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
119
- // Recursively merge objects
120
- result[key] = mergeSettings(result[key] || {}, value);
121
- } else if (!(key in result)) {
122
- // Only add new keys, never overwrite existing
123
- result[key] = value;
124
- }
125
- }
126
-
127
- return result;
128
- }
129
-
130
- /**
131
- * Remove only morph-spec managed entries from settings
132
- * @param {string} settingsPath - Path to settings.local.json
133
- * @returns {Promise<Object>} Summary of what was removed
134
- */
135
- export async function resetMorphSettings(settingsPath) {
136
- if (!existsSync(settingsPath)) {
137
- return { removed: false, reason: 'file not found' };
138
- }
139
-
140
- const settings = await readSettingsSafe(settingsPath);
141
- const managed = settings[MORPH_MANAGED_KEY];
142
-
143
- if (!managed) {
144
- return { removed: false, reason: 'no _morph_managed section found' };
145
- }
146
-
147
- // Remove managed permissions
148
- if (managed.permissions_added && Array.isArray(settings.permissions)) {
149
- settings.permissions = settings.permissions.filter(
150
- p => !managed.permissions_added.includes(p)
151
- );
152
- if (settings.permissions.length === 0) {
153
- delete settings.permissions;
154
- }
155
- }
156
-
157
- // Remove the managed marker itself
158
- delete settings[MORPH_MANAGED_KEY];
159
-
160
- // Write back
161
- await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
162
-
163
- return {
164
- removed: true,
165
- permissionsRemoved: managed.permissions_added || [],
166
- previouslyGenerated: managed.generated_at
167
- };
168
- }
169
-
170
- /**
171
- * Save integrations.json with detected config
172
- * @param {string} targetPath - Project root directory
173
- * @param {Object} detectedConfig - Output from detectClaudeConfig()
174
- */
175
- export async function saveIntegrations(targetPath, detectedConfig) {
176
- const integrationsPath = join(targetPath, '.morph', 'config', 'integrations.json');
177
- const integrationsDir = join(targetPath, '.morph', 'config');
178
-
179
- if (!existsSync(integrationsDir)) {
180
- await mkdir(integrationsDir, { recursive: true });
181
- }
182
-
183
- const integrations = {
184
- version: '1.0.0',
185
- detectedAt: new Date().toISOString(),
186
- plugins: detectedConfig.plugins,
187
- mcpServers: detectedConfig.mcpServers,
188
- superpowers: detectedConfig.superpowers,
189
- customSkills: detectedConfig.customSkills.length,
190
- customCommands: detectedConfig.customCommands.length,
191
- hooks: detectedConfig.hooks.length
192
- };
193
-
194
- await writeFile(integrationsPath, JSON.stringify(integrations, null, 2) + '\n', 'utf-8');
195
- return integrationsPath;
196
- }
197
-
198
- // ============================================================================
199
- // Helpers
200
- // ============================================================================
201
-
202
- async function readSettingsSafe(settingsPath) {
203
- try {
204
- if (!existsSync(settingsPath)) return {};
205
- return JSON.parse(await readFile(settingsPath, 'utf-8'));
206
- } catch {
207
- return {};
208
- }
209
- }
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
+ */
8
+
9
+ import { join } from 'path';
10
+ import { existsSync } from 'fs';
11
+ import { readFile, writeFile, mkdir } from 'fs/promises';
12
+
13
+ const MORPH_MANAGED_KEY = '_morph_managed';
14
+
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
+ export async function generateSettings(targetPath, detectedConfig, morphConfig = {}) {
23
+ const claudeDir = join(targetPath, '.claude');
24
+ const settingsPath = join(claudeDir, 'settings.local.json');
25
+
26
+ // Read existing settings
27
+ const existing = await readSettingsSafe(settingsPath);
28
+
29
+ // Build stack-based permissions
30
+ const stack = morphConfig?.project?.architecture || 'unknown';
31
+ const permissions = buildPermissions(stack, detectedConfig);
32
+
33
+ // Build generated settings
34
+ const generated = {};
35
+
36
+ if (permissions.length > 0) {
37
+ generated.permissions = permissions;
38
+ }
39
+
40
+ // Track what we manage
41
+ const managed = {
42
+ permissions_added: permissions,
43
+ mcps_detected: detectedConfig.mcpServers.map(s => s.name),
44
+ plugins_detected: detectedConfig.plugins.map(p => p.name),
45
+ generated_at: new Date().toISOString()
46
+ };
47
+
48
+ // Merge without overwriting user config
49
+ const merged = mergeSettings(existing, generated);
50
+ merged[MORPH_MANAGED_KEY] = managed;
51
+
52
+ // Ensure directory exists
53
+ if (!existsSync(claudeDir)) {
54
+ await mkdir(claudeDir, { recursive: true });
55
+ }
56
+
57
+ await writeFile(settingsPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
58
+
59
+ return {
60
+ settingsPath,
61
+ permissionsAdded: permissions.length,
62
+ managed
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Build permission rules based on detected stack
68
+ * @param {string} stack - Detected project stack
69
+ * @param {Object} detectedConfig - Detected Claude config
70
+ * @returns {Array<string>} Permission patterns
71
+ */
72
+ export function buildPermissions(stack, detectedConfig = {}) {
73
+ const permissions = [];
74
+
75
+ // Stack-based permissions
76
+ const stackPermissions = {
77
+ nodejs: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
78
+ typescript: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)', 'Bash(tsc:*)'],
79
+ nextjs: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
80
+ react: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
81
+ vue: ['Bash(node:*)', 'Bash(npm:*)', 'Bash(npx:*)'],
82
+ dotnet: ['Bash(dotnet:*)', 'Bash(nuget:*)'],
83
+ python: ['Bash(python:*)', 'Bash(pip:*)', 'Bash(pytest:*)'],
84
+ go: ['Bash(go:*)']
85
+ };
86
+
87
+ if (stackPermissions[stack]) {
88
+ permissions.push(...stackPermissions[stack]);
89
+ }
90
+
91
+ // morph-spec CLI always needs node/npx
92
+ if (!permissions.includes('Bash(npx:*)')) {
93
+ permissions.push('Bash(npx:*)');
94
+ }
95
+
96
+ return permissions;
97
+ }
98
+
99
+ /**
100
+ * Deep merge settings without overwriting user entries
101
+ * @param {Object} existing - Current settings.local.json
102
+ * @param {Object} generated - Generated settings to merge
103
+ * @returns {Object} Merged settings
104
+ */
105
+ export function mergeSettings(existing, generated) {
106
+ const result = { ...existing };
107
+
108
+ for (const [key, value] of Object.entries(generated)) {
109
+ if (key === MORPH_MANAGED_KEY) continue;
110
+
111
+ if (Array.isArray(value) && Array.isArray(result[key])) {
112
+ // Merge arrays without duplicates
113
+ const set = new Set(result[key]);
114
+ for (const item of value) {
115
+ set.add(item);
116
+ }
117
+ result[key] = [...set];
118
+ } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
119
+ // Recursively merge objects
120
+ result[key] = mergeSettings(result[key] || {}, value);
121
+ } else if (!(key in result)) {
122
+ // Only add new keys, never overwrite existing
123
+ result[key] = value;
124
+ }
125
+ }
126
+
127
+ return result;
128
+ }
129
+
130
+ /**
131
+ * Remove only morph-spec managed entries from settings
132
+ * @param {string} settingsPath - Path to settings.local.json
133
+ * @returns {Promise<Object>} Summary of what was removed
134
+ */
135
+ export async function resetMorphSettings(settingsPath) {
136
+ if (!existsSync(settingsPath)) {
137
+ return { removed: false, reason: 'file not found' };
138
+ }
139
+
140
+ const settings = await readSettingsSafe(settingsPath);
141
+ const managed = settings[MORPH_MANAGED_KEY];
142
+
143
+ if (!managed) {
144
+ return { removed: false, reason: 'no _morph_managed section found' };
145
+ }
146
+
147
+ // Remove managed permissions
148
+ if (managed.permissions_added && Array.isArray(settings.permissions)) {
149
+ settings.permissions = settings.permissions.filter(
150
+ p => !managed.permissions_added.includes(p)
151
+ );
152
+ if (settings.permissions.length === 0) {
153
+ delete settings.permissions;
154
+ }
155
+ }
156
+
157
+ // Remove the managed marker itself
158
+ delete settings[MORPH_MANAGED_KEY];
159
+
160
+ // Write back
161
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
162
+
163
+ return {
164
+ removed: true,
165
+ permissionsRemoved: managed.permissions_added || [],
166
+ previouslyGenerated: managed.generated_at
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Save integrations.json with detected config
172
+ * @param {string} targetPath - Project root directory
173
+ * @param {Object} detectedConfig - Output from detectClaudeConfig()
174
+ */
175
+ export async function saveIntegrations(targetPath, detectedConfig) {
176
+ const integrationsPath = join(targetPath, '.morph', 'config', 'integrations.json');
177
+ const integrationsDir = join(targetPath, '.morph', 'config');
178
+
179
+ if (!existsSync(integrationsDir)) {
180
+ await mkdir(integrationsDir, { recursive: true });
181
+ }
182
+
183
+ const integrations = {
184
+ version: '1.0.0',
185
+ detectedAt: new Date().toISOString(),
186
+ plugins: detectedConfig.plugins,
187
+ mcpServers: detectedConfig.mcpServers,
188
+ superpowers: detectedConfig.superpowers,
189
+ customSkills: detectedConfig.customSkills.length,
190
+ customCommands: detectedConfig.customCommands.length,
191
+ hooks: detectedConfig.hooks.length
192
+ };
193
+
194
+ await writeFile(integrationsPath, JSON.stringify(integrations, null, 2) + '\n', 'utf-8');
195
+ return integrationsPath;
196
+ }
197
+
198
+ // ============================================================================
199
+ // Helpers
200
+ // ============================================================================
201
+
202
+ async function readSettingsSafe(settingsPath) {
203
+ try {
204
+ if (!existsSync(settingsPath)) return {};
205
+ return JSON.parse(await readFile(settingsPath, 'utf-8'));
206
+ } catch {
207
+ return {};
208
+ }
209
+ }
@@ -126,12 +126,52 @@ function parseShortstat(text) {
126
126
  * @returns {number}
127
127
  */
128
128
  function countUntrackedFiles(cwd) {
129
+ return listUntrackedFiles(cwd).length;
130
+ }
131
+
132
+ /**
133
+ * Split git's stdout into clean, forward-slashed, non-empty path lines.
134
+ *
135
+ * One helper for both probes because the two disagreeing about what a "line" is
136
+ * — a stray `\r` on Windows, a trailing empty line — would make the count and
137
+ * the listing describe different sets from the same output.
138
+ *
139
+ * @param {string} text
140
+ * @returns {string[]}
141
+ */
142
+ function splitLines(text) {
143
+ return String(text)
144
+ .split('\n')
145
+ .map((line) => line.trim().replace(/\\/g, '/'))
146
+ .filter(Boolean);
147
+ }
148
+
149
+ /**
150
+ * The brand-new UNTRACKED files, by NAME — the listing behind
151
+ * {@link countUntrackedFiles}, and the second half of {@link changedPathsAgainst}.
152
+ *
153
+ * The two callers need the same fact at different resolutions, and this is the
154
+ * ONLY place the probe is spelled: `git diff` never shows a file that was never
155
+ * `git add`-ed, regardless of ref or working-tree mode, so anything that answers
156
+ * "what changed" has to ask twice. A second copy of this command is exactly how
157
+ * the invisible-new-file bug would come back on one caller and not the other.
158
+ *
159
+ * `--exclude-standard` honours gitignore the way `git status` does, so build
160
+ * output never inflates either answer.
161
+ *
162
+ * Fail-open to `[]`: this probe is secondary to the diff and must never take the
163
+ * whole result down.
164
+ *
165
+ * @param {string} cwd
166
+ * @returns {string[]}
167
+ */
168
+ function listUntrackedFiles(cwd) {
129
169
  try {
130
- const out = execFileSync('git', ['ls-files', '--others', '--exclude-standard'], { cwd, stdio: 'pipe' })
131
- .toString();
132
- return out.split('\n').filter((l) => l.trim()).length;
170
+ return splitLines(
171
+ execFileSync('git', ['ls-files', '--others', '--exclude-standard'], { cwd, stdio: 'pipe' }).toString()
172
+ );
133
173
  } catch {
134
- return 0;
174
+ return [];
135
175
  }
136
176
  }
137
177
 
@@ -172,3 +212,51 @@ export function getDiffStat(baseRef, cwd = process.cwd()) {
172
212
  return null;
173
213
  }
174
214
  }
215
+
216
+ /**
217
+ * Every path this working tree CHANGED against `baseRef` — tracked edits plus
218
+ * brand-new untracked files.
219
+ *
220
+ * WHY IT LIVES BESIDE getDiffStat, AND REUSES ITS UNTRACKED PROBE
221
+ *
222
+ * The two answer the same question at different resolutions ("how much changed"
223
+ * versus "what changed"), and both depend on the SAME fragile fact: `git diff`
224
+ * is tracked-edits-only, so a brand-new file is invisible to it no matter which
225
+ * ref is named. `getDiffStat`'s docblock has documented that for a while; a
226
+ * second copy of the probe here is precisely how the "new file is invisible"
227
+ * bug comes back on one of the two call sites and not the other. So
228
+ * `countUntrackedFiles`'s command is not retyped — the listing is.
229
+ *
230
+ * `git diff --name-only <baseRef>` against the WORKING TREE, deliberately not
231
+ * `baseRef...HEAD`: a gate-check in the middle of implement must see work that
232
+ * is not committed yet. Someone who has only staged the prompt change has still
233
+ * changed the prompt.
234
+ *
235
+ * FAIL-OPEN, and the caller must know what that means. `[]` comes back both for
236
+ * "nothing changed" and for "the ref did not resolve", exactly like
237
+ * `getDiffStat` returning null. The distinction that MATTERS to the caller is a
238
+ * different one — "the project declares no watched path" versus "it declares
239
+ * some and none were touched" — and that one is answered by the caller's own
240
+ * `declared` flag, never by the emptiness of this array.
241
+ *
242
+ * @param {string|null} baseRef - typically `detectDefaultBranch(cwd)`
243
+ * @param {string} [cwd]
244
+ * @returns {string[]} Repo-relative paths with forward slashes, deduplicated,
245
+ * sorted. Empty when `baseRef` is falsy, cwd is not a repo, or the diff fails.
246
+ */
247
+ export function changedPathsAgainst(baseRef, cwd = process.cwd()) {
248
+ if (!baseRef) return [];
249
+
250
+ let tracked;
251
+ try {
252
+ tracked = splitLines(
253
+ execFileSync('git', ['diff', '--name-only', baseRef], { cwd, stdio: 'pipe' }).toString()
254
+ );
255
+ } catch {
256
+ // A baseRef that does not resolve is not a reason to claim "nothing
257
+ // changed" loudly — the caller's policy is written to be inert on `[]`.
258
+ return [];
259
+ }
260
+
261
+ return [...new Set([...tracked, ...listUntrackedFiles(cwd)])].sort();
262
+ }