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
@@ -0,0 +1,182 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+
5
+ from .config.constants import (
6
+ GITHUB_SHAPE_ERROR_TEMPLATE,
7
+ PULL_BASE_KEY,
8
+ PULL_DRAFT_KEY,
9
+ PULL_HEAD_KEY,
10
+ PULL_LIST_RESOURCE_NAME,
11
+ PULL_MERGE_SHA_KEY,
12
+ PULL_NUMBER_KEY,
13
+ PULL_REF_KEY,
14
+ PULL_RESOURCE_NAME,
15
+ PULL_SHA_KEY,
16
+ )
17
+ from .model import PullRequestCandidate
18
+
19
+
20
+ class GitHubError(RuntimeError):
21
+ """Raised when GitHub metadata has an invalid shape."""
22
+
23
+
24
+ def _require_pull_list(all_pull_entries: object) -> list[object]:
25
+ if isinstance(all_pull_entries, list):
26
+ return all_pull_entries
27
+ raise GitHubError(
28
+ GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=PULL_LIST_RESOURCE_NAME)
29
+ )
30
+
31
+
32
+ def _parse_pull_candidates(
33
+ repository_slug: str,
34
+ all_pull_entries: list[object],
35
+ should_require_merge_commit: bool,
36
+ ) -> tuple[PullRequestCandidate, ...]:
37
+ all_candidates: list[PullRequestCandidate] = []
38
+ for each_pull in all_pull_entries:
39
+ maybe_candidate = parse_recognized_candidate(
40
+ repository_slug,
41
+ each_pull,
42
+ should_require_merge_commit=should_require_merge_commit,
43
+ )
44
+ if maybe_candidate is not None:
45
+ all_candidates.append(maybe_candidate)
46
+ return tuple(all_candidates)
47
+
48
+
49
+ def _read_pull_branches(
50
+ all_pull_fields: Mapping[str, object],
51
+ ) -> tuple[Mapping[str, object], Mapping[str, object]]:
52
+ base = require_mapping(all_pull_fields.get(PULL_BASE_KEY), PULL_RESOURCE_NAME)
53
+ head = require_mapping(all_pull_fields.get(PULL_HEAD_KEY), PULL_RESOURCE_NAME)
54
+ return base, head
55
+
56
+
57
+ def _read_pull_draft(all_pull_fields: Mapping[str, object]) -> bool:
58
+ is_draft = all_pull_fields.get(PULL_DRAFT_KEY)
59
+ if isinstance(is_draft, bool):
60
+ return is_draft
61
+ raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=PULL_RESOURCE_NAME))
62
+
63
+
64
+ def parse_candidate(
65
+ repository_slug: str,
66
+ raw_pull: object,
67
+ *,
68
+ should_require_merge_commit: bool,
69
+ ) -> PullRequestCandidate:
70
+ """Parse pull request metadata.
71
+
72
+ Args:
73
+ repository_slug: Repository slug.
74
+ raw_pull: GitHub pull JSON.
75
+
76
+ Returns:
77
+ Parsed pull request.
78
+
79
+ Raises:
80
+ GitHubError: If metadata is invalid.
81
+ """
82
+ pull = require_mapping(raw_pull, PULL_RESOURCE_NAME)
83
+ base, head = _read_pull_branches(pull)
84
+ pull_number = require_positive_integer(pull, PULL_NUMBER_KEY, PULL_RESOURCE_NAME)
85
+ return PullRequestCandidate(
86
+ repository_slug=repository_slug,
87
+ pull_request_number=pull_number,
88
+ base_ref=require_text(base, PULL_REF_KEY, PULL_RESOURCE_NAME),
89
+ base_sha=require_text(base, PULL_SHA_KEY, PULL_RESOURCE_NAME),
90
+ head_sha=require_text(head, PULL_SHA_KEY, PULL_RESOURCE_NAME),
91
+ merge_sha=_read_merge_sha(pull, should_require_merge_commit),
92
+ is_draft=_read_pull_draft(pull),
93
+ )
94
+
95
+
96
+ def _read_merge_sha(
97
+ all_pull_fields: Mapping[str, object],
98
+ should_require_merge_commit: bool,
99
+ ) -> str:
100
+ merge_sha = all_pull_fields.get(PULL_MERGE_SHA_KEY)
101
+ if merge_sha is None and not should_require_merge_commit:
102
+ return ""
103
+ if not isinstance(merge_sha, str) or not merge_sha:
104
+ raise GitHubError(
105
+ GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=PULL_RESOURCE_NAME)
106
+ )
107
+ return merge_sha
108
+
109
+
110
+ def parse_recognized_candidate(
111
+ repository_slug: str,
112
+ raw_pull: object,
113
+ *,
114
+ should_require_merge_commit: bool = True,
115
+ ) -> PullRequestCandidate | None:
116
+ """Parse a pull request with a merge commit.
117
+
118
+ Args:
119
+ repository_slug: Repository slug.
120
+ raw_pull: GitHub pull JSON.
121
+
122
+ Returns:
123
+ Parsed pull request or None.
124
+ """
125
+ pull = require_mapping(raw_pull, PULL_RESOURCE_NAME)
126
+ if not should_require_merge_commit:
127
+ return parse_candidate(
128
+ repository_slug,
129
+ pull,
130
+ should_require_merge_commit=False,
131
+ )
132
+ if pull.get(PULL_MERGE_SHA_KEY) is None:
133
+ return None
134
+ return parse_candidate(repository_slug, pull, should_require_merge_commit=True)
135
+
136
+
137
+ def require_mapping(raw_payload: object, resource: str) -> Mapping[str, object]:
138
+ if not isinstance(raw_payload, Mapping):
139
+ raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
140
+ return raw_payload
141
+
142
+
143
+ def require_text(
144
+ all_payload_fields: Mapping[str, object], field_name: str, resource: str
145
+ ) -> str:
146
+ """Read required text from a JSON object.
147
+
148
+ Args:
149
+ all_payload_fields: JSON object.
150
+
151
+ Returns:
152
+ Required text.
153
+
154
+ Raises:
155
+ GitHubError: If the field is absent.
156
+ """
157
+ field_text = all_payload_fields.get(field_name)
158
+ if not isinstance(field_text, str) or not field_text:
159
+ raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
160
+ return field_text
161
+
162
+
163
+ def require_positive_integer(
164
+ all_payload_fields: Mapping[str, object], field_name: str, resource: str
165
+ ) -> int:
166
+ """Read a required positive integer.
167
+
168
+ Args:
169
+ all_payload_fields: JSON object.
170
+
171
+ Returns:
172
+ Required integer.
173
+
174
+ Raises:
175
+ GitHubError: If the field is invalid.
176
+ """
177
+ field_integer = all_payload_fields.get(field_name)
178
+ if isinstance(field_integer, bool) or not isinstance(field_integer, int):
179
+ raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
180
+ if field_integer < 1:
181
+ raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
182
+ return field_integer
@@ -0,0 +1,91 @@
1
+ from __future__ import annotations
2
+
3
+ import urllib.error
4
+ import urllib.request
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from http.client import HTTPResponse as OpenedHttpReply
8
+
9
+ import jwt
10
+
11
+ from .config.constants import (
12
+ UTF8_ENCODING,
13
+ )
14
+ from .config.timing import HTTP_TIMEOUT_SECONDS
15
+ from .github_parsing import GitHubError
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class HttpReply:
20
+ status_code: int
21
+ headers: dict[str, str]
22
+ body: bytes
23
+
24
+
25
+ HttpRequester = Callable[[str, str, dict[str, str], bytes | None], HttpReply]
26
+ JwtEncoder = Callable[[dict[str, int | str], str, str], str]
27
+
28
+
29
+ def _read_http_error(error: urllib.error.HTTPError) -> HttpReply:
30
+ return HttpReply(
31
+ status_code=error.code,
32
+ headers=dict(error.headers.items()),
33
+ body=error.read(),
34
+ )
35
+
36
+
37
+ def request_http(
38
+ method: str,
39
+ url: str,
40
+ all_headers: dict[str, str],
41
+ body: bytes | None,
42
+ ) -> HttpReply:
43
+ """Send one GitHub HTTP request.
44
+
45
+ Args:
46
+ method: HTTP method.
47
+ url: Absolute request URL.
48
+ all_headers: Request headers.
49
+ body: Encoded request body, or None.
50
+
51
+ Returns:
52
+ The HTTP reply, including error replies with a status code.
53
+
54
+ Raises:
55
+ GitHubError: If the request fails before a status code exists.
56
+ """
57
+ request = urllib.request.Request(
58
+ url=url, data=body, headers=all_headers, method=method
59
+ )
60
+ try:
61
+ with urllib.request.urlopen(request, timeout=HTTP_TIMEOUT_SECONDS) as reply:
62
+ return _read_http_reply(reply)
63
+ except urllib.error.HTTPError as error:
64
+ return _read_http_error(error)
65
+ except (urllib.error.URLError, OSError) as error:
66
+ raise GitHubError(str(error)) from error
67
+
68
+
69
+ def _read_http_reply(reply: OpenedHttpReply) -> HttpReply:
70
+ return HttpReply(
71
+ status_code=reply.status,
72
+ headers=dict(reply.headers.items()),
73
+ body=reply.read(),
74
+ )
75
+
76
+
77
+ def encode_app_jwt(
78
+ all_claims: dict[str, int | str], private_key: str, algorithm: str
79
+ ) -> str:
80
+ """Encode an app JWT.
81
+
82
+ Args:
83
+ all_claims: JWT claims.
84
+
85
+ Returns:
86
+ Encoded token.
87
+ """
88
+ encoded_token = jwt.encode(all_claims, private_key, algorithm=algorithm)
89
+ if isinstance(encoded_token, bytes):
90
+ return encoded_token.decode(UTF8_ENCODING)
91
+ return encoded_token
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+ from types import TracebackType
7
+ from typing import Self
8
+
9
+ if sys.platform == "win32":
10
+ import msvcrt
11
+ else:
12
+ import fcntl
13
+
14
+ from .config.constants import (
15
+ SUPERVISOR_LOCK_ERROR,
16
+ SUPERVISOR_LOCK_FILE_MODE,
17
+ SUPERVISOR_LOCK_FILENAME,
18
+ SUPERVISOR_LOCK_IO_ERROR,
19
+ )
20
+
21
+ if sys.platform == "win32":
22
+
23
+ def _acquire_file_lock(file_descriptor: int) -> None:
24
+ """Acquire one nonblocking Windows lock."""
25
+ os.lseek(file_descriptor, 0, os.SEEK_SET)
26
+ msvcrt.locking(file_descriptor, msvcrt.LK_NBLCK, 1)
27
+
28
+ def _release_file_lock(file_descriptor: int) -> None:
29
+ """Release one Windows lock."""
30
+ os.lseek(file_descriptor, 0, os.SEEK_SET)
31
+ msvcrt.locking(file_descriptor, msvcrt.LK_UNLCK, 1)
32
+
33
+ else:
34
+
35
+ def _acquire_file_lock(file_descriptor: int) -> None:
36
+ """Acquire one nonblocking POSIX lock."""
37
+ fcntl.flock(file_descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
38
+
39
+ def _release_file_lock(file_descriptor: int) -> None:
40
+ """Release one POSIX lock."""
41
+ fcntl.flock(file_descriptor, fcntl.LOCK_UN)
42
+
43
+
44
+ class SupervisorLockError(RuntimeError):
45
+ """Raised when the supervisor lock cannot be acquired or released."""
46
+
47
+
48
+ class SupervisorLock:
49
+ def __init__(self, cache_root: Path) -> None:
50
+ self.cache_root = cache_root.resolve()
51
+ self.lock_path = self.cache_root / SUPERVISOR_LOCK_FILENAME
52
+ self.file_descriptor: int | None = None
53
+
54
+ def __enter__(self) -> Self:
55
+ self.acquire()
56
+ return self
57
+
58
+ def __exit__(
59
+ self,
60
+ exception_type: type[BaseException] | None,
61
+ exception: BaseException | None,
62
+ traceback: TracebackType | None,
63
+ ) -> None:
64
+ self.release()
65
+
66
+ def acquire(self) -> None:
67
+ """Acquire the operating-system lock for this cache root.
68
+
69
+ Raises:
70
+ SupervisorLockError: If another owner holds the lock or I/O fails.
71
+ """
72
+ try:
73
+ self.cache_root.mkdir(parents=True, exist_ok=True)
74
+ file_descriptor = os.open(
75
+ self.lock_path,
76
+ os.O_CREAT | os.O_RDWR,
77
+ SUPERVISOR_LOCK_FILE_MODE,
78
+ )
79
+ except OSError as error:
80
+ raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
81
+ try:
82
+ _acquire_file_lock(file_descriptor)
83
+ except (BlockingIOError, PermissionError) as error:
84
+ _close_file_descriptor_without_raising(file_descriptor)
85
+ raise SupervisorLockError(SUPERVISOR_LOCK_ERROR) from error
86
+ except OSError as error:
87
+ _close_file_descriptor_without_raising(file_descriptor)
88
+ raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
89
+ self.file_descriptor = file_descriptor
90
+
91
+ def release(self) -> None:
92
+ """Release the operating-system lock and close its file descriptor.
93
+
94
+ Raises:
95
+ SupervisorLockError: If unlocking or closing the file fails.
96
+ """
97
+ file_descriptor = self.file_descriptor
98
+ if file_descriptor is None:
99
+ return
100
+ self.file_descriptor = None
101
+ try:
102
+ _release_file_lock(file_descriptor)
103
+ except OSError as error:
104
+ raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
105
+ finally:
106
+ _close_file_descriptor(file_descriptor)
107
+
108
+
109
+ def _close_file_descriptor(file_descriptor: int) -> None:
110
+ """Close one lock file descriptor."""
111
+ try:
112
+ os.close(file_descriptor)
113
+ except OSError as error:
114
+ raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
115
+
116
+
117
+ def _close_file_descriptor_without_raising(file_descriptor: int) -> None:
118
+ """Close one lock file descriptor while a lock failure is already being raised."""
119
+ try:
120
+ os.close(file_descriptor)
121
+ except OSError:
122
+ return
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+
6
+ from .config.constants import (
7
+ STATUS_ERROR,
8
+ STATUS_FAILURE,
9
+ STATUS_PENDING,
10
+ STATUS_SUCCESS,
11
+ )
12
+
13
+
14
+ class StatusState(str, Enum):
15
+ PENDING = STATUS_PENDING
16
+ ERROR = STATUS_ERROR
17
+ FAILURE = STATUS_FAILURE
18
+ SUCCESS = STATUS_SUCCESS
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class RepositorySettings:
23
+ slug: str
24
+ clone_url: str
25
+
26
+ @property
27
+ def name(self) -> str:
28
+ return self.slug.split("/", maxsplit=1)[1]
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class PullRequestCandidate:
33
+ repository_slug: str
34
+ pull_request_number: int
35
+ base_ref: str
36
+ base_sha: str
37
+ head_sha: str
38
+ merge_sha: str
39
+ is_draft: bool
40
+
41
+ @property
42
+ def key(self) -> tuple[str, int]:
43
+ return self.repository_slug, self.pull_request_number
@@ -0,0 +1 @@
1
+ PyJWT[crypto]>=2.10,<3
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Keep a pstack skills-directory plugin manifest listing every skill folder.
3
+ *
4
+ * Claude Code loads a folder under `~/.claude/skills` as a plugin when the
5
+ * folder holds `.claude-plugin/plugin.json`, and it namespaces that plugin's
6
+ * skills as `pstack:how`. It does not scan a plugin root for skill folders, so
7
+ * the manifest names each sub-skill path:
8
+ *
9
+ * plugin root holds: how/SKILL.md, why/SKILL.md, docs/, .claude-plugin/
10
+ * manifest before: "skills": ["./how"]
11
+ * manifest after: "skills": ["./how", "./why"] <- why/ picked up
12
+ * second run: no write <- the list matches
13
+ * docs/, .claude-plugin/ <- skipped, no SKILL.md
14
+ *
15
+ * The installer calls `refreshPstackPluginManifest` for a pstack folder the
16
+ * user installed. Running this file directly refreshes the same manifest after
17
+ * a pstack update.
18
+ */
19
+
20
+ import {
21
+ existsSync,
22
+ mkdirSync,
23
+ readdirSync,
24
+ readFileSync,
25
+ realpathSync,
26
+ writeFileSync,
27
+ } from 'node:fs';
28
+ import { homedir } from 'node:os';
29
+ import { join } from 'node:path';
30
+ import { fileURLToPath } from 'node:url';
31
+
32
+ export const PSTACK_PLUGIN_DIRECTORY_NAME = 'pstack';
33
+ export const PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH = join('.claude-plugin', 'plugin.json');
34
+ export const SKILL_MANIFEST_FILE_NAME = 'SKILL.md';
35
+ export const SKILL_PATH_PREFIX = './';
36
+
37
+ const CLAUDE_HOME_DIRECTORY_NAME = '.claude';
38
+ const MANIFEST_SKILLS_KEY = 'skills';
39
+ const MANIFEST_INDENT_SPACES = 2;
40
+ const FILE_ENCODING = 'utf8';
41
+ const HIDDEN_NAME_PREFIX = '.';
42
+
43
+ /**
44
+ * Fields a first write seeds from the published pstack plugin. An existing
45
+ * manifest keeps its own values. This refresher changes only the skills list.
46
+ */
47
+ const SEEDED_MANIFEST_FIELDS = Object.freeze({
48
+ $schema: 'https://anthropic.com/claude-code/plugin.schema.json',
49
+ name: PSTACK_PLUGIN_DIRECTORY_NAME,
50
+ version: '0.14.8',
51
+ description:
52
+ 'if you want to go fast, go deep first. pstack helps you write less, but higher '
53
+ + 'quality code. rigorous agent workflows you can parallelize with confidence.',
54
+ author: Object.freeze({ name: 'Lauren Tan' }),
55
+ homepage: 'https://github.com/cursor/plugins/tree/main/pstack',
56
+ repository: 'https://github.com/cursor/plugins',
57
+ license: 'MIT',
58
+ });
59
+
60
+ /**
61
+ * Name every sub-folder of a plugin root that Claude Code loads as a skill.
62
+ *
63
+ * @param {string} pluginRoot Plugin folder holding one sub-folder per skill.
64
+ * @returns {string[]} The sorted names of each visible sub-folder holding a
65
+ * skill manifest. A hidden folder and a folder without `SKILL.md` are left out.
66
+ */
67
+ export function findPstackSkillDirectoryNames(pluginRoot) {
68
+ return readdirSync(pluginRoot, { withFileTypes: true })
69
+ .filter((eachEntry) => eachEntry.isDirectory())
70
+ .map((eachEntry) => eachEntry.name)
71
+ .filter((eachName) => !eachName.startsWith(HIDDEN_NAME_PREFIX))
72
+ .filter((eachName) => existsSync(join(pluginRoot, eachName, SKILL_MANIFEST_FILE_NAME)))
73
+ .sort();
74
+ }
75
+
76
+ function readManifestSkillPaths(manifestPath) {
77
+ if (!existsSync(manifestPath)) return { manifest: null, previousSkillPaths: [] };
78
+ const manifest = JSON.parse(readFileSync(manifestPath, FILE_ENCODING));
79
+ const listedSkillPaths = manifest[MANIFEST_SKILLS_KEY];
80
+ if (typeof listedSkillPaths === 'string') {
81
+ return { manifest, previousSkillPaths: [listedSkillPaths] };
82
+ }
83
+ if (Array.isArray(listedSkillPaths)) {
84
+ return { manifest, previousSkillPaths: listedSkillPaths };
85
+ }
86
+ return { manifest, previousSkillPaths: [] };
87
+ }
88
+
89
+ /**
90
+ * Rewrite a pstack plugin manifest's skills list to match its folder.
91
+ *
92
+ * Writes only when the list changes. A second call on an unchanged folder
93
+ * leaves the file byte for byte. An existing manifest keeps every field but
94
+ * the skills list.
95
+ *
96
+ * @param {string} pluginRoot Plugin folder holding the skill sub-folders.
97
+ * @returns {{didWrite: boolean, reason: string, previousSkillPaths: string[],
98
+ * currentSkillPaths: string[]}} Whether this call wrote, and the paths
99
+ * before and after. `reason` names a skip, and is empty on a write or an
100
+ * unchanged list.
101
+ */
102
+ export function refreshPstackPluginManifest(pluginRoot) {
103
+ const declined = { didWrite: false, previousSkillPaths: [], currentSkillPaths: [] };
104
+ if (!existsSync(pluginRoot)) {
105
+ return { ...declined, reason: 'plugin root is absent' };
106
+ }
107
+ if (existsSync(join(pluginRoot, SKILL_MANIFEST_FILE_NAME))) {
108
+ return { ...declined, reason: 'plugin root is itself a skill' };
109
+ }
110
+ const allSkillNames = findPstackSkillDirectoryNames(pluginRoot);
111
+ if (allSkillNames.length === 0) {
112
+ return { ...declined, reason: 'plugin root holds no skills' };
113
+ }
114
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
115
+ const { manifest, previousSkillPaths } = readManifestSkillPaths(manifestPath);
116
+ const currentSkillPaths = allSkillNames.map((eachName) => SKILL_PATH_PREFIX + eachName);
117
+ const outcome = { reason: '', previousSkillPaths, currentSkillPaths };
118
+ const isUnchanged = manifest !== null
119
+ && previousSkillPaths.length === currentSkillPaths.length
120
+ && previousSkillPaths.every((eachPath, eachIndex) => eachPath === currentSkillPaths[eachIndex]);
121
+ if (isUnchanged) {
122
+ return { ...outcome, didWrite: false };
123
+ }
124
+ const refreshedManifest = {
125
+ ...(manifest === null ? SEEDED_MANIFEST_FIELDS : manifest),
126
+ [MANIFEST_SKILLS_KEY]: currentSkillPaths,
127
+ };
128
+ mkdirSync(join(pluginRoot, '.claude-plugin'), { recursive: true });
129
+ writeFileSync(
130
+ manifestPath,
131
+ JSON.stringify(refreshedManifest, null, MANIFEST_INDENT_SPACES) + '\n',
132
+ FILE_ENCODING,
133
+ );
134
+ return { ...outcome, didWrite: true };
135
+ }
136
+
137
+ /**
138
+ * Return the pstack plugin folder under a Claude home.
139
+ *
140
+ * @param {string} claudeHome The Claude home directory holding `skills`.
141
+ * @returns {string} The pstack plugin folder path, present or not.
142
+ */
143
+ export function pstackPluginRootFor(claudeHome) {
144
+ return join(claudeHome, 'skills', PSTACK_PLUGIN_DIRECTORY_NAME);
145
+ }
146
+
147
+ /**
148
+ * Return the Claude home used when no folder argument is given.
149
+ *
150
+ * A named profile sets `CLAUDE_CONFIG_DIR`. The installer writes into that
151
+ * root. The command-line form reads the same variable.
152
+ *
153
+ * @param {Record<string, string|undefined>} environment The process environment.
154
+ * @returns {string} The Claude home directory holding `skills`.
155
+ */
156
+ export function claudeHomeFrom(environment) {
157
+ return environment.CLAUDE_CONFIG_DIR || join(homedir(), CLAUDE_HOME_DIRECTORY_NAME);
158
+ }
159
+
160
+ function main() {
161
+ const explicitRoot = process.argv[2];
162
+ const pluginRoot = explicitRoot || pstackPluginRootFor(claudeHomeFrom(process.env));
163
+ const outcome = refreshPstackPluginManifest(pluginRoot);
164
+ if (!outcome.didWrite && outcome.reason) {
165
+ console.log(`No manifest written: ${outcome.reason}.`);
166
+ return;
167
+ }
168
+ if (!outcome.didWrite) {
169
+ console.log(`No change. ${outcome.currentSkillPaths.length} skills listed.`);
170
+ return;
171
+ }
172
+ console.log(`Updated. ${outcome.currentSkillPaths.length} skills listed.`);
173
+ for (const eachPath of outcome.currentSkillPaths) {
174
+ if (!outcome.previousSkillPaths.includes(eachPath)) console.log(` added ${eachPath}`);
175
+ }
176
+ for (const eachPath of outcome.previousSkillPaths) {
177
+ if (!outcome.currentSkillPaths.includes(eachPath)) console.log(` removed ${eachPath}`);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Return whether node ran this module as its entry point.
183
+ *
184
+ * A directory link to this script gives node an invoked path that differs from
185
+ * this module's own path. Both sides resolve to their real location before
186
+ * the comparison.
187
+ *
188
+ * @param {string|undefined} invokedPath The path node reports as `argv[1]`.
189
+ * @returns {boolean} True when node ran this module as its entry point.
190
+ */
191
+ function isEntryPoint(invokedPath) {
192
+ if (!invokedPath) return false;
193
+ try {
194
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(invokedPath);
195
+ } catch {
196
+ return fileURLToPath(import.meta.url) === invokedPath;
197
+ }
198
+ }
199
+
200
+ if (isEntryPoint(process.argv[1])) main();