@event4u/agent-config 16.2.0 → 16.4.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 (651) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +337 -234
  5. package/README.md +15 -13
  6. package/agents/templates/.ai-council.yml.example +26 -8
  7. package/dist/agent-src/commands/analyze/inbox.md +11 -8
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  9. package/dist/agent-src/commands/bug/fix.md +17 -2
  10. package/dist/agent-src/commands/bug/investigate.md +1 -1
  11. package/dist/agent-src/commands/commit/in-chunks.md +40 -31
  12. package/dist/agent-src/commands/commit.md +72 -33
  13. package/dist/agent-src/commands/condense.md +2 -8
  14. package/dist/agent-src/commands/council/debate.md +11 -7
  15. package/dist/agent-src/commands/council/default.md +10 -9
  16. package/dist/agent-src/commands/council.md +1 -1
  17. package/dist/agent-src/commands/estimate-ticket.md +1 -1
  18. package/dist/agent-src/commands/feature/explore.md +3 -3
  19. package/dist/agent-src/commands/feature/plan.md +2 -2
  20. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  21. package/dist/agent-src/commands/fix/ci.md +18 -4
  22. package/dist/agent-src/commands/fix/commit-messages.md +48 -23
  23. package/dist/agent-src/commands/fix/pr-comments.md +3 -6
  24. package/dist/agent-src/commands/implement-ticket.md +4 -4
  25. package/dist/agent-src/commands/jira-ticket.md +1 -1
  26. package/dist/agent-src/commands/memory/add.md +4 -16
  27. package/dist/agent-src/commands/memory/load.md +5 -6
  28. package/dist/agent-src/commands/memory/promote.md +3 -16
  29. package/dist/agent-src/commands/mission/upgrade.md +3 -2
  30. package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
  31. package/dist/agent-src/commands/optimize/deep.md +4 -2
  32. package/dist/agent-src/commands/optimize/rtk.md +4 -26
  33. package/dist/agent-src/commands/pr/create/description-only.md +18 -15
  34. package/dist/agent-src/commands/pr/create.md +42 -34
  35. package/dist/agent-src/commands/pr/merge.md +136 -9
  36. package/dist/agent-src/commands/prepare-for-review.md +13 -11
  37. package/dist/agent-src/commands/project/analyze.md +7 -35
  38. package/dist/agent-src/commands/refine-ticket.md +1 -1
  39. package/dist/agent-src/commands/review/changes.md +14 -19
  40. package/dist/agent-src/commands/roadmap/create.md +1 -1
  41. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  42. package/dist/agent-src/commands/roadmap/next.md +10 -1
  43. package/dist/agent-src/commands/roadmap/process-full.md +56 -42
  44. package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -0
  45. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  46. package/dist/agent-src/commands/roadmap.md +18 -4
  47. package/dist/agent-src/commands/tests/create.md +17 -3
  48. package/dist/agent-src/commands/tests/execute.md +22 -5
  49. package/dist/agent-src/commands/worktree/create.md +7 -3
  50. package/dist/agent-src/commands/worktree/verify.md +2 -1
  51. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  52. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  53. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
  54. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
  55. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
  56. package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
  57. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +7 -7
  58. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
  59. package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
  60. package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
  61. package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
  62. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
  63. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +99 -54
  64. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  65. package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
  66. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
  67. package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
  68. package/dist/agent-src/contexts/execution/toolchain-resolver.md +95 -7
  69. package/dist/agent-src/contexts/subagent-configuration.md +1 -1
  70. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  71. package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  72. package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  73. package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  74. package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
  75. package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  76. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  77. package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
  78. package/dist/agent-src/guidelines/php/git.md +15 -2
  79. package/dist/agent-src/rules/active-remediation.md +1 -2
  80. package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
  81. package/dist/agent-src/rules/architecture.md +0 -1
  82. package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
  83. package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
  84. package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
  85. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  86. package/dist/agent-src/rules/brand-consistency.md +0 -1
  87. package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
  88. package/dist/agent-src/rules/broken-access-control.md +1 -1
  89. package/dist/agent-src/rules/cli-output-handling.md +0 -1
  90. package/dist/agent-src/rules/code-comment-discipline.md +0 -1
  91. package/dist/agent-src/rules/code-provenance.md +0 -1
  92. package/dist/agent-src/rules/command-suggestion-policy.md +1 -1
  93. package/dist/agent-src/rules/commit-conventions.md +1 -2
  94. package/dist/agent-src/rules/communication-through-line.md +1 -2
  95. package/dist/agent-src/rules/content-quoting-floor.md +1 -2
  96. package/dist/agent-src/rules/context-hygiene.md +6 -6
  97. package/dist/agent-src/rules/copilot-routing.md +1 -2
  98. package/dist/agent-src/rules/council-availability.md +4 -4
  99. package/dist/agent-src/rules/cross-source-consistency.md +4 -6
  100. package/dist/agent-src/rules/decision-revisit-gate.md +17 -35
  101. package/dist/agent-src/rules/delegation-policy.md +2 -3
  102. package/dist/agent-src/rules/design-fidelity.md +12 -16
  103. package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
  104. package/dist/agent-src/rules/devcontainer-routing.md +1 -2
  105. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
  106. package/dist/agent-src/rules/docker-commands.md +0 -1
  107. package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
  108. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -1
  109. package/dist/agent-src/rules/domain-safety-pii.md +1 -1
  110. package/dist/agent-src/rules/domain-safety-retention.md +1 -1
  111. package/dist/agent-src/rules/downstream-changes.md +0 -1
  112. package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
  113. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  114. package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
  115. package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
  116. package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -2
  117. package/dist/agent-src/rules/finance-safety-floor.md +1 -1
  118. package/dist/agent-src/rules/fix-what-you-see.md +6 -32
  119. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
  120. package/dist/agent-src/rules/git-history-discipline.md +2 -2
  121. package/dist/agent-src/rules/guidelines.md +0 -1
  122. package/dist/agent-src/rules/history-discipline.md +0 -1
  123. package/dist/agent-src/rules/icon-consistency.md +0 -1
  124. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -1
  125. package/dist/agent-src/rules/improve-before-implement.md +0 -1
  126. package/dist/agent-src/rules/invite-challenge.md +0 -1
  127. package/dist/agent-src/rules/laravel-routing.md +1 -2
  128. package/dist/agent-src/rules/laravel-translations.md +0 -1
  129. package/dist/agent-src/rules/legal-safety-floor.md +3 -3
  130. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
  131. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
  132. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
  133. package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
  134. package/dist/agent-src/rules/media-governance-routing.md +13 -17
  135. package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
  136. package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
  137. package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
  138. package/dist/agent-src/rules/missing-tool-handling.md +0 -1
  139. package/dist/agent-src/rules/model-recommendation.md +0 -1
  140. package/dist/agent-src/rules/neighbour-precedence.md +64 -0
  141. package/dist/agent-src/rules/no-attribution-footers.md +0 -1
  142. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
  143. package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
  144. package/dist/agent-src/rules/no-roadmap-references.md +0 -1
  145. package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
  146. package/dist/agent-src/rules/onboarding-gate.md +0 -1
  147. package/dist/agent-src/rules/output-discipline.md +0 -1
  148. package/dist/agent-src/rules/package-ci-checks.md +0 -1
  149. package/dist/agent-src/rules/persona-governance.md +4 -5
  150. package/dist/agent-src/rules/php-coding.md +0 -1
  151. package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
  152. package/dist/agent-src/rules/preservation-guard.md +0 -1
  153. package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -4
  154. package/dist/agent-src/rules/question-not-instruction.md +1 -1
  155. package/dist/agent-src/rules/reviewer-awareness.md +0 -1
  156. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
  157. package/dist/agent-src/rules/roadmap-progress-sync.md +4 -7
  158. package/dist/agent-src/rules/role-mode-adherence.md +0 -1
  159. package/dist/agent-src/rules/rule-type-governance.md +0 -1
  160. package/dist/agent-src/rules/runtime-safety.md +1 -1
  161. package/dist/agent-src/rules/scale-discipline.md +0 -1
  162. package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
  163. package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
  164. package/dist/agent-src/rules/self-repair-loop.md +0 -1
  165. package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
  166. package/dist/agent-src/rules/session-canary.md +7 -62
  167. package/dist/agent-src/rules/settings-ask-protocol.md +6 -6
  168. package/dist/agent-src/rules/size-enforcement.md +0 -1
  169. package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
  170. package/dist/agent-src/rules/skill-quality.md +0 -1
  171. package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
  172. package/dist/agent-src/rules/source-confidentiality.md +1 -1
  173. package/dist/agent-src/rules/source-discovery-gate.md +0 -1
  174. package/dist/agent-src/rules/source-of-truth.md +0 -1
  175. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
  176. package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
  177. package/dist/agent-src/rules/symfony-routing.md +1 -2
  178. package/dist/agent-src/rules/think-before-action.md +0 -1
  179. package/dist/agent-src/rules/token-budget-discipline.md +12 -46
  180. package/dist/agent-src/rules/token-efficiency.md +0 -1
  181. package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
  182. package/dist/agent-src/rules/tool-safety.md +1 -1
  183. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  184. package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
  185. package/dist/agent-src/rules/upstream-proposal.md +0 -1
  186. package/dist/agent-src/rules/user-interaction.md +0 -1
  187. package/dist/agent-src/rules/user-interrupt-priority.md +1 -1
  188. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  189. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  190. package/dist/agent-src/scripts/gate_budget.ts +47 -23
  191. package/dist/agent-src/scripts/gate_execute.ts +12 -4
  192. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  193. package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
  194. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
  195. package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
  196. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  197. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  198. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  199. package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
  200. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  201. package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
  202. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
  203. package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
  204. package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
  205. package/dist/agent-src/skills/database/data/manifest.json +2 -2
  206. package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
  207. package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
  208. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  209. package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
  210. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
  211. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  212. package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
  213. package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
  214. package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
  215. package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
  216. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  217. package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
  218. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  219. package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
  220. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  221. package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
  222. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  223. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  224. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  225. package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
  226. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  227. package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
  228. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
  229. package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
  230. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  231. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
  232. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  233. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  234. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  235. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  236. package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
  237. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  238. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
  239. package/dist/agent-src/templates/AGENTS.md +1 -1
  240. package/dist/agent-src/templates/agent-settings.md +46 -129
  241. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
  242. package/dist/agent-src/templates/command.md +2 -2
  243. package/dist/agent-src/templates/roadmaps.md +6 -1
  244. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  245. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  246. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  247. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  248. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  249. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  250. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  251. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  252. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  253. package/dist/agent-src/templates/skill.md +1 -1
  254. package/dist/cli/registry.js +2 -1
  255. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  256. package/dist/cli-delegate/check_update_banner.js +207 -120
  257. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  258. package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
  259. package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
  260. package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
  261. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  262. package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
  263. package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
  264. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  265. package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
  266. package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
  267. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  268. package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
  269. package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
  270. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
  271. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
  272. package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
  273. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  274. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  275. package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
  276. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
  277. package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
  278. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
  279. package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
  280. package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
  281. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  282. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  283. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
  284. package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
  285. package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
  286. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  287. package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
  288. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
  289. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  290. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  291. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  292. package/dist/cli-delegate/cmd_analyze_session.js +223 -43
  293. package/dist/cli-delegate/cmd_brand_status.js +3 -2
  294. package/dist/cli-delegate/cmd_conformance.js +1252 -249
  295. package/dist/cli-delegate/cmd_converge.js +3 -3
  296. package/dist/cli-delegate/cmd_doctor.js +19 -9
  297. package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
  298. package/dist/cli-delegate/cmd_explain.js +45 -21
  299. package/dist/cli-delegate/cmd_export.js +13 -10
  300. package/dist/cli-delegate/cmd_fleet.js +3 -3
  301. package/dist/cli-delegate/cmd_git_convention.js +1851 -0
  302. package/dist/cli-delegate/cmd_handoff.js +44 -22
  303. package/dist/cli-delegate/cmd_mcp_available.js +3 -2
  304. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  305. package/dist/cli-delegate/cmd_migrate.js +3 -3
  306. package/dist/cli-delegate/cmd_packs_active.js +5 -4
  307. package/dist/cli-delegate/cmd_preflight.js +6 -6
  308. package/dist/cli-delegate/cmd_prune.js +14 -11
  309. package/dist/cli-delegate/cmd_refresh.js +2 -1
  310. package/dist/cli-delegate/cmd_route_audit.js +10 -8
  311. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  312. package/dist/cli-delegate/cmd_session_recycle.js +10 -7
  313. package/dist/cli-delegate/cmd_settings_check.js +54 -7
  314. package/dist/cli-delegate/cmd_settings_get.js +18 -8
  315. package/dist/cli-delegate/cmd_settings_migrate.js +13 -10
  316. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  317. package/dist/cli-delegate/cmd_sync.js +14 -11
  318. package/dist/cli-delegate/cmd_uninstall.js +15 -12
  319. package/dist/cli-delegate/cmd_update.js +7 -7
  320. package/dist/cli-delegate/cmd_upgrade.js +9 -8
  321. package/dist/cli-delegate/cmd_validate.js +15 -12
  322. package/dist/cli-delegate/cmd_versions.js +5 -5
  323. package/dist/cli-delegate/deferral_carry.js +16 -0
  324. package/dist/cli-delegate/gate_budget.js +1 -1
  325. package/dist/cli-delegate/gate_execute.js +3 -3
  326. package/dist/cli-delegate/roadmap_gates.js +6 -6
  327. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  328. package/dist/discovery/deprecation-report.md +1 -1
  329. package/dist/discovery/discovery-manifest.json +232 -141
  330. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  331. package/dist/discovery/discovery-manifest.summary.md +3 -3
  332. package/dist/discovery/dormancy-report.md +1 -1
  333. package/dist/discovery/orphan-report.md +1 -1
  334. package/dist/discovery/packs.json +26 -9
  335. package/dist/discovery/trust-report.md +3 -3
  336. package/dist/discovery/workspaces.json +7 -4
  337. package/dist/hooks/dispatch.js +5655 -6623
  338. package/dist/hooks/dispatch.sha256 +1 -1
  339. package/dist/install/claudePathsPlan.js +47 -6
  340. package/dist/install/claudeRuleRewrite.js +7 -0
  341. package/dist/install/emit_host_rules_cli.js +39 -36
  342. package/dist/install/globalRuleScope.js +87 -0
  343. package/dist/install/host_rule_ownership.js +160 -0
  344. package/dist/install/install.mjs +2336 -1249
  345. package/dist/install/installThinLayer.js +289 -0
  346. package/dist/install/installedRuleLinks.js +262 -0
  347. package/dist/install/wizard-plan.js +24 -0
  348. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  349. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  350. package/dist/mcp/registry-manifest.json +5 -5
  351. package/dist/mcp/server.json +2 -2
  352. package/dist/mcp/server.mjs +36504 -30986
  353. package/dist/router.json +1 -1
  354. package/dist/scripts/_lib/agent_settings.js +26 -120
  355. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  356. package/dist/scripts/_lib/compile_time_toggles.js +12 -21
  357. package/dist/scripts/_lib/git_convention.js +232 -0
  358. package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
  359. package/dist/scripts/_lib/installed_layer.js +704 -0
  360. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  361. package/dist/scripts/_lib/package_root.js +68 -0
  362. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  363. package/dist/scripts/_lib/rule_injection.js +67 -10
  364. package/dist/scripts/_lib/rule_law_section.js +122 -0
  365. package/dist/scripts/_lib/settings_removed_keys.js +168 -0
  366. package/dist/scripts/_lib/thin_rules.js +535 -0
  367. package/dist/scripts/_lib/windsurf_render.js +60 -0
  368. package/dist/scripts/condense.js +14 -26
  369. package/dist/scripts/project_thin_rules.js +14 -294
  370. package/dist/server/app.js +2 -1
  371. package/dist/server/gitKeysGate.js +105 -0
  372. package/dist/server/routes/settings.js +67 -8
  373. package/dist/server/routes/wizard.js +4 -0
  374. package/dist/server/schemas/settings.js +29 -81
  375. package/dist/shared/skillRanking.js +103 -16
  376. package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
  377. package/dist/ui/index.html +1 -1
  378. package/docs/MIGRATION.md +72 -1
  379. package/docs/contracts/settings-classes.md +39 -67
  380. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  381. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  382. package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  383. package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  384. package/docs/guidelines/agent-infra/layered-settings.md +13 -1
  385. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  386. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  387. package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
  388. package/docs/guidelines/php/git.md +15 -2
  389. package/package.json +1 -1
  390. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  391. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  392. package/src/agent-src/scripts/gate_budget.ts +47 -23
  393. package/src/agent-src/scripts/gate_execute.ts +12 -4
  394. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  395. package/src/agent-src/templates/AGENTS.md +1 -1
  396. package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  397. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  398. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  399. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  400. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  401. package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  402. package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  403. package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  404. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  405. package/src/config/agent-settings.template.yml +141 -334
  406. package/src/config/agents-paths.yml +16 -0
  407. package/src/config/assurance-capability-registry.json +7 -3
  408. package/src/config/ci-local-parity.yml +3 -0
  409. package/src/config/continuity-surface.json +10 -3
  410. package/src/config/discovery/packs.yml +17 -1
  411. package/src/config/evaluator-budgets.json +23 -10
  412. package/src/config/gate-coverage.yml +138 -3
  413. package/src/config/gate-violation-baselines.json +64 -16
  414. package/src/config/gitignore-block.txt +19 -0
  415. package/src/config/hook-bundle-budget.json +25 -0
  416. package/src/config/hook-token-budget.json +6 -1
  417. package/src/config/host-instruction-limits.json +33 -0
  418. package/src/config/release-gate-locality.yml +2 -1
  419. package/src/config/routing-coverage-seed.json +67 -4
  420. package/src/config/rule-activation-census.json +7 -3
  421. package/src/config/rule-consequence-class.json +161 -0
  422. package/src/config/rule-law-ceilings.json +105 -0
  423. package/src/config/rule-obligations.json +833 -0
  424. package/src/install/claudePathsPlan.ts +46 -4
  425. package/src/install/claudeRuleRewrite.ts +10 -1
  426. package/src/install/emit_host_rules_cli.ts +43 -34
  427. package/src/install/globalRuleScope.ts +109 -0
  428. package/src/install/host_rule_ownership.ts +170 -0
  429. package/src/install/installThinLayer.ts +378 -0
  430. package/src/install/installedRuleLinks.ts +308 -0
  431. package/src/install/wizard-plan.ts +24 -0
  432. package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
  433. package/src/scripts/_cli/cmd_conformance.ts +13 -2
  434. package/src/scripts/_cli/cmd_doctor.ts +33 -81
  435. package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -0
  436. package/src/scripts/_cli/cmd_explain.ts +72 -14
  437. package/src/scripts/_cli/cmd_git_convention.ts +637 -0
  438. package/src/scripts/_cli/cmd_settings_check.ts +66 -8
  439. package/src/scripts/_cli/cmd_settings_get.ts +18 -3
  440. package/src/scripts/_cli/doctor_execution.ts +72 -1
  441. package/src/scripts/_dispatch.bash +53 -4
  442. package/src/scripts/_lib/agent_settings.ts +26 -124
  443. package/src/scripts/_lib/blocked_by_marker.ts +1 -1
  444. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  445. package/src/scripts/_lib/compile_time_toggles.ts +12 -21
  446. package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
  447. package/src/scripts/_lib/estate_offsets.ts +241 -0
  448. package/src/scripts/_lib/exemption_shape.ts +143 -0
  449. package/src/scripts/_lib/fnv.ts +23 -0
  450. package/src/scripts/_lib/foreign_scan.ts +106 -0
  451. package/src/scripts/_lib/git_base_ref.ts +92 -0
  452. package/src/scripts/_lib/git_convention.ts +291 -0
  453. package/src/scripts/_lib/git_convention_carrier.ts +389 -0
  454. package/src/scripts/_lib/git_convention_grammar.ts +246 -0
  455. package/src/scripts/_lib/git_convention_measure.ts +344 -0
  456. package/src/scripts/_lib/gitignore_block.ts +314 -0
  457. package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
  458. package/src/scripts/_lib/graph_feeder_record.ts +437 -0
  459. package/src/scripts/_lib/hook_settings.ts +73 -0
  460. package/src/scripts/_lib/host_capability.ts +40 -19
  461. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  462. package/src/scripts/_lib/installed_layer.ts +963 -0
  463. package/src/scripts/_lib/json_merge.ts +82 -0
  464. package/src/scripts/_lib/json_pointers.ts +93 -11
  465. package/src/scripts/_lib/knowledge_global.ts +14 -1
  466. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  467. package/src/scripts/_lib/machine_wake.ts +16 -3
  468. package/src/scripts/_lib/module_reach.ts +456 -0
  469. package/src/scripts/_lib/neighbour_census.ts +760 -0
  470. package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
  471. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  472. package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
  473. package/src/scripts/_lib/prompt_shape.ts +46 -0
  474. package/src/scripts/_lib/ratification_artifact.ts +220 -1
  475. package/src/scripts/_lib/ratification_subject.ts +92 -0
  476. package/src/scripts/_lib/release_findings_ingest.ts +15 -7
  477. package/src/scripts/_lib/release_highlights.ts +5 -0
  478. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  479. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  480. package/src/scripts/_lib/review_coverage.ts +134 -0
  481. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  482. package/src/scripts/_lib/rule_injection.ts +130 -10
  483. package/src/scripts/_lib/rule_law_section.ts +147 -0
  484. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  485. package/src/scripts/_lib/rule_obligations.ts +205 -0
  486. package/src/scripts/_lib/self_repair.ts +1 -1
  487. package/src/scripts/_lib/settings_removed_keys.ts +173 -0
  488. package/src/scripts/_lib/skill_origin.ts +254 -0
  489. package/src/scripts/_lib/structured_ask.ts +64 -21
  490. package/src/scripts/_lib/test_red_state.ts +5 -0
  491. package/src/scripts/_lib/thin_rules.ts +620 -0
  492. package/src/scripts/_lib/touched_file_quality.ts +570 -0
  493. package/src/scripts/_lib/turn_end_refusals.ts +77 -2
  494. package/src/scripts/_lib/windsurf_render.ts +63 -0
  495. package/src/scripts/_lib/worker_budget.ts +1 -1
  496. package/src/scripts/adr/regenerate_index.ts +16 -1
  497. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  498. package/src/scripts/ai_council/clients.ts +26 -57
  499. package/src/scripts/ai_council/config.ts +8 -8
  500. package/src/scripts/ai_council/modification_review.ts +100 -0
  501. package/src/scripts/ai_council/modification_review_questions.ts +90 -0
  502. package/src/scripts/ai_council/orchestrator.ts +5 -5
  503. package/src/scripts/ai_council/spend_gate.ts +18 -7
  504. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  505. package/src/scripts/ai_team/config.ts +11 -16
  506. package/src/scripts/audit_skill_overlap.ts +261 -0
  507. package/src/scripts/audit_user_type_axis.ts +20 -8
  508. package/src/scripts/before_complete_hook.ts +243 -12
  509. package/src/scripts/bench_ab_clone.ts +1 -2
  510. package/src/scripts/bench_concern_spawn_path.ts +129 -0
  511. package/src/scripts/bench_graph_feeder_latency.ts +584 -0
  512. package/src/scripts/bench_touched_file_quality.ts +238 -0
  513. package/src/scripts/check_agent_artifact_location.ts +36 -0
  514. package/src/scripts/check_branch_freshness.ts +147 -24
  515. package/src/scripts/check_enforcement_coverage.ts +130 -0
  516. package/src/scripts/check_enforcement_matrix.ts +377 -20
  517. package/src/scripts/check_estate_count.ts +130 -153
  518. package/src/scripts/check_evaluator_budgets.ts +4 -0
  519. package/src/scripts/check_finding_dispositions.ts +196 -21
  520. package/src/scripts/check_gate_coverage.ts +4 -3
  521. package/src/scripts/check_gate_reachability.ts +48 -1
  522. package/src/scripts/check_generator_sync.ts +48 -0
  523. package/src/scripts/check_hook_bundle_composition.ts +477 -0
  524. package/src/scripts/check_kernel_edit_ratified.ts +132 -14
  525. package/src/scripts/check_memory.ts +7 -1
  526. package/src/scripts/check_pack_size.ts +1 -1
  527. package/src/scripts/check_release_highlights.ts +10 -0
  528. package/src/scripts/check_release_manifest_content.ts +189 -0
  529. package/src/scripts/check_release_pr_shape.ts +38 -15
  530. package/src/scripts/check_standing_rule_delivery.ts +129 -13
  531. package/src/scripts/check_suppression_hygiene.ts +2 -5
  532. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  533. package/src/scripts/ci_settle.ts +42 -5
  534. package/src/scripts/classify_merge_risk.ts +320 -0
  535. package/src/scripts/closure_scan.ts +91 -1
  536. package/src/scripts/code_graph/detect.ts +127 -7
  537. package/src/scripts/code_graph/foreign.ts +194 -0
  538. package/src/scripts/code_graph/query.ts +217 -4
  539. package/src/scripts/code_graph/types.ts +26 -2
  540. package/src/scripts/code_graph/validate.ts +3 -1
  541. package/src/scripts/code_graph/verbs.ts +103 -0
  542. package/src/scripts/command_suggester/settings.ts +8 -108
  543. package/src/scripts/command_suggester/types.ts +4 -5
  544. package/src/scripts/compile_hook_manifest.ts +33 -1
  545. package/src/scripts/condense.ts +14 -27
  546. package/src/scripts/consumer_matrix.ts +102 -0
  547. package/src/scripts/context_hygiene_hook.ts +13 -23
  548. package/src/scripts/cost/preflight.mjs +12 -1
  549. package/src/scripts/council_cli.ts +7 -7
  550. package/src/scripts/dispatch_r2_reviewer.ts +24 -11
  551. package/src/scripts/estate_exemption_shape.ts +202 -0
  552. package/src/scripts/explain_run.ts +8 -13
  553. package/src/scripts/generate_git_convention_grammar.ts +127 -0
  554. package/src/scripts/generate_host_cost_table.ts +21 -4
  555. package/src/scripts/handoff_context_hook.ts +86 -37
  556. package/src/scripts/hook_manifest.json +1 -1
  557. package/src/scripts/hook_manifest.yaml +175 -15
  558. package/src/scripts/hooks/block_config_weakening.ts +115 -7
  559. package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
  560. package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
  561. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  562. package/src/scripts/hooks/concern_registry.ts +2 -0
  563. package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
  564. package/src/scripts/hooks/dispatch_hook.ts +52 -52
  565. package/src/scripts/hooks/evidence_independence.ts +12 -4
  566. package/src/scripts/hooks/exit_codes.ts +2 -1
  567. package/src/scripts/hooks/host_lowering.json +1 -1
  568. package/src/scripts/hooks/host_lowering.ts +19 -1
  569. package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
  570. package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
  571. package/src/scripts/hooks/payload_stub.ts +10 -1
  572. package/src/scripts/hooks/push_settle_hook.ts +4 -3
  573. package/src/scripts/hooks/rule_inject_hook.ts +795 -60
  574. package/src/scripts/hooks/state_io.ts +8 -4
  575. package/src/scripts/hooks/table_fingerprint.ts +21 -0
  576. package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
  577. package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
  578. package/src/scripts/hooks_status.ts +112 -0
  579. package/src/scripts/injection_scan_hook.ts +7 -0
  580. package/src/scripts/install-hooks.sh +42 -14
  581. package/src/scripts/install.ts +78 -215
  582. package/src/scripts/installed_layer_report.ts +103 -0
  583. package/src/scripts/language_mirror_hook.ts +15 -6
  584. package/src/scripts/learning_sidecar.ts +119 -8
  585. package/src/scripts/lint_blocker_citations.ts +198 -0
  586. package/src/scripts/lint_evidence_artifacts.ts +9 -0
  587. package/src/scripts/lint_hook_concern_budget.ts +10 -21
  588. package/src/scripts/lint_hook_manifest.ts +53 -0
  589. package/src/scripts/lint_major_migration_sections.ts +156 -26
  590. package/src/scripts/lint_pack_boundaries.ts +465 -29
  591. package/src/scripts/lint_roadmap_blockers.ts +282 -29
  592. package/src/scripts/lint_roadmap_complexity.ts +4 -2
  593. package/src/scripts/lint_rule_law_section.ts +647 -0
  594. package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
  595. package/src/scripts/lint_settings_classes.ts +1 -1
  596. package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
  597. package/src/scripts/lint_workflow_security.ts +173 -6
  598. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  599. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  600. package/src/scripts/measure_routing_signal.ts +54 -16
  601. package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
  602. package/src/scripts/memory_learn_hook.ts +91 -6
  603. package/src/scripts/model_rule_injection.ts +55 -8
  604. package/src/scripts/onboarding_gate_hook.ts +7 -6
  605. package/src/scripts/print_required_checks.ts +15 -3
  606. package/src/scripts/probe_host_compliance.ts +33 -5
  607. package/src/scripts/probe_turnaround.ts +183 -3
  608. package/src/scripts/project_thin_rules.ts +34 -327
  609. package/src/scripts/ratification_header.ts +77 -0
  610. package/src/scripts/refusal_envelope.ts +138 -0
  611. package/src/scripts/release_publication.ts +29 -3
  612. package/src/scripts/report_dispatch_import_closure.ts +76 -0
  613. package/src/scripts/report_evidence_temperature.ts +657 -0
  614. package/src/scripts/report_installed_rule_links.ts +274 -0
  615. package/src/scripts/report_label_agreement.ts +233 -0
  616. package/src/scripts/report_module_reach.ts +212 -0
  617. package/src/scripts/report_obligation_mechanism.ts +228 -0
  618. package/src/scripts/report_parked_blockers.ts +222 -0
  619. package/src/scripts/report_required_checks_drift.ts +196 -0
  620. package/src/scripts/report_single_token_triggers.ts +277 -0
  621. package/src/scripts/report_skill_ranker_confusion.ts +543 -0
  622. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  623. package/src/scripts/report_verification_record_coverage.ts +306 -0
  624. package/src/scripts/routing_doctor.ts +2 -2
  625. package/src/scripts/rule_activation_census.ts +5 -2
  626. package/src/scripts/rule_trigger_eval.ts +9 -9
  627. package/src/scripts/schemas/agent-settings.schema.json +17 -31
  628. package/src/scripts/schemas/command.schema.json +1 -1
  629. package/src/scripts/schemas/review-findings.schema.json +3 -2
  630. package/src/scripts/schemas/rule.schema.json +5 -0
  631. package/src/scripts/schemas/skill.schema.json +1 -1
  632. package/src/scripts/self_review_gate.ts +101 -9
  633. package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
  634. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  635. package/src/scripts/sync_gitignore.ts +29 -235
  636. package/src/scripts/sync_pr_branch.ts +403 -132
  637. package/src/scripts/trigger_eval_grandfather.json +0 -21
  638. package/src/server/schemas/settings.ts +42 -176
  639. package/src/shared/skillRanking.ts +169 -14
  640. package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
  641. package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
  642. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  643. package/src/scripts/_lib/config_chain.ts +0 -477
  644. package/src/scripts/_lib/conformance_report.ts +0 -403
  645. package/src/scripts/_lib/eval_discrimination.ts +0 -95
  646. package/src/scripts/_lib/evaluator_promotion.ts +0 -225
  647. package/src/scripts/_lib/file_slicer.ts +0 -117
  648. package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
  649. package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
  650. package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
  651. package/src/scripts/_lib/role_split.ts +0 -338
