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
@@ -286,7 +286,7 @@ test('installer fault after_file_staging restores prior managed installation', (
286
286
  'utf8',
287
287
  );
288
288
 
289
- const failedRun = runInstaller(homeDirectory, ['--only', 'journal'], {
289
+ const failedRun = runInstaller(homeDirectory, ['--only', 'core'], {
290
290
  faultPhase: FAULT_PHASES.AFTER_FILE_STAGING,
291
291
  });
292
292
  assert.notEqual(failedRun.status, 0, failedRun.stdout + failedRun.stderr);
@@ -333,7 +333,7 @@ test('installer fault after_manifest_write restores prior manifest', () => {
333
333
  'utf8',
334
334
  );
335
335
 
336
- const failedRun = runInstaller(homeDirectory, ['--only', 'journal'], {
336
+ const failedRun = runInstaller(homeDirectory, ['--only', 'core'], {
337
337
  faultPhase: FAULT_PHASES.AFTER_MANIFEST_WRITE,
338
338
  });
339
339
  assert.notEqual(failedRun.status, 0, failedRun.stdout + failedRun.stderr);
@@ -387,7 +387,7 @@ test('successful install after prior install leaves one manifest and no fault',
387
387
  const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-txn-e2e-ok-'));
388
388
  try {
389
389
  seedPriorInstall(homeDirectory);
390
- const okRun = runInstaller(homeDirectory, ['--only', 'journal']);
390
+ const okRun = runInstaller(homeDirectory, ['--only', 'core']);
391
391
  assert.equal(okRun.status, 0, okRun.stdout + okRun.stderr);
392
392
  const manifestPath = join(homeDirectory, '.claude', '.claude-dev-env-manifest.json');
393
393
  assert.equal(existsSync(manifestPath), true);
@@ -0,0 +1,211 @@
1
+ import { test } from 'node:test';
2
+ import { strict as assert } from 'node:assert';
3
+ import {
4
+ mkdtempSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ rmSync,
8
+ symlinkSync,
9
+ writeFileSync,
10
+ } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { dirname, join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { execFileSync } from 'node:child_process';
15
+ import {
16
+ PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH,
17
+ findPstackSkillDirectoryNames,
18
+ refreshPstackPluginManifest,
19
+ } from '../scripts/refresh_pstack_plugin_skills.mjs';
20
+
21
+ function buildPluginRoot(homeDirectory, skillNames, manifest) {
22
+ const pluginRoot = join(homeDirectory, 'pstack');
23
+ for (const eachSkillName of skillNames) {
24
+ const skillDirectory = join(pluginRoot, eachSkillName);
25
+ mkdirSync(skillDirectory, { recursive: true });
26
+ writeFileSync(join(skillDirectory, 'SKILL.md'), '---\n');
27
+ }
28
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
29
+ mkdirSync(join(pluginRoot, '.claude-plugin'), { recursive: true });
30
+ if (manifest !== null) {
31
+ writeFileSync(manifestPath, JSON.stringify(manifest));
32
+ }
33
+ return pluginRoot;
34
+ }
35
+
36
+ function withTemporaryHome(runAssertions) {
37
+ const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-pstack-manifest-'));
38
+ try {
39
+ runAssertions(homeDirectory);
40
+ } finally {
41
+ rmSync(homeDirectory, { recursive: true, force: true });
42
+ }
43
+ }
44
+
45
+ test('finds only sub-folders that hold a skill entry file', () => {
46
+ withTemporaryHome((homeDirectory) => {
47
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how', 'why'], { skills: [] });
48
+ mkdirSync(join(pluginRoot, 'docs'), { recursive: true });
49
+ writeFileSync(join(pluginRoot, '.claude-plugin', 'SKILL.md'), '---\n');
50
+
51
+ assert.deepEqual(findPstackSkillDirectoryNames(pluginRoot), ['how', 'why']);
52
+ });
53
+ });
54
+
55
+ test('writes a manifest when the plugin folder has none', () => {
56
+ withTemporaryHome((homeDirectory) => {
57
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], null);
58
+
59
+ const outcome = refreshPstackPluginManifest(pluginRoot);
60
+
61
+ assert.equal(outcome.didWrite, true);
62
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
63
+ const written = JSON.parse(readFileSync(manifestPath, 'utf8'));
64
+ assert.equal(written.name, 'pstack');
65
+ assert.deepEqual(written.skills, ['./how']);
66
+ });
67
+ });
68
+
69
+ test('adds a skill folder the manifest does not list', () => {
70
+ withTemporaryHome((homeDirectory) => {
71
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how', 'why'], {
72
+ name: 'pstack',
73
+ skills: ['./how'],
74
+ });
75
+
76
+ const outcome = refreshPstackPluginManifest(pluginRoot);
77
+
78
+ assert.equal(outcome.didWrite, true);
79
+ assert.deepEqual(outcome.previousSkillPaths, ['./how']);
80
+ assert.deepEqual(outcome.currentSkillPaths, ['./how', './why']);
81
+ });
82
+ });
83
+
84
+ test('leaves the manifest byte for byte when nothing changed', () => {
85
+ withTemporaryHome((homeDirectory) => {
86
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], {
87
+ name: 'pstack',
88
+ skills: ['./how'],
89
+ });
90
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
91
+ writeFileSync(manifestPath, JSON.stringify({ name: 'pstack', skills: ['./how'] }));
92
+ const before = readFileSync(manifestPath, 'utf8');
93
+
94
+ const outcome = refreshPstackPluginManifest(pluginRoot);
95
+
96
+ assert.equal(outcome.didWrite, false);
97
+ assert.equal(readFileSync(manifestPath, 'utf8'), before);
98
+ });
99
+ });
100
+
101
+ test('reads a string skills value as a one-entry list', () => {
102
+ withTemporaryHome((homeDirectory) => {
103
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], {
104
+ name: 'pstack',
105
+ skills: './',
106
+ });
107
+
108
+ const outcome = refreshPstackPluginManifest(pluginRoot);
109
+
110
+ assert.deepEqual(outcome.previousSkillPaths, ['./']);
111
+ assert.deepEqual(outcome.currentSkillPaths, ['./how']);
112
+ });
113
+ });
114
+
115
+ test('keeps manifest fields the refresher does not own', () => {
116
+ withTemporaryHome((homeDirectory) => {
117
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], {
118
+ name: 'pstack',
119
+ version: '9.9.9',
120
+ description: 'a description the user edited',
121
+ skills: [],
122
+ });
123
+
124
+ refreshPstackPluginManifest(pluginRoot);
125
+
126
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
127
+ const written = JSON.parse(readFileSync(manifestPath, 'utf8'));
128
+ assert.equal(written.version, '9.9.9');
129
+ assert.equal(written.description, 'a description the user edited');
130
+ });
131
+ });
132
+
133
+ test('refuses a folder that is itself one skill', () => {
134
+ withTemporaryHome((homeDirectory) => {
135
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], null);
136
+ writeFileSync(join(pluginRoot, 'SKILL.md'), '---\n');
137
+
138
+ const outcome = refreshPstackPluginManifest(pluginRoot);
139
+
140
+ assert.equal(outcome.didWrite, false);
141
+ assert.equal(outcome.reason, 'plugin root is itself a skill');
142
+ });
143
+ });
144
+
145
+ test('running the file directly reports what it did', () => {
146
+ withTemporaryHome((homeDirectory) => {
147
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], null);
148
+ const modulePath = join(
149
+ dirname(dirname(fileURLToPath(import.meta.url))),
150
+ 'scripts',
151
+ 'refresh_pstack_plugin_skills.mjs',
152
+ );
153
+
154
+ const firstRun = execFileSync('node', [modulePath, pluginRoot], { encoding: 'utf8' });
155
+ const secondRun = execFileSync('node', [modulePath, pluginRoot], { encoding: 'utf8' });
156
+
157
+ assert.match(firstRun, /Updated\. 1 skills listed\./);
158
+ assert.match(secondRun, /No change\. 1 skills listed\./);
159
+ });
160
+ });
161
+
162
+ test('running the file through a directory link still reports what it did', () => {
163
+ withTemporaryHome((homeDirectory) => {
164
+ const pluginRoot = buildPluginRoot(homeDirectory, ['how'], null);
165
+ const scriptsDirectory = join(
166
+ dirname(dirname(fileURLToPath(import.meta.url))),
167
+ 'scripts',
168
+ );
169
+ const linkedScriptsDirectory = join(homeDirectory, 'linked-scripts');
170
+ symlinkSync(scriptsDirectory, linkedScriptsDirectory, 'junction');
171
+ const linkedModulePath = join(linkedScriptsDirectory, 'refresh_pstack_plugin_skills.mjs');
172
+
173
+ const output = execFileSync('node', [linkedModulePath, pluginRoot], { encoding: 'utf8' });
174
+
175
+ assert.match(output, /Updated\. 1 skills listed\./);
176
+ });
177
+ });
178
+
179
+ test('with no folder argument it follows CLAUDE_CONFIG_DIR', () => {
180
+ withTemporaryHome((homeDirectory) => {
181
+ const claudeConfigDirectory = join(homeDirectory, 'profile', '.claude');
182
+ const pluginRoot = join(claudeConfigDirectory, 'skills', 'pstack');
183
+ mkdirSync(join(pluginRoot, 'how'), { recursive: true });
184
+ writeFileSync(join(pluginRoot, 'how', 'SKILL.md'), '---\n');
185
+ const modulePath = join(
186
+ dirname(dirname(fileURLToPath(import.meta.url))),
187
+ 'scripts',
188
+ 'refresh_pstack_plugin_skills.mjs',
189
+ );
190
+
191
+ const output = execFileSync('node', [modulePath], {
192
+ encoding: 'utf8',
193
+ env: { ...process.env, CLAUDE_CONFIG_DIR: claudeConfigDirectory },
194
+ });
195
+
196
+ assert.match(output, /Updated\. 1 skills listed\./);
197
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
198
+ assert.deepEqual(JSON.parse(readFileSync(manifestPath, 'utf8')).skills, ['./how']);
199
+ });
200
+ });
201
+
202
+ test('refuses a folder that holds no skills', () => {
203
+ withTemporaryHome((homeDirectory) => {
204
+ const pluginRoot = buildPluginRoot(homeDirectory, [], null);
205
+
206
+ const outcome = refreshPstackPluginManifest(pluginRoot);
207
+
208
+ assert.equal(outcome.didWrite, false);
209
+ assert.equal(outcome.reason, 'plugin root holds no skills');
210
+ });
211
+ });
@@ -43,7 +43,7 @@ Before writing ANY constant: search `config/` for the exact value → semantic m
43
43
 
