@daniellins/power-claude 0.16.3

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.

Potentially problematic release.


This version of @daniellins/power-claude might be problematic. Click here for more details.

Files changed (467) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -0
  3. package/bin/cli.js +124 -0
  4. package/installer/__tests__/doctor-ready-oracles.test.cjs +140 -0
  5. package/installer/__tests__/merge-settings.test.cjs +330 -0
  6. package/installer/adopt.js +161 -0
  7. package/installer/copy.js +203 -0
  8. package/installer/doctor.js +266 -0
  9. package/installer/install.js +155 -0
  10. package/installer/manifest.js +102 -0
  11. package/installer/motor-scripts.js +50 -0
  12. package/installer/motor-scripts.json +39 -0
  13. package/installer/uninstall.js +99 -0
  14. package/installer/update.js +376 -0
  15. package/installer/util.js +113 -0
  16. package/installer/wizard.js +64 -0
  17. package/package.json +26 -0
  18. package/scripts/__fixtures__/anti-path-demo/agents/ap-writer.md +13 -0
  19. package/scripts/__fixtures__/anti-path-demo/templates/write-tmpl.md +7 -0
  20. package/scripts/__fixtures__/anti-path-demo/workflows/wf-ap.smoke.json +10 -0
  21. package/scripts/__fixtures__/anti-path-demo/workflows/wf-ap.yaml +25 -0
  22. package/scripts/__fixtures__/assess-tier/retrofit-leve/pod/agents/content-chief.md +1 -0
  23. package/scripts/__fixtures__/assess-tier/retrofit-leve/pod/workflows/wf-criacao-redteam.yaml +3 -0
  24. package/scripts/__fixtures__/assess-tier/retrofit-leve/pod-spec.json +13 -0
  25. package/scripts/__fixtures__/fidelity/fake-green-average-in-band.yaml +21 -0
  26. package/scripts/__fixtures__/fidelity/fake-green-average.yaml +18 -0
  27. package/scripts/__fixtures__/fidelity/fake-green-denominator.yaml +19 -0
  28. package/scripts/__fixtures__/fidelity/fake-green-missing-evidence.yaml +18 -0
  29. package/scripts/__fixtures__/fidelity/fake-green-threshold.yaml +18 -0
  30. package/scripts/__fixtures__/fidelity/fake-green-weight.yaml +18 -0
  31. package/scripts/__fixtures__/fidelity/golden.yaml +31 -0
  32. package/scripts/__fixtures__/fidelity/trindade-incoherent.yaml +15 -0
  33. package/scripts/__fixtures__/forge-canonical/pod-spec-example.json +37 -0
  34. package/scripts/__fixtures__/migrate-squad/authored/agents/offer-engineer.md +17 -0
  35. package/scripts/__fixtures__/migrate-squad/authored/knowledge/no-source.md +7 -0
  36. package/scripts/__fixtures__/migrate-squad/authored/knowledge/value-stack-method.md +8 -0
  37. package/scripts/__fixtures__/migrate-squad/authored/specs/wf-sample.smoke.json +24 -0
  38. package/scripts/__fixtures__/migrate-squad/authored/specs/wf-sample.spec.json +45 -0
  39. package/scripts/__fixtures__/migrate-squad/legacy-squad/HEADLINE.md +3 -0
  40. package/scripts/__fixtures__/migrate-squad/legacy-squad/README.md +4 -0
  41. package/scripts/__fixtures__/migrate-squad/legacy-squad/agents/hormozi-offer.md +14 -0
  42. package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/dead-redundant.md +4 -0
  43. package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/method-guide.md +6 -0
  44. package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/veto-screen.md +4 -0
  45. package/scripts/__fixtures__/migrate-squad/legacy-squad/config/config.yaml +2 -0
  46. package/scripts/__fixtures__/migrate-squad/legacy-squad/data/case-library.md +3 -0
  47. package/scripts/__fixtures__/migrate-squad/legacy-squad/scripts/validate-mvn.py +16 -0
  48. package/scripts/__fixtures__/migrate-squad/legacy-squad/sops/sop-sample.md +3 -0
  49. package/scripts/__fixtures__/migrate-squad/legacy-squad/squad-io.yaml +14 -0
  50. package/scripts/__fixtures__/migrate-squad/legacy-squad/tasks/gather-context.md +7 -0
  51. package/scripts/__fixtures__/migrate-squad/legacy-squad/templates/output-tmpl.md +5 -0
  52. package/scripts/__fixtures__/migrate-squad/legacy-squad/workflows/wf-sample.yaml +24 -0
  53. package/scripts/__fixtures__/pipeline/pipelines/pipeline-checkpoint.yaml +21 -0
  54. package/scripts/__fixtures__/pipeline/pipelines/pipeline-demo.yaml +22 -0
  55. package/scripts/__fixtures__/pipeline/pods/pipe-a/agents/maker.md +13 -0
  56. package/scripts/__fixtures__/pipeline/pods/pipe-a/memory/learnings.consolidated.md +8 -0
  57. package/scripts/__fixtures__/pipeline/pods/pipe-a/memory/learnings.jsonl +83 -0
  58. package/scripts/__fixtures__/pipeline/pods/pipe-a/templates/make-tmpl.md +8 -0
  59. package/scripts/__fixtures__/pipeline/pods/pipe-a/workflows/wf-a.yaml +16 -0
  60. package/scripts/__fixtures__/pipeline/pods/pipe-b/agents/user.md +13 -0
  61. package/scripts/__fixtures__/pipeline/pods/pipe-b/memory/learnings.consolidated.md +8 -0
  62. package/scripts/__fixtures__/pipeline/pods/pipe-b/memory/learnings.jsonl +14 -0
  63. package/scripts/__fixtures__/pipeline/pods/pipe-b/templates/use-tmpl.md +6 -0
  64. package/scripts/__fixtures__/pipeline/pods/pipe-b/workflows/wf-b.yaml +16 -0
  65. package/scripts/__fixtures__/pod-run/agents/forge-chief.md +13 -0
  66. package/scripts/__fixtures__/pod-run/agents/pod-critic.md +13 -0
  67. package/scripts/__fixtures__/pod-run/templates/build-tmpl.md +8 -0
  68. package/scripts/__fixtures__/pod-run/templates/gate-tmpl.md +6 -0
  69. package/scripts/__fixtures__/pod-run/workflows/wf-min.yaml +43 -0
  70. package/scripts/__fixtures__/pod-run/workflows/wf-skip-modes.yaml +50 -0
  71. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-all-skippable.yaml +30 -0
  72. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-criteria-escalar-de-bloco.yaml +40 -0
  73. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-skip-mode-desconhecido.yaml +30 -0
  74. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-unknown-key.yaml +29 -0
  75. package/scripts/__fixtures__/pod-workflow/invalid-cycle.yaml +26 -0
  76. package/scripts/__fixtures__/pod-workflow/invalid-fix-loop-backedge.yaml +29 -0
  77. package/scripts/__fixtures__/pod-workflow/invalid-fix-loop-forward.yaml +29 -0
  78. package/scripts/__fixtures__/pod-workflow/invalid-handoff-open.yaml +27 -0
  79. package/scripts/__fixtures__/pod-workflow/invalid-knowledge-shape.yaml +18 -0
  80. package/scripts/__fixtures__/pod-workflow/invalid-missing-agent.yaml +15 -0
  81. package/scripts/__fixtures__/pod-workflow/invalid-missing-outputs.yaml +15 -0
  82. package/scripts/__fixtures__/pod-workflow/invalid-missing-template.yaml +15 -0
  83. package/scripts/__fixtures__/pod-workflow/invalid-missing-veto.yaml +13 -0
  84. package/scripts/__fixtures__/pod-workflow/invalid-template-not-on-disk.yaml +16 -0
  85. package/scripts/__fixtures__/pod-workflow/invalid-veto-prose.yaml +17 -0
  86. package/scripts/__fixtures__/pod-workflow/knowledge/a.md +4 -0
  87. package/scripts/__fixtures__/pod-workflow/knowledge/b.md +4 -0
  88. package/scripts/__fixtures__/pod-workflow/knowledge/c.md +4 -0
  89. package/scripts/__fixtures__/pod-workflow/templates/step-tmpl.md +7 -0
  90. package/scripts/__fixtures__/pod-workflow/valid-checkpoint-skip-modes.yaml +29 -0
  91. package/scripts/__fixtures__/pod-workflow/valid-fix-loop.yaml +30 -0
  92. package/scripts/__fixtures__/pod-workflow/valid-knowledge.yaml +30 -0
  93. package/scripts/__fixtures__/pod-workflow/valid-veto-always-true.yaml +20 -0
  94. package/scripts/__fixtures__/pod-workflow/valid.yaml +27 -0
  95. package/scripts/__fixtures__/smoke-canary/agents/canary-reporter.md +13 -0
  96. package/scripts/__fixtures__/smoke-canary/agents/canary-researcher.md +13 -0
  97. package/scripts/__fixtures__/smoke-canary/agents/canary-synth.md +13 -0
  98. package/scripts/__fixtures__/smoke-canary/memory/learnings.consolidated.md +10 -0
  99. package/scripts/__fixtures__/smoke-canary/memory/learnings.jsonl +448 -0
  100. package/scripts/__fixtures__/smoke-canary/templates/report-tmpl.md +9 -0
  101. package/scripts/__fixtures__/smoke-canary/templates/research-tmpl.md +7 -0
  102. package/scripts/__fixtures__/smoke-canary/templates/synth-tmpl.md +9 -0
  103. package/scripts/__fixtures__/smoke-canary/workflows/wf-canary.smoke.json +11 -0
  104. package/scripts/__fixtures__/smoke-canary/workflows/wf-canary.yaml +53 -0
  105. package/scripts/__fixtures__/source-audit/fake-green-bronze-unsignaled.yaml +10 -0
  106. package/scripts/__fixtures__/source-audit/fake-green-counts.yaml +9 -0
  107. package/scripts/__fixtures__/source-audit/fake-green-missing-evidence.yaml +9 -0
  108. package/scripts/__fixtures__/source-audit/fake-green-unclassified.yaml +10 -0
  109. package/scripts/__fixtures__/source-audit/fake-green-verdict.yaml +10 -0
  110. package/scripts/__fixtures__/source-audit/golden-blocked.yaml +10 -0
  111. package/scripts/__fixtures__/source-audit/golden.yaml +11 -0
  112. package/scripts/__fixtures__/sweep-forms/agents/forms-count.md +13 -0
  113. package/scripts/__fixtures__/sweep-forms/agents/forms-field.md +13 -0
  114. package/scripts/__fixtures__/sweep-forms/agents/forms-fileref.md +13 -0
  115. package/scripts/__fixtures__/sweep-forms/agents/forms-json.md +13 -0
  116. package/scripts/__fixtures__/sweep-forms/agents/forms-section.md +13 -0
  117. package/scripts/__fixtures__/sweep-forms/specs/wf-forms.spec.json +5 -0
  118. package/scripts/__fixtures__/sweep-forms/templates/t_count.md +5 -0
  119. package/scripts/__fixtures__/sweep-forms/templates/t_field.md +3 -0
  120. package/scripts/__fixtures__/sweep-forms/templates/t_fileref.md +3 -0
  121. package/scripts/__fixtures__/sweep-forms/templates/t_json.md +0 -0
  122. package/scripts/__fixtures__/sweep-forms/templates/t_section.md +3 -0
  123. package/scripts/__fixtures__/sweep-forms/workflows/wf-forms.smoke.json +13 -0
  124. package/scripts/__fixtures__/sweep-forms/workflows/wf-forms.yaml +67 -0
  125. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-1-noop-state-json.fixture.md +18 -0
  126. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-2-crash-buildworkflowyaml.fixture.md +26 -0
  127. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-3-nasce-verde-skip-modes.fixture.md +29 -0
  128. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-4-guarda-sem-irmao.fixture.md +17 -0
  129. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-5a-diff-stat-sem-guarda.fixture.md +17 -0
  130. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-5b-test-f-sobre-diretorio.fixture.md +20 -0
  131. package/scripts/__fixtures__/veto-checks/copy-clean.md +4 -0
  132. package/scripts/__fixtures__/veto-checks/copy-dashes.md +4 -0
  133. package/scripts/__fixtures__/veto-checks/n8n-bad.md +18 -0
  134. package/scripts/__fixtures__/veto-checks/n8n-ok.md +22 -0
  135. package/scripts/__fixtures__/veto-checks/page-bad.html +7 -0
  136. package/scripts/__fixtures__/veto-checks/phash/copy.png +0 -0
  137. package/scripts/__fixtures__/veto-checks/phash/distinct.png +0 -0
  138. package/scripts/__fixtures__/veto-checks/phash/distinct2.png +0 -0
  139. package/scripts/__fixtures__/veto-checks/phash/make-fixtures.cjs +203 -0
  140. package/scripts/__fixtures__/veto-checks/phash/ref.png +0 -0
  141. package/scripts/__fixtures__/veto-checks/slide-ok.html +15 -0
  142. package/scripts/__fixtures__/veto-checks/slide-overflow.html +14 -0
  143. package/scripts/__fixtures__/veto-checks/sweep-pod/agents/vc-checker.md +13 -0
  144. package/scripts/__fixtures__/veto-checks/sweep-pod/templates/check-tmpl.md +6 -0
  145. package/scripts/__fixtures__/veto-checks/sweep-pod/workflows/wf-vc.smoke.json +10 -0
  146. package/scripts/__fixtures__/veto-checks/sweep-pod/workflows/wf-vc.yaml +19 -0
  147. package/scripts/__fixtures__/voz-do-dono/README.md +23 -0
  148. package/scripts/__fixtures__/voz-do-dono/dono-escalar.yaml +9 -0
  149. package/scripts/__fixtures__/voz-do-dono/dono-guia.yaml +10 -0
  150. package/scripts/__fixtures__/voz-do-dono/peca-3a-pessoa.md +6 -0
  151. package/scripts/__fixtures__/voz-do-dono/peca-com-lexico-proibido.md +6 -0
  152. package/scripts/__fixtures__/voz-do-dono/peca-limpa-1a-pessoa.md +6 -0
  153. package/scripts/__tests__/derive-ready-transitions.test.cjs +269 -0
  154. package/scripts/__tests__/enforce-cycle-authority.test.cjs +869 -0
  155. package/scripts/__tests__/enforce-git-push-authority.test.cjs +701 -0
  156. package/scripts/__tests__/enforce-ready-oracles.test.cjs +745 -0
  157. package/scripts/__tests__/motor-scripts.test.cjs +339 -0
  158. package/scripts/__tests__/pc-run-pod-counter.test.cjs +58 -0
  159. package/scripts/__tests__/pc-run-pod.test.cjs +578 -0
  160. package/scripts/__tests__/validate-pod-workflow.test.cjs +90 -0
  161. package/scripts/__tests__/validate-story-acs.test.cjs +607 -0
  162. package/scripts/anti-path.test.cjs +163 -0
  163. package/scripts/arena.cjs +514 -0
  164. package/scripts/arena.test.cjs +213 -0
  165. package/scripts/brief-leitura-obrigatoria.test.cjs +364 -0
  166. package/scripts/consolidate-knowledge-runner.test.cjs +249 -0
  167. package/scripts/default-funcional.test.cjs +153 -0
  168. package/scripts/derive-ready-transitions.cjs +325 -0
  169. package/scripts/forge-additive.test.cjs +158 -0
  170. package/scripts/forge-assess-tier.cjs +205 -0
  171. package/scripts/forge-assess-tier.test.cjs +114 -0
  172. package/scripts/forge-canonical.test.cjs +624 -0
  173. package/scripts/forge-generate.cjs +1303 -0
  174. package/scripts/forge-harden-learning.cjs +921 -0
  175. package/scripts/forge-harden-learning.test.cjs +242 -0
  176. package/scripts/forge-ingest-memory.cjs +136 -0
  177. package/scripts/forge-upgrade-pod.cjs +368 -0
  178. package/scripts/forge.test.cjs +609 -0
  179. package/scripts/harden-knowledge.test.cjs +612 -0
  180. package/scripts/harness-g5-lite.cjs +243 -0
  181. package/scripts/harness-g5-lite.test.cjs +134 -0
  182. package/scripts/hn-carousel-contrato-entrada.test.cjs +96 -0
  183. package/scripts/migrate-squad-orchestration.test.cjs +146 -0
  184. package/scripts/migration-manifest-sf3.2.test.cjs +382 -0
  185. package/scripts/migration-manifest-sf3.3.test.cjs +170 -0
  186. package/scripts/migration-manifest-sf5.5.test.cjs +189 -0
  187. package/scripts/migration-manifest.cjs +1146 -0
  188. package/scripts/migration-manifest.test.cjs +186 -0
  189. package/scripts/npm-scope-invocation.test.cjs +194 -0
  190. package/scripts/owner-layer.cjs +442 -0
  191. package/scripts/owner-layer.test.cjs +358 -0
  192. package/scripts/pc-run-pipeline.cjs +289 -0
  193. package/scripts/pc-run-pipeline.test.cjs +256 -0
  194. package/scripts/pc-run-pod-countgte.test.cjs +66 -0
  195. package/scripts/pc-run-pod.cjs +1355 -0
  196. package/scripts/phash-gate.test.cjs +433 -0
  197. package/scripts/pod-export-compile.cjs +487 -0
  198. package/scripts/pod-export-compile.test.cjs +312 -0
  199. package/scripts/pod-export-executor.cjs +678 -0
  200. package/scripts/pod-export-executor.test.cjs +1341 -0
  201. package/scripts/pod-export-hardening.test.cjs +531 -0
  202. package/scripts/pod-export-postprocess.cjs +427 -0
  203. package/scripts/pod-export-postprocess.test.cjs +558 -0
  204. package/scripts/pod-export-queue-race.test.cjs +865 -0
  205. package/scripts/pod-export-release.cjs +473 -0
  206. package/scripts/pod-export-release.test.cjs +267 -0
  207. package/scripts/pod-export-service.cjs +1297 -0
  208. package/scripts/pod-export-service.test.cjs +489 -0
  209. package/scripts/pod-export-tools.cjs +470 -0
  210. package/scripts/pod-export-ui.cjs +805 -0
  211. package/scripts/pod-export-ui.test.cjs +301 -0
  212. package/scripts/pod-memory-budget.test.cjs +309 -0
  213. package/scripts/pod-memory-note.cjs +357 -0
  214. package/scripts/pod-memory-note.test.cjs +452 -0
  215. package/scripts/pod-memory.cjs +1252 -0
  216. package/scripts/pod-memory.test.cjs +951 -0
  217. package/scripts/render-png.test.cjs +307 -0
  218. package/scripts/smoke-isolation.test.cjs +100 -0
  219. package/scripts/smoke-pod-workflow.cjs +755 -0
  220. package/scripts/smoke-sweep.test.cjs +170 -0
  221. package/scripts/validate-adopt.js +235 -0
  222. package/scripts/validate-branding.js +84 -0
  223. package/scripts/validate-budgets.js +140 -0
  224. package/scripts/validate-fidelity-output.cjs +267 -0
  225. package/scripts/validate-fidelity-output.test.cjs +101 -0
  226. package/scripts/validate-install.js +230 -0
  227. package/scripts/validate-no-clone.cjs +220 -0
  228. package/scripts/validate-no-clone.test.cjs +77 -0
  229. package/scripts/validate-pipeline.cjs +145 -0
  230. package/scripts/validate-pod-workflow.cjs +878 -0
  231. package/scripts/validate-source-audit.cjs +170 -0
  232. package/scripts/validate-source-audit.test.cjs +90 -0
  233. package/scripts/validate-story-acs.cjs +862 -0
  234. package/scripts/validate-update-scope-motor.js +421 -0
  235. package/scripts/veto-checks/html-valid.cjs +92 -0
  236. package/scripts/veto-checks/lexical-check.cjs +62 -0
  237. package/scripts/veto-checks/phash-gate.cjs +589 -0
  238. package/scripts/veto-checks/render-gate.cjs +165 -0
  239. package/scripts/veto-checks/render-png.cjs +358 -0
  240. package/scripts/veto-checks.test.cjs +219 -0
  241. package/scripts/watcher-audit.cjs +178 -0
  242. package/template/.claude/CLAUDE.md +103 -0
  243. package/template/.claude/agents/pc-analyst.md +100 -0
  244. package/template/.claude/agents/pc-architect.md +32 -0
  245. package/template/.claude/agents/pc-dev.md +27 -0
  246. package/template/.claude/agents/pc-devops.md +45 -0
  247. package/template/.claude/agents/pc-planner.md +32 -0
  248. package/template/.claude/agents/pc-po.md +25 -0
  249. package/template/.claude/agents/pc-qa.md +25 -0
  250. package/template/.claude/commands/power/academy.md +9 -0
  251. package/template/.claude/commands/power/automation.md +9 -0
  252. package/template/.claude/commands/power/content-carousel.md +267 -0
  253. package/template/.claude/commands/power/content.md +9 -0
  254. package/template/.claude/commands/power/dev.md +9 -0
  255. package/template/.claude/commands/power/forge.md +224 -0
  256. package/template/.claude/commands/power/hormozi.md +221 -0
  257. package/template/.claude/commands/power/modo.md +57 -0
  258. package/template/.claude/hooks/README.md +114 -0
  259. package/template/.claude/hooks/context-budget.cjs +64 -0
  260. package/template/.claude/hooks/enforce-cycle-authority.cjs +457 -0
  261. package/template/.claude/hooks/enforce-git-push-authority.cjs +925 -0
  262. package/template/.claude/hooks/enforce-ready-oracles.cjs +502 -0
  263. package/template/.claude/rules/authority.md +76 -0
  264. package/template/.claude/rules/curadoria-de-conhecimento.md +30 -0
  265. package/template/.claude/rules/model-matrix.md +34 -0
  266. package/template/.claude/rules/orchestration.md +133 -0
  267. package/template/.claude/settings.json +50 -0
  268. package/template/.claude/skills/pc-apply-qa-fixes/SKILL.md +108 -0
  269. package/template/.claude/skills/pc-apply-qa-fixes/references/fix-loop.md +40 -0
  270. package/template/.claude/skills/pc-apply-qa-fixes/references/fix-request-template.md +70 -0
  271. package/template/.claude/skills/pc-arena/SKILL.md +96 -0
  272. package/template/.claude/skills/pc-close-story/SKILL.md +105 -0
  273. package/template/.claude/skills/pc-commit/SKILL.md +108 -0
  274. package/template/.claude/skills/pc-develop-story/SKILL.md +116 -0
  275. package/template/.claude/skills/pc-full-cycle/SKILL.md +150 -0
  276. package/template/.claude/skills/pc-pipeline/SKILL.md +63 -0
  277. package/template/.claude/skills/pc-pipeline/references/gate-templates.md +46 -0
  278. package/template/.claude/skills/pc-review-story/SKILL.md +129 -0
  279. package/template/.claude/skills/pc-review-story/references/false-positive-detection.md +50 -0
  280. package/template/.claude/skills/pc-review-story/references/nfr-assess.md +47 -0
  281. package/template/.claude/skills/pc-review-story/references/risk-profile.md +75 -0
  282. package/template/.claude/skills/pc-review-story/references/security-checklist.md +69 -0
  283. package/template/.claude/skills/pc-review-story/references/test-design.md +37 -0
  284. package/template/.claude/skills/pc-review-story/references/trace-requirements.md +46 -0
  285. package/template/.claude/skills/pc-run-pod/SKILL.md +123 -0
  286. package/template/.claude/skills/pc-validate-story/SKILL.md +114 -0
  287. package/template/.claude/skills/pc-wave/SKILL.md +133 -0
  288. package/template/.env.example +10 -0
  289. package/template/knowledge/analyst/brainstorming.md +40 -0
  290. package/template/knowledge/analyst/brief-skeleton.md +32 -0
  291. package/template/knowledge/analyst/competitor-skeleton.md +39 -0
  292. package/template/knowledge/analyst/elicitation-menu.md +49 -0
  293. package/template/knowledge/analyst/market-research-skeleton.md +36 -0
  294. package/template/knowledge/analyst/research-prompt-structure.md +48 -0
  295. package/template/knowledge/analyst/roi-skeleton.md +51 -0
  296. package/template/knowledge/voice-dna/README.md +99 -0
  297. package/template/knowledge/voice-dna/voice-dna.example.yaml +157 -0
  298. package/template/pods/README.md +64 -0
  299. package/template/pods/academy/README.md +37 -0
  300. package/template/pods/academy/agents/academy-chief.md +27 -0
  301. package/template/pods/academy/examples/palestra.example.md +142 -0
  302. package/template/pods/academy/pod-spec.json +44 -0
  303. package/template/pods/academy/templates/materiais-tmpl.md +17 -0
  304. package/template/pods/academy/templates/revisao-didatica-tmpl.md +20 -0
  305. package/template/pods/academy/workflows/wf-materiais-revisao.yaml +30 -0
  306. package/template/pods/authority-content/README.md +43 -0
  307. package/template/pods/authority-content/agents/content-chief.md +28 -0
  308. package/template/pods/authority-content/pod-spec.json +45 -0
  309. package/template/pods/authority-content/templates/criacao-na-voz-tmpl.md +17 -0
  310. package/template/pods/authority-content/templates/red-team-factual-tmpl.md +23 -0
  311. package/template/pods/authority-content/workflows/wf-criacao-redteam.yaml +30 -0
  312. package/template/pods/automation/README.md +37 -0
  313. package/template/pods/automation/agents/langgraph-architect.md +30 -0
  314. package/template/pods/automation/agents/n8n-architect.md +30 -0
  315. package/template/pods/automation/examples/README.md +46 -0
  316. package/template/pods/automation/examples/n8n-content-radar.example.json +380 -0
  317. package/template/pods/automation/knowledge/langgraph-patterns.md +126 -0
  318. package/template/pods/automation/knowledge/n8n-patterns.md +131 -0
  319. package/template/pods/automation/memory/README.md +16 -0
  320. package/template/pods/automation/memory/learnings.consolidated.md +8 -0
  321. package/template/pods/automation/memory/learnings.jsonl +0 -0
  322. package/template/pods/automation/pod-spec.json +109 -0
  323. package/template/pods/automation/specs/wf-langgraph-build.spec.json +107 -0
  324. package/template/pods/automation/squad-io.yaml +28 -0
  325. package/template/pods/automation/templates/lg-build-tmpl.md +17 -0
  326. package/template/pods/automation/templates/lg-design-tmpl.md +23 -0
  327. package/template/pods/automation/templates/lg-discover-tmpl.md +20 -0
  328. package/template/pods/automation/templates/lg-handoff-tmpl.md +20 -0
  329. package/template/pods/automation/templates/lg-test-tmpl.md +20 -0
  330. package/template/pods/automation/templates/n8n-build-tmpl.md +14 -0
  331. package/template/pods/automation/templates/n8n-design-tmpl.md +23 -0
  332. package/template/pods/automation/templates/n8n-discover-tmpl.md +20 -0
  333. package/template/pods/automation/templates/n8n-handoff-tmpl.md +20 -0
  334. package/template/pods/automation/templates/n8n-test-tmpl.md +20 -0
  335. package/template/pods/automation/workflows/wf-langgraph-build.smoke.json +11 -0
  336. package/template/pods/automation/workflows/wf-langgraph-build.yaml +64 -0
  337. package/template/pods/automation/workflows/wf-n8n-build.smoke.json +11 -0
  338. package/template/pods/automation/workflows/wf-n8n-build.yaml +65 -0
  339. package/template/pods/content-carousel/README.md +91 -0
  340. package/template/pods/content-carousel/agents/carousel-builder.md +28 -0
  341. package/template/pods/content-carousel/agents/carousel-visual.md +24 -0
  342. package/template/pods/content-carousel/agents/hn-curator.md +74 -0
  343. package/template/pods/content-carousel/config/heuristic-watcher-rubric.yaml +59 -0
  344. package/template/pods/content-carousel/knowledge/aquisicao-ancora.md +443 -0
  345. package/template/pods/content-carousel/knowledge/cta-brief.md +25 -0
  346. package/template/pods/content-carousel/knowledge/dono.example.yaml +45 -0
  347. package/template/pods/content-carousel/knowledge/identidade-visual-mascote.md +62 -0
  348. package/template/pods/content-carousel/knowledge/image-generation.md +62 -0
  349. package/template/pods/content-carousel/knowledge/selecao-de-ancora.md +60 -0
  350. package/template/pods/content-carousel/knowledge/slides-html.md +56 -0
  351. package/template/pods/content-carousel/memory/README.md +18 -0
  352. package/template/pods/content-carousel/memory/learnings.consolidated.md +6 -0
  353. package/template/pods/content-carousel/memory/learnings.jsonl +0 -0
  354. package/template/pods/content-carousel/pod-spec.json +100 -0
  355. package/template/pods/content-carousel/squad-io.yaml +10 -0
  356. package/template/pods/content-carousel/templates/assembly-tmpl.md +23 -0
  357. package/template/pods/content-carousel/templates/digest-tmpl.md +29 -0
  358. package/template/pods/content-carousel/templates/visuals-tmpl.md +20 -0
  359. package/template/pods/content-carousel/workflows/wf-content-carousel.smoke.json +23 -0
  360. package/template/pods/content-carousel/workflows/wf-content-carousel.yaml +80 -0
  361. package/template/pods/dev-ia/README.md +45 -0
  362. package/template/pods/dev-ia/agents/db-architect.md +33 -0
  363. package/template/pods/dev-ia/agents/ux-architect.md +29 -0
  364. package/template/pods/dev-ia/knowledge/db/migration-protocol.md +95 -0
  365. package/template/pods/dev-ia/knowledge/db/rls-patterns.md +101 -0
  366. package/template/pods/dev-ia/knowledge/db/schema-review-checklist.md +76 -0
  367. package/template/pods/dev-ia/memory/README.md +16 -0
  368. package/template/pods/dev-ia/memory/learnings.consolidated.md +8 -0
  369. package/template/pods/dev-ia/memory/learnings.jsonl +0 -0
  370. package/template/pods/dev-ia/pod-spec.json +34 -0
  371. package/template/pods/dev-ia/templates/schema-review-tmpl.md +23 -0
  372. package/template/pods/dev-ia/templates/ux-flow-review-tmpl.md +23 -0
  373. package/template/pods/dev-ia/tools/db-ops/backup-manager.sh +321 -0
  374. package/template/pods/dev-ia/tools/db-ops/best-practices-auditor.sh +534 -0
  375. package/template/pods/dev-ia/tools/db-ops/bootstrap-runner.sh +255 -0
  376. package/template/pods/dev-ia/tools/db-ops/data-loader.sh +616 -0
  377. package/template/pods/dev-ia/tools/db-ops/detect-database.sh +113 -0
  378. package/template/pods/dev-ia/tools/db-ops/explain-analyzer.sh +285 -0
  379. package/template/pods/dev-ia/tools/db-ops/health-checker.sh +323 -0
  380. package/template/pods/dev-ia/tools/db-ops/migration-runner.sh +308 -0
  381. package/template/pods/dev-ia/tools/db-ops/postgresql-schema-loader.sh +334 -0
  382. package/template/pods/dev-ia/tools/db-ops/query-runner.sh +171 -0
  383. package/template/pods/dev-ia/tools/db-ops/rls-policy-installer.sh +480 -0
  384. package/template/pods/dev-ia/tools/db-ops/rls-test-suite.sh +172 -0
  385. package/template/pods/dev-ia/tools/db-ops/schema-context-loader.sh +176 -0
  386. package/template/pods/hormozi-method/README.md +52 -0
  387. package/template/pods/hormozi-method/agents/acquisition-strategist.md +33 -0
  388. package/template/pods/hormozi-method/agents/conversion-copywriter.md +34 -0
  389. package/template/pods/hormozi-method/agents/monetization-architect.md +35 -0
  390. package/template/pods/hormozi-method/agents/offer-engineer.md +37 -0
  391. package/template/pods/hormozi-method/knowledge/core-four-leads.md +51 -0
  392. package/template/pods/hormozi-method/knowledge/grand-slam-offer.md +66 -0
  393. package/template/pods/hormozi-method/knowledge/growth-decision.md +38 -0
  394. package/template/pods/hormozi-method/knowledge/hooks-and-ads.md +56 -0
  395. package/template/pods/hormozi-method/knowledge/launch-email.md +31 -0
  396. package/template/pods/hormozi-method/knowledge/money-model.md +46 -0
  397. package/template/pods/hormozi-method/knowledge/opportunity-screening.md +44 -0
  398. package/template/pods/hormozi-method/knowledge/pages.md +25 -0
  399. package/template/pods/hormozi-method/knowledge/pos-lancamento-leads.md +56 -0
  400. package/template/pods/hormozi-method/knowledge/pricing.md +36 -0
  401. package/template/pods/hormozi-method/knowledge/retention-ltv.md +37 -0
  402. package/template/pods/hormozi-method/knowledge/scale-roadmap.md +35 -0
  403. package/template/pods/hormozi-method/knowledge/swipefiles-curados.md +44 -0
  404. package/template/pods/hormozi-method/knowledge/value-equation-diagnostic.md +77 -0
  405. package/template/pods/hormozi-method/memory/README.md +16 -0
  406. package/template/pods/hormozi-method/memory/learnings.consolidated.md +6 -0
  407. package/template/pods/hormozi-method/memory/learnings.jsonl +0 -0
  408. package/template/pods/hormozi-method/pod-spec.json +50 -0
  409. package/template/pods/hormozi-method/specs/wf-full-launch-sequence.spec.json +149 -0
  410. package/template/pods/hormozi-method/specs/wf-lead-magnet-pipeline.spec.json +125 -0
  411. package/template/pods/hormozi-method/specs/wf-money-model-design.spec.json +117 -0
  412. package/template/pods/hormozi-method/specs/wf-paid-ads-campaign.spec.json +132 -0
  413. package/template/pods/hormozi-method/specs/wf-scaling-diagnostic.spec.json +121 -0
  414. package/template/pods/hormozi-method/squad-io.yaml +43 -0
  415. package/template/pods/hormozi-method/templates/conversion-copywriter-tmpl.md +20 -0
  416. package/template/pods/hormozi-method/templates/launch-execucao-tmpl.md +17 -0
  417. package/template/pods/hormozi-method/templates/launch-fechamento-tmpl.md +17 -0
  418. package/template/pods/hormozi-method/templates/launch-pos-tmpl.md +17 -0
  419. package/template/pods/hormozi-method/templates/launch-preparacao-tmpl.md +17 -0
  420. package/template/pods/hormozi-method/templates/lm-copy-tmpl.md +17 -0
  421. package/template/pods/hormozi-method/templates/lm-distribuicao-tmpl.md +18 -0
  422. package/template/pods/hormozi-method/templates/lm-estrategia-tmpl.md +17 -0
  423. package/template/pods/hormozi-method/templates/mm-arquitetura-tmpl.md +18 -0
  424. package/template/pods/hormozi-method/templates/mm-copy-tmpl.md +17 -0
  425. package/template/pods/hormozi-method/templates/mm-diagnostico-tmpl.md +14 -0
  426. package/template/pods/hormozi-method/templates/monetization-architect-tmpl.md +17 -0
  427. package/template/pods/hormozi-method/templates/offer-engineer-tmpl.md +20 -0
  428. package/template/pods/hormozi-method/templates/paid-criativos-tmpl.md +19 -0
  429. package/template/pods/hormozi-method/templates/paid-fundacao-tmpl.md +17 -0
  430. package/template/pods/hormozi-method/templates/paid-teste-escala-tmpl.md +17 -0
  431. package/template/pods/hormozi-method/templates/scaling-diagnostico-tmpl.md +17 -0
  432. package/template/pods/hormozi-method/templates/scaling-priorizacao-tmpl.md +17 -0
  433. package/template/pods/hormozi-method/templates/scaling-roadmap-tmpl.md +17 -0
  434. package/template/pods/hormozi-method/workflows/wf-full-launch-sequence.smoke.json +12 -0
  435. package/template/pods/hormozi-method/workflows/wf-full-launch-sequence.yaml +56 -0
  436. package/template/pods/hormozi-method/workflows/wf-grand-slam-offer.smoke.json +11 -0
  437. package/template/pods/hormozi-method/workflows/wf-grand-slam-offer.yaml +45 -0
  438. package/template/pods/hormozi-method/workflows/wf-lead-magnet-pipeline.smoke.json +9 -0
  439. package/template/pods/hormozi-method/workflows/wf-lead-magnet-pipeline.yaml +45 -0
  440. package/template/pods/hormozi-method/workflows/wf-money-model-design.smoke.json +11 -0
  441. package/template/pods/hormozi-method/workflows/wf-money-model-design.yaml +45 -0
  442. package/template/pods/hormozi-method/workflows/wf-paid-ads-campaign.smoke.json +9 -0
  443. package/template/pods/hormozi-method/workflows/wf-paid-ads-campaign.yaml +46 -0
  444. package/template/pods/hormozi-method/workflows/wf-scaling-diagnostic.smoke.json +11 -0
  445. package/template/pods/hormozi-method/workflows/wf-scaling-diagnostic.yaml +44 -0
  446. package/template/pods/squad-forge/README.md +53 -0
  447. package/template/pods/squad-forge/agents/forge-chief.md +31 -0
  448. package/template/pods/squad-forge/agents/mind-extractor.md +29 -0
  449. package/template/pods/squad-forge/agents/pod-critic.md +26 -0
  450. package/template/pods/squad-forge/config/heuristic-watcher-rubric.yaml +74 -0
  451. package/template/pods/squad-forge/knowledge/fidelity-rubric.yaml +176 -0
  452. package/template/pods/squad-forge/knowledge/g6-memory-check.md +17 -0
  453. package/template/pods/squad-forge/knowledge/memory-ingest.md +16 -0
  454. package/template/pods/squad-forge/knowledge/source-audit-rubric.yaml +60 -0
  455. package/template/pods/squad-forge/memory/README.md +16 -0
  456. package/template/pods/squad-forge/memory/learnings.consolidated.md +8 -0
  457. package/template/pods/squad-forge/memory/learnings.jsonl +0 -0
  458. package/template/pods/squad-forge/pod-spec.json +166 -0
  459. package/template/pods/squad-forge/squad-io.yaml +13 -0
  460. package/template/pods/squad-forge/tasks/pipeline.md +158 -0
  461. package/template/pods/squad-forge/templates/discovery-tmpl.md +40 -0
  462. package/template/pods/squad-forge/templates/generation-tmpl.md +18 -0
  463. package/template/pods/squad-forge/templates/red-team-tmpl.md +17 -0
  464. package/template/pods/squad-forge/templates/register-tmpl.md +17 -0
  465. package/template/pods/squad-forge/templates/workflow-tmpl.yaml +101 -0
  466. package/template/pods/squad-forge/workflows/wf-forge.smoke.json +12 -0
  467. package/template/pods/squad-forge/workflows/wf-forge.yaml +60 -0
