@polymorphism-tech/morph-spec 8.34.2 → 8.35.1

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 (328) hide show
  1. package/CLAUDE.md +22 -5
  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 +236 -11
  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/core/worktree-env-inject.js +43 -0
  26. package/framework/hooks/claude-code/statusline.sh +11 -11
  27. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  28. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  29. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  30. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  31. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  32. package/framework/hooks/shared/e2e-check.js +135 -22
  33. package/framework/hooks/shared/payload-utils.js +15 -2
  34. package/framework/hooks/shared/stdin-reader.js +26 -26
  35. package/framework/rules/ai-agents-standards.md +14 -2
  36. package/framework/schemas/ai-pin.schema.json +97 -0
  37. package/framework/schemas/config.schema.json +107 -0
  38. package/framework/schemas/feature.schema.json +23 -4
  39. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  40. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  41. package/framework/skills/core/morph-apply/SKILL.md +26 -5
  42. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  43. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  44. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  45. package/framework/skills/core/morph-implement/SKILL.md +18 -2
  46. package/framework/skills/core/morph-media/SKILL.md +215 -0
  47. package/framework/skills/core/morph-plan/SKILL.md +33 -2
  48. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  49. package/framework/skills/core/morph-review/SKILL.md +166 -9
  50. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  51. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  52. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  53. package/framework/standards/PROVENANCE.json +81 -3
  54. package/framework/standards/STANDARDS.json +1483 -839
  55. package/framework/standards/ai-agents/agent-archetypes.md +160 -0
  56. package/framework/standards/ai-agents/agent-session.md +164 -68
  57. package/framework/standards/ai-agents/agent-spec.md +196 -0
  58. package/framework/standards/ai-agents/batching.md +61 -37
  59. package/framework/standards/ai-agents/context-providers.md +97 -24
  60. package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
  61. package/framework/standards/ai-agents/cost-and-budget.md +256 -0
  62. package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
  63. package/framework/standards/ai-agents/evals-with-cache.md +289 -0
  64. package/framework/standards/ai-agents/guardrails.md +222 -0
  65. package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
  66. package/framework/standards/ai-agents/mcp-server.md +119 -18
  67. package/framework/standards/ai-agents/mcp-tools.md +85 -32
  68. package/framework/standards/ai-agents/media-pipeline.md +352 -0
  69. package/framework/standards/ai-agents/media-video.md +267 -0
  70. package/framework/standards/ai-agents/middleware-patterns.md +39 -5
  71. package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
  72. package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
  73. package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
  74. package/framework/standards/ai-agents/modalities-vision.md +6 -2
  75. package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
  76. package/framework/standards/ai-agents/observability-patterns.md +227 -17
  77. package/framework/standards/ai-agents/production.md +38 -9
  78. package/framework/standards/ai-agents/prompt-sources.md +389 -0
  79. package/framework/standards/ai-agents/providers/model-registry.md +174 -39
  80. package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
  81. package/framework/standards/ai-agents/service-tier-flex.md +27 -19
  82. package/framework/standards/ai-agents/setup.md +166 -50
  83. package/framework/standards/ai-agents/structured-output.md +34 -3
  84. package/framework/standards/ai-agents/sweet-spot.md +23 -3
  85. package/framework/standards/ai-agents/testing-ai.md +236 -0
  86. package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
  87. package/framework/standards/ai-agents/workflows.md +27 -7
  88. package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
  89. package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
  90. package/framework/standards/infrastructure/docker/local-compose-isolation.md +3 -1
  91. package/framework/templates/.idea/morph-templates.xml +92 -0
  92. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  93. package/framework/templates/IDE-SNIPPETS.md +262 -262
  94. package/framework/templates/REGISTRY.json +13 -0
  95. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  96. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  97. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  98. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  99. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
  100. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  101. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  102. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  103. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  104. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  105. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  106. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  107. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  108. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  109. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
  110. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  111. package/framework/templates/code/typescript/contracts.ts +167 -167
  112. package/framework/templates/docs/clarifications.md +253 -253
  113. package/framework/templates/docs/schema-analysis.md +119 -119
  114. package/framework/templates/docs/ui-components.md +52 -52
  115. package/framework/templates/docs/ui-design-system.md +76 -76
  116. package/framework/templates/docs/ui-flows.md +167 -167
  117. package/framework/templates/docs/ui-mockups.md +98 -98
  118. package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
  119. package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
  120. package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
  121. package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
  122. package/framework/templates/dotnet/ai-kit/README.md +128 -0
  123. package/framework/templates/dotnet/ai-kit/global.json +9 -0
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
  148. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
  149. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
  150. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
  151. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
  152. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
  153. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
  154. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
  155. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
  156. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
  157. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
  158. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
  159. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
  160. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
  161. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
  162. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
  163. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
  164. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
  165. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
  166. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
  167. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
  168. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
  169. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
  170. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
  171. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
  172. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
  173. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
  174. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
  175. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
  176. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
  180. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
  181. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
  182. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
  183. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
  184. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
  185. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
  186. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
  187. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
  188. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
  189. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
  190. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
  191. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
  192. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
  193. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
  194. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
  195. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
  196. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
  197. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
  198. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
  199. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
  200. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
  201. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
  202. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
  203. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
  204. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
  205. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
  206. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
  207. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
  208. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
  209. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
  210. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
  211. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
  212. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
  213. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
  214. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
  215. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
  216. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
  217. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
  218. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
  219. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
  220. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
  221. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
  222. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
  223. package/framework/templates/feature/decisions.md +122 -122
  224. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  225. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  226. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  227. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  228. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  229. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  230. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  231. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  232. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  233. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  234. package/framework/templates/infrastructure/github/README.md +56 -56
  235. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  236. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  237. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  238. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  239. package/framework/templates/integrations/asaas-client.cs +387 -387
  240. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  241. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  242. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  243. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  244. package/framework/templates/project/validate.js +120 -120
  245. package/framework/templates/state.template.json +17 -17
  246. package/framework/templates/ui/design-system.css +226 -226
  247. package/package.json +3 -3
  248. package/src/commands/dashboard/dashboard.js +53 -53
  249. package/src/commands/e2e/e2e.js +15 -2
  250. package/src/commands/index.js +13 -13
  251. package/src/commands/project/checkpoint.js +209 -209
  252. package/src/commands/project/doctor.js +148 -6
  253. package/src/commands/project/update.js +469 -153
  254. package/src/commands/project/worktree.js +76 -4
  255. package/src/commands/state/advance.js +24 -2
  256. package/src/commands/state/approve.js +33 -1
  257. package/src/commands/state/create.js +72 -11
  258. package/src/commands/state/finish.js +379 -24
  259. package/src/commands/state/gate-check.js +190 -6
  260. package/src/commands/state/score.js +36 -1
  261. package/src/commands/validation/verify.js +134 -25
  262. package/src/core/state/phase-state-machine.js +197 -197
  263. package/src/core/state/state-manager.js +9 -1
  264. package/src/core/templates/template-renderer.js +477 -477
  265. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  266. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  267. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  268. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  269. package/src/dashboard/app/main.jsx +13 -13
  270. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  271. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  272. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  273. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  274. package/src/dashboard/app/pages/Standards.jsx +332 -332
  275. package/src/dashboard/app/styles/index.css +47 -47
  276. package/src/dashboard/index.html +12 -12
  277. package/src/dashboard/package.json +24 -24
  278. package/src/dashboard/scanner/phases-scanner.js +15 -15
  279. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  280. package/src/dashboard/scanner/rules-scanner.js +37 -37
  281. package/src/dashboard/scanner/skills-scanner.js +43 -43
  282. package/src/dashboard/scanner/squads-scanner.js +13 -13
  283. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  284. package/src/dashboard/vite.config.js +24 -24
  285. package/src/lib/ai/ai-checks.js +182 -0
  286. package/src/lib/ai/ai-project-scan.js +347 -0
  287. package/src/lib/ai/code-audit.js +181 -0
  288. package/src/lib/ai/comment-stripper.js +184 -0
  289. package/src/lib/ai/pin-drift.js +138 -0
  290. package/src/lib/ai/registry-audit.js +285 -0
  291. package/src/lib/detectors/design-system-detector.js +188 -188
  292. package/src/lib/detectors/index.js +217 -217
  293. package/src/lib/e2e/compose-env.js +50 -4
  294. package/src/lib/evaluator-report.js +269 -15
  295. package/src/lib/generators/design-system-generator.js +508 -508
  296. package/src/lib/generators/settings-generator.js +209 -209
  297. package/src/lib/git/default-branch.js +92 -4
  298. package/src/lib/git/pr-files.js +56 -0
  299. package/src/lib/hooks/hook-executor.js +167 -167
  300. package/src/lib/installers/effects.js +193 -0
  301. package/src/lib/installers/mcp-installer.js +719 -719
  302. package/src/lib/installers/templates-reconcile.js +3 -3
  303. package/src/lib/risk-detector.js +284 -0
  304. package/src/lib/standards/verified-against.js +276 -0
  305. package/src/lib/tasks/eval-node.js +485 -0
  306. package/src/lib/tasks/linux-runner.js +297 -0
  307. package/src/lib/tasks/task-classifier.js +30 -30
  308. package/src/lib/tasks/task-parser.js +209 -209
  309. package/src/lib/tasks/test-nodes.js +486 -0
  310. package/src/lib/tasks/test-runner.js +410 -33
  311. package/src/lib/validators/checkpoint-persistence.js +39 -39
  312. package/src/lib/validators/css/css-validator.js +340 -340
  313. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  314. package/src/lib/validators/shared/issue-counter.js +14 -14
  315. package/src/lib/validators/verify-runner.js +583 -28
  316. package/src/lib/worktree/docker-audit.js +287 -0
  317. package/src/lib/worktree/env-allocator.js +24 -0
  318. package/src/lib/worktree/worktree-audit.js +30 -1
  319. package/src/scripts/setup-infra.js +24 -11
  320. package/src/utils/agent-id-mapper.js +11 -11
  321. package/src/utils/agents-installer.js +18 -8
  322. package/src/utils/claude-md-injector.js +10 -5
  323. package/src/utils/claude-settings-manager.js +300 -300
  324. package/src/utils/file-copier.js +11 -4
  325. package/src/utils/hooks-installer.js +29 -26
  326. package/src/utils/skills-installer.js +36 -29
  327. package/src/utils/version-checker.js +68 -3
  328. package/framework/standards/ai-agents/prompts-in-db.md +0 -214