44
44
  ## 5. NO ABBREVIATIONS
45
45
 
46
- Full words only (`context`, not `ctx`). Exceptions: `i`/`j`/`k` in loops, `e` for exception. Naming patterns: loop vars `each_*`; booleans `is_/has_/should_/can_/was_/did_`; collections `all_*`; maps `X_by_Y`; preposition params (`from_path=`, `to=`, `into=`). Banned names: `result`, `data`, `output`, `response`, `value`, `item`, `temp`. Banned prefixes: `handle`, `process`, `manage`, `do`. Name a component for what it IS — `Overlay`, `Validator`, `InvoicePreview`.
46
+ Full words only (`context`, not `ctx`). Exceptions: `i`/`j`/`k` in loops, `e` for exception. Naming patterns: loop vars `each_*`; booleans `is_/has_/should_/can_/was_/did_`; collections `all_*`; maps `X_by_Y`; preposition params (`from_path=`, `to=`, `into=`). Banned names: `result`, `data`, `output`, `response`, `value`, `item`, `temp`. Banned prefixes: `handle`, `process`, `manage`, `do`. Name components for what they are: `Overlay`, `Validator`, `InvoicePreview`. The failure-blast-radius rule requires `ItemBlocked` or `RunFatal`. Names ending with either bypass the banned-noun word check.
47
47
 