@@ -0,0 +1,701 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * enforce-git-push-authority.test.cjs — primeira suíte automatizada deste
5
+ * hook (achado (d) do Contexto da story SF12.3 — sem teste desde que foi
6
+ * escrito). Cobre os 7 controles do AC43 (Bloco M, D4/D1):
7
+ *
8
+ * 1. pc-devops + git push → allow (sem output)
9
+ * 2. pc-devops + gh pr create → allow (sem output)
10
+ * 3. pc-devops + gh pr merge → ask (3º nível D4 — merge nunca automático)
11
+ * 4. pc-dev + git push → ask (papel errado)
12
+ * 5. force-push com pc-devops → deny (único deny do arquivo, incondicional)
13
+ * 6. autoproclamação (env var + prefixo inline, SEM fallback) → ask
14
+ * 7. agent_id ausente (main thread) → ask
15
+ *
16
+ * Mais o instrumento de mutação (mesmo padrão do AC14/AC18): gera cópia
17
+ * mutante em tmp com a checagem de identidade neutralizada, executa como
18
+ * subprocesso com o payload do controle 5 (force-push) — o teste FALHA se
19
+ * a mutante destravar force-push.
20
+ *
21
+ * Fix round F1 (gate FAIL do `pc-qa`, 2026-08-06): o parser original
22
+ * dependia de contiguidade textual (regex `\bgit\s+push\b`/`\s--force\b`) e
23
+ * era bypassável por sintaxe de shell comum. Controles 8-13 abaixo cobrem os
24
+ * bypasses reproduzidos pelo QA (flag curta combinada, opção global `-c`
25
+ * antes do subcomando) e variações adicionais (o QA pediu explicitamente
26
+ * "e com as que você mesmo pensar"): `-C`, `--git-dir=`, comando encadeado,
27
+ * `git` por caminho absoluto, e a prova de que `pc-devops` não force-pusha
28
+ * por NENHUMA delas.
29
+ *
30
+ * Fix round F3 (2º gate FAIL do `pc-qa`, mesmo dia): alias de git — o texto
31
+ * do comando não denuncia a operação quando um alias resolve para `push`.
32
+ * `testFixRoundF3()` cobre as 3 fontes de resolução (inline `-c
33
+ * alias.X=valor`; `git config alias.X valor` num segmento anterior da
34
+ * mesma cadeia; alias JÁ CONFIGURADO no repo, exigindo consulta real via
35
+ * subprocesso a `git config --get`, testada com um repositório git
36
+ * temporário de verdade) + o controle negativo (subcomando desconhecido
37
+ * que NÃO é alias → silêncio, nunca `ask` por via das dúvidas) + o
38
+ * controle de "não deu para confirmar com segurança" (git inacessível →
39
+ * `ask`, nunca silêncio nem crash). Por decisão do dono (D6), este hook
40
+ * continua sendo guard-rail contra erro, não barreira contra evasão
41
+ * deliberada — `sh -c`/`bash -c`/`npm run`/`node -e`/scripts externos
42
+ * seguem fora de alcance por desenho, não testados aqui (D7 documenta a
43
+ * fronteira, não este arquivo).
44
+ *
45
+ * Fix round F4 (3º gate FAIL do `pc-qa`, mesmo dia): o guarda de
46
+ * profundidade de `analyzeSegment` (alias que resolve para outro alias)
47
+ * retornava `null` (silêncio) ao estourar o limite — "não sei" tratado como
48
+ * "sei que não". `testFixRoundF4()` reproduz o achado do QA com um
49
+ * repositório git real (cadeia de 5 aliases terminando em force-push — o
50
+ * caso exato do gate) e uma cadeia BEM mais longa (20 níveis) para provar
51
+ * que estourar o limite nunca mais é silêncio. Cobre também os 2 outros
52
+ * achados do sweep pedido pelo QA (mesma família — "não sei" tratado como
53
+ * "sei que não"): o fallback de JSON malformado usava o parser tokenizado,
54
+ * que colapsa um JSON compacto sem espaço fora de strings num único token e
55
+ * NUNCA detectava nada (mesmo com "git push --force" literal no texto) —
56
+ * corrigido para regex de substring sobre o texto bruto; e `main()` não
57
+ * tinha rede de segurança de topo — uma exceção não prevista terminava o
58
+ * processo sem emitir decisão nenhuma (fail-open pior que `ask`) —
59
+ * corrigido com `try/catch` em `main()`, testado forçando `readStdin()` a
60
+ * lançar.
61
+ *
62
+ * Fix round F5 (4º gate FAIL do `pc-qa`, mesmo dia): assimetria entre os
63
+ * 2 branches do parser — o `git` ganhou resolução de alias em 3 fontes no
64
+ * F3/F4, o `gh` continuou casando `pr create`/`pr merge` de forma literal,
65
+ * mesmo o `gh` tendo alias NATIVO (`gh alias set`) tão real quanto o do
66
+ * git. `testFixRoundF5()` reproduz os 2 casos exatos do QA (`gh prc
67
+ * --title x`, alias de `pr create`, pc-dev; `gh qm 5 --auto`, alias de
68
+ * `pr merge`, pc-devops — o caso que furava a garantia mais repetida da
69
+ * story) com `GH_CONFIG_DIR` isolado e `gh alias set` real, mais alias de
70
+ * shell (prefixo `!` e via flag `--shell`), alias definido na própria
71
+ * cadeia, cadeia de 2 aliases, os 2 controles negativos (subcomando
72
+ * desconhecido que não é alias; subcomando real do gh), `gh` inacessível,
73
+ * e a prova final de que `gh pr merge` não é alcançável sem `ask` por
74
+ * nenhuma forma testada, nem para pc-devops.
75
+ */
76
+
77
+ const fs = require('node:fs');
78
+ const os = require('node:os');
79
+ const path = require('node:path');
80
+ const { spawnSync } = require('node:child_process');
81
+
82
+ const REPO_ROOT = path.resolve(__dirname, '..', '..');
83
+ const HOOK_PATH = path.join(REPO_ROOT, 'template', '.claude', 'hooks', 'enforce-git-push-authority.cjs');
84
+
85
+ let failures = 0;
86
+ function assert(ok, msg) {
87
+ if (ok) {
88
+ console.log(`[OK ] ${msg}`);
89
+ } else {
90
+ console.error(`[FALHA] ${msg}`);
91
+ failures++;
92
+ }
93
+ }
94
+
95
+ function runHookAtPath(hookPath, rawInput, env) {
96
+ const result = spawnSync(process.execPath, [hookPath], {
97
+ input: rawInput,
98
+ encoding: 'utf8',
99
+ timeout: 10000,
100
+ env: env || process.env,
101
+ });
102
+ if (result.error) throw result.error;
103
+ let decision = null;
104
+ const out = (result.stdout || '').trim();
105
+ if (out) {
106
+ try {
107
+ decision = JSON.parse(out)?.hookSpecificOutput?.permissionDecision || null;
108
+ } catch {
109
+ decision = 'UNPARSEABLE_OUTPUT';
110
+ }
111
+ }
112
+ return { decision, stdout: out };
113
+ }
114
+
115
+ function runHook(rawInput, env) {
116
+ return runHookAtPath(HOOK_PATH, rawInput, env);
117
+ }
118
+
119
+ function payloadCmd({ agentType, agentId = 's-1', command, cwd }) {
120
+ const p = { tool_input: { command } };
121
+ if (agentId !== undefined) p.agent_id = agentId;
122
+ if (agentType !== undefined) p.agent_type = agentType;
123
+ if (cwd !== undefined) p.cwd = cwd;
124
+ return JSON.stringify(p);
125
+ }
126
+
127
+ function testControlesAC43() {
128
+ console.log('\n-- AC43: os 7 controles --');
129
+
130
+ // 1. pc-devops + git push → allow
131
+ assert(
132
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git push origin main' })).decision === null,
133
+ 'Controle 1: pc-devops + git push → allow, sem output',
134
+ );
135
+
136
+ // 2. pc-devops + gh pr create → allow
137
+ assert(
138
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh pr create --title x --body y' })).decision === null,
139
+ 'Controle 2: pc-devops + gh pr create → allow, sem output',
140
+ );
141
+
142
+ // 3. pc-devops + gh pr merge → ask (nunca automático, nem para devops)
143
+ assert(
144
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh pr merge 42' })).decision === 'ask',
145
+ 'Controle 3: pc-devops + gh pr merge → ask (3º nível D4)',
146
+ );
147
+
148
+ // 4. pc-dev + git push → ask (papel errado)
149
+ assert(
150
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git push' })).decision === 'ask',
151
+ 'Controle 4: pc-dev + git push → ask (papel errado)',
152
+ );
153
+
154
+ // 5. force-push com pc-devops → deny (único deny, incondicional)
155
+ const forcePush = ['git', 'push', '--for' + 'ce'].join(' '); // evita literal "--force" no fonte do teste
156
+ assert(
157
+ runHook(payloadCmd({ agentType: 'pc-devops', command: forcePush })).decision === 'deny',
158
+ 'Controle 5: force-push com pc-devops → deny (nenhuma identidade destrava force-push)',
159
+ );
160
+ assert(
161
+ runHook(payloadCmd({ agentType: 'pc-dev', command: forcePush })).decision === 'deny',
162
+ 'Controle 5b: force-push com pc-dev → deny (mesma regra, qualquer papel)',
163
+ );
164
+
165
+ // 6. autoproclamação — env var no processo do hook E prefixo inline no comando —
166
+ // NÃO altera o resultado (M-1: fallback removido)
167
+ const autoproclamacaoPayload = payloadCmd({ agentType: 'pc-dev', command: 'PC_ACTIVE_AGENT=devops git push' });
168
+ const envComAutoproclamacao = { ...process.env, PC_ACTIVE_AGENT: 'pc-devops', CLAUDE_AGENT_NAME: 'pc-devops', CLAUDE_CODE_AGENT: 'pc-devops' };
169
+ assert(
170
+ runHook(autoproclamacaoPayload, envComAutoproclamacao).decision === 'ask',
171
+ 'Controle 6: autoproclamação (env var do processo + prefixo inline no comando) NÃO destrava — ask (prova M-1 na prática)',
172
+ );
173
+
174
+ // 7. agent_id ausente (main thread) → ask
175
+ assert(
176
+ runHook(JSON.stringify({ tool_input: { command: 'git push' } })).decision === 'ask',
177
+ 'Controle 7: agent_id ausente (main thread) → ask — preserva a única rede que sobra ao dono',
178
+ );
179
+
180
+ // Sanidade adicional: comando não-remoto não dispara nada
181
+ assert(
182
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'ls -la' })).decision === null,
183
+ 'Sanidade: comando Bash não-remoto → silêncio total',
184
+ );
185
+ }
186
+
187
+ function testFixRoundF1() {
188
+ console.log('\n-- Fix round F1: bypass de parser reproduzido pelo QA + variações --');
189
+
190
+ // 8. Flag curta combinada (-uf): força embutida, não isolada — deny, mesmo
191
+ // para pc-devops. Bypass EXATO reproduzido pelo gate FAIL (F1, caso 1).
192
+ assert(
193
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git push -uf origin main' })).decision === 'deny',
194
+ 'Controle 8: `git push -uf origin main` (pc-devops) → deny (flag curta combinada é force-push)',
195
+ );
196
+ assert(
197
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git push -fu origin main' })).decision === 'deny',
198
+ 'Controle 8b: `git push -fu origin main` (ordem invertida do cluster) → deny',
199
+ );
200
+
201
+ // 9. Opção global do git (-c) antes do subcomando, com --force explícito —
202
+ // deny, mesmo para pc-devops. Bypass EXATO do F1, caso 2.
203
+ assert(
204
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git -c foo.bar=baz push --force origin main' })).decision === 'deny',
205
+ 'Controle 9: `git -c foo.bar=baz push --force origin main` (pc-devops) → deny (global opt não esconde --force)',
206
+ );
207
+
208
+ // 10. Mesma opção global, SEM --force, com papel errado — ask (não mais
209
+ // silêncio). Bypass EXATO do F1, caso 3 (o mais grave: bypassava TODO
210
+ // o mecanismo, não só o force-push).
211
+ assert(
212
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git -c foo.bar=baz push origin main' })).decision === 'ask',
213
+ 'Controle 10: `git -c foo.bar=baz push origin main` (pc-dev) → ask (opção global não contorna a matriz de papel)',
214
+ );
215
+
216
+ // 11. Variações adicionais de opção global (-C <path>, --git-dir=<path>) —
217
+ // mesma régua: o subcomando push continua reconhecido.
218
+ assert(
219
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git -C /tmp/algum-repo push origin main' })).decision === 'ask',
220
+ 'Controle 11a: `git -C <path> push` (pc-dev) → ask (opção global de valor separado)',
221
+ );
222
+ assert(
223
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git --git-dir=/tmp/algum-repo/.git push origin main' })).decision === 'ask',
224
+ 'Controle 11b: `git --git-dir=<path> push` (pc-dev) → ask (opção global inline)',
225
+ );
226
+ assert(
227
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git -C /tmp/algum-repo push --force origin main' })).decision === 'deny',
228
+ 'Controle 11c: `git -C <path> push --force` (pc-devops) → deny (global opt + force juntas)',
229
+ );
230
+
231
+ // 12. Comando encadeado (`&&`): a operação governada está no 2º segmento —
232
+ // precisa ser tratada, não silenciada por vir depois de um comando
233
+ // inofensivo.
234
+ assert(
235
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'echo preparando publicação && git push --force origin main' })).decision === 'deny',
236
+ 'Controle 12: comando encadeado com `&&`, force-push no 2º segmento → deny',
237
+ );
238
+ assert(
239
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'npm test; git push origin main' })).decision === 'ask',
240
+ 'Controle 12b: comando encadeado com `;`, push no 2º segmento (pc-dev) → ask',
241
+ );
242
+
243
+ // 13. `git` invocado por caminho absoluto/relativo — continua reconhecido
244
+ // (o token termina em "/git", não é só o literal "git").
245
+ assert(
246
+ runHook(payloadCmd({ agentType: 'pc-dev', command: '/usr/bin/git push origin main' })).decision === 'ask',
247
+ 'Controle 13: `/usr/bin/git push` (pc-dev) → ask (caminho absoluto não esconde o subcomando)',
248
+ );
249
+
250
+ // Prova final exigida pelo briefing: pc-devops NÃO consegue force-push por
251
+ // NENHUMA das variações acima.
252
+ const devopsForceAttempts = [
253
+ 'git push -uf origin main',
254
+ 'git push -fu origin main',
255
+ 'git -c foo.bar=baz push --force origin main',
256
+ 'git -C /tmp/algum-repo push --force origin main',
257
+ 'git --git-dir=/tmp/x/.git push --force origin main',
258
+ 'echo x && git push --force origin main',
259
+ '/usr/bin/git push --force origin main',
260
+ ];
261
+ const todasNegadas = devopsForceAttempts.every(
262
+ (command) => runHook(payloadCmd({ agentType: 'pc-devops', command })).decision === 'deny',
263
+ );
264
+ assert(
265
+ todasNegadas,
266
+ `Prova final: pc-devops NÃO force-pusha por nenhuma das ${devopsForceAttempts.length} variações testadas (flag combinada, opção global -c/-C/--git-dir=, comando encadeado, caminho absoluto)`,
267
+ );
268
+ }
269
+
270
+ function initTmpGitRepo(prefix) {
271
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
272
+ spawnSync('git', ['init', '-q'], { cwd: dir });
273
+ spawnSync('git', ['config', 'user.email', 't@example.com'], { cwd: dir });
274
+ spawnSync('git', ['config', 'user.name', 'tester'], { cwd: dir });
275
+ return dir;
276
+ }
277
+
278
+ function testFixRoundF3() {
279
+ console.log('\n-- Fix round F3: alias de git (o texto do comando não denuncia sozinho) --');
280
+
281
+ // Fonte 1 (mais barata): alias definido INLINE no próprio comando, via
282
+ // `-c alias.p=push`. Nenhum subprocesso de `git config` é necessário.
283
+ assert(
284
+ runHook(payloadCmd({
285
+ agentType: 'pc-devops',
286
+ command: 'git -c alias.p=push p --force origin main',
287
+ })).decision === 'deny',
288
+ 'Caso 1: `git -c alias.p=push p --force origin main` (pc-devops) → deny (alias inline resolve para force-push)',
289
+ );
290
+
291
+ // Fonte 2: `git config alias.z push` num segmento ANTERIOR da mesma
292
+ // cadeia, usado no segmento seguinte — resolvido por texto, sem tocar a
293
+ // config real do disco.
294
+ assert(
295
+ runHook(payloadCmd({
296
+ agentType: 'pc-dev',
297
+ command: 'git config alias.z push; git z origin main',
298
+ })).decision === 'ask',
299
+ 'Caso 2: `git config alias.z push; git z origin main` (pc-dev) → ask (alias definido na própria cadeia)',
300
+ );
301
+ assert(
302
+ runHook(payloadCmd({
303
+ agentType: 'pc-devops',
304
+ command: 'git config alias.z push; git z origin main',
305
+ })).decision === null,
306
+ 'Caso 2b: mesma cadeia, pc-devops → allow, sem output (push comum via alias)',
307
+ );
308
+
309
+ // Fonte 3 (mais cara, exige subprocesso): alias JÁ CONFIGURADO no
310
+ // repositório — nada no TEXTO do comando denuncia isso. É o caso que o
311
+ // QA nomeou como o que importa: "o texto do comando não determina a
312
+ // operação". Repositório git temporário real, alias real.
313
+ const repo = initTmpGitRepo('egpa-alias-');
314
+ try {
315
+ spawnSync('git', ['config', 'alias.z', 'push'], { cwd: repo });
316
+ spawnSync('git', ['config', 'alias.fp', 'push --force'], { cwd: repo });
317
+ spawnSync('git', ['config', 'alias.shellforce', '!git push --force'], { cwd: repo });
318
+
319
+ assert(
320
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git z origin main', cwd: repo })).decision === 'ask',
321
+ 'Caso 3: alias `z=push` JÁ CONFIGURADO no repo (nada no texto denuncia), pc-dev → ask',
322
+ );
323
+ assert(
324
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git z origin main', cwd: repo })).decision === null,
325
+ 'Caso 3b: mesmo alias pré-configurado, pc-devops → allow, sem output',
326
+ );
327
+ assert(
328
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git fp origin main', cwd: repo })).decision === 'deny',
329
+ 'Caso 3c: alias `fp="push --force"` pré-configurado, pc-devops → deny (nenhuma identidade destrava)',
330
+ );
331
+ assert(
332
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git shellforce', cwd: repo })).decision === 'deny',
333
+ 'Caso 3d: alias de SHELL `shellforce="!git push --force"` pré-configurado, pc-devops → deny',
334
+ );
335
+
336
+ // Controle negativo: subcomando desconhecido que NÃO é alias nenhum —
337
+ // a consulta resolve com segurança "não existe" e o hook fica em
338
+ // silêncio (nunca `ask` por via das dúvidas quando já se sabe a resposta).
339
+ assert(
340
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git totalmente-inexistente origin main', cwd: repo })).decision === null,
341
+ 'Controle negativo: subcomando desconhecido, CONCLUSIVAMENTE não é alias → silêncio (não vira ask por precaução vazia)',
342
+ );
343
+ assert(
344
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git status', cwd: repo })).decision === null,
345
+ 'Controle negativo: subcomando REAL do git (`status`) nunca aciona a máquina de alias → silêncio',
346
+ );
347
+ } finally {
348
+ fs.rmSync(repo, { recursive: true, force: true });
349
+ }
350
+
351
+ // Controle "não deu para confirmar com segurança": `git` inacessível
352
+ // (PATH quebrado) — a consulta ao alias falha na origem, não é "não é
353
+ // alias". Resultado tem que ser `ask`, nunca silêncio nem crash.
354
+ const semGitNoPath = runHookAtPath(HOOK_PATH, payloadCmd({
355
+ agentType: 'pc-devops',
356
+ command: 'git z origin main',
357
+ cwd: '/tmp',
358
+ }), { PATH: '/caminho/que/nao/existe' });
359
+ assert(
360
+ semGitNoPath.decision === 'ask',
361
+ 'Controle: `git` inacessível (PATH quebrado) durante a resolução de alias → ask, nunca silêncio nem crash (mesmo para pc-devops)',
362
+ );
363
+ }
364
+
365
+ // Configura uma cadeia de `n` aliases no repositório `repo`: d1 → d2 → ...
366
+ // → dn → `finalBody` (ex.: "push --force"). `git d1 ...` dispara n níveis
367
+ // de resolução até chegar no corpo final.
368
+ function buildAliasChain(repo, n, finalBody) {
369
+ spawnSync('git', ['config', `alias.d${n}`, finalBody], { cwd: repo });
370
+ for (let i = n - 1; i >= 1; i--) {
371
+ spawnSync('git', ['config', `alias.d${i}`, `d${i + 1}`], { cwd: repo });
372
+ }
373
+ }
374
+
375
+ function testFixRoundF4() {
376
+ console.log('\n-- Fix round F4: guarda de profundidade da resolução de alias (não sei ≠ sei que não) --');
377
+
378
+ // Reprodução EXATA do achado do QA: cadeia de 5 aliases terminando em
379
+ // force-push, com um repositório git real. Com o cap novo (mais folgado
380
+ // que o antigo ">3"), esta cadeia é curta o bastante para resolver POR
381
+ // COMPLETO — a resposta correta é `deny`, não apenas `ask`: o hook SABE
382
+ // que é force-push, não precisa admitir dúvida aqui.
383
+ const repo5 = initTmpGitRepo('egpa-chain5-');
384
+ try {
385
+ buildAliasChain(repo5, 5, 'push --force');
386
+ assert(
387
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git d1 origin main', cwd: repo5 })).decision === 'deny',
388
+ 'Cadeia de 5 aliases terminando em force-push, pc-devops → deny (resolve por completo, ANTES: silêncio — o achado exato do QA)',
389
+ );
390
+ assert(
391
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git d1 origin main', cwd: repo5 })).decision === 'deny',
392
+ 'Mesma cadeia de 5, pc-dev → deny (force-push nega para qualquer papel)',
393
+ );
394
+ } finally {
395
+ fs.rmSync(repo5, { recursive: true, force: true });
396
+ }
397
+
398
+ // Cadeia BEM mais longa (20 aliases) — deliberadamente além de qualquer
399
+ // cap razoável de custo. O hook não tem obrigação de resolver isto por
400
+ // completo, mas TEM obrigação de nunca ficar em silêncio: "não consegui
401
+ // seguir a cadeia até o fim" tem que virar `ask`, nunca allow.
402
+ const repo20 = initTmpGitRepo('egpa-chain20-');
403
+ try {
404
+ buildAliasChain(repo20, 20, 'push --force');
405
+ assert(
406
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'git d1 origin main', cwd: repo20 })).decision === 'ask',
407
+ 'Cadeia de 20 aliases terminando em force-push, pc-devops → ask (excede o limite de profundidade — NUNCA silêncio, era o bug do F4)',
408
+ );
409
+ assert(
410
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git d1 origin main', cwd: repo20 })).decision === 'ask',
411
+ 'Mesma cadeia de 20, pc-dev → ask (mesmo resultado, papel não muda a incerteza)',
412
+ );
413
+ } finally {
414
+ fs.rmSync(repo20, { recursive: true, force: true });
415
+ }
416
+
417
+ // Cadeia longa SEM força no fim — mesma exigência: exceder o limite
418
+ // nunca pode terminar em allow silencioso para o papel errado.
419
+ const repo20np = initTmpGitRepo('egpa-chain20np-');
420
+ try {
421
+ buildAliasChain(repo20np, 20, 'push');
422
+ assert(
423
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'git d1 origin main', cwd: repo20np })).decision === 'ask',
424
+ 'Cadeia de 20 aliases terminando em push comum (sem força), pc-dev → ask (não silêncio, mesmo sem força no fim)',
425
+ );
426
+ } finally {
427
+ fs.rmSync(repo20np, { recursive: true, force: true });
428
+ }
429
+
430
+ // Achado extra do sweep (mesma família): o fallback de JSON malformado
431
+ // usava o parser TOKENIZADO, que colapsa um JSON compacto (sem espaço
432
+ // fora de strings) num único token só — nunca detectava nada, mesmo com
433
+ // "git push --force" literal no texto. Reproduzido com JSON truncado
434
+ // (não fecha as chaves) contendo force-push de verdade.
435
+ const malformedWithForce = '{"agent_type":"pc-devops","tool_input":{"command":"git push --force"}';
436
+ assert(
437
+ runHook(malformedWithForce).decision === 'deny',
438
+ 'Achado do sweep: JSON malformado (truncado) com "git push --force" literal no texto → deny (ANTES: o parser tokenizado colapsava o JSON num token só e nunca suspeitava — silêncio)',
439
+ );
440
+ const malformedNoOp = '{"agent_type":"pc-dev","tool_input":{"command":"ls -la"}';
441
+ assert(
442
+ runHook(malformedNoOp).decision === null,
443
+ 'Controle: JSON malformado sem operação governada no texto → silêncio (o fix não virou paranoia genérica)',
444
+ );
445
+ const malformedWithAlias = '{"agent_type":"pc-devops","tool_input":{"command":"git config alias.z push; git z --force"}';
446
+ assert(
447
+ runHook(malformedWithAlias).decision === 'deny',
448
+ 'Achado do sweep: JSON malformado mencionando `git config ... alias` → deny (fail-closed proporcional cobre também o rastro textual de definição de alias)',
449
+ );
450
+
451
+ // Achado extra do sweep (mesma família): `main()` não tinha rede de
452
+ // segurança de topo — uma exceção não prevista em QUALQUER ponto do
453
+ // fluxo terminava o processo sem `hookSpecificOutput` nenhum (fail-open
454
+ // pior que `ask`). Forçado diretamente: `readStdin()` lança (stdin
455
+ // ilegível) — antes dessa correção, isso era engolido e virava "comando
456
+ // vazio" (silêncio); agora propaga e o catch-all de `main()` emite `ask`.
457
+ const hook = require(HOOK_PATH);
458
+ const originalReadFileSync = fs.readFileSync;
459
+ fs.readFileSync = function patchedReadFileSync(fd, ...args) {
460
+ if (fd === 0) throw new Error('SIMULATED_STDIN_FAILURE (teste)');
461
+ return originalReadFileSync.call(fs, fd, ...args);
462
+ };
463
+ let captured = '';
464
+ const originalWrite = process.stdout.write.bind(process.stdout);
465
+ process.stdout.write = (chunk) => {
466
+ captured += chunk;
467
+ return true;
468
+ };
469
+ try {
470
+ hook.main();
471
+ } finally {
472
+ fs.readFileSync = originalReadFileSync;
473
+ process.stdout.write = originalWrite;
474
+ }
475
+ let decision = null;
476
+ try {
477
+ decision = JSON.parse(captured)?.hookSpecificOutput?.permissionDecision || null;
478
+ } catch {
479
+ decision = 'UNPARSEABLE_OUTPUT';
480
+ }
481
+ assert(
482
+ decision === 'ask',
483
+ 'Achado do sweep: `readStdin()` lançando (stdin ilegível) → `main()` emite ask via catch-all de topo, nunca crash silencioso sem decisão nenhuma',
484
+ );
485
+ }
486
+
487
+ // Cria um `GH_CONFIG_DIR` temporário e isolado (o `gh` NUNCA usa `cwd` para
488
+ // resolver aliases — é config global do usuário, ao contrário do `git`,
489
+ // que é por repositório; isolamos via env var para não depender/poluir a
490
+ // config real da máquina que roda o teste).
491
+ function initTmpGhConfigDir(prefix) {
492
+ return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
493
+ }
494
+
495
+ function ghEnv(ghConfigDir, extra) {
496
+ return { ...process.env, GH_CONFIG_DIR: ghConfigDir, ...(extra || {}) };
497
+ }
498
+
499
+ function ghAliasSet(ghConfigDir, args) {
500
+ spawnSync('gh', ['alias', 'set', ...args, '--clobber'], { env: ghEnv(ghConfigDir) });
501
+ }
502
+
503
+ function testFixRoundF5() {
504
+ console.log('\n-- Fix round F5: alias NATIVO do gh (o branch gh não tinha NENHUMA resolução) --');
505
+
506
+ // Reprodução EXATA dos 2 casos do QA.
507
+ const ghDirPrc = initTmpGhConfigDir('egpa-gh-prc-');
508
+ try {
509
+ ghAliasSet(ghDirPrc, ['prc', 'pr create']);
510
+ assert(
511
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'gh prc --title x' }), ghEnv(ghDirPrc)).decision === 'ask',
512
+ 'Caso 1 (QA): `gh prc --title x` (alias de `pr create`), pc-dev → ask (ANTES: silêncio)',
513
+ );
514
+ } finally {
515
+ fs.rmSync(ghDirPrc, { recursive: true, force: true });
516
+ }
517
+
518
+ const ghDirQm = initTmpGhConfigDir('egpa-gh-qm-');
519
+ try {
520
+ ghAliasSet(ghDirQm, ['qm', 'pr merge --auto']);
521
+ assert(
522
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh qm 5 --auto' }), ghEnv(ghDirQm)).decision === 'ask',
523
+ 'Caso 2 (QA, o que importa): `gh qm 5 --auto` (alias de `pr merge`), pc-devops → ask — ANTES furava a garantia mais repetida da story (merge sempre pede aprovação, mesmo p/ devops)',
524
+ );
525
+ } finally {
526
+ fs.rmSync(ghDirQm, { recursive: true, force: true });
527
+ }
528
+
529
+ // Controle: literal continua ask (não regrediu).
530
+ assert(
531
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh pr merge 5' })).decision === 'ask',
532
+ 'Controle: `gh pr merge 5` literal, pc-devops → ask (não regrediu)',
533
+ );
534
+
535
+ // Alias de shell (prefixo "!", igual ao git) e via flag --shell (sem "!"
536
+ // explícito no valor — o próprio `gh` normaliza ao gravar, e a fonte de
537
+ // sessão replica essa normalização).
538
+ const ghDirShell = initTmpGhConfigDir('egpa-gh-shell-');
539
+ try {
540
+ ghAliasSet(ghDirShell, ['shellmerge', '!gh pr merge --auto']);
541
+ assert(
542
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh shellmerge' }), ghEnv(ghDirShell)).decision === 'ask',
543
+ 'Alias de SHELL `shellmerge="!gh pr merge --auto"` pré-configurado, pc-devops → ask',
544
+ );
545
+ } finally {
546
+ fs.rmSync(ghDirShell, { recursive: true, force: true });
547
+ }
548
+
549
+ const ghDirShellFlag = initTmpGhConfigDir('egpa-gh-shellflag-');
550
+ try {
551
+ ghAliasSet(ghDirShellFlag, ['--shell', 'shellflag2', 'gh pr merge --auto']);
552
+ assert(
553
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh shellflag2' }), ghEnv(ghDirShellFlag)).decision === 'ask',
554
+ 'Alias criado via `--shell` (sem `!` explícito no comando de set — o `gh` normaliza), pc-devops → ask',
555
+ );
556
+ } finally {
557
+ fs.rmSync(ghDirShellFlag, { recursive: true, force: true });
558
+ }
559
+
560
+ // Fonte barata: alias definido num segmento ANTERIOR da mesma cadeia
561
+ // (`gh alias set` + uso, sem tocar a config real do disco).
562
+ const ghDirEmpty = initTmpGhConfigDir('egpa-gh-session-');
563
+ try {
564
+ assert(
565
+ runHook(
566
+ payloadCmd({ agentType: 'pc-devops', command: "gh alias set qm2 'pr merge --auto'; gh qm2 5" }),
567
+ ghEnv(ghDirEmpty),
568
+ ).decision === 'ask',
569
+ 'Alias definido na PRÓPRIA cadeia (`gh alias set qm2 ...; gh qm2 5`), pc-devops → ask (fonte barata, sem 2ª consulta ao disco)',
570
+ );
571
+ } finally {
572
+ fs.rmSync(ghDirEmpty, { recursive: true, force: true });
573
+ }
574
+
575
+ // Cadeia de aliases (alias que resolve para outro alias) — o `gh` real
576
+ // não encadeia (medido: `gh alias set b a` seguido de `gh b` falha com
577
+ // "unknown command" se `a` também for só um alias), mas este hook é MAIS
578
+ // cauteloso que o `gh`, não menos: resolve através da cadeia via a mesma
579
+ // recursão/guarda de profundidade do branch `git`.
580
+ const ghDirChain = initTmpGhConfigDir('egpa-gh-chain-');
581
+ try {
582
+ ghAliasSet(ghDirChain, ['level2', 'pr merge --auto']);
583
+ ghAliasSet(ghDirChain, ['level1', 'level2']);
584
+ assert(
585
+ runHook(payloadCmd({ agentType: 'pc-devops', command: 'gh level1' }), ghEnv(ghDirChain)).decision === 'ask',
586
+ 'Cadeia de 2 aliases (`level1` → `level2` → `pr merge --auto`), pc-devops → ask',
587
+ );
588
+ } finally {
589
+ fs.rmSync(ghDirChain, { recursive: true, force: true });
590
+ }
591
+
592
+ // Controles negativos: subcomando desconhecido que NÃO é alias, e
593
+ // subcomando REAL do gh — nenhum dos dois deve acionar a máquina de
594
+ // alias nem virar `ask` por precaução vazia.
595
+ const ghDirNoAlias = initTmpGhConfigDir('egpa-gh-noalias-');
596
+ try {
597
+ assert(
598
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'gh totalmente-inexistente' }), ghEnv(ghDirNoAlias)).decision === null,
599
+ 'Controle negativo: subcomando de gh desconhecido, CONCLUSIVAMENTE não é alias → silêncio',
600
+ );
601
+ } finally {
602
+ fs.rmSync(ghDirNoAlias, { recursive: true, force: true });
603
+ }
604
+ assert(
605
+ runHook(payloadCmd({ agentType: 'pc-dev', command: 'gh issue list' })).decision === null,
606
+ 'Controle negativo: subcomando REAL do gh (`issue`) nunca aciona a máquina de alias → silêncio',
607
+ );
608
+
609
+ // Controle: `gh` inacessível (PATH quebrado) durante a resolução → ask,
610
+ // nunca silêncio nem crash — mesma régua do branch `git`.
611
+ const semGhNoPath = runHookAtPath(HOOK_PATH, payloadCmd({
612
+ agentType: 'pc-devops',
613
+ command: 'gh algumaliasqualquer',
614
+ }), { PATH: '/caminho/que/nao/existe' });
615
+ assert(
616
+ semGhNoPath.decision === 'ask',
617
+ 'Controle: `gh` inacessível (PATH quebrado) → ask, nunca silêncio nem crash (mesmo para pc-devops)',
618
+ );
619
+
620
+ // Prova final: `gh pr merge` — a garantia mais repetida da story — não é
621
+ // alcançável em silêncio nem em `allow` por NENHUMA das formas testadas,
622
+ // nem mesmo para pc-devops.
623
+ const ghDirFinal = initTmpGhConfigDir('egpa-gh-final-');
624
+ ghAliasSet(ghDirFinal, ['qmFinal', 'pr merge --auto']);
625
+ ghAliasSet(ghDirFinal, ['shellFinal', '!gh pr merge --auto']);
626
+ try {
627
+ const attempts = [
628
+ ['gh pr merge 5', {}],
629
+ ['gh qmFinal 5', ghEnv(ghDirFinal)],
630
+ ['gh shellFinal', ghEnv(ghDirFinal)],
631
+ ];
632
+ const todasAsk = attempts.every(
633
+ ([command, env]) => runHook(payloadCmd({ agentType: 'pc-devops', command }), env).decision === 'ask',
634
+ );
635
+ assert(
636
+ todasAsk,
637
+ `Prova final: \`gh pr merge\` inalcançável sem \`ask\` por nenhuma das ${attempts.length} formas testadas (literal, alias comum, alias de shell), mesmo para pc-devops`,
638
+ );
639
+ } finally {
640
+ fs.rmSync(ghDirFinal, { recursive: true, force: true });
641
+ }
642
+ }
643
+
644
+ function buildMutant(tmpDir) {
645
+ const source = fs.readFileSync(HOOK_PATH, 'utf8');
646
+ const target = `function isDevOpsAgent(input) {\n return typeof input?.agent_type === 'string' && input.agent_type === DEVOPS_AGENT_TYPE;\n}`;
647
+ const mutated = `function isDevOpsAgent(input) {\n return true; // MUTANTE — checagem de identidade removida (AC43)\n}`;
648
+ if (!source.includes(target)) {
649
+ throw new Error('Instrumento de mutação desalinhado com o fonte real do hook — AC43 não é prova válida.');
650
+ }
651
+ const mutantSource = source.replace(target, mutated);
652
+ const mutantPath = path.join(tmpDir, 'enforce-git-push-authority.mutant.cjs');
653
+ fs.writeFileSync(mutantPath, mutantSource);
654
+ return mutantPath;
655
+ }
656
+
657
+ function testMutacao() {
658
+ console.log('\n-- AC43: instrumento de mutação (a checagem de identidade não pode destravar force-push) --');
659
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'egpa-mutant-'));
660
+ try {
661
+ const mutantPath = buildMutant(tmpDir);
662
+ const forcePush = ['git', 'push', '--for' + 'ce'].join(' ');
663
+ const denyPayload = payloadCmd({ agentType: 'pc-dev', command: forcePush });
664
+
665
+ assert(runHook(denyPayload).decision === 'deny', 'controle: hook REAL nega force-push (pré-condição da prova)');
666
+ assert(
667
+ runHookAtPath(mutantPath, denyPayload).decision === 'deny',
668
+ 'a mutante (isDevOpsAgent sempre true) NÃO destrava force-push — a checagem de force-push é independente da de identidade (defesa em profundidade confirmada)',
669
+ );
670
+
671
+ // A mutante deveria, no entanto, mudar o resultado do controle 4 (não-force):
672
+ // prova que a mutação realmente afeta a checagem de identidade (mutante viva).
673
+ const nonForcePayload = payloadCmd({ agentType: 'pc-dev', command: 'git push' });
674
+ assert(runHook(nonForcePayload).decision === 'ask', 'controle: hook REAL pergunta pc-dev + push (não-force) (pré-condição)');
675
+ assert(
676
+ runHookAtPath(mutantPath, nonForcePayload).decision === null,
677
+ 'a mutante PERMITE pc-dev fazer push não-force — prova que a checagem de identidade real é o que trava',
678
+ );
679
+ } finally {
680
+ fs.rmSync(tmpDir, { recursive: true, force: true });
681
+ }
682
+ }
683
+
684
+ function main() {
685
+ console.log('POWER CLAUDE — suíte do hook enforce-git-push-authority.cjs (SF12.3, Bloco M, AC42-43 + fix rounds F1/F3/F4/F5)\n');
686
+ testControlesAC43();
687
+ testFixRoundF1();
688
+ testFixRoundF3();
689
+ testFixRoundF4();
690
+ testFixRoundF5();
691
+ testMutacao();
692
+
693
+ if (failures > 0) {
694
+ console.error(`\nSuíte FALHOU: ${failures} violação(ões).`);
695
+ process.exit(1);
696
+ }
697
+ console.log('\nSuíte verde.');
698
+ process.exit(0);
699
+ }
700
+
701
+ main();