@polymorphism-tech/morph-spec 8.46.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CLAUDE.md +53 -44
  2. package/README.md +3 -4
  3. package/bin/morph-spec.js +38 -55
  4. package/bin/validate.js +5 -134
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -2
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +75 -18
  11. package/framework/commands/create-umbraco-lp.md +2 -0
  12. package/framework/commands/migrate-lp-to-umbraco.md +2 -0
  13. package/framework/commands/morph-apply.md +5 -28
  14. package/framework/commands/morph-archive.md +2 -2
  15. package/framework/commands/morph-hotfix.md +8 -8
  16. package/framework/commands/morph-infra.md +17 -203
  17. package/framework/commands/morph-media.md +3 -0
  18. package/framework/commands/morph-preflight.md +7 -31
  19. package/framework/commands/morph-proposal.md +2 -50
  20. package/framework/commands/morph-status.md +3 -3
  21. package/framework/commands/morph-troubleshoot.md +2 -14
  22. package/framework/docs/CLI.md +585 -286
  23. package/framework/evals/review-checklist.md +82 -41
  24. package/framework/hooks/README.md +10 -11
  25. package/framework/hooks/claude-code/core/comment-guard.js +1 -1
  26. package/framework/hooks/claude-code/core/denylist.json +33 -3
  27. package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
  28. package/framework/hooks/claude-code/core/gate-guard.js +2 -11
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
  30. package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
  31. package/framework/hooks/claude-code/core/state-sync.js +17 -19
  32. package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
  33. package/framework/hooks/claude-code/core/validate-completion.js +33 -97
  34. package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
  35. package/framework/hooks/claude-code/core/worktree-context.js +5 -4
  36. package/framework/hooks/claude-code/statusline.py +55 -41
  37. package/framework/hooks/package.json +3 -0
  38. package/framework/hooks/shared/activity-logger.js +1 -1
  39. package/framework/hooks/shared/build-check.js +10 -6
  40. package/framework/hooks/shared/command-tokens.js +169 -0
  41. package/framework/hooks/shared/comment-rules.js +1 -1
  42. package/framework/hooks/shared/comment-scanner.js +10 -23
  43. package/framework/hooks/shared/comment-stripper.js +1 -1
  44. package/framework/hooks/shared/e2e-check.js +67 -31
  45. package/framework/hooks/shared/errata.js +72 -0
  46. package/framework/hooks/shared/feature-locator.js +1 -1
  47. package/framework/hooks/shared/frontend-locations.js +6 -0
  48. package/framework/hooks/shared/gate-question.js +18 -0
  49. package/framework/hooks/shared/phase-utils.js +15 -32
  50. package/framework/hooks/shared/slice-progress.js +118 -0
  51. package/framework/hooks/shared/spawn-command.js +23 -0
  52. package/framework/hooks/shared/state-reader.js +61 -87
  53. package/framework/hooks/shared/state-writer.js +3 -3
  54. package/framework/mcp/registry.json +3 -4
  55. package/framework/phases.json +23 -39
  56. package/framework/rules/ai-agents-standards.md +18 -16
  57. package/framework/rules/asaas-standards.md +3 -1
  58. package/framework/rules/csharp-standards.md +10 -8
  59. package/framework/rules/data-standards.md +5 -5
  60. package/framework/rules/dotnet-templates.md +1 -1
  61. package/framework/rules/frontend-standards.md +4 -2
  62. package/framework/rules/gsap-standards.md +10 -5
  63. package/framework/rules/hangfire-standards.md +3 -1
  64. package/framework/rules/infrastructure-standards.md +4 -2
  65. package/framework/rules/neon-auth-standards.md +3 -1
  66. package/framework/rules/neon-standards.md +6 -4
  67. package/framework/rules/nextjs-standards.md +13 -11
  68. package/framework/rules/nextjs-templates.md +1 -1
  69. package/framework/rules/resend-standards.md +3 -1
  70. package/framework/schemas/config.schema.json +14 -9
  71. package/framework/schemas/feature.schema.json +15 -52
  72. package/framework/skills/README.md +4 -5
  73. package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
  74. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
  75. package/framework/skills/core/morph-apply/SKILL.md +150 -223
  76. package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
  77. package/framework/skills/core/morph-machine/SKILL.md +4 -4
  78. package/framework/skills/core/morph-media/SKILL.md +3 -3
  79. package/framework/skills/core/morph-plan/SKILL.md +138 -300
  80. package/framework/skills/core/morph-review/SKILL.md +112 -137
  81. package/framework/skills/core/morph-standards/SKILL.md +29 -41
  82. package/framework/skills/core/morph-uiux/SKILL.md +78 -153
  83. package/framework/standards/STANDARDS.json +90 -1219
  84. package/framework/standards/STANDARDS.meta.json +1305 -0
  85. package/framework/standards/ai-agents/evals-with-cache.md +2 -3
  86. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  87. package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
  88. package/framework/standards/ai-agents/prompt-sources.md +6 -4
  89. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  90. package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
  91. package/framework/standards/ai-agents/setup.md +2 -2
  92. package/framework/standards/ai-agents/structured-output.md +1 -1
  93. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  94. package/framework/standards/architecture/code-comments.md +8 -8
  95. package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
  96. package/framework/standards/backend/api/validation.md +135 -47
  97. package/framework/standards/backend/database/postgresql/database.md +11 -7
  98. package/framework/standards/backend/database/vector-search-rag.md +207 -324
  99. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
  100. package/framework/standards/backend/dotnet/result-pattern.md +40 -6
  101. package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
  102. package/framework/standards/backend/dotnet/scrutor.md +1 -1
  103. package/framework/standards/backend/dotnet/testing.md +1 -1
  104. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
  105. package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
  106. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
  107. package/framework/standards/data/nosql/cache/redis.md +120 -44
  108. package/framework/standards/data/nosql/cosmos-db.md +114 -71
  109. package/framework/standards/frontend/design-system/animations.md +1 -1
  110. package/framework/standards/frontend/design-system/naming.md +1 -1
  111. package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
  112. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
  113. package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
  114. package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
  115. package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
  116. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  117. package/framework/templates/README.md +2 -3
  118. package/framework/templates/REGISTRY.json +6 -170
  119. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  120. package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
  121. package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
  122. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
  123. package/framework/templates/docs/plan.md +72 -0
  124. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  125. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
  126. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  127. package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
  128. package/framework/templates/infrastructure/github/README.md +2 -1
  129. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
  130. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  131. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  132. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  133. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  134. package/package.json +1 -1
  135. package/src/commands/e2e/e2e.js +2 -2
  136. package/src/commands/observability/cost.js +18 -60
  137. package/src/commands/observability/telemetry.js +3 -5
  138. package/src/commands/project/doctor-procs.js +1 -1
  139. package/src/commands/project/doctor.js +9 -9
  140. package/src/commands/project/fleet.js +4 -4
  141. package/src/commands/project/heavy.js +3 -3
  142. package/src/commands/project/init.js +6 -1
  143. package/src/commands/project/setup-infra-cmd.js +2 -1
  144. package/src/commands/project/stack-scan.js +58 -0
  145. package/src/commands/project/standards.js +79 -25
  146. package/src/commands/project/status.js +29 -69
  147. package/src/commands/project/update.js +33 -19
  148. package/src/commands/project/worktree.js +1 -1
  149. package/src/commands/state/advance.js +62 -46
  150. package/src/commands/state/approve.js +95 -138
  151. package/src/commands/state/archive.js +20 -84
  152. package/src/commands/state/auto-approval.js +68 -0
  153. package/src/commands/state/create.js +28 -1
  154. package/src/commands/state/delete.js +1 -1
  155. package/src/commands/state/finish.js +11 -23
  156. package/src/commands/state/gate-check.js +86 -95
  157. package/src/commands/state/gate-decision.js +6 -4
  158. package/src/commands/state/handoff.js +1 -1
  159. package/src/commands/state/list.js +9 -9
  160. package/src/commands/state/report-check.js +157 -311
  161. package/src/commands/state/retype.js +9 -3
  162. package/src/commands/validation/mutate.js +77 -14
  163. package/src/commands/validation/plan-check.js +81 -0
  164. package/src/commands/validation/validate-feature.js +60 -135
  165. package/src/commands/validation/verify.js +41 -82
  166. package/src/core/paths/output-schema.js +20 -67
  167. package/src/core/state/gate-order.js +5 -6
  168. package/src/core/state/lifecycle-audit.js +2 -2
  169. package/src/core/state/state-manager.js +17 -98
  170. package/src/core/templates/template-registry.js +2 -2
  171. package/src/core/templates/template-seed.js +22 -0
  172. package/src/core/work-types.js +10 -10
  173. package/src/dashboard/app/App.jsx +0 -2
  174. package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
  175. package/src/dashboard/app/components/CommandPalette.jsx +7 -8
  176. package/src/dashboard/app/components/EntityLink.jsx +1 -2
  177. package/src/dashboard/app/components/Markdown.jsx +1 -1
  178. package/src/dashboard/app/components/Sidebar.jsx +1 -2
  179. package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
  180. package/src/dashboard/app/lib/phases.js +2 -4
  181. package/src/dashboard/app/pages/Activity.jsx +1 -1
  182. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
  183. package/src/dashboard/app/pages/Features.jsx +9 -9
  184. package/src/dashboard/app/pages/Guide.jsx +2 -2
  185. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
  186. package/src/dashboard/app/pages/RulesMap.jsx +3 -3
  187. package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
  188. package/src/dashboard/app/pages/Standards.jsx +1 -1
  189. package/src/dashboard/scanner/activity-scanner.js +6 -16
  190. package/src/dashboard/scanner/agents-scanner.js +1 -1
  191. package/src/dashboard/scanner/docs-scanner.js +5 -23
  192. package/src/dashboard/scanner/features-scanner.js +95 -60
  193. package/src/dashboard/scanner/index.js +0 -2
  194. package/src/dashboard/scanner/project-api.js +1 -1
  195. package/src/dashboard/scanner/rules-scanner.js +15 -10
  196. package/src/dashboard/scanner/skills-scanner.js +2 -3
  197. package/src/dashboard/scanner/standards-scanner.js +2 -1
  198. package/src/lib/ci/ci-verify.js +17 -1
  199. package/src/lib/ci/github-ci.js +1 -1
  200. package/src/lib/detectors/index.js +3 -8
  201. package/src/lib/generators/handoff-generator.js +34 -65
  202. package/src/lib/generators/settings-generator.js +11 -89
  203. package/src/lib/git/evidence-staleness.js +1 -1
  204. package/src/lib/git/tree-fingerprint.js +19 -7
  205. package/src/lib/git/tree-state.js +2 -2
  206. package/src/lib/heavy/cleanup.js +1 -5
  207. package/src/lib/installers/memory-installer.js +6 -8
  208. package/src/lib/mutation/commands.js +3 -2
  209. package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
  210. package/src/lib/mutation/plan.js +14 -6
  211. package/src/lib/mutation/runner.js +1 -1
  212. package/src/lib/mutation/subset.js +46 -9
  213. package/src/lib/plan/artifact-vocabulary.js +60 -0
  214. package/src/lib/plan/checks.js +234 -0
  215. package/src/lib/plan/plan-parser.js +209 -0
  216. package/src/lib/plan/plan-template.js +18 -0
  217. package/src/lib/review-reports.js +222 -0
  218. package/src/lib/risk-detector.js +134 -135
  219. package/src/lib/specs/adr-index.js +50 -69
  220. package/src/lib/specs/design-seed.js +18 -0
  221. package/src/lib/specs/frontmatter.js +44 -0
  222. package/src/lib/specs/gfm-table.js +17 -0
  223. package/src/lib/specs/old-format.js +68 -0
  224. package/src/lib/specs/revocations.js +37 -95
  225. package/src/lib/specs/screen-checklist.js +18 -17
  226. package/src/lib/stack/dotnet-build-ignores.js +47 -0
  227. package/src/lib/stack/stack-profile.js +1 -1
  228. package/src/lib/stack/stack-scan.js +267 -0
  229. package/src/lib/standards/registry.js +43 -0
  230. package/src/lib/standards/sections.js +58 -0
  231. package/src/lib/tasks/eval-node.js +4 -12
  232. package/src/lib/tasks/test-nodes.js +1 -74
  233. package/src/lib/tasks/test-runner.js +75 -117
  234. package/src/lib/telemetry/metrics.js +8 -69
  235. package/src/lib/telemetry/telemetry-reader.js +5 -118
  236. package/src/lib/telemetry/verify-event.js +5 -7
  237. package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
  238. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
  239. package/src/lib/validators/reuse/reuse-validator.js +24 -48
  240. package/src/lib/validators/reuse/symbol-scanner.js +2 -4
  241. package/src/lib/validators/traceability-check.js +85 -129
  242. package/src/lib/validators/validation-runner.js +8 -88
  243. package/src/lib/validators/verify-runner.js +134 -301
  244. package/src/lib/worktree/docker-audit.js +1 -2
  245. package/src/lib/worktree/fleet-scanner.js +25 -38
  246. package/src/scripts/setup-infra.js +42 -22
  247. package/src/utils/agents-installer.js +92 -13
  248. package/src/utils/claude-settings-manager.js +0 -35
  249. package/src/utils/file-copier.js +34 -6
  250. package/src/utils/hooks-installer.js +16 -9
  251. package/src/utils/skills-installer.js +1 -2
  252. package/framework/commands/commit.md +0 -166
  253. package/framework/evals/trace-schema.md +0 -67
  254. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  255. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  256. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  257. package/framework/hooks/shared/tasks-validator.js +0 -111
  258. package/framework/rubrics/backend.json +0 -41
  259. package/framework/rubrics/default.json +0 -36
  260. package/framework/rubrics/frontend.json +0 -36
  261. package/framework/rules/morph-task-tracking.md +0 -54
  262. package/framework/rules/morph-workflow.md +0 -244
  263. package/framework/schemas/tasks.schema.json +0 -79
  264. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  265. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  266. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  267. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  268. package/framework/templates/docs/proposal.md +0 -192
  269. package/framework/templates/docs/spec.md +0 -57
  270. package/framework/templates/docs/ui-components.md +0 -52
  271. package/framework/templates/docs/ui-design-system.md +0 -76
  272. package/framework/templates/docs/ui-flows.md +0 -167
  273. package/framework/templates/examples/spec-examples.md +0 -91
  274. package/framework/templates/feature/decisions.md +0 -124
  275. package/framework/templates/feature/recap.md +0 -145
  276. package/framework/templates/project/validate.js +0 -97
  277. package/framework/templates/tasks.template.json +0 -17
  278. package/src/commands/state/usecase.js +0 -143
  279. package/src/commands/tasks/dag.js +0 -181
  280. package/src/dashboard/app/pages/Rubrics.jsx +0 -97
  281. package/src/dashboard/scanner/rubrics-scanner.js +0 -12
  282. package/src/lib/agents/output-collector.js +0 -76
  283. package/src/lib/evaluator-report.js +0 -91
  284. package/src/lib/specs/acervo-format.js +0 -70
  285. package/src/lib/specs/acervo-lifecycle.js +0 -264
  286. package/src/lib/specs/done-criteria-check.js +0 -200
  287. package/src/lib/specs/usecase-check.js +0 -301
  288. package/src/lib/specs/usecase-promoter.js +0 -301
  289. package/src/lib/specs/usecase-registry.js +0 -237
  290. package/src/lib/tasks/dispatch-planner.js +0 -319
  291. package/src/lib/tasks/mermaid-renderer.js +0 -132
  292. package/src/lib/tasks/task-classifier.js +0 -30
  293. package/src/lib/tasks/task-parser.js +0 -209
  294. package/src/lib/tasks/tasks-json-parser.js +0 -296
  295. package/src/lib/tasks/test-scope.js +0 -597
  296. package/src/lib/validators/architecture/architecture-validator.js +0 -270
  297. package/src/lib/validators/architecture/index.js +0 -7
  298. package/src/lib/validators/css/css-validator.js +0 -340
  299. package/src/lib/validators/css/index.js +0 -7
  300. package/src/lib/validators/design-system/design-system-validator.js +0 -237
  301. package/src/lib/validators/design-system/index.js +0 -7
  302. package/src/lib/validators/score-aggregator.js +0 -244
  303. package/src/lib/validators/ui/index.js +0 -7
  304. package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
