@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.12 → 0.1.26

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 (405) hide show
  1. package/bin/adl +2 -2
  2. package/bin/git +16 -17
  3. package/bin/git-ado +25 -24
  4. package/bin/git-defaults +173 -0
  5. package/bin/health-check +8 -1
  6. package/bin/kimi-hooks +1 -1
  7. package/bin/lib/adl-package.sh +257 -0
  8. package/bin/lib/fleet-host.sh +8 -5
  9. package/bin/reconcile +482 -232
  10. package/bin/reconcile-host +30 -20
  11. package/bin/setup +20 -17
  12. package/bin/shell-path-policy +15 -5
  13. package/bin/sync-and-reconcile +62 -64
  14. package/bin/validate-manifest +33 -9
  15. package/package.json +2 -10
  16. package/packages/agent-hooks/README.md +14 -9
  17. package/packages/agent-hooks/assets/hooks.json +0 -20
  18. package/packages/agent-hooks/src/cli/args.ts +1 -1
  19. package/packages/agent-hooks/src/cli/doctor.ts +6 -58
  20. package/packages/agent-hooks/src/deploy/deploy.ts +12 -41
  21. package/packages/agent-hooks/src/deploy/doctor.ts +8 -11
  22. package/packages/agent-hooks/src/deploy/index.ts +14 -14
  23. package/packages/agent-hooks/src/deploy/json-merge.ts +7 -8
  24. package/packages/agent-hooks/src/deploy/legacy-lock.ts +2 -2
  25. package/packages/agent-hooks/src/deploy/list.ts +1 -1
  26. package/packages/agent-hooks/src/deploy/lock.ts +31 -12
  27. package/packages/agent-hooks/src/deploy/profile.ts +3 -4
  28. package/packages/agent-hooks/src/deploy/source.ts +1 -1
  29. package/packages/agent-hooks/src/gating/config.ts +10 -24
  30. package/packages/agent-hooks/src/gating/root.ts +3 -3
  31. package/packages/agent-hooks/src/hooks/briefing/handler.ts +2 -2
  32. package/packages/agent-hooks/src/hooks/briefing/index.ts +27 -287
  33. package/packages/agent-hooks/src/hooks/contract.ts +17 -17
  34. package/packages/agent-hooks/src/hooks/crg/background.ts +27 -0
  35. package/packages/agent-hooks/src/hooks/crg/index.ts +7 -6
  36. package/packages/agent-hooks/src/hooks/log.ts +8 -10
  37. package/packages/agent-hooks/src/hooks/user-prompt-submit/match.ts +1 -1
  38. package/packages/agent-hooks/src/hooks/user-prompt-submit/tokenizer.ts +1 -1
  39. package/packages/agent-hooks/src/hooks/user-prompt-submit/vector.ts +6 -5
  40. package/packages/agent-hooks/src/hosts/registry.ts +1 -1
  41. package/packages/agent-hooks/src/log/adl-dir.ts +44 -0
  42. package/packages/agent-hooks/src/log/writer.ts +16 -2
  43. package/packages/agent-hooks/src/main.ts +7 -41
  44. package/packages/agent-hooks/src/pre-tool-use/handler.ts +71 -82
  45. package/packages/agent-hooks/src/rules/engine.ts +13 -17
  46. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +3 -3
  47. package/packages/agent-hooks/src/rules/impl/adl-git-credential-bypass.ts +12 -14
  48. package/packages/agent-hooks/src/rules/impl/adl-git-identity.ts +28 -28
  49. package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +8 -9
  50. package/packages/agent-hooks/src/rules/impl/adl-human-azdo-bypass.ts +56 -59
  51. package/packages/agent-hooks/src/rules/impl/adl-identity.ts +16 -23
  52. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +19 -16
  53. package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +2 -2
  54. package/packages/agent-hooks/src/rules/impl/adl-pr-review-role.ts +9 -12
  55. package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +5 -7
  56. package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +411 -0
  57. package/packages/agent-hooks/src/rules/impl/adl-token-minting-role.ts +4 -5
  58. package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +6 -7
  59. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +77 -70
  60. package/packages/agent-hooks/src/rules/impl/deny-allowlist.ts +159 -0
  61. package/packages/agent-hooks/src/rules/impl/git-helpers.ts +21 -23
  62. package/packages/agent-hooks/src/rules/impl/git-worktree.ts +14 -28
  63. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -3
  64. package/packages/agent-hooks/src/rules/impl/messages.ts +1 -1
  65. package/packages/agent-hooks/src/rules/impl/pr-completion.ts +1 -1
  66. package/packages/agent-hooks/src/rules/impl/tool-azure-devops.ts +35 -46
  67. package/packages/agent-hooks/src/rules/impl/tool-find.ts +34 -7
  68. package/packages/agent-hooks/src/rules/impl/tool-grep.ts +34 -12
  69. package/packages/agent-hooks/src/rules/impl/tool-native-search.ts +52 -9
  70. package/packages/agent-hooks/src/rules/ports.ts +5 -7
  71. package/packages/agent-hooks/src/rules/registry.ts +4 -6
  72. package/packages/agent-hooks/src/rules/shell/git-identity.ts +86 -101
  73. package/packages/agent-hooks/src/rules/shell/git-internals.ts +1 -1
  74. package/packages/agent-hooks/src/rules/shell/invocation.ts +24 -118
  75. package/packages/agent-hooks/src/rules/shell/piped-script.ts +1 -1
  76. package/packages/agent-hooks/src/rules/shell/prefixes.ts +1 -1
  77. package/packages/agent-hooks/src/rules/shell/protected-carrier-writes.ts +11 -10
  78. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +26 -26
  79. package/packages/agent-hooks/src/rules/shell/substitution.ts +1 -1
  80. package/packages/agent-hooks/src/rules/shell/xargs.ts +2 -2
  81. package/packages/agent-hooks/src/rules/spec.ts +2 -2
  82. package/packages/agent-settings/README.md +287 -0
  83. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +14 -16
  84. package/packages/agent-settings/assets/agents/architect.toml +2 -0
  85. package/packages/agent-settings/assets/agents/code-reviewer.toml +2 -0
  86. package/packages/agent-settings/assets/agents/executor.toml +2 -0
  87. package/packages/agent-settings/assets/agents/explore.toml +2 -0
  88. package/packages/agent-settings/assets/agents/planner.toml +2 -0
  89. package/packages/agent-settings/assets/mcp.json +0 -4
  90. package/packages/agent-settings/deploy-profile.json +139 -0
  91. package/packages/agent-settings/deploy-profile.schema.json +147 -0
  92. package/packages/agent-settings/managed/README.md +29 -30
  93. package/packages/agent-settings/managed/codex-requirements.toml +9 -9
  94. package/packages/agent-settings/package.json +20 -0
  95. package/packages/agent-settings/src/deploy/agent-render.ts +2 -2
  96. package/packages/agent-settings/src/deploy/agent-toml.ts +1 -1
  97. package/packages/agent-settings/src/deploy/agents-deploy.ts +17 -3
  98. package/packages/agent-settings/src/deploy/doctor.ts +3 -83
  99. package/packages/agent-settings/src/deploy/index.ts +6 -33
  100. package/packages/agent-settings/src/deploy/legacy-lock.ts +6 -6
  101. package/packages/agent-settings/src/deploy/list.ts +5 -19
  102. package/packages/agent-settings/src/deploy/lock.ts +8 -18
  103. package/packages/agent-settings/src/deploy/mcp-deploy.ts +10 -5
  104. package/packages/agent-settings/src/deploy/mcp-merge.ts +22 -3
  105. package/packages/agent-settings/src/deploy/mcp-source.ts +1 -1
  106. package/packages/agent-settings/src/deploy/profile.ts +11 -35
  107. package/packages/agent-settings/src/deploy/retire-lean-ctx.ts +184 -0
  108. package/packages/agent-settings/src/deploy/settings.ts +17 -22
  109. package/packages/agent-settings/src/global-surface-sweep.ts +0 -2
  110. package/packages/agent-settings/src/kimi-hooks.ts +339 -192
  111. package/packages/agent-settings/src/opencode-hooks.ts +1 -1
  112. package/packages/agent-settings/tsconfig.json +23 -0
  113. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +120 -0
  114. package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +276 -0
  115. package/packages/agent-skills/assets/debug-issue/SKILL.md +8 -7
  116. package/packages/agent-skills/assets/explore-codebase/SKILL.md +13 -13
  117. package/packages/agent-skills/assets/refactor-safely/SKILL.md +12 -11
  118. package/packages/agent-skills/assets/review-changes/SKILL.md +7 -7
  119. package/packages/agent-skills/assets/testing/references/e2e.md +37 -0
  120. package/packages/agent-skills/assets/testing/references/integration.md +37 -0
  121. package/packages/agent-skills/assets/testing/references/unit.md +32 -0
  122. package/packages/agent-skills/dist/deploy.d.ts +2 -0
  123. package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
  124. package/packages/agent-skills/dist/deploy.js +27 -1
  125. package/packages/agent-skills/dist/deploy.js.map +1 -1
  126. package/packages/agent-skills/dist/doctor.d.ts +0 -2
  127. package/packages/agent-skills/dist/doctor.d.ts.map +1 -1
  128. package/packages/agent-skills/dist/doctor.js +1 -1
  129. package/packages/agent-skills/dist/doctor.js.map +1 -1
  130. package/packages/agent-skills/dist/fs-utils.d.ts +0 -1
  131. package/packages/agent-skills/dist/fs-utils.d.ts.map +1 -1
  132. package/packages/agent-skills/dist/fs-utils.js +1 -1
  133. package/packages/agent-skills/dist/fs-utils.js.map +1 -1
  134. package/packages/agent-skills/src/deploy.ts +29 -1
  135. package/packages/agent-skills/src/doctor.ts +1 -1
  136. package/packages/agent-skills/src/fs-utils.ts +1 -1
  137. package/packages/azdo/README.md +10 -9
  138. package/packages/azdo/package.json +1 -3
  139. package/packages/azdo/src/azdo-pl.ts +2 -54
  140. package/packages/azdo/src/azdo-pr-review.ts +4 -4
  141. package/packages/azdo/src/azdo-repo-doctor.ts +101 -52
  142. package/packages/azdo/src/azdo-wit.ts +41 -2
  143. package/packages/azdo/src/lib/agent-identity.ts +12 -14
  144. package/packages/azdo/src/lib/agent-token.ts +2 -2
  145. package/packages/azdo/src/lib/client.ts +0 -5
  146. package/packages/azdo/src/lib/graph-identity.ts +4 -4
  147. package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
  148. package/packages/azdo/src/workflow-rules.ts +128 -0
  149. package/packages/fleet/manifest.json +35 -11
  150. package/packages/fleet/manifest.schema.json +62 -10
  151. package/packages/fleet/src/default-agent-file.ts +3 -3
  152. package/packages/fleet/src/install-adl-package.ts +73 -15
  153. package/packages/fleet/src/install-embedding-model.ts +13 -13
  154. package/packages/fleet/src/managed-settings.ts +7 -7
  155. package/packages/fleet/src/repo-maintain.ts +91 -55
  156. package/packages/fleet/src/scratch-reap.ts +5 -5
  157. package/packages/fleet/src/vscode-settings.ts +5 -5
  158. package/packages/repo-controls/README.md +141 -43
  159. package/packages/repo-controls/assets/promote.ts +4 -4
  160. package/packages/repo-controls/assets/reconcile-guard.sh +10 -46
  161. package/packages/repo-controls/assets/version-bump.sh +31 -84
  162. package/packages/repo-controls/bunfig.toml +6 -7
  163. package/packages/repo-controls/package/adl-compliance/AGENTS.md +5 -6
  164. package/packages/repo-controls/package/adl-compliance/conftest.py +3 -1
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +4 -16
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +9 -2
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +1 -0
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +11 -0
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/__init__.py +7 -0
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/check.py +266 -0
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/c.md +1 -0
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/d.md +1 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.hidden.md +1 -0
  174. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/a.md +1 -0
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/b.md +1 -0
  176. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/data.csv +1 -0
  177. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/diagram.png +1 -0
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/mock.png +1 -0
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/sub/nested.md +1 -0
  180. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs.md +1 -0
  181. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/grants.yaml +4 -0
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p1.md +1 -0
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p2.md +1 -0
  184. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p3.md +1 -0
  185. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p4.md +1 -0
  186. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p5.md +1 -0
  187. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/build.md +1 -0
  188. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/overview.md +1 -0
  189. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/install.md +1 -0
  190. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/start.md +1 -0
  191. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/grants.yaml +4 -0
  192. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-001-tenancy.md +1 -0
  193. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-002-carrier.md +1 -0
  194. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-003-cli.md +1 -0
  195. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-old.md +1 -0
  196. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-older.md +1 -0
  197. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/a.md +1 -0
  198. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/b.md +1 -0
  199. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/c.md +1 -0
  200. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/grants.yaml +4 -0
  201. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-01.md +1 -0
  202. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-02.md +1 -0
  203. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-03.md +1 -0
  204. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-04.md +1 -0
  205. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-05.md +1 -0
  206. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-06.md +1 -0
  207. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-07.md +1 -0
  208. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-08.md +1 -0
  209. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-09.md +1 -0
  210. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-10.md +1 -0
  211. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-11.md +1 -0
  212. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-12.md +1 -0
  213. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-13.md +1 -0
  214. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-14.md +1 -0
  215. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-15.md +1 -0
  216. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-16.md +1 -0
  217. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/grants.yaml +4 -0
  218. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/a.md +1 -0
  219. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/b.md +1 -0
  220. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/c.md +1 -0
  221. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/grants.yaml +4 -0
  222. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/a.md +1 -0
  223. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/b.md +1 -0
  224. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/c.md +1 -0
  225. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/grants.yaml +4 -0
  226. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/a.md +1 -0
  227. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/b.md +1 -0
  228. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/c.md +1 -0
  229. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/d.md +1 -0
  230. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/e.md +1 -0
  231. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/grants.yaml +4 -0
  232. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/a.md +1 -0
  233. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/b.md +1 -0
  234. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/c.md +1 -0
  235. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/short/only.md +1 -0
  236. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/grants.yaml +4 -0
  237. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/a.md +1 -0
  238. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/b.md +1 -0
  239. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/c.md +1 -0
  240. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/d.md +1 -0
  241. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/a.md +1 -0
  242. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/b.md +1 -0
  243. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/grants.yaml +6 -0
  244. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/a.md +1 -0
  245. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/b.md +1 -0
  246. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/c.md +1 -0
  247. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/grants.yaml +3 -0
  248. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/test_check.py +345 -0
  249. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +18 -5
  250. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +35 -0
  251. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/__init__.py +7 -0
  252. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/check.py +91 -0
  253. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/AGENTS.md +3 -0
  254. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/CLAUDE.md +3 -0
  255. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/-leading-hyphen.md +3 -0
  256. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/AGENTS.md +3 -0
  257. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/double--hyphen.md +3 -0
  258. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/getting started.md +3 -0
  259. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/nested/page-here.md +3 -0
  260. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/trailing-hyphen-.md +3 -0
  261. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +4 -0
  262. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/user_profile.md +3 -0
  263. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/UI-CONTRACT.png +2 -0
  264. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/diagram.png +1 -0
  265. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/index.md +4 -0
  266. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/notes.md +3 -0
  267. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/reference.html +1 -0
  268. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/2026-10-experiment.md +3 -0
  269. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +6 -0
  270. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs.md +5 -0
  271. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +194 -0
  272. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +14 -2
  273. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +5 -5
  274. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-legacy/docs/compliance/index.md +10 -0
  275. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-type/docs/compliance/index.md +10 -0
  276. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +58 -6
  277. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +0 -1
  278. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +64 -17
  279. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +60 -9
  280. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +17 -0
  281. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +13 -0
  282. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +0 -1
  283. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +0 -1
  284. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +9 -0
  285. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +10 -0
  286. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +10 -0
  287. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs-okf-profiles.yaml +5 -0
  288. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs/architecture/bad-status.md +0 -1
  289. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +0 -1
  290. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/experiment-ok.md +15 -0
  291. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +15 -0
  292. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +16 -0
  293. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +11 -0
  294. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +11 -0
  295. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +11 -0
  296. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-sources.md +10 -0
  297. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +11 -0
  298. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +11 -0
  299. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +16 -0
  300. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +14 -0
  301. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/created-date.md +21 -0
  302. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/research-shaped.md +19 -0
  303. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/operations/runbook.md +26 -0
  304. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +228 -2
  305. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +5 -0
  306. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +11 -2
  307. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +5 -3
  308. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +160 -44
  309. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +0 -2
  310. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/a.ts +1 -0
  311. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/b.ts +1 -0
  312. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/grants.yaml +3 -0
  313. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +0 -2
  314. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  315. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +5 -0
  316. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/a.ts +1 -0
  317. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/b.ts +1 -0
  318. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/c.ts +1 -0
  319. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +4 -0
  320. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  321. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +5 -0
  322. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +0 -2
  323. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  324. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/grants.yaml +3 -0
  325. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/register.py +127 -0
  326. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +265 -29
  327. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +75 -2
  328. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +194 -0
  329. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +56 -10
  330. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +256 -0
  331. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +40 -12
  332. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +108 -0
  333. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +1 -1
  334. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +2 -3
  335. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +23 -6
  336. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +76 -0
  337. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +58 -17
  338. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +161 -0
  339. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +35 -3
  340. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +108 -1
  341. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +281 -61
  342. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/absolute-path.yaml +3 -0
  343. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/backslash-path.yaml +3 -0
  344. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/baseline-is-a-list.yaml +2 -0
  345. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/boolean-size.yaml +3 -0
  346. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/dot-segment.yaml +3 -0
  347. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/duplicate-path.yaml +4 -0
  348. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-file.yaml +0 -0
  349. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-segment.yaml +3 -0
  350. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/float-size.yaml +3 -0
  351. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/negative-size.yaml +3 -0
  352. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-over-limit-baseline.yaml +1 -0
  353. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-schema-version.yaml +1 -0
  354. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/non-string-path.yaml +3 -0
  355. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/not-yaml.yaml +2 -0
  356. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/null-size.yaml +3 -0
  357. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/parent-segment.yaml +3 -0
  358. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/string-size.yaml +3 -0
  359. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/top-level-is-a-list.yaml +2 -0
  360. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/trailing-slash.yaml +3 -0
  361. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/unknown-key.yaml +3 -0
  362. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/wrong-schema-version.yaml +2 -0
  363. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/zero-size.yaml +3 -0
  364. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/valid.yaml +4 -0
  365. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +758 -45
  366. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +4 -3
  367. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +2 -2
  368. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +490 -0
  369. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +368 -55
  370. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +4 -2
  371. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +523 -0
  372. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +1179 -0
  373. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +122 -3
  374. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +33 -1
  375. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +0 -11
  376. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +16 -1
  377. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -2
  378. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +12 -1
  379. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +133 -147
  380. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +133 -147
  381. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  382. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +52 -10
  383. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +23 -4
  384. package/packages/repo-controls/package.json +2 -9
  385. package/packages/repo-controls/scripts/hooks/post-merge +13 -0
  386. package/packages/repo-controls/scripts/hooks/pre-push +3 -3
  387. package/packages/repo-controls/src/azdo-push.ts +64 -36
  388. package/packages/repo-controls/src/azure-devops-governance.ts +7 -9
  389. package/packages/repo-controls/src/azure-devops-remote.ts +1 -1
  390. package/packages/repo-controls/src/cli.ts +29 -8
  391. package/packages/repo-controls/src/compliance-config.ts +410 -2
  392. package/packages/repo-controls/src/config.ts +133 -60
  393. package/packages/repo-controls/src/constants.ts +13 -27
  394. package/packages/repo-controls/src/coverage-ratchet.ts +4 -4
  395. package/packages/repo-controls/src/doctor.ts +46 -19
  396. package/packages/repo-controls/src/gitignore-block.ts +5 -29
  397. package/packages/repo-controls/src/hook-log.ts +1 -2
  398. package/packages/repo-controls/src/hook.ts +201 -45
  399. package/packages/repo-controls/src/install.ts +14 -8
  400. package/packages/repo-controls/src/integrity.ts +3 -3
  401. package/packages/repo-controls/src/lfs.ts +111 -0
  402. package/packages/repo-controls/src/promote.ts +8 -8
  403. package/packages/repo-controls/src/runtime.ts +9 -1
  404. package/packages/repo-controls/src/workflows-drift.ts +4 -5
  405. package/packages/repo-controls/src/worktree-guard.ts +3 -3