@@ -0,0 +1,2569 @@
1
+ import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
+ import {
3
+ HOOK_SIGNATURES,
4
+ entryCommands,
5
+ isManagedEntry
6
+ } from "./chunk-DEIV7XP7.js";
7
+ import {
8
+ manifest_path,
9
+ readRecordedHashes
10
+ } from "./chunk-OKJUIAPS.js";
11
+ import {
12
+ COUNCIL_CONFIG_ENV,
13
+ load_council_config,
14
+ resolve_config_path
15
+ } from "./chunk-WQWXGIWB.js";
16
+ import {
17
+ scanText
18
+ } from "./chunk-KB5FZ655.js";
19
+ import {
20
+ DEFAULT_SCAN_ROOTS,
21
+ Finding,
22
+ ROOT,
23
+ TOKEN_RE,
24
+ _scan,
25
+ classifyToken,
26
+ iter_corpus,
27
+ legacy_pragma_finding,
28
+ py_json_dumps_indent2,
29
+ report,
30
+ report_child_scanned,
31
+ scan_file
32
+ } from "./chunk-JQOM7TTM.js";
33
+ import {
34
+ DeadScopeError,
35
+ assertScanned
36
+ } from "./chunk-I3EIN2YG.js";
37
+ import {
38
+ readHookStdin
39
+ } from "./chunk-F7DQWYR5.js";
40
+ import {
41
+ require_dist
42
+ } from "./chunk-OY53J6II.js";
43
+ import {
44
+ SRC_SKILLS
45
+ } from "./chunk-YCU3TIXH.js";
46
+ import {
47
+ resolvePackageRoot
48
+ } from "./chunk-MCJYCQPF.js";
49
+ import {
50
+ __toESM
51
+ } from "./chunk-J55WI3RR.js";
52
+
53
+ // src/scripts/_cli/cmd_doctor_neighbours.ts
54
+ var YAML = __toESM(require_dist(), 1);
55
+ import { spawnSync } from "node:child_process";
56
+ import * as fs11 from "node:fs";
57
+ import * as path10 from "node:path";
58
+ import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL6 } from "node:url";
59
+
60
+ // src/scripts/_lib/neighbour_census.ts
61
+ import { createHash as createHash2 } from "node:crypto";
62
+ import * as fs3 from "node:fs";
63
+ import * as os2 from "node:os";
64
+ import * as path3 from "node:path";
65
+
66
+ // src/scripts/_lib/neighbour_tool_use.ts
67
+ import * as fs from "node:fs";
68
+ import * as path from "node:path";
69
+ var MCP_TOOL_PREFIX = "mcp__";
70
+ var FOREIGN_TOOL_USE_REL = "agents/runtime/neighbour-tool-use.json";
71
+ var TOOL_USE_WINDOW_DAYS = 30;
72
+ var SETTINGS_FILENAME = ".agent-settings.yml";
73
+ function usageDay(now) {
74
+ return now.toISOString().slice(0, 10);
75
+ }
76
+ var DAY_RE = /^\d{4}-\d{2}-\d{2}$/u;
77
+ function isUsageDay(value) {
78
+ return typeof value === "string" && DAY_RE.test(value);
79
+ }
80
+ function serverSegment(key) {
81
+ return key.replace(/[^A-Za-z0-9_-]/gu, "_");
82
+ }
83
+ function toolBelongsTo(tool, segment) {
84
+ if (!tool.startsWith(MCP_TOOL_PREFIX)) return false;
85
+ const rest = tool.slice(MCP_TOOL_PREFIX.length);
86
+ return rest === segment || rest.startsWith(`${segment}__`);
87
+ }
88
+ function resolveStoreRoot(start) {
89
+ let dir = path.resolve(start);
90
+ for (; ; ) {
91
+ try {
92
+ if (fs.statSync(path.join(dir, SETTINGS_FILENAME)).isFile()) return dir;
93
+ } catch {
94
+ }
95
+ const parent = path.dirname(dir);
96
+ if (parent === dir) return path.resolve(start);
97
+ dir = parent;
98
+ }
99
+ }
100
+ function toolsUsedByServer(projectRoot, now, segments) {
101
+ const known = [...new Set(segments)].sort((a, b) => b.length - a.length);
102
+ const counts = new Map(known.map((s) => [s, 0]));
103
+ if (known.length === 0) return counts;
104
+ let decoded;
105
+ try {
106
+ const root = resolveStoreRoot(projectRoot);
107
+ decoded = JSON.parse(fs.readFileSync(path.join(root, FOREIGN_TOOL_USE_REL), "utf-8"));
108
+ } catch {
109
+ return counts;
110
+ }
111
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return counts;
112
+ const today = usageDay(now);
113
+ const floor = usageDay(new Date(now.getTime() - (TOOL_USE_WINDOW_DAYS - 1) * 864e5));
114
+ for (const [tool, seen] of Object.entries(decoded)) {
115
+ if (!isUsageDay(seen) || seen < floor || seen > today) continue;
116
+ for (const segment of known) {
117
+ if (toolBelongsTo(tool, segment)) {
118
+ counts.set(segment, (counts.get(segment) ?? 0) + 1);
119
+ break;
120
+ }
121
+ }
122
+ }
123
+ return counts;
124
+ }
125
+
126
+ // src/scripts/_lib/skill_origin.ts
127
+ var import_yaml = __toESM(require_dist(), 1);
128
+ import { createHash } from "node:crypto";
129
+ import * as fs2 from "node:fs";
130
+ import * as os from "node:os";
131
+ import * as path2 from "node:path";
132
+ var AUTHORED_SKILL_ROOT = path2.join("src", "skills");
133
+ var SCAN_CACHE_RELATIVE = path2.join("agents", "reports", "neighbour-scan.json");
134
+ function scanCachePath(projectRoot) {
135
+ return path2.join(projectRoot, SCAN_CACHE_RELATIVE);
136
+ }
137
+ function qualifiedSkillName(name, origin) {
138
+ return origin === "package" ? name : `${origin}:${name}`;
139
+ }
140
+ function packageClaimedPaths(projectRoot) {
141
+ const manifest = manifest_path(projectRoot);
142
+ let doc;
143
+ try {
144
+ doc = (0, import_yaml.parse)(fs2.readFileSync(manifest, "utf-8"), { version: "1.1" });
145
+ } catch {
146
+ return null;
147
+ }
148
+ if (doc === null || typeof doc !== "object" || Array.isArray(doc)) return null;
149
+ const tools = doc["tools"];
150
+ const out = /* @__PURE__ */ new Set();
151
+ if (!Array.isArray(tools)) return out;
152
+ for (const tool of tools) {
153
+ if (tool === null || typeof tool !== "object" || Array.isArray(tool)) continue;
154
+ const files = tool["files"];
155
+ if (!Array.isArray(files)) continue;
156
+ for (const entry of files) {
157
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) continue;
158
+ const raw = entry["path"];
159
+ if (typeof raw !== "string" || raw === "") continue;
160
+ out.add(path2.resolve(projectRoot, _expanduser(raw)));
161
+ }
162
+ }
163
+ return out;
164
+ }
165
+ function _expanduser(p) {
166
+ if (p === "~") return os.homedir();
167
+ if (p.startsWith("~/") || process.platform === "win32" && p.startsWith("~\\")) {
168
+ return path2.join(os.homedir(), p.slice(2));
169
+ }
170
+ return p;
171
+ }
172
+ function makeSkillOriginResolver(projectRoot, opts = {}) {
173
+ const claims = opts.claims !== void 0 ? opts.claims : packageClaimedPaths(projectRoot);
174
+ const root = path2.resolve(projectRoot);
175
+ const authored = path2.join(path2.resolve(opts.packageRoot ?? projectRoot), AUTHORED_SKILL_ROOT);
176
+ const under = (abs, dir) => abs === dir || abs.startsWith(dir + path2.sep);
177
+ return (skillMdPath) => {
178
+ const abs = path2.resolve(skillMdPath);
179
+ if (under(abs, authored)) return "package";
180
+ if (claims === null) return "package";
181
+ if (claims.has(abs)) return "package";
182
+ return under(abs, root) ? "project" : "home";
183
+ };
184
+ }
185
+ function classifyCompat(args) {
186
+ if (args.unscanned !== null) return "unscanned";
187
+ if (args.shadows.length > 0) return "shadowed";
188
+ if (args.overlaps.length > 0) return "overlapping";
189
+ return "unclassified";
190
+ }
191
+ function fileDigest(file) {
192
+ try {
193
+ return createHash("sha256").update(fs2.readFileSync(file)).digest("hex");
194
+ } catch {
195
+ return null;
196
+ }
197
+ }
198
+ function scanVerdict(cache, qualified, digest) {
199
+ if (cache === null) return { ok: false, kind: "no-scan-record" };
200
+ const rec = cache.entries.find((e) => e.qualified === qualified);
201
+ if (rec === void 0) return { ok: false, kind: "no-scan-record" };
202
+ if (digest === null || rec.digest !== digest) return { ok: false, kind: "digest-changed" };
203
+ if (rec.unscanned !== null) return { ok: false, kind: rec.unscanned };
204
+ return { ok: true };
205
+ }
206
+
207
+ // src/scripts/_lib/neighbour_census.ts
208
+ var OUR_MCP_KEY = "agent-config";
209
+ function sha256(text) {
210
+ return createHash2("sha256").update(text, "utf-8").digest("hex");
211
+ }
212
+ function readJson(p) {
213
+ try {
214
+ const parsed = JSON.parse(fs3.readFileSync(p, "utf-8"));
215
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
216
+ return parsed;
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
221
+ function commandHead(command) {
222
+ const trimmed = command.trim();
223
+ if (trimmed === "") return "";
224
+ const lastStatement = trimmed.split(";").pop() ?? trimmed;
225
+ const tokens = lastStatement.trim().split(/\s+/).filter((t) => t !== "");
226
+ let i = 0;
227
+ while (i < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[i] ?? "")) i += 1;
228
+ const head = tokens[i] ?? "";
229
+ if (head === "") return "(redacted)";
230
+ if (!/^[A-Za-z0-9_./\\:-]+$/.test(head) || head.includes("@")) return "(redacted)";
231
+ return scanText(head).length === 0 ? head : "(redacted)";
232
+ }
233
+ function hostNameFor(label) {
234
+ const user = label.startsWith("~/");
235
+ const dir = label.replace(/^~\//, "").replace(/\/[^/]+$/, "");
236
+ const name = dir.split("/").map((seg) => seg.replace(/^\./, "")).join("/");
237
+ return user ? `user:${name}` : `project:${name}`;
238
+ }
239
+ function hookFiles(projectRoot, homeRoot) {
240
+ const home = homeRoot ?? os2.homedir();
241
+ const expand = (label) => label.startsWith("~/") ? path3.join(home, label.slice(2)) : path3.join(projectRoot, label);
242
+ const out = Object.entries(HOOK_SIGNATURES).map(([label, signature]) => ({
243
+ host: hostNameFor(label),
244
+ file: expand(label),
245
+ signature
246
+ }));
247
+ out.push({
248
+ host: "project:claude",
249
+ file: path3.join(projectRoot, ".claude", "settings.json"),
250
+ signature: "dispatch:hook --platform claude"
251
+ });
252
+ return out;
253
+ }
254
+ function censusHookFile(host, file, signature) {
255
+ const doc = readJson(file);
256
+ if (doc === null) return { entries: [], oursBound: false, present: false };
257
+ const hooks = doc["hooks"];
258
+ if (hooks === null || typeof hooks !== "object" || Array.isArray(hooks)) {
259
+ return { entries: [], oursBound: false, present: true };
260
+ }
261
+ const entries = [];
262
+ let oursBound = false;
263
+ for (const [event, list] of Object.entries(hooks)) {
264
+ if (!Array.isArray(list)) continue;
265
+ for (const raw of list) {
266
+ if (isManagedEntry(raw, signature)) {
267
+ oursBound = true;
268
+ continue;
269
+ }
270
+ const commands = entryCommands(raw);
271
+ const head = commandHead(commands[0] ?? "");
272
+ const obj = raw ?? {};
273
+ const matcher = typeof obj["matcher"] === "string" ? obj["matcher"] : null;
274
+ const timeout = typeof obj["timeout"] === "number" ? obj["timeout"] : null;
275
+ entries.push({
276
+ id: `${host}:${event}/${head === "" ? "unreadable" : head}`,
277
+ shape: "hook_group",
278
+ digest: sha256(JSON.stringify(raw)),
279
+ source: file,
280
+ event,
281
+ matcher,
282
+ command_head: head,
283
+ timeout,
284
+ // No host exposes another package's hook execution to a hook,
285
+ // so a neighbour's effect is not readable from its
286
+ // registration. `unknown` is the honest answer, and 2.3 treats
287
+ // it as capable of denying rather than assuming it is not.
288
+ effect: "unknown"
289
+ });
290
+ }
291
+ }
292
+ entries.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
293
+ return { entries, oursBound, present: true };
294
+ }
295
+ function unclaimedUnder(dir, origin, shape, owned, opts = {}) {
296
+ let names;
297
+ try {
298
+ names = fs3.readdirSync(dir).sort();
299
+ } catch {
300
+ return [];
301
+ }
302
+ const out = [];
303
+ for (const name of names) {
304
+ if (name.startsWith(".")) continue;
305
+ const full = path3.join(dir, name);
306
+ const probe = opts.nested === true ? path3.join(full, "SKILL.md") : full;
307
+ let body;
308
+ try {
309
+ const st = fs3.statSync(probe);
310
+ if (!st.isFile()) continue;
311
+ body = fs3.readFileSync(probe, "utf-8");
312
+ } catch {
313
+ continue;
314
+ }
315
+ if (owned.has(path3.resolve(probe))) continue;
316
+ out.push({
317
+ id: `${origin}:${opts.nested === true ? name : name.replace(/\.[^.]+$/, "")}`,
318
+ shape,
319
+ digest: sha256(body),
320
+ source: probe
321
+ });
322
+ }
323
+ return out;
324
+ }
325
+ function foreignSections(file, templateFile, origin) {
326
+ let body;
327
+ try {
328
+ body = fs3.readFileSync(file, "utf-8");
329
+ } catch {
330
+ return [];
331
+ }
332
+ let ours = /* @__PURE__ */ new Set();
333
+ try {
334
+ ours = new Set(
335
+ fs3.readFileSync(templateFile, "utf-8").split("\n").filter((l) => /^#{1,2} /.test(l)).map((l) => l.replace(/^#{1,2} /, "").trim())
336
+ );
337
+ } catch {
338
+ }
339
+ const out = [];
340
+ const lines = body.split("\n");
341
+ const isHeading = (n) => /^#{1,2} /.test(lines[n] ?? "");
342
+ for (let i = 0; i < lines.length; i += 1) {
343
+ if (!isHeading(i)) continue;
344
+ const heading = (lines[i] ?? "").replace(/^#{1,2} /, "").trim();
345
+ if (ours.has(heading)) continue;
346
+ let j = i + 1;
347
+ while (j < lines.length && !isHeading(j)) j += 1;
348
+ out.push({
349
+ id: `${origin}:${heading}`,
350
+ shape: "instruction_section",
351
+ digest: sha256(lines.slice(i, j).join("\n")),
352
+ source: file
353
+ });
354
+ }
355
+ return out;
356
+ }
357
+ function foreignMcpServers(projectRoot, now) {
358
+ const file = path3.join(projectRoot, ".mcp.json");
359
+ const doc = readJson(file);
360
+ const servers = doc?.["mcpServers"];
361
+ if (servers === null || servers === void 0 || typeof servers !== "object" || Array.isArray(servers)) {
362
+ return [];
363
+ }
364
+ const foreign = Object.entries(servers).filter(
365
+ ([name]) => name !== OUR_MCP_KEY
366
+ );
367
+ const used = toolsUsedByServer(projectRoot, now, foreign.map(([name]) => serverSegment(name)));
368
+ return foreign.map(([name, spec]) => ({
369
+ id: `project:${name}`,
370
+ shape: "mcp_server",
371
+ digest: sha256(JSON.stringify(spec)),
372
+ source: file,
373
+ tools_used_30d: used.get(serverSegment(name)) ?? 0,
374
+ tools_advertised: "unknown",
375
+ tools_window_days: TOOL_USE_WINDOW_DAYS
376
+ })).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
377
+ }
378
+ function labelFor(entries, oursBound, gatedEvents2) {
379
+ if (!oursBound) {
380
+ return {
381
+ label: "uncontrolled",
382
+ reason: "this package has no hook group registered in this file"
383
+ };
384
+ }
385
+ if (entries.length === 0) {
386
+ return { label: "controlled", reason: "no foreign hook entry in this file" };
387
+ }
388
+ const onGated = entries.filter((e) => gatedEvents2.has(e.event));
389
+ const stopEntries = entries.filter((e) => /stop/i.test(e.event));
390
+ if (onGated.length > 0 || stopEntries.length > 0) {
391
+ const named = [...new Set([...onGated, ...stopEntries].map((e) => e.id))].sort();
392
+ return {
393
+ label: "degraded",
394
+ reason: `foreign entr(ies) of unread effect on a slot this package gates: ${named.join(", ")}`
395
+ };
396
+ }
397
+ return {
398
+ label: "coordinated",
399
+ reason: `${entries.length} foreign entr(ies), none on a slot this package gates`
400
+ };
401
+ }
402
+ function census(projectRoot, opts = {}) {
403
+ const gated = opts.gatedEvents ?? /* @__PURE__ */ new Set();
404
+ const owned = new Set(readRecordedHashes(manifest_path(projectRoot), projectRoot).keys());
405
+ const home = opts.homeRoot ?? os2.homedir();
406
+ const hosts = [];
407
+ const hookGroups = [];
408
+ for (const { host, file, signature } of hookFiles(projectRoot, home)) {
409
+ const { entries, oursBound, present } = censusHookFile(host, file, signature);
410
+ if (!present) continue;
411
+ const { label, reason } = labelFor(entries, oursBound, gated);
412
+ hosts.push({
413
+ host,
414
+ file,
415
+ present,
416
+ hook_groups: entries,
417
+ ours_bound: oursBound,
418
+ label,
419
+ label_reason: reason
420
+ });
421
+ hookGroups.push(...entries);
422
+ }
423
+ const ourSkillsForCompat = opts.ourSkillNames ?? /* @__PURE__ */ new Set();
424
+ const originOf = makeSkillOriginResolver(projectRoot, {
425
+ ...opts.packageRoot === void 0 ? {} : { packageRoot: opts.packageRoot }
426
+ });
427
+ const scanCache = opts.scanCache !== void 0 ? opts.scanCache : null;
428
+ const overlaps = opts.overlaps ?? /* @__PURE__ */ new Map();
429
+ const skills = [
430
+ ...unclaimedUnder(path3.join(projectRoot, ".claude", "skills"), "project", "skill", owned, {
431
+ nested: true
432
+ }),
433
+ ...unclaimedUnder(path3.join(home, ".claude", "skills"), "user", "skill", owned, {
434
+ nested: true
435
+ })
436
+ ].map((entry) => {
437
+ const name = entry.id.split(":").slice(1).join(":");
438
+ const origin = originOf(entry.source);
439
+ const qualified = qualifiedSkillName(name, origin);
440
+ const unscanned = opts.scan !== void 0 ? opts.scan(entry.source) : (() => {
441
+ const v = scanVerdict(scanCache, qualified, fileDigest(entry.source));
442
+ return v.ok ? null : v.kind;
443
+ })();
444
+ const shadows = ourSkillsForCompat.has(name) ? [name] : [];
445
+ const hit = overlaps.get(path3.resolve(entry.source));
446
+ const overlapNames = unscanned === null && hit !== void 0 ? [...hit.names] : [];
447
+ return {
448
+ ...entry,
449
+ shape: "skill",
450
+ qualified,
451
+ compat: classifyCompat({ unscanned, shadows, overlaps: overlapNames }),
452
+ also: [.../* @__PURE__ */ new Set([...shadows, ...overlapNames])].sort(),
453
+ unscanned,
454
+ similarity: overlapNames.length > 0 && hit !== void 0 ? hit.similarity : null
455
+ };
456
+ });
457
+ const commands = unclaimedUnder(
458
+ path3.join(projectRoot, ".claude", "commands"),
459
+ "project",
460
+ "command",
461
+ owned
462
+ );
463
+ const agents = unclaimedUnder(
464
+ path3.join(projectRoot, ".claude", "agents"),
465
+ "project",
466
+ "agent",
467
+ owned
468
+ );
469
+ const ruleFiles = [
470
+ ...unclaimedUnder(path3.join(projectRoot, ".cursor", "rules"), "cursor", "rule_file", owned),
471
+ ...unclaimedUnder(path3.join(projectRoot, ".windsurf", "rules"), "windsurf", "rule_file", owned),
472
+ ...unclaimedUnder(path3.join(projectRoot, ".claude", "rules"), "claude", "rule_file", owned)
473
+ ];
474
+ const templates = opts.templatesRoot ?? "";
475
+ const instructionSections = [
476
+ ...foreignSections(
477
+ path3.join(projectRoot, "AGENTS.md"),
478
+ path3.join(templates, "AGENTS.md"),
479
+ "AGENTS.md"
480
+ ),
481
+ ...foreignSections(
482
+ path3.join(projectRoot, ".github", "copilot-instructions.md"),
483
+ path3.join(templates, "copilot-instructions.md"),
484
+ "copilot-instructions.md"
485
+ )
486
+ ];
487
+ const warnings = [];
488
+ const ourSkills = opts.ourSkillNames ?? /* @__PURE__ */ new Set();
489
+ for (const s of skills) {
490
+ const name = s.id.split(":").slice(1).join(":");
491
+ if (ourSkills.has(name)) {
492
+ warnings.push({
493
+ kind: "shadowed",
494
+ detail: `a skill named \`${name}\` sits at ${s.source} and this package ships one under the same name; which one the host resolves is the host's choice, not ours`
495
+ });
496
+ }
497
+ }
498
+ warnings.push(...doubleGateWarnings(hookGroups, gated));
499
+ warnings.push(...settingsTakeover(path3.join(projectRoot, ".claude", "settings.json")));
500
+ warnings.push(...settingsTakeover(path3.join(home, ".claude", "settings.json")));
501
+ for (const h of hosts) {
502
+ if (!h.ours_bound) {
503
+ warnings.push({
504
+ kind: "removed-after-install",
505
+ detail: `${h.file} carries hook entries but none of ours \u2014 this package's group is absent after its own install`
506
+ });
507
+ }
508
+ }
509
+ return {
510
+ project_root: projectRoot,
511
+ hosts,
512
+ hook_groups: hookGroups,
513
+ skills,
514
+ commands,
515
+ agents,
516
+ mcp_servers: foreignMcpServers(projectRoot, opts.now ?? /* @__PURE__ */ new Date()),
517
+ rule_files: ruleFiles,
518
+ instruction_sections: instructionSections,
519
+ warnings
520
+ };
521
+ }
522
+ function doubleGateWarnings(entries, gatedEvents2) {
523
+ const byEvent = /* @__PURE__ */ new Map();
524
+ for (const e of entries) {
525
+ if (!gatedEvents2.has(e.event)) continue;
526
+ const arr = byEvent.get(e.event);
527
+ if (arr) arr.push(e);
528
+ else byEvent.set(e.event, [e]);
529
+ }
530
+ return [...byEvent.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).map(([event, list]) => ({
531
+ kind: "double-gate",
532
+ detail: `${event}: ${list.length} foreign entr(ies) (${list.map((e) => e.id).join(", ")}) share this event with a gate of ours. The host runs both; either deny applies.`
533
+ }));
534
+ }
535
+ function settingsTakeover(settingsFile) {
536
+ const doc = readJson(settingsFile);
537
+ if (doc === null) return [];
538
+ const out = [];
539
+ if (doc["model"] !== void 0) {
540
+ out.push({
541
+ kind: "settings-takeover",
542
+ detail: `${settingsFile} sets \`model\` \u2014 the host resolves every turn against it, including the ones this package's rules are loaded into`
543
+ });
544
+ }
545
+ const env = doc["env"];
546
+ if (env !== null && typeof env === "object" && !Array.isArray(env)) {
547
+ const keys = Object.keys(env).filter((k) => k.startsWith("CLAUDE_CODE_")).sort();
548
+ if (keys.length > 0) {
549
+ out.push({
550
+ kind: "settings-takeover",
551
+ detail: `${settingsFile} sets host environment key(s) ${keys.join(", ")} \u2014 these change the harness the hooks this package bound run inside`
552
+ });
553
+ }
554
+ }
555
+ return out;
556
+ }
557
+ function gatedEventsFrom(manifest, nativeAliases = {}) {
558
+ const concerns = manifest.concerns ?? {};
559
+ const permission = new Set(
560
+ Object.keys(concerns).filter((n) => concerns[n]?.effect === "permission")
561
+ );
562
+ const out = /* @__PURE__ */ new Set();
563
+ for (const [host, events] of Object.entries(manifest.platforms ?? {})) {
564
+ if (events === null || typeof events !== "object") continue;
565
+ for (const [event, chain] of Object.entries(events)) {
566
+ if (!Array.isArray(chain)) continue;
567
+ if (!chain.some((c) => permission.has(String(c)))) continue;
568
+ out.add(event);
569
+ const native = nativeAliases[host]?.[event];
570
+ if (typeof native === "string") out.add(native);
571
+ for (const [nativeName, acEvent] of Object.entries(nativeAliases[host] ?? {})) {
572
+ if (acEvent === event) out.add(nativeName);
573
+ }
574
+ }
575
+ }
576
+ return out;
577
+ }
578
+
579
+ // src/scripts/_lib/neighbour_scan.ts
580
+ import * as fs7 from "node:fs";
581
+ import * as path7 from "node:path";
582
+
583
+ // src/scripts/lint_instruction_smuggling.ts
584
+ import * as path4 from "node:path";
585
+ import * as fs4 from "node:fs";
586
+ import { fileURLToPath, pathToFileURL } from "node:url";
587
+ var CHECK = "instruction-smuggling";
588
+ var _INLINE_CODE = /`[^`]*`/g;
589
+ var _SUPPRESS = new RegExp(
590
+ "\\b(do not|don'?t|never)\\s+(mention|tell|inform|disclose|reveal|notify)\\b[^.]{0,40}\\b(the )?(user|human|reviewer|operator|owner)\\b|without (telling|informing|notifying|explaining to) (the )?(user|them|anyone)",
591
+ "i"
592
+ );
593
+ var _INJECT = new RegExp(
594
+ "<\\s*(important|system|admin|secret|critical)\\s*>|ignore (all |the )?(previous|prior|above) (instructions|prompts|rules)|disregard (all |the )?(previous|prior|above)|you are now (a|an|the)\\b|new system prompt",
595
+ "i"
596
+ );
597
+ var _MED = [
598
+ [/\bcurl\b[^\n|]*\|\s*(ba|z|fi)?sh\b/i, "pipe-to-shell (curl|sh)"],
599
+ [/\bwget\b[^\n|]*\|\s*(ba|z|fi)?sh\b/i, "pipe-to-shell (wget|sh)"],
600
+ [/\b(socat|nc)\b[^\n]*\b(exec|-e)\b|\/dev\/tcp\//i, "reverse-shell signature"],
601
+ [/(~\/\.ssh\/id_[rd]sa|\/etc\/shadow|\.aws\/credentials)/, "secret-path read"]
602
+ ];
603
+ function _strip_inline_code(text) {
604
+ return text.replace(_INLINE_CODE, (m) => " ".repeat(m.length));
605
+ }
606
+ function _scan2(sf) {
607
+ const pragmaForm = sf.pragma_form(CHECK);
608
+ if (pragmaForm === "legacy") {
609
+ return [legacy_pragma_finding(sf, CHECK)];
610
+ }
611
+ const finish = (hits) => pragmaForm === "bound" ? sf.filter_bound_pragma(CHECK, hits) : hits;
612
+ const out = [];
613
+ for (const [lineno, text] of sf.iter_lines({ skip_example_fence: true, skip_any_fence: true })) {
614
+ const prose = _strip_inline_code(text);
615
+ if (_SUPPRESS.test(prose)) {
616
+ out.push(
617
+ new Finding(
618
+ sf.rel,
619
+ lineno,
620
+ CHECK,
621
+ "HIGH",
622
+ "disclosure-suppression imperative in prose",
623
+ sf.weight
624
+ )
625
+ );
626
+ }
627
+ if (_INJECT.test(prose)) {
628
+ out.push(
629
+ new Finding(
630
+ sf.rel,
631
+ lineno,
632
+ CHECK,
633
+ "HIGH",
634
+ "injection / role-takeover phrase in prose",
635
+ sf.weight
636
+ )
637
+ );
638
+ }
639
+ for (const [rx, label] of _MED) {
640
+ if (rx.test(prose)) {
641
+ out.push(
642
+ new Finding(
643
+ sf.rel,
644
+ lineno,
645
+ CHECK,
646
+ "MED",
647
+ `${label} in prose (verify intent)`,
648
+ sf.weight
649
+ )
650
+ );
651
+ }
652
+ }
653
+ }
654
+ return finish(out);
655
+ }
656
+ function _argError(msg) {
657
+ process.stderr.write("usage: lint_instruction_smuggling [-h] [--json] [--root DIR]\n");
658
+ process.stderr.write(`lint_instruction_smuggling: error: ${msg}
659
+ `);
660
+ process.exit(2);
661
+ }
662
+ function parse_args(argv) {
663
+ const out = { json: false };
664
+ const extra = [];
665
+ let rootPending = false;
666
+ for (const a of argv) {
667
+ if (rootPending) {
668
+ out.root = a;
669
+ rootPending = false;
670
+ continue;
671
+ }
672
+ if (a === "-h" || a === "--help") {
673
+ process.stdout.write("usage: lint_instruction_smuggling [-h] [--json] [--root DIR]\n");
674
+ process.exit(0);
675
+ } else if (a === "--json") {
676
+ out.json = true;
677
+ } else if (a === "--root" || a.startsWith("--root=")) {
678
+ const eq = a.indexOf("=");
679
+ if (eq !== -1) {
680
+ out.root = a.slice(eq + 1);
681
+ } else {
682
+ rootPending = true;
683
+ }
684
+ } else {
685
+ extra.push(a);
686
+ }
687
+ }
688
+ if (extra.length > 0) {
689
+ _argError(`unrecognized arguments: ${extra.join(" ")}`);
690
+ }
691
+ return out;
692
+ }
693
+ function main(argv = null) {
694
+ const args = parse_args(argv ?? process.argv.slice(2));
695
+ const scanBase = args.root === void 0 ? ROOT : path4.resolve(args.root);
696
+ const findings = [];
697
+ let corpusFiles = 0;
698
+ for (const sf of iter_corpus(DEFAULT_SCAN_ROOTS, [".md"], scanBase)) {
699
+ corpusFiles += 1;
700
+ for (const h of _scan2(sf)) {
701
+ findings.push(h);
702
+ }
703
+ }
704
+ try {
705
+ assertScanned({
706
+ gate: "lint_instruction_smuggling",
707
+ scanned: corpusFiles,
708
+ units: "corpus file(s)",
709
+ roots: DEFAULT_SCAN_ROOTS
710
+ });
711
+ } catch (exc) {
712
+ if (exc instanceof DeadScopeError) {
713
+ process.stderr.write(`\u274C ${exc.message}
714
+ `);
715
+ return 1;
716
+ }
717
+ throw exc;
718
+ }
719
+ if (args.json) {
720
+ report_child_scanned(corpusFiles);
721
+ }
722
+ if (args.json) {
723
+ const payload = py_json_dumps_indent2(findings.map((f) => f.toDict()));
724
+ process.stdout.write(payload + "\n");
725
+ return findings.some((f) => f.is_fail) ? 1 : 0;
726
+ }
727
+ return report(findings, { check_label: "instruction-smuggling" });
728
+ }
729
+ function _isCliEntry() {
730
+ if (process.argv[1] === void 0) {
731
+ return false;
732
+ }
733
+ const argvUrl = pathToFileURL(path4.resolve(process.argv[1])).href;
734
+ if (import.meta.url === argvUrl) {
735
+ return true;
736
+ }
737
+ try {
738
+ const here = fs4.realpathSync(fileURLToPath(import.meta.url));
739
+ const argv = fs4.realpathSync(path4.resolve(process.argv[1]));
740
+ return here === argv;
741
+ } catch {
742
+ return false;
743
+ }
744
+ }
745
+ if (_isCliEntry()) {
746
+ process.exitCode = main();
747
+ }
748
+
749
+ // src/scripts/lint_confusables.ts
750
+ import * as path5 from "node:path";
751
+ import * as fs5 from "node:fs";
752
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
753
+ var CHECK2 = "mixed-script-confusable";
754
+ var _TOKEN = TOKEN_RE;
755
+ var _classify_token = classifyToken;
756
+ function _scan3(sf) {
757
+ const pragmaForm = sf.pragma_form(CHECK2);
758
+ if (pragmaForm === "legacy") {
759
+ return [legacy_pragma_finding(sf, CHECK2)];
760
+ }
761
+ const finish = (hits) => pragmaForm === "bound" ? sf.filter_bound_pragma(CHECK2, hits) : hits;
762
+ const out = [];
763
+ for (const [lineno, text] of sf.iter_lines({ skip_example_fence: true })) {
764
+ const tokens = text.match(_TOKEN);
765
+ if (tokens === null) continue;
766
+ for (const tok of tokens) {
767
+ const desc = _classify_token(tok);
768
+ if (desc !== null) {
769
+ out.push(
770
+ new Finding(
771
+ sf.rel,
772
+ lineno,
773
+ CHECK2,
774
+ "HIGH",
775
+ `mixed-script confusable token "${tok}" \u2014 ${desc}`,
776
+ sf.weight
777
+ )
778
+ );
779
+ }
780
+ }
781
+ }
782
+ return finish(out);
783
+ }
784
+ function _argError2(msg) {
785
+ process.stderr.write("usage: lint_confusables [-h] [--json] [--root DIR]\n");
786
+ process.stderr.write(`lint_confusables: error: ${msg}
787
+ `);
788
+ process.exit(2);
789
+ }
790
+ function parse_args2(argv) {
791
+ const out = { json: false };
792
+ const extra = [];
793
+ let rootPending = false;
794
+ for (const a of argv) {
795
+ if (rootPending) {
796
+ out.root = a;
797
+ rootPending = false;
798
+ continue;
799
+ }
800
+ if (a === "-h" || a === "--help") {
801
+ process.stdout.write("usage: lint_confusables [-h] [--json] [--root DIR]\n");
802
+ process.exit(0);
803
+ } else if (a === "--json") {
804
+ out.json = true;
805
+ } else if (a === "--root" || a.startsWith("--root=")) {
806
+ const eq = a.indexOf("=");
807
+ if (eq !== -1) {
808
+ out.root = a.slice(eq + 1);
809
+ } else {
810
+ rootPending = true;
811
+ }
812
+ } else {
813
+ extra.push(a);
814
+ }
815
+ }
816
+ if (extra.length > 0) {
817
+ _argError2(`unrecognized arguments: ${extra.join(" ")}`);
818
+ }
819
+ return out;
820
+ }
821
+ function main2(argv = null) {
822
+ const args = parse_args2(argv ?? process.argv.slice(2));
823
+ const scanBase = args.root === void 0 ? ROOT : path5.resolve(args.root);
824
+ const findings = [];
825
+ let scanned = 0;
826
+ for (const sf of iter_corpus(DEFAULT_SCAN_ROOTS, [".md"], scanBase)) {
827
+ scanned += 1;
828
+ for (const h of _scan3(sf)) {
829
+ findings.push(h);
830
+ }
831
+ }
832
+ try {
833
+ assertScanned({
834
+ gate: "lint_confusables",
835
+ scanned,
836
+ units: "file(s)",
837
+ roots: DEFAULT_SCAN_ROOTS
838
+ });
839
+ } catch (exc) {
840
+ if (exc instanceof DeadScopeError) {
841
+ process.stderr.write(`\u274C ${exc.message}
842
+ `);
843
+ return 2;
844
+ }
845
+ throw exc;
846
+ }
847
+ if (args.json) {
848
+ report_child_scanned(scanned);
849
+ }
850
+ if (args.json) {
851
+ const payload = py_json_dumps_indent2(findings.map((f) => f.toDict()));
852
+ process.stdout.write(payload + "\n");
853
+ return findings.some((f) => f.is_fail) ? 1 : 0;
854
+ }
855
+ return report(findings, { check_label: "mixed-script-confusable" });
856
+ }
857
+ function _isCliEntry2() {
858
+ if (process.argv[1] === void 0) {
859
+ return false;
860
+ }
861
+ const argvUrl = pathToFileURL2(path5.resolve(process.argv[1])).href;
862
+ if (import.meta.url === argvUrl) {
863
+ return true;
864
+ }
865
+ try {
866
+ const here = fs5.realpathSync(fileURLToPath2(import.meta.url));
867
+ const argv = fs5.realpathSync(path5.resolve(process.argv[1]));
868
+ return here === argv;
869
+ } catch {
870
+ return false;
871
+ }
872
+ }
873
+ if (_isCliEntry2()) {
874
+ process.exitCode = main2();
875
+ }
876
+
877
+ // src/scripts/lint_skill_frontmatter_safety.ts
878
+ import * as path6 from "node:path";
879
+ import * as fs6 from "node:fs";
880
+ import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
881
+ var CHECK3 = "dangerous-frontmatter";
882
+ var _WILDCARD_TOOL = /(^|[\s,[\"'])(\*|Bash\(\*\))(\s|,|\]|"|'|$)/;
883
+ var _BARE_BASH = /(^|[\s,[\"'])Bash(\s|,|\]|"|'|$)/;
884
+ var _RE_EXECUTION = /^execution:\s*$/;
885
+ var _RE_EXEC_KEY = /^\s+([A-Za-z0-9_-]+):\s*(.*)$/;
886
+ var _RE_PERMISSION_MODE = /^\s*permissionMode:\s*['"]?bypassPermissions/;
887
+ var _RE_ALLOWED_TOOLS = /^\s*allowed-tools:\s*(.+)$/;
888
+ var _RE_TOOLS_KEY = /^tools:\s*(.*)$/;
889
+ var _RE_SEQ_ITEM = /^\s+-\s*(.+)$/;
890
+ function _stripQuotes(s) {
891
+ let lo = 0;
892
+ let hi = s.length;
893
+ while (lo < hi && (s[lo] === "'" || s[lo] === '"')) lo++;
894
+ while (hi > lo && (s[hi - 1] === "'" || s[hi - 1] === '"')) hi--;
895
+ return s.slice(lo, hi);
896
+ }
897
+ function _indent(text) {
898
+ return text.length - text.replace(/^\s+/, "").length;
899
+ }
900
+ function _frontmatter(sf) {
901
+ if (sf.lines.length === 0 || sf.lines[0].trim() !== "---") {
902
+ return null;
903
+ }
904
+ const body = [];
905
+ for (let i = 1; i < sf.lines.length; i++) {
906
+ if (sf.lines[i].trim() === "---") {
907
+ return [body, i + 1];
908
+ }
909
+ body.push([i + 1, sf.lines[i]]);
910
+ }
911
+ return null;
912
+ }
913
+ function _exec_block(body) {
914
+ const out = {};
915
+ let in_exec = false;
916
+ let base_indent = 0;
917
+ for (const [lineno, text] of body) {
918
+ if (_RE_EXECUTION.test(text)) {
919
+ in_exec = true;
920
+ base_indent = _indent(text);
921
+ continue;
922
+ }
923
+ if (in_exec) {
924
+ const indent = _indent(text);
925
+ if (text.trim() && indent <= base_indent) {
926
+ in_exec = false;
927
+ continue;
928
+ }
929
+ const m = _RE_EXEC_KEY.exec(text);
930
+ if (m) {
931
+ out[m[1]] = [lineno, m[2].trim()];
932
+ }
933
+ }
934
+ }
935
+ return out;
936
+ }
937
+ function grantSurface(rel) {
938
+ return rel.replace(/\\/g, "/").includes("src/subagents/") ? "subagent" : "skill-like";
939
+ }
940
+ function _scan4(sf) {
941
+ const pragmaForm = sf.pragma_form(CHECK3);
942
+ if (pragmaForm === "legacy") {
943
+ return [legacy_pragma_finding(sf, CHECK3)];
944
+ }
945
+ const finish = (hits) => pragmaForm === "bound" ? sf.filter_bound_pragma(CHECK3, hits) : hits;
946
+ const fm = _frontmatter(sf);
947
+ if (!fm) {
948
+ return [];
949
+ }
950
+ const [body] = fm;
951
+ const out = [];
952
+ const ex = _exec_block(body);
953
+ const handler = _stripQuotes((ex["handler"] ?? [0, ""])[1]);
954
+ const is_execution_skill = Object.keys(ex).length > 0 && handler !== "" && handler !== "none";
955
+ const surface = grantSurface(sf.rel);
956
+ for (const [lineno, text] of body) {
957
+ if (surface === "skill-like" && _RE_TOOLS_KEY.test(text)) {
958
+ out.push(
959
+ new Finding(
960
+ sf.rel,
961
+ lineno,
962
+ CHECK3,
963
+ "HIGH",
964
+ "top-level `tools:` on a non-subagent surface \u2014 subagent-v1 only; the loader ignores it here, so this artefact is NOT restricted (use execution.allowed_tools)",
965
+ sf.weight
966
+ )
967
+ );
968
+ }
969
+ if (surface === "subagent" && _RE_EXECUTION.test(text)) {
970
+ out.push(
971
+ new Finding(
972
+ sf.rel,
973
+ lineno,
974
+ CHECK3,
975
+ "HIGH",
976
+ "`execution:` block on a subagent definition \u2014 subagent-v1 has no such key, so any allowed_tools under it restricts nothing (use top-level `tools:`)",
977
+ sf.weight
978
+ )
979
+ );
980
+ }
981
+ }
982
+ for (const [lineno, text] of body) {
983
+ if (_RE_PERMISSION_MODE.test(text)) {
984
+ out.push(
985
+ new Finding(
986
+ sf.rel,
987
+ lineno,
988
+ CHECK3,
989
+ "HIGH",
990
+ "permissionMode: bypassPermissions (consent bypass)",
991
+ sf.weight
992
+ )
993
+ );
994
+ }
995
+ const m = _RE_ALLOWED_TOOLS.exec(text);
996
+ if (m && (_WILDCARD_TOOL.test(m[1]) || _BARE_BASH.test(m[1]) && !is_execution_skill)) {
997
+ out.push(
998
+ new Finding(
999
+ sf.rel,
1000
+ lineno,
1001
+ CHECK3,
1002
+ "HIGH",
1003
+ "wildcard / bare-Bash tool grant (over-broad)",
1004
+ sf.weight
1005
+ )
1006
+ );
1007
+ }
1008
+ }
1009
+ for (let i = 0; i < body.length; i += 1) {
1010
+ const [lineno, text] = body[i];
1011
+ const key = _RE_TOOLS_KEY.exec(text);
1012
+ if (!key) {
1013
+ continue;
1014
+ }
1015
+ const grants = [];
1016
+ const inline = (key[1] ?? "").trim();
1017
+ if (inline !== "") {
1018
+ grants.push(inline);
1019
+ }
1020
+ for (let j = i + 1; j < body.length; j += 1) {
1021
+ const [, next] = body[j];
1022
+ const item = _RE_SEQ_ITEM.exec(next);
1023
+ if (!item) {
1024
+ break;
1025
+ }
1026
+ grants.push(_stripQuotes(item[1].trim()));
1027
+ }
1028
+ const joined = grants.join(", ");
1029
+ if (joined !== "" && (_WILDCARD_TOOL.test(joined) || _BARE_BASH.test(joined))) {
1030
+ out.push(
1031
+ new Finding(
1032
+ sf.rel,
1033
+ lineno,
1034
+ CHECK3,
1035
+ "HIGH",
1036
+ "wildcard / bare-Bash tool grant (over-broad)",
1037
+ sf.weight
1038
+ )
1039
+ );
1040
+ }
1041
+ }
1042
+ if (Object.keys(ex).length === 0) {
1043
+ return finish(out);
1044
+ }
1045
+ const etype = _stripQuotes((ex["type"] ?? [0, ""])[1]);
1046
+ const safety = _stripQuotes((ex["safety_mode"] ?? [0, ""])[1]);
1047
+ const exec_line = (ex["type"] ?? ex["handler"] ?? [0, ""])[0];
1048
+ if (etype === "automated") {
1049
+ if (handler === "" || handler === "none") {
1050
+ out.push(
1051
+ new Finding(
1052
+ sf.rel,
1053
+ exec_line,
1054
+ CHECK3,
1055
+ "HIGH",
1056
+ "automated execution with handler none/missing (runtime-safety)",
1057
+ sf.weight
1058
+ )
1059
+ );
1060
+ }
1061
+ if (safety !== "strict") {
1062
+ out.push(
1063
+ new Finding(
1064
+ sf.rel,
1065
+ exec_line,
1066
+ CHECK3,
1067
+ "HIGH",
1068
+ "automated execution without safety_mode: strict (runtime-safety)",
1069
+ sf.weight
1070
+ )
1071
+ );
1072
+ }
1073
+ if (!("allowed_tools" in ex)) {
1074
+ out.push(
1075
+ new Finding(
1076
+ sf.rel,
1077
+ exec_line,
1078
+ CHECK3,
1079
+ "HIGH",
1080
+ "automated execution without an explicit allowed_tools declaration",
1081
+ sf.weight
1082
+ )
1083
+ );
1084
+ }
1085
+ }
1086
+ const [at_line, at_val] = ex["allowed_tools"] ?? [0, ""];
1087
+ if (at_val && _WILDCARD_TOOL.test(at_val)) {
1088
+ out.push(
1089
+ new Finding(
1090
+ sf.rel,
1091
+ at_line,
1092
+ CHECK3,
1093
+ "HIGH",
1094
+ "execution.allowed_tools wildcard (* / Bash(*)) grant",
1095
+ sf.weight
1096
+ )
1097
+ );
1098
+ } else if (at_val && _BARE_BASH.test(at_val) && !is_execution_skill) {
1099
+ out.push(
1100
+ new Finding(
1101
+ sf.rel,
1102
+ at_line,
1103
+ CHECK3,
1104
+ "HIGH",
1105
+ "bare Bash grant on a non-execution skill (handler none/missing)",
1106
+ sf.weight
1107
+ )
1108
+ );
1109
+ }
1110
+ return finish(out);
1111
+ }
1112
+ function _argError3(msg) {
1113
+ process.stderr.write("usage: lint_skill_frontmatter_safety [-h] [--json] [--root DIR]\n");
1114
+ process.stderr.write(`lint_skill_frontmatter_safety: error: ${msg}
1115
+ `);
1116
+ process.exit(2);
1117
+ }
1118
+ function parse_args3(argv) {
1119
+ const out = { json: false };
1120
+ const extra = [];
1121
+ let rootPending = false;
1122
+ for (const a of argv) {
1123
+ if (rootPending) {
1124
+ out.root = a;
1125
+ rootPending = false;
1126
+ continue;
1127
+ }
1128
+ if (a === "-h" || a === "--help") {
1129
+ process.stdout.write("usage: lint_skill_frontmatter_safety [-h] [--json] [--root DIR]\n");
1130
+ process.exit(0);
1131
+ } else if (a === "--json") {
1132
+ out.json = true;
1133
+ } else if (a === "--root" || a.startsWith("--root=")) {
1134
+ const eq = a.indexOf("=");
1135
+ if (eq !== -1) {
1136
+ out.root = a.slice(eq + 1);
1137
+ } else {
1138
+ rootPending = true;
1139
+ }
1140
+ } else {
1141
+ extra.push(a);
1142
+ }
1143
+ }
1144
+ if (extra.length > 0) {
1145
+ _argError3(`unrecognized arguments: ${extra.join(" ")}`);
1146
+ }
1147
+ return out;
1148
+ }
1149
+ function main3(argv = null) {
1150
+ const args = parse_args3(argv ?? process.argv.slice(2));
1151
+ const scanBase = args.root === void 0 ? ROOT : path6.resolve(args.root);
1152
+ const findings = [];
1153
+ const roots = ["src/skills", "src/agent-src", "src/domains", "src/subagents"];
1154
+ let scanned = 0;
1155
+ for (const sf of iter_corpus(roots, [".md"], scanBase)) {
1156
+ scanned += 1;
1157
+ for (const h of _scan4(sf)) {
1158
+ findings.push(h);
1159
+ }
1160
+ }
1161
+ try {
1162
+ assertScanned({
1163
+ gate: "lint_skill_frontmatter_safety",
1164
+ scanned,
1165
+ units: "markdown file(s)",
1166
+ roots
1167
+ });
1168
+ } catch (exc) {
1169
+ if (exc instanceof DeadScopeError) {
1170
+ process.stderr.write(`\u274C ${exc.message}
1171
+ `);
1172
+ return 1;
1173
+ }
1174
+ throw exc;
1175
+ }
1176
+ if (args.json) {
1177
+ report_child_scanned(scanned);
1178
+ }
1179
+ if (args.json) {
1180
+ const payload = py_json_dumps_indent2(findings.map((f) => f.toDict()));
1181
+ process.stdout.write(payload + "\n");
1182
+ return findings.some((f) => f.is_fail) ? 1 : 0;
1183
+ }
1184
+ return report(findings, { check_label: "dangerous-frontmatter", scanned_roots: roots });
1185
+ }
1186
+ function _isCliEntry3() {
1187
+ if (process.argv[1] === void 0) {
1188
+ return false;
1189
+ }
1190
+ const argvUrl = pathToFileURL3(path6.resolve(process.argv[1])).href;
1191
+ if (import.meta.url === argvUrl) {
1192
+ return true;
1193
+ }
1194
+ try {
1195
+ const here = fs6.realpathSync(fileURLToPath3(import.meta.url));
1196
+ const argv = fs6.realpathSync(path6.resolve(process.argv[1]));
1197
+ return here === argv;
1198
+ } catch {
1199
+ return false;
1200
+ }
1201
+ }
1202
+ if (_isCliEntry3()) {
1203
+ process.exitCode = main3();
1204
+ }
1205
+
1206
+ // src/scripts/_lib/neighbour_scan.ts
1207
+ var SHAPES = [
1208
+ ["instruction-smuggling", _scan2],
1209
+ ["dangerous-frontmatter", _scan4],
1210
+ ["hidden-unicode", _scan],
1211
+ ["mixed-script-confusable", _scan3]
1212
+ ];
1213
+ function scanNeighbourBody(skillMdPath) {
1214
+ let sf;
1215
+ try {
1216
+ sf = scan_file(skillMdPath);
1217
+ } catch {
1218
+ return { kind: "unreadable", findings: [] };
1219
+ }
1220
+ const findings = [];
1221
+ for (const [id, scan] of SHAPES) {
1222
+ try {
1223
+ findings.push(...scan(sf));
1224
+ } catch {
1225
+ findings.push(
1226
+ new Finding(sf.rel, 0, id, "HIGH", "shape check threw on this body", 1)
1227
+ );
1228
+ }
1229
+ }
1230
+ return { kind: findings.length === 0 ? null : findings[0].check, findings };
1231
+ }
1232
+ function writeScanCache(file, entries) {
1233
+ const payload = {
1234
+ schema_version: 1,
1235
+ entries: [...entries].sort((a, b) => a.qualified < b.qualified ? -1 : 1)
1236
+ };
1237
+ try {
1238
+ fs7.mkdirSync(path7.dirname(file), { recursive: true });
1239
+ fs7.writeFileSync(file, `${JSON.stringify(payload, null, 2)}
1240
+ `, "utf-8");
1241
+ return true;
1242
+ } catch {
1243
+ return false;
1244
+ }
1245
+ }
1246
+
1247
+ // src/scripts/_lib/neighbour_contradictions.ts
1248
+ import * as fs8 from "node:fs";
1249
+ var RANK_PROJECT = 3;
1250
+ var RANK_SUITE = 4;
1251
+ var RANK_NEIGHBOUR_ALWAYS_ON = 6;
1252
+ var RANK_NEIGHBOUR_SKILL = 7;
1253
+ var RANK_LABEL = {
1254
+ [RANK_PROJECT]: "project instruction file",
1255
+ [RANK_SUITE]: "this suite's routed guidance",
1256
+ [RANK_NEIGHBOUR_ALWAYS_ON]: "neighbour's always-on text",
1257
+ [RANK_NEIGHBOUR_SKILL]: "neighbour skill body (discoverable)"
1258
+ };
1259
+ var NEGATION = /\b(never|don't|do not|must not|mustn't|should not|shouldn't|avoid|stop)\b/iu;
1260
+ var DIRECTIVE_WORD = /\b(always|must|should|never|don't|do not|avoid)\b/iu;
1261
+ var NON_IMPERATIVE_START = /* @__PURE__ */ new Set([
1262
+ "the",
1263
+ "a",
1264
+ "an",
1265
+ "this",
1266
+ "that",
1267
+ "these",
1268
+ "those",
1269
+ "it",
1270
+ "its",
1271
+ "we",
1272
+ "you",
1273
+ "i",
1274
+ "our",
1275
+ "they",
1276
+ "there",
1277
+ "when",
1278
+ "if",
1279
+ "see",
1280
+ "for",
1281
+ "in",
1282
+ "on",
1283
+ "as",
1284
+ "by",
1285
+ "with",
1286
+ "from"
1287
+ ]);
1288
+ var STOP = /* @__PURE__ */ new Set([
1289
+ "always",
1290
+ "never",
1291
+ "must",
1292
+ "should",
1293
+ "avoid",
1294
+ "stop",
1295
+ "don",
1296
+ "with",
1297
+ "without",
1298
+ "that",
1299
+ "this",
1300
+ "they",
1301
+ "them",
1302
+ "then",
1303
+ "than",
1304
+ "into",
1305
+ "from",
1306
+ "your",
1307
+ "when",
1308
+ "what",
1309
+ "will",
1310
+ "each",
1311
+ "every",
1312
+ "only",
1313
+ "also",
1314
+ "here",
1315
+ "there",
1316
+ "have",
1317
+ "been",
1318
+ "more",
1319
+ "some",
1320
+ "such",
1321
+ "like",
1322
+ "just"
1323
+ ]);
1324
+ function stem(word) {
1325
+ let w = word.toLowerCase().replace(/[^a-z]/gu, "");
1326
+ for (const suffix of ["ing", "ed", "es", "s"]) {
1327
+ if (w.length > suffix.length + 3 && w.endsWith(suffix)) {
1328
+ w = w.slice(0, -suffix.length);
1329
+ break;
1330
+ }
1331
+ }
1332
+ return w;
1333
+ }
1334
+ function isDirective(text) {
1335
+ if (DIRECTIVE_WORD.test(text)) return true;
1336
+ const first = (text.split(/\s+/u)[0] ?? "").toLowerCase().replace(/[^a-z']/gu, "");
1337
+ return first.length > 0 && !NON_IMPERATIVE_START.has(first);
1338
+ }
1339
+ function extractInstructions(source, body) {
1340
+ let text;
1341
+ try {
1342
+ text = body ?? fs8.readFileSync(source.file, "utf-8");
1343
+ } catch {
1344
+ return [];
1345
+ }
1346
+ const out = [];
1347
+ let fenced = false;
1348
+ let frontmatter = false;
1349
+ text.split("\n").forEach((raw, i) => {
1350
+ const line = raw.trim();
1351
+ if (i === 0 && line === "---") {
1352
+ frontmatter = true;
1353
+ return;
1354
+ }
1355
+ if (frontmatter) {
1356
+ if (line === "---") frontmatter = false;
1357
+ return;
1358
+ }
1359
+ if (line.startsWith("```") || line.startsWith("~~~")) {
1360
+ fenced = !fenced;
1361
+ return;
1362
+ }
1363
+ if (fenced || line === "" || line.startsWith("#") || line.startsWith("|") || line.startsWith(">")) return;
1364
+ const clean = line.replace(/^([-*+]|\d+\.)\s+/u, "").replace(/[*_`]/gu, "");
1365
+ if (clean.split(/\s+/u).length < 3 || !isDirective(clean)) return;
1366
+ const stems = new Set(
1367
+ clean.split(/\s+/u).map(stem).filter((s) => s.length >= 4 && !STOP.has(s))
1368
+ );
1369
+ out.push({ source, line: i + 1, text: clean, negative: NEGATION.test(clean), stems });
1370
+ });
1371
+ return out;
1372
+ }
1373
+ function where(l) {
1374
+ return `${l.source.label}:${l.line}`;
1375
+ }
1376
+ function modalityPairs(lines, cap = 20) {
1377
+ const out = [];
1378
+ for (let i = 0; i < lines.length && out.length < cap; i += 1) {
1379
+ for (let j = i + 1; j < lines.length && out.length < cap; j += 1) {
1380
+ const x = lines[i];
1381
+ const y = lines[j];
1382
+ if (x.source.rank === y.source.rank || x.negative === y.negative) continue;
1383
+ const shared = [...x.stems].filter((s) => y.stems.has(s)).sort();
1384
+ if (shared.length === 0) continue;
1385
+ const [hi, lo] = x.source.rank <= y.source.rank ? [x, y] : [y, x];
1386
+ out.push({
1387
+ kind: "opposite-modality",
1388
+ a: { label: where(hi), rank: hi.source.rank, text: hi.text },
1389
+ b: { label: where(lo), rank: lo.source.rank, text: lo.text },
1390
+ subject: shared[0]
1391
+ });
1392
+ }
1393
+ }
1394
+ return out;
1395
+ }
1396
+ function collisionPair(neighbourQualified, ourName) {
1397
+ return {
1398
+ kind: "name-collision",
1399
+ a: { label: `suite:${ourName}`, rank: RANK_SUITE, text: `this suite's skill \`${ourName}\`` },
1400
+ b: {
1401
+ label: neighbourQualified,
1402
+ rank: RANK_NEIGHBOUR_SKILL,
1403
+ text: `neighbour skill \`${neighbourQualified}\``
1404
+ },
1405
+ subject: ourName
1406
+ };
1407
+ }
1408
+ function winnerBy21(p) {
1409
+ const w = p.a.rank <= p.b.rank ? p.a : p.b;
1410
+ return `${w.label} (rank ${w.rank}, ${RANK_LABEL[w.rank] ?? "unranked"})`;
1411
+ }
1412
+ function seatPrompt(pairs, rubric) {
1413
+ const rows = pairs.map(
1414
+ (p, i) => `PAIR ${i + 1} (${p.kind}, subject "${p.subject}")
1415
+ A [${p.a.label}]: ${p.a.text}
1416
+ B [${p.b.label}]: ${p.b.text}`
1417
+ );
1418
+ return [
1419
+ "# Neighbour contradiction review",
1420
+ "",
1421
+ "For each pair, decide whether A and B give an agent instructions that cannot both be followed.",
1422
+ "Answer one line per pair, exactly `PAIR <n>: contradiction`, `PAIR <n>: compatible` or `PAIR <n>: unclear`,",
1423
+ "then at most one sentence of reason on the same line. The rubric for which source wins is below; you do not",
1424
+ "need to restate it.",
1425
+ "",
1426
+ "## Rubric \u2014 neighbour-precedence",
1427
+ "",
1428
+ rubric.trim(),
1429
+ "",
1430
+ "## Pairs",
1431
+ "",
1432
+ ...rows,
1433
+ ""
1434
+ ].join("\n");
1435
+ }
1436
+ function parseVerdicts(text, count) {
1437
+ const out = Array.from({ length: count }, () => "unclear");
1438
+ for (const m of text.matchAll(/PAIR\s+(\d+)\s*:\s*(contradiction|compatible|unclear)/giu)) {
1439
+ const n = Number(m[1]) - 1;
1440
+ if (n >= 0 && n < count) out[n] = (m[2] ?? "unclear").toLowerCase();
1441
+ }
1442
+ return out;
1443
+ }
1444
+ function reviewRows(pairs, verdicts) {
1445
+ return pairs.map((p, i) => ({
1446
+ pair: `${p.a.label} "${p.a.text}" \xD7 ${p.b.label} "${p.b.text}"`,
1447
+ verdict: verdicts[i] ?? "unclear",
1448
+ wins: winnerBy21(p)
1449
+ }));
1450
+ }
1451
+ function renderReview(rows) {
1452
+ if (rows.length === 0) return [" no candidate pairs \u2014 nothing to review"];
1453
+ return [" pair | verdict | which wins by 2.1", ...rows.map((r) => ` ${r.pair} | ${r.verdict} | ${r.wins}`)];
1454
+ }
1455
+
1456
+ // src/scripts/council_availability_hook.ts
1457
+ import * as fs9 from "node:fs";
1458
+ import * as path8 from "node:path";
1459
+ import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL4 } from "node:url";
1460
+ var EXIT_ALLOW = 0;
1461
+ function resolveAvailability(cwd, env) {
1462
+ let p;
1463
+ try {
1464
+ p = String(resolve_config_path(cwd, { env }));
1465
+ } catch {
1466
+ return null;
1467
+ }
1468
+ const exists = fs9.existsSync(p);
1469
+ const provenance = env[COUNCIL_CONFIG_ENV] ? "env-override" : "user-global";
1470
+ if (!exists) {
1471
+ return { configured: false, path: p, exists: false, provenance, membersEnabled: 0, memberNames: [] };
1472
+ }
1473
+ try {
1474
+ const cfg = load_council_config(p);
1475
+ const enabled = [...cfg.members.entries()].filter(([, m]) => m.enabled).map(([n]) => n);
1476
+ return {
1477
+ configured: cfg.enabled && enabled.length > 0,
1478
+ path: p,
1479
+ exists: true,
1480
+ provenance,
1481
+ membersEnabled: enabled.length,
1482
+ memberNames: enabled
1483
+ };
1484
+ } catch {
1485
+ return { configured: false, path: p, exists: true, provenance, membersEnabled: 0, memberNames: [] };
1486
+ }
1487
+ }
1488
+ function buildFact(a) {
1489
+ const head = a.configured ? `An AI council IS configured: ${String(a.membersEnabled)} enabled member(s) \u2014 ${a.memberNames.join(", ")}.` : a.exists ? "An AI council config exists but is NOT usable (unreadable, disabled, or no enabled member)." : "No AI council is configured on this machine.";
1490
+ return [
1491
+ "<council-availability>",
1492
+ head,
1493
+ `Resolved from ${a.provenance}: ${a.path}`,
1494
+ "The project tree is NEVER searched for council config (ADR-104), and the",
1495
+ "config has not lived in `.agent-settings.yml` since the Phase-0 migration.",
1496
+ "So do NOT infer council availability from any project file, in either",
1497
+ "direction \u2014 this line is the answer, and `agent-config council:status`",
1498
+ "re-checks it on demand without spending anything.",
1499
+ a.configured ? "Never announce that no council is configured, and never substitute a weaker path on that basis." : "A substitute is legitimate here \u2014 say which one you are using and why.",
1500
+ "</council-availability>"
1501
+ ].join("\n");
1502
+ }
1503
+ function _workspaceRoot(env) {
1504
+ for (const key of ["project_dir", "projectDir", "cwd", "workspace_root"]) {
1505
+ const v = env[key];
1506
+ if (typeof v === "string" && v.trim() !== "") {
1507
+ return v;
1508
+ }
1509
+ }
1510
+ return process.cwd();
1511
+ }
1512
+ function run(stdin, options = {}) {
1513
+ let envelope = {};
1514
+ if (stdin.trim() !== "") {
1515
+ try {
1516
+ const parsed = JSON.parse(stdin);
1517
+ if (parsed !== null && typeof parsed === "object") {
1518
+ envelope = parsed;
1519
+ }
1520
+ } catch {
1521
+ return EXIT_ALLOW;
1522
+ }
1523
+ }
1524
+ const event = envelope["event"];
1525
+ const slot = typeof event === "string" && event !== "" ? event : "session_start";
1526
+ if (slot !== "session_start") {
1527
+ return EXIT_ALLOW;
1528
+ }
1529
+ const a = resolveAvailability(_workspaceRoot(envelope), options.env ?? process.env);
1530
+ if (a === null) {
1531
+ return EXIT_ALLOW;
1532
+ }
1533
+ process.stdout.write(
1534
+ `${JSON.stringify({
1535
+ decision: "allow",
1536
+ reason: `council-availability: ${a.configured ? "configured" : "not configured"}`,
1537
+ context: buildFact(a)
1538
+ })}
1539
+ `
1540
+ );
1541
+ return EXIT_ALLOW;
1542
+ }
1543
+ function main4(argv) {
1544
+ const args = argv ?? process.argv.slice(2);
1545
+ let env = process.env;
1546
+ void args;
1547
+ return run(readHookStdin(), { env });
1548
+ }
1549
+ function _isCliEntry4() {
1550
+ if (true) {
1551
+ return false;
1552
+ }
1553
+ if (process.argv[1] === void 0) {
1554
+ return false;
1555
+ }
1556
+ return import.meta.url === pathToFileURL4(path8.resolve(process.argv[1])).href;
1557
+ }
1558
+ if (_isCliEntry4()) {
1559
+ process.exit(main4());
1560
+ }
1561
+
1562
+ // src/scripts/audit_skill_overlap.ts
1563
+ var import_yaml2 = __toESM(require_dist(), 1);
1564
+ import { createHash as createHash3 } from "node:crypto";
1565
+ import * as fs10 from "node:fs";
1566
+ import * as path9 from "node:path";
1567
+ import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL5 } from "node:url";
1568
+ var _HERE = fileURLToPath5(import.meta.url);
1569
+ var ROOT2 = path9.resolve(path9.dirname(_HERE), "..", "..");
1570
+ var REPORT_DIR = path9.join(ROOT2, "agents", "reports");
1571
+ var OUT_JSON_NAME = "skill-overlap.json";
1572
+ var OUT_MD_NAME = "skill-overlap.md";
1573
+ var OUT_JSON = path9.join(REPORT_DIR, OUT_JSON_NAME);
1574
+ var OUT_MD = path9.join(REPORT_DIR, OUT_MD_NAME);
1575
+ var ALLOWLIST = path9.join(ROOT2, "src", "scripts", "audit_skill_overlap_allowlist.json");
1576
+ var CROSS_CACHE_NAME = "neighbour-overlap.json";
1577
+ var ALLOWLIST_CAP = 20;
1578
+ var FM_RE = /^---\n([\s\S]*?)\n---/m;
1579
+ var OVERLAP_THRESHOLD = 0.7;
1580
+ var STOPWORDS = /* @__PURE__ */ new Set([
1581
+ "the",
1582
+ "and",
1583
+ "for",
1584
+ "with",
1585
+ "when",
1586
+ "use",
1587
+ "or",
1588
+ "of",
1589
+ "to",
1590
+ "a",
1591
+ "an",
1592
+ "is",
1593
+ "in",
1594
+ "on",
1595
+ "by",
1596
+ "be",
1597
+ "at",
1598
+ "as",
1599
+ "it",
1600
+ "if",
1601
+ "are",
1602
+ "this",
1603
+ "that",
1604
+ "from",
1605
+ "but",
1606
+ "not",
1607
+ "can",
1608
+ "any",
1609
+ "all",
1610
+ "no",
1611
+ "after",
1612
+ "before",
1613
+ "during",
1614
+ "user",
1615
+ "agent",
1616
+ "code",
1617
+ "project",
1618
+ "via",
1619
+ "into",
1620
+ "onto",
1621
+ "even",
1622
+ "without",
1623
+ "naming",
1624
+ "run",
1625
+ "runs",
1626
+ "running",
1627
+ "each",
1628
+ "every",
1629
+ "one",
1630
+ "two",
1631
+ "now",
1632
+ "then",
1633
+ "also",
1634
+ "based",
1635
+ "default",
1636
+ "skill",
1637
+ "you",
1638
+ "your",
1639
+ "should",
1640
+ "must",
1641
+ "see",
1642
+ "do",
1643
+ "not"
1644
+ ]);
1645
+ function _isDir(p) {
1646
+ try {
1647
+ return fs10.statSync(p).isDirectory();
1648
+ } catch {
1649
+ return false;
1650
+ }
1651
+ }
1652
+ function _isFile(p) {
1653
+ try {
1654
+ return fs10.statSync(p).isFile();
1655
+ } catch {
1656
+ return false;
1657
+ }
1658
+ }
1659
+ function _default_skill_root() {
1660
+ return SRC_SKILLS();
1661
+ }
1662
+ function _keyword_vector(text) {
1663
+ const counter = /* @__PURE__ */ new Map();
1664
+ const re = /[a-z][a-z0-9_-]{2,}/g;
1665
+ const lowered = text.toLowerCase();
1666
+ let m;
1667
+ while ((m = re.exec(lowered)) !== null) {
1668
+ const t = m[0];
1669
+ if (!STOPWORDS.has(t)) {
1670
+ counter.set(t, (counter.get(t) ?? 0) + 1);
1671
+ }
1672
+ }
1673
+ return counter;
1674
+ }
1675
+ function _cosine(a, b) {
1676
+ const shared = [];
1677
+ for (const k of a.keys()) {
1678
+ if (b.has(k)) {
1679
+ shared.push(k);
1680
+ }
1681
+ }
1682
+ if (shared.length === 0) {
1683
+ return 0;
1684
+ }
1685
+ let num = 0;
1686
+ for (const t of shared) {
1687
+ num += a.get(t) * b.get(t);
1688
+ }
1689
+ let da = 0;
1690
+ for (const v of a.values()) {
1691
+ da += v * v;
1692
+ }
1693
+ let db = 0;
1694
+ for (const v of b.values()) {
1695
+ db += v * v;
1696
+ }
1697
+ da = Math.sqrt(da);
1698
+ db = Math.sqrt(db);
1699
+ return da && db ? num / (da * db) : 0;
1700
+ }
1701
+ function _parse(md, field = "body") {
1702
+ const text = fs10.readFileSync(md, "utf-8");
1703
+ let fm = {};
1704
+ let body = text;
1705
+ const m = FM_RE.exec(text);
1706
+ if (m && m.index === 0) {
1707
+ try {
1708
+ fm = (0, import_yaml2.parse)(m[1], { version: "1.1" }) ?? {};
1709
+ } catch {
1710
+ fm = {};
1711
+ }
1712
+ body = text.slice(m.index + m[0].length);
1713
+ }
1714
+ const isObj = fm !== null && typeof fm === "object" && !Array.isArray(fm);
1715
+ const name = isObj ? fm["name"] : null;
1716
+ const packsVal = isObj ? fm["packs"] : null;
1717
+ const descVal = isObj ? fm["description"] : null;
1718
+ const source = field === "description" ? descVal ? String(descVal) : "" : body;
1719
+ return {
1720
+ name: name ? String(name) : path9.basename(path9.dirname(md)),
1721
+ relpath: _relPosix(md, ROOT2),
1722
+ abspath: path9.resolve(md),
1723
+ packs: Array.isArray(packsVal) ? new Set(packsVal.map((x) => String(x))) : /* @__PURE__ */ new Set(),
1724
+ vector: _keyword_vector(source)
1725
+ };
1726
+ }
1727
+ function _relPosix(child, root) {
1728
+ return path9.relative(root, child).split(path9.sep).join("/");
1729
+ }
1730
+ function _rglobSkillMd(root) {
1731
+ const out = [];
1732
+ const walk = (dir) => {
1733
+ let entries;
1734
+ try {
1735
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
1736
+ } catch {
1737
+ return;
1738
+ }
1739
+ for (const ent of entries) {
1740
+ const full = path9.join(dir, ent.name);
1741
+ if (ent.name === "SKILL.md") {
1742
+ out.push(full);
1743
+ }
1744
+ if (ent.isDirectory() || ent.isSymbolicLink() && _isDir(full)) {
1745
+ walk(full);
1746
+ }
1747
+ }
1748
+ };
1749
+ walk(root);
1750
+ out.sort(_pathlibCompare);
1751
+ return out;
1752
+ }
1753
+ function _pathlibCompare(a, b) {
1754
+ const pa = a.split(path9.sep);
1755
+ const pb = b.split(path9.sep);
1756
+ const n = Math.min(pa.length, pb.length);
1757
+ for (let i = 0; i < n; i++) {
1758
+ const x = pa[i];
1759
+ const y = pb[i];
1760
+ if (x !== y) {
1761
+ return x < y ? -1 : 1;
1762
+ }
1763
+ }
1764
+ return pa.length - pb.length;
1765
+ }
1766
+ function collect(root = _default_skill_root(), field = "body") {
1767
+ const skills = [];
1768
+ const seen = /* @__PURE__ */ new Set();
1769
+ for (const md of _rglobSkillMd(root)) {
1770
+ if (md.split(path9.sep).includes("_archive")) {
1771
+ continue;
1772
+ }
1773
+ const key = path9.basename(path9.dirname(md));
1774
+ if (seen.has(key)) {
1775
+ continue;
1776
+ }
1777
+ seen.add(key);
1778
+ skills.push(_parse(md, field));
1779
+ }
1780
+ return skills;
1781
+ }
1782
+ function pyRound(x, ndigits) {
1783
+ const factor = 10 ** ndigits;
1784
+ const scaled = x * factor;
1785
+ const floor = Math.floor(scaled);
1786
+ const diff = scaled - floor;
1787
+ let r;
1788
+ if (diff > 0.5) {
1789
+ r = floor + 1;
1790
+ } else if (diff < 0.5) {
1791
+ r = floor;
1792
+ } else {
1793
+ r = floor % 2 === 0 ? floor : floor + 1;
1794
+ }
1795
+ return r / factor;
1796
+ }
1797
+ function find_pairs(skills, threshold) {
1798
+ const pairs = [];
1799
+ for (let i = 0; i < skills.length; i++) {
1800
+ for (let j = i + 1; j < skills.length; j++) {
1801
+ const a = skills[i];
1802
+ const b = skills[j];
1803
+ const sim = _cosine(a.vector, b.vector);
1804
+ if (sim >= threshold) {
1805
+ const shared = [];
1806
+ for (const p of a.packs) {
1807
+ if (b.packs.has(p)) {
1808
+ shared.push(p);
1809
+ }
1810
+ }
1811
+ shared.sort();
1812
+ pairs.push({
1813
+ a: a.name,
1814
+ b: b.name,
1815
+ a_path: a.relpath,
1816
+ b_path: b.relpath,
1817
+ similarity: pyRound(sim, 3),
1818
+ same_domain: shared.length > 0,
1819
+ shared_packs: shared
1820
+ });
1821
+ }
1822
+ }
1823
+ }
1824
+ return stableSort(pairs, (p) => [p.same_domain ? 0 : 1, -p.similarity]);
1825
+ }
1826
+ function skillMdsUnder(root) {
1827
+ return _rglobSkillMd(root).filter((md) => !md.split(path9.sep).includes("_archive"));
1828
+ }
1829
+ function crossPairsForFiles(neighbourFiles, ourRoot, threshold = OVERLAP_THRESHOLD, field = "body") {
1830
+ const theirs = [];
1831
+ for (const md of neighbourFiles) {
1832
+ try {
1833
+ theirs.push(_parse(md, field));
1834
+ } catch {
1835
+ }
1836
+ }
1837
+ const ours = collect(ourRoot, field);
1838
+ const out = [];
1839
+ for (const n of theirs) {
1840
+ for (const o of ours) {
1841
+ const sim = _cosine(n.vector, o.vector);
1842
+ if (sim < threshold) continue;
1843
+ out.push({
1844
+ neighbour: n.name,
1845
+ neighbour_path: n.abspath,
1846
+ ours: o.name,
1847
+ similarity: pyRound(sim, 3)
1848
+ });
1849
+ }
1850
+ }
1851
+ return stableSort(out, (p) => [-p.similarity]);
1852
+ }
1853
+ function crossRootOverlapMap(pairs) {
1854
+ const out = /* @__PURE__ */ new Map();
1855
+ for (const p of pairs) {
1856
+ const hit = out.get(p.neighbour_path);
1857
+ if (hit === void 0) {
1858
+ out.set(p.neighbour_path, { names: [p.ours], similarity: p.similarity });
1859
+ continue;
1860
+ }
1861
+ if (!hit.names.includes(p.ours)) hit.names.push(p.ours);
1862
+ if (p.similarity > hit.similarity) hit.similarity = p.similarity;
1863
+ }
1864
+ for (const hit of out.values()) hit.names.sort();
1865
+ return out;
1866
+ }
1867
+ function censusDigest(neighbourFiles, ourRoot, threshold, field = "body") {
1868
+ const h = createHash3("sha256");
1869
+ h.update(`${String(threshold)}
1870
+ ${field}
1871
+ `);
1872
+ const sides = [
1873
+ [...neighbourFiles].sort(),
1874
+ skillMdsUnder(ourRoot)
1875
+ ];
1876
+ for (const side of sides) {
1877
+ h.update("side\n");
1878
+ for (const md of side) {
1879
+ let bytes;
1880
+ try {
1881
+ bytes = fs10.readFileSync(md);
1882
+ } catch {
1883
+ continue;
1884
+ }
1885
+ h.update(path9.resolve(md));
1886
+ h.update("\0");
1887
+ h.update(createHash3("sha256").update(bytes).digest("hex"));
1888
+ h.update("\n");
1889
+ }
1890
+ }
1891
+ return h.digest("hex");
1892
+ }
1893
+ function cachedCrossRootPairs(neighbourFiles, ourRoot, cacheFile, threshold = OVERLAP_THRESHOLD, field = "body") {
1894
+ const digest = censusDigest(neighbourFiles, ourRoot, threshold, field);
1895
+ try {
1896
+ const parsed = JSON.parse(fs10.readFileSync(cacheFile, "utf-8"));
1897
+ if (parsed.digest === digest && Array.isArray(parsed.pairs)) {
1898
+ return { pairs: parsed.pairs, digest, cached: true };
1899
+ }
1900
+ } catch {
1901
+ }
1902
+ const pairs = crossPairsForFiles(neighbourFiles, ourRoot, threshold, field);
1903
+ const payload = { schema_version: 1, digest, threshold, pairs };
1904
+ try {
1905
+ fs10.mkdirSync(path9.dirname(cacheFile), { recursive: true });
1906
+ fs10.writeFileSync(cacheFile, `${JSON.stringify(payload, null, 2)}
1907
+ `, "utf-8");
1908
+ } catch {
1909
+ }
1910
+ return { pairs, digest, cached: false };
1911
+ }
1912
+ function stableSort(arr, key) {
1913
+ return arr.map((v, i) => [v, i]).sort((x, y) => {
1914
+ const kx = key(x[0]);
1915
+ const ky = key(y[0]);
1916
+ for (let i = 0; i < Math.max(kx.length, ky.length); i++) {
1917
+ const d = (kx[i] ?? 0) - (ky[i] ?? 0);
1918
+ if (d !== 0) {
1919
+ return d;
1920
+ }
1921
+ }
1922
+ return x[1] - y[1];
1923
+ }).map(([v]) => v);
1924
+ }
1925
+ function _pct(x) {
1926
+ const pct = pyRound(x * 100, 0);
1927
+ return `${pct}%`;
1928
+ }
1929
+ function render_md(skills, pairs, threshold, field = "body") {
1930
+ const merge = pairs.filter((p) => p.same_domain);
1931
+ const cross = pairs.filter((p) => !p.same_domain);
1932
+ const L = [
1933
+ "# Skill-family overlap report (6.0.0-C Phase 4 Step 8)\n",
1934
+ field === "description" ? `> **NON-CANONICAL measurement.** Cosine over the \`description:\` frontmatter only, across ${skills.length} skills, at \u2265 ${_pct(threshold)}. Routing happens on descriptions, so this answers a question the canonical body metric does not \u2014 but its scores are NOT comparable to the canonical 0.70 merge threshold and must never be mixed into a merge decision. The canonical run is the one without \`--descriptions\`.
1935
+ ` : `> Content cosine-similarity over ${skills.length} skills; pairs at \u2265 ${_pct(threshold)}. **Same-domain pairs (shared \`packs:\`) are the merge candidates** consumed by [\`evidence-based-pruning.md\`](../../docs/contracts/evidence-based-pruning.md). This report merges NOTHING \u2014 it is input to a future, human-driven consolidation roadmap.
1936
+ `,
1937
+ `
1938
+ - Skills scanned: **${skills.length}**`,
1939
+ `
1940
+ - Overlap pairs \u2265 ${_pct(threshold)}: **${pairs.length}** (${merge.length} same-domain merge candidates, ${cross.length} cross-domain)
1941
+ `,
1942
+ "\n## Merge candidates \u2014 same-domain, \u2265 threshold\n"
1943
+ ];
1944
+ if (merge.length === 0) {
1945
+ L.push("None \u2014 no same-domain pair exceeds the overlap threshold.\n");
1946
+ } else {
1947
+ L.push("| Skill A | Skill B | similarity | shared packs |");
1948
+ L.push("|---|---|--:|---|");
1949
+ for (const p of merge) {
1950
+ L.push(
1951
+ `| \`${p.a}\` | \`${p.b}\` | ${_pct(p.similarity)} | ${p.shared_packs.join(", ")} |`
1952
+ );
1953
+ }
1954
+ }
1955
+ L.push("\n## Cross-domain overlaps (informational, not merge candidates)\n");
1956
+ if (cross.length === 0) {
1957
+ L.push("None.\n");
1958
+ } else {
1959
+ L.push("| Skill A | Skill B | similarity |");
1960
+ L.push("|---|---|--:|");
1961
+ for (const p of cross) {
1962
+ L.push(`| \`${p.a}\` | \`${p.b}\` | ${_pct(p.similarity)} |`);
1963
+ }
1964
+ }
1965
+ return L.join("\n") + "\n";
1966
+ }
1967
+ function _pyJsonStr(s) {
1968
+ let out = '"';
1969
+ for (const ch of s) {
1970
+ const code = ch.codePointAt(0);
1971
+ if (ch === '"') {
1972
+ out += '\\"';
1973
+ } else if (ch === "\\") {
1974
+ out += "\\\\";
1975
+ } else if (ch === "\n") {
1976
+ out += "\\n";
1977
+ } else if (ch === "\r") {
1978
+ out += "\\r";
1979
+ } else if (ch === " ") {
1980
+ out += "\\t";
1981
+ } else if (ch === "\b") {
1982
+ out += "\\b";
1983
+ } else if (ch === "\f") {
1984
+ out += "\\f";
1985
+ } else if (code < 32) {
1986
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
1987
+ } else if (code < 127) {
1988
+ out += ch;
1989
+ } else if (code <= 65535) {
1990
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
1991
+ } else {
1992
+ const c = code - 65536;
1993
+ const hi = 55296 + (c >> 10);
1994
+ const lo = 56320 + (c & 1023);
1995
+ out += `\\u${hi.toString(16).padStart(4, "0")}\\u${lo.toString(16).padStart(4, "0")}`;
1996
+ }
1997
+ }
1998
+ return out + '"';
1999
+ }
2000
+ function _pyNumber(n) {
2001
+ if (Number.isInteger(n)) {
2002
+ return String(n);
2003
+ }
2004
+ return String(n);
2005
+ }
2006
+ function _floatStr(n) {
2007
+ if (Number.isInteger(n)) {
2008
+ return `${n}.0`;
2009
+ }
2010
+ return String(n);
2011
+ }
2012
+ function _pairToJson(p) {
2013
+ return {
2014
+ a: p.a,
2015
+ b: p.b,
2016
+ a_path: p.a_path,
2017
+ b_path: p.b_path,
2018
+ // similarity is a float (round(x,3)); force float repr.
2019
+ similarity: new FloatTag(p.similarity),
2020
+ same_domain: p.same_domain,
2021
+ shared_packs: p.shared_packs
2022
+ };
2023
+ }
2024
+ var FloatTag = class {
2025
+ constructor(value) {
2026
+ this.value = value;
2027
+ }
2028
+ value;
2029
+ };
2030
+ function main5(argv = null) {
2031
+ const args = parse_args4(argv ?? process.argv.slice(2));
2032
+ const root = args.root ?? _default_skill_root();
2033
+ if (args.neighbourRoot !== void 0) {
2034
+ return _mainCrossRoot(args, root);
2035
+ }
2036
+ const skills = collect(root, args.field);
2037
+ try {
2038
+ assertScanned({
2039
+ gate: "audit_skill_overlap",
2040
+ scanned: skills.length,
2041
+ units: "skill(s)",
2042
+ roots: [_relPosix(root, ROOT2)]
2043
+ });
2044
+ } catch (exc) {
2045
+ if (!(exc instanceof DeadScopeError)) {
2046
+ throw exc;
2047
+ }
2048
+ process.stderr.write(`\u274C ${exc.message}
2049
+ `);
2050
+ return 3;
2051
+ }
2052
+ process.stderr.write(`scanned: ${String(skills.length)}
2053
+ `);
2054
+ const pairs = find_pairs(skills, args.threshold);
2055
+ const outDir = args.outDir ?? REPORT_DIR;
2056
+ const outJson = path9.join(outDir, OUT_JSON_NAME);
2057
+ const outMd = path9.join(outDir, OUT_MD_NAME);
2058
+ fs10.mkdirSync(outDir, { recursive: true });
2059
+ const jsonObj = {
2060
+ threshold: new FloatTag(args.threshold),
2061
+ skills_scanned: skills.length,
2062
+ pairs: pairs.map(_pairToJson)
2063
+ };
2064
+ fs10.writeFileSync(outJson, _dumpsWithFloats(jsonObj), "utf-8");
2065
+ fs10.writeFileSync(outMd, render_md(skills, pairs, args.threshold, args.field), "utf-8");
2066
+ const merge = pairs.filter((p) => p.same_domain);
2067
+ if (!args.quiet) {
2068
+ process.stdout.write(
2069
+ `\u2705 Skill overlap: ${skills.length} skills, ${pairs.length} pair(s) \u2265 ${_pct(args.threshold)} (${merge.length} same-domain merge candidate(s)).
2070
+ `
2071
+ );
2072
+ process.stdout.write(` JSON: ${_relPosix(outJson, ROOT2)}
2073
+ `);
2074
+ process.stdout.write(` MD: ${_relPosix(outMd, ROOT2)}
2075
+ `);
2076
+ }
2077
+ if (args.printTable) {
2078
+ process.stdout.write(
2079
+ `\u2139\uFE0F skill overlap${args.strict ? "" : " (advisory)"}: ${skills.length} skills scanned, ${merge.length} same-domain pair(s) \u2265 ${_pct(args.threshold)}
2080
+ `
2081
+ );
2082
+ for (const p of merge) {
2083
+ process.stdout.write(
2084
+ ` ${p.similarity.toFixed(3)} ${p.a} \u2194 ${p.b} [${p.shared_packs.join(", ")}]
2085
+ `
2086
+ );
2087
+ }
2088
+ }
2089
+ if (!args.strict) {
2090
+ return 0;
2091
+ }
2092
+ let allow;
2093
+ try {
2094
+ allow = _loadAllowlist(args.allowlist ?? ALLOWLIST);
2095
+ } catch (exc) {
2096
+ process.stderr.write(`\u274C ${exc instanceof Error ? exc.message : String(exc)}
2097
+ `);
2098
+ return 2;
2099
+ }
2100
+ const blocking = merge.filter((p) => !allow.has(_pairKey(p.a, p.b)));
2101
+ if (blocking.length === 0) {
2102
+ return 0;
2103
+ }
2104
+ process.stderr.write(
2105
+ `\u274C audit_skill_overlap: ${blocking.length} same-pack pair(s) at or above ${_pct(args.threshold)} with no reviewed justification:
2106
+ `
2107
+ );
2108
+ for (const p of blocking) {
2109
+ process.stderr.write(
2110
+ ` ${p.similarity.toFixed(3)} ${p.a} \u2194 ${p.b} [${p.shared_packs.join(", ")}]
2111
+ `
2112
+ );
2113
+ }
2114
+ process.stderr.write(
2115
+ ` Merge the pair, or add {"pair": "${_pairKey(blocking[0].a, blocking[0].b)}", "reason": "\u2026"} to ${_relPosix(args.allowlist ?? ALLOWLIST, ROOT2)} (cap ${ALLOWLIST_CAP}).
2116
+ `
2117
+ );
2118
+ return 1;
2119
+ }
2120
+ function _mainCrossRoot(args, ourRoot) {
2121
+ const neighbourRoot = args.neighbourRoot;
2122
+ const outDir = args.outDir ?? REPORT_DIR;
2123
+ const cacheFile = args.crossCache ?? path9.join(outDir, CROSS_CACHE_NAME);
2124
+ const { pairs, digest, cached } = cachedCrossRootPairs(
2125
+ skillMdsUnder(neighbourRoot),
2126
+ ourRoot,
2127
+ cacheFile,
2128
+ args.threshold,
2129
+ args.field
2130
+ );
2131
+ const theirs = collect(neighbourRoot, args.field);
2132
+ process.stderr.write(`scanned: ${String(theirs.length)}
2133
+ `);
2134
+ if (!args.quiet) {
2135
+ process.stdout.write(
2136
+ `\u2705 Neighbour overlap: ${theirs.length} neighbour skill(s) \xD7 ${collect(ourRoot, args.field).length} of ours, ${pairs.length} pair(s) \u2265 ${_pct(args.threshold)} (${cached ? "from cache" : "recomputed"}).
2137
+ `
2138
+ );
2139
+ process.stdout.write(` census digest: ${digest.slice(0, 12)}
2140
+ `);
2141
+ process.stdout.write(` cache: ${_relPosix(cacheFile, ROOT2)}
2142
+ `);
2143
+ }
2144
+ const byPath = crossRootOverlapMap(pairs);
2145
+ for (const skill of theirs) {
2146
+ const hit = byPath.get(skill.abspath);
2147
+ if (hit === void 0) continue;
2148
+ process.stdout.write(
2149
+ ` ${hit.similarity.toFixed(3)} ${skill.name} also: ${hit.names.join(", ")}
2150
+ `
2151
+ );
2152
+ }
2153
+ return 0;
2154
+ }
2155
+ function _pairKey(a, b) {
2156
+ return a < b ? `${a}::${b}` : `${b}::${a}`;
2157
+ }
2158
+ function _loadAllowlist(file) {
2159
+ if (!_isFile(file)) {
2160
+ return /* @__PURE__ */ new Set();
2161
+ }
2162
+ const data = JSON.parse(fs10.readFileSync(file, "utf-8"));
2163
+ const entries = Array.isArray(data.entries) ? data.entries : [];
2164
+ if (entries.length > ALLOWLIST_CAP) {
2165
+ throw new Error(
2166
+ `audit_skill_overlap: allowlist has ${entries.length} entries (> ${ALLOWLIST_CAP}). Per the autonomous-execution allowlist-growth antipattern, the threshold is wrong, not the corpus \u2014 re-litigate the bar, do not grow the list.`
2167
+ );
2168
+ }
2169
+ const out = /* @__PURE__ */ new Set();
2170
+ for (const e of entries) {
2171
+ const [a, b] = String(e.pair).split("::");
2172
+ if (a === void 0 || b === void 0 || (e.reason ?? "").trim() === "") {
2173
+ throw new Error(
2174
+ `audit_skill_overlap: allowlist entry ${JSON.stringify(e)} needs "pair": "a::b" and a non-empty "reason".`
2175
+ );
2176
+ }
2177
+ out.add(_pairKey(a, b));
2178
+ }
2179
+ return out;
2180
+ }
2181
+ function _dumpsWithFloats(obj, level = 0) {
2182
+ if (obj instanceof FloatTag) {
2183
+ return _floatStr(obj.value);
2184
+ }
2185
+ if (obj === null || obj === void 0) {
2186
+ return "null";
2187
+ }
2188
+ if (typeof obj === "number") {
2189
+ return _pyNumber(obj);
2190
+ }
2191
+ if (typeof obj === "string") {
2192
+ return _pyJsonStr(obj);
2193
+ }
2194
+ if (obj === true) {
2195
+ return "true";
2196
+ }
2197
+ if (obj === false) {
2198
+ return "false";
2199
+ }
2200
+ if (Array.isArray(obj)) {
2201
+ if (obj.length === 0) {
2202
+ return "[]";
2203
+ }
2204
+ const pad2 = " ".repeat(2 * (level + 1));
2205
+ const closePad2 = " ".repeat(2 * level);
2206
+ return `[
2207
+ ${obj.map((v) => pad2 + _dumpsWithFloats(v, level + 1)).join(",\n")}
2208
+ ${closePad2}]`;
2209
+ }
2210
+ const keys = Object.keys(obj);
2211
+ if (keys.length === 0) {
2212
+ return "{}";
2213
+ }
2214
+ const pad = " ".repeat(2 * (level + 1));
2215
+ const closePad = " ".repeat(2 * level);
2216
+ const parts = keys.map(
2217
+ (k) => `${pad}${_pyJsonStr(k)}: ${_dumpsWithFloats(obj[k], level + 1)}`
2218
+ );
2219
+ return `{
2220
+ ${parts.join(",\n")}
2221
+ ${closePad}}`;
2222
+ }
2223
+ function parse_args4(argv) {
2224
+ const args = {
2225
+ threshold: OVERLAP_THRESHOLD,
2226
+ quiet: false,
2227
+ printTable: false,
2228
+ strict: false,
2229
+ field: "body"
2230
+ };
2231
+ const takeValue = (flag, inline, next) => {
2232
+ const v = inline ?? next();
2233
+ if (v === void 0) {
2234
+ process.stderr.write(`argument ${flag}: expected one argument
2235
+ `);
2236
+ process.exit(2);
2237
+ }
2238
+ return v;
2239
+ };
2240
+ for (let i = 0; i < argv.length; i++) {
2241
+ const a = argv[i];
2242
+ if (a === "--quiet") {
2243
+ args.quiet = true;
2244
+ } else if (a === "--strict") {
2245
+ args.strict = true;
2246
+ } else if (a === "--allowlist" || a.startsWith("--allowlist=")) {
2247
+ const inline = a.startsWith("--allowlist=") ? a.slice("--allowlist=".length) : void 0;
2248
+ args.allowlist = path9.resolve(ROOT2, takeValue("--allowlist", inline, () => argv[++i]));
2249
+ } else if (a === "--descriptions") {
2250
+ args.field = "description";
2251
+ } else if (a === "--print-table") {
2252
+ args.printTable = true;
2253
+ } else if (a === "--threshold" || a.startsWith("--threshold=")) {
2254
+ const inline = a.startsWith("--threshold=") ? a.slice("--threshold=".length) : void 0;
2255
+ args.threshold = Number(takeValue("--threshold", inline, () => argv[++i]));
2256
+ } else if (a === "--neighbour-root" || a.startsWith("--neighbour-root=")) {
2257
+ const inline = a.startsWith("--neighbour-root=") ? a.slice("--neighbour-root=".length) : void 0;
2258
+ args.neighbourRoot = path9.resolve(
2259
+ ROOT2,
2260
+ takeValue("--neighbour-root", inline, () => argv[++i])
2261
+ );
2262
+ } else if (a === "--cross-cache" || a.startsWith("--cross-cache=")) {
2263
+ const inline = a.startsWith("--cross-cache=") ? a.slice("--cross-cache=".length) : void 0;
2264
+ args.crossCache = path9.resolve(ROOT2, takeValue("--cross-cache", inline, () => argv[++i]));
2265
+ } else if (a === "--root" || a.startsWith("--root=")) {
2266
+ const inline = a.startsWith("--root=") ? a.slice("--root=".length) : void 0;
2267
+ args.root = path9.resolve(ROOT2, takeValue("--root", inline, () => argv[++i]));
2268
+ } else if (a === "--out-dir" || a.startsWith("--out-dir=")) {
2269
+ const inline = a.startsWith("--out-dir=") ? a.slice("--out-dir=".length) : void 0;
2270
+ args.outDir = path9.resolve(ROOT2, takeValue("--out-dir", inline, () => argv[++i]));
2271
+ } else {
2272
+ process.stderr.write(`unrecognized arguments: ${a}
2273
+ `);
2274
+ process.exit(2);
2275
+ }
2276
+ }
2277
+ return args;
2278
+ }
2279
+ function _isCliEntry5() {
2280
+ if (process.argv[1] === void 0) {
2281
+ return false;
2282
+ }
2283
+ const argvUrl = pathToFileURL5(path9.resolve(process.argv[1])).href;
2284
+ if (import.meta.url === argvUrl) {
2285
+ return true;
2286
+ }
2287
+ try {
2288
+ const here = fs10.realpathSync(fileURLToPath5(import.meta.url));
2289
+ const argv = fs10.realpathSync(path9.resolve(process.argv[1]));
2290
+ return here === argv;
2291
+ } catch {
2292
+ return false;
2293
+ }
2294
+ }
2295
+ var _isMain = _isCliEntry5();
2296
+ if (_isMain) {
2297
+ process.exitCode = main5();
2298
+ }
2299
+
2300
+ // src/scripts/_cli/cmd_doctor_neighbours.ts
2301
+ function gatedEvents(packageRoot) {
2302
+ try {
2303
+ const file = path10.join(packageRoot, "src", "scripts", "hook_manifest.yaml");
2304
+ const doc = YAML.parse(fs11.readFileSync(file, "utf-8"));
2305
+ return gatedEventsFrom(doc, doc.native_event_aliases ?? {});
2306
+ } catch {
2307
+ return /* @__PURE__ */ new Set();
2308
+ }
2309
+ }
2310
+ function ourSkillNames(packageRoot) {
2311
+ try {
2312
+ return new Set(
2313
+ fs11.readdirSync(path10.join(packageRoot, "src", "skills"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name)
2314
+ );
2315
+ } catch {
2316
+ return /* @__PURE__ */ new Set();
2317
+ }
2318
+ }
2319
+ function runCensus(projectRoot, packageRoot, homeRoot) {
2320
+ const scanned = /* @__PURE__ */ new Map();
2321
+ const scan = (p) => {
2322
+ const hit = scanned.get(p);
2323
+ if (hit !== void 0) return hit;
2324
+ const kind = scanNeighbourBody(p).kind;
2325
+ scanned.set(p, kind);
2326
+ return kind;
2327
+ };
2328
+ const base = {
2329
+ gatedEvents: gatedEvents(packageRoot),
2330
+ templatesRoot: path10.join(packageRoot, "src", "agent-src", "templates"),
2331
+ ourSkillNames: ourSkillNames(packageRoot),
2332
+ packageRoot,
2333
+ scan,
2334
+ ...homeRoot === void 0 ? {} : { homeRoot }
2335
+ };
2336
+ const first = census(projectRoot, base);
2337
+ const ourRoot = path10.join(packageRoot, "src", "skills");
2338
+ let overlaps = /* @__PURE__ */ new Map();
2339
+ if (first.skills.length > 0 && fs11.existsSync(ourRoot)) {
2340
+ try {
2341
+ const { pairs } = cachedCrossRootPairs(
2342
+ first.skills.map((s) => s.source),
2343
+ ourRoot,
2344
+ path10.join(projectRoot, "agents", "reports", CROSS_CACHE_NAME),
2345
+ OVERLAP_THRESHOLD
2346
+ );
2347
+ overlaps = crossRootOverlapMap(pairs);
2348
+ } catch {
2349
+ }
2350
+ }
2351
+ const full = census(projectRoot, { ...base, overlaps });
2352
+ writeScanCache(
2353
+ scanCachePath(projectRoot),
2354
+ // `fileDigest`, not the census's own `digest`: the ranker re-digests the
2355
+ // file bytes it is about to index, and a cache written under a different
2356
+ // digest function would read as `digest-changed` on every prompt.
2357
+ full.skills.map((s) => ({
2358
+ qualified: s.qualified,
2359
+ digest: fileDigest(s.source) ?? "",
2360
+ unscanned: s.unscanned
2361
+ }))
2362
+ );
2363
+ return full;
2364
+ }
2365
+ function contradictionCandidates(projectRoot, packageRoot, homeRoot) {
2366
+ const ours = ourSkillNames(packageRoot);
2367
+ const c = census(projectRoot, {
2368
+ gatedEvents: gatedEvents(packageRoot),
2369
+ templatesRoot: path10.join(packageRoot, "src", "agent-src", "templates"),
2370
+ ourSkillNames: ours,
2371
+ packageRoot,
2372
+ scan: (p) => scanNeighbourBody(p).kind,
2373
+ ...homeRoot === void 0 ? {} : { homeRoot }
2374
+ });
2375
+ const sources = [];
2376
+ for (const name of ["CLAUDE.md", "AGENTS.md"]) {
2377
+ const file = path10.join(projectRoot, name);
2378
+ if (fs11.existsSync(file)) sources.push({ label: name, file, rank: RANK_PROJECT });
2379
+ }
2380
+ for (const r of c.rule_files) {
2381
+ if (scanNeighbourBody(r.source).kind === null) {
2382
+ sources.push({ label: r.id, file: r.source, rank: RANK_NEIGHBOUR_ALWAYS_ON });
2383
+ }
2384
+ }
2385
+ for (const s of c.skills) {
2386
+ if (s.unscanned === null) sources.push({ label: s.qualified, file: s.source, rank: RANK_NEIGHBOUR_SKILL });
2387
+ }
2388
+ const pairs = modalityPairs(sources.flatMap((src) => extractInstructions(src)));
2389
+ for (const s of c.skills) {
2390
+ const name = s.qualified.split(":").slice(1).join(":") || s.qualified;
2391
+ if (ours.has(name)) pairs.push(collisionPair(s.qualified, name));
2392
+ }
2393
+ return pairs;
2394
+ }
2395
+ function councilSeat(projectRoot) {
2396
+ return (prompt) => {
2397
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/gu, "-");
2398
+ const q = path10.join(projectRoot, "agents", "runtime", "council", "questions", `neighbour-contradictions-${stamp}.md`);
2399
+ const out = path10.join(projectRoot, "agents", "runtime", "council", "responses", `neighbour-contradictions-${stamp}.md`);
2400
+ try {
2401
+ fs11.mkdirSync(path10.dirname(q), { recursive: true });
2402
+ fs11.writeFileSync(q, prompt, "utf-8");
2403
+ const args = ["council", "run", q, "--single", "--prompt-mode", "analysis", "--output", out, "--confirm", "--invocation", "user_explicit"];
2404
+ const res = spawnSync("agent-config", args, { cwd: projectRoot, encoding: "utf-8" });
2405
+ if (res.status !== 0) return null;
2406
+ const doc = JSON.parse(fs11.readFileSync(out, "utf-8"));
2407
+ return doc.responses?.map((r) => r.text ?? "").join("\n") ?? null;
2408
+ } catch {
2409
+ return null;
2410
+ }
2411
+ };
2412
+ }
2413
+ function runContradictions(projectRoot, packageRoot, opts = {}) {
2414
+ const configured = opts.configured ?? resolveAvailability(projectRoot, process.env)?.configured ?? false;
2415
+ const pairs = contradictionCandidates(projectRoot, packageRoot, opts.homeRoot);
2416
+ if (!configured) {
2417
+ return {
2418
+ lines: [` contradictions: n/a \u2014 no council configured (${pairs.length} candidate pair(s) formed, none reviewed)`],
2419
+ rows: null
2420
+ };
2421
+ }
2422
+ if (pairs.length === 0) return { lines: renderReview([]), rows: [] };
2423
+ let rubric = "";
2424
+ for (const rel of [["dist", "agent-src", "rules"], ["src", "rules"]]) {
2425
+ try {
2426
+ rubric = fs11.readFileSync(path10.join(packageRoot, ...rel, "neighbour-precedence.md"), "utf-8");
2427
+ break;
2428
+ } catch {
2429
+ }
2430
+ }
2431
+ const answer = (opts.seat ?? councilSeat(projectRoot))(seatPrompt(pairs, rubric));
2432
+ if (answer === null) {
2433
+ return { lines: [` contradictions: n/a \u2014 the council seat did not answer (${pairs.length} candidate pair(s) formed)`], rows: null };
2434
+ }
2435
+ const rows = reviewRows(pairs, parseVerdicts(answer, pairs.length));
2436
+ return { lines: renderReview(rows), rows };
2437
+ }
2438
+ function summaryLine(c) {
2439
+ const counts = [
2440
+ `${c.hook_groups.length} hook group(s)`,
2441
+ `${c.skills.length} skill(s)`,
2442
+ `${c.commands.length} command(s)`,
2443
+ `${c.agents.length} agent(s)`,
2444
+ `${c.mcp_servers.length} mcp server(s)`,
2445
+ `${c.rule_files.length} rule file(s)`,
2446
+ `${c.instruction_sections.length} instruction section(s)`
2447
+ ].join(", ");
2448
+ const labels = c.hosts.map((h) => `${h.host}=${h.label}`).join(" ");
2449
+ return ` \u{1F3D8}\uFE0F neighbours: ${counts}${labels === "" ? "" : ` \xB7 ${labels}`} (run \`doctor neighbours\` for the census)`;
2450
+ }
2451
+ function renderText(c) {
2452
+ const out = [];
2453
+ out.push(` \u{1F4CD} project_root: ${c.project_root}`);
2454
+ out.push("");
2455
+ for (const h of c.hosts) {
2456
+ out.push(` ${h.host}: ${h.label} \u2014 ${h.label_reason}`);
2457
+ for (const e of h.hook_groups) {
2458
+ const matcher = e.matcher === null ? "" : ` matcher=${e.matcher}`;
2459
+ const timeout = e.timeout === null ? "" : ` timeout=${e.timeout}`;
2460
+ out.push(` ${e.event}: ${e.command_head}${matcher}${timeout} effect=${e.effect}`);
2461
+ }
2462
+ }
2463
+ const section = (title, rows) => {
2464
+ if (rows.length === 0) return;
2465
+ out.push("");
2466
+ out.push(` ${title} (${rows.length})`);
2467
+ for (const r of rows) out.push(` ${r.id}`);
2468
+ };
2469
+ if (c.skills.length > 0) {
2470
+ out.push("");
2471
+ out.push(` skills (${c.skills.length})`);
2472
+ for (const s of c.skills) {
2473
+ const also = s.also.length === 0 ? "" : ` also: ${s.also.join(", ")}`;
2474
+ const why = s.unscanned === null ? "" : ` unscanned: ${s.unscanned}`;
2475
+ out.push(` ${s.qualified} compat: ${s.compat}${also}${why}`);
2476
+ }
2477
+ }
2478
+ section("commands", c.commands);
2479
+ section("agents", c.agents);
2480
+ if (c.mcp_servers.length > 0) {
2481
+ out.push("");
2482
+ out.push(` mcp servers (${c.mcp_servers.length})`);
2483
+ for (const m of c.mcp_servers) {
2484
+ out.push(
2485
+ ` ${m.id} tools used (${m.tools_window_days}d): ${m.tools_used_30d} advertised: ${m.tools_advertised}`
2486
+ );
2487
+ }
2488
+ }
2489
+ section("rule files", c.rule_files);
2490
+ section("instruction sections", c.instruction_sections);
2491
+ if (c.warnings.length > 0) {
2492
+ out.push("");
2493
+ for (const w of c.warnings) out.push(` \u26A0\uFE0F ${w.kind}: ${w.detail}`);
2494
+ }
2495
+ out.push("");
2496
+ out.push(
2497
+ " The host owns load order. This census says what is there and what each entry would do;"
2498
+ );
2499
+ out.push(" it cannot say what ran first, and no label here refuses anything.");
2500
+ return out;
2501
+ }
2502
+ function main6(argv = process.argv.slice(2)) {
2503
+ let json = false;
2504
+ let contradictions = false;
2505
+ let root = process.cwd();
2506
+ let home;
2507
+ for (let i = 0; i < argv.length; i += 1) {
2508
+ if (argv[i] === "--json") json = true;
2509
+ else if (argv[i] === "--contradictions") contradictions = true;
2510
+ else if (argv[i] === "--project") {
2511
+ root = argv[i + 1] ?? root;
2512
+ i += 1;
2513
+ } else if (argv[i] === "--home") {
2514
+ home = argv[i + 1];
2515
+ i += 1;
2516
+ } else if (argv[i] === "neighbours") {
2517
+ continue;
2518
+ } else {
2519
+ process.stderr.write(`doctor neighbours: unknown argument: ${argv[i]}
2520
+ `);
2521
+ return 2;
2522
+ }
2523
+ }
2524
+ if (contradictions) {
2525
+ const r = runContradictions(path10.resolve(root), resolvePackageRoot(import.meta.url), home === void 0 ? {} : { homeRoot: home });
2526
+ if (json) process.stdout.write(`${JSON.stringify({ contradictions: r.rows })}
2527
+ `);
2528
+ else for (const line of r.lines) process.stdout.write(`${line}
2529
+ `);
2530
+ return 0;
2531
+ }
2532
+ const c = runCensus(path10.resolve(root), resolvePackageRoot(import.meta.url), home);
2533
+ if (json) {
2534
+ process.stdout.write(`${JSON.stringify(c)}
2535
+ `);
2536
+ } else {
2537
+ for (const line of renderText(c)) process.stdout.write(`${line}
2538
+ `);
2539
+ }
2540
+ return 0;
2541
+ }
2542
+ function _isCliEntry6() {
2543
+ const bundled = true;
2544
+ const cliDelegate = true;
2545
+ if (bundled && !cliDelegate) return false;
2546
+ if (process.argv[1] === void 0) return false;
2547
+ if (cliDelegate && path10.basename(process.argv[1], ".js") === "cmd_doctor_neighbours") {
2548
+ return true;
2549
+ }
2550
+ const argvUrl = pathToFileURL6(path10.resolve(process.argv[1])).href;
2551
+ if (import.meta.url === argvUrl) return true;
2552
+ try {
2553
+ return fs11.realpathSync(fileURLToPath6(import.meta.url)) === fs11.realpathSync(path10.resolve(process.argv[1]));
2554
+ } catch {
2555
+ return false;
2556
+ }
2557
+ }
2558
+ if (_isCliEntry6()) {
2559
+ process.exit(main6());
2560
+ }
2561
+
2562
+ export {
2563
+ runCensus,
2564
+ contradictionCandidates,
2565
+ runContradictions,
2566
+ summaryLine,
2567
+ renderText,
2568
+ main6 as main
2569
+ };