claude-dev-env 4.3.0 → 7.1.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 (480) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +5 -4
  12. package/.agents/skills/pull-request/SKILL.md +11 -13
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
  15. package/.agents/skills/pull-request/scripts/pull_request.py +35 -44
  16. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
  17. package/.agents/skills/pull-request/scripts/test_pull_request.py +35 -3
  18. package/.agents/skills/session-continuity/README.md +189 -0
  19. package/.agents/skills/session-continuity/SKILL.md +114 -0
  20. package/.agents/skills/session-continuity/continuity.mjs +375 -0
  21. package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
  24. package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
  25. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  26. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  27. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  28. package/bin/cde.mjs +36 -8
  29. package/bin/cde.test.mjs +48 -0
  30. package/bin/ever-shipped-skills.mjs +2 -20
  31. package/bin/ever-shipped-skills.test.mjs +8 -0
  32. package/bin/expand_home_directory_tokens.mjs +4 -5
  33. package/bin/expand_home_directory_tokens.test.mjs +128 -0
  34. package/bin/git_hooks_installer.mjs +22 -3
  35. package/bin/git_hooks_installer.test.mjs +159 -5
  36. package/bin/install-session-continuity.mjs +78 -0
  37. package/bin/install-session-continuity.test.mjs +42 -0
  38. package/bin/install.agents-home.test.mjs +15 -13
  39. package/bin/install.codex-rules.test.mjs +5 -2
  40. package/bin/install.cursor-rules.test.mjs +5 -2
  41. package/bin/install.mjs +506 -63
  42. package/bin/install.policy-lint.test.mjs +130 -0
  43. package/bin/install.profiles.test.mjs +10 -0
  44. package/bin/install.prune.test.mjs +286 -37
  45. package/bin/install.pstack-plugin.test.mjs +114 -0
  46. package/bin/install.test.mjs +537 -120
  47. package/bin/install.transaction.test.mjs +3 -3
  48. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  49. package/docs/CODE_RULES.md +1 -1
  50. package/hooks/advisory/pr_done_reminder.py +342 -0
  51. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  52. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  53. package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
  54. package/hooks/blocking/code_rules_paired_test.py +22 -15
  55. package/hooks/blocking/precommit_code_rules_gate.py +5 -8
  56. package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
  57. package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
  58. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
  59. package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
  60. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  61. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  62. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  63. package/hooks/blocking/test_pre_tool_use_dispatcher.py +43 -342
  64. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  65. package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
  66. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  67. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
  68. package/hooks/git-hooks/post_commit.py +33 -0
  69. package/hooks/git-hooks/pre_commit.py +18 -49
  70. package/hooks/git-hooks/pre_push.py +45 -541
  71. package/hooks/git-hooks/test_installed_policy_lint.py +208 -0
  72. package/hooks/git-hooks/test_native_hook_support.py +129 -0
  73. package/hooks/git-hooks/test_native_policy_commit.py +120 -0
  74. package/hooks/git-hooks/test_post_commit.py +43 -0
  75. package/hooks/git-hooks/test_pre_commit.py +38 -148
  76. package/hooks/git-hooks/test_pre_push.py +48 -1631
  77. package/hooks/git-hooks/test_verification_notice.py +60 -0
  78. package/hooks/git-hooks/test_verification_start.py +173 -0
  79. package/hooks/git-hooks/verification_notice.py +192 -0
  80. package/hooks/git-hooks/verification_notice_context.py +253 -0
  81. package/hooks/git-hooks/verification_notice_state.py +237 -0
  82. package/hooks/git-hooks/verification_start.py +100 -0
  83. package/hooks/hooks.json +1 -120
  84. package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
  85. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
  86. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
  87. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
  88. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  89. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -93
  90. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
  91. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
  92. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
  93. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  94. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +46 -201
  95. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
  96. package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
  97. package/hooks/validators/run_all_validators.py +5 -1
  98. package/hooks/validators/security_checks.py +57 -2
  99. package/hooks/validators/test_hook_timing_harness.py +2 -5
  100. package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
  101. package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
  102. package/hooks/validators/test_security_checks.py +57 -0
  103. package/package.json +2 -2
  104. package/rules/confirm-implementation-forks.md +2 -0
  105. package/rules/durable-post-artifacts.md +2 -1
  106. package/rules/git-workflow.md +18 -6
  107. package/rules/pstack-host-mapping.md +48 -0
  108. package/rules/vault-context.md +3 -5
  109. package/rules/verify-runtime-state.md +7 -0
  110. package/scripts/audit-hooks-cli.mjs +5 -0
  111. package/scripts/audit-hooks.mjs +72 -16
  112. package/scripts/automatic_advisory/__init__.py +1 -0
  113. package/scripts/automatic_advisory/checkout.py +51 -0
  114. package/scripts/automatic_advisory/cli.py +334 -0
  115. package/scripts/automatic_advisory/config/__init__.py +1 -0
  116. package/scripts/automatic_advisory/config/constants.py +93 -0
  117. package/scripts/automatic_advisory/config/timing.py +4 -0
  118. package/scripts/automatic_advisory/configuration.py +197 -0
  119. package/scripts/automatic_advisory/execution.py +350 -0
  120. package/scripts/automatic_advisory/git.py +69 -0
  121. package/scripts/automatic_advisory/model.py +107 -0
  122. package/scripts/automatic_advisory/process_host.py +32 -0
  123. package/scripts/automatic_advisory/publisher.py +84 -0
  124. package/scripts/automatic_advisory/runner.py +399 -0
  125. package/scripts/automatic_advisory/state.py +116 -0
  126. package/scripts/automatic_advisory/window_flags.py +41 -0
  127. package/scripts/automatic_advisory/windows_job.py +122 -0
  128. package/scripts/check.ps1 +0 -1
  129. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  130. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  131. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  132. package/scripts/codex_compat_materializer.py +99 -13
  133. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  134. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +35 -43
  135. package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +10 -10
  136. package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +14 -1
  137. package/scripts/local_report_cli.py +184 -0
  138. package/scripts/local_report_core.py +247 -0
  139. package/scripts/local_report_publisher.py +12 -0
  140. package/scripts/local_report_validation.py +357 -0
  141. package/scripts/local_verification/__init__.py +23 -0
  142. package/scripts/local_verification/check_runner.py +364 -0
  143. package/scripts/local_verification/check_support.py +99 -0
  144. package/scripts/local_verification/cli.py +158 -0
  145. package/scripts/local_verification/command_runner.py +84 -0
  146. package/scripts/local_verification/config/__init__.py +1 -0
  147. package/scripts/local_verification/config/constants.py +89 -0
  148. package/scripts/local_verification/config/timing.py +1 -0
  149. package/scripts/local_verification/git_state.py +146 -0
  150. package/scripts/local_verification/manifest.py +224 -0
  151. package/scripts/local_verification/model.py +174 -0
  152. package/scripts/local_verification/runner.py +234 -0
  153. package/scripts/policy_lint/adapter_detectors.py +13 -6
  154. package/scripts/policy_lint/adapter_pairing.py +58 -3
  155. package/scripts/policy_lint/adapters.py +7 -4
  156. package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
  157. package/scripts/policy_lint/config/constants.py +5 -0
  158. package/scripts/policy_lint/engine.py +48 -4
  159. package/scripts/policy_lint/model.py +1 -0
  160. package/scripts/policy_lint/registry.py +1 -1
  161. package/scripts/policy_lint/selection.py +2 -1
  162. package/scripts/policy_lint/selection_git.py +2 -0
  163. package/scripts/pr_verification/__init__.py +1 -0
  164. package/scripts/pr_verification/branch_refs.py +114 -0
  165. package/scripts/pr_verification/config/__init__.py +1 -0
  166. package/scripts/pr_verification/config/constants.py +84 -0
  167. package/scripts/pr_verification/config/timing.py +5 -0
  168. package/scripts/pr_verification/github.py +137 -0
  169. package/scripts/pr_verification/github_api.py +376 -0
  170. package/scripts/pr_verification/github_labels.py +88 -0
  171. package/scripts/pr_verification/github_parsing.py +182 -0
  172. package/scripts/pr_verification/github_transport.py +91 -0
  173. package/scripts/pr_verification/lock.py +122 -0
  174. package/scripts/pr_verification/model.py +43 -0
  175. package/scripts/pr_verification/requirements.txt +1 -0
  176. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  177. package/scripts/shared_tree_paths.py +32 -3
  178. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +97 -0
  179. package/scripts/test_shared_tree_paths.py +33 -0
  180. package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
  181. package/scripts/tests/test-audit-hooks.mjs +94 -2
  182. package/scripts/tests/test_automatic_advisory.py +376 -0
  183. package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
  184. package/scripts/tests/test_automatic_advisory_polling.py +216 -0
  185. package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
  186. package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
  187. package/scripts/tests/test_automatic_advisory_settings.py +48 -0
  188. package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
  189. package/scripts/tests/test_closed_pr_label.py +157 -0
  190. package/scripts/tests/test_codex_compat_materializer.py +189 -6
  191. package/scripts/tests/test_github_labels.py +101 -0
  192. package/scripts/tests/test_github_transport.py +125 -0
  193. package/scripts/tests/test_local_report_publisher.py +396 -0
  194. package/scripts/tests/test_local_report_publisher_cli.py +85 -0
  195. package/scripts/tests/test_local_verification.py +348 -0
  196. package/scripts/tests/test_local_verification_cli.py +98 -0
  197. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  198. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  199. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  200. package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
  201. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  202. package/scripts/tests/test_policy_lint_selection.py +25 -2
  203. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  204. package/scripts/tests/test_pr_verification_github.py +392 -0
  205. package/scripts/tests/test_pr_verification_lock.py +168 -0
  206. package/scripts/tests/test_rules.py +3 -7
  207. package/scripts/tests/test_verification_notice.py +347 -0
  208. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  209. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  210. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  211. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  212. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  213. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  214. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  215. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  216. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  217. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  218. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  219. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  220. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  221. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  222. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  223. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  224. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  225. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  226. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  227. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  228. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  229. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  230. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  231. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  232. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  233. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  234. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  235. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  236. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  237. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  238. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  239. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  240. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  241. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  242. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  243. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  244. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  245. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  246. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  247. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  248. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  249. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  250. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  251. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  252. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  253. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  254. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  255. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  256. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  257. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  258. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  259. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  260. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  261. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  262. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  263. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  264. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  265. package/.agents/skills/autoconverge/SKILL.md +0 -431
  266. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  267. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  268. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  269. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  270. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  271. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  272. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  273. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  274. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  275. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  276. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  277. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  278. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  279. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  280. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  281. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  283. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  284. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  285. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  286. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  287. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  288. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  289. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  290. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  291. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  292. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  293. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  294. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  295. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  296. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  297. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  298. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  299. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  300. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  301. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  302. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  303. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  304. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  305. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  306. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  307. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  308. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  309. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  310. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  311. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  312. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  313. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  314. package/.agents/skills/hitl/SKILL.md +0 -45
  315. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  316. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  317. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  318. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  319. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  320. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  321. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  322. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  323. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  324. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  325. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  326. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  327. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  328. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  329. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  330. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  331. package/.agents/skills/prototype/SKILL.md +0 -85
  332. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  333. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  334. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  335. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  336. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  337. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  338. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  339. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  340. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  341. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  342. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  343. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  344. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  345. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  346. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  347. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  348. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  349. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  350. package/.agents/skills/rebase/AGENTS.md +0 -1
  351. package/.agents/skills/rebase/SKILL.md +0 -178
  352. package/.agents/skills/review-router/SKILL.md +0 -51
  353. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  354. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  355. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  356. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  357. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  358. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  359. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  360. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  361. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  362. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  363. package/.agents/skills/review-tier/SKILL.md +0 -56
  364. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  365. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  366. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  367. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  368. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  369. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  370. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  371. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  372. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  373. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  374. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  375. package/.agents/skills/session-log/AGENTS.md +0 -1
  376. package/.agents/skills/session-log/SKILL.md +0 -170
  377. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  378. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  379. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  380. package/.agents/skills/session-tidy/SKILL.md +0 -191
  381. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  382. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  383. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  384. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  385. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  386. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  387. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  388. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  389. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  390. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  391. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  392. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  393. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  394. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  395. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  396. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  397. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  398. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  399. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  400. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  401. package/.agents/skills/update/AGENTS.md +0 -1
  402. package/.agents/skills/update/SKILL.md +0 -190
  403. package/_shared/pr-loop/AGENTS.md +0 -1
  404. package/_shared/pr-loop/audit-contract.md +0 -183
  405. package/_shared/pr-loop/audit-reply-template.md +0 -151
  406. package/_shared/pr-loop/code-rules-gate.md +0 -65
  407. package/_shared/pr-loop/fix-protocol.md +0 -60
  408. package/_shared/pr-loop/gh-payloads.md +0 -75
  409. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  410. package/_shared/pr-loop/precatch-rubric.md +0 -66
  411. package/_shared/pr-loop/preflight-proposal.md +0 -56
  412. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  413. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  414. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  415. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  416. package/_shared/pr-loop/state-schema.md +0 -45
  417. package/_shared/pr-loop/worker-spawn.md +0 -189
  418. package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
  419. package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
  420. package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
  421. package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
  422. package/hooks/blocking/destructive_command_blocker.py +0 -2073
  423. package/hooks/blocking/fable_spawn_gate.py +0 -187
  424. package/hooks/blocking/luna_fast_mode_gate.py +0 -160
  425. package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
  426. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
  427. package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
  428. package/hooks/blocking/pii_prevention_blocker.py +0 -365
  429. package/hooks/blocking/piped_pytest_blocker.py +0 -1223
  430. package/hooks/blocking/question_to_user_enforcer.py +0 -130
  431. package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
  432. package/hooks/blocking/sensitive_file_protector.py +0 -167
  433. package/hooks/blocking/session_edit_stage_gate.py +0 -684
  434. package/hooks/blocking/session_handoff_blocker.py +0 -182
  435. package/hooks/blocking/shell_substitution_blocker.py +0 -350
  436. package/hooks/blocking/test_ask_user_question_shape.py +0 -69
  437. package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
  438. package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
  439. package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
  440. package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
  441. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
  442. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
  443. package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
  444. package/hooks/blocking/test_fable_spawn_gate.py +0 -381
  445. package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
  446. package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
  447. package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
  448. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
  449. package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
  450. package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
  451. package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
  452. package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
  453. package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
  454. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
  455. package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
  456. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
  457. package/hooks/blocking/test_sensitive_file_protector.py +0 -365
  458. package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
  459. package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
  460. package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
  461. package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
  462. package/hooks/blocking/test_session_handoff_blocker.py +0 -318
  463. package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
  464. package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
  465. package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
  466. package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
  467. package/hooks/blocking/unscoped_search_blocker.py +0 -391
  468. package/hooks/blocking/write_existing_file_blocker.py +0 -124
  469. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
  470. package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
  471. package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
  472. package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
  473. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
  474. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
  475. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
  476. package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
  477. package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
  478. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
  479. /package/.agents/skills/{_shared/pr-loop → session-continuity}/AGENTS.md +0 -0
  480. /package/{_shared/pr-loop → .agents/skills/session-continuity}/CLAUDE.md +0 -0