@@ -30,14 +30,14 @@
30
30
  * file may contain provider API keys, so this script never prints file
31
31
  * content — only marker state, paths, and validation results.
32
32
  *
33
- * STATUS.md 12p decision 13 (Tarun, 2026-09-29, "adl is LOCAL REPO FIRST …
33
+ * Local-repo-first rule (Tarun, 2026-09-29, "adl is LOCAL REPO FIRST …
34
34
  * the repos where adl-config.toml does not exist has no business of running
35
35
  * hooks"): this carrier STAYS global, as the one exception, because Kimi has
36
36
  * no per-repo hook configuration to deploy into — confirmed from Kimi's own
37
37
  * docs, <https://moonshotai.github.io/kimi-code/en/customization/hooks.html>
38
38
  * (fetched 2026-09-29): "All hook rules are written in the `[[hooks]]` array
39
39
  * in `~/.kimi-code/config.toml`", no project-local alternative documented.
40
- * Decision 13's per-repo-only requirement covers Kimi too, and is built —
40
+ * The per-repo-only requirement covers Kimi too, and is built —
41
41
  * not here, since the TOML `command` string this file writes is a fixed
42
42
  * shell command with no access to the hook payload's JSON `cwd`, but inside
43
43
  * `adl hook` itself, which resolves the invoking repository root from the
@@ -48,13 +48,21 @@
48
48
  * So this global carrier is safe: a Kimi session outside a managed repo
49
49
  * still invokes `adl hook`, which no-ops before any rule runs.
50
50
  */
51
- import { spawnSync } from 'node:child_process'
52
- import { createHash } from 'node:crypto'
53
- import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'
54
- import { dirname, isAbsolute, join, resolve } from 'node:path'
55
-
56
- export const BEGIN = '# BEGIN ADL KIMI HOOKS'
57
- export const END = '# END ADL KIMI HOOKS'
51
+ import { spawnSync } from "node:child_process";
52
+ import { createHash } from "node:crypto";
53
+ import {
54
+ copyFileSync,
55
+ existsSync,
56
+ mkdirSync,
57
+ readFileSync,
58
+ renameSync,
59
+ rmSync,
60
+ writeFileSync,
61
+ } from "node:fs";
62
+ import { dirname, isAbsolute, join, resolve } from "node:path";
63
+
64
+ export const BEGIN = "# BEGIN ADL KIMI HOOKS";
65
+ export const END = "# END ADL KIMI HOOKS";
58
66
 
59
67
  /**
60
68
  * Legacy markers used before the Enact -> adl rename. A real Kimi install
@@ -66,10 +74,30 @@ export const END = '# END ADL KIMI HOOKS'
66
74
  * replaced IN PLACE with the current-marker block on a plain apply/uninstall
67
75
  * — never left orphaned alongside a freshly-inserted second block.
68
76
  */
69
- const LEGACY_BEGIN = '# BEGIN ENACT KIMI HOOKS'
70
- const LEGACY_END = '# END ENACT KIMI HOOKS'
77
+ const LEGACY_BEGIN = "# BEGIN ENACT KIMI HOOKS";
78
+ const LEGACY_END = "# END ENACT KIMI HOOKS";
79
+
80
+ const HOOK_BIN = "adl hook";
81
+
82
+ interface DoctorCommandResult {
83
+ readonly status: number | null;
84
+ readonly stdout: string;
85
+ readonly stderr: string;
86
+ }
71
87
 
72
- const HOOK_BIN = 'adl hook'
88
+ export type DoctorCommandRunner = (
89
+ command: string,
90
+ args: readonly string[],
91
+ ) => DoctorCommandResult;
92
+
93
+ const runDoctorCommand: DoctorCommandRunner = (command, args) => {
94
+ const result = spawnSync(command, [...args], { encoding: "utf8" });
95
+ return {
96
+ status: result.status,
97
+ stdout: result.stdout,
98
+ stderr: result.stderr,
99
+ };
100
+ };
73
101
 
74
102
  /**
75
103
  * Broad file-write tool matcher shared with every other host's CRG
@@ -80,13 +108,13 @@ const HOOK_BIN = 'adl hook'
80
108
  * simply never matches.
81
109
  */
82
110
  const CRG_WRITE_MATCHER =
83
- 'Write|Edit|MultiEdit|StrReplace|str_replace_editor|edit_file|str_replace|replace_in_file|create_file|write_file'
111
+ "Write|Edit|MultiEdit|StrReplace|str_replace_editor|edit_file|str_replace|replace_in_file|create_file|write_file";
84
112
 
85
113
  export interface HookEntry {
86
- readonly event: string
87
- readonly matcher?: string
88
- readonly command: string
89
- readonly timeout: number
114
+ readonly event: string;
115
+ readonly matcher?: string;
116
+ readonly command: string;
117
+ readonly timeout: number;
90
118
  }
91
119
 
92
120
  /**
@@ -96,17 +124,15 @@ export interface HookEntry {
96
124
  * read-only reference — see the "why hardcode, not derive" note below):
97
125
  * - PreToolUse (no matcher -> every tool) -> pre-tool-use gate
98
126
  * - PreToolUse (matcher "Bash") -> CRG pre-commit
99
- * - PreToolUse (no matcher -> every tool) -> lean-ctx pre-tool-use
100
127
  * - UserPromptSubmit (no matcher -> every prompt) -> skill-match suggestions
101
128
  * - SessionStart (no matcher -> both "startup" and "resume") -> briefing
102
129
  * - PostToolUse on file-write tools -> CRG after-file-edit
103
- * - PostToolUse (no matcher -> every tool) -> lean-ctx post-tool-use
104
130
  * - SessionEnd (no matcher -> both "exit" and "archive"; the session-close
105
131
  * event, not the per-turn "Stop" event) -> CRG session-stop, matching
106
132
  * every other host's own sessionEnd/SessionEnd wiring for this hook.
107
133
  *
108
134
  * Order within PreToolUse mirrors the canonical file exactly: catch-all guard
109
- * -> Bash-matched crg pre-commit -> catch-all lean-ctx. The guard must see
135
+ * -> Bash-matched crg pre-commit. The guard must see
110
136
  * every tool before host-specific narrower checks run.
111
137
  *
112
138
  * Why this is hardcoded rather than derived from the canonical hooks list at
@@ -124,32 +150,60 @@ export interface HookEntry {
124
150
  * pinned copy, so a deployer change requires an explicit behavior review.
125
151
  */
126
152
  export const MANAGED_HOOKS: readonly HookEntry[] = [
127
- { event: 'PreToolUse', command: `${HOOK_BIN} pre-tool-use --host=kimi`, timeout: 30 },
128
- { event: 'PreToolUse', matcher: 'Bash', command: `${HOOK_BIN} crg pre-commit --host=kimi`, timeout: 30 },
129
- { event: 'PreToolUse', command: `${HOOK_BIN} lean-ctx pre-tool-use --host=kimi`, timeout: 30 },
130
- { event: 'UserPromptSubmit', command: `${HOOK_BIN} user-prompt-submit --host=kimi`, timeout: 30 },
131
- { event: 'SessionStart', command: `${HOOK_BIN} briefing --host=kimi`, timeout: 15 },
132
- { event: 'PostToolUse', matcher: CRG_WRITE_MATCHER, command: `${HOOK_BIN} crg after-file-edit --host=kimi`, timeout: 30 },
133
- { event: 'PostToolUse', command: `${HOOK_BIN} lean-ctx post-tool-use --host=kimi`, timeout: 30 },
134
- { event: 'SessionEnd', command: `${HOOK_BIN} crg session-stop --host=kimi`, timeout: 30 },
135
- ]
153
+ {
154
+ event: "PreToolUse",
155
+ command: `${HOOK_BIN} pre-tool-use --host=kimi`,
156
+ timeout: 30,
157
+ },
158
+ {
159
+ event: "PreToolUse",
160
+ matcher: "Bash",
161
+ command: `${HOOK_BIN} crg pre-commit --host=kimi`,
162
+ timeout: 30,
163
+ },
164
+ {
165
+ event: "UserPromptSubmit",
166
+ command: `${HOOK_BIN} user-prompt-submit --host=kimi`,
167
+ timeout: 30,
168
+ },
169
+ {
170
+ event: "SessionStart",
171
+ command: `${HOOK_BIN} briefing --host=kimi`,
172
+ timeout: 15,
173
+ },
174
+ {
175
+ event: "PostToolUse",
176
+ matcher: CRG_WRITE_MATCHER,
177
+ command: `${HOOK_BIN} crg after-file-edit --host=kimi`,
178
+ timeout: 30,
179
+ },
180
+ {
181
+ event: "SessionEnd",
182
+ command: `${HOOK_BIN} crg session-stop --host=kimi`,
183
+ timeout: 30,
184
+ },
185
+ ];
136
186
 
137
187
  function tomlEntry(entry: HookEntry): string {
138
- const lines = ['[[hooks]]', `event = ${JSON.stringify(entry.event)}`]
139
- if (entry.matcher !== undefined) lines.push(`matcher = ${JSON.stringify(entry.matcher)}`)
140
- lines.push(`command = ${JSON.stringify(entry.command)}`, `timeout = ${entry.timeout}`)
141
- return lines.join('\n')
188
+ const lines = ["[[hooks]]", `event = ${JSON.stringify(entry.event)}`];
189
+ if (entry.matcher !== undefined)
190
+ lines.push(`matcher = ${JSON.stringify(entry.matcher)}`);
191
+ lines.push(
192
+ `command = ${JSON.stringify(entry.command)}`,
193
+ `timeout = ${entry.timeout}`,
194
+ );
195
+ return lines.join("\n");
142
196
  }
143
197
 
144
198
  export function managedBlock(): string {
145
199
  const header = [
146
200
  BEGIN,
147
- '# Deployed by packages/agent-settings/src/kimi-hooks.ts (bin/kimi-hooks).',
148
- '# Everything between the markers is regenerated on every apply —',
149
- '# edit the deployer, not this block.',
150
- ]
151
- const body = MANAGED_HOOKS.map(tomlEntry).join('\n\n')
152
- return [...header, body, END].join('\n')
201
+ "# Deployed by packages/agent-settings/src/kimi-hooks.ts (bin/kimi-hooks).",
202
+ "# Everything between the markers is regenerated on every apply —",
203
+ "# edit the deployer, not this block.",
204
+ ];
205
+ const body = MANAGED_HOOKS.map(tomlEntry).join("\n\n");
206
+ return [...header, body, END].join("\n");
153
207
  }
