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,8 +1,9 @@
|
|
|
1
1
|
"""Constants for the PreToolUse dispatcher that hosts Write/Edit/MultiEdit hooks.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The roster keeps action-boundary checks, repository checks, and checks without
|
|
4
|
+
a proven local replacement. File-policy rules with a staged replacement run
|
|
5
|
+
through cde lint in native pre-commit. Their detectors stay importable for that
|
|
6
|
+
linter. Bash cleanup protection uses its own roster.
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
9
|
from __future__ import annotations
|
|
@@ -55,14 +56,10 @@ ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES: frozenset[str] = frozenset(
|
|
|
55
56
|
ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES | {APPLY_PATCH_TOOL_NAME}
|
|
56
57
|
)
|
|
57
58
|
|
|
58
|
-
# apply_patch reaches this narrower roster: the gates whose miss causes immediate
|
|
59
|
-
# harm, plus the TDD gate and the edit tracker. It stays off the full lint surface.
|
|
60
59
|
ALL_IMMEDIATE_HARM_SCRIPT_PATHS: tuple[str, ...] = (
|
|
61
60
|
"blocking/pii_prevention_blocker.py",
|
|
62
61
|
"blocking/sensitive_file_protector.py",
|
|
63
62
|
"blocking/write_existing_file_blocker.py",
|
|
64
|
-
"blocking/code_rules_enforcer.py",
|
|
65
|
-
"blocking/tdd_enforcer.py",
|
|
66
63
|
)
|
|
67
64
|
|
|
68
65
|
|
|
@@ -71,19 +68,18 @@ STATE_DESCRIPTION_BLOCKER_MODULE_NAME = "state_description_blocker"
|
|
|
71
68
|
|
|
72
69
|
@dataclass(frozen=True)
|
|
73
70
|
class HostedHookEntry:
|
|
74
|
-
"""
|
|
71
|
+
"""One hosted hook, the tools it applies to, and whether a crash blocks.
|
|
75
72
|
|
|
76
73
|
Attributes:
|
|
77
74
|
script_relative_path: Hook path relative to the hooks/ directory.
|
|
78
75
|
applicable_tool_names: Tool names this hook applies to. The dispatcher
|
|
79
76
|
skips the hook when the payload's tool is not in this set.
|
|
80
|
-
is_blocking: True when a crash
|
|
77
|
+
is_blocking: True when a crash is a blocking signal. False when the
|
|
81
78
|
hook is advisory and a crash stays silent.
|
|
82
|
-
native_module_name:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
string or None.
|
|
79
|
+
native_module_name: Importable module whose evaluate function the
|
|
80
|
+
dispatcher calls in-process, or None when the hook runs via runpy
|
|
81
|
+
under __main__. evaluate takes the payload dict and returns a
|
|
82
|
+
deny-reason string or None.
|
|
87
83
|
"""
|
|
88
84
|
|
|
89
85
|
script_relative_path: str
|
|
@@ -109,39 +105,10 @@ ALL_HOSTED_HOOK_ENTRIES: tuple[HostedHookEntry, ...] = (
|
|
|
109
105
|
script_relative_path="validation/hook_format_validator.py",
|
|
110
106
|
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
111
107
|
),
|
|
112
|
-
HostedHookEntry(
|
|
113
|
-
script_relative_path="blocking/code_rules_enforcer.py",
|
|
114
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES,
|
|
115
|
-
),
|
|
116
|
-
HostedHookEntry(
|
|
117
|
-
script_relative_path="blocking/tdd_enforcer.py",
|
|
118
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES,
|
|
119
|
-
),
|
|
120
|
-
HostedHookEntry(
|
|
121
|
-
script_relative_path="blocking/windows_rmtree_blocker.py",
|
|
122
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
123
|
-
),
|
|
124
108
|
HostedHookEntry(
|
|
125
109
|
script_relative_path="blocking/duplicate_rmtree_helper_blocker.py",
|
|
126
110
|
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
127
111
|
),
|
|
128
|
-
HostedHookEntry(
|
|
129
|
-
script_relative_path="blocking/state_description_blocker.py",
|
|
130
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
131
|
-
native_module_name=STATE_DESCRIPTION_BLOCKER_MODULE_NAME,
|
|
132
|
-
),
|
|
133
|
-
HostedHookEntry(
|
|
134
|
-
script_relative_path="blocking/subprocess_budget_completeness.py",
|
|
135
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
136
|
-
),
|
|
137
|
-
HostedHookEntry(
|
|
138
|
-
script_relative_path="blocking/hook_prose_detector_consistency.py",
|
|
139
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
140
|
-
),
|
|
141
|
-
HostedHookEntry(
|
|
142
|
-
script_relative_path="blocking/workflow_substitution_slot_blocker.py",
|
|
143
|
-
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
144
|
-
),
|
|
145
112
|
HostedHookEntry(
|
|
146
113
|
script_relative_path="blocking/claude_md_orphan_file_blocker.py",
|
|
147
114
|
applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
@@ -1,16 +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
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
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"
|
|
14
31
|
|
|
15
32
|
|
|
16
33
|
def test_every_roster_entry_applies_to_bash() -> None:
|
|
@@ -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"
|
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/",
|
|
@@ -40,7 +40,8 @@ name does not form a path.
|
|
|
40
40
|
|
|
41
41
|
## Validation
|
|
42
42
|
|
|
43
|
-
|
|
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
|
|
44
45
|
server write. Pass the matching action and body file. Use `pr-create`,
|
|
45
46
|
`pr-edit`, `pr-comment`, `pr-review`, `issue-create`, `issue-edit`,
|
|
46
47
|
`issue-comment`, or `github-mcp-post`.
|
package/rules/git-workflow.md
CHANGED
|
@@ -19,13 +19,13 @@ User-level rule: applies to **every** git repo that uses GitHub with `gh`. Small
|
|
|
19
19
|
|
|
20
20
|
Use the `pr-description-writer` agent before creating a pull request or
|
|
21
21
|
rewriting its full description. Publish its title and body file through
|
|
22
|
-
`.agents/skills/pull-request/scripts/pull_request.py`.
|
|
23
|
-
`pr-title-description` skill may review the writer's output but cannot replace
|
|
24
|
-
the required writer.
|
|
22
|
+
`.agents/skills/pull-request/scripts/pull_request.py`.
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
29
|
|
|
30
30
|
Use `.agents/skills/pull-request/scripts/recover_legacy_author.py
|
|
31
31
|
<exact-state-file> --confirm-inactive` only for one explicitly selected legacy
|
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() {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const CI_DIRECTORY = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const DRIVER_PATH = join(CI_DIRECTORY, 'installer-lifecycle.mjs');
|
|
10
|
+
const DRIVER_TIMEOUT_MILLISECONDS = 60_000;
|
|
11
|
+
|
|
12
|
+
test('the installer lifecycle driver completes in an isolated home', () => {
|
|
13
|
+
const lifecycleProcess = spawnSync(process.execPath, [DRIVER_PATH], {
|
|
14
|
+
encoding: 'utf8',
|
|
15
|
+
timeout: DRIVER_TIMEOUT_MILLISECONDS,
|
|
16
|
+
});
|
|
17
|
+
assert.equal(lifecycleProcess.status, 0, `${lifecycleProcess.stdout}\n${lifecycleProcess.stderr}`);
|
|
18
|
+
assert.match(lifecycleProcess.stdout, /ALL CHECKS PASSED/);
|
|
19
|
+
assert.match(lifecycleProcess.stdout, /Sandbox removed:/);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('the Windows adapter invokes the CI-owned lifecycle driver', () => {
|
|
23
|
+
const adapterSource = readFileSync(join(CI_DIRECTORY, 'windows-installer-lifecycle.ps1'), 'utf8');
|
|
24
|
+
assert.match(adapterSource, /\$driverPath = Join-Path \$PSScriptRoot 'installer-lifecycle\.mjs'/);
|
|
25
|
+
assert.doesNotMatch(adapterSource, /skills\\run-claude-dev-env/);
|
|
26
|
+
});
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
# Windows CI adapter for the Node installer lifecycle driver.
|
|
2
|
-
#
|
|
3
|
-
# The driver already redirects HOME, USERPROFILE, and GIT_CONFIG_GLOBAL into a
|
|
4
|
-
# throwaway sandbox. This adapter records that isolation contract, runs the
|
|
5
|
-
# 16-check driver, and writes bounded evidence under -EvidenceRoot so a failed
|
|
6
|
-
# Windows runner leaves an actionable artifact.
|
|
7
|
-
#
|
|
8
|
-
# Usage (from packages/claude-dev-env):
|
|
9
|
-
# pwsh -NoProfile -File scripts/ci/windows-installer-lifecycle.ps1 -EvidenceRoot <dir>
|
|
10
|
-
|
|
11
1
|
[CmdletBinding()]
|
|
12
2
|
param(
|
|
13
3
|
[Parameter(Mandatory = $true)]
|
|
@@ -17,7 +7,7 @@ param(
|
|
|
17
7
|
$ErrorActionPreference = 'Stop'
|
|
18
8
|
|
|
19
9
|
$packageRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
20
|
-
$driverPath = Join-Path $
|
|
10
|
+
$driverPath = Join-Path $PSScriptRoot 'installer-lifecycle.mjs'
|
|
21
11
|
$allIsolationEnvNames = @(
|
|
22
12
|
'HOME',
|
|
23
13
|
'USERPROFILE',
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
These name the shape of the installed tree itself, not any one worker or
|
|
4
|
-
skill that reads it, so every caller of shared_tree_paths shares them.
|
|
5
|
-
"""
|
|
1
|
+
"""Directory names for the installed shared tree."""
|
|
6
2
|
|
|
7
3
|
from __future__ import annotations
|
|
8
4
|
|
|
@@ -15,3 +11,6 @@ SCRIPTS_DIRECTORY_NAME: str = "scripts"
|
|
|
15
11
|
PROCESS_TREE_DIRECTORY_NAME: str = "process-tree"
|
|
16
12
|
|
|
17
13
|
PROCESS_TREE_KILL_MODULE_FILENAME: str = "process_tree_kill.py"
|
|
14
|
+
|
|
15
|
+
AGENTS_DIRECTORY_SUFFIX: str = ".agents"
|
|
16
|
+
DEFAULT_MANAGED_ROOT_NAME: str = ".claude"
|
|
@@ -8,7 +8,7 @@ from collections.abc import Sequence
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
|
-
from
|
|
11
|
+
from durable_post_lint_config.config.constants import (
|
|
12
12
|
ACTION_PR_CREATE,
|
|
13
13
|
ACTION_PR_EDIT,
|
|
14
14
|
ALL_BARE_VOLATILE_PATH_MARKERS,
|
|
@@ -82,14 +82,7 @@ def _text_has_anchored_marker(normalized_text: str, marker: str) -> bool:
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def find_volatile_path_marker(body_text: str) -> str | None:
|
|
85
|
-
"""Return the first volatile path marker in a GitHub post body.
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
body_text: Post body text to scan.
|
|
89
|
-
|
|
90
|
-
Returns:
|
|
91
|
-
The matched marker, or None when the body names no volatile path.
|
|
92
|
-
"""
|
|
85
|
+
"""Return the first volatile path marker in a GitHub post body."""
|
|
93
86
|
normalized_text = body_text.replace("\\", PATH_ANCHOR_CHARACTER).lower()
|
|
94
87
|
for each_marker in ALL_PATH_ANCHORED_VOLATILE_PATH_MARKERS:
|
|
95
88
|
if _text_has_anchored_marker(normalized_text, each_marker):
|
|
@@ -101,17 +94,7 @@ def find_volatile_path_marker(body_text: str) -> str | None:
|
|
|
101
94
|
|
|
102
95
|
|
|
103
96
|
def read_body_file(body_file: Path) -> str:
|
|
104
|
-
"""Read one UTF-8 body file without exposing its contents on failure.
|
|
105
|
-
|
|
106
|
-
Args:
|
|
107
|
-
body_file: Local post body file.
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
Decoded body text.
|
|
111
|
-
|
|
112
|
-
Raises:
|
|
113
|
-
DurablePostInputError: The file is missing, unreadable, or invalid UTF-8.
|
|
114
|
-
"""
|
|
97
|
+
"""Read one UTF-8 body file without exposing its contents on failure."""
|
|
115
98
|
try:
|
|
116
99
|
return body_file.read_text(encoding=BODY_FILE_ENCODING)
|
|
117
100
|
except (OSError, UnicodeError) as error:
|
|
@@ -184,19 +167,7 @@ def lint_durable_post(
|
|
|
184
167
|
title: str | None,
|
|
185
168
|
body_text: str | None,
|
|
186
169
|
) -> tuple[DurablePostFinding, ...]:
|
|
187
|
-
"""Return content findings for one locally valid GitHub post request.
|
|
188
|
-
|
|
189
|
-
Args:
|
|
190
|
-
action: GitHub post action name.
|
|
191
|
-
title: Pull request title when the action accepts one.
|
|
192
|
-
body_text: Decoded body text when the action accepts a body.
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
Content findings in stable rule order.
|
|
196
|
-
|
|
197
|
-
Raises:
|
|
198
|
-
DurablePostUsageError: The action and supplied fields are not a valid request.
|
|
199
|
-
"""
|
|
170
|
+
"""Return content findings for one locally valid GitHub post request."""
|
|
200
171
|
_validate_request_shape(action, title, body_text)
|
|
201
172
|
return (*_title_findings(title), *_body_findings(action, body_text))
|
|
202
173
|
|
|
@@ -210,14 +181,7 @@ def _parse_arguments(all_arguments: Sequence[str]) -> argparse.Namespace:
|
|
|
210
181
|
|
|
211
182
|
|
|
212
183
|
def main(all_arguments: Sequence[str]) -> int:
|
|
213
|
-
"""Validate one GitHub post and return its result status.
|
|
214
|
-
|
|
215
|
-
Args:
|
|
216
|
-
all_arguments: Command arguments after the script name.
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
Zero when clean, one for content findings, or two for invalid input.
|
|
220
|
-
"""
|
|
184
|
+
"""Validate one GitHub post and return its result status."""
|
|
221
185
|
arguments = _parse_arguments(all_arguments)
|
|
222
186
|
try:
|
|
223
187
|
body_text = (
|
|
@@ -23,7 +23,6 @@ ALL_POST_ACTIONS: frozenset[str] = frozenset(
|
|
|
23
23
|
ACTION_PR_REVIEW,
|
|
24
24
|
}
|
|
25
25
|
)
|
|
26
|
-
|
|
27
26
|
ALL_BODY_REQUIRED_ACTIONS: frozenset[str] = frozenset(
|
|
28
27
|
{
|
|
29
28
|
ACTION_GITHUB_MCP_POST,
|
|
@@ -35,13 +34,10 @@ ALL_BODY_REQUIRED_ACTIONS: frozenset[str] = frozenset(
|
|
|
35
34
|
ACTION_PR_REVIEW,
|
|
36
35
|
}
|
|
37
36
|
)
|
|
38
|
-
|
|
39
37
|
ALL_PR_DESCRIPTION_ACTIONS: frozenset[str] = frozenset(
|
|
40
38
|
{ACTION_PR_CREATE, ACTION_PR_EDIT}
|
|
41
39
|
)
|
|
42
|
-
|
|
43
40
|
ALL_TITLE_ACTIONS: frozenset[str] = frozenset({ACTION_PR_CREATE, ACTION_PR_EDIT})
|
|
44
|
-
|
|
45
41
|
ALL_CONVENTIONAL_TITLE_TYPES: tuple[str, ...] = (
|
|
46
42
|
"build",
|
|
47
43
|
"chore",
|
|
@@ -55,11 +51,9 @@ ALL_CONVENTIONAL_TITLE_TYPES: tuple[str, ...] = (
|
|
|
55
51
|
"style",
|
|
56
52
|
"test",
|
|
57
53
|
)
|
|
58
|
-
|
|
59
54
|
CONVENTIONAL_TITLE_PATTERN: re.Pattern[str] = re.compile(
|
|
60
55
|
rf"^(?:{'|'.join(ALL_CONVENTIONAL_TITLE_TYPES)})(?:\([^)\r\n]+\))?!?: [^\s].*$"
|
|
61
56
|
)
|
|
62
|
-
|
|
63
57
|
ALL_REQUIRED_PR_DESCRIPTION_HEADINGS: tuple[str, ...] = (
|
|
64
58
|
"Summary",
|
|
65
59
|
"Description",
|
|
@@ -67,12 +61,10 @@ ALL_REQUIRED_PR_DESCRIPTION_HEADINGS: tuple[str, ...] = (
|
|
|
67
61
|
"How",
|
|
68
62
|
"Verification",
|
|
69
63
|
)
|
|
70
|
-
|
|
71
64
|
ALL_PATH_ANCHORED_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
|
|
72
65
|
".claude-profile-a/jobs/",
|
|
73
66
|
".claude/worktrees/",
|
|
74
67
|
)
|
|
75
|
-
|
|
76
68
|
ALL_BARE_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
|
|
77
69
|
"appdata/local/temp",
|
|
78
70
|
"/tmp/",
|
|
@@ -80,7 +72,6 @@ ALL_BARE_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
|
|
|
80
72
|
"$env:temp",
|
|
81
73
|
"$claude_job_dir",
|
|
82
74
|
)
|
|
83
|
-
|
|
84
75
|
BODY_FILE_ENCODING = "utf-8"
|
|
85
76
|
CLEAN_EXIT_CODE = 0
|
|
86
77
|
CONTENT_FINDING_EXIT_CODE = 1
|
|
@@ -88,7 +79,6 @@ EMPTY_BODY_CODE = "empty-body"
|
|
|
88
79
|
INPUT_ERROR_EXIT_CODE = 2
|
|
89
80
|
PATH_ANCHOR_CHARACTER = "/"
|
|
90
81
|
PATH_SEGMENT_START_CHARACTERS = "_-"
|
|
91
|
-
|
|
92
82
|
BODY_FILE_UNREADABLE_MESSAGE = "body file is not readable UTF-8 text"
|
|
93
83
|
BODY_REQUIRED_MESSAGE = "this action requires a body file"
|
|
94
84
|
BODY_MUST_NOT_BE_EMPTY_MESSAGE = "body file must contain text"
|
|
@@ -29,24 +29,31 @@ def terminology_diagnostics(
|
|
|
29
29
|
document_set: DocumentSet,
|
|
30
30
|
load_module: adapter_support.HookModuleLoader,
|
|
31
31
|
) -> tuple[Diagnostic, ...]:
|
|
32
|
-
"""Report
|
|
32
|
+
"""Report prose that near-misses a newly introduced identifier.
|
|
33
33
|
|
|
34
34
|
Args:
|
|
35
|
-
document_set:
|
|
35
|
+
document_set: Staged or base change selection and repository root.
|
|
36
36
|
load_module: Shared script module loader.
|
|
37
37
|
|
|
38
38
|
Returns:
|
|
39
39
|
Diagnostics that name the prose file and line.
|
|
40
40
|
"""
|
|
41
|
-
|
|
41
|
+
all_arguments: tuple[Path | str, ...]
|
|
42
|
+
if document_set.selection is SelectionKind.BASE:
|
|
43
|
+
if document_set.base_revision is None:
|
|
44
|
+
raise ValueError("Base selection is missing its comparison revision")
|
|
45
|
+
checker_name = "strict_base_terminology_findings"
|
|
46
|
+
all_arguments = (document_set.repository_root, document_set.base_revision)
|
|
47
|
+
elif document_set.selection is SelectionKind.STAGED:
|
|
48
|
+
checker_name = "strict_staged_terminology_findings"
|
|
49
|
+
all_arguments = (document_set.repository_root,)
|
|
50
|
+
else:
|
|
42
51
|
return ()
|
|
43
52
|
terminology_module = load_module(constants.TERMINOLOGY_SWEEP_MODULE_NAME)
|
|
44
53
|
helper_stdout = StringIO()
|
|
45
54
|
helper_stderr = StringIO()
|
|
46
55
|
with redirect_stdout(helper_stdout), redirect_stderr(helper_stderr):
|
|
47
|
-
all_findings = terminology_module
|
|
48
|
-
document_set.repository_root
|
|
49
|
-
)
|
|
56
|
+
all_findings = getattr(terminology_module, checker_name)(*all_arguments)
|
|
50
57
|
return tuple(_terminology_diagnostic(each_finding) for each_finding in all_findings)
|
|
51
58
|
|
|
52
59
|
|