@@ -7,67 +7,43 @@
7
7
  * fresh instance starts focused instead of inheriting a wall of stale context.
8
8
  *
9
9
  * Deterministic by construction — everything here is derived from feature.json,
10
- * tasks.json, decisions.md and the worktree env manifest. The document is
10
+ * the extracted plan checks, the slice reports and the worktree env manifest. The document is
11
11
  * therefore REGENERABLE (never hand-edited, never merged): `morph-spec handoff`
12
12
  * rebuilds it, and setup/create/advance/approve regenerate it at the moments a
13
13
  * new session is most likely to pick the feature up.
14
14
  *
15
- * Every section is fail-open: a missing tasks.json or a corrupt decisions.md
16
- * degrades that section, never the document.
15
+ * Every section is fail-open: missing or corrupt plan checks degrade that
16
+ * section, never the document.
17
17
  */
18
18
 
19
19
  import { existsSync, readFileSync, writeFileSync } from 'fs';
20
20
  import { join } from 'path';
21
21
  import { ensureCommonExclude } from '../../../framework/hooks/shared/git-exclude.js';
22
22
  import { derivePhaseForFeature, getPendingGates } from '../../../framework/hooks/shared/state-reader.js';
23
-
24
- const PHASE_DIRS = {
25
- proposal: '0-proposal',
26
- uiux: '1-design',
27
- plan: '2-plan',
28
- implement: '3-implement',
29
- review: '4-review',
30
- };
23
+ import { sliceProgress, readySlices } from '../../../framework/hooks/shared/slice-progress.js';
31
24
 