154
208
 
155
209
  /**
@@ -164,14 +218,14 @@ export function managedBlock(): string {
164
218
  */
165
219
  const PRIOR_BLOCK_HASHES: readonly string[] = [
166
220
  // 5-hook block (PreToolUse guard, UserPromptSubmit, SessionStart, PostToolUse crg, SessionEnd) —
167
- // shipped before crg pre-commit / lean-ctx pre-tool-use / lean-ctx post-tool-use parity was added.
168
- 'fa83560b374bb59b7a45ffa91b5c9d3bfbc2aea36b7e28293b58168fb77db292',
221
+ // shipped before crg pre-commit parity was added.
222
+ "fa83560b374bb59b7a45ffa91b5c9d3bfbc2aea36b7e28293b58168fb77db292",
169
223
  // 8-hook block before the repository guard became a catch-all PreToolUse hook.
170
- '9e6ef84ff230cf29011408dfc7d5798059a6df7790892213efc343e03381d85a',
224
+ "9e6ef84ff230cf29011408dfc7d5798059a6df7790892213efc343e03381d85a",
171
225
  // 8-hook block calling the standalone `enact-hook` binary, before it folded
172
226
  // into the one `adl` binary (item 15/P4, docs/specs/two-branch-standard-
173
227
  // 2026-09-27.md) and HOOK_BIN became `adl hook` — hard cut, no compat shim.
174
- '0fa75023b0bb836c199bb0f1f7327fafacea73b4e494a0f45662a1fa3522b7ef',
228
+ "0fa75023b0bb836c199bb0f1f7327fafacea73b4e494a0f45662a1fa3522b7ef",
175
229
  // Same 8-hook `enact-hook` body as the entry above, but from one generation
176
230
  // earlier: the header comment still reads the pre-reorg deployer path
177
231
  // ("global-surface-sweep/src/kimi-hooks.ts") because `acf8ab1` (reorganise
@@ -185,29 +239,43 @@ const PRIOR_BLOCK_HASHES: readonly string[] = [
185
239
  // managed block was classified `drifted` instead of `ours-outdated`,
186
240
  // aborting a plain `apply` with "hand-edited, or unrecognized content"
187
241
  // even though it is byte-for-byte a block this deployer itself shipped.
188
- 'bc3f34452cbec3dc0aeb77d917816fd4fbc73a8baedfa09f4e9313a56b330d94',
189
- ]
242
+ "bc3f34452cbec3dc0aeb77d917816fd4fbc73a8baedfa09f4e9313a56b330d94",
243
+ // 10-hook `adl hook` block from the window after the enact→adl rename but
244
+ // before lean-ctx was retired (2026-10-04): current markers, current header,
245
+ // and the two `adl hook lean-ctx pre|post-tool-use` entries the canonical
246
+ // block no longer carries. Found live on m3's ~/.kimi-code/config.toml —
247
+ // classified `drifted` and aborted the scheduled reconcile's plain apply
248
+ // even though the only delta from canonical is the two retired lean-ctx
249
+ // entries (diff verified byte-for-byte; see PREVIOUS_LEAN_CTX_ERA_BLOCK in
250
+ // tests/kimi-hooks-upgrade.test.ts).
251
+ "29d2953dd893b6d244bf825b10a7e9deb1a1c25b2acc0db1b74327e19cc8a253",
252
+ ];
190
253
 
