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
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
"""Behavioral tests for the Bash PostToolUse dispatcher's hosted-hook roster."""
|
|
2
2
|
|
|
3
|
+
from hooks_constants import bash_post_call_dispatcher_constants as constants
|
|
3
4
|
from hooks_constants.bash_post_call_dispatcher_constants import (
|
|
4
5
|
ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES,
|
|
5
6
|
)
|
|
6
7
|
from hooks_constants.bash_pre_tool_use_dispatcher_constants import BASH_TOOL_NAME
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
def
|
|
10
|
+
def test_roster_runs_the_recorder_then_the_pr_done_reminder() -> None:
|
|
10
11
|
all_script_paths = [
|
|
11
12
|
each_entry.script_relative_path for each_entry in ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES
|
|
12
13
|
]
|
|
13
|
-
assert
|
|
14
|
+
assert all_script_paths == [
|
|
15
|
+
"observability/test_failure_recorder.py",
|
|
16
|
+
"advisory/pr_done_reminder.py",
|
|
17
|
+
]
|
|
14
18
|
|
|
15
19
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
def test_pr_done_reminder_also_serves_the_powershell_tool() -> None:
|
|
21
|
+
reminder_entry = ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES[-1]
|
|
22
|
+
assert reminder_entry.script_relative_path == "advisory/pr_done_reminder.py"
|
|
23
|
+
assert "PowerShell" in reminder_entry.applicable_tool_names
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_context_forwarding_keys_match_the_hook_output_contract() -> None:
|
|
27
|
+
assert constants.POST_TOOL_USE_HOOK_EVENT_NAME == "PostToolUse"
|
|
28
|
+
assert constants.HOOK_SPECIFIC_OUTPUT_KEY == "hookSpecificOutput"
|
|
29
|
+
assert constants.ADDITIONAL_CONTEXT_KEY == "additionalContext"
|
|
30
|
+
assert constants.ADDITIONAL_CONTEXT_JOIN_SEPARATOR == "\n\n"
|
|
21
31
|
|
|
22
32
|
|
|
23
33
|
def test_every_roster_entry_applies_to_bash() -> None:
|
|
@@ -20,28 +20,21 @@ POWERSHELL_TOOL_NAME = ALL_CONSTANT_BINDINGS["POWERSHELL_TOOL_NAME"]
|
|
|
20
20
|
|
|
21
21
|
_EXPECTED_BASH_ORDER = (
|
|
22
22
|
"blocking/destructive_command_blocker.py",
|
|
23
|
-
"blocking/gh_body_arg_blocker.py",
|
|
24
23
|
"blocking/shell_substitution_blocker.py",
|
|
25
24
|
"blocking/piped_pytest_blocker.py",
|
|
26
25
|
"blocking/cursor_cli_python_misfire_blocker.py",
|
|
27
26
|
"blocking/unscoped_search_blocker.py",
|
|
28
27
|
"blocking/nas_ssh_binary_enforcer.py",
|
|
29
|
-
"blocking/volatile_path_in_post_blocker.py",
|
|
30
28
|
"blocking/pii_prevention_blocker.py",
|
|
31
|
-
"blocking/conventional_pr_title_gate.py",
|
|
32
29
|
"blocking/block_main_commit.py",
|
|
33
30
|
"blocking/session_edit_stage_gate.py",
|
|
34
|
-
"blocking/test_preflight_check.py",
|
|
35
31
|
"blocking/windows_rmtree_blocker.py",
|
|
36
|
-
"blocking/gh_pr_author_enforcer.py",
|
|
37
|
-
"blocking/pr_description_writer_gate.py",
|
|
38
32
|
)
|
|
39
33
|
|
|
40
34
|
_POWERSHELL_APPLICABLE = (
|
|
41
35
|
"blocking/cursor_cli_python_misfire_blocker.py",
|
|
42
36
|
"blocking/unscoped_search_blocker.py",
|
|
43
37
|
"blocking/pii_prevention_blocker.py",
|
|
44
|
-
"blocking/pr_description_writer_gate.py",
|
|
45
38
|
)
|
|
46
39
|
|
|
47
40
|
|
|
@@ -6,9 +6,7 @@ tokens that keep the recognizer to one unchained command, the pytest program
|
|
|
6
6
|
and Python interpreter basenames, and the flag-token marker the path
|
|
7
7
|
extractor uses to skip an option rather than an existing test file argument.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
module reuses that name rather than a second literal. The pytest-program and
|
|
11
|
-
interpreter recognition here is deliberately its own small, stdlib-only
|
|
9
|
+
The pytest-program and interpreter recognition here is deliberately its own small, stdlib-only
|
|
12
10
|
check rather than a reuse of ``piped_pytest_blocker_constants`` /
|
|
13
11
|
``pytest_invocation``: those solve the harder pipe-and-wrapper problem and
|
|
14
12
|
pull in a much larger transitive import for every Bash call regardless of
|
|
@@ -19,6 +17,8 @@ from __future__ import annotations
|
|
|
19
17
|
|
|
20
18
|
import re
|
|
21
19
|
|
|
20
|
+
BASH_TOOL_NAME = "Bash"
|
|
21
|
+
|
|
22
22
|
ALL_PYTEST_MENTION_SUBSTRINGS: tuple[str, ...] = ("pytest", "py.test")
|
|
23
23
|
EXIT_CODE_ERROR_PREFIX: str = "Error: Exit code "
|
|
24
24
|
OPTION_TOKEN_PREFIX: str = "-"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Tests for GitHub post body extraction constants."""
|
|
2
|
+
|
|
3
|
+
from hooks_constants import gh_post_body_texts_constants as constants
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_post_actions_cover_pull_requests_and_issues() -> None:
|
|
7
|
+
assert constants.ALL_GH_POST_SUBCOMMANDS == {
|
|
8
|
+
"issue": frozenset({"comment", "create", "edit"}),
|
|
9
|
+
"pr": frozenset({"comment", "create", "edit", "review"}),
|
|
10
|
+
}
|
|
11
|
+
assert constants.ALL_MCP_BODY_PARAM_NAMES == ("body", "comment")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_parser_constants_are_stable() -> None:
|
|
15
|
+
assert constants.BODY_FILE_ENCODING == "utf-8"
|
|
16
|
+
assert constants.BODY_FLAG_WITH_VALUE_STEP == 2
|
|
17
|
+
assert constants.GH_COMMAND_NAME == "gh"
|
|
18
|
+
assert constants.MINIMUM_POST_SUBCOMMAND_TOKEN_COUNT == 2
|
|
19
|
+
assert constants.TOKEN_JOIN_SEPARATOR == " "
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Pin the PR done reminder's command shape and checklist wording."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from hooks_constants import pr_done_reminder_constants as constants
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_gh_probe_asks_for_every_field_the_checklist_reads() -> None:
|
|
9
|
+
assert constants.ALL_GH_PR_VIEW_ARGUMENTS[:3] == ("gh", "pr", "view")
|
|
10
|
+
all_requested_fields = constants.ALL_GH_PR_VIEW_ARGUMENTS[-1].split(",")
|
|
11
|
+
for each_field in ("number", "url", "isDraft", "mergeable", "statusCheckRollup", "labels"):
|
|
12
|
+
assert each_field in all_requested_fields
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_git_global_options_that_take_a_value_are_named() -> None:
|
|
16
|
+
assert constants.ALL_GIT_OPTIONS_WITH_VALUE == frozenset({"-C", "-c"})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_powershell_wrappers_and_their_command_flags_are_named() -> None:
|
|
20
|
+
assert "pwsh" in constants.ALL_POWERSHELL_PROGRAM_NAMES
|
|
21
|
+
assert "powershell" in constants.ALL_POWERSHELL_PROGRAM_NAMES
|
|
22
|
+
assert "-command" in constants.ALL_POWERSHELL_COMMAND_FLAGS
|
|
23
|
+
assert "-c" in constants.ALL_POWERSHELL_COMMAND_FLAGS
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_checklist_lines_join_on_a_single_newline() -> None:
|
|
27
|
+
assert constants.REMINDER_LINE_SEPARATOR == "\n"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_every_mergeable_value_has_a_hint() -> None:
|
|
31
|
+
assert set(constants.ALL_REMINDER_HINTS_BY_MERGEABLE) == {
|
|
32
|
+
constants.MERGEABLE_CLEAN_VALUE,
|
|
33
|
+
constants.MERGEABLE_CONFLICTING_VALUE,
|
|
34
|
+
constants.MERGEABLE_UNKNOWN_VALUE,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_header_says_it_never_blocks() -> None:
|
|
39
|
+
assert "never a block" in constants.REMINDER_HEADER
|
|
40
|
+
assert "never a block" in constants.NO_PULL_REQUEST_REMINDER
|
|
@@ -13,6 +13,9 @@ try:
|
|
|
13
13
|
if str(_BLOCKING_DIR) not in sys.path:
|
|
14
14
|
sys.path.insert(0, str(_BLOCKING_DIR))
|
|
15
15
|
|
|
16
|
+
from hooks_constants.bash_pre_tool_use_dispatcher_constants import (
|
|
17
|
+
ALL_BASH_HOSTED_HOOK_ENTRIES,
|
|
18
|
+
)
|
|
16
19
|
from hooks_constants.pre_tool_use_dispatcher_constants import (
|
|
17
20
|
ALL_HOSTED_HOOK_ENTRIES,
|
|
18
21
|
ALL_IMMEDIATE_HARM_SCRIPT_PATHS,
|
|
@@ -74,10 +77,12 @@ def test_duplicate_rmtree_helper_blocker_runs_via_runpy() -> None:
|
|
|
74
77
|
assert entry.native_module_name is None
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
def test_windows_rmtree_write_check_is_retired_but_bash_check_remains() -> None:
|
|
81
|
+
assert _entry_for("blocking/windows_rmtree_blocker.py") is None
|
|
82
|
+
all_bash_paths = {
|
|
83
|
+
each_entry.script_relative_path for each_entry in ALL_BASH_HOSTED_HOOK_ENTRIES
|
|
84
|
+
}
|
|
85
|
+
assert "blocking/windows_rmtree_blocker.py" in all_bash_paths
|
|
81
86
|
|
|
82
87
|
|
|
83
88
|
def test_blocking_hook_crash_deny_reason_surfaces_the_constant() -> None:
|
|
@@ -119,22 +124,11 @@ def test_every_hosted_script_path_exists_under_the_hooks_root() -> None:
|
|
|
119
124
|
)
|
|
120
125
|
|
|
121
126
|
|
|
122
|
-
def
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
]
|
|
128
|
-
assert nativized_entries, (
|
|
129
|
-
"the roster must carry at least one nativized hook for this test to lock the contract"
|
|
130
|
-
)
|
|
131
|
-
for each_entry in nativized_entries:
|
|
132
|
-
native_module = importlib.import_module(each_entry.native_module_name)
|
|
133
|
-
evaluate_function = getattr(native_module, "evaluate", None)
|
|
134
|
-
assert callable(evaluate_function), (
|
|
135
|
-
f"{each_entry.native_module_name} must expose a callable named evaluate, "
|
|
136
|
-
"matching the native_module_name docstring contract"
|
|
137
|
-
)
|
|
127
|
+
def test_retired_native_detector_keeps_its_callable_linter_interface() -> None:
|
|
128
|
+
native_module = importlib.import_module("state_description_blocker")
|
|
129
|
+
assert native_module.find_violations("The API uses port 8080.", "guide.md") == []
|
|
130
|
+
assert native_module.find_violations("Previously set via env var.", "guide.md")
|
|
131
|
+
assert all(each_entry.native_module_name is None for each_entry in ALL_HOSTED_HOOK_ENTRIES)
|
|
138
132
|
|
|
139
133
|
|
|
140
134
|
def test_four_way_set_covers_every_mutation_tool_name() -> None:
|
|
@@ -144,7 +138,7 @@ def test_four_way_set_covers_every_mutation_tool_name() -> None:
|
|
|
144
138
|
|
|
145
139
|
|
|
146
140
|
def test_immediate_harm_hooks_reach_apply_patch() -> None:
|
|
147
|
-
"""PII, sensitive-path, existing-file
|
|
141
|
+
"""PII, sensitive-path, and existing-file checks retain apply_patch coverage."""
|
|
148
142
|
for each_script_path in ALL_IMMEDIATE_HARM_SCRIPT_PATHS:
|
|
149
143
|
entry = _entry_for(each_script_path)
|
|
150
144
|
assert entry is not None, f"{each_script_path} must stay on the hosted roster"
|
|
@@ -154,8 +148,6 @@ def test_immediate_harm_hooks_reach_apply_patch() -> None:
|
|
|
154
148
|
|
|
155
149
|
|
|
156
150
|
_ALL_DEFERRED_LINT_SCRIPT_PATHS = (
|
|
157
|
-
"blocking/state_description_blocker.py",
|
|
158
|
-
"blocking/workflow_substitution_slot_blocker.py",
|
|
159
151
|
"blocking/claude_md_orphan_file_blocker.py",
|
|
160
152
|
"blocking/package_inventory_stale_blocker.py",
|
|
161
153
|
"blocking/env_var_table_code_drift_blocker.py",
|
|
@@ -164,9 +156,6 @@ _ALL_DEFERRED_LINT_SCRIPT_PATHS = (
|
|
|
164
156
|
"blocking/docstring_rule_gate_count_blocker.py",
|
|
165
157
|
"blocking/duplicate_rmtree_helper_blocker.py",
|
|
166
158
|
"validation/hook_format_validator.py",
|
|
167
|
-
"blocking/hook_prose_detector_consistency.py",
|
|
168
|
-
"blocking/subprocess_budget_completeness.py",
|
|
169
|
-
"blocking/windows_rmtree_blocker.py",
|
|
170
159
|
"advisory/refactor_guard.py",
|
|
171
160
|
"advisory/migration_safety_advisor.py",
|
|
172
161
|
)
|
|
@@ -177,7 +166,7 @@ def test_lint_and_advisory_hooks_stay_off_the_apply_patch_roster() -> None:
|
|
|
177
166
|
|
|
178
167
|
apply_patch reaches only a gate whose violation is already real and
|
|
179
168
|
unrecoverable the moment the call returns: a leaked secret, an exposed
|
|
180
|
-
sensitive path, a blind overwrite
|
|
169
|
+
sensitive path, or a blind overwrite. Every
|
|
181
170
|
hook named here instead judges the quality of the authored content for a
|
|
182
171
|
consequence the write itself does not cause right now: doc-inventory
|
|
183
172
|
drift, prose or naming style, a future-runtime correctness pattern (an
|
|
@@ -225,13 +214,12 @@ def test_edit_and_multi_edit_applicable_sets_are_equal() -> None:
|
|
|
225
214
|
)
|
|
226
215
|
|
|
227
216
|
|
|
228
|
-
def
|
|
217
|
+
def test_retained_boundary_hooks_reach_multi_edit() -> None:
|
|
229
218
|
all_multi_edit_widened_script_paths = (
|
|
230
219
|
"blocking/write_existing_file_blocker.py",
|
|
231
|
-
"blocking/
|
|
232
|
-
"blocking/
|
|
233
|
-
|
|
234
|
-
)
|
|
220
|
+
"blocking/sensitive_file_protector.py",
|
|
221
|
+
"blocking/pii_prevention_blocker.py",
|
|
222
|
+
)
|
|
235
223
|
for each_script_path in all_multi_edit_widened_script_paths:
|
|
236
224
|
entry = _entry_for(each_script_path)
|
|
237
225
|
assert entry is not None, f"{each_script_path} must stay on the hosted roster"
|
|
@@ -101,13 +101,13 @@ try:
|
|
|
101
101
|
for each_bootstrap_directory in (_hooks_root_directory, _blocking_directory):
|
|
102
102
|
if each_bootstrap_directory not in sys.path:
|
|
103
103
|
sys.path.insert(0, each_bootstrap_directory)
|
|
104
|
-
from hooks_constants.gh_pr_author_swap_constants import BASH_TOOL_NAME
|
|
105
104
|
from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
|
|
106
105
|
from hooks_constants.test_failure_recorder_constants import (
|
|
107
106
|
ALL_CHAINING_OPERATOR_SUBSTRINGS,
|
|
108
107
|
ALL_PYTEST_MENTION_SUBSTRINGS,
|
|
109
108
|
ALL_PYTEST_PROGRAM_BASENAMES,
|
|
110
109
|
ALL_REDIRECTION_TOKENS,
|
|
110
|
+
BASH_TOOL_NAME,
|
|
111
111
|
EXIT_CODE_ERROR_PREFIX,
|
|
112
112
|
MODULE_RUN_FLAG,
|
|
113
113
|
NODE_ID_FILE_PATH_SEPARATOR,
|
package/hooks/pyproject.toml
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Tests for durable GitHub post body extraction."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from gh_post_body_texts import (
|
|
8
|
+
extract_gh_post_body_texts_for_privacy_gate,
|
|
9
|
+
extract_mcp_body_texts,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.parametrize(
|
|
14
|
+
("command", "expected_text"),
|
|
15
|
+
[
|
|
16
|
+
('gh pr create --body "pull request text"', "pull request text"),
|
|
17
|
+
('gh pr comment 12 --body="comment text"', "comment text"),
|
|
18
|
+
('gh issue edit 7 -b "issue text"', "issue text"),
|
|
19
|
+
('GH_HOST=github.example gh pr review 8 --body "review text"', "review text"),
|
|
20
|
+
],
|
|
21
|
+
)
|
|
22
|
+
def test_inline_post_bodies_are_extracted(command: str, expected_text: str) -> None:
|
|
23
|
+
all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(command)
|
|
24
|
+
assert all_body_texts == [expected_text]
|
|
25
|
+
assert deny_reason is None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_relative_body_file_uses_supplied_working_directory(tmp_path: Path) -> None:
|
|
29
|
+
body_file = tmp_path / "body.md"
|
|
30
|
+
body_file.write_text("body file text", encoding="utf-8")
|
|
31
|
+
all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(
|
|
32
|
+
"gh pr comment 12 --body-file body.md",
|
|
33
|
+
str(tmp_path),
|
|
34
|
+
)
|
|
35
|
+
assert all_body_texts == ["body file text"]
|
|
36
|
+
assert deny_reason is None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize("body_file_text", ["missing.md", "$BODY_FILE", "-"])
|
|
40
|
+
def test_unreadable_body_file_fails_closed(body_file_text: str) -> None:
|
|
41
|
+
all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(
|
|
42
|
+
f"gh pr comment 12 --body-file {body_file_text}"
|
|
43
|
+
)
|
|
44
|
+
assert all_body_texts == []
|
|
45
|
+
assert deny_reason is not None
|
|
46
|
+
assert "could not be read" in deny_reason
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_invalid_utf8_body_file_fails_closed(tmp_path: Path) -> None:
|
|
50
|
+
body_file = tmp_path / "body.md"
|
|
51
|
+
body_file.write_bytes(b"\xff")
|
|
52
|
+
all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(
|
|
53
|
+
f'gh issue create --body-file "{body_file}"'
|
|
54
|
+
)
|
|
55
|
+
assert all_body_texts == []
|
|
56
|
+
assert deny_reason is not None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.parametrize(
|
|
60
|
+
"command",
|
|
61
|
+
[
|
|
62
|
+
"gh pr view 12",
|
|
63
|
+
'echo "gh pr comment 12 --body text"',
|
|
64
|
+
"git status",
|
|
65
|
+
"",
|
|
66
|
+
],
|
|
67
|
+
)
|
|
68
|
+
def test_non_post_commands_have_no_body(command: str) -> None:
|
|
69
|
+
assert extract_gh_post_body_texts_for_privacy_gate(command) == ([], None)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_mcp_body_and_comment_values_are_extracted() -> None:
|
|
73
|
+
assert extract_mcp_body_texts(
|
|
74
|
+
{"body": "body text", "comment": "comment text", "title": "ignored"}
|
|
75
|
+
) == ["body text", "comment text"]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_mcp_non_string_and_empty_values_are_ignored() -> None:
|
|
79
|
+
assert extract_mcp_body_texts({"body": "", "comment": 7}) == []
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-dev-env",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"cde": "bin/cde.mjs"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"test": "node --test \"bin/*.test.mjs\" \".agents/skills/**/*.test.mjs\" \"tests/fresh-session/**/*.test.mjs\""
|
|
12
|
+
"test": "node --test \"bin/*.test.mjs\" \".agents/skills/**/*.test.mjs\" \"tests/fresh-session/**/*.test.mjs\" \"scripts/ci/*.test.mjs\""
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"bin/",
|
|
@@ -1,38 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# GitHub post input rules
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## When this applies
|
|
4
|
+
|
|
5
|
+
Use this rule for a GitHub issue, pull request, comment, or review created
|
|
6
|
+
through `gh` or a GitHub MCP post tool.
|
|
4
7
|
|
|
5
8
|
## Rule
|
|
6
9
|
|
|
7
|
-
A post
|
|
10
|
+
A post remains after the job ends. Job scratch directories, worktrees, and
|
|
11
|
+
system temp folders do not. Do not put a path from one of those directories in
|
|
12
|
+
a post.
|
|
8
13
|
|
|
9
|
-
Handle
|
|
14
|
+
Handle text and binary content differently:
|
|
10
15
|
|
|
11
|
-
- **Text data**
|
|
12
|
-
|
|
16
|
+
- **Text data** such as logs, tables, diffs, and stack traces belongs inline in
|
|
17
|
+
the post body. Do not link a scratch file that holds text data.
|
|
18
|
+
- **Binary artifacts** such as images, screenshots, and archives belong in the
|
|
19
|
+
repository's durable `artifacts` release. Use the helper:
|
|
13
20
|
|
|
14
21
|
```
|
|
15
22
|
python3 ~/.claude/scripts/gh_artifact_upload.py <file-path> <owner/repo>
|
|
16
23
|
```
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
The helper creates the `artifacts` prerelease when needed, uploads the file
|
|
26
|
+
under a `YYYYMMDD_HHMMSS_<name>` asset name, and prints a permanent download
|
|
27
|
+
URL. Put that URL in the post.
|
|
19
28
|
|
|
20
29
|
## Volatile paths that must not appear in a post body
|
|
21
30
|
|
|
22
|
-
- A job scratch directory
|
|
23
|
-
- A worktree
|
|
24
|
-
- A system temp location
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Both slash directions count.
|
|
28
|
-
|
|
29
|
-
The worktree and job-scratch entries count as a path when either:
|
|
31
|
+
- A job scratch directory: `.claude-profile-a/jobs/`
|
|
32
|
+
- A worktree: `.claude/worktrees/`
|
|
33
|
+
- A system temp location: `AppData\\Local\\Temp`, `%TEMP%`, `$env:TEMP`, or
|
|
34
|
+
`/tmp/`
|
|
35
|
+
- The job scratch environment variable: `$CLAUDE_JOB_DIR`
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
Both slash directions count. The path rule applies when a slash or backslash
|
|
38
|
+
precedes a marker, or when a path segment follows it. A standalone directory
|
|
39
|
+
name does not form a path.
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
## Validation
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
|
|
44
|
+
otherwise), then run `<managed-root>/scripts/durable_post_lint.py` before the
|
|
45
|
+
server write. Pass the matching action and body file. Use `pr-create`,
|
|
46
|
+
`pr-edit`, `pr-comment`, `pr-review`, `issue-create`, `issue-edit`,
|
|
47
|
+
`issue-comment`, or `github-mcp-post`.
|
|
37
48
|
|
|
38
|
-
The
|
|
49
|
+
The linter reads the body file and reports a volatile local path without
|
|
50
|
+
printing the body. Fix the body and rerun the linter before posting.
|
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
# gh CLI
|
|
1
|
+
# gh CLI conventions
|
|
2
2
|
|
|
3
|
-
Two
|
|
3
|
+
Two `gh` call shapes need explicit handling.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Put body content in a file
|
|
6
6
|
|
|
7
|
-
Every `gh` command
|
|
7
|
+
Every `gh` command that carries markdown body content uses
|
|
8
|
+
`--body-file <path>`. This applies to `gh pr create`, `gh pr edit`,
|
|
9
|
+
`gh pr comment`, `gh pr review`, `gh issue create`, `gh issue edit`, and
|
|
10
|
+
`gh issue comment`. Never pass a `--body` or `-b` string. Write the file as
|
|
11
|
+
BOM-free UTF-8:
|
|
8
12
|
|
|
9
13
|
```powershell
|
|
10
14
|
[IO.File]::WriteAllText($bodyPath, $body, [Text.UTF8Encoding]::new($false))
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
MCP GitHub tools take `body` as a structured parameter
|
|
17
|
+
MCP GitHub tools take `body` as a structured parameter. Write the same body to
|
|
18
|
+
a UTF-8 file and run the shared linter before sending that parameter.
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
For pull requests, use
|
|
21
|
+
`.agents/skills/pull-request/scripts/pull_request.py`. It passes
|
|
22
|
+
`--body-file` to `gh` after the action-aware linter succeeds. For issues and
|
|
23
|
+
GitHub MCP posts, run the linter directly with `issue-create`, `issue-edit`,
|
|
24
|
+
`issue-comment`, or `github-mcp-post` as the action.
|
|
16
25
|
|
|
17
26
|
## Paginated reads slurp before they filter
|
|
18
27
|
|
|
19
|
-
Every `gh api` read of a paginated GitHub list endpoint
|
|
28
|
+
Every `gh api` read of a paginated GitHub list endpoint uses
|
|
29
|
+
`--paginate --slurp` and pipes the result to external `jq`. This applies to PR
|
|
30
|
+
reviews, comments, and files, plus issue comments, pulls, and issues. The
|
|
31
|
+
built-in `--jq` runs once per page and can produce a wrong cross-page result.
|
|
20
32
|
|
|
21
|
-
Single-object endpoints
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- [`destructive-commands.md`](destructive-commands.md) — why a body describing `rm -rf` must travel by file path.
|
|
26
|
-
- [`durable-post-artifacts.md`](durable-post-artifacts.md) — what a post body may reference.
|
|
33
|
+
Single-object endpoints such as `pulls/<n>` and `issues/<n>` do not need
|
|
34
|
+
pagination and may use `--jq` directly. For a newest-first walk, sort the
|
|
35
|
+
slurped array and take the last element. For one page, cap the request with a
|
|
36
|
+
`per_page` query parameter.
|
package/rules/git-workflow.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Git
|
|
1
|
+
# Git workflow
|
|
2
2
|
|
|
3
|
-
User-level rule: applies to **every** git repo that uses GitHub with `gh
|
|
3
|
+
User-level rule: applies to **every** git repo that uses GitHub with `gh`. Small or non-primary repos follow the same rule unless the user says otherwise in the session.
|
|
4
4
|
|
|
5
|
-
## Workflow
|
|
5
|
+
## Workflow decision tree
|
|
6
6
|
|
|
7
7
|
**When to use stacked PRs:** Feature B depends on Feature A's implementation
|
|
8
8
|
|
|
@@ -13,19 +13,32 @@ User-level rule: applies to **every** git repo that uses GitHub with `gh` (no ex
|
|
|
13
13
|
2. Get reviewed and MERGE infrastructure first
|
|
14
14
|
3. Launch parallel feature PRs that use merged infrastructure
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Pull request submission rules
|
|
17
17
|
|
|
18
18
|
**ALWAYS create PRs as DRAFT:** Use `gh pr create --draft` for ALL PRs
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Use the `pr-description-writer` agent before creating a pull request or
|
|
21
|
+
rewriting its full description. Publish its title and body file through
|
|
22
|
+
`.agents/skills/pull-request/scripts/pull_request.py`.
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
|
|
25
|
+
otherwise), then run `<managed-root>/scripts/durable_post_lint.py` before any
|
|
26
|
+
pull request, issue, or GitHub MCP post. The linter checks the action-specific
|
|
27
|
+
title, body, and volatile-path rules before credential lookup or network
|
|
28
|
+
access.
|
|
29
|
+
|
|
30
|
+
Use `.agents/skills/pull-request/scripts/recover_legacy_author.py
|
|
31
|
+
<exact-state-file> --confirm-inactive` only for one explicitly selected legacy
|
|
32
|
+
author record. Do not infer a record from age alone. Keep every other record
|
|
33
|
+
untouched.
|
|
34
|
+
|
|
35
|
+
## Git golden rules
|
|
23
36
|
|
|
24
37
|
1. **DRAFT BEFORE PUSH**: When pushing ANYTHING to a PR, it MUST be in draft state first
|
|
25
38
|
- Before push: `gh pr ready --undo`
|
|
26
39
|
- After review approved: `gh pr ready`
|
|
27
40
|
|
|
28
|
-
## Never
|
|
41
|
+
## Never commit working documents or images
|
|
29
42
|
|
|
30
43
|
**NEVER commit these files to the repo:**
|
|
31
44
|
|
|
@@ -49,4 +62,4 @@ An image a PR needs as visual evidence is not an exception to that row. Upload i
|
|
|
49
62
|
|
|
50
63
|
Repair only reported findings, then re-verify after every repair.
|
|
51
64
|
|
|
52
|
-
Every `gh` post in this workflow uses `--body-file` per `gh-cli-conventions.md` and keeps volatile scratch paths out per `durable-post-artifacts.md
|
|
65
|
+
Every `gh` post in this workflow uses `--body-file` per `gh-cli-conventions.md` and keeps volatile scratch paths out per `durable-post-artifacts.md`. Stage session edits per `re-stage-before-commit.md` before each commit.
|
package/scripts/check.ps1
CHANGED
|
@@ -1,29 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Smoke driver for the claude-dev-env installer.
|
|
4
|
-
*
|
|
5
|
-
* The installer (bin/install.mjs) writes into os.homedir()/.claude and
|
|
6
|
-
* os.homedir()/.agents and has
|
|
7
|
-
* three global side effects: it copies files under ~/.claude/ and ~/.agents/, sets
|
|
8
|
-
* `git config --global core.hooksPath`, and writes ~/.mypy.ini. There is no
|
|
9
|
-
* flag to redirect the target, so this driver isolates every side effect by
|
|
10
|
-
* spawning the installer with HOME, USERPROFILE, and GIT_CONFIG_GLOBAL all
|
|
11
|
-
* pointed at a throwaway sandbox directory. Node's os.homedir() follows
|
|
12
|
-
* USERPROFILE on Windows and HOME on POSIX, so setting both redirects the
|
|
13
|
-
* install target on either platform without touching the real ~/.claude/.
|
|
14
|
-
*
|
|
15
|
-
* The isolated GIT_CONFIG_GLOBAL is seeded with `safe.directory = *` so the
|
|
16
|
-
* installer's `git status` source-conflict guard runs even when the checkout
|
|
17
|
-
* lives somewhere git flags as dubious ownership (for example a UNC network
|
|
18
|
-
* share), while the installer's `git config --global core.hooksPath` write
|
|
19
|
-
* still lands only in the sandbox config.
|
|
20
|
-
*
|
|
21
|
-
* It runs the three lifecycle commands an agent cares about — `--help`, a full
|
|
22
|
-
* install, and `--uninstall` — and asserts the observable result of each
|
|
23
|
-
* against the sandbox tree. Exit code 0 means every assertion held.
|
|
24
|
-
*
|
|
25
|
-
* Usage: node .claude/skills/run-claude-dev-env/driver.mjs
|
|
26
|
-
*/
|
|
27
2
|
|
|
28
3
|
import { spawnSync } from 'node:child_process';
|
|
29
4
|
import {
|
|
@@ -41,7 +16,7 @@ import { tmpdir } from 'node:os';
|
|
|
41
16
|
import { fileURLToPath } from 'node:url';
|
|
42
17
|
|
|
43
18
|
const DRIVER_DIRECTORY = dirname(fileURLToPath(import.meta.url));
|
|
44
|
-
const PACKAGE_ROOT = resolve(DRIVER_DIRECTORY, '..', '..'
|
|
19
|
+
const PACKAGE_ROOT = resolve(DRIVER_DIRECTORY, '..', '..');
|
|
45
20
|
const INSTALL_ENTRY = join(PACKAGE_ROOT, 'bin', 'install.mjs');
|
|
46
21
|
|
|
47
22
|
const checks = [];
|
|
@@ -68,7 +43,7 @@ function collectFilesRecursive(directory) {
|
|
|
68
43
|
|
|
69
44
|
function runInstaller(sandboxHome, installerArguments) {
|
|
70
45
|
const gitConfigGlobal = join(sandboxHome, '.gitconfig-sandbox');
|
|
71
|
-
const
|
|
46
|
+
const installerProcess = spawnSync(process.execPath, [INSTALL_ENTRY, ...installerArguments], {
|
|
72
47
|
cwd: PACKAGE_ROOT,
|
|
73
48
|
encoding: 'utf8',
|
|
74
49
|
env: {
|
|
@@ -78,7 +53,7 @@ function runInstaller(sandboxHome, installerArguments) {
|
|
|
78
53
|
GIT_CONFIG_GLOBAL: gitConfigGlobal,
|
|
79
54
|
},
|
|
80
55
|
});
|
|
81
|
-
return
|
|
56
|
+
return installerProcess;
|
|
82
57
|
}
|
|
83
58
|
|
|
84
59
|
function main() {
|