32
25
  /** Ordered pointer candidates — only existing paths make it into the doc. */
33
26
  const POINTER_CANDIDATES = [
34
- ['0-proposal/proposal.md', 'proposta: problema, escopo e riscos'],
35
- // Behaviour before solution — the same order morph-plan writes them in.
36
- ['2-plan/usecases', 'use cases: comportamento e pós-condições testáveis (pasta)'],
37
- ['2-plan/spec.md', 'especificação técnica'],
38
- ['2-plan/tasks.json', 'DAG de tasks (status vivo)'],
39
- ['decisions.md', 'decisões registradas'],
40
- ['3-implement/recap.md', 'recap da implementação'],
41
- ['4-review/review-report.md', 'relatório do Gate 3'],
27
+ ['2-plan/plan.md', 'plano: problema, cenários com prova, decisões e fatias'],
28
+ ['2-plan/checks.json', 'cenários e fatias extraídos pelo plan-check'],
29
+ ['1-design/design-system.md', 'design system da feature'],
30
+ ['3-implement/reports', 'relatórios das fatias (**Arquivos:**/**Standards:**): o registro da implementação'],
31
+ ['4-review/verifier.md', 'verificador do Gate 3: cada cenário provado'],
32
+ ['4-review/reviewer.md', 'revisor do Gate 3: escopo, arquitetura, regressão'],
33
+ ['4-review/security.md', 'segurança do Gate 3 (só com caminho sensível)'],
42
34
  ];