191
254
  function hashBlock(text: string): string {
192
- return createHash('sha256').update(text).digest('hex')
255
+ return createHash("sha256").update(text).digest("hex");
193
256
  }
194
257
 
195
- export type BlockState = 'missing' | 'matches' | 'ours-outdated' | 'drifted' | 'malformed'
258
+ export type BlockState =
259
+ | "missing"
260
+ | "matches"
261
+ | "ours-outdated"
262
+ | "drifted"
263
+ | "malformed";
196
264
 
197
265
  interface Region {
198
- readonly start: number
266
+ readonly start: number;
199
267
  /** Exclusive — the index immediately after the closing marker's text. */
200
- readonly end: number
268
+ readonly end: number;
201
269
  }
202
270
 
203
- type RegionResult = Region | 'missing' | 'malformed'
271
+ type RegionResult = Region | "missing" | "malformed";
204
272
 
205
273
  function findRegion(content: string, begin: string, end: string): RegionResult {
206
- const start = content.indexOf(begin)
207
- const stop = content.indexOf(end)
208
- if (start < 0 && stop < 0) return 'missing'
209
- if (start < 0 || stop < 0 || stop < start) return 'malformed'
210
- return { start, end: stop + end.length }
274
+ const start = content.indexOf(begin);
275
+ const stop = content.indexOf(end);
276
+ if (start < 0 && stop < 0) return "missing";
277
+ if (start < 0 || stop < 0 || stop < start) return "malformed";
278
+ return { start, end: stop + end.length };
211
279
  }