@@ -0,0 +1,182 @@
1
+ /**
2
+ * ai-checks.js — o coletor das cinco checagens de `morph-spec doctor --ai`.
3
+ *
4
+ * Contrato de uma checagem:
5
+ *
6
+ * { id, name, status: 'ok'|'warn'|'error'|'skip', reason?, findings: [], unverified?: [] }
7
+ *
8
+ * Duas regras que este módulo existe para tornar mecânicas:
9
+ *
10
+ * 1. **`status: 'skip'` sai com `reason` obrigatória.** "Não verificado" sem
11
+ * motivo é ruído — quem lê não consegue decidir se falta insumo ou se falta
12
+ * checagem. Um `skip` sem razão é bug deste arquivo, e há canário.
13
+ * 2. **`null` de uma função de checagem é DESCONHECIDO, nunca "não achei
14
+ * nada".** `[]` é "perguntei e não achei" e vira `ok`. É a doutrina de
15
+ * `pr-index.js`, já viva no `doctor`.
16
+ *
17
+ * E uma proibição: **nada alcançável daqui escreve.** Os candidatos óbvios ao
18
+ * erro — `scripts/generate-ai-pin-props.js` e
19
+ * `scripts/generate-standards-registry.js` — escrevem por padrão e NÃO entram
20
+ * no grafo de imports deste módulo. É a lição do T10 da #90, onde a auditoria
21
+ * ia ALOCAR um bloco de porta ao auditar, inventando o estado que reportava.
22
+ * O canário assere a lista de chamadas feitas, não só o retorno.
23
+ */
24
+
25
+ import { scanAiPackageRefs } from './ai-project-scan.js';
26
+ import { resolveAiPin, auditPinDrift } from './pin-drift.js';
27
+ import { locateAiRegistry, auditModelPricing, auditApiPerAlias } from './registry-audit.js';
28
+ import { auditRenamedApi, auditPromptSource, renamedApiGate } from './code-audit.js';
29
+
30
+ /** Os cinco ids, na ordem em que o relatório os imprime. */
31
+ export const AI_CHECK_IDS = ['pin-drift', 'removed-api', 'pricing-missing', 'api-per-alias', 'prompt-source'];
32
+
33
+ /** Nome legível de cada checagem. */
34
+ export const AI_CHECK_NAMES = {
35
+ 'pin-drift': 'pin × projeto (versões de pacote de IA)',
36
+ 'removed-api': 'API renomeada antes do 1.0 GA',
37
+ 'pricing-missing': 'tarifa por alias do registry de modelos',
38
+ 'api-per-alias': 'campo `api` por alias do registry de modelos',
39
+ 'prompt-source': 'fonte declarada das instruções do agente',
40
+ };
41
+
42
+ /** O pacote cuja versão gateia a checagem de API renomeada. */
43
+ export const MAF_PACKAGE = 'Microsoft.Agents.AI';
44
+
45
+ /** As dependências reais. Substituíveis inteiras ou em parte, por canário. */
46
+ export function defaultAiDeps() {
47
+ return {
48
+ scanAiPackageRefs,
49
+ resolveAiPin,
50
+ auditPinDrift,
51
+ renamedApiGate,
52
+ auditRenamedApi,
53
+ locateAiRegistry,
54
+ auditModelPricing,
55
+ auditApiPerAlias,
56
+ auditPromptSource,
57
+ };
58
+ }
59
+
60
+ function statusFrom(findings) {
61
+ if (findings.some((f) => f.severity === 'error')) return 'error';
62
+ if (findings.some((f) => f.severity === 'warn')) return 'warn';
63
+ return 'ok';
64
+ }
65
+
66
+ function skip(id, reason, extra = {}) {
67
+ return { id, name: AI_CHECK_NAMES[id], status: 'skip', reason, findings: [], ...extra };
68
+ }
69
+
70
+ function resolved(id, findings, extra = {}) {
71
+ return { id, name: AI_CHECK_NAMES[id], status: statusFrom(findings), findings, ...extra };
72
+ }
73
+
74
+ function skipAll(reason) {
75
+ return AI_CHECK_IDS.map((id) => skip(id, reason));
76
+ }
77
+
78
+ /**
79
+ * Roda as cinco checagens sobre um projeto.
80
+ *
81
+ * As três condições de silêncio total acontecem ANTES de qualquer leitura do
82
+ * pin — e é essa ordem, não o retorno, que o canário assere: sem `.csproj`
83
+ * alcançável, `resolveAiPin` não é sequer chamada.
84
+ *
85
+ * @param {string} targetPath - raiz do projeto
86
+ * @param {object} [options]
87
+ * @param {object|null} [options.config] - `.morph/config/config.json` já lido
88
+ * @param {object} [options.deps] - substituição parcial de `defaultAiDeps()`
89
+ * @returns {Array<{id: string, name: string, status: string, reason?: string, findings: Array}>}
90
+ */
91
+ export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
92
+ const d = { ...defaultAiDeps(), ...deps };
93
+
94
+ const scan = d.scanAiPackageRefs(targetPath);
95
+
96
+ // Condição 4 do silêncio: UNKNOWN nunca vira "sem drift".
97
+ if (scan === null) {
98
+ return skipAll('nenhum arquivo de projeto .NET foi legível a partir desta raiz — '
99
+ + 'não verificado, o que NÃO é o mesmo que "sem drift"');
100
+ }
101
+
102
+ // Condição 1: sem `.csproj` alcançável. O pin nem é lido.
103
+ if (scan.projectFiles.length === 0) {
104
+ return skipAll('nenhum `.csproj` alcançável a partir desta raiz — não há projeto .NET a julgar');
105
+ }
106
+
107
+ // Condição 3: `.csproj` presentes, nenhum pacote de IA entre eles.
108
+ const aiPackages = scan.packages.filter((p) => p.ai);
109
+ if (aiPackages.length === 0) {
110
+ return skipAll(`${scan.projectFiles.length} projeto(s) .NET varrido(s) e nenhum pacote de IA `
111
+ + 'declarado entre eles — um projeto .NET sem MAF não é um projeto MAF quebrado');
112
+ }
113
+
114
+ const unverified = scan.unreadable.slice();
115
+ const checks = [];
116
+
117
+ // ── pin-drift ────────────────────────────────────────────────────────────
118
+ // Condição 2: sem pin resolvível, a checagem que depende dele sai skip.
119
+ const pin = d.resolveAiPin(targetPath);
120
+ if (!pin) {
121
+ checks.push(skip('pin-drift', 'nenhum `ai-pin.json` resolvível (nem em `.morph/framework/`, nem '
122
+ + 'no pacote da CLI) — falta de régua é ausência de dado, nunca defeito do projeto', { unverified }));
123
+ } else {
124
+ const findings = d.auditPinDrift({ scan, pin });
125
+ checks.push(findings === null
126
+ ? skip('pin-drift', 'a comparação com o pin não pôde ser feita', { unverified })
127
+ : resolved('pin-drift', findings, { unverified }));
128
+ }
129
+
130
+ // ── removed-api ──────────────────────────────────────────────────────────
131
+ const maf = scan.packages.find((p) => p.name === MAF_PACKAGE) ?? null;
132
+ const gate = d.renamedApiGate(maf);
133
+ if (gate !== null) {
134
+ checks.push(skip('removed-api', gate));
135
+ } else {
136
+ const findings = d.auditRenamedApi(targetPath, { mafPackage: maf });
137
+ checks.push(findings === null
138
+ ? skip('removed-api', 'a varredura de `.cs` não pôde ser feita')
139
+ : resolved('removed-api', findings));
140
+ }
141
+
142
+ // ── pricing-missing e api-per-alias ──────────────────────────────────────
143
+ const registry = d.locateAiRegistry(targetPath, config);
144
+ const noRegistry = 'nenhum registry de modelos encontrado — nem `project.ai.modelRegistry` no '
145
+ + 'config, nem um `.json` com `defaultAlias` + mapa de aliases, nem uma seção `Pricing` num '
146
+ + '`appsettings*.json`';
147
+
148
+ const pricing = registry ? d.auditModelPricing(registry) : null;
149
+ checks.push(pricing === null ? skip('pricing-missing', noRegistry) : resolved('pricing-missing', pricing));
150
+
151
+ const perAlias = registry ? d.auditApiPerAlias(registry) : null;
152
+ checks.push(perAlias === null
153
+ ? skip('api-per-alias', registry
154
+ ? 'o registry encontrado não declara um mapa de aliases — não há alias sobre o qual perguntar'
155
+ : noRegistry)
156
+ : resolved('api-per-alias', perAlias));
157
+
158
+ // ── prompt-source ────────────────────────────────────────────────────────
159
+ const prompts = d.auditPromptSource(targetPath);
160
+ checks.push(prompts === null
161
+ ? skip('prompt-source', 'nenhum `.cs` alcançável a partir desta raiz')
162
+ : resolved('prompt-source', prompts));
163
+
164
+ return checks;
165
+ }
166
+
167
+ /**
168
+ * Código de saída do `doctor --ai`: 1 quando há ao menos um `error`, 0 caso
169
+ * contrário. `warn` e `skip` NUNCA mudam o código de saída — só `pin-drift`
170
+ * produz `error`, e só na faixa flutuante, a única classe com prejuízo medido.
171
+ *
172
+ * @param {Array<{status: string}>} checks
173
+ * @returns {0|1}
174
+ */
175
+ export function aiExitCode(checks) {
176
+ return checks.some((c) => c.status === 'error') ? 1 : 0;
177
+ }
178
+
179
+ /** true quando NENHUMA checagem teve insumo — o relatório sai em uma linha. */
180
+ export function allSkipped(checks) {
181
+ return checks.length > 0 && checks.every((c) => c.status === 'skip');
182
+ }
@@ -0,0 +1,347 @@
1
+ /**
2
+ * ai-project-scan.js — varredura READ-ONLY dos `.csproj` / `Directory.Packages.props`
3
+ * de um projeto .NET, para responder "o que este projeto declara sobre pacotes de IA?".
4
+ *
5
+ * Três invariantes deste módulo, todas cobradas por canário:
6
+ *
7
+ * 1. **Nada aqui escreve.** Nenhum import de `fs` que mute, nenhum `spawn`.
8
+ * A auditoria não pode inventar o estado que reporta — é a lição do T10 da
9
+ * #90, onde a auditoria de portas ia ALOCAR um bloco ao auditar.
10
+ * 2. **`null` é DESCONHECIDO, `[]` é "perguntei e não achei".** Colapsar os
11
+ * dois é a doutrina que `src/lib/git/pr-index.js` já pinou neste repositório.
12
+ * Aqui: `scanAiPackageRefs` devolve `null` quando havia arquivos candidatos
13
+ * e NENHUM foi legível; devolve um resultado com `projectFiles: []` quando
14
+ * varreu e não achou `.csproj` nenhum.
15
+ * 3. **Worktree é o checkout de OUTRA feature.** `worktrees/` e `.worktrees/`
16
+ * ficam fora da varredura pela mesma razão pinada em `test-runner.js`:
17
+ * varrê-los reporta sobre código fora da árvore avaliada. Os nomes vêm de
18
+ * `src/lib/worktree/dirs.js`, nunca duplicados aqui.
19
+ */
20
+
21
+ import { readdirSync, readFileSync, statSync, existsSync } from 'node:fs';
22
+ import { join, basename } from 'node:path';
23
+ import { WORKTREE_DIR_NAMES } from '../worktree/dirs.js';
24
+
25
+ /** Diretórios que a varredura nunca desce. */
26
+ export const SCAN_IGNORED_DIRS = new Set([
27
+ 'node_modules',
28
+ 'bin',
29
+ 'obj',
30
+ '.git',
31
+ ...WORKTREE_DIR_NAMES,
32
+ ]);
33
+
34
+ /**
35
+ * Prefixos de pacote NuGet que caracterizam "pacote de IA".
36
+ *
37
+ * Deliberadamente curto: a lista existe para decidir se há assunto (condição 3
38
+ * do silêncio do `doctor --ai`), não para catalogar o ecossistema. Um pacote de
39
+ * IA fora desta lista aparece na varredura como pacote comum e, se estiver no
40
+ * pin, `pin-drift` o pega mesmo assim — o pin é a outra metade do predicado.
41
+ */
42
+ export const AI_PACKAGE_PREFIXES = [
43
+ 'Microsoft.Agents.AI',
44
+ 'Microsoft.Extensions.AI',
45
+ 'Microsoft.SemanticKernel',
46
+ 'OpenAI',
47
+ 'Azure.AI.',
48
+ 'Google.GenAI',
49
+ 'ModelContextProtocol',
50
+ 'Anthropic.',
51
+ ];
52
+
53
+ /**
54
+ * @param {string} name - nome do pacote NuGet
55
+ * @returns {boolean} true quando o nome casa com um dos prefixos de IA
56
+ */
57
+ export function isAiPackage(name) {
58
+ if (typeof name !== 'string' || name.length === 0) return false;
59
+ return AI_PACKAGE_PREFIXES.some(
60
+ (p) => name === p || name.startsWith(p.endsWith('.') ? p : `${p}.`)
61
+ );
62
+ }
63
+
64
+ /** Remove o BOM UTF-8 do início do texto (mesmo cuidado de `doctor.js` e `env.js`). */
65
+ function stripBom(text) {
66
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
67
+ }
68
+
69
+ /**
70
+ * Checagem de boa-formação de XML suficiente para MSBuild, sem dependência nova.
71
+ *
72
+ * Não valida schema: valida que as tags fecham na ordem certa e que todo `<`
73
+ * tem o seu `>`. É o predicado que separa "li o arquivo" de "não pude
74
+ * perguntar" — e é ele que faz um `.csproj` truncado virar `unreadable` em vez
75
+ * de virar "sem drift".
76
+ *
77
+ * @param {string} xml
78
+ * @returns {boolean}
79
+ */
80
+ export function isWellFormedXml(xml) {
81
+ const stack = [];
82
+ let i = 0;
83
+ while (i < xml.length) {
84
+ const lt = xml.indexOf('<', i);
85
+ if (lt === -1) break;
86
+
87
+ if (xml.startsWith('<!--', lt)) {
88
+ const end = xml.indexOf('-->', lt + 4);
89
+ if (end === -1) return false;
90
+ i = end + 3;
91
+ continue;
92
+ }
93
+ if (xml.startsWith('<![CDATA[', lt)) {
94
+ const end = xml.indexOf(']]>', lt + 9);
95
+ if (end === -1) return false;
96
+ i = end + 3;
97
+ continue;
98
+ }
99
+ if (xml.startsWith('<?', lt) || xml.startsWith('<!', lt)) {
100
+ const end = xml.indexOf('>', lt);
101
+ if (end === -1) return false;
102
+ i = end + 1;
103
+ continue;
104
+ }
105
+
106
+ const gt = xml.indexOf('>', lt);
107
+ if (gt === -1) return false;
108
+ const raw = xml.slice(lt + 1, gt).trim();
109
+ if (raw.length === 0) return false;
110
+
111
+ if (raw.startsWith('/')) {
112
+ const name = raw.slice(1).trim();
113
+ if (stack.pop() !== name) return false;
114
+ } else if (!raw.endsWith('/')) {
115
+ const name = raw.split(/[\s/>]/)[0];
116
+ if (!name) return false;
117
+ stack.push(name);
118
+ }
119
+ i = gt + 1;
120
+ }
121
+ return stack.length === 0;
122
+ }
123
+
124
+ /** Todas as tags de um nome, com os atributos crus. */
125
+ function* elements(xml, tagName) {
126
+ const re = new RegExp(`<${tagName}\\b([^>]*?)(/?)>`, 'g');
127
+ let m;
128
+ while ((m = re.exec(xml)) !== null) {
129
+ yield { attrs: m[1], selfClosing: m[2] === '/', index: m.index, end: re.lastIndex };
130
+ }
131
+ }
132
+
133
+ /** Valor de um atributo dentro do texto cru de atributos de uma tag. */
134
+ function attr(attrs, name) {
135
+ const m = new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`, 'i').exec(attrs)
136
+ || new RegExp(`\\b${name}\\s*=\\s*'([^']*)'`, 'i').exec(attrs);
137
+ return m ? m[1].trim() : null;
138
+ }
139
+
140
+ /**
141
+ * Elemento filho `<Version>x</Version>` de um `<PackageReference>` não
142
+ * auto-fechado. Recebe o XML e o índice logo após a tag de abertura.
143
+ */
144
+ function childVersion(xml, startIndex, tagName) {
145
+ const close = xml.indexOf(`</${tagName}>`, startIndex);
146
+ if (close === -1) return null;
147
+ const inner = xml.slice(startIndex, close);
148
+ const m = /<Version\s*>([^<]*)<\/Version\s*>/i.exec(inner);
149
+ return m ? m[1].trim() : null;
150
+ }
151
+
152
+ /**
153
+ * Classifica a string de versão declarada no projeto.
154
+ *
155
+ * - `fixed` — `1.20.0` ou a forma de igualdade exata `[1.20.0]`
156
+ * - `floating` — contém `*`, ou é intervalo (`[1.0,)`, `[1.0,2.0)`, `(,2.0]`)
157
+ * - `unresolved` — referência a propriedade MSBuild (`$(MafVersion)`): a
158
+ * varredura NÃO resolve propriedades, e chutar seria inventar o dado
159
+ * - `absent` — sem versão declarada e sem entrada de CPM
160
+ *
161
+ * @param {string|null} raw
162
+ * @returns {{ kind: 'fixed'|'floating'|'unresolved'|'absent', value: string|null }}
163
+ */
164
+ export function classifyVersion(raw) {
165
+ if (raw === null || raw === undefined || String(raw).trim() === '') {
166
+ return { kind: 'absent', value: null };
167
+ }
168
+ const value = String(raw).trim();
169
+ if (value.includes('$(')) return { kind: 'unresolved', value };
170
+ if (value.includes('*')) return { kind: 'floating', value };
171
+ if (/^\[\s*[^,\[\]()]+\s*\]$/.test(value)) {
172
+ return { kind: 'fixed', value: value.slice(1, -1).trim() };
173
+ }
174
+ if (/[[\](),]/.test(value)) return { kind: 'floating', value };
175
+ return { kind: 'fixed', value };
176
+ }
177
+
178
+ /**
179
+ * Coleta recursiva de arquivos que casam com um predicado, respeitando as
180
+ * exclusões de `SCAN_IGNORED_DIRS`.
181
+ *
182
+ * Exportada porque `registry-audit.js` e `code-audit.js` precisam da MESMA
183
+ * travessia com as MESMAS exclusões. Uma segunda cópia da lista de diretórios
184
+ * ignorados é como um dos três volta a varrer `worktrees/` sozinho.
185
+ *
186
+ * @param {string} root
187
+ * @param {(name: string, fullPath: string) => boolean} matches
188
+ * @returns {string[]}
189
+ */
190
+ export function collectProjectFiles(root, matches) {
191
+ const out = [];
192
+ if (!root || !existsSync(root)) return out;
193
+ collectFiles(root, matches, out);
194
+ return out;
195
+ }
196
+
197
+ /** Coleta recursiva de arquivos que casam com um predicado, respeitando as exclusões. */
198
+ function collectFiles(root, matches, out, depth = 0) {
199
+ if (depth > 24) return;
200
+ let entries;
201
+ try {
202
+ entries = readdirSync(root, { withFileTypes: true });
203
+ } catch {
204
+ return;
205
+ }
206
+ for (const entry of entries) {
207
+ const name = entry.name;
208
+ const full = join(root, name);
209
+ let isDir = entry.isDirectory();
210
+ if (entry.isSymbolicLink()) {
211
+ // Junction/symlink: pode apontar para fora da árvore (é assim que um
212
+ // worktree do Windows enxerga `.claude/`). Não descemos.
213
+ continue;
214
+ }
215
+ if (isDir) {
216
+ if (SCAN_IGNORED_DIRS.has(name)) continue;
217
+ collectFiles(full, matches, out, depth + 1);
218
+ continue;
219
+ }
220
+ if (matches(name, full)) out.push(full);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Varre `root` e devolve o que os projetos .NET declaram sobre pacotes.
226
+ *
227
+ * Sem limite de 10 arquivos (o de `scanStackDependencies`, que é do relatório
228
+ * de arquitetura e tem outro consumidor): uma solução .NET real tem dezenas de
229
+ * `.csproj`, e cortar em 10 é reportar sobre uma amostra chamando-a de todo.
230
+ *
231
+ * @param {string} root - raiz da varredura (o projeto)
232
+ * @returns {null | {
233
+ * projectFiles: string[],
234
+ * cpmFiles: string[],
235
+ * unreadable: string[],
236
+ * packages: Array<{ name: string, file: string, ai: boolean,
237
+ * version: string|null, versionKind: string,
238
+ * declared: string|null, fromCpm: boolean }>
239
+ * }} `null` = não pude perguntar (havia candidatos e nenhum era legível).
240
+ */
241
+ export function scanAiPackageRefs(root) {
242
+ if (!root || !existsSync(root)) return null;
243
+ try {
244
+ if (!statSync(root).isDirectory()) return null;
245
+ } catch {
246
+ return null;
247
+ }
248
+
249
+ const projectFiles = [];
250
+ const cpmFiles = [];
251
+ collectFiles(root, (n) => n.toLowerCase().endsWith('.csproj'), projectFiles);
252
+ collectFiles(root, (n) => n.toLowerCase() === 'directory.packages.props', cpmFiles);
253
+
254
+ const candidates = [...cpmFiles, ...projectFiles];
255
+ if (candidates.length === 0) {
256
+ // Perguntei e não achei — NÃO é desconhecido.
257
+ return { projectFiles: [], cpmFiles: [], unreadable: [], packages: [] };
258
+ }
259
+
260
+ const unreadable = [];
261
+ const readable = new Map();
262
+ for (const file of candidates) {
263
+ let xml;
264
+ try {
265
+ xml = stripBom(readFileSync(file, 'utf8'));
266
+ } catch {
267
+ unreadable.push(file);
268
+ continue;
269
+ }
270
+ if (!isWellFormedXml(xml)) {
271
+ unreadable.push(file);
272
+ continue;
273
+ }
274
+ readable.set(file, xml);
275
+ }
276
+
277
+ if (readable.size === 0) {
278
+ // Havia o que perguntar e nenhuma resposta chegou: DESCONHECIDO.
279
+ return null;
280
+ }
281
+
282
+ // Central Package Management primeiro: um PackageReference sem Version num
283
+ // projeto com CPM não é achado de versão ausente (UC-24 A1).
284
+ const cpm = new Map();
285
+ for (const file of cpmFiles) {
286
+ const xml = readable.get(file);
287
+ if (!xml) continue;
288
+ for (const el of elements(xml, 'PackageVersion')) {
289
+ const name = attr(el.attrs, 'Include') ?? attr(el.attrs, 'Update');
290
+ if (!name) continue;
291
+ const version = attr(el.attrs, 'Version')
292
+ ?? (el.selfClosing ? null : childVersion(xml, el.end, 'PackageVersion'));
293
+ cpm.set(name, { version, file });
294
+ }
295
+ }
296
+
297
+ const packages = [];
298
+ for (const file of projectFiles) {
299
+ const xml = readable.get(file);
300
+ if (!xml) continue;
301
+ for (const el of elements(xml, 'PackageReference')) {
302
+ const name = attr(el.attrs, 'Include') ?? attr(el.attrs, 'Update');
303
+ if (!name) continue;
304
+ const inline = attr(el.attrs, 'Version')
305
+ ?? (el.selfClosing ? null : childVersion(xml, el.end, 'PackageReference'));
306
+ const central = cpm.get(name);
307
+ const fromCpm = inline === null && central !== undefined;
308
+ const declared = inline !== null ? inline : (fromCpm ? central.version : null);
309
+ const { kind, value } = classifyVersion(declared);
310
+ packages.push({
311
+ name,
312
+ file,
313
+ ai: isAiPackage(name),
314
+ version: value,
315
+ versionKind: kind,
316
+ declared,
317
+ fromCpm,
318
+ });
319
+ }
320
+ }
321
+
322
+ // Um Directory.Packages.props com PackageVersion de pacote de IA e nenhum
323
+ // .csproj que o referencie ainda declara a intenção — mas não é uso, e este
324
+ // módulo reporta declaração de uso. Fica de fora, de propósito.
325
+
326
+ return { projectFiles, cpmFiles, unreadable, packages };
327
+ }
328
+
329
+ /**
330
+ * Versão declarada de um pacote específico, ou `null` quando o projeto não a
331
+ * declara de forma resolvível. Usado pelo gate do `removed-api` (só roda com
332
+ * `Microsoft.Agents.AI` >= 1.0) e pelo relatório.
333
+ *
334
+ * @param {ReturnType<typeof scanAiPackageRefs>} scan
335
+ * @param {string} name
336
+ * @returns {{ name: string, version: string|null, versionKind: string, declared: string|null, file: string } | null}
337
+ */
338
+ export function findPackage(scan, name) {
339
+ if (!scan || !Array.isArray(scan.packages)) return null;
340
+ return scan.packages.find((p) => p.name === name) ?? null;
341
+ }
342
+
343
+ /** Nomes de arquivo (sem diretório) dos projetos varridos — para mensagens. */
344
+ export function projectFileNames(scan) {
345
+ if (!scan) return [];
346
+ return scan.projectFiles.map((f) => basename(f));
347
+ }