48
48
  ### Public compatibility definitions
49
49
 
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env python3
2
+ """PostToolUse context reminder: the PR done checklist after a push.
3
+
4
+ This hook never blocks. It watches every Bash call finish and, after a
5
+ successful ``git push`` or ``gh pr create``, probes the branch's pull request
6
+ through ``gh pr view`` and adds one loud checklist to the agent's context::
7
+
8
+ === PR DONE CHECKLIST (context reminder, never a block) ===
9
+ PR #42 https://github.com/acme/widgets/pull/42
10
+ Mergeable: CONFLICTING (CONFLICTS with the base branch. Merge origin/main ...)
11
+ CI checks: 1 failing, 2 pending, 4 total
12
+ Draft: yes
13
+ Label done: not set
14
+ Verdict: NOT DONE. Do not add the done label yet.
15
+ Re-check: gh pr view 42 --json mergeable,mergeStateStatus,statusCheckRollup,labels
16
+ A PR is done only when every line above is clean.
17
+
18
+ Why a reminder and not a gate: agents keep working. The rule "a PR is done
19
+ only when GitHub reports it mergeable and CI is clean" arrives as live state
20
+ at the exact moment the agent just pushed, so no instruction file has to be
21
+ remembered. A block would stall the run; this only informs it.
22
+
23
+ Quiet branches: a non-Bash tool, a command that is not a push or a PR
24
+ creation, a push whose harness-reported exit status is non-zero, or a ``gh``
25
+ failure other than "no pull requests found" each emit nothing.
26
+
27
+ Hosted by ``blocking/bash_post_call_dispatcher.py``, which forwards the
28
+ ``hookSpecificOutput.additionalContext`` this hook prints.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import json
34
+ import shlex
35
+ import subprocess
36
+ import sys
37
+ from pathlib import Path
38
+
39
+ try:
40
+ _hooks_root_directory = str(Path(__file__).resolve().parent.parent)
41
+ if _hooks_root_directory not in sys.path:
42
+ sys.path.insert(0, _hooks_root_directory)
43
+ from hooks_constants.bash_pre_tool_use_dispatcher_constants import (
44
+ ALL_BASH_AND_POWERSHELL_TOOL_NAMES,
45
+ )
46
+ from hooks_constants.pr_done_reminder_constants import (
47
+ ADD_LABEL_COMMAND_TEMPLATE,
48
+ ALL_FAILING_CHECK_CONCLUSIONS,
49
+ ALL_PASSING_CHECK_CONCLUSIONS,
50
+ CHECK_COMPLETED_STATUS,
51
+ DONE_LABEL_NAME,
52
+ EXIT_CODE_ERROR_PREFIX,
53
+ GH_PR_CREATE_ACTION,
54
+ GH_PR_SUBCOMMAND,
55
+ ALL_GH_PR_VIEW_ARGUMENTS,
56
+ ALL_GIT_OPTIONS_WITH_VALUE,
57
+ ALL_POWERSHELL_COMMAND_FLAGS,
58
+ ALL_POWERSHELL_PROGRAM_NAMES,
59
+ GH_PR_VIEW_TIMEOUT_SECONDS,
60
+ GH_PROGRAM_NAME,
61
+ GIT_PROGRAM_NAME,
62
+ GIT_PUSH_SUBCOMMAND,
63
+ MERGEABLE_CLEAN_VALUE,
64
+ MERGEABLE_UNKNOWN_VALUE,
65
+ NO_PULL_REQUEST_MARKER,
66
+ NO_PULL_REQUEST_REMINDER,
67
+ RECHECK_COMMAND_TEMPLATE,
68
+ REMINDER_FOOTER,
69
+ REMINDER_HEADER,
70
+ REMINDER_LINE_SEPARATOR,
71
+ ALL_REMINDER_HINTS_BY_MERGEABLE,
72
+ STATUS_CONTEXT_STATE_KEY,
73
+ VERDICT_DONE,
74
+ VERDICT_NOT_DONE,
75
+ )
76
+ from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
77
+ from hooks_constants.shell_command_segments import (
78
+ effective_leading_program,
79
+ split_into_segments,
80
+ token_basename,
81
+ )
82
+ except ImportError as import_error:
83
+ raise ImportError(
84
+ "pr_done_reminder: cannot import its dependencies; "
85
+ "ensure the hooks directory is importable."
86
+ ) from import_error
87
+
88
+
89
+ def _command_tokens(command: str) -> list[str]:
90
+ try:
91
+ return shlex.split(command, posix=True)
92
+ except ValueError:
93
+ return command.split()
94
+
95
+
96
+ def _segment_program_and_arguments(all_segment_tokens: list[str]) -> tuple[str, list[str]]:
97
+ program_token = effective_leading_program(all_segment_tokens)
98
+ if program_token is None:
99
+ return "", []
100
+ program_index = all_segment_tokens.index(program_token)
101
+ return token_basename(program_token), all_segment_tokens[program_index + 1 :]
102
+
103
+
104
+ def _git_subcommand(all_arguments: list[str]) -> str | None:
105
+ """Return git's subcommand after its global options.
106
+
107
+ ``git -C <path> push`` and ``git -c <key=value> push`` carry an option
108
+ value before the subcommand; ``--git-dir=<path>`` carries none. The first
109
+ token left after those is the subcommand, so ``git stash push`` yields
110
+ ``stash`` and ``git log --grep push`` yields ``log``.
111
+ """
112
+ should_skip_next_token = False
113
+ for each_argument in all_arguments:
114
+ if should_skip_next_token:
115
+ should_skip_next_token = False
116
+ continue
117
+ if each_argument in ALL_GIT_OPTIONS_WITH_VALUE:
118
+ should_skip_next_token = True
119
+ continue
120
+ if each_argument.startswith("-"):
121
+ continue
122
+ return each_argument
123
+ return None
124
+
125
+
126
+ def _is_git_push(program_name: str, all_arguments: list[str]) -> bool:
127
+ return (
128
+ program_name == GIT_PROGRAM_NAME and _git_subcommand(all_arguments) == GIT_PUSH_SUBCOMMAND
129
+ )
130
+
131
+
132
+ def _is_gh_pr_create(program_name: str, all_arguments: list[str]) -> bool:
133
+ return program_name == GH_PROGRAM_NAME and all_arguments[:2] == [
134
+ GH_PR_SUBCOMMAND,
135
+ GH_PR_CREATE_ACTION,
136
+ ]
137
+
138
+
139
+ def _powershell_inner_commands(all_command_tokens: list[str]) -> list[str]:
140
+ """Return the script texts every ``pwsh -Command "..."`` wrapper runs.
141
+
142
+ Read from the raw shlex tokens, before segment splitting: the quoted
143
+ script is one token here, and splitting it on ``;`` first would cut a
144
+ ``git add -A; git push`` script in half.
145
+ """
146
+ all_inner_commands: list[str] = []
147
+ has_seen_powershell_program = False
148
+ for each_token_index, each_token in enumerate(all_command_tokens[:-1]):
149
+ if token_basename(each_token) in ALL_POWERSHELL_PROGRAM_NAMES:
150
+ has_seen_powershell_program = True
151
+ continue
152
+ if has_seen_powershell_program and each_token.lower() in ALL_POWERSHELL_COMMAND_FLAGS:
153
+ all_inner_commands.append(all_command_tokens[each_token_index + 1])
154
+ has_seen_powershell_program = False
155
+ return all_inner_commands
156
+
157
+
158
+ def command_triggers_reminder(command: str) -> bool:
159
+ """Return True when any segment of the command is a git push or a gh pr create.
160
+
161
+ A ``pwsh -Command "..."`` wrapper is unwrapped and its inner text scanned
162
+ the same way, since the shell rule routes Bash-tool commands through pwsh.
163
+
164
+ Args:
165
+ command: The Bash command text the agent ran.
166
+ """
167
+ all_command_tokens = _command_tokens(command)
168
+ for each_inner_command in _powershell_inner_commands(all_command_tokens):
169
+ if command_triggers_reminder(each_inner_command):
170
+ return True
171
+ for each_segment in split_into_segments(all_command_tokens):
172
+ program_name, all_arguments = _segment_program_and_arguments(each_segment)
173
+ if _is_git_push(program_name, all_arguments) or _is_gh_pr_create(
174
+ program_name, all_arguments
175
+ ):
176
+ return True
177
+ return False
178
+
179
+
180
+ def _harness_reported_failure(tool_response: object) -> bool:
181
+ return isinstance(tool_response, str) and tool_response.startswith(EXIT_CODE_ERROR_PREFIX)
182
+
183
+
184
+ def _check_outcome(all_check_fields: dict[str, object]) -> str:
185
+ """Return failing, pending, or passing for one statusCheckRollup entry.
186
+
187
+ A CheckRun entry carries ``status`` and ``conclusion``; a StatusContext
188
+ entry carries ``state``. Both shapes appear in the same rollup list.
189
+ """
190
+ state_text = str(all_check_fields.get(STATUS_CONTEXT_STATE_KEY) or "").upper()
191
+ if state_text:
192
+ if state_text in ALL_FAILING_CHECK_CONCLUSIONS:
193
+ return "failing"
194
+ return "passing" if state_text in ALL_PASSING_CHECK_CONCLUSIONS else "pending"
195
+ if str(all_check_fields.get("status") or "").upper() != CHECK_COMPLETED_STATUS:
196
+ return "pending"
197
+ conclusion_text = str(all_check_fields.get("conclusion") or "").upper()
198
+ return "failing" if conclusion_text in ALL_FAILING_CHECK_CONCLUSIONS else "passing"
199
+
200
+
201
+ def _check_counts(all_pr_fields: dict[str, object]) -> dict[str, int]:
202
+ all_checks = all_pr_fields.get("statusCheckRollup")
203
+ all_check_counts = {"failing": 0, "pending": 0, "passing": 0, "total": 0}
204
+ if not isinstance(all_checks, list):
205
+ return all_check_counts
206
+ for each_check_fields in all_checks:
207
+ if isinstance(each_check_fields, dict):
208
+ all_check_counts[_check_outcome(each_check_fields)] += 1
209
+ all_check_counts["total"] += 1
210
+ return all_check_counts
211
+
212
+
213
+ def _checks_line(all_check_counts: dict[str, int]) -> str:
214
+ if all_check_counts["total"] == 0:
215
+ return "none reported"
216
+ return (
217
+ f"{all_check_counts['failing']} failing, "
218
+ f"{all_check_counts['pending']} pending, "
219
+ f"{all_check_counts['total']} total"
220
+ )
221
+
222
+
223
+ def _has_done_label(all_pr_fields: dict[str, object]) -> bool:
224
+ all_labels = all_pr_fields.get("labels")
225
+ if not isinstance(all_labels, list):
226
+ return False
227
+ return any(
228
+ isinstance(each_label, dict) and each_label.get("name") == DONE_LABEL_NAME
229
+ for each_label in all_labels
230
+ )
231
+
232
+
233
+ def _is_done(mergeable_value: str, all_check_counts: dict[str, int]) -> bool:
234
+ return (
235
+ mergeable_value == MERGEABLE_CLEAN_VALUE
236
+ and all_check_counts["failing"] == 0
237
+ and all_check_counts["pending"] == 0
238
+ )
239
+
240
+
241
+ def _verdict_lines(number: object, is_done: bool) -> list[str]:
242
+ """Return the verdict line, the add-label command on DONE, and the re-check line."""
243
+ all_lines = [f"Verdict: {VERDICT_DONE if is_done else VERDICT_NOT_DONE}"]
244
+ if is_done:
245
+ add_label_command = ADD_LABEL_COMMAND_TEMPLATE.format(number=number, label=DONE_LABEL_NAME)
246
+ all_lines.append(f"Add label: {add_label_command}")
247
+ all_lines.append(f"Re-check: {RECHECK_COMMAND_TEMPLATE.format(number=number)}")
248
+ return all_lines
249
+
250
+
251
+ def build_reminder_context(all_pr_fields: dict[str, object]) -> str:
252
+ """Build the checklist text from a ``gh pr view --json`` object.
253
+
254
+ Args:
255
+ all_pr_fields: The parsed pull request JSON with number, url, isDraft,
256
+ mergeable, mergeStateStatus, statusCheckRollup, and labels.
257
+
258
+ Returns:
259
+ The multi-line checklist ending in a DONE or NOT DONE verdict.
260
+ """
261
+ number = all_pr_fields.get("number", "?")
262
+ mergeable_value = str(all_pr_fields.get("mergeable") or MERGEABLE_UNKNOWN_VALUE).upper()
263
+ mergeable_hint = ALL_REMINDER_HINTS_BY_MERGEABLE.get(
264
+ mergeable_value, ALL_REMINDER_HINTS_BY_MERGEABLE[MERGEABLE_UNKNOWN_VALUE]
265
+ )
266
+ all_check_counts = _check_counts(all_pr_fields)
267
+ all_lines = [
268
+ REMINDER_HEADER,
269
+ f"PR #{number} {all_pr_fields.get('url', '')}",
270
+ f"Mergeable: {mergeable_value} ({mergeable_hint})",
271
+ f"CI checks: {_checks_line(all_check_counts)}",
272
+ f"Draft: {'yes' if all_pr_fields.get('isDraft') else 'no'}",
273
+ f"Label done: {'set' if _has_done_label(all_pr_fields) else 'not set'}",
274
+ *_verdict_lines(number, _is_done(mergeable_value, all_check_counts)),
275
+ REMINDER_FOOTER,
276
+ ]
277
+ return REMINDER_LINE_SEPARATOR.join(all_lines)
278
+
279
+
280
+ def _probe_pull_request(cwd: str) -> str | None:
281
+ """Return the reminder text from a live ``gh pr view``, or None to stay quiet."""
282
+ try:
283
+ completed_process = subprocess.run(
284
+ list(ALL_GH_PR_VIEW_ARGUMENTS),
285
+ cwd=cwd or None,
286
+ capture_output=True,
287
+ text=True,
288
+ encoding="utf-8",
289
+ errors="replace",
290
+ timeout=GH_PR_VIEW_TIMEOUT_SECONDS,
291
+ check=False,
292
+ )
293
+ except (OSError, subprocess.TimeoutExpired):
294
+ return None
295
+ if completed_process.returncode != 0:
296
+ combined_output = f"{completed_process.stdout}\n{completed_process.stderr}".lower()
297
+ return NO_PULL_REQUEST_REMINDER if NO_PULL_REQUEST_MARKER in combined_output else None
298
+ try:
299
+ all_pr_fields = json.loads(completed_process.stdout)
300
+ except json.JSONDecodeError:
301
+ return None
302
+ if not isinstance(all_pr_fields, dict):
303
+ return None
304
+ return build_reminder_context(all_pr_fields)
305
+
306
+
307
+ def _emit_context(context_text: str) -> None:
308
+ payload = {
309
+ "hookSpecificOutput": {
310
+ "hookEventName": "PostToolUse",
311
+ "additionalContext": context_text,
312
+ }
313
+ }
314
+ sys.stdout.write(json.dumps(payload))
315
+ sys.stdout.flush()
316
+
317
+
318
+ def main() -> None:
319
+ """Add the PR done checklist to context after a successful push or PR creation.
320
+
321
+ Reads the PostToolUse payload from stdin. Every quiet branch returns with
322
+ no output, so this hook can never alter or block a tool call.
323
+ """
324
+ hook_payload = read_hook_input_dictionary_from_stdin()
325
+ if (
326
+ hook_payload is None
327
+ or hook_payload.get("tool_name") not in ALL_BASH_AND_POWERSHELL_TOOL_NAMES
328
+ ):
329
+ return
330
+ tool_input = hook_payload.get("tool_input")
331
+ command = tool_input.get("command", "") if isinstance(tool_input, dict) else ""
332
+ if not isinstance(command, str) or not command_triggers_reminder(command):
333
+ return
334
+ if _harness_reported_failure(hook_payload.get("tool_response")):
335
+ return
336
+ context_text = _probe_pull_request(str(hook_payload.get("cwd") or ""))
337
+ if context_text is not None:
338
+ _emit_context(context_text)
339
+
340
+
341
+ if __name__ == "__main__":
342
+ main()