43
35
 
44
36
  function readJson(path) {
45
37
  return JSON.parse(readFileSync(path, 'utf8').replace(/^/, ''));
46
38
  }
47
39
 
48
- function taskSummary(featureDir) {
49
- try {
50
- const tasksPath = join(featureDir, '2-plan', 'tasks.json');
51
- if (!existsSync(tasksPath)) return null;
52
- const data = readJson(tasksPath);
53
- if (!Array.isArray(data.tasks)) return null;
54
- const done = data.tasks.filter((t) => t.status === 'done').length;
55
- const doneIds = new Set(data.tasks.filter((t) => t.status === 'done').map((t) => t.id));
56
- const ready = data.tasks.filter(
57
- (t) => t.status === 'pending' && Array.isArray(t.dependencies) && t.dependencies.every((d) => doneIds.has(d)),
58
- );
59
- return { done, total: data.tasks.length, ready };
60
- } catch {
61
- return null;
62
- }
63
- }
64
-
65
40
  function decisionTitles(featureDir, limit = 5) {
66
41
  try {
67
- const path = join(featureDir, 'decisions.md');
42
+ const path = join(featureDir, '2-plan', 'checks.json');
68
43
  if (!existsSync(path)) return [];
69
- const titles = [...readFileSync(path, 'utf8').matchAll(/^##\s+(.+)$/gm)].map((m) => m[1].trim());
70
- return titles.slice(-limit);
44
+ const adrs = readJson(path).adrs;
45
+ if (!Array.isArray(adrs)) return [];
46
+ return adrs.map((a) => `${a.id} — ${a.title}`).slice(-limit);
71
47
  } catch {
72
48
  return [];
73
49
  }
@@ -86,34 +62,27 @@ function readWorktreeEnv(cwd) {
86
62
  }
87
63
  }
88
64
 
89
- function nextAction(feature, phase, tasks, featureName) {
65
+ function nextAction(feature, phase, slices, featureName) {
90
66
  const gates = feature.approvals || feature.approvalGates || {};
91
67
  const approved = (g) => gates[g]?.approved === true;
92
68
 
93
69
  switch (phase) {
94
- case 'proposal':
95
- // An approved Gate 1 with no plan artifact yet means "plan work is open":
96
- // the derived phase only flips when 2-plan/spec.md lands (derivePhase is
97
- // artifact-first). Pointing back at the proposal here would be wrong.
98
- return approved('proposal')
99
- ? `Gate 1 aprovado — produza \`2-plan/spec.md\` + \`tasks.json\` (a fase avança com o primeiro artefato; Gate 2 em seguida).`
100
- : `Complete \`0-proposal/proposal.md\` e aprove o Gate 1 (\`morph-spec advance ${featureName}\`). Skill: /morph-proposal ${featureName}.`;
70
+ case 'plan':
71
+ return `Escreva \`2-plan/plan.md\`, rode \`morph-spec plan-check ${featureName}\` até zero erro e aprove o gate do plano (\`morph-spec advance ${featureName}\`). Skill: /morph-proposal ${featureName}.`;
101
72
  case 'uiux':
102
73
  return approved('uiux')
103
- ? `Gate de uiux aprovado — produza \`2-plan/spec.md\` + \`tasks.json\` (Gate 2 em seguida).`
74
+ ? `Gate de uiux aprovado — implemente as fatias do plano via /morph-apply ${featureName}.`
104
75
  : `Complete \`1-design/design-system.md\` e aprove o gate de uiux (\`morph-spec advance ${featureName}\`).`;
105
- case 'plan':
106
- return `Produza \`2-plan/spec.md\` + \`tasks.json\` e aprove o Gate 2 (\`morph-spec advance ${featureName}\`).`;
107
76
  case 'implement': {
108
- const ready = tasks?.ready?.map((t) => `${t.id} "${t.title}"`).join(', ');
77
+ const ready = readySlices(slices).map((s) => `${s.id} "${s.title}"`).join(', ');
109
78
  return ready
110
- ? `Execute as tasks ready — ${ready} — via /morph-apply ${featureName} (task fecha com verify verde e critérios provados).`
111
- : `Todas as tasks concluídas ou bloqueadas — rode \`morph-spec advance ${featureName}\` para escancarar a review, ou destrave dependências. Skill: /morph-apply ${featureName}.`;
79
+ ? `Implemente as fatias prontas — ${ready} — via /morph-apply ${featureName} (cada fatia grava 3-implement/reports/{id}.md).`
80
+ : `Todas as fatias têm relatório final — rode \`morph-spec verify ${featureName}\` e siga para a review. Skill: /morph-apply ${featureName}.`;
112
81
  }
113
82
  case 'review':
114
83
  return approved('review')
115
84
  ? `Gate 3 aprovado — feche: \`morph-spec finish ${featureName} --pr\` (abre PR) ou \`--merge\` (merge local na default). Arquiva + integra num passo.`
116
- : `Produza \`4-review/review-report.md\` (avaliador independente) e aprove o Gate 3; depois \`morph-spec finish ${featureName} --pr|--merge\`.`;
85
+ : `Rode /morph-review ${featureName} (verificador ∥ revisor, segurança se exigida) e aprove o Gate 3; depois \`morph-spec finish ${featureName} --pr|--merge\`.`;
117
86
  default:
118
87
  return `Rode \`morph-spec status ${featureName}\` para orientar o próximo passo.`;
119
88
  }
@@ -131,7 +100,7 @@ function nextAction(feature, phase, tasks, featureName) {
131
100
  * copy at birth (create.js) — a handoff built FROM the worktree's own copy
132
101
  * would otherwise always say "no worktree registered", even while sitting
133
102
  * inside one. Pass `undefined` (the default) to fall back to `feature.worktree`.
134
- * @returns {{ markdown: string, meta: { generatedAt: string, phase: string, tasksDone: number, tasksTotal: number } } | null}
103
+ * @returns {{ markdown: string, meta: { generatedAt: string, phase: string, slicesDone: number, slicesTotal: number } } | null}
135
104
  */
136
105
  export function buildHandoff(featureName, cwd = process.cwd(), opts = {}) {
137
106
  const featureDir = join(cwd, '.morph', 'features', featureName);
@@ -144,15 +113,15 @@ export function buildHandoff(featureName, cwd = process.cwd(), opts = {}) {
144
113
  return null;
145
114
  }
146
115
 
147
- let phase = 'proposal';
116
+ let phase = 'plan';
148
117
  let pendingGates = [];
149
118
  try {
150
- phase = derivePhaseForFeature(featureName, cwd) || 'proposal';
119
+ phase = derivePhaseForFeature(featureName, cwd) || 'plan';
151
120
  pendingGates = getPendingGates(featureName, cwd) || [];
152
- } catch { /* fail-open — proposal defaults keep the doc useful */ }
121
+ } catch { /* fail-open — plan defaults keep the doc useful */ }
153
122
 
154
123
  const generatedAt = new Date().toISOString();
155
- const tasks = taskSummary(featureDir);
124
+ const slices = sliceProgress(featureDir);
156
125
  const env = readWorktreeEnv(cwd);
157
126
  const decisions = decisionTitles(featureDir);
158
127
  const worktree = opts.worktree !== undefined ? opts.worktree : (feature.worktree || null);
@@ -187,9 +156,9 @@ export function buildHandoff(featureName, cwd = process.cwd(), opts = {}) {
187
156
  '## Estado',
188
157
  '',
189
158
  `- Gates: ${gateLine}`,
190
- tasks
191
- ? `- Tasks: ${tasks.done}/${tasks.total} done · ready agora: ${tasks.ready.length ? tasks.ready.map((t) => `**${t.id}** "${t.title}"`).join(', ') : 'nenhuma'}`
192
- : '- Tasks: sem tasks.json ainda (fase de planejamento não concluída)',
159
+ slices
160
+ ? `- Fatias: ${slices.done}/${slices.total} feitas${slices.partial ? ` · ${slices.partial} parcial(is)` : ''} · prontas agora: ${readySlices(slices).map((s) => `**${s.id}** "${s.title}"`).join(', ') || 'nenhuma'}`
161
+ : '- Fatias: sem checks.json ainda (rode plan-check)',
193
162
  pendingGates.length ? `- Gates pendentes: ${pendingGates.join(', ')}` : null,
194
163
  '',
195
164
  '## Leia nesta ordem (ponteiros — nada copiado)',
@@ -200,7 +169,7 @@ export function buildHandoff(featureName, cwd = process.cwd(), opts = {}) {
200
169
  '',
201
170
  '## Próxima ação',
202
171
  '',
203
- nextAction(feature, phase, tasks, featureName),
172
+ nextAction(feature, phase, slices, featureName),
204
173
  '',
205
174
  '## Retomar',
206
175
  '',
@@ -214,8 +183,8 @@ export function buildHandoff(featureName, cwd = process.cwd(), opts = {}) {
214
183
  meta: {
215
184
  generatedAt,
216
185
  phase,
217
- tasksDone: tasks?.done ?? 0,
218
- tasksTotal: tasks?.total ?? 0,
186
+ slicesDone: slices?.done ?? 0,
187
+ slicesTotal: slices?.total ?? 0,
219
188
  },
220
189
  };
221
190
  }
@@ -23,25 +23,12 @@ export async function generateSettings(targetPath, detectedConfig, morphConfig =
23
23
  const claudeDir = join(targetPath, '.claude');
24
24
  const settingsPath = join(claudeDir, 'settings.local.json');
25
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
26
+ const existing = withoutLegacyPermissions(await readSettingsSafe(settingsPath));
34
27
  const generated = {};
35
28
 
36
- if (permissions.length > 0) {
37
- generated.permissions = permissions;
38
- }
39
-
40
- // Track what we manage
41
29
  const managed = {
42
- permissions_added: permissions,
43
30
  mcps_detected: detectedConfig.mcpServers.map(s => s.name),
44
- plugins_detected: detectedConfig.plugins.map(p => p.name),
31
+ plugins_detected: detectedConfig.plugins.map(p => (typeof p === 'string' ? p : p?.name)),
45
32
  generated_at: new Date().toISOString()
46
33
  };
47
34
 
@@ -58,44 +45,10 @@ export async function generateSettings(targetPath, detectedConfig, morphConfig =
58
45
 
59
46
  return {
60
47
  settingsPath,
61
- permissionsAdded: permissions.length,
62
48
  managed
63
49
  };
64
50
  }
65
51
 
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
52
  /**
100
53
  * Deep merge settings without overwriting user entries
101
54
  * @param {Object} existing - Current settings.local.json
@@ -127,46 +80,6 @@ export function mergeSettings(existing, generated) {
127
80
  return result;
128
81
  }
129
82
 
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
83
  /**
171
84
  * Save integrations.json with detected config
172
85
  * @param {string} targetPath - Project root directory
@@ -198,6 +111,15 @@ export async function saveIntegrations(targetPath, detectedConfig) {
198
111
  // Helpers
199
112
  // ============================================================================
200
113
 
114
+ // Claude Code reads only `permissions: { allow, deny }`; a bare array (older generator) is ignored.
115
+ // Versões antigas gravavam regras curinga (node:*, npm:*, npx:*…) como array, forma que o
116
+ // Claude Code ignora: nunca valeram. Converter para { allow } as ligaria em silêncio.
117
+ function withoutLegacyPermissions(settings) {
118
+ if (!Array.isArray(settings.permissions)) return settings;
119
+ const { permissions, ...rest } = settings;
120
+ return rest;
121
+ }
122
+
201
123
  async function readSettingsSafe(settingsPath) {
202
124
  try {
203
125
  if (!existsSync(settingsPath)) return {};
@@ -7,7 +7,7 @@ const CONFIG_PATH = '.morph/config/config.json';
7
7
 
8
8
  const gitOut = (cwd, args) => execFileSync('git', args, { cwd, stdio: 'pipe', maxBuffer: 64 * 1024 * 1024 }).toString();
9
9
 
10
- function withoutBaseline(text) {
10
+ export function withoutBaseline(text) {
11
11
  const config = JSON.parse(text);
12
12
  if (Array.isArray(config?.project?.tests)) {
13
13
  for (const node of config.project.tests) delete node?.expectedCount;
@@ -1,15 +1,15 @@
1
1
  /**
2
- * A hash of what a verify run reads: the commit, the uncommitted changes and
3
- * the untracked files, plus the project's `config.json` (it names the test
4
- * commands). Everything else under `.morph/` stays out — verify itself writes
5
- * feature.json and the logs on every run, and counting that would make two
6
- * runs over the same code look different.
2
+ * A hash of what a verify run reads: the committed tree, the uncommitted changes and the
3
+ * untracked files, plus `config.json` without `expectedCount` (it names the test commands).
4
+ * The rest of `.morph/` stays out, as in `treeMovedSince`: committing verify's own
5
+ * bookkeeping must not make the same code look different.
7
6
  */
8
7
 
9
8
  import { execFileSync } from 'child_process';
10
9
  import { createHash } from 'crypto';
11
10
  import { existsSync, readFileSync, statSync } from 'fs';
12
11
  import { join } from 'path';
12
+ import { withoutBaseline } from './evidence-staleness.js';
13
13
 
14
14
  /** Pathspec for "the project minus its `.morph/` bookkeeping". */
15
15
  export const OUTSIDE_MORPH = ['--', '.', ':(exclude).morph'];
@@ -31,7 +31,11 @@ export function treeFingerprint(projectPath) {
31
31
  const hash = createHash('sha256');
32
32
  let head = null;
33
33
  try { head = git(projectPath, ['rev-parse', 'HEAD']).toString().trim(); } catch { /* no commit yet */ }
34
- hash.update(`head\0${head ?? 'no-commit'}\0`);
34
+ // Tree ids are content-addressed: the top-level entries minus .morph change only with code.
35
+ const tree = head
36
+ ? git(projectPath, ['ls-tree', '-z', 'HEAD']).toString().split('\0').filter((e) => e && e.split('\t')[1] !== '.morph')
37
+ : ['no-commit'];
38
+ hash.update(`tree\0${tree.join('\0')}\0`);
35
39
  // Before the first commit there is no HEAD to diff against: every file is
36
40
  // untracked, and the listing below covers them.
37
41
  if (head) hash.update(git(projectPath, ['diff', 'HEAD', '--binary', ...OUTSIDE_MORPH]));
@@ -50,9 +54,17 @@ export function treeFingerprint(projectPath) {
50
54
  }
51
55
  const config = join(projectPath, '.morph', 'config', 'config.json');
52
56
  hash.update('\0config\0');
53
- if (existsSync(config)) hash.update(readFileSync(config));
57
+ if (existsSync(config)) hash.update(normalizedConfig(readFileSync(config, 'utf8')));
54
58
  return hash.digest('hex');
55
59
  } catch {
56
60
  return null;
57
61
  }
58
62
  }
63
+
64
+ function normalizedConfig(text) {
65
+ try {
66
+ return JSON.stringify(withoutBaseline(text));
67
+ } catch {
68
+ return text;
69
+ }
70
+ }
@@ -11,7 +11,7 @@ import { OUTSIDE_MORPH, treeFingerprint, git as gitBuffer } from './tree-fingerp
11
11
  import { detectDefaultBranch } from './default-branch.js';
12
12
  import { resolveBaseRef } from './branch-classifier.js';
13
13
 
14
- /** What an evaluator writes next to a mutation it has not reverted yet. */
14
+ /** What the verifier writes next to a mutation it has not reverted yet. */
15
15
  export const MUTATION_MARKER = 'MUTACAO';
16
16
  const MAX_LISTED = 50;
17
17
  const MAX_COMMITS = 10;
@@ -50,7 +50,7 @@ function commitBlobs(cwd, commit, paths) {
50
50
 
51
51
  /**
52
52
  * What the tree is before a dispatch whose work must leave it unchanged (the
53
- * evaluator mutates and reverts): HEAD plus the blob of every file that
53
+ * verifier mutates and reverts): HEAD plus the blob of every file that
54
54
  * differs from it. Kept on disk, so a session that resumes after the agent
55
55
  * died compares against the tree before the dispatch, not its own start.
56
56
  * @returns {{ at: string, head: string|null, files: Object<string, string|null> }|null}
@@ -6,7 +6,6 @@ import { heavyDir, runExclusive } from '../../../framework/hooks/shared/heavy-sl
6
6
 
7
7
  const DOTNET_ACTIVE = /\bdotnet(?:\.exe)?["']?\s+(?:build|test|publish|pack|msbuild|restore|run|watch|vstest)\b/i;
8
8
  const IDLE_WORKER = /\/nodemode:\d|vbcscompiler|razorserver/i;
9
- const SHUTDOWN_SCOPES = new Set(['feature', 'e2e-down', 'prune']);
10
9
  const SHUTDOWN_TIMEOUT_MS = 30_000;
11
10
  const TAIL_CHARS = 400;
12
11
 
@@ -33,16 +32,13 @@ export function dotnetBusy(procs) {
33
32
  });
34
33
  }
35
34
 
36
- export function afterHeavyRun({ projectPath, config, scope, env = process.env, deps = {} }) {
35
+ export function afterHeavyRun({ projectPath, config, env = process.env, deps = {} }) {
37
36
  const {
38
37
  exec = defaultProcExec, list = listProcesses, dotnet = hasDotnet, exclusive = runExclusive,
39
38
  } = deps;
40
39
 
41
40
  if (!config.enabled) return { status: 'skipped', reason: 'semáforo desligado: sem ele nada prova que outra sessão não está compilando' };
42
41
  if (!config.shutdownBuildServer) return { status: 'skipped', reason: 'desligado em heavy.shutdownBuildServer' };
43
- if (!SHUTDOWN_SCOPES.has(scope)) {
44
- return { status: 'skipped', reason: 'escopo de task: o servidor de compilação segue quente para a próxima task' };
45
- }
46
42
  if (!dotnet(projectPath)) return { status: 'skipped', reason: 'projeto sem .NET' };
47
43
 
48
44
  const guarded = exclusive({
@@ -41,8 +41,7 @@ metadata:
41
41
  - \`.morph/framework/MORPH.md\` — NLH charter (read once per session)
42
42
  - \`.morph/framework/agents.json\` — 8 agents: 7 domain personas + 1 stage persona (\`scout\`, read-only)
43
43
  - \`.morph/framework/standards/STANDARDS.json\` — standards index (load on demand)
44
- - \`.morph/framework/schemas/tasks.schema.json\` — JSON Schema for tasks.json
45
- - \`.morph/features/{feature}/\` — feature artifacts: 0-proposal / 1-design / 2-plan / 3-implement / 4-review
44
+ - \`.morph/features/{feature}/\` — feature artifacts: 2-plan / 1-design / 3-implement / 4-review
46
45
  - \`.morph/context/\` — long-lived architectural context (design-system, decisions)
47
46
  - \`.morph/state.json\` — feature state (managed by state-sync hook; Edit fallback allowed)
48
47
 
@@ -50,8 +49,8 @@ metadata:
50
49
 
51
50
  | Command | Skill invoked |
52
51
  |---------|---------------|
53
- | \`/morph-proposal {feature}\` | \`Skill(morph-proposal)\` — brainstorming → Gate 1 → plan → Gate 2 |
54
- | \`/morph-apply {feature}\` | \`Skill(morph-apply)\` — DAG-adaptive implement loop → review → Gate 3 |
52
+ | \`/morph-proposal {feature}\` | \`Skill(morph-plan)\` — entrevista → plan.md → plan-check → gate do plano |
53
+ | \`/morph-apply {feature}\` | \`Skill(morph-apply)\` — fatias do plano → verify → review → Gate 3 |
55
54
  | \`/morph-status [feature]\` | inspect state.json |
56
55
  | \`/morph-archive {feature}\` | move completed feature to .morph/archive/ |
57
56
  | \`/morph-preflight\` | pre-deploy validation |
@@ -59,13 +58,13 @@ metadata:
59
58
 
60
59
  ## Core skills
61
60
 
62
- \`morph-proposal\`, \`morph-apply\`, \`morph-brainstorming\`, \`morph-plan\`, \`morph-implement\`, \`morph-review\`, \`morph-stack-scan\`, \`morph-standards\`
61
+ \`morph-plan\`, \`morph-apply\`, \`morph-uiux\`, \`morph-review\`, \`morph-hotfix\`, \`morph-standards\`
63
62
 
64
63
  **Why:** This project is managed by morph-spec — spec-driven autonomous development with trust-aware gates.
65
64
 
66
65
  **How to apply:**
67
66
  - When user mentions a feature, check \`.morph/features/\` first.
68
- - When user types \`/morph-proposal\`, invoke \`Skill(morph-proposal)\` directly.
67
+ - When user types \`/morph-proposal\`, invoke \`Skill(morph-plan)\` directly.
69
68
  - In \`trust: "auto"\`, gates auto-pass; only AskUserQuestion on risk signals.
70
69
  `;
71
70
  }
@@ -83,10 +82,9 @@ metadata:
83
82
  - \`.morph/context/design-system.md\` — design tokens and components (source of truth)
84
83
  - \`.morph/framework/MORPH.md\` — NLH charter, Trust Mode rules, gates, skills table
85
84
  - \`.morph/framework/standards/STANDARDS.json\` — index; load specific standard via \`path\` field
86
- - \`.morph/framework/schemas/tasks.schema.json\` — tasks.json schema reference
87
85
  - \`.morph/framework/phases.json\` — phase folder mapping and skill associations
88
86
  - \`.morph/framework/agents.json\` — domain persona definitions (dotnet-senior, ef-modeler, etc.)
89
- - \`.morph/framework/evals/review-checklist.md\` — what the Gate 3 evaluator checks (verdict SHIP/FIX + evidence-typed blockers)
87
+ - \`.morph/framework/evals/review-checklist.md\` — what the Gate 3 verifier, reviewer and security check (verdict SHIP/FIX + evidence-typed blockers)
90
88
  `;
91
89
 
92
90
  /**
@@ -1,4 +1,5 @@
1
- import { dotnetBuildCommandFor, resolveTestBuildTargets } from '../tasks/test-runner.js';
1
+ import { resolve } from 'node:path';
2
+ import { discoverDotnetBuildTargets, dotnetBuildCommandFor } from '../tasks/test-runner.js';
2
3
 
3
4
  const FILTER = /--filter(?:[ =:])(?:"([^"]*)"|'([^']*)'|(\S+))/;
4
5
  const quote = (s) => `"${String(s).replace(/"/g, '\\"')}"`;
@@ -32,7 +33,7 @@ const VERBOSITY = /\s(?:-v|--verbosity)[ =:]?\S+/g;
32
33
  export function dotnetBuildCommands(node, projectPath) {
33
34
  const configuration = CONFIGURATION.exec(node.command ?? '');
34
35
  const flag = configuration ? ` -c ${configuration[1] ?? configuration[2]}` : '';
35
- return resolveTestBuildTargets(node)
36
+ return discoverDotnetBuildTargets(node, resolve(projectPath || '.', node.cwd || '.'))
36
37
  .map((target) => dotnetBuildCommandFor(target, projectPath))
37
38
  .filter(Boolean)
38
39
  .map((command) => `${command.replace(VERBOSITY, '')} -v m${flag}`);