@@ -1,187 +0,0 @@
1
- #!/usr/bin/env python3
2
- """PreToolUse gate: deny a fable-tier subagent spawn that carries no marker.
3
-
4
- One exact token in the spawn prompt authorizes a spawn at the fable tier::
5
-
6
- model: fable, no FABLE-SPAWN-AUTHORIZED flag: deny
7
- model: claude-fable-5, no FABLE-SPAWN-AUTHORIZED flag: deny
8
- model: fable, FABLE-SPAWN-AUTHORIZED ok: allow
9
- model: claude-sonnet-4, prompt either way ok: allow
10
- no model field, prompt either way ok: allow
11
-
12
- The gate reads an ``Agent`` or ``Task`` call and finds the tier in the model
13
- field whether that field carries the bare alias or a full model id, in any
14
- letter case. Its deny reason points at the advisor-protocol document rather
15
- than quoting the token, so a denial pasted into a retry authorizes nothing.
16
- A deny also sets ``additionalContext`` so the spawner sees the recovery path
17
- (re-spawn at opus / an opus-equivalent tier, or authorize a fable bind).
18
- """
19
-
20
- from __future__ import annotations
21
-
22
- import json
23
- import sys
24
- from pathlib import Path
25
- from typing import Mapping, TextIO
26
-
27
- _hooks_dir = str(Path(__file__).resolve().parent.parent)
28
- if _hooks_dir not in sys.path:
29
- sys.path.insert(0, _hooks_dir)
30
-
31
- from hooks_constants.fable_spawn_gate_constants import ( # noqa: E402
32
- ALL_SPAWN_TOOL_NAMES,
33
- CALLING_HOOK_NAME,
34
- DENY_ADDITIONAL_CONTEXT,
35
- DENY_PREVIEW_TEMPLATE,
36
- DENY_REASON,
37
- FABLE_MODEL_ALIAS,
38
- FABLE_SPAWN_AUTHORIZATION_MARKER,
39
- HOOK_EVENT_NAME,
40
- MAXIMUM_PREVIEW_MODEL_LENGTH,
41
- MODEL_FIELD_NAME,
42
- MODEL_SEGMENT_SPLIT_PATTERN,
43
- PROMPT_FIELD_NAME,
44
- TOOL_INPUT_FIELD_NAME,
45
- TOOL_NAME_FIELD_NAME,
46
- )
47
- from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
48
- from hooks_constants.pre_tool_use_stdin import ( # noqa: E402
49
- read_hook_input_dictionary_from_stdin,
50
- )
51
-
52
-
53
- def _model_names_the_fable_tier(model_identifier: str) -> bool:
54
- """Report whether a model string names the fable tier.
55
-
56
- The field carries either the bare alias or a full model id, so the
57
- lowercased string is cut on its delimiters and the tier is looked for as
58
- a whole segment::
59
-
60
- fable -> ['fable'] flag: fable tier
61
- claude-fable-5 -> ['claude', 'fable', '5'] flag: fable tier
62
- claude-sonnet-4 -> ['claude', 'sonnet', '4'] ok: another tier
63
- affable -> ['affable'] ok: another tier
64
-
65
- Args:
66
- model_identifier: The spawn's ``model`` string.
67
-
68
- Returns:
69
- True when a model segment reads ``fable`` in any letter case; False
70
- for another tier.
71
- """
72
- all_segments = MODEL_SEGMENT_SPLIT_PATTERN.split(model_identifier.strip().lower())
73
- return FABLE_MODEL_ALIAS in all_segments
74
-
75
-
76
- def _prompt_carries_authorization_marker(all_tool_input: Mapping[str, object]) -> bool:
77
- """Report whether the spawn prompt holds the exact authorization token.
78
-
79
- Args:
80
- all_tool_input: The spawn's ``tool_input`` mapping.
81
-
82
- Returns:
83
- True when the prompt is a string holding the marker token; False for
84
- an unmarked prompt and for an absent or non-string field.
85
- """
86
- spawn_prompt = all_tool_input.get(PROMPT_FIELD_NAME)
87
- if not isinstance(spawn_prompt, str):
88
- return False
89
- return FABLE_SPAWN_AUTHORIZATION_MARKER in spawn_prompt
90
-
91
-
92
- def _denied_spawn_model(all_payload_by_field: Mapping[str, object]) -> str | None:
93
- """Return the model string of an unauthorized fable spawn, or None.
94
-
95
- The model string travels back as the decision, so the deny path holds a
96
- proven fable string and never re-derives one.
97
-
98
- Args:
99
- all_payload_by_field: The parsed PreToolUse payload, keyed by
100
- top-level field name.
101
-
102
- Returns:
103
- The ``model`` string for a spawn tool call at the fable tier whose
104
- prompt lacks the authorization marker; None for every allowed call.
105
- """
106
- if all_payload_by_field.get(TOOL_NAME_FIELD_NAME, "") not in ALL_SPAWN_TOOL_NAMES:
107
- return None
108
- tool_input = all_payload_by_field.get(TOOL_INPUT_FIELD_NAME, {})
109
- if not isinstance(tool_input, dict):
110
- return None
111
- model_identifier = tool_input.get(MODEL_FIELD_NAME)
112
- if not isinstance(model_identifier, str):
113
- return None
114
- if not _model_names_the_fable_tier(model_identifier):
115
- return None
116
- if _prompt_carries_authorization_marker(tool_input):
117
- return None
118
- return model_identifier
119
-
120
-
121
- def _build_denial_preview(model_identifier: str) -> str:
122
- """Build the bounded preview the hook-blocks log records for a denial.
123
-
124
- The preview names the model field that tripped the gate, so its length
125
- holds steady whatever the spawn prompt carries::
126
-
127
- fable -> model=fable marker_present=False
128
- claude-fable-5 -> model=claude-fable-5 marker_present=False
129
-
130
- Args:
131
- model_identifier: The fable model string the denial was decided on.
132
-
133
- Returns:
134
- One preview line naming the model text and the marker state.
135
- """
136
- return DENY_PREVIEW_TEMPLATE.format(
137
- model_text=model_identifier[:MAXIMUM_PREVIEW_MODEL_LENGTH]
138
- )
139
-
140
-
141
- def _emit_denial(
142
- decision_stream: TextIO,
143
- all_payload_by_field: Mapping[str, object],
144
- model_identifier: str,
145
- ) -> None:
146
- """Log the denied spawn and write the PreToolUse deny payload.
147
-
148
- Args:
149
- decision_stream: Writable text stream — production code passes
150
- ``sys.stdout``; tests pass a ``StringIO`` to capture the JSON.
151
- all_payload_by_field: The parsed PreToolUse payload, whose tool name
152
- names the denied spawn in the hook-blocks log.
153
- model_identifier: The fable model string the denial was decided on.
154
- """
155
- denial = {
156
- "hookSpecificOutput": {
157
- "hookEventName": HOOK_EVENT_NAME,
158
- "permissionDecision": "deny",
159
- "permissionDecisionReason": DENY_REASON,
160
- "additionalContext": DENY_ADDITIONAL_CONTEXT,
161
- }
162
- }
163
- log_hook_block(
164
- calling_hook_name=CALLING_HOOK_NAME,
165
- hook_event=HOOK_EVENT_NAME,
166
- block_reason=DENY_REASON,
167
- tool_name=str(all_payload_by_field.get(TOOL_NAME_FIELD_NAME, "")),
168
- offending_input_preview=_build_denial_preview(model_identifier),
169
- )
170
- decision_stream.write(json.dumps(denial) + "\n")
171
- decision_stream.flush()
172
-
173
-
174
- def main() -> None:
175
- """Read the PreToolUse payload and deny an unmarked fable spawn."""
176
- hook_payload = read_hook_input_dictionary_from_stdin()
177
- if hook_payload is None:
178
- sys.exit(0)
179
- denied_model = _denied_spawn_model(hook_payload)
180
- if denied_model is None:
181
- sys.exit(0)
182
- _emit_denial(sys.stdout, hook_payload, denied_model)
183
- sys.exit(0)
184
-
185
-
186
- if __name__ == "__main__":
187
- main()
@@ -1,160 +0,0 @@
1
- #!/usr/bin/env python3
2
- """PreToolUse gate: deny Luna spawns that use an unsupported service tier."""
3
-
4
- from __future__ import annotations
5
-
6
- import json
7
- import sys
8
- from pathlib import Path
9
- from typing import Mapping, TextIO
10
-
11
- _hooks_dir = str(Path(__file__).resolve().parent.parent)
12
- if _hooks_dir not in sys.path:
13
- sys.path.insert(0, _hooks_dir)
14
-
15
- from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
16
- from hooks_constants.luna_fast_mode_gate_constants import ( # noqa: E402
17
- ALL_SPAWN_TOOL_NAMES,
18
- CALLING_HOOK_NAME,
19
- CODEX_AGENT_TOOL_NAME,
20
- DENY_ADDITIONAL_CONTEXT,
21
- DENY_PREVIEW_TEMPLATE,
22
- DENY_REASON,
23
- FAST_SERVICE_TIER,
24
- HOOK_EVENT_NAME,
25
- LUNA_MODEL_ALIAS,
26
- MAXIMUM_PREVIEW_FIELD_LENGTH,
27
- MODEL_FIELD_NAME,
28
- MODEL_SEGMENT_SPLIT_PATTERN,
29
- PRIORITY_SERVICE_TIER,
30
- SERVICE_TIER_FIELD_NAME,
31
- TOOL_INPUT_FIELD_NAME,
32
- TOOL_NAME_FIELD_NAME,
33
- )
34
- from hooks_constants.pre_tool_use_stdin import ( # noqa: E402
35
- read_hook_input_dictionary_from_stdin,
36
- )
37
-
38
-
39
- def _model_names_the_luna_tier(model_identifier: str) -> bool:
40
- """Report whether a model string names the Luna tier.
41
-
42
- Args:
43
- model_identifier: The spawn's ``model`` string.
44
-
45
- Returns:
46
- True when a model segment reads ``luna`` in any letter case; False
47
- for another tier.
48
- """
49
- all_segments = MODEL_SEGMENT_SPLIT_PATTERN.split(model_identifier.strip().lower())
50
- return LUNA_MODEL_ALIAS in all_segments
51
-
52
-
53
- def _is_service_tier_allowed(tool_name: object, service_tier: object) -> bool:
54
- if service_tier == FAST_SERVICE_TIER:
55
- return True
56
- return (
57
- tool_name == CODEX_AGENT_TOOL_NAME
58
- and service_tier == PRIORITY_SERVICE_TIER
59
- )
60
-
61
-
62
- def _denied_spawn_details(
63
- all_payload_by_field: Mapping[str, object],
64
- ) -> tuple[str, object] | None:
65
- """Return model and service tier for a Luna spawn that must be denied.
66
-
67
- Args:
68
- all_payload_by_field: The parsed PreToolUse payload.
69
-
70
- Returns:
71
- The model and service tier for an invalid Luna spawn; None otherwise.
72
- """
73
- tool_name = all_payload_by_field.get(TOOL_NAME_FIELD_NAME, "")
74
- if tool_name not in ALL_SPAWN_TOOL_NAMES:
75
- return None
76
- tool_input = all_payload_by_field.get(TOOL_INPUT_FIELD_NAME, {})
77
- if not isinstance(tool_input, dict):
78
- return None
79
- model_identifier = tool_input.get(MODEL_FIELD_NAME)
80
- if not isinstance(model_identifier, str):
81
- return None
82
- if not _model_names_the_luna_tier(model_identifier):
83
- return None
84
- service_tier = tool_input.get(SERVICE_TIER_FIELD_NAME)
85
- if _is_service_tier_allowed(tool_name, service_tier):
86
- return None
87
- return model_identifier, service_tier
88
-
89
-
90
- def _build_denial_preview(model_identifier: str, service_tier: object) -> str:
91
- """Build a bounded preview for the block log without recording the prompt.
92
-
93
- Args:
94
- model_identifier: The Luna model string.
95
- service_tier: The supplied service tier value.
96
-
97
- Returns:
98
- A bounded preview naming only the model and service tier fields.
99
- """
100
- model_text = model_identifier[:MAXIMUM_PREVIEW_FIELD_LENGTH]
101
- service_tier_text = str(service_tier)[:MAXIMUM_PREVIEW_FIELD_LENGTH]
102
- return DENY_PREVIEW_TEMPLATE.format(
103
- model_text=model_text,
104
- service_tier_text=service_tier_text,
105
- )
106
-
107
-
108
- def _emit_denial(
109
- decision_stream: TextIO,
110
- all_payload_by_field: Mapping[str, object],
111
- model_identifier: str,
112
- service_tier: object,
113
- ) -> None:
114
- """Log the denied spawn and write the PreToolUse deny payload.
115
-
116
- Args:
117
- decision_stream: Writable stream for the JSON decision.
118
- all_payload_by_field: The parsed PreToolUse payload.
119
- model_identifier: The denied Luna model string.
120
- service_tier: The invalid service tier value.
121
- """
122
- denial = {
123
- "hookSpecificOutput": {
124
- "hookEventName": HOOK_EVENT_NAME,
125
- "permissionDecision": "deny",
126
- "permissionDecisionReason": DENY_REASON,
127
- "additionalContext": DENY_ADDITIONAL_CONTEXT,
128
- }
129
- }
130
- log_hook_block(
131
- calling_hook_name=CALLING_HOOK_NAME,
132
- hook_event=HOOK_EVENT_NAME,
133
- block_reason=DENY_REASON,
134
- tool_name=str(all_payload_by_field.get(TOOL_NAME_FIELD_NAME, "")),
135
- offending_input_preview=_build_denial_preview(model_identifier, service_tier),
136
- )
137
- decision_stream.write(json.dumps(denial) + "\n")
138
- decision_stream.flush()
139
-
140
-
141
- def main() -> None:
142
- """Read the PreToolUse payload and deny an invalid Luna spawn."""
143
- hook_payload = read_hook_input_dictionary_from_stdin()
144
- if hook_payload is None:
145
- sys.exit(0)
146
- denied_details = _denied_spawn_details(hook_payload)
147
- if denied_details is None:
148
- sys.exit(0)
149
- denied_model, denied_service_tier = denied_details
150
- _emit_denial(
151
- sys.stdout,
152
- hook_payload,
153
- denied_model,
154
- denied_service_tier,
155
- )
156
- sys.exit(0)
157
-
158
-
159
- if __name__ == "__main__":
160
- main()
@@ -1,163 +0,0 @@
1
- #!/usr/bin/env python3
2
- """PreToolUse hook: force the Windows OpenSSH binary for NAS ssh/scp/sftp commands.
3
-
4
- Root cause: Git Bash's MSYS ssh reads ``~/.ssh/id_ed25519`` as world-readable
5
- through its ACL mapping, rejects the key as "bad permissions", offers no key, and
6
- falls back to an interactive password prompt that hangs unattended agent sessions.
7
- The Windows OpenSSH binary under ``System32/OpenSSH`` authenticates the same key
8
- without prompting.
9
-
10
- Detection strategy: this hook fires only on the Bash tool. The command is
11
- shlex-tokenized (quotes removed), glued shell control operators are exploded off
12
- the tokens, and the tokens are split into simple-command segments on ``&&`` /
13
- ``||`` / ``;`` / ``|&`` / ``|`` / ``&``. For each segment the effective leading program is
14
- found by skipping ``VAR=value`` assignments and launcher wrappers (``timeout``,
15
- ``nohup``, ``nice``, ``stdbuf``, ``setsid``, ``env``) with their flags and duration
16
- arguments. A segment is evaluated only when its leading program's basename is
17
- ``ssh``/``scp``/``sftp`` (with or without a ``.exe`` suffix) AND a token in that same
18
- segment holds the NAS host (resolved from the ``CLAUDE_NAS_*`` environment variables or ``~/.claude/local-identity.json``) as a connection host — bare,
19
- ``user@host``, or a ``host:path`` target. The address inside a remote-command
20
- argument or a remote path component on another host does not count.
21
-
22
- An evaluated segment is DENIED with the binary-swap message when its leading program
23
- is a bare ssh-family word rather than a path ending in ``OpenSSH/ssh.exe`` (or
24
- ``scp.exe`` / ``sftp.exe``). An evaluated segment whose leading program is that full
25
- OpenSSH path is DENIED with the batch-mode message when no token in the segment
26
- carries ``BatchMode=yes``, so an authentication regression fails loudly rather than
27
- prompting. Everything else is allowed: ssh to any other host, the full OpenSSH path
28
- with ``BatchMode=yes``, non-Bash tools, a command that only mentions the address
29
- without an ssh-family leading program, and a command shlex cannot tokenize.
30
- """
31
-
32
- import json
33
- import re
34
- import shlex
35
- import sys
36
- from pathlib import Path
37
-
38
- _hooks_dir = str(Path(__file__).resolve().parent.parent)
39
- if _hooks_dir not in sys.path:
40
- sys.path.insert(0, _hooks_dir)
41
-
42
- from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
43
- from hooks_constants.local_identity import ( # noqa: E402
44
- bare_ssh_binary_deny_message,
45
- missing_batch_mode_deny_message,
46
- nas_host,
47
- )
48
- from hooks_constants.nas_ssh_binary_enforcer_constants import ( # noqa: E402
49
- ALL_OPENSSH_BINARY_PATH_SUFFIXES,
50
- ALL_SSH_FAMILY_COMMAND_BASENAMES,
51
- BASH_TOOL_NAME,
52
- BATCH_MODE_PATTERN,
53
- )
54
- from hooks_constants.shell_command_segments import ( # noqa: E402
55
- effective_leading_program,
56
- split_into_segments,
57
- token_basename,
58
- )
59
-
60
-
61
- def _is_openssh_binary_path(token: str) -> bool:
62
- normalized_token = token.replace("\\", "/").lower()
63
- return any(
64
- normalized_token.endswith(each_suffix)
65
- for each_suffix in ALL_OPENSSH_BINARY_PATH_SUFFIXES
66
- )
67
-
68
-
69
- def _nas_host_token_pattern() -> re.Pattern[str]:
70
- return re.compile(rf"(?:^|@){re.escape(nas_host())}(?::|$)")
71
-
72
-
73
- def _segment_references_nas(
74
- all_segment_tokens: list[str], nas_host_pattern: re.Pattern[str]
75
- ) -> bool:
76
- return any(nas_host_pattern.search(each_token) for each_token in all_segment_tokens)
77
-
78
-
79
- def _segment_carries_batch_mode(all_segment_tokens: list[str]) -> bool:
80
- return any(BATCH_MODE_PATTERN.search(each_token) for each_token in all_segment_tokens)
81
-
82
-
83
- def _find_nas_ssh_violation(command: str) -> str | None:
84
- """Return the deny message for a NAS ssh-family command, or None to allow.
85
-
86
- ::
87
-
88
- ssh -p 22 operator@nas.example.local "ls" flag: bare-binary message
89
- "…/OpenSSH/ssh.exe" -p 22 operator@nas… flag: missing-batch-mode message
90
- "…/OpenSSH/ssh.exe" -o BatchMode=yes …NAS ok: None
91
- ssh operator@example.com "ls" ok: None
92
-
93
- Tokenizes the command, splits it into simple-command segments, and evaluates
94
- each segment whose leading program is an ssh-family word AND whose tokens carry
95
- the NAS address. A bare ssh-family leader returns the binary-swap message; the
96
- full OpenSSH path leader without a ``BatchMode=yes`` token returns the batch-mode
97
- message. Returns None when shlex cannot tokenize the command.
98
-
99
- Args:
100
- command: The raw Bash command string from the tool input.
101
-
102
- Returns:
103
- The deny message string when a segment violates the NAS ssh policy, else None.
104
- """
105
- try:
106
- all_command_tokens = shlex.split(command)
107
- except ValueError:
108
- return None
109
- nas_host_pattern = _nas_host_token_pattern()
110
- for each_segment in split_into_segments(all_command_tokens):
111
- leading_program = effective_leading_program(each_segment)
112
- if leading_program is None:
113
- continue
114
- if token_basename(leading_program) not in ALL_SSH_FAMILY_COMMAND_BASENAMES:
115
- continue
116
- if not _segment_references_nas(each_segment, nas_host_pattern):
117
- continue
118
- if not _is_openssh_binary_path(leading_program):
119
- return bare_ssh_binary_deny_message()
120
- if not _segment_carries_batch_mode(each_segment):
121
- return missing_batch_mode_deny_message()
122
- return None
123
-
124
-
125
- def main() -> None:
126
- try:
127
- hook_input = json.load(sys.stdin)
128
- except json.JSONDecodeError:
129
- sys.exit(0)
130
-
131
- tool_name = hook_input.get("tool_name", "")
132
- if tool_name != BASH_TOOL_NAME:
133
- sys.exit(0)
134
-
135
- command = hook_input.get("tool_input", {}).get("command", "")
136
- if not command:
137
- sys.exit(0)
138
-
139
- deny_reason = _find_nas_ssh_violation(command)
140
- if deny_reason is None:
141
- sys.exit(0)
142
-
143
- deny_payload = {
144
- "hookSpecificOutput": {
145
- "hookEventName": "PreToolUse",
146
- "permissionDecision": "deny",
147
- "permissionDecisionReason": deny_reason,
148
- }
149
- }
150
- log_hook_block(
151
- calling_hook_name="nas_ssh_binary_enforcer.py",
152
- hook_event="PreToolUse",
153
- block_reason=deny_reason,
154
- tool_name=tool_name,
155
- offending_input_preview=command,
156
- )
157
- print(json.dumps(deny_payload))
158
- sys.stdout.flush()
159
- sys.exit(0)
160
-
161
-
162
- if __name__ == "__main__":
163
- main()