212
280
 
213
281
  /**
@@ -217,9 +285,9 @@ function findRegion(content: string, begin: string, end: string): RegionResult {
217
285
  * never mistaken for "missing" and duplicated alongside a fresh insert.
218
286
  */
219
287
  function locateManagedRegion(content: string): RegionResult {
220
- const current = findRegion(content, BEGIN, END)
221
- if (current !== 'missing') return current
222
- return findRegion(content, LEGACY_BEGIN, LEGACY_END)
288
+ const current = findRegion(content, BEGIN, END);
289
+ if (current !== "missing") return current;
290
+ return findRegion(content, LEGACY_BEGIN, LEGACY_END);
223
291
  }
224
292
 
225
293
  /**
@@ -228,24 +296,27 @@ function locateManagedRegion(content: string): RegionResult {
228
296
  * missing) and pass it in.
229
297
  */
230
298
  export function classifyBlock(existingContent: string | null): BlockState {
231
- if (existingContent === null || existingContent.length === 0) return 'missing'
232
- const region = locateManagedRegion(existingContent)
233
- if (region === 'missing') return 'missing'
234
- if (region === 'malformed') return 'malformed'
235
- const installed = existingContent.slice(region.start, region.end)
236
- if (installed === managedBlock()) return 'matches'
237
- return PRIOR_BLOCK_HASHES.includes(hashBlock(installed)) ? 'ours-outdated' : 'drifted'
299
+ if (existingContent === null || existingContent.length === 0)
300
+ return "missing";
301
+ const region = locateManagedRegion(existingContent);
302
+ if (region === "missing") return "missing";
303
+ if (region === "malformed") return "malformed";
304
+ const installed = existingContent.slice(region.start, region.end);
305
+ if (installed === managedBlock()) return "matches";
306
+ return PRIOR_BLOCK_HASHES.includes(hashBlock(installed))
307
+ ? "ours-outdated"
308
+ : "drifted";
238
309
  }
