claude-dev-env 4.2.0 → 7.0.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.
- package/.agents/agents/pr-description-writer.md +2 -2
- package/.agents/agents/test_agent_frontmatter.py +2 -2
- package/.agents/agents/test_pr_description_writer_style.py +1 -3
- package/.agents/skills/e-code-review/SKILL.md +12 -12
- package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
- package/.agents/skills/e-code-review/reference/fix.md +15 -17
- package/.agents/skills/e-code-review/reference/loop.md +35 -39
- package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
- package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
- package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
- package/.agents/skills/issue-tracker/SKILL.md +7 -0
- package/.agents/skills/pull-request/SKILL.md +216 -0
- package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +69 -0
- package/.agents/skills/pull-request/scripts/pull_request.py +265 -0
- package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
- package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +58 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +396 -0
- package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
- package/.agents/skills/skill-builder/SKILL.md +3 -196
- package/.agents/skills/test-runner/SKILL.md +69 -0
- package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
- package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
- package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
- package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
- package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
- package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
- package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
- package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
- package/bin/ever-shipped-skills.mjs +2 -0
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/install.agents-home.test.mjs +15 -13
- package/bin/install.mjs +37 -1
- package/bin/install.policy-lint.test.mjs +78 -0
- package/bin/install.prune.test.mjs +149 -20
- package/bin/install.pstack-plugin.test.mjs +114 -0
- package/bin/install.test.mjs +61 -7
- package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
- package/hooks/advisory/pr_done_reminder.py +342 -0
- package/hooks/advisory/test_pr_done_reminder.py +249 -0
- package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
- package/hooks/blocking/code_rules_paired_test.py +22 -15
- package/hooks/blocking/pii_prevention_blocker.py +32 -51
- package/hooks/blocking/test_bash_post_call_dispatcher.py +55 -17
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
- package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
- package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
- package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
- package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
- package/hooks/blocking/test_pii_prevention_blocker.py +37 -89
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
- package/hooks/gh_post_body_texts.py +195 -0
- package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
- package/hooks/git-hooks/pre_commit.py +52 -11
- package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
- package/hooks/git-hooks/test_native_policy_commit.py +87 -0
- package/hooks/git-hooks/test_pre_commit.py +246 -0
- package/hooks/hooks.json +1 -16
- package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -2
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
- package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
- package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
- package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +17 -7
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
- package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
- package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
- package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
- package/hooks/observability/test_failure_recorder.py +1 -1
- package/hooks/pyproject.toml +1 -1
- package/hooks/test_gh_post_body_texts.py +79 -0
- package/package.json +2 -2
- package/rules/durable-post-artifacts.md +32 -20
- package/rules/gh-cli-conventions.md +23 -13
- package/rules/git-workflow.md +21 -8
- package/scripts/check.ps1 +0 -1
- package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
- package/scripts/ci/installer-lifecycle.test.mjs +26 -0
- package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
- package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
- package/scripts/durable_post_lint.py +206 -0
- package/scripts/durable_post_lint_config/config/constants.py +96 -0
- package/scripts/durable_post_lint_config/config/test_constants.py +25 -0
- package/scripts/policy_lint/adapter_configuration.py +53 -12
- package/scripts/policy_lint/adapter_detectors.py +13 -6
- package/scripts/policy_lint/adapters.py +7 -4
- package/scripts/policy_lint/config/constants.py +5 -0
- package/scripts/policy_lint/engine.py +48 -4
- package/scripts/policy_lint/model.py +1 -0
- package/scripts/policy_lint/registry.py +1 -1
- package/scripts/policy_lint/selection.py +2 -1
- package/scripts/policy_lint/selection_git.py +2 -0
- package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
- package/scripts/repository_checks/config/constants.py +0 -5
- package/scripts/shared_tree_paths.py +32 -3
- package/scripts/test_durable_post_lint.py +332 -0
- package/scripts/test_shared_tree_paths.py +33 -0
- package/scripts/tests/test_adapter_configuration.py +54 -0
- package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
- package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
- package/scripts/tests/test_policy_lint_selection.py +25 -2
- package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
- package/scripts/tests/test_rules.py +3 -7
- package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
- package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
- package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
- package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
- package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
- package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
- package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
- package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
- package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
- package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
- package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
- package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
- package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
- package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
- package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
- package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
- package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
- package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
- package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
- package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
- package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
- package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
- package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
- package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
- package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
- package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
- package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
- package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
- package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
- package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
- package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
- package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
- package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
- package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
- package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
- package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
- package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
- package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
- package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/SKILL.md +0 -431
- package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
- package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
- package/.agents/skills/autoconverge/reference/convergence.md +0 -219
- package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
- package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
- package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
- package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
- package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
- package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
- package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
- package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
- package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
- package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
- package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
- package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
- package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
- package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
- package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
- package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
- package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
- package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
- package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
- package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
- package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
- package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
- package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
- package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
- package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
- package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
- package/.agents/skills/hitl/SKILL.md +0 -45
- package/.agents/skills/pr-cleanup/SKILL.md +0 -140
- package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
- package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
- package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
- package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
- package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
- package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
- package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
- package/.agents/skills/pr-refinement/SKILL.md +0 -31
- package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
- package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
- package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
- package/.agents/skills/pr-small-cl/SKILL.md +0 -52
- package/.agents/skills/pr-title-description/SKILL.md +0 -96
- package/.agents/skills/prototype/SKILL.md +0 -85
- package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
- package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
- package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
- package/.agents/skills/prototype/scripts/conftest.py +0 -15
- package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
- package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
- package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
- package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
- package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
- package/.agents/skills/prototype/workflows/promotion.md +0 -25
- package/.agents/skills/prototype/workflows/sandbox.md +0 -35
- package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
- package/.agents/skills/rebase/AGENTS.md +0 -1
- package/.agents/skills/rebase/SKILL.md +0 -178
- package/.agents/skills/review-router/SKILL.md +0 -51
- package/.agents/skills/review-router/reference/route-policy.json +0 -7
- package/.agents/skills/review-router/reference/task-seeds.md +0 -1
- package/.agents/skills/review-router/scripts/conftest.py +0 -4
- package/.agents/skills/review-router/scripts/review_router.py +0 -50
- package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
- package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
- package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
- package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
- package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
- package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
- package/.agents/skills/review-tier/SKILL.md +0 -56
- package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
- package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
- package/.agents/skills/review-tier/scripts/conftest.py +0 -4
- package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
- package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
- package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
- package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
- package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
- package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-log/AGENTS.md +0 -1
- package/.agents/skills/session-log/SKILL.md +0 -170
- package/.agents/skills/session-log/templates/frontmatter.md +0 -40
- package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-tidy/AGENTS.md +0 -1
- package/.agents/skills/session-tidy/SKILL.md +0 -191
- package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
- package/.agents/skills/skill-builder/references/description-field.md +0 -111
- package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
- package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
- package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
- package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
- package/.agents/skills/skill-builder/references/skill-types.md +0 -232
- package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
- package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
- package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
- package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
- package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
- package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
- package/.agents/skills/update/.claude/CLAUDE.md +0 -1
- package/.agents/skills/update/AGENTS.md +0 -1
- package/.agents/skills/update/SKILL.md +0 -190
- package/_shared/pr-loop/AGENTS.md +0 -1
- package/_shared/pr-loop/CLAUDE.md +0 -1
- package/_shared/pr-loop/audit-contract.md +0 -183
- package/_shared/pr-loop/audit-reply-template.md +0 -151
- package/_shared/pr-loop/code-rules-gate.md +0 -65
- package/_shared/pr-loop/fix-protocol.md +0 -60
- package/_shared/pr-loop/gh-payloads.md +0 -75
- package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
- package/_shared/pr-loop/precatch-rubric.md +0 -66
- package/_shared/pr-loop/preflight-proposal.md +0 -56
- package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
- package/_shared/pr-loop/state-schema.md +0 -45
- package/_shared/pr-loop/worker-spawn.md +0 -189
- package/hooks/_gh_pr_author_swap_utils.py +0 -1225
- package/hooks/blocking/conventional_pr_title_gate.py +0 -443
- package/hooks/blocking/gh_body_arg_blocker.py +0 -176
- package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
- package/hooks/blocking/gh_pr_author_restore.py +0 -105
- package/hooks/blocking/pr_description_writer_gate.py +0 -256
- package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
- package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
- package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
- package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
- package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
- package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
- package/hooks/blocking/test_preflight_check.py +0 -343
- package/hooks/blocking/test_test_preflight_check.py +0 -80
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
- package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
- package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
- package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
- package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
- package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
- package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
- package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
- package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
- package/hooks/test__gh_pr_author_swap_utils.py +0 -333
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pull-request
|
|
3
|
+
description: >-
|
|
4
|
+
Validate and publish GitHub pull request actions. Triggers: create PR, open
|
|
5
|
+
pull request, edit PR, update PR body, comment on PR, review PR, publish a
|
|
6
|
+
draft PR, scoped GitHub author.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Pull request
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
- [Principle](#principle)
|
|
14
|
+
- [When this applies](#when-this-applies)
|
|
15
|
+
- [Constraints](#constraints)
|
|
16
|
+
- [Dependencies](#dependencies)
|
|
17
|
+
- [Sub-skills](#sub-skills)
|
|
18
|
+
- [Task seeding](#task-seeding)
|
|
19
|
+
- [Process](#process)
|
|
20
|
+
- [Exit handling](#exit-handling)
|
|
21
|
+
- [Examples](#examples)
|
|
22
|
+
- [Gotchas](#gotchas)
|
|
23
|
+
- [File index](#file-index)
|
|
24
|
+
- [Folder map](#folder-map)
|
|
25
|
+
|
|
26
|
+
## Principle
|
|
27
|
+
|
|
28
|
+
Publish one GitHub pull request action from validated local files and a
|
|
29
|
+
process-local author value. Keep the parent environment unchanged.
|
|
30
|
+
|
|
31
|
+
## When this applies
|
|
32
|
+
|
|
33
|
+
Use this skill for one pull request action:
|
|
34
|
+
|
|
35
|
+
- Create a draft pull request.
|
|
36
|
+
- Edit a pull request title or body.
|
|
37
|
+
- Add a pull request comment.
|
|
38
|
+
- Submit a pull request review.
|
|
39
|
+
- Recover one selected legacy author record before the action.
|
|
40
|
+
|
|
41
|
+
Send issue create, edit, and comment work to `issue-tracker`. Send review and
|
|
42
|
+
fix-loop work to `e-code-review`. Send commits to `source-command-commit`.
|
|
43
|
+
|
|
44
|
+
Require one repository and one action target. Stop before author lookup or
|
|
45
|
+
network work when the repository, pull request, action, author, or required
|
|
46
|
+
local file is missing or ambiguous.
|
|
47
|
+
|
|
48
|
+
Use these exact refusal responses when the request belongs to another skill:
|
|
49
|
+
|
|
50
|
+
- Issue action: `Use issue-tracker for this GitHub issue action.`
|
|
51
|
+
- Commit or push: `Use source-command-commit for this commit or push.`
|
|
52
|
+
- Missing target: `Provide one repository and one pull request action target.`
|
|
53
|
+
|
|
54
|
+
## Constraints
|
|
55
|
+
|
|
56
|
+
- Run local validation and privacy checks before account lookup or network work.
|
|
57
|
+
- Use a body file. Do not send inline body text through the shell.
|
|
58
|
+
- Keep `GITHUB_DEFAULT_ACCOUNT`, the parent environment, and global GitHub CLI
|
|
59
|
+
state unchanged.
|
|
60
|
+
- Run one create, edit, comment, review, or recovery action per command.
|
|
61
|
+
- Use global account switching only for one explicitly selected legacy record.
|
|
62
|
+
- Read back the remote state before reporting success.
|
|
63
|
+
|
|
64
|
+
## Dependencies
|
|
65
|
+
|
|
66
|
+
The commands use Python 3.11 or later and the Python standard library. GitHub
|
|
67
|
+
actions also require an authenticated `gh` executable. The linter and artifact
|
|
68
|
+
uploader ship in this package. Set `GITHUB_DEFAULT_ACCOUNT` to one exact GitHub
|
|
69
|
+
CLI login when the action needs a selected author. Leave it unset to use the
|
|
70
|
+
current process environment.
|
|
71
|
+
|
|
72
|
+
## Sub-skills
|
|
73
|
+
|
|
74
|
+
| Skill | When | Produces | If missing |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `pr-description-writer` | Before create or a full body rewrite | A reviewed title and body file | Stop the create or rewrite and report that authoring is required |
|
|
77
|
+
| `privacy-hygiene` | Before any durable GitHub post | A clean body and repository privacy sweep | Stop before publication and report the missing gate |
|
|
78
|
+
| `issue-tracker` | Issue create, edit, or comment requests | Issue state and issue URLs | Route the request there. |
|
|
79
|
+
| `e-code-review` | Review or review/fix loop requests | Structured findings or a clean review result | Route the request there. |
|
|
80
|
+
| `source-command-commit` | Commit or push requests | A verified commit or pushed branch | Route the request there. |
|
|
81
|
+
|
|
82
|
+
## Task seeding
|
|
83
|
+
|
|
84
|
+
At the start of the work, register every item in
|
|
85
|
+
`reference/publication-tasks.md` through `TaskCreate`, `TodoWrite`, or the host
|
|
86
|
+
task equivalent when one is available. If no task tool is available, use the
|
|
87
|
+
catalog and the running work ledger. Mark each task with `PASS`, `FAIL` plus
|
|
88
|
+
file and line evidence, or `N/A` plus a reason for a conditional task.
|
|
89
|
+
|
|
90
|
+
## Process
|
|
91
|
+
|
|
92
|
+
### 1. Scope the action
|
|
93
|
+
|
|
94
|
+
Resolve the repository, action, and selected GitHub author. For create, resolve
|
|
95
|
+
the source branch, base, and head. For other actions, resolve one existing pull
|
|
96
|
+
request target. Create actions publish drafts. Record the target before any write.
|
|
97
|
+
|
|
98
|
+
### 2. Author the title and body
|
|
99
|
+
|
|
100
|
+
For create and full body rewrite, invoke the installed
|
|
101
|
+
`pr-description-writer`. Require its title and body file as the authoring
|
|
102
|
+
output. A comment or review may use a supplied body file when it does not
|
|
103
|
+
rewrite the pull request description.
|
|
104
|
+
|
|
105
|
+
### 3. Run the local linter
|
|
106
|
+
|
|
107
|
+
Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
|
|
108
|
+
otherwise), then run `<managed-root>/scripts/durable_post_lint.py` with the
|
|
109
|
+
matching action and title or body file. Use `pr-create`, `pr-edit`, `pr-comment`,
|
|
110
|
+
or `pr-review`. The linter owns the action-specific body, title, and
|
|
111
|
+
volatile-path rules.
|
|
112
|
+
|
|
113
|
+
Exit code `0` continues to the next gate. Any non-zero exit stops the action. Fix
|
|
114
|
+
the named local input and rerun the linter. Do not look up the author or make a
|
|
115
|
+
network call while validation fails.
|
|
116
|
+
|
|
117
|
+
### 4. Run privacy and artifact checks
|
|
118
|
+
|
|
119
|
+
Invoke `privacy-hygiene` before the GitHub post. Upload binary evidence with
|
|
120
|
+
`scripts/gh_artifact_upload.py` and replace local artifact references with
|
|
121
|
+
permanent URLs. Rerun the linter after changing the body.
|
|
122
|
+
|
|
123
|
+
### 5. Resolve the process-local author
|
|
124
|
+
|
|
125
|
+
Run `.agents/skills/pull-request/scripts/pull_request.py` with one structured
|
|
126
|
+
action and the selected author. The command runs the linter again, resolves
|
|
127
|
+
the author value, and passes it only to the child `gh` process. It never
|
|
128
|
+
changes the parent environment or calls `gh auth switch`.
|
|
129
|
+
|
|
130
|
+
If the selected author has no usable value, stop and report the account lookup
|
|
131
|
+
failure. Never print the value.
|
|
132
|
+
|
|
133
|
+
### 6. Recover a selected legacy record
|
|
134
|
+
|
|
135
|
+
Run `.agents/skills/pull-request/scripts/recover_legacy_author.py
|
|
136
|
+
<exact-state-file> --confirm-inactive` only when the user selects one legacy
|
|
137
|
+
state file. The command checks the record's age, secure file metadata, and
|
|
138
|
+
contents. The confirmation flag records that the caller verified inactivity.
|
|
139
|
+
Leave every other record untouched. Delete the selected record only after a
|
|
140
|
+
successful restore. Mark this step `N/A` when no legacy state file is selected.
|
|
141
|
+
|
|
142
|
+
### 7. Run the action
|
|
143
|
+
|
|
144
|
+
Run `pull_request.py` for exactly one of `create`, `edit`, `comment`, or
|
|
145
|
+
`review`. Supply the validated body file path. Treat a non-zero exit as a
|
|
146
|
+
failed publication and preserve the local inputs for retry.
|
|
147
|
+
|
|
148
|
+
### 8. Read back the remote state
|
|
149
|
+
|
|
150
|
+
Use the same selected process-local author for a read-only remote readback.
|
|
151
|
+
Confirm the pull request URL and number, title, body, head SHA, and draft state.
|
|
152
|
+
For comments and reviews, confirm the new remote entry. Report artifact URLs
|
|
153
|
+
without exposing author values.
|
|
154
|
+
|
|
155
|
+
## Exit handling
|
|
156
|
+
|
|
157
|
+
Stop before network work when authoring, local validation, privacy, or author
|
|
158
|
+
selection fails. A failed action preserves its body and any recovery record.
|
|
159
|
+
Report the generic error and the next required input. A successful action ends
|
|
160
|
+
only after remote readback proves the requested state.
|
|
161
|
+
|
|
162
|
+
## Examples
|
|
163
|
+
|
|
164
|
+
Create example: the writer produces `pr-body.md`. The linter exits `0` for
|
|
165
|
+
`pr-create`. `pull_request.py create` publishes one draft pull request. The
|
|
166
|
+
readback matches the title, body, head SHA, and draft state.
|
|
167
|
+
|
|
168
|
+
Rejected comment example: the comment body names a worktree file. The linter
|
|
169
|
+
returns a non-zero exit. No account lookup or GitHub request runs. Replace the
|
|
170
|
+
path with inline text or a permanent artifact URL, then rerun.
|
|
171
|
+
|
|
172
|
+
Recovery example: one old record remains after an interrupted legacy account
|
|
173
|
+
swap. The user selects that exact file and confirms its session is inactive.
|
|
174
|
+
The recovery command restores the named account and deletes only that record.
|
|
175
|
+
|
|
176
|
+
## Gotchas
|
|
177
|
+
|
|
178
|
+
- Relative body-file paths depend on the caller directory. Use an absolute path
|
|
179
|
+
when another process or worktree starts the command.
|
|
180
|
+
- A record older than 30 minutes can still belong to a live session. Require
|
|
181
|
+
`--confirm-inactive` before recovery.
|
|
182
|
+
- Normal pull request actions never call `gh auth switch`. If that command
|
|
183
|
+
appears outside explicit recovery, stop.
|
|
184
|
+
- GitHub CLI output can contain account data. The command captures account
|
|
185
|
+
lookup and recovery output and prints only generic errors.
|
|
186
|
+
|
|
187
|
+
## File index
|
|
188
|
+
|
|
189
|
+
| Path | Purpose |
|
|
190
|
+
|---|---|
|
|
191
|
+
| `SKILL.md` | Hub for pull request publication, validation, author selection, recovery, and readback |
|
|
192
|
+
| `reference/publication-tasks.md` | Ordered tasks for every publication gate |
|
|
193
|
+
| `scripts/pull_request.py` | Command for author selection and create, edit, comment, and review actions |
|
|
194
|
+
| `scripts/github_pr_command_constants/` | Constants for pull request actions, recovery, exit codes, and generic messages |
|
|
195
|
+
| `scripts/test_pull_request.py` | Tests for `pull_request.py` |
|
|
196
|
+
| `scripts/recover_legacy_author.py` | Command for one selected legacy author record |
|
|
197
|
+
| `scripts/test_github_pr_command_constants.py` | Tests for the command and recovery constants |
|
|
198
|
+
| `scripts/test_recover_legacy_author.py` | Tests for `recover_legacy_author.py` |
|
|
199
|
+
| `<managed-root>/scripts/durable_post_lint.py` | Shared action-aware title, body, and path validator |
|
|
200
|
+
| `scripts/gh_artifact_upload.py` | Helper for permanent GitHub binary evidence URLs |
|
|
201
|
+
| `scripts/tests/test_gh_artifact_upload.py` | Tests for binary evidence upload |
|
|
202
|
+
|
|
203
|
+
## Folder map
|
|
204
|
+
|
|
205
|
+
```text
|
|
206
|
+
pull-request/
|
|
207
|
+
├── SKILL.md
|
|
208
|
+
├── reference/
|
|
209
|
+
│ └── publication-tasks.md
|
|
210
|
+
└── scripts/
|
|
211
|
+
├── pull_request.py
|
|
212
|
+
├── github_pr_command_constants/
|
|
213
|
+
├── recover_legacy_author.py
|
|
214
|
+
├── test_pull_request.py
|
|
215
|
+
└── test_recover_legacy_author.py
|
|
216
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Pull request publication tasks
|
|
2
|
+
|
|
3
|
+
Register each line as one session task before starting the process. Complete
|
|
4
|
+
each task with `PASS`, `FAIL` plus file and line evidence, or `N/A` plus a
|
|
5
|
+
reason when the task is conditional. This file is a task seed catalog, not a
|
|
6
|
+
markdown progress board.
|
|
7
|
+
|
|
8
|
+
1. Resolve one repository, action, and selected author. For create, resolve the source branch, base, and head. For other actions, resolve one existing pull request target. Create actions publish drafts.
|
|
9
|
+
2. Invoke the required `pr-description-writer` for create or full body rewrite, then record its title and body file. Mark `N/A` for comment or review bodies that do not rewrite the pull request description.
|
|
10
|
+
3. Validate every title, body, artifact, and local path input. Reject inline body input.
|
|
11
|
+
4. Resolve the active managed root and run `<managed-root>/scripts/durable_post_lint.py` for the matching action. Record exit code `0` before credential lookup or network work.
|
|
12
|
+
5. Run `privacy-hygiene` before the GitHub post and record its clean result.
|
|
13
|
+
6. Upload binary evidence through `scripts/gh_artifact_upload.py`, replace local references with permanent URLs, and rerun the body linter. Mark `N/A` when no binary evidence exists.
|
|
14
|
+
7. Resolve the selected author in the child process environment and prove that the parent environment and global `gh` account remain unchanged.
|
|
15
|
+
8. Run `.agents/skills/pull-request/scripts/pull_request.py` for exactly one action: `create`, `edit`, `comment`, or `review`.
|
|
16
|
+
9. When the user selects a legacy state file, run `recover_legacy_author.py <exact-state-file> --confirm-inactive` for exactly that record. Preserve every other record and delete the selected record only after successful restore. Mark `N/A` otherwise.
|
|
17
|
+
10. Read back the remote pull request title, body, head SHA, draft state, and requested comment or review, then record the URL and number without credentials.
|
|
18
|
+
11. Mark every session task complete with evidence and report the action, remote URL, readback result, and any remaining blocker.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Constants for the explicit GitHub pull request command."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Configuration constants for the explicit GitHub pull request command."""
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Configuration constants for process-local GitHub pull request actions."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
ACTION_COMMENT = "comment"
|
|
6
|
+
ACTION_CREATE = "create"
|
|
7
|
+
ACTION_EDIT = "edit"
|
|
8
|
+
ACTION_REVIEW = "review"
|
|
9
|
+
|
|
10
|
+
LINTER_ACTION_COMMENT = "pr-comment"
|
|
11
|
+
LINTER_ACTION_CREATE = "pr-create"
|
|
12
|
+
LINTER_ACTION_EDIT = "pr-edit"
|
|
13
|
+
LINTER_ACTION_REVIEW = "pr-review"
|
|
14
|
+
|
|
15
|
+
REVIEW_EVENT_APPROVE = "approve"
|
|
16
|
+
REVIEW_EVENT_COMMENT = "comment"
|
|
17
|
+
REVIEW_EVENT_REQUEST_CHANGES = "request-changes"
|
|
18
|
+
ALL_REVIEW_FLAGS_BY_EVENT: dict[str, str] = {
|
|
19
|
+
REVIEW_EVENT_APPROVE: "--approve",
|
|
20
|
+
REVIEW_EVENT_COMMENT: "--comment",
|
|
21
|
+
REVIEW_EVENT_REQUEST_CHANGES: "--request-changes",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ALL_LINTER_ACTIONS_BY_COMMAND: dict[str, str] = {
|
|
25
|
+
ACTION_COMMENT: LINTER_ACTION_COMMENT,
|
|
26
|
+
ACTION_CREATE: LINTER_ACTION_CREATE,
|
|
27
|
+
ACTION_EDIT: LINTER_ACTION_EDIT,
|
|
28
|
+
ACTION_REVIEW: LINTER_ACTION_REVIEW,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
SELECTED_ACCOUNT_ENVIRONMENT_KEY = "GITHUB_DEFAULT_ACCOUNT"
|
|
32
|
+
CONFIG_DIR_OVERRIDE_ENVIRONMENT_KEY = "CLAUDE_CONFIG_DIR"
|
|
33
|
+
DEFAULT_MANAGED_ROOT_DIRECTORY_NAME = ".claude"
|
|
34
|
+
ALL_DURABLE_POST_LINTER_RELATIVE_PATH_PARTS = ("scripts", "durable_post_lint.py")
|
|
35
|
+
GITHUB_TOKEN_ENVIRONMENT_KEY = "GITHUB" + "_TOKEN"
|
|
36
|
+
GH_TOKEN_ENVIRONMENT_KEY = "GH" + "_TOKEN"
|
|
37
|
+
ALL_GITHUB_AUTHORIZATION_ENVIRONMENT_KEYS = (
|
|
38
|
+
GITHUB_TOKEN_ENVIRONMENT_KEY,
|
|
39
|
+
GH_TOKEN_ENVIRONMENT_KEY,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
ACCOUNT_LOOKUP_FAILED_MESSAGE = "error: GitHub account lookup failed\n"
|
|
43
|
+
ACCOUNT_LOOKUP_EMPTY_MESSAGE = "error: GitHub account lookup returned no value\n"
|
|
44
|
+
ACTION_FAILED_MESSAGE = "error: GitHub action failed\n"
|
|
45
|
+
ACCOUNT_LOOKUP_FAILURE_EXIT_CODE = 1
|
|
46
|
+
PACKAGE_ROOT_PARENT_INDEX = 4
|
|
47
|
+
|
|
48
|
+
ALL_GH_AUTH_SWITCH_COMMAND_HEAD: tuple[str, ...] = (
|
|
49
|
+
"gh",
|
|
50
|
+
"auth",
|
|
51
|
+
"switch",
|
|
52
|
+
"--user",
|
|
53
|
+
)
|
|
54
|
+
LEGACY_RECORD_ACTIVE_MESSAGE = "error: selected legacy state record is active\n"
|
|
55
|
+
LEGACY_RECORD_CHANGED_MESSAGE = "error: selected legacy state record changed\n"
|
|
56
|
+
LEGACY_RECORD_CONFIRMATION_MESSAGE = "error: confirm inactive legacy state\n"
|
|
57
|
+
LEGACY_RECORD_REJECTED_MESSAGE = "error: selected legacy state record is invalid\n"
|
|
58
|
+
LEGACY_RESTORE_FAILED_MESSAGE = "error: GitHub account restore failed\n"
|
|
59
|
+
LEGACY_RECORD_INACTIVE_CONFIRMATION_FLAG = "--confirm-inactive"
|
|
60
|
+
LEGACY_RECORD_ORIGINAL_ACCOUNT_KEY = "original_account"
|
|
61
|
+
LEGACY_RECORD_PERMISSION_MODE = 0o600
|
|
62
|
+
LEGACY_RECORD_STALE_AGE_SECONDS = 1800
|
|
63
|
+
LEGACY_RECORD_NAME_PATTERN: re.Pattern[str] = re.compile(
|
|
64
|
+
r"^gh_pr_author_swap_[A-Za-z0-9_-]+\.json$"
|
|
65
|
+
)
|
|
66
|
+
RECOVERY_CLEAN_EXIT_CODE = 0
|
|
67
|
+
RECOVERY_FAILED_EXIT_CODE = 1
|
|
68
|
+
RECOVERY_REJECTED_EXIT_CODE = 2
|
|
69
|
+
RECOVERY_UNRESOLVED_EXIT_CODE = 3
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Run validated GitHub pull request actions with process-local account selection."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from github_pr_command_constants.config.constants import (
|
|
13
|
+
ACCOUNT_LOOKUP_EMPTY_MESSAGE,
|
|
14
|
+
ACCOUNT_LOOKUP_FAILED_MESSAGE,
|
|
15
|
+
ACCOUNT_LOOKUP_FAILURE_EXIT_CODE,
|
|
16
|
+
ACTION_COMMENT,
|
|
17
|
+
ACTION_CREATE,
|
|
18
|
+
ACTION_EDIT,
|
|
19
|
+
ACTION_FAILED_MESSAGE,
|
|
20
|
+
ACTION_REVIEW,
|
|
21
|
+
ALL_DURABLE_POST_LINTER_RELATIVE_PATH_PARTS,
|
|
22
|
+
ALL_GITHUB_AUTHORIZATION_ENVIRONMENT_KEYS,
|
|
23
|
+
ALL_LINTER_ACTIONS_BY_COMMAND,
|
|
24
|
+
ALL_REVIEW_FLAGS_BY_EVENT,
|
|
25
|
+
CONFIG_DIR_OVERRIDE_ENVIRONMENT_KEY,
|
|
26
|
+
DEFAULT_MANAGED_ROOT_DIRECTORY_NAME,
|
|
27
|
+
GH_TOKEN_ENVIRONMENT_KEY,
|
|
28
|
+
GITHUB_TOKEN_ENVIRONMENT_KEY,
|
|
29
|
+
PACKAGE_ROOT_PARENT_INDEX,
|
|
30
|
+
REVIEW_EVENT_COMMENT,
|
|
31
|
+
SELECTED_ACCOUNT_ENVIRONMENT_KEY,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
CommandRunner = Callable[..., subprocess.CompletedProcess[str]]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
38
|
+
parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
|
|
39
|
+
all_actions = parser.add_subparsers(dest="action", required=True)
|
|
40
|
+
create = all_actions.add_parser(ACTION_CREATE, allow_abbrev=False)
|
|
41
|
+
create.add_argument("--repo", required=True)
|
|
42
|
+
create.add_argument("--base", required=True)
|
|
43
|
+
create.add_argument("--head", required=True)
|
|
44
|
+
create.add_argument("--title", required=True)
|
|
45
|
+
create.add_argument("--body-file", required=True, type=Path)
|
|
46
|
+
edit = all_actions.add_parser(ACTION_EDIT, allow_abbrev=False)
|
|
47
|
+
edit.add_argument("--repo", required=True)
|
|
48
|
+
edit.add_argument("--number", required=True)
|
|
49
|
+
edit.add_argument("--title")
|
|
50
|
+
edit.add_argument("--body-file", type=Path)
|
|
51
|
+
comment = all_actions.add_parser(ACTION_COMMENT, allow_abbrev=False)
|
|
52
|
+
comment.add_argument("--repo", required=True)
|
|
53
|
+
comment.add_argument("--number", required=True)
|
|
54
|
+
comment.add_argument("--body-file", required=True, type=Path)
|
|
55
|
+
review = all_actions.add_parser(ACTION_REVIEW, allow_abbrev=False)
|
|
56
|
+
review.add_argument("--repo", required=True)
|
|
57
|
+
review.add_argument("--number", required=True)
|
|
58
|
+
review.add_argument("--body-file", required=True, type=Path)
|
|
59
|
+
review.add_argument(
|
|
60
|
+
"--event",
|
|
61
|
+
choices=tuple(ALL_REVIEW_FLAGS_BY_EVENT),
|
|
62
|
+
default=REVIEW_EVENT_COMMENT,
|
|
63
|
+
)
|
|
64
|
+
return parser
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _configured_managed_root(all_environment: Mapping[str, str]) -> Path:
|
|
68
|
+
configured_root_text = all_environment.get(CONFIG_DIR_OVERRIDE_ENVIRONMENT_KEY, "").strip()
|
|
69
|
+
if not configured_root_text:
|
|
70
|
+
return Path.home() / DEFAULT_MANAGED_ROOT_DIRECTORY_NAME
|
|
71
|
+
configured_root = Path(configured_root_text).expanduser()
|
|
72
|
+
if configured_root.is_absolute():
|
|
73
|
+
return configured_root
|
|
74
|
+
return Path.home() / configured_root
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _linter_path(all_environment: Mapping[str, str]) -> Path:
|
|
78
|
+
source_candidate = Path(__file__).resolve().parents[PACKAGE_ROOT_PARENT_INDEX].joinpath(
|
|
79
|
+
*ALL_DURABLE_POST_LINTER_RELATIVE_PATH_PARTS
|
|
80
|
+
)
|
|
81
|
+
if source_candidate.is_file():
|
|
82
|
+
return source_candidate
|
|
83
|
+
return _configured_managed_root(all_environment).joinpath(
|
|
84
|
+
*ALL_DURABLE_POST_LINTER_RELATIVE_PATH_PARTS
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _linter_arguments(
|
|
89
|
+
arguments: argparse.Namespace, all_environment: Mapping[str, str]
|
|
90
|
+
) -> list[str]:
|
|
91
|
+
all_arguments = [
|
|
92
|
+
sys.executable,
|
|
93
|
+
str(_linter_path(all_environment)),
|
|
94
|
+
"--action",
|
|
95
|
+
ALL_LINTER_ACTIONS_BY_COMMAND[arguments.action],
|
|
96
|
+
]
|
|
97
|
+
if getattr(arguments, "title", None) is not None:
|
|
98
|
+
all_arguments.extend(["--title", arguments.title])
|
|
99
|
+
if getattr(arguments, "body_file", None) is not None:
|
|
100
|
+
all_arguments.extend(["--body-file", str(arguments.body_file)])
|
|
101
|
+
return all_arguments
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _create_arguments(arguments: argparse.Namespace) -> list[str]:
|
|
105
|
+
return [
|
|
106
|
+
"gh",
|
|
107
|
+
"pr",
|
|
108
|
+
"create",
|
|
109
|
+
"--draft",
|
|
110
|
+
"--repo",
|
|
111
|
+
arguments.repo,
|
|
112
|
+
"--base",
|
|
113
|
+
arguments.base,
|
|
114
|
+
"--head",
|
|
115
|
+
arguments.head,
|
|
116
|
+
"--title",
|
|
117
|
+
arguments.title,
|
|
118
|
+
"--body-file",
|
|
119
|
+
str(arguments.body_file),
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _edit_arguments(arguments: argparse.Namespace) -> list[str]:
|
|
124
|
+
all_arguments = ["gh", "pr", "edit", arguments.number, "--repo", arguments.repo]
|
|
125
|
+
if arguments.title is not None:
|
|
126
|
+
all_arguments.extend(["--title", arguments.title])
|
|
127
|
+
if arguments.body_file is not None:
|
|
128
|
+
all_arguments.extend(["--body-file", str(arguments.body_file)])
|
|
129
|
+
return all_arguments
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _post_arguments(arguments: argparse.Namespace) -> list[str]:
|
|
133
|
+
if arguments.action == ACTION_CREATE:
|
|
134
|
+
return _create_arguments(arguments)
|
|
135
|
+
if arguments.action == ACTION_EDIT:
|
|
136
|
+
return _edit_arguments(arguments)
|
|
137
|
+
if arguments.action == ACTION_COMMENT:
|
|
138
|
+
return [
|
|
139
|
+
"gh",
|
|
140
|
+
"pr",
|
|
141
|
+
"comment",
|
|
142
|
+
arguments.number,
|
|
143
|
+
"--repo",
|
|
144
|
+
arguments.repo,
|
|
145
|
+
"--body-file",
|
|
146
|
+
str(arguments.body_file),
|
|
147
|
+
]
|
|
148
|
+
return [
|
|
149
|
+
"gh",
|
|
150
|
+
"pr",
|
|
151
|
+
"review",
|
|
152
|
+
arguments.number,
|
|
153
|
+
"--repo",
|
|
154
|
+
arguments.repo,
|
|
155
|
+
ALL_REVIEW_FLAGS_BY_EVENT[arguments.event],
|
|
156
|
+
"--body-file",
|
|
157
|
+
str(arguments.body_file),
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _lookup_environment(all_environment: Mapping[str, str]) -> dict[str, str]:
|
|
162
|
+
lookup_environment = dict(all_environment)
|
|
163
|
+
for each_name in ALL_GITHUB_AUTHORIZATION_ENVIRONMENT_KEYS:
|
|
164
|
+
lookup_environment.pop(each_name, None)
|
|
165
|
+
return lookup_environment
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _lookup_account_authorization(
|
|
169
|
+
selected_account: str,
|
|
170
|
+
all_environment: Mapping[str, str],
|
|
171
|
+
command_runner: CommandRunner,
|
|
172
|
+
) -> str | None:
|
|
173
|
+
try:
|
|
174
|
+
completed_lookup = command_runner(
|
|
175
|
+
["gh", "auth", "token", "--user", selected_account],
|
|
176
|
+
capture_output=True,
|
|
177
|
+
check=False,
|
|
178
|
+
env=_lookup_environment(all_environment),
|
|
179
|
+
shell=False,
|
|
180
|
+
text=True,
|
|
181
|
+
)
|
|
182
|
+
except OSError:
|
|
183
|
+
sys.stderr.write(ACCOUNT_LOOKUP_FAILED_MESSAGE)
|
|
184
|
+
return None
|
|
185
|
+
if completed_lookup.returncode != 0:
|
|
186
|
+
sys.stderr.write(ACCOUNT_LOOKUP_FAILED_MESSAGE)
|
|
187
|
+
return None
|
|
188
|
+
account_authorization = completed_lookup.stdout.strip()
|
|
189
|
+
if not account_authorization:
|
|
190
|
+
sys.stderr.write(ACCOUNT_LOOKUP_EMPTY_MESSAGE)
|
|
191
|
+
return None
|
|
192
|
+
return account_authorization
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _action_environment(
|
|
196
|
+
all_environment: Mapping[str, str],
|
|
197
|
+
command_runner: CommandRunner,
|
|
198
|
+
) -> dict[str, str] | None:
|
|
199
|
+
selected_account = all_environment.get(SELECTED_ACCOUNT_ENVIRONMENT_KEY)
|
|
200
|
+
if not selected_account:
|
|
201
|
+
return dict(all_environment)
|
|
202
|
+
account_authorization = _lookup_account_authorization(
|
|
203
|
+
selected_account, all_environment, command_runner
|
|
204
|
+
)
|
|
205
|
+
if account_authorization is None:
|
|
206
|
+
return None
|
|
207
|
+
action_environment = dict(all_environment)
|
|
208
|
+
action_environment.pop(GITHUB_TOKEN_ENVIRONMENT_KEY, None)
|
|
209
|
+
action_environment[GH_TOKEN_ENVIRONMENT_KEY] = account_authorization
|
|
210
|
+
return action_environment
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _run_post_action(
|
|
214
|
+
arguments: argparse.Namespace,
|
|
215
|
+
all_action_environment: Mapping[str, str],
|
|
216
|
+
command_runner: CommandRunner,
|
|
217
|
+
) -> int:
|
|
218
|
+
try:
|
|
219
|
+
completed_action = command_runner(
|
|
220
|
+
_post_arguments(arguments),
|
|
221
|
+
check=False,
|
|
222
|
+
env=all_action_environment,
|
|
223
|
+
shell=False,
|
|
224
|
+
)
|
|
225
|
+
except OSError:
|
|
226
|
+
sys.stderr.write(ACTION_FAILED_MESSAGE)
|
|
227
|
+
return ACCOUNT_LOOKUP_FAILURE_EXIT_CODE
|
|
228
|
+
return completed_action.returncode
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _run_action(
|
|
232
|
+
arguments: argparse.Namespace,
|
|
233
|
+
all_environment: Mapping[str, str],
|
|
234
|
+
command_runner: CommandRunner,
|
|
235
|
+
) -> int:
|
|
236
|
+
action_environment = _action_environment(all_environment, command_runner)
|
|
237
|
+
if action_environment is None:
|
|
238
|
+
return ACCOUNT_LOOKUP_FAILURE_EXIT_CODE
|
|
239
|
+
return _run_post_action(arguments, action_environment, command_runner)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def main(
|
|
243
|
+
all_arguments: Sequence[str],
|
|
244
|
+
*,
|
|
245
|
+
all_environment: Mapping[str, str],
|
|
246
|
+
command_runner: CommandRunner,
|
|
247
|
+
) -> int:
|
|
248
|
+
"""Run one validated pull request action."""
|
|
249
|
+
arguments = _build_parser().parse_args(list(all_arguments))
|
|
250
|
+
completed_lint = command_runner(
|
|
251
|
+
_linter_arguments(arguments, all_environment), check=False, shell=False
|
|
252
|
+
)
|
|
253
|
+
if completed_lint.returncode != 0:
|
|
254
|
+
return completed_lint.returncode
|
|
255
|
+
return _run_action(arguments, all_environment, command_runner)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if __name__ == "__main__":
|
|
259
|
+
raise SystemExit(
|
|
260
|
+
main(
|
|
261
|
+
sys.argv[1:],
|
|
262
|
+
all_environment=os.environ,
|
|
263
|
+
command_runner=subprocess.run,
|
|
264
|
+
)
|
|
265
|
+
)
|