claude-dev-env 4.3.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 +5 -4
- package/.agents/skills/pull-request/SKILL.md +11 -13
- package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
- package/.agents/skills/pull-request/scripts/pull_request.py +33 -44
- package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +1 -3
- package/.agents/skills/skill-builder/SKILL.md +3 -196
- 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/install.agents-home.test.mjs +15 -13
- package/bin/install.mjs +28 -0
- package/bin/install.policy-lint.test.mjs +78 -0
- package/bin/install.prune.test.mjs +59 -20
- package/bin/install.pstack-plugin.test.mjs +114 -0
- package/bin/install.test.mjs +37 -0
- 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/test_bash_post_call_dispatcher.py +65 -4
- 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_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/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 -1
- package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
- 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 +19 -2
- 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/package.json +2 -2
- package/rules/durable-post-artifacts.md +2 -1
- package/rules/git-workflow.md +6 -6
- 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/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +5 -41
- package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +0 -10
- package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +1 -1
- 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/shared_tree_paths.py +32 -3
- package/scripts/test_shared_tree_paths.py +33 -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/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +0 -0
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
"""Preflight guard for the working directory and worktree before a PR-convergence run.
|
|
2
|
-
|
|
3
|
-
Usage:
|
|
4
|
-
python preflight_worktree.py --owner <O> --repo <R> --mode <strict|classify>
|
|
5
|
-
|
|
6
|
-
Modes:
|
|
7
|
-
strict — autoconverge: the working directory must be the PR's own repo so
|
|
8
|
-
EnterWorktree can create and enter the branch worktree. Any other
|
|
9
|
-
state aborts (exit 1).
|
|
10
|
-
classify — pr-converge: emit the environment classification so the caller can
|
|
11
|
-
route. same_repo and different_repo both succeed (exit 0); a
|
|
12
|
-
re-rooted session (no git work tree, or no readable origin) aborts
|
|
13
|
-
(exit 1).
|
|
14
|
-
|
|
15
|
-
Output (stdout):
|
|
16
|
-
A line 'PREFLIGHT_OUTCOME=<same_repo|different_repo|re_rooted>', a
|
|
17
|
-
human-readable summary, and, on abort, a recovery instruction.
|
|
18
|
-
|
|
19
|
-
Exit codes:
|
|
20
|
-
0 — safe to continue for the given mode
|
|
21
|
-
1 — abort (the caller must stop and recover)
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
from __future__ import annotations
|
|
25
|
-
|
|
26
|
-
import argparse
|
|
27
|
-
import subprocess
|
|
28
|
-
import sys
|
|
29
|
-
from dataclasses import dataclass
|
|
30
|
-
from pathlib import Path
|
|
31
|
-
|
|
32
|
-
_self_dir = Path(__file__).resolve().parent
|
|
33
|
-
if str(_self_dir) not in sys.path:
|
|
34
|
-
sys.path.insert(0, str(_self_dir))
|
|
35
|
-
|
|
36
|
-
from skills_pr_loop_constants.preflight_constants import (
|
|
37
|
-
ABORT_DIFFERENT_REPO_STRICT_TEMPLATE,
|
|
38
|
-
ABORT_RE_ROOTED_TEMPLATE,
|
|
39
|
-
ABORT_WORKTREE_BROKEN_TEMPLATE,
|
|
40
|
-
ALL_GIT_IS_INSIDE_WORK_TREE_ARGS,
|
|
41
|
-
ALL_GIT_REMOTE_GET_URL_ARGS,
|
|
42
|
-
ALL_GIT_WORKTREE_LIST_ARGS,
|
|
43
|
-
ALL_PREFLIGHT_MODES,
|
|
44
|
-
CWD_IDENTITY_UNKNOWN,
|
|
45
|
-
EXIT_PREFLIGHT_ABORT,
|
|
46
|
-
EXIT_PREFLIGHT_OK,
|
|
47
|
-
GIT_DIRECTORY_FLAG,
|
|
48
|
-
GIT_EXECUTABLE,
|
|
49
|
-
GIT_INSIDE_WORK_TREE_TRUE,
|
|
50
|
-
GIT_SUBPROCESS_TIMEOUT_SECONDS,
|
|
51
|
-
MODE_ARG_FLAG,
|
|
52
|
-
MODE_STRICT,
|
|
53
|
-
OUTCOME_DIFFERENT_REPO,
|
|
54
|
-
OUTCOME_MARKER_TEMPLATE,
|
|
55
|
-
OUTCOME_RE_ROOTED,
|
|
56
|
-
OUTCOME_SAME_REPO,
|
|
57
|
-
OWNER_ARG_FLAG,
|
|
58
|
-
PREFLIGHT_CLI_DESCRIPTION,
|
|
59
|
-
REMOTE_URL_IDENTITY_PATTERN,
|
|
60
|
-
REPO_ARG_FLAG,
|
|
61
|
-
ROUTE_DIFFERENT_REPO_TEMPLATE,
|
|
62
|
-
SUMMARY_DIFFERENT_REPO_TEMPLATE,
|
|
63
|
-
SUMMARY_RE_ROOTED_TEMPLATE,
|
|
64
|
-
SUMMARY_SAME_REPO_TEMPLATE,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@dataclass(frozen=True)
|
|
69
|
-
class RepoIdentity:
|
|
70
|
-
"""A GitHub repository identity parsed from a git remote URL.
|
|
71
|
-
|
|
72
|
-
Attributes:
|
|
73
|
-
owner: Repository owner (login or org), lower-cased for comparison.
|
|
74
|
-
repo: Repository name, lower-cased for comparison.
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
owner: str
|
|
78
|
-
repo: str
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@dataclass(frozen=True)
|
|
82
|
-
class PreflightVerdict:
|
|
83
|
-
"""Classification of the working directory against a target PR repo.
|
|
84
|
-
|
|
85
|
-
Attributes:
|
|
86
|
-
outcome: One of OUTCOME_SAME_REPO, OUTCOME_DIFFERENT_REPO, or
|
|
87
|
-
OUTCOME_RE_ROOTED.
|
|
88
|
-
cwd_identity: Parsed identity of the working directory's origin, or
|
|
89
|
-
None when there is no git work tree, no origin, or an unparseable
|
|
90
|
-
origin URL.
|
|
91
|
-
has_healthy_worktree_machinery: True when 'git worktree list' runs
|
|
92
|
-
cleanly in the working directory.
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
outcome: str
|
|
96
|
-
cwd_identity: RepoIdentity | None
|
|
97
|
-
has_healthy_worktree_machinery: bool
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def parse_repo_identity(remote_url: str) -> RepoIdentity | None:
|
|
101
|
-
"""Parse a GitHub owner/repo from a git remote URL.
|
|
102
|
-
|
|
103
|
-
Accepts the https, git@ (scp-like), and ssh:// forms and drops a trailing
|
|
104
|
-
'.git'. Comparison is case-insensitive, so the returned owner and repo are
|
|
105
|
-
lower-cased.
|
|
106
|
-
|
|
107
|
-
Args:
|
|
108
|
-
remote_url: The remote URL from 'git remote get-url origin'.
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
A RepoIdentity, or None when the URL is not a GitHub remote.
|
|
112
|
-
"""
|
|
113
|
-
match = REMOTE_URL_IDENTITY_PATTERN.search(remote_url.strip())
|
|
114
|
-
if match is None:
|
|
115
|
-
return None
|
|
116
|
-
return RepoIdentity(
|
|
117
|
-
owner=match.group("owner").lower(),
|
|
118
|
-
repo=match.group("repo").lower(),
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _run_git(
|
|
123
|
-
working_directory: Path, all_git_arguments: tuple[str, ...]
|
|
124
|
-
) -> subprocess.CompletedProcess[str]:
|
|
125
|
-
"""Run a git subcommand in a working directory with a bounded timeout.
|
|
126
|
-
|
|
127
|
-
Args:
|
|
128
|
-
working_directory: Directory to run git in (passed via 'git -C').
|
|
129
|
-
all_git_arguments: The git subcommand and its arguments.
|
|
130
|
-
|
|
131
|
-
Returns:
|
|
132
|
-
The completed process with captured text stdout and stderr.
|
|
133
|
-
"""
|
|
134
|
-
return subprocess.run(
|
|
135
|
-
[
|
|
136
|
-
GIT_EXECUTABLE,
|
|
137
|
-
GIT_DIRECTORY_FLAG,
|
|
138
|
-
str(working_directory),
|
|
139
|
-
*all_git_arguments,
|
|
140
|
-
],
|
|
141
|
-
capture_output=True,
|
|
142
|
-
text=True,
|
|
143
|
-
check=False,
|
|
144
|
-
timeout=GIT_SUBPROCESS_TIMEOUT_SECONDS,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def is_inside_work_tree(working_directory: Path) -> bool:
|
|
149
|
-
"""Report whether the working directory is inside a git work tree.
|
|
150
|
-
|
|
151
|
-
Args:
|
|
152
|
-
working_directory: Directory to test.
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
True when 'git rev-parse --is-inside-work-tree' prints 'true'.
|
|
156
|
-
"""
|
|
157
|
-
completed = _run_git(working_directory, ALL_GIT_IS_INSIDE_WORK_TREE_ARGS)
|
|
158
|
-
return completed.returncode == 0 and (
|
|
159
|
-
completed.stdout.strip() == GIT_INSIDE_WORK_TREE_TRUE
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def worktree_machinery_is_healthy(working_directory: Path) -> bool:
|
|
164
|
-
"""Report whether git worktree machinery works in the working directory.
|
|
165
|
-
|
|
166
|
-
EnterWorktree relies on 'git worktree' to create and enter the branch
|
|
167
|
-
worktree; a failing worktree list means it cannot.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
working_directory: Directory to test.
|
|
171
|
-
|
|
172
|
-
Returns:
|
|
173
|
-
True when 'git worktree list' exits cleanly.
|
|
174
|
-
"""
|
|
175
|
-
completed = _run_git(working_directory, ALL_GIT_WORKTREE_LIST_ARGS)
|
|
176
|
-
return completed.returncode == 0
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def classify_environment(
|
|
180
|
-
working_directory: Path, pr_identity: RepoIdentity
|
|
181
|
-
) -> PreflightVerdict:
|
|
182
|
-
"""Classify the working directory against the target PR repository.
|
|
183
|
-
|
|
184
|
-
Args:
|
|
185
|
-
working_directory: The session's current working directory.
|
|
186
|
-
pr_identity: The PR's owner/repo (lower-cased for comparison).
|
|
187
|
-
|
|
188
|
-
Returns:
|
|
189
|
-
A PreflightVerdict naming the outcome and the worktree-machinery health.
|
|
190
|
-
"""
|
|
191
|
-
if not is_inside_work_tree(working_directory):
|
|
192
|
-
return PreflightVerdict(OUTCOME_RE_ROOTED, None, False)
|
|
193
|
-
has_healthy_machinery = worktree_machinery_is_healthy(working_directory)
|
|
194
|
-
remote = _run_git(working_directory, ALL_GIT_REMOTE_GET_URL_ARGS)
|
|
195
|
-
if remote.returncode != 0:
|
|
196
|
-
return PreflightVerdict(OUTCOME_RE_ROOTED, None, has_healthy_machinery)
|
|
197
|
-
cwd_identity = parse_repo_identity(remote.stdout)
|
|
198
|
-
if cwd_identity is None:
|
|
199
|
-
return PreflightVerdict(OUTCOME_DIFFERENT_REPO, None, has_healthy_machinery)
|
|
200
|
-
if cwd_identity == pr_identity:
|
|
201
|
-
return PreflightVerdict(OUTCOME_SAME_REPO, cwd_identity, has_healthy_machinery)
|
|
202
|
-
return PreflightVerdict(OUTCOME_DIFFERENT_REPO, cwd_identity, has_healthy_machinery)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def decide_exit_code(verdict: PreflightVerdict, mode: str) -> int:
|
|
206
|
-
"""Decide the process exit code from a classification and the mode.
|
|
207
|
-
|
|
208
|
-
Args:
|
|
209
|
-
verdict: The classification of the working directory.
|
|
210
|
-
mode: MODE_STRICT (autoconverge) or MODE_CLASSIFY (pr-converge).
|
|
211
|
-
|
|
212
|
-
Returns:
|
|
213
|
-
EXIT_PREFLIGHT_OK when it is safe to continue, else EXIT_PREFLIGHT_ABORT.
|
|
214
|
-
"""
|
|
215
|
-
if verdict.outcome == OUTCOME_RE_ROOTED:
|
|
216
|
-
return EXIT_PREFLIGHT_ABORT
|
|
217
|
-
if verdict.outcome == OUTCOME_SAME_REPO:
|
|
218
|
-
if not verdict.has_healthy_worktree_machinery:
|
|
219
|
-
return EXIT_PREFLIGHT_ABORT
|
|
220
|
-
return EXIT_PREFLIGHT_OK
|
|
221
|
-
if mode == MODE_STRICT:
|
|
222
|
-
return EXIT_PREFLIGHT_ABORT
|
|
223
|
-
return EXIT_PREFLIGHT_OK
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def _cwd_identity_labels(verdict: PreflightVerdict) -> tuple[str, str]:
|
|
227
|
-
"""Return display labels for the working directory's owner and repo.
|
|
228
|
-
|
|
229
|
-
Args:
|
|
230
|
-
verdict: The classification of the working directory.
|
|
231
|
-
|
|
232
|
-
Returns:
|
|
233
|
-
The lower-cased owner and repo, or the unknown placeholder for each
|
|
234
|
-
when the working directory identity could not be parsed.
|
|
235
|
-
"""
|
|
236
|
-
if verdict.cwd_identity is None:
|
|
237
|
-
return CWD_IDENTITY_UNKNOWN, CWD_IDENTITY_UNKNOWN
|
|
238
|
-
return verdict.cwd_identity.owner, verdict.cwd_identity.repo
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def _same_repo_lines(
|
|
242
|
-
verdict: PreflightVerdict, working_directory: Path, pr_identity: RepoIdentity
|
|
243
|
-
) -> list[str]:
|
|
244
|
-
"""Build the report lines for the same-repo outcome.
|
|
245
|
-
|
|
246
|
-
Args:
|
|
247
|
-
verdict: The classification of the working directory.
|
|
248
|
-
working_directory: The session's current working directory.
|
|
249
|
-
pr_identity: The PR's owner/repo.
|
|
250
|
-
|
|
251
|
-
Returns:
|
|
252
|
-
A summary line, plus an abort line when the worktree machinery is broken.
|
|
253
|
-
"""
|
|
254
|
-
report_lines = [
|
|
255
|
-
SUMMARY_SAME_REPO_TEMPLATE.format(
|
|
256
|
-
owner=pr_identity.owner, repo=pr_identity.repo
|
|
257
|
-
)
|
|
258
|
-
]
|
|
259
|
-
if not verdict.has_healthy_worktree_machinery:
|
|
260
|
-
report_lines.append(
|
|
261
|
-
ABORT_WORKTREE_BROKEN_TEMPLATE.format(cwd=working_directory)
|
|
262
|
-
)
|
|
263
|
-
return report_lines
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def _different_repo_lines(
|
|
267
|
-
verdict: PreflightVerdict, mode: str, pr_identity: RepoIdentity
|
|
268
|
-
) -> list[str]:
|
|
269
|
-
"""Build the report lines for the different-repo outcome.
|
|
270
|
-
|
|
271
|
-
Args:
|
|
272
|
-
verdict: The classification of the working directory.
|
|
273
|
-
mode: MODE_STRICT (autoconverge) or MODE_CLASSIFY (pr-converge).
|
|
274
|
-
pr_identity: The PR's owner/repo.
|
|
275
|
-
|
|
276
|
-
Returns:
|
|
277
|
-
A summary line, plus an abort line under strict mode or a route line
|
|
278
|
-
under classify mode.
|
|
279
|
-
"""
|
|
280
|
-
cwd_owner, cwd_repo = _cwd_identity_labels(verdict)
|
|
281
|
-
report_lines = [
|
|
282
|
-
SUMMARY_DIFFERENT_REPO_TEMPLATE.format(
|
|
283
|
-
cwd_owner=cwd_owner,
|
|
284
|
-
cwd_repo=cwd_repo,
|
|
285
|
-
owner=pr_identity.owner,
|
|
286
|
-
repo=pr_identity.repo,
|
|
287
|
-
)
|
|
288
|
-
]
|
|
289
|
-
if mode == MODE_STRICT:
|
|
290
|
-
report_lines.append(
|
|
291
|
-
ABORT_DIFFERENT_REPO_STRICT_TEMPLATE.format(
|
|
292
|
-
cwd_owner=cwd_owner,
|
|
293
|
-
cwd_repo=cwd_repo,
|
|
294
|
-
owner=pr_identity.owner,
|
|
295
|
-
repo=pr_identity.repo,
|
|
296
|
-
)
|
|
297
|
-
)
|
|
298
|
-
else:
|
|
299
|
-
report_lines.append(
|
|
300
|
-
ROUTE_DIFFERENT_REPO_TEMPLATE.format(
|
|
301
|
-
owner=pr_identity.owner, repo=pr_identity.repo
|
|
302
|
-
)
|
|
303
|
-
)
|
|
304
|
-
return report_lines
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
def _re_rooted_lines(working_directory: Path, pr_identity: RepoIdentity) -> list[str]:
|
|
308
|
-
"""Build the report lines for the re-rooted outcome.
|
|
309
|
-
|
|
310
|
-
Args:
|
|
311
|
-
working_directory: The session's current working directory.
|
|
312
|
-
pr_identity: The PR's owner/repo.
|
|
313
|
-
|
|
314
|
-
Returns:
|
|
315
|
-
A summary line and an abort line with the recovery instruction.
|
|
316
|
-
"""
|
|
317
|
-
return [
|
|
318
|
-
SUMMARY_RE_ROOTED_TEMPLATE.format(cwd=working_directory),
|
|
319
|
-
ABORT_RE_ROOTED_TEMPLATE.format(owner=pr_identity.owner, repo=pr_identity.repo),
|
|
320
|
-
]
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def build_report_lines(
|
|
324
|
-
verdict: PreflightVerdict,
|
|
325
|
-
mode: str,
|
|
326
|
-
working_directory: Path,
|
|
327
|
-
pr_identity: RepoIdentity,
|
|
328
|
-
) -> list[str]:
|
|
329
|
-
"""Build the stdout report lines for a classification.
|
|
330
|
-
|
|
331
|
-
Args:
|
|
332
|
-
verdict: The classification of the working directory.
|
|
333
|
-
mode: MODE_STRICT (autoconverge) or MODE_CLASSIFY (pr-converge).
|
|
334
|
-
working_directory: The session's current working directory.
|
|
335
|
-
pr_identity: The PR's owner/repo.
|
|
336
|
-
|
|
337
|
-
Returns:
|
|
338
|
-
Lines to print: the machine-readable outcome marker first, then a
|
|
339
|
-
summary and a recovery or routing instruction when relevant.
|
|
340
|
-
"""
|
|
341
|
-
report_lines = [OUTCOME_MARKER_TEMPLATE.format(outcome=verdict.outcome)]
|
|
342
|
-
if verdict.outcome == OUTCOME_SAME_REPO:
|
|
343
|
-
report_lines.extend(_same_repo_lines(verdict, working_directory, pr_identity))
|
|
344
|
-
elif verdict.outcome == OUTCOME_DIFFERENT_REPO:
|
|
345
|
-
report_lines.extend(_different_repo_lines(verdict, mode, pr_identity))
|
|
346
|
-
else:
|
|
347
|
-
report_lines.extend(_re_rooted_lines(working_directory, pr_identity))
|
|
348
|
-
return report_lines
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
def main(all_arguments: list[str]) -> int:
|
|
352
|
-
"""Classify the working directory and report whether the run can continue.
|
|
353
|
-
|
|
354
|
-
Args:
|
|
355
|
-
all_arguments: The argument vector (sys.argv[1:] in normal use).
|
|
356
|
-
|
|
357
|
-
Returns:
|
|
358
|
-
The process exit code (EXIT_PREFLIGHT_OK or EXIT_PREFLIGHT_ABORT).
|
|
359
|
-
"""
|
|
360
|
-
parser = argparse.ArgumentParser(description=PREFLIGHT_CLI_DESCRIPTION)
|
|
361
|
-
parser.add_argument(OWNER_ARG_FLAG, required=True)
|
|
362
|
-
parser.add_argument(REPO_ARG_FLAG, required=True)
|
|
363
|
-
parser.add_argument(MODE_ARG_FLAG, required=True, choices=ALL_PREFLIGHT_MODES)
|
|
364
|
-
parsed_arguments = parser.parse_args(all_arguments)
|
|
365
|
-
pr_identity = RepoIdentity(
|
|
366
|
-
owner=parsed_arguments.owner.lower(),
|
|
367
|
-
repo=parsed_arguments.repo.lower(),
|
|
368
|
-
)
|
|
369
|
-
working_directory = Path.cwd()
|
|
370
|
-
try:
|
|
371
|
-
verdict = classify_environment(working_directory, pr_identity)
|
|
372
|
-
except subprocess.TimeoutExpired:
|
|
373
|
-
print(
|
|
374
|
-
OUTCOME_MARKER_TEMPLATE.format(outcome=OUTCOME_RE_ROOTED),
|
|
375
|
-
file=sys.stdout,
|
|
376
|
-
)
|
|
377
|
-
print(
|
|
378
|
-
ABORT_RE_ROOTED_TEMPLATE.format(
|
|
379
|
-
owner=pr_identity.owner, repo=pr_identity.repo
|
|
380
|
-
),
|
|
381
|
-
file=sys.stdout,
|
|
382
|
-
)
|
|
383
|
-
return EXIT_PREFLIGHT_ABORT
|
|
384
|
-
for each_report_line in build_report_lines(
|
|
385
|
-
verdict, parsed_arguments.mode, working_directory, pr_identity
|
|
386
|
-
):
|
|
387
|
-
print(each_report_line, file=sys.stdout)
|
|
388
|
-
return decide_exit_code(verdict, parsed_arguments.mode)
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if __name__ == "__main__":
|
|
392
|
-
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Select the converge pacer for a skill and host tool surface.
|
|
3
|
-
|
|
4
|
-
Pacer values::
|
|
5
|
-
|
|
6
|
-
workflow — Claude Workflow tool (autoconverge native)
|
|
7
|
-
schedule_wakeup — Claude ScheduleWakeup ticks (pr-converge native)
|
|
8
|
-
portable — continuous in-session driver (third-party hosts)
|
|
9
|
-
|
|
10
|
-
Rules::
|
|
11
|
-
|
|
12
|
-
autoconverge + Workflow present -> workflow
|
|
13
|
-
autoconverge + Workflow absent -> portable
|
|
14
|
-
pr-converge + ScheduleWakeup present -> schedule_wakeup
|
|
15
|
-
pr-converge + ScheduleWakeup absent -> portable
|
|
16
|
-
|
|
17
|
-
Import ``select_converge_pacer`` or run as a CLI::
|
|
18
|
-
|
|
19
|
-
python select_converge_pacer.py --skill <pr-converge|autoconverge> \\
|
|
20
|
-
--has-workflow <0|1> --has-schedule-wakeup <0|1>
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
from __future__ import annotations
|
|
24
|
-
|
|
25
|
-
import argparse
|
|
26
|
-
import json
|
|
27
|
-
import sys
|
|
28
|
-
from dataclasses import asdict, dataclass
|
|
29
|
-
from pathlib import Path
|
|
30
|
-
|
|
31
|
-
_self_dir = Path(__file__).resolve().parent
|
|
32
|
-
if str(_self_dir) not in sys.path:
|
|
33
|
-
sys.path.insert(0, str(_self_dir))
|
|
34
|
-
|
|
35
|
-
from skills_pr_loop_constants.pacer_constants import ( # noqa: E402
|
|
36
|
-
ALL_ENTRY_SKILLS,
|
|
37
|
-
ALL_FALSY_FLAG_TOKENS,
|
|
38
|
-
ALL_TRUTHY_FLAG_TOKENS,
|
|
39
|
-
CLI_HAS_SCHEDULE_WAKEUP_FLAG,
|
|
40
|
-
CLI_HAS_WORKFLOW_FLAG,
|
|
41
|
-
CLI_SKILL_FLAG,
|
|
42
|
-
ENTRY_SKILL_AUTOCONVERGE,
|
|
43
|
-
ENTRY_SKILL_JOIN_SEPARATOR,
|
|
44
|
-
EXIT_SUCCESS,
|
|
45
|
-
EXIT_USAGE_ERROR,
|
|
46
|
-
INVALID_BOOL_FLAG_ERROR,
|
|
47
|
-
PACER_PORTABLE,
|
|
48
|
-
PACER_SCHEDULE_WAKEUP,
|
|
49
|
-
PACER_WORKFLOW,
|
|
50
|
-
RESULT_KEY_ENTRY_SKILL,
|
|
51
|
-
RESULT_KEY_HAS_SCHEDULE_WAKEUP,
|
|
52
|
-
RESULT_KEY_HAS_WORKFLOW,
|
|
53
|
-
RESULT_KEY_PACER,
|
|
54
|
-
UNKNOWN_ENTRY_SKILL_ERROR,
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@dataclass(frozen=True)
|
|
59
|
-
class ConvergePacerSelection:
|
|
60
|
-
"""Result of pacer selection for one skill invocation."""
|
|
61
|
-
|
|
62
|
-
pacer: str
|
|
63
|
-
entry_skill: str
|
|
64
|
-
has_workflow: bool
|
|
65
|
-
has_schedule_wakeup: bool
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def parse_bool_flag(flag_token: str) -> bool:
|
|
69
|
-
"""Parse a CLI boolean token into a bool.
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
flag_token: Token from ``--has-workflow`` or ``--has-schedule-wakeup``.
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
True for truthy tokens, False for falsy tokens.
|
|
76
|
-
|
|
77
|
-
Raises:
|
|
78
|
-
ValueError: When the token is not a known boolean form.
|
|
79
|
-
"""
|
|
80
|
-
normalized_token = flag_token.strip().lower()
|
|
81
|
-
if normalized_token in ALL_TRUTHY_FLAG_TOKENS:
|
|
82
|
-
return True
|
|
83
|
-
if normalized_token in ALL_FALSY_FLAG_TOKENS:
|
|
84
|
-
return False
|
|
85
|
-
raise ValueError(INVALID_BOOL_FLAG_ERROR.format(got=flag_token))
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def select_converge_pacer(
|
|
89
|
-
*,
|
|
90
|
-
entry_skill: str,
|
|
91
|
-
has_workflow: bool,
|
|
92
|
-
has_schedule_wakeup: bool,
|
|
93
|
-
) -> ConvergePacerSelection:
|
|
94
|
-
"""Return the pacer the named entry skill must use on this host.
|
|
95
|
-
|
|
96
|
-
Args:
|
|
97
|
-
entry_skill: ``pr-converge`` or ``autoconverge``.
|
|
98
|
-
has_workflow: True when the host tool list includes ``Workflow``.
|
|
99
|
-
has_schedule_wakeup: True when the host tool list includes
|
|
100
|
-
``ScheduleWakeup``.
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
A ``ConvergePacerSelection`` with the chosen ``pacer`` name.
|
|
104
|
-
|
|
105
|
-
Raises:
|
|
106
|
-
ValueError: When ``entry_skill`` is not a known converge entry.
|
|
107
|
-
"""
|
|
108
|
-
normalized_skill = entry_skill.strip().lower()
|
|
109
|
-
if normalized_skill not in ALL_ENTRY_SKILLS:
|
|
110
|
-
raise ValueError(
|
|
111
|
-
UNKNOWN_ENTRY_SKILL_ERROR.format(
|
|
112
|
-
allowed=ENTRY_SKILL_JOIN_SEPARATOR.join(ALL_ENTRY_SKILLS),
|
|
113
|
-
got=entry_skill,
|
|
114
|
-
)
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
if normalized_skill == ENTRY_SKILL_AUTOCONVERGE:
|
|
118
|
-
selected_pacer = PACER_WORKFLOW if has_workflow else PACER_PORTABLE
|
|
119
|
-
else:
|
|
120
|
-
selected_pacer = (
|
|
121
|
-
PACER_SCHEDULE_WAKEUP if has_schedule_wakeup else PACER_PORTABLE
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
return ConvergePacerSelection(
|
|
125
|
-
pacer=selected_pacer,
|
|
126
|
-
entry_skill=normalized_skill,
|
|
127
|
-
has_workflow=has_workflow,
|
|
128
|
-
has_schedule_wakeup=has_schedule_wakeup,
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def selection_as_json_dict(
|
|
133
|
-
selection: ConvergePacerSelection,
|
|
134
|
-
) -> dict[str, str | bool]:
|
|
135
|
-
"""Serialize a selection for JSON stdout.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
selection: Pacer selection to serialize.
|
|
139
|
-
|
|
140
|
-
Returns:
|
|
141
|
-
Dict with stable result keys for CLI and tests.
|
|
142
|
-
"""
|
|
143
|
-
selection_by_field = asdict(selection)
|
|
144
|
-
return {
|
|
145
|
-
RESULT_KEY_PACER: selection_by_field["pacer"],
|
|
146
|
-
RESULT_KEY_ENTRY_SKILL: selection_by_field["entry_skill"],
|
|
147
|
-
RESULT_KEY_HAS_WORKFLOW: selection_by_field["has_workflow"],
|
|
148
|
-
RESULT_KEY_HAS_SCHEDULE_WAKEUP: selection_by_field[
|
|
149
|
-
"has_schedule_wakeup"
|
|
150
|
-
],
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def build_argument_parser() -> argparse.ArgumentParser:
|
|
155
|
-
"""Build the CLI parser for pacer selection.
|
|
156
|
-
|
|
157
|
-
Returns:
|
|
158
|
-
Configured argument parser.
|
|
159
|
-
"""
|
|
160
|
-
parser = argparse.ArgumentParser(
|
|
161
|
-
description=(
|
|
162
|
-
"Select workflow, schedule_wakeup, or portable pacer for a "
|
|
163
|
-
"converge entry skill."
|
|
164
|
-
)
|
|
165
|
-
)
|
|
166
|
-
parser.add_argument(
|
|
167
|
-
CLI_SKILL_FLAG,
|
|
168
|
-
required=True,
|
|
169
|
-
choices=list(ALL_ENTRY_SKILLS),
|
|
170
|
-
help="Entry skill invoking converge.",
|
|
171
|
-
)
|
|
172
|
-
parser.add_argument(
|
|
173
|
-
CLI_HAS_WORKFLOW_FLAG,
|
|
174
|
-
required=True,
|
|
175
|
-
help="1/true when the host tool list includes Workflow.",
|
|
176
|
-
)
|
|
177
|
-
parser.add_argument(
|
|
178
|
-
CLI_HAS_SCHEDULE_WAKEUP_FLAG,
|
|
179
|
-
required=True,
|
|
180
|
-
help="1/true when the host tool list includes ScheduleWakeup.",
|
|
181
|
-
)
|
|
182
|
-
return parser
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def main(all_argv: list[str]) -> int:
|
|
186
|
-
"""CLI entry: print one JSON selection line on stdout.
|
|
187
|
-
|
|
188
|
-
Args:
|
|
189
|
-
all_argv: Argument vector (without the program name).
|
|
190
|
-
|
|
191
|
-
Returns:
|
|
192
|
-
Process exit code (0 success, 2 usage/validation error).
|
|
193
|
-
"""
|
|
194
|
-
parser = build_argument_parser()
|
|
195
|
-
parsed_arguments = parser.parse_args(all_argv)
|
|
196
|
-
try:
|
|
197
|
-
has_workflow = parse_bool_flag(parsed_arguments.has_workflow)
|
|
198
|
-
has_schedule_wakeup = parse_bool_flag(
|
|
199
|
-
parsed_arguments.has_schedule_wakeup
|
|
200
|
-
)
|
|
201
|
-
selection = select_converge_pacer(
|
|
202
|
-
entry_skill=parsed_arguments.skill,
|
|
203
|
-
has_workflow=has_workflow,
|
|
204
|
-
has_schedule_wakeup=has_schedule_wakeup,
|
|
205
|
-
)
|
|
206
|
-
except ValueError as validation_error:
|
|
207
|
-
print(str(validation_error), file=sys.stderr)
|
|
208
|
-
return EXIT_USAGE_ERROR
|
|
209
|
-
|
|
210
|
-
print(json.dumps(selection_as_json_dict(selection), sort_keys=True))
|
|
211
|
-
return EXIT_SUCCESS
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if __name__ == "__main__":
|
|
215
|
-
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@../AGENTS.md
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"""Constants for audit_category_schema path resolution and parity checks."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import re
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
SCRIPTS_DIRECTORY = Path(__file__).resolve().parent.parent
|
|
9
|
-
_skills_home_directory = SCRIPTS_DIRECTORY.parents[3]
|
|
10
|
-
PACKAGE_ROOT = (
|
|
11
|
-
_skills_home_directory.parent
|
|
12
|
-
if _skills_home_directory.name == ".agents"
|
|
13
|
-
else _skills_home_directory
|
|
14
|
-
)
|
|
15
|
-
SCHEMA_RELATIVE_PATH = Path("audit-rubrics") / "audit-categories.json"
|
|
16
|
-
CATEGORY_RUBRICS_DIRECTORY = PACKAGE_ROOT / "audit-rubrics" / "category_rubrics"
|
|
17
|
-
PROMPTS_DIRECTORY = PACKAGE_ROOT / "audit-rubrics" / "prompts"
|
|
18
|
-
HEADING_PATTERN = re.compile(r"^# Category ([A-Q]) — (.+)$")
|
|
19
|
-
HEADING_LETTER_GROUP = 1
|
|
20
|
-
HEADING_TITLE_GROUP = 2
|
|
21
|
-
EXPECTED_CATEGORY_IDS = tuple("ABCDEFGHIJKLMNOPQ")
|
|
22
|
-
SCHEMA_VERSION = 1
|
|
23
|
-
JSON_INDENT = 2
|
|
24
|
-
NEWLINE = "\n"
|
|
25
|
-
UTF8_ENCODING = "utf-8"
|
|
26
|
-
RUBRIC_SUB_BUCKET_ROW_TEMPLATE = r"^\| ({category_id}\d+) \| ([^|]+) \|"
|
|
27
|
-
SKELETON_COUNT_PATTERN = re.compile(r"decomposed into (\d+) sub-buckets")
|
|
28
|
-
SKELETON_PLACEHOLDER_FRAGMENT = "decomposed into [N] sub-buckets"
|
|
29
|
-
SKELETON_SEPARATOR_LINE = "---"
|
|
30
|
-
CATEGORY_SLUG_PREFIX_TEMPLATE = "category-{letter}-"
|
|
31
|
-
CATEGORY_ID_KEY = "id"
|
|
32
|
-
CATEGORY_TITLE_KEY = "title"
|
|
33
|
-
CATEGORY_SLUG_KEY = "slug"
|
|
34
|
-
CATEGORY_SUB_BUCKETS_KEY = "sub_buckets"
|
|
35
|
-
SUB_BUCKET_AXIS_KEY = "axis_name"
|
|
36
|
-
SCHEMA_CATEGORIES_KEY = "categories"
|
|
37
|
-
SCHEMA_VERSION_KEY = "schema_version"
|