239
310
 
240
311
  function spliceIn(base: string): string {
241
- const block = managedBlock()
242
- const region = locateManagedRegion(base)
243
- if (region !== 'missing' && region !== 'malformed') {
244
- return `${base.slice(0, region.start)}${block}${base.slice(region.end)}`
312
+ const block = managedBlock();
313
+ const region = locateManagedRegion(base);
314
+ if (region !== "missing" && region !== "malformed") {
315
+ return `${base.slice(0, region.start)}${block}${base.slice(region.end)}`;
245
316
  }
246
- if (base.length === 0) return `${block}\n`
247
- const trimmed = base.replace(/\n+$/, '')
248
- return `${trimmed}\n\n${block}\n`
317
+ if (base.length === 0) return `${block}\n`;
318
+ const trimmed = base.replace(/\n+$/, "");
319
+ return `${trimmed}\n\n${block}\n`;
249
320
  }
250
321
 
251
322
  /**
@@ -255,50 +326,63 @@ function spliceIn(base: string): string {
255
326
  * freshly-applied block returns the file to byte-identical pre-apply content.
256
327
  */
257
328
  function spliceOut(base: string): string {
258
- const region = locateManagedRegion(base)
259
- if (region === 'missing' || region === 'malformed') return base
260
- const head = base.slice(0, region.start).replace(/\n+$/, '')
261
- const tail = base.slice(region.end).replace(/^\n+/, '')
262
- if (head.length === 0) return tail
263
- if (tail.length === 0) return `${head}\n`
264
- return `${head}\n\n${tail}`
329
+ const region = locateManagedRegion(base);
330
+ if (region === "missing" || region === "malformed") return base;
331
+ const head = base.slice(0, region.start).replace(/\n+$/, "");
332
+ const tail = base.slice(region.end).replace(/^\n+/, "");
333
+ if (head.length === 0) return tail;
334
+ if (tail.length === 0) return `${head}\n`;
335
+ return `${head}\n\n${tail}`;
265
336
  }
266
337
 
267
338
  function resolveConfigPath(home: string): string {
268
- if (!isAbsolute(home)) throw new Error(`--home must be absolute: ${home}`)
269
- return join(resolve(home), '.kimi-code', 'config.toml')
339
+ if (!isAbsolute(home)) throw new Error(`--home must be absolute: ${home}`);
340
+ return join(resolve(home), ".kimi-code", "config.toml");
270
341
  }
271
342
 
272
343
  function readConfig(path: string): string | null {
273
- if (!existsSync(path)) return null
274
- return readFileSync(path, 'utf8')
344
+ if (!existsSync(path)) return null;
345
+ return readFileSync(path, "utf8");
275
346
  }
276
347
 
277
348
  /** Real TOML syntax check via Bun's built-in parser — never a stub. */
278
349
  function assertParseable(text: string, context: string): void {
279
- if (text.trim().length === 0) return
350
+ if (text.trim().length === 0) return;
280
351
  try {
281
352
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Bun global, no @types/bun in this package
282
- ;(globalThis as any).Bun.TOML.parse(text)
353
+ (globalThis as any).Bun.TOML.parse(text);
283
354
  } catch (error) {
284
- throw new Error(`${context} is not valid TOML: ${error instanceof Error ? error.message : String(error)}`)
355
+ throw new Error(
356
+ `${context} is not valid TOML: ${error instanceof Error ? error.message : String(error)}`,
357
+ );
285
358
  }
286
359
  }
287
360
 
288
361
  /** Best-effort schema check against the real `kimi` CLI when it is on PATH; a no-op note otherwise. */
289
- function doctorCheck(path: string): string | null {
290
- const which = spawnSync('bash', ['-lc', 'command -v kimi'], { encoding: 'utf8' })
291
- if (which.status !== 0) return null
292
- const result = spawnSync('kimi', ['doctor', 'config', path], { encoding: 'utf8' })
362
+ function doctorCheck(
363
+ path: string,
364
+ runCommand: DoctorCommandRunner,
365
+ ): string | null {
366
+ // Probe the effective PATH this wrapper established. A login shell can add
367
+ // user startup paths and accidentally run an unrelated host Kimi install.
368
+ const which = runCommand("bash", ["-c", "command -v kimi"]);
369
+ if (which.status !== 0) return null;
370
+ const kimi = which.stdout.trim();
371
+ const result = runCommand(kimi, ["doctor", "config", path]);
293
372
  if (result.status !== 0) {
294
- throw new Error(`kimi doctor rejected the candidate config: ${(result.stderr || result.stdout || '').trim()}`)
373
+ throw new Error(
374
+ `kimi doctor rejected the candidate config: ${(result.stderr || result.stdout || "").trim()}`,
375
+ );
295
376
  }
296
- return 'kimi doctor: OK'
377
+ return "kimi doctor: OK";
297
378
  }
298
379
 
299
380
  function backupPath(configPath: string): string {
300
- const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z')
301
- return `${configPath}.pre-kimi-hooks-${stamp}`
381
+ const stamp = new Date()
382
+ .toISOString()
383
+ .replace(/[-:]/g, "")
384
+ .replace(/\.\d+Z$/, "Z");
385
+ return `${configPath}.pre-kimi-hooks-${stamp}`;
302
386
  }
303
387
 
304
388
  /**
@@ -307,149 +391,212 @@ function backupPath(configPath: string): string {
307
391
  * the existing file, then atomically rename the staged file into place. A
308
392
  * rejected candidate leaves the existing config completely untouched.
309
393
  */
310
- function commitConfig(path: string, next: string, notes: string[]): string | undefined {
311
- assertParseable(next, 'candidate config')
312
- mkdirSync(dirname(path), { recursive: true })
313
- const temporary = join(dirname(path), `.kimi-hooks.${process.pid}.${Date.now()}.tmp`)
314
- writeFileSync(temporary, next)
394
+ function commitConfig(
395
+ path: string,
396
+ next: string,
397
+ notes: string[],
398
+ runCommand: DoctorCommandRunner = runDoctorCommand,
399
+ ): string | undefined {
400
+ assertParseable(next, "candidate config");
401
+ mkdirSync(dirname(path), { recursive: true });
402
+ const temporary = join(
403
+ dirname(path),
404
+ `.kimi-hooks.${process.pid}.${Date.now()}.tmp`,
405
+ );
406
+ writeFileSync(temporary, next);
315
407
  try {
316
- const doctorNote = doctorCheck(temporary)
317
- if (doctorNote) notes.push(doctorNote)
408
+ const doctorNote = doctorCheck(temporary, runCommand);
409
+ if (doctorNote) notes.push(doctorNote);
318
410
  } catch (error) {
319
- rmSync(temporary, { force: true })
320
- throw error
411
+ rmSync(temporary, { force: true });
412
+ throw error;
321
413
  }
322
- let backup: string | undefined
414
+ let backup: string | undefined;
323
415
  if (existsSync(path)) {
324
- backup = backupPath(path)
325
- copyFileSync(path, backup)
326
- notes.push(`backed up previous config to ${backup}`)
416
+ backup = backupPath(path);
417
+ copyFileSync(path, backup);
418
+ notes.push(`backed up previous config to ${backup}`);
327
419
  }
328
- renameSync(temporary, path)
329
- return backup
420
+ renameSync(temporary, path);
421
+ return backup;
330
422
  }
331
423
 
332
424
  export interface PlanResult {
333
- readonly path: string
334
- readonly state: BlockState
335
- readonly wouldChangeOnApply: boolean
336
- readonly wouldChangeOnUninstall: boolean
425
+ readonly path: string;
426
+ readonly state: BlockState;
427
+ readonly wouldChangeOnApply: boolean;
428
+ readonly wouldChangeOnUninstall: boolean;
337
429
  }
338
430
 
339
431
  /** Read-only: current state and what apply/uninstall would do. Never touches the file. */
340
432
  export function planKimiHooks(home: string): PlanResult {
341
- const path = resolveConfigPath(home)
342
- const state = classifyBlock(readConfig(path))
433
+ const path = resolveConfigPath(home);
434
+ const state = classifyBlock(readConfig(path));
343
435
  return {
344
436
  path,
345
437
  state,
346
- wouldChangeOnApply: state === 'missing' || state === 'drifted' || state === 'ours-outdated',
347
- wouldChangeOnUninstall: state === 'matches' || state === 'drifted' || state === 'ours-outdated',
348
- }
438
+ wouldChangeOnApply:
439
+ state === "missing" || state === "drifted" || state === "ours-outdated",
440
+ wouldChangeOnUninstall:
441
+ state === "matches" || state === "drifted" || state === "ours-outdated",
442
+ };
349
443
  }
350
444
 
351
445
  export interface OpResult {
352
- readonly path: string
353
- readonly stateBefore: BlockState
354
- readonly action: 'none' | 'installed' | 'updated' | 'removed'
355
- readonly notes: readonly string[]
356
- readonly backupPath: string | undefined
446
+ readonly path: string;
447
+ readonly stateBefore: BlockState;
448
+ readonly action: "none" | "installed" | "updated" | "removed";
449
+ readonly notes: readonly string[];
450
+ readonly backupPath: string | undefined;
357
451
  }
358
452
 
359
- export function applyKimiHooks(home: string, options: { readonly force?: boolean } = {}): OpResult {
360
- const path = resolveConfigPath(home)
361
- const base = readConfig(path)
362
- const state = classifyBlock(base)
363
- const notes: string[] = []
364
-
365
- if (state === 'malformed') {
366
- throw new Error(`malformed managed-block markers in ${path} (BEGIN/END unpaired or out of order) — fix manually`)
453
+ export function applyKimiHooks(
454
+ home: string,
455
+ options: {
456
+ readonly force?: boolean;
457
+ readonly doctorCommandRunner?: DoctorCommandRunner;
458
+ } = {},
459
+ ): OpResult {
460
+ const path = resolveConfigPath(home);
461
+ const base = readConfig(path);
462
+ const state = classifyBlock(base);
463
+ const notes: string[] = [];
464
+
465
+ if (state === "malformed") {
466
+ throw new Error(
467
+ `malformed managed-block markers in ${path} (BEGIN/END unpaired or out of order) — fix manually`,
468
+ );
367
469
  }
368
- if (base !== null) assertParseable(base, path)
369
-
370
- if (state === 'matches') {
371
- notes.push('managed hook block already present and canonical')
372
- return { path, stateBefore: state, action: 'none', notes, backupPath: undefined }
470
+ if (base !== null) assertParseable(base, path);
471
+
472
+ if (state === "matches") {
473
+ notes.push("managed hook block already present and canonical");
474
+ return {
475
+ path,
476
+ stateBefore: state,
477
+ action: "none",
478
+ notes,
479
+ backupPath: undefined,
480
+ };
373
481
  }
374
- if (state === 'ours-outdated') {
375
- notes.push('upgrading managed hook block from a previous ENACT version (no --force needed)')
482
+ if (state === "ours-outdated") {
483
+ notes.push(
484
+ "upgrading managed hook block from a previous ENACT version (no --force needed)",
485
+ );
376
486
  }
377
- if (state === 'drifted' && !(options.force ?? false)) {
487
+ if (state === "drifted" && !(options.force ?? false)) {
378
488
  throw new Error(
379
489
  `${path} has a managed hook block that differs from the canonical block (hand-edited, or unrecognized content) — rerun apply with --force to overwrite it`,
380
- )
490
+ );
381
491
  }
382
492
 
383
- const next = spliceIn(base ?? '')
384
- const backup = commitConfig(path, next, notes)
385
- return { path, stateBefore: state, action: state === 'missing' ? 'installed' : 'updated', notes, backupPath: backup }
493
+ const next = spliceIn(base ?? "");
494
+ const backup = commitConfig(path, next, notes, options.doctorCommandRunner);
495
+ return {
496
+ path,
497
+ stateBefore: state,
498
+ action: state === "missing" ? "installed" : "updated",
499
+ notes,
500
+ backupPath: backup,
501
+ };
386
502
  }
387
503
 
388
- export function uninstallKimiHooks(home: string, options: { readonly force?: boolean } = {}): OpResult {
389
- const path = resolveConfigPath(home)
390
- const base = readConfig(path)
391
- const state = classifyBlock(base)
392
- const notes: string[] = []
504
+ export function uninstallKimiHooks(
505
+ home: string,
506
+ options: { readonly force?: boolean } = {},
507
+ ): OpResult {
508
+ const path = resolveConfigPath(home);
509
+ const base = readConfig(path);
510
+ const state = classifyBlock(base);
511
+ const notes: string[] = [];
393
512
 
394
- if (state === 'malformed') {
395
- throw new Error(`malformed managed-block markers in ${path} (BEGIN/END unpaired or out of order) — fix manually`)
513
+ if (state === "malformed") {
514
+ throw new Error(
515
+ `malformed managed-block markers in ${path} (BEGIN/END unpaired or out of order) — fix manually`,
516
+ );
396
517
  }
397
- if (base !== null) assertParseable(base, path)
398
-
399
- if (state === 'missing') {
400
- notes.push('managed hook block not present; nothing to remove')
401
- return { path, stateBefore: state, action: 'none', notes, backupPath: undefined }
518
+ if (base !== null) assertParseable(base, path);
519
+
520
+ if (state === "missing") {
521
+ notes.push("managed hook block not present; nothing to remove");
522
+ return {
523
+ path,
524
+ stateBefore: state,
525
+ action: "none",
526
+ notes,
527
+ backupPath: undefined,
528
+ };
402
529
  }
403
- if (state === 'ours-outdated') {
404
- notes.push('removing managed hook block from a previous ENACT version (no --force needed)')
530
+ if (state === "ours-outdated") {
531
+ notes.push(
532
+ "removing managed hook block from a previous ENACT version (no --force needed)",
533
+ );
405
534
  }
406
- if (state === 'drifted' && !(options.force ?? false)) {
535
+ if (state === "drifted" && !(options.force ?? false)) {
407
536
  throw new Error(
408
537
  `${path} has a managed hook block that differs from the canonical block (hand-edited, or unrecognized content) — rerun uninstall with --force to remove it anyway`,
409
- )
538
+ );
410
539
  }
411
540
 
412
- const next = spliceOut(base as string)
413
- const backup = commitConfig(path, next, notes)
414
- return { path, stateBefore: state, action: 'removed', notes, backupPath: backup }
541
+ const next = spliceOut(base as string);
542
+ const backup = commitConfig(path, next, notes);
543
+ return {
544
+ path,
545
+ stateBefore: state,
546
+ action: "removed",
547
+ notes,
548
+ backupPath: backup,
549
+ };
415
550
  }
416
551
 
417
552
  function usage(): never {
418
- throw new Error('usage: kimi-hooks --home ABSOLUTE_PATH --mode plan|apply|uninstall [--force]')
553
+ throw new Error(
554
+ "usage: kimi-hooks --home ABSOLUTE_PATH --mode plan|apply|uninstall [--force]",
555
+ );
419
556
  }
420
557
 
421
558
  export function main(argv: readonly string[]): void {
422
- let home = ''
423
- let mode = ''
424
- let force = false
425
- for (let index = 0; index < argv.length; index += 1) {
426
- const argument = argv[index]
427
- if (argument === '--home') home = argv[++index] ?? ''
428
- else if (argument === '--mode') mode = argv[++index] ?? ''
429
- else if (argument === '--force') force = true
430
- else usage()
431
- }
432
- if (home.length === 0 || !['plan', 'apply', 'uninstall'].includes(mode)) usage()
433
-
434
- if (mode === 'plan') {
435
- const result = planKimiHooks(home)
436
- console.log(`kimi-hooks: config=${result.path}`)
437
- console.log(`kimi-hooks: state=${result.state}`)
438
- console.log(`kimi-hooks: apply would ${result.wouldChangeOnApply ? 'change this file' : 'no-op'}`)
439
- console.log(`kimi-hooks: uninstall would ${result.wouldChangeOnUninstall ? 'change this file' : 'no-op'}`)
440
- return
559
+ try {
560
+ let home = "";
561
+ let mode = "";
562
+ let force = false;
563
+ for (let index = 0; index < argv.length; index += 1) {
564
+ const argument = argv[index];
565
+ if (argument === "--home") home = argv[++index] ?? "";
566
+ else if (argument === "--mode") mode = argv[++index] ?? "";
567
+ else if (argument === "--force") force = true;
568
+ else usage();
569
+ }
570
+ if (home.length === 0 || !["plan", "apply", "uninstall"].includes(mode))
571
+ usage();
572
+
573
+ if (mode === "plan") {
574
+ const result = planKimiHooks(home);
575
+ console.log(`kimi-hooks: config=${result.path}`);
576
+ console.log(`kimi-hooks: state=${result.state}`);
577
+ console.log(
578
+ `kimi-hooks: apply would ${result.wouldChangeOnApply ? "change this file" : "no-op"}`,
579
+ );
580
+ console.log(
581
+ `kimi-hooks: uninstall would ${result.wouldChangeOnUninstall ? "change this file" : "no-op"}`,
582
+ );
583
+ return;
584
+ }
585
+
586
+ const result =
587
+ mode === "apply"
588
+ ? applyKimiHooks(home, { force })
589
+ : uninstallKimiHooks(home, { force });
590
+ for (const note of result.notes) console.log(`kimi-hooks: ${note}`);
591
+ console.log(`kimi-hooks: ${result.action} (${result.path})`);
592
+ } catch (cause) {
593
+ console.error(
594
+ `kimi-hooks: ${cause instanceof Error ? cause.message : String(cause)}`,
595
+ );
596
+ process.exitCode = 1;
441
597
  }
442
-
443
- const result = mode === 'apply' ? applyKimiHooks(home, { force }) : uninstallKimiHooks(home, { force })
444
- for (const note of result.notes) console.log(`kimi-hooks: ${note}`)
445
- console.log(`kimi-hooks: ${result.action} (${result.path})`)
446
598
  }
447
599
 
448
600
  if (import.meta.main) {
449
- try {
450
- main(process.argv.slice(2))
451
- } catch (cause) {
452
- console.error(`kimi-hooks: ${cause instanceof Error ? cause.message : String(cause)}`)
453
- process.exitCode = 1
454
- }
601
+ main(process.argv.slice(2));
455
602
  }