claude-dev-env 2.4.0 → 2.7.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/CLAUDE.md +26 -59
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +6 -3
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +12 -2
- package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +309 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +540 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +206 -70
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/__init__.py +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/_repo_test_helpers.py +76 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_baseline_import_isolation.py +248 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_regression.py +309 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +91 -58
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +202 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
- package/_shared/pr-loop/worker-spawn.md +1 -1
- package/agents/CLAUDE.md +3 -2
- package/agents/caveman.md +0 -1
- package/agents/clasp-deployment-orchestrator.md +0 -1
- package/agents/clean-coder.md +0 -1
- package/agents/code-advisor.md +0 -1
- package/agents/code-quality-agent.md +1 -2
- package/agents/code-verifier.md +36 -8
- package/agents/deep-research.md +0 -1
- package/agents/docs-agent.md +0 -1
- package/agents/git-commit-crafter.md +0 -1
- package/agents/issue-tracker.md +42 -0
- package/agents/plan-packet-validator.md +0 -1
- package/agents/pr-description-writer.md +0 -1
- package/agents/test_agent_frontmatter.py +67 -18
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
- package/bin/CLAUDE.md +68 -5
- package/bin/codex-compat.mjs +104 -0
- package/bin/codex-compat.test.mjs +51 -0
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +88 -0
- package/bin/install.mjs +1138 -114
- package/bin/install.prune.test.mjs +869 -19
- package/bin/install.test.mjs +906 -2
- package/codex-capability-map.json +13 -0
- package/commands/implement.md +1 -1
- package/commands/right-size.md +1 -1
- package/docs/CLAUDE.md +1 -0
- package/docs/CODE_RULES.md +2 -0
- package/docs/codex-compatibility.md +25 -0
- package/docs/host-pool-health-monitor.md +102 -0
- package/docs/nas-ssh-invocation.md +96 -12
- package/docs/references/CLAUDE.md +4 -2
- package/docs/references/advisor-tool.md +13 -0
- package/docs/references/code-review-enforcement.md +35 -0
- package/docs/references/team-advisor-skill.md +14 -0
- package/hooks/blocking/CLAUDE.md +4 -0
- package/hooks/blocking/code_review_pr_create_gate.py +7 -3
- package/hooks/blocking/code_review_push_gate.py +9 -4
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
- package/hooks/blocking/config/__init__.py +5 -5
- package/hooks/blocking/config/code_review_enforcement_constants.py +40 -7
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +58 -0
- package/hooks/blocking/config/verified_commit_constants.py +160 -159
- package/hooks/blocking/eli11_reply_enforcer.py +479 -0
- package/hooks/blocking/gh_body_arg_blocker.py +1 -1
- package/hooks/blocking/nas_ssh_binary_enforcer.py +8 -46
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
- package/hooks/blocking/shell_substitution_blocker.py +129 -0
- package/hooks/blocking/state_description_blocker.py +1 -1
- package/hooks/blocking/stop_dispatcher.py +1 -1
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +2 -3
- package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
- package/hooks/blocking/test_code_review_push_gate.py +16 -0
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
- package/hooks/blocking/test_eli11_reply_enforcer.py +457 -0
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
- package/hooks/blocking/test_shell_substitution_blocker.py +124 -0
- package/hooks/blocking/test_stop_dispatcher.py +23 -0
- package/hooks/blocking/test_unscoped_search_blocker.py +102 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +804 -808
- package/hooks/blocking/test_verification_verdict_store.py +54 -0
- package/hooks/blocking/test_verified_commit_gate.py +581 -581
- package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
- package/hooks/blocking/unscoped_search_blocker.py +391 -0
- package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
- package/hooks/blocking/verification_verdict_store.py +1039 -1036
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
- package/hooks/blocking/verifier_verdict_minter.py +280 -280
- package/hooks/git-hooks/CLAUDE.md +3 -0
- package/hooks/git-hooks/conftest.py +30 -0
- package/hooks/git-hooks/gate_utils.py +2 -2
- package/hooks/git-hooks/git_hooks_constants/__init__.py +41 -2
- package/hooks/git-hooks/pre_push.py +75 -4
- package/hooks/git-hooks/pre_push_base_reference.py +166 -0
- package/hooks/git-hooks/test_config.py +0 -15
- package/hooks/git-hooks/test_gate_utils.py +3 -15
- package/hooks/git-hooks/test_pre_commit.py +1 -15
- package/hooks/git-hooks/test_pre_push.py +257 -23
- package/hooks/git-hooks/test_pre_push_base_reference.py +339 -0
- package/hooks/hooks.json +10 -12
- package/hooks/hooks_constants/CLAUDE.md +7 -2
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +4 -4
- package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +101 -0
- package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +2 -8
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
- package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
- package/hooks/hooks_constants/shell_command_segments.py +82 -0
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +67 -0
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +5 -6
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/unscoped_search_blocker_constants.py +153 -0
- package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
- package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
- package/hooks/pyproject.toml +1 -0
- package/hooks/validators/CLAUDE.md +1 -0
- package/hooks/validators/config/__init__.py +0 -0
- package/hooks/validators/config/directory_exemption_constants.py +183 -0
- package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
- package/hooks/validators/conftest.py +4 -0
- package/hooks/validators/ruff_integration.py +49 -5
- package/hooks/validators/run_all_validators.py +206 -9
- package/hooks/validators/test_directory_exemption_constants.py +185 -0
- package/hooks/validators/test_python_antipattern_checks.py +110 -5
- package/hooks/validators/test_ruff_integration.py +92 -1
- package/hooks/validators/test_run_all_validators.py +115 -68
- package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
- package/package.json +13 -3
- package/rules/CLAUDE.md +17 -22
- package/rules/agent-spawn-protocol.md +6 -6
- package/rules/anti-corollary-tests.md +1 -1
- package/rules/bdd.md +1 -1
- package/rules/cleanup-temp-files.md +10 -4
- package/rules/code-standards.md +7 -0
- package/rules/conservative-action.md +1 -5
- package/rules/context7.md +0 -4
- package/rules/destructive-commands.md +47 -0
- package/rules/doc-inventory-integrity.md +48 -0
- package/rules/doc-prose-cuts.md +58 -0
- package/rules/docstring-prose-matches-implementation.md +53 -44
- package/rules/durable-post-artifacts.md +0 -4
- package/rules/eli11-replies.md +31 -0
- package/rules/explore-thoroughly.md +4 -4
- package/rules/falsify-before-green.md +68 -0
- package/rules/file-global-constants.md +1 -1
- package/rules/filesystem-search.md +51 -0
- package/rules/gh-cli-conventions.md +27 -0
- package/rules/git-workflow.md +26 -0
- package/rules/hedging-claims.md +9 -0
- package/rules/long-horizon-autonomy.md +0 -4
- package/rules/measurement-denominators.md +48 -0
- package/rules/nas-ssh-invocation.md +23 -5
- package/rules/parallel-tools.md +2 -2
- package/rules/plain-illustrative-docstrings.md +3 -7
- package/rules/plain-language.md +2 -0
- package/rules/proof-of-work-pr-comments.md +0 -4
- package/rules/re-stage-before-commit.md +2 -0
- package/rules/research-mode.md +10 -0
- package/rules/shell-invocation.md +21 -0
- package/rules/testing.md +4 -0
- package/rules/verified-commit-gate-skip.md +3 -27
- package/rules/verify-before-asking.md +5 -0
- package/rules/windows-filesystem-safe.md +1 -1
- package/rules/workers-done-before-complete.md +4 -0
- package/scripts/CLAUDE.md +1 -0
- package/scripts/Capture-PoolHealth.ps1 +410 -0
- package/scripts/Migrate-ShellPolicy.ps1 +1 -1
- package/scripts/_code_review_test_support.py +404 -0
- package/scripts/claude_chain_runner.py +141 -1
- package/scripts/codex_capability_bridge.py +171 -0
- package/scripts/codex_compat_materializer.py +1087 -0
- package/scripts/codex_compat_watcher.py +502 -0
- package/scripts/conftest.py +16 -1
- package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
- package/scripts/dev_env_scripts_constants/code_review_constants.py +37 -0
- package/scripts/invoke_code_review.py +11 -4
- package/scripts/resolve_worker_spawn.py +626 -626
- package/scripts/spawn_grok_batch.py +672 -672
- package/scripts/sync_to_cursor/rules.py +0 -10
- package/scripts/test_claude_chain_runner.py +131 -0
- package/scripts/test_invoke_code_review.py +85 -908
- package/scripts/test_invoke_code_review_chain.py +70 -0
- package/scripts/test_invoke_code_review_cli.py +192 -0
- package/scripts/test_invoke_code_review_contract.py +256 -0
- package/scripts/test_invoke_code_review_git.py +123 -0
- package/scripts/test_invoke_code_review_mode.py +99 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -1014
- package/scripts/tests/test_code_review_constants.py +80 -0
- package/scripts/tests/test_codex_capability_bridge.py +91 -0
- package/scripts/tests/test_codex_compat_materializer.py +632 -0
- package/scripts/tests/test_codex_compat_watcher.py +599 -0
- package/scripts/tests/test_sync_to_cursor.py +0 -1
- package/skills/CLAUDE.md +2 -0
- package/skills/auditing-claude-config/SKILL.md +114 -114
- package/skills/autoconverge/SKILL.md +427 -427
- package/skills/autoconverge/reference/convergence.md +24 -3
- package/skills/autoconverge/workflow/CLAUDE.md +1 -0
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
- package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
- package/skills/autoconverge/workflow/converge.mjs +168 -1
- package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
- package/skills/bugteam/reference/copilot-gap-analysis.md +1 -1
- package/skills/closeout/SKILL.md +33 -50
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
- package/skills/codex-review/scripts/run_codex_review.py +233 -1
- package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
- package/skills/condensing-instructions/SKILL.md +72 -0
- package/skills/copilot-review/SKILL.md +119 -119
- package/skills/e-code-review/SKILL.md +52 -0
- package/skills/e-code-review/reference/fix.md +54 -0
- package/skills/e-code-review/reference/loop.md +43 -0
- package/skills/e-code-review/reference/low.md +57 -0
- package/skills/e-code-review/reference/medium.md +153 -0
- package/skills/e-code-review/reference/xhigh.md +182 -0
- package/skills/e-simplify/SKILL.md +97 -0
- package/skills/fresh-branch/CLAUDE.md +1 -1
- package/skills/fresh-branch/SKILL.md +5 -6
- package/skills/fresh-branch/scripts/create_fresh_branch.py +42 -24
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -3
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +30 -126
- package/skills/issue-tracker/SKILL.md +92 -0
- package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
- package/skills/issue-tracker/reference/handoff-schema.md +64 -0
- package/skills/issue-tracker/reference/operation-matrix.md +41 -0
- package/skills/orchestrator/SKILL.md +177 -22
- package/skills/orchestrator/scripts/status_gate.py +625 -0
- package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
- package/skills/orchestrator/scripts/test_status_gate.py +439 -0
- package/skills/orchestrator-refresh/SKILL.md +129 -35
- package/skills/plan-to-pr/SKILL.md +155 -0
- package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
- package/skills/plan-to-pr/reference/model-routing.md +36 -0
- package/skills/plan-to-pr/reference/packet-contract.md +43 -0
- package/skills/plan-to-pr/reference/packet-schema.json +57 -0
- package/skills/plan-to-pr/reference/process-inventory.md +22 -0
- package/skills/plan-to-pr/reference/review-loop.md +33 -0
- package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
- package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
- package/skills/plan-to-pr/reference/task-seeds.md +14 -0
- package/skills/plan-to-pr/reference/task-ticket.md +38 -0
- package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
- package/skills/plan-to-pr/scripts/config/constants.py +193 -0
- package/skills/plan-to-pr/scripts/create_packet.py +173 -0
- package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
- package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
- package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
- package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
- package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
- package/skills/plan-to-pr/scripts/validate_run.py +173 -0
- package/skills/plan-to-pr/test_skill_contract.py +207 -0
- package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
- package/skills/pr-converge/SKILL.md +472 -469
- package/skills/pr-converge/reference/examples.md +3 -3
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +7 -4
- package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
- package/skills/pr-converge/reference/per-tick.md +5 -5
- package/skills/pr-converge/reference/progress-checklist.md +1 -1
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
- package/skills/pr-converge/test_step5_host_branch.py +1 -1
- package/skills/pr-fix-protocol/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -68
- package/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
- package/skills/prototype/workflows/promotion.md +1 -1
- package/skills/release-notes-html/SKILL.md +164 -0
- package/skills/session-log/SKILL.md +1 -1
- package/skills/task-build/CLAUDE.md +8 -7
- package/skills/task-build/SKILL.md +16 -8
- package/skills/task-build/reference/tool-routing.md +19 -0
- package/rules/claude-md-orphan-file.md +0 -28
- package/rules/cleanup-command-forms.md +0 -23
- package/rules/code-reviews.md +0 -11
- package/rules/env-var-table-code-drift.md +0 -10
- package/rules/gh-body-file.md +0 -5
- package/rules/gh-paginate.md +0 -3
- package/rules/hook-prose-matches-detector.md +0 -15
- package/rules/no-historical-clutter.md +0 -26
- package/rules/no-inline-destructive-literals.md +0 -9
- package/rules/no-justification-noise.md +0 -61
- package/rules/package-inventory-stale-entry.md +0 -25
- package/rules/right-sized-engineering.md +0 -28
- package/rules/self-contained-docs.md +0 -17
- package/rules/shell-invocation-policy.md +0 -5
- package/rules/tdd.md +0 -7
- package/skills/closeout/reference/issue-body-templates.md +0 -108
|
@@ -8,6 +8,7 @@ when that content violates a validator, rather than grading the whole branch.
|
|
|
8
8
|
import json
|
|
9
9
|
import subprocess
|
|
10
10
|
import sys
|
|
11
|
+
import tempfile
|
|
11
12
|
from collections import Counter
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from unittest.mock import patch
|
|
@@ -16,12 +17,21 @@ import pytest
|
|
|
16
17
|
|
|
17
18
|
from .run_all_validators import (
|
|
18
19
|
ValidatorResult,
|
|
20
|
+
_escapes_temporary_root,
|
|
21
|
+
_hooks_subprocess_working_directory_and_environment,
|
|
19
22
|
_scope_new_and_preexisting,
|
|
23
|
+
_temporary_path_preserving_directory_signal,
|
|
20
24
|
_violation_line_number,
|
|
21
25
|
main,
|
|
22
26
|
run_validators_entrypoint_subprocess,
|
|
23
27
|
)
|
|
24
28
|
|
|
29
|
+
CONFIG_DIR_TARGET_PATH = (
|
|
30
|
+
"CDP Automations/os_update_workflow/config/submission_constants.py"
|
|
31
|
+
)
|
|
32
|
+
PARENT_TRAVERSAL_TARGET_PATH = "../../escape_target.py"
|
|
33
|
+
RELATIVE_CONFIG_TARGET_PATH = "config/x.py"
|
|
34
|
+
|
|
25
35
|
CLEAN_PYTHON_SOURCE = (
|
|
26
36
|
"def add_two_numbers(first_number: int, second_number: int) -> int:\n"
|
|
27
37
|
" return first_number + second_number\n"
|
|
@@ -85,7 +95,9 @@ class TestPreToolUseGate:
|
|
|
85
95
|
def test_edit_validates_reconstructed_post_edit_content(
|
|
86
96
|
self, tmp_path: Path
|
|
87
97
|
) -> None:
|
|
88
|
-
|
|
98
|
+
target_directory = tmp_path / "neutral_edit_target"
|
|
99
|
+
target_directory.mkdir(exist_ok=True)
|
|
100
|
+
target_file = target_directory / "calculate.py"
|
|
89
101
|
target_file.write_text(CLEAN_PYTHON_SOURCE, encoding="utf-8")
|
|
90
102
|
completed = run_gate(
|
|
91
103
|
{
|
|
@@ -107,6 +119,152 @@ class TestPreToolUseGate:
|
|
|
107
119
|
assert completed.returncode == 0, completed.stderr
|
|
108
120
|
assert "deny" not in completed.stdout
|
|
109
121
|
|
|
122
|
+
def test_write_to_config_dir_path_is_not_denied(self) -> None:
|
|
123
|
+
completed = run_gate(
|
|
124
|
+
{
|
|
125
|
+
"tool_name": "Write",
|
|
126
|
+
"tool_input": {
|
|
127
|
+
"file_path": CONFIG_DIR_TARGET_PATH,
|
|
128
|
+
"content": VIOLATING_PYTHON_SOURCE,
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
assert completed.returncode == 0, completed.stderr
|
|
133
|
+
assert "deny" not in completed.stdout
|
|
134
|
+
|
|
135
|
+
def test_write_relative_config_path_not_denied_when_cwd_under_system_temp(
|
|
136
|
+
self, monkeypatch: pytest.MonkeyPatch
|
|
137
|
+
) -> None:
|
|
138
|
+
system_temp_root = Path(tempfile.gettempdir()).resolve()
|
|
139
|
+
monkeypatch.setenv("TEMP", str(system_temp_root))
|
|
140
|
+
monkeypatch.setenv("TMP", str(system_temp_root))
|
|
141
|
+
monkeypatch.setenv("TMPDIR", str(system_temp_root))
|
|
142
|
+
with tempfile.TemporaryDirectory(
|
|
143
|
+
dir=str(system_temp_root), prefix="gate_cwd_"
|
|
144
|
+
) as temporary_root_string:
|
|
145
|
+
temporary_cwd = Path(temporary_root_string) / "gate_cwd"
|
|
146
|
+
temporary_cwd.mkdir()
|
|
147
|
+
|
|
148
|
+
def working_directory_under_system_temp() -> tuple[str, dict[str, str]]:
|
|
149
|
+
_working_directory, environment = (
|
|
150
|
+
_hooks_subprocess_working_directory_and_environment()
|
|
151
|
+
)
|
|
152
|
+
return str(temporary_cwd), environment
|
|
153
|
+
|
|
154
|
+
with patch(
|
|
155
|
+
"validators.run_all_validators._hooks_subprocess_working_directory_and_environment",
|
|
156
|
+
side_effect=working_directory_under_system_temp,
|
|
157
|
+
):
|
|
158
|
+
completed = run_gate(
|
|
159
|
+
{
|
|
160
|
+
"tool_name": "Write",
|
|
161
|
+
"tool_input": {
|
|
162
|
+
"file_path": RELATIVE_CONFIG_TARGET_PATH,
|
|
163
|
+
"content": VIOLATING_PYTHON_SOURCE,
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
assert completed.returncode == 0, completed.stderr
|
|
168
|
+
assert "deny" not in completed.stdout
|
|
169
|
+
|
|
170
|
+
def test_write_to_parent_traversal_path_still_validates(self) -> None:
|
|
171
|
+
completed = run_gate(
|
|
172
|
+
{
|
|
173
|
+
"tool_name": "Write",
|
|
174
|
+
"tool_input": {
|
|
175
|
+
"file_path": PARENT_TRAVERSAL_TARGET_PATH,
|
|
176
|
+
"content": VIOLATING_PYTHON_SOURCE,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
assert completed.returncode == 0, completed.stderr
|
|
181
|
+
assert '"permissionDecision": "deny"' in completed.stdout
|
|
182
|
+
assert "Magic Values" in completed.stdout
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class TestTemporaryPathPreservingDirectorySignal:
|
|
186
|
+
def test_strips_parent_traversal_segments(self, tmp_path: Path) -> None:
|
|
187
|
+
assert _escapes_temporary_root("..") is True
|
|
188
|
+
staged_path = _temporary_path_preserving_directory_signal(
|
|
189
|
+
tmp_path, PARENT_TRAVERSAL_TARGET_PATH
|
|
190
|
+
)
|
|
191
|
+
assert staged_path == tmp_path / "escape_target.py"
|
|
192
|
+
assert staged_path.resolve().is_relative_to(tmp_path.resolve())
|
|
193
|
+
|
|
194
|
+
def test_keeps_config_directory_segment(self, tmp_path: Path) -> None:
|
|
195
|
+
staged_path = _temporary_path_preserving_directory_signal(
|
|
196
|
+
tmp_path, CONFIG_DIR_TARGET_PATH
|
|
197
|
+
)
|
|
198
|
+
assert staged_path.name == "submission_constants.py"
|
|
199
|
+
assert "config" in staged_path.parts
|
|
200
|
+
assert staged_path == tmp_path / "config" / "submission_constants.py"
|
|
201
|
+
|
|
202
|
+
def test_drops_leading_anchor(self, tmp_path: Path) -> None:
|
|
203
|
+
absolute_target = tmp_path / "config" / "submission_constants.py"
|
|
204
|
+
staging_root = tmp_path / "staging_root"
|
|
205
|
+
staging_root.mkdir()
|
|
206
|
+
staged_path = _temporary_path_preserving_directory_signal(
|
|
207
|
+
staging_root, str(absolute_target)
|
|
208
|
+
)
|
|
209
|
+
relative_parts = staged_path.relative_to(staging_root).parts
|
|
210
|
+
assert relative_parts == ("config", "submission_constants.py")
|
|
211
|
+
assert absolute_target.anchor not in relative_parts
|
|
212
|
+
assert staged_path.resolve().is_relative_to(staging_root.resolve())
|
|
213
|
+
|
|
214
|
+
def test_path_without_exemption_directory_stages_flat_basename(
|
|
215
|
+
self, tmp_path: Path
|
|
216
|
+
) -> None:
|
|
217
|
+
pytest_shaped_target = (
|
|
218
|
+
tmp_path / "test_edit_introducing_new_viol0" / "legacy_module.py"
|
|
219
|
+
)
|
|
220
|
+
pytest_shaped_target.parent.mkdir(parents=True, exist_ok=True)
|
|
221
|
+
pytest_shaped_target.write_text(CLEAN_MARKER_FUNCTION, encoding="utf-8")
|
|
222
|
+
staging_root = tmp_path / "staging_root"
|
|
223
|
+
staging_root.mkdir()
|
|
224
|
+
staged_path = _temporary_path_preserving_directory_signal(
|
|
225
|
+
staging_root, str(pytest_shaped_target)
|
|
226
|
+
)
|
|
227
|
+
assert staged_path == staging_root / "legacy_module.py"
|
|
228
|
+
|
|
229
|
+
def test_relative_config_path_preserves_config_under_system_temp_cwd(
|
|
230
|
+
self, monkeypatch: pytest.MonkeyPatch
|
|
231
|
+
) -> None:
|
|
232
|
+
system_temp_root = Path(tempfile.gettempdir()).resolve()
|
|
233
|
+
monkeypatch.setenv("TEMP", str(system_temp_root))
|
|
234
|
+
monkeypatch.setenv("TMP", str(system_temp_root))
|
|
235
|
+
monkeypatch.setenv("TMPDIR", str(system_temp_root))
|
|
236
|
+
with tempfile.TemporaryDirectory(
|
|
237
|
+
dir=str(system_temp_root), prefix="staging_cwd_"
|
|
238
|
+
) as temporary_root_string:
|
|
239
|
+
temporary_root = Path(temporary_root_string)
|
|
240
|
+
previous_cwd = Path.cwd()
|
|
241
|
+
try:
|
|
242
|
+
monkeypatch.chdir(temporary_root)
|
|
243
|
+
staging_root = temporary_root / "staging_root"
|
|
244
|
+
staging_root.mkdir()
|
|
245
|
+
staged_path = _temporary_path_preserving_directory_signal(
|
|
246
|
+
staging_root, RELATIVE_CONFIG_TARGET_PATH
|
|
247
|
+
)
|
|
248
|
+
assert staged_path == staging_root / "config" / "x.py"
|
|
249
|
+
assert "config" in staged_path.parts
|
|
250
|
+
assert staged_path.name == "x.py"
|
|
251
|
+
finally:
|
|
252
|
+
monkeypatch.chdir(previous_cwd)
|
|
253
|
+
|
|
254
|
+
def test_escapes_temporary_root_rejects_absolute_and_parent_parts(
|
|
255
|
+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
256
|
+
) -> None:
|
|
257
|
+
sandbox_temp_root = tmp_path / "system_temp"
|
|
258
|
+
sandbox_temp_root.mkdir()
|
|
259
|
+
monkeypatch.setenv("TEMP", str(sandbox_temp_root))
|
|
260
|
+
monkeypatch.setenv("TMP", str(sandbox_temp_root))
|
|
261
|
+
monkeypatch.setenv("TMPDIR", str(sandbox_temp_root))
|
|
262
|
+
assert _escapes_temporary_root("..") is True
|
|
263
|
+
assert _escapes_temporary_root("scripts") is False
|
|
264
|
+
assert _escapes_temporary_root("config") is False
|
|
265
|
+
absolute_part = str(Path(tempfile.gettempdir()).resolve().anchor) or "/"
|
|
266
|
+
assert _escapes_temporary_root(absolute_part) is True
|
|
267
|
+
|
|
110
268
|
|
|
111
269
|
class TestCliModeRegression:
|
|
112
270
|
def test_cli_mode_reports_violations_and_exits_one(
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-dev-env",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"claude-dev-env": "bin/install.mjs"
|
|
7
|
+
"claude-dev-env": "bin/install.mjs",
|
|
8
|
+
"codex-compat": "bin/codex-compat.mjs"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"test": "node --test \"bin/*.test.mjs\" \"skills/**/*.test.mjs\""
|
|
@@ -19,9 +20,18 @@
|
|
|
19
20
|
"hooks/",
|
|
20
21
|
"system-prompts/",
|
|
21
22
|
"scripts/",
|
|
23
|
+
"codex-capability-map.json",
|
|
22
24
|
"_shared/",
|
|
23
25
|
"audit-rubrics/",
|
|
24
|
-
"CLAUDE.md"
|
|
26
|
+
"CLAUDE.md",
|
|
27
|
+
"!**/__pycache__/**",
|
|
28
|
+
"!**/*.py[cod]",
|
|
29
|
+
"!**/.pytest_cache/**",
|
|
30
|
+
"!**/.mypy_cache/**",
|
|
31
|
+
"!**/.ruff_cache/**",
|
|
32
|
+
"!**/*.log",
|
|
33
|
+
"!**/debug-*.log",
|
|
34
|
+
"!**/*.egg-info/**"
|
|
25
35
|
],
|
|
26
36
|
"keywords": [
|
|
27
37
|
"claude-code",
|
package/rules/CLAUDE.md
CHANGED
|
@@ -11,35 +11,33 @@ Rule files installed into `~/.claude/rules/` by `bin/install.mjs`. A rule withou
|
|
|
11
11
|
|
|
12
12
|
| File | Rule |
|
|
13
13
|
|---|---|
|
|
14
|
-
| `agent-spawn-protocol.md` |
|
|
14
|
+
| `agent-spawn-protocol.md` | Check context sufficiency before a spawn and ask subagents for file-and-line answers; `/prompt-generator` is recommended for a complex or user-facing spawn |
|
|
15
15
|
| `anti-corollary-tests.md` | Tests must carry information: no corollary matrices over canonical reductions, no suite that only matches a dead-implementation default, stated mutation in the audit lane |
|
|
16
16
|
| `ask-user-question-required.md` | Every user-directed question goes through the `AskUserQuestion` tool — no plain-text questions |
|
|
17
17
|
| `bdd.md` | BDD discovery-driven development workflow and Example Mapping reference |
|
|
18
|
-
| `claude-md-orphan-file.md` | Every backticked bare filename in a per-directory `CLAUDE.md` table's first column names a file in that directory's subtree |
|
|
19
|
-
| `cleanup-command-forms.md` | Never use bash `rm` to clean up; use the PowerShell `Remove-Item` and `git worktree remove --force` forms the `destructive_command_blocker` hook never prompts on, and carry the rule into every subagent prompt |
|
|
20
18
|
| `cleanup-temp-files.md` | Remove temporary files created during a task when the task is complete |
|
|
21
|
-
| `code-
|
|
22
|
-
| `code-standards.md` | Pointer to `CODE_RULES.md` as the single source of truth |
|
|
19
|
+
| `code-standards.md` | Pointer to `CODE_RULES.md` as the single source of truth, including §8 (TDD) and §7 (right-sizing); BDD is the outer process and TDD the inner loop |
|
|
23
20
|
| `confirm-implementation-forks.md` | Stop and ask when two or more workable implementation paths change the deliverable |
|
|
24
21
|
| `conservative-action.md` | Research and recommend when intent is ambiguous; act only on explicit request |
|
|
25
22
|
| `context7.md` | Use Context7 MCP to fetch current library docs; always prefer live docs over built-in knowledge |
|
|
23
|
+
| `destructive-commands.md` | Allowed removal forms and the ephemeral namespace the `destructive_command_blocker` auto-allows; keep destructive literals out of a Bash command string even as data |
|
|
24
|
+
| `doc-inventory-integrity.md` | Three inventory shapes stay in step with the code: a per-directory `CLAUDE.md` file list, a package `README`/`SKILL.md` inventory, and an env-var summary table |
|
|
25
|
+
| `doc-prose-cuts.md` | Four sentence shapes to cut from prose: exclusion claims, justification sentences, conversation references, and time references |
|
|
26
26
|
| `docstring-prose-matches-implementation.md` | Prose enumerations in docstrings cover every behavior the body applies |
|
|
27
27
|
| `durable-post-artifacts.md` | GitHub post bodies never reference volatile scratch paths; text embeds inline and binary artifacts upload to the `artifacts` release with the permanent URL linked |
|
|
28
|
+
| `eli11-replies.md` | Every chat reply the user reads follows one shape: action first, detail last, few words; `plain-language.md` governs word choice, this rule governs reply length and shape |
|
|
28
29
|
| `explore-thoroughly.md` | Read relevant files and map existing patterns before proposing a change |
|
|
30
|
+
| `falsify-before-green.md` | A check's green counts as evidence only after that same check ran red on a named break, with a passing control beside it |
|
|
29
31
|
| `file-global-constants.md` | File-global constants need at least two same-file references; otherwise move value to `config/` |
|
|
30
|
-
| `
|
|
31
|
-
| `gh-
|
|
32
|
-
| `git-workflow.md` | PR workflow: always create as draft, one commit per review stage, never commit working docs or images |
|
|
33
|
-
| `
|
|
32
|
+
| `filesystem-search.md` | Every filesystem search names a scope; `es.exe`, `Glob`, `Grep`, and `Read` are equally sanctioned, and the `unscoped_search_blocker` denies a walk from a root |
|
|
33
|
+
| `gh-cli-conventions.md` | `--body-file` for every `gh` body; `--paginate --slurp` piped to external `jq` for every paginated list read |
|
|
34
|
+
| `git-workflow.md` | PR workflow: always create as draft, one commit per review stage, never commit working docs or images; carries the review-response protocol and a See-also block for its seven siblings |
|
|
35
|
+
| `hedging-claims.md` | State the evidence or name the claim unverified; the `hedging_language_blocker` Stop hook sends a hedged response back for a re-check |
|
|
34
36
|
| `long-horizon-autonomy.md` | Autonomous-run behaviors: act on what you have, do not end on a promise, delegate and keep working |
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `no-
|
|
38
|
-
| `no-inline-destructive-literals.md` | No destructive-command literals in Bash tool command strings, even as data |
|
|
39
|
-
| `no-justification-noise.md` | Markdown states facts a reader can act on; cut a present-tense sentence that only justifies a stated choice or restates a gain the reader already works out from the behavior or from a rule enforced elsewhere |
|
|
40
|
-
| `env-var-table-code-drift.md` | Every env-var summary table row in a `.md` file names a code file whose source references the variable |
|
|
37
|
+
| `measurement-denominators.md` | Every reported count names what it scanned and carries its denominator; a rate needs two runs; the consumer cross-checks the count against the scope its denominator names |
|
|
38
|
+
| `nas-ssh-invocation.md` | Reach the NAS through the paramiko-backed `nas_ssh_key.py` runner, which signs in-process; every ssh-family client reads the key through file permissions, refuses it, and stalls an unattended run on a password prompt |
|
|
39
|
+
| `no-cross-skill-duplicate-helpers.md` | Within one skill a duplicated helper is blocked; across two skill folders a small self-contained copy is a sanctioned isolation tradeoff that draws a non-blocking advisory naming the source skill |
|
|
41
40
|
| `orphan-css-class.md` | Every `class="..."` attribute in Python-generated markup has a matching selector in the `<style>` block |
|
|
42
|
-
| `package-inventory-stale-entry.md` | A new production code file added to a directory carries an entry in that directory's `README.md`/`CLAUDE.md` file inventory |
|
|
43
41
|
| `paired-test-coverage.md` | A public function omitted by a module's established paired test suite must get a behavioral test |
|
|
44
42
|
| `parallel-tools.md` | Make all independent tool calls in a single response |
|
|
45
43
|
| `plain-illustrative-docstrings.md` | Public docstring narrative reads plainly and shows behavior with a diagram block (a `::` example or a doctest), painting a concrete scene a general developer follows on first read; a run-on backstop hook, a prose-wall backstop hook, and Category O9 audit enforce it |
|
|
@@ -48,13 +46,10 @@ Rule files installed into `~/.claude/rules/` by `bin/install.mjs`. A rule withou
|
|
|
48
46
|
| `proof-of-work-pr-comments.md` | Every PR carries one five-part proof-of-work comment before it leaves draft; the `pr_description_enforcer` hook audits proof-shaped comments and gates `gh pr ready` |
|
|
49
47
|
| `re-stage-before-commit.md` | Stage the files edited this session before `git commit`; the session edit stage gate denies a commit that leaves a tracked session edit unstaged, with `-a`, a pathspec, a preceding `git add`, and `# partial-commit` as escapes |
|
|
50
48
|
| `research-mode.md` | Three anti-hallucination constraints: say "I don't know", verify with citations, quote for factual grounding |
|
|
51
|
-
| `
|
|
52
|
-
| `self-contained-docs.md` | Every document is fully self-contained; no references to the conversation that produced it |
|
|
53
|
-
| `shell-invocation-policy.md` | All Windows shell commands use `pwsh`; `Audit-ShellPolicy.ps1` reports the non-`pwsh` forms in the `settings.json` permission rules and `Migrate-ShellPolicy.ps1` rewrites them to `pwsh`, both run on demand, not as a live gate |
|
|
54
|
-
| `tdd.md` | Test-driven development: red → green → refactor, no production code before a failing test |
|
|
49
|
+
| `shell-invocation.md` | Windows shell commands run through `pwsh`; no `$(...)`, backtick, or process substitution in a Bash tool command |
|
|
55
50
|
| `testing.md` | Test quality and infrastructure standards |
|
|
56
51
|
| `vault-context.md` | Search Obsidian vault for prior sessions and decisions before substantive project work |
|
|
57
|
-
| `verified-commit-gate-skip.md` |
|
|
52
|
+
| `verified-commit-gate-skip.md` | Code-verifier review is optional; commits and pushes use the checks selected for the task |
|
|
58
53
|
| `verify-before-asking.md` | Answer questions by inspecting files or running tools before asking; recalled facts expire until re-checked this session |
|
|
59
54
|
| `verify-runtime-state.md` | A "component is fine / not at fault" verdict rests on a live probe this session, never code reading or prior-session memory |
|
|
60
55
|
| `windows-filesystem-safe.md` | Use safe `rmtree` patterns on Windows; `mkdirSync` with `recursive: true` on possibly-existing paths |
|
|
@@ -63,4 +58,4 @@ Rule files installed into `~/.claude/rules/` by `bin/install.mjs`. A rule withou
|
|
|
63
58
|
|
|
64
59
|
## Hook enforcement
|
|
65
60
|
|
|
66
|
-
Rules marked with ⚡ in
|
|
61
|
+
Rules marked with ⚡ in `~/.claude/docs/CODE_RULES.md` are backed by a blocking hook in `hooks/blocking/`. Rules without a hook are judgment-based and enforced via audit rubrics (`audit-rubrics/`).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# Agent Spawn
|
|
1
|
+
# Agent Spawn Guidance
|
|
2
2
|
|
|
3
|
-
Before any Agent or Task tool spawn
|
|
3
|
+
Before any Agent or Task tool spawn, check context sufficiency: you can name the files involved, the constraints, and what success looks like, and the task is unambiguous. When you cannot, investigate or ask the user first — a spawn with incomplete context returns work you throw away.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
2. **Craft the prompt with `/prompt-generator`** — feed it the goal, the target files from step 1, the constraints, the output format, and the acceptance criteria; use its output as the agent's `prompt`.
|
|
7
|
-
3. **Spawn** with that structured prompt.
|
|
5
|
+
Ask each research subagent for precise file-and-line answers. A finding that names a path and a line number is one a reader can check.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
`/prompt-generator` is recommended for a complex spawn, or one whose output the user reads directly: feed it the goal, the target files, the constraints, the output format, and the acceptance criteria, then use its output as the agent's `prompt`. Inside a scoped autonomous run, an inline structured prompt you write yourself is fine.
|
|
8
|
+
|
|
9
|
+
Full step detail and the relationship to other rules: `@~/.claude/docs/agent-spawn-protocol.md`.
|
|
@@ -59,7 +59,7 @@ A structural hook is the wrong tool here. "Is this a corollary?" and "would this
|
|
|
59
59
|
|
|
60
60
|
| Rule | Role |
|
|
61
61
|
|---|---|
|
|
62
|
-
| `
|
|
62
|
+
| `code-standards.md` | Points at CODE_RULES §8: write a failing test before production code |
|
|
63
63
|
| `testing.md` | Mocks and test infrastructure standards |
|
|
64
64
|
| `paired-test-coverage.md` | Every public function in an established suite gets a behavioral test |
|
|
65
65
|
| `anti-corollary-tests.md` | Each test carries information; no corollary matrices; no suite that only matches the dead default |
|
package/rules/bdd.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
1. **Deliberate Discovery** — Reduce uncertainty before code; surface what you do not know (Smart & Molak §5.4).
|
|
14
14
|
2. **Illustrate** — Explore goals, constraints, and concrete examples in chat; "given … when … then …" style outcomes.
|
|
15
15
|
3. **Formulate** — Express behavior as narrow **"should …"** specifications the user can approve.
|
|
16
|
-
4. **Automate** —
|
|
16
|
+
4. **Automate** — Build each formulated behavior through the TDD inner loop: CODE_RULES §8, red-green-refactor, canonical as stated in [`code-standards.md`](code-standards.md).
|
|
17
17
|
|
|
18
18
|
Conversation is the essential practice: if discovery is skipped, structured formats do not rescue the workflow (Minimal BDD).
|
|
19
19
|
|
|
@@ -15,13 +15,19 @@ Source: [Anthropic — Reduce file creation in agentic coding](https://platform.
|
|
|
15
15
|
- Leave the working directory cleaner than you found it.
|
|
16
16
|
- If a file was created at the user's explicit request (not as a byproduct of your process), leave it in place.
|
|
17
17
|
|
|
18
|
+
## Exceptions to the removal duty
|
|
19
|
+
|
|
20
|
+
Three kinds of file are already ephemeral and need no explicit removal:
|
|
21
|
+
|
|
22
|
+
- A file under the OS temporary root.
|
|
23
|
+
- A file under `$CLAUDE_JOB_DIR`, which the harness clears with the job.
|
|
24
|
+
- A child agent's scratch file, which the parent removes at teardown.
|
|
25
|
+
|
|
26
|
+
Use an allowed removal form for everything else: [`destructive-commands.md`](destructive-commands.md) names them.
|
|
27
|
+
|
|
18
28
|
## What counts as temporary
|
|
19
29
|
|
|
20
30
|
- Scripts written to test a hypothesis or run a one-off check
|
|
21
31
|
- Debug output files, log dumps, or intermediate data exports
|
|
22
32
|
- Helper files created to work around tool limitations
|
|
23
33
|
- Any file the user did not ask for and would not expect to find after the task
|
|
24
|
-
|
|
25
|
-
## Why
|
|
26
|
-
|
|
27
|
-
Temporary files accumulate across sessions and clutter the project root. Latest models sometimes use files as scratchpads during iteration, and these leftovers confuse both the user and future sessions if not cleaned up.
|
package/rules/code-standards.md
CHANGED
|
@@ -4,3 +4,10 @@
|
|
|
4
4
|
> This is the single source of truth for code standards. Non-negotiable.
|
|
5
5
|
|
|
6
6
|
`CODE_RULES.md` (`~/.claude/docs/CODE_RULES.md`) is the compact reference for every standard: self-documenting names, centralized configuration, constant reuse, no magic literals, full words, complete type hints, required-vs-optional parameters, construction logic in the model, temporary-code `TODO:` markers, behavior-first component names, and TDD.
|
|
7
|
+
|
|
8
|
+
Two standards live there in full and nowhere else:
|
|
9
|
+
|
|
10
|
+
- **TDD** — CODE_RULES §8 is canonical: red, green, refactor, with no production code before a failing test.
|
|
11
|
+
- **Right-sized engineering** — CODE_RULES §7 is canonical: functions over classes, concrete over abstract, an abstraction added at the commit that introduces its second concrete implementation.
|
|
12
|
+
|
|
13
|
+
BDD is the outer process and TDD is the inner loop: [`bdd.md`](bdd.md) discovers and formulates the behavior a feature needs, then each formulated behavior is built through the CODE_RULES §8 red-green-refactor cycle.
|
|
@@ -14,8 +14,4 @@ Proceed with edits, file modifications, or implementations only when the user ex
|
|
|
14
14
|
- If the user describes a problem, investigate and recommend. Do not jump to implementation.
|
|
15
15
|
- If the user says "do it", "go ahead", "make the change", or similarly explicit language, proceed with action.
|
|
16
16
|
- Once the user has explicitly asked and you have what you need, act — do not re-open settled facts or decisions, and do not re-survey options you will not pursue. This rule governs the ambiguous case; the clear case belongs to `long-horizon-autonomy`.
|
|
17
|
-
- When in doubt,
|
|
18
|
-
|
|
19
|
-
## Why
|
|
20
|
-
|
|
21
|
-
Acting prematurely wastes effort and round-trips when the user wanted a different approach. Exploring first produces better outcomes than committing early. This is especially important with models that have a strong action bias.
|
|
17
|
+
- When in doubt, put the decision to the user through `AskUserQuestion` with two options: show the approach first, or make the change now.
|
package/rules/context7.md
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: true
|
|
3
|
-
---
|
|
4
|
-
|
|
5
1
|
When working with libraries, frameworks, or APIs — use Context7 MCP to fetch current documentation instead of relying on training data. This includes setup questions, code generation, API references, and anything involving specific packages.
|
|
6
2
|
|
|
7
3
|
## Steps
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Destructive Commands in Bash
|
|
2
|
+
|
|
3
|
+
The `destructive_command_blocker` PreToolUse hook watches every Bash-tool command and matches destructive patterns (`rm -rf`, `git reset --hard`, `dd`, `mkfs`, `chmod -R`, fork bombs) as raw text, with no quote-awareness. Anything it cannot prove safe falls through to a confirmation prompt. In a background or auto-mode run no human can answer that prompt, so the call stalls.
|
|
4
|
+
|
|
5
|
+
Two consequences follow: use an allowed removal form, and keep a destructive literal out of the command string even when it rides only as data.
|
|
6
|
+
|
|
7
|
+
## Removal forms that never prompt
|
|
8
|
+
|
|
9
|
+
- **Scratch and probe files** — the PowerShell tool: `Remove-Item -Recurse -Force -Confirm:$false <absolute path>`. The hook watches only the Bash tool, so a PowerShell removal never reaches it.
|
|
10
|
+
- **Worktrees** — `git worktree remove --force <path>`. This matches no destructive pattern.
|
|
11
|
+
- **Tracked files** — `git rm <path>`, which records the deletion in the index.
|
|
12
|
+
- **Bash `rm` when unavoidable** — one standalone `rm`, absolute literal paths, no chaining, no globs, every target inside the ephemeral namespace below.
|
|
13
|
+
|
|
14
|
+
## The ephemeral namespace the hook auto-allows
|
|
15
|
+
|
|
16
|
+
An `rm` is auto-allowed when it is a single invocation and every target resolves inside one of:
|
|
17
|
+
|
|
18
|
+
- The OS temporary root (`tempfile.gettempdir()`).
|
|
19
|
+
- A path rooted at `/tmp` or `/temp`, drive-letter tolerant.
|
|
20
|
+
- A path holding a `/worktrees/` or `/worktree/` segment, or a directory git reports inside a worktree admin directory.
|
|
21
|
+
- `~/.claude`.
|
|
22
|
+
|
|
23
|
+
A bare ephemeral root (`/tmp`, the OS temp root itself, a bare directory named `worktrees` or `worktree`) is refused, so a single stray argument cannot wipe the whole namespace.
|
|
24
|
+
|
|
25
|
+
Four environment variables resolve inside a target token: `TEMP`, `TMP`, `TMPDIR`, and `CLAUDE_JOB_DIR`. Any other variable, a `$(...)` or backtick expansion, or a brace glob makes the target unresolvable and the command prompts. A `$CLAUDE_JOB_DIR` path is auto-allowed only when it resolves into the namespace above — the variable is readable, not a blanket pass. Set `CLAUDE_DESTRUCTIVE_DISABLE_EPHEMERAL_AUTO_ALLOW` to a truthy value to turn the whole auto-allow off.
|
|
26
|
+
|
|
27
|
+
A file left in the OS temp directory or under `$CLAUDE_JOB_DIR` is cleaned by the harness and needs no explicit removal — see the exception clause in [`cleanup-temp-files.md`](cleanup-temp-files.md).
|
|
28
|
+
|
|
29
|
+
## Keep destructive literals out of the command string
|
|
30
|
+
|
|
31
|
+
A destructive literal carried only as data — a commit message, a PR or issue body, an echoed string, a `python -c` / `node -e` / `awk` argument, a heredoc — trips the same raw-text match even though the shell never executes it.
|
|
32
|
+
|
|
33
|
+
- Bodies that describe destructive-command behavior go in a file passed by path: `git commit -F <file>`, `gh … --body-file <file>` (see [`gh-cli-conventions.md`](gh-cli-conventions.md)). Never `git commit -m` or `gh … -b`.
|
|
34
|
+
- To exercise or verify the blocker, or any hook, run the committed test suite (`python -m pytest <test_file>`), which passes the command strings as in-language data. Never an inline `python -c` harness.
|
|
35
|
+
|
|
36
|
+
## Every subagent prompt carries the rule
|
|
37
|
+
|
|
38
|
+
A prompt-delivered directive reaches only the agent that gets it. An agent that spawns its own workers — review lenses, fix agents, verifiers — copies this line into every subagent prompt it issues, so a grandchild cleaning up its own probe file uses an allowed form:
|
|
39
|
+
|
|
40
|
+
> Never use bash rm in any form. Delete scratch/probe files with the PowerShell tool (Remove-Item -Recurse -Force -Confirm:$false <absolute path>), or leave them in the OS temp dir; remove worktrees only via git worktree remove --force.
|
|
41
|
+
|
|
42
|
+
Prefer that a child leaves its scratch files for the parent to remove at teardown.
|
|
43
|
+
|
|
44
|
+
## Sibling rules
|
|
45
|
+
|
|
46
|
+
- [`cleanup-temp-files.md`](cleanup-temp-files.md) — which scratch files a task removes, and which it leaves.
|
|
47
|
+
- [`windows-filesystem-safe.md`](windows-filesystem-safe.md) — the safe `rmtree` / `force_rmtree` patterns for read-only Windows files.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.md"
|
|
4
|
+
- "**/*.py"
|
|
5
|
+
- "**/*.mjs"
|
|
6
|
+
- "**/*.js"
|
|
7
|
+
- "**/*.ts"
|
|
8
|
+
- "**/*.ps1"
|
|
9
|
+
- "**/*.sh"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Documentation Inventory Integrity
|
|
13
|
+
|
|
14
|
+
A doc that inventories code is a contract: a reader trusts the listing to map the directory, trusts a shown command to run, and trusts a table row to name the file that reads the variable. Three hooks hold the three inventory shapes in step with the code.
|
|
15
|
+
|
|
16
|
+
## 1. A per-directory `CLAUDE.md` names files that exist
|
|
17
|
+
|
|
18
|
+
Every bare filename a per-directory `CLAUDE.md` names points at a file in the subtree that `CLAUDE.md` describes — both the filenames its table cells list and the scripts its fenced run commands invoke (`python script.py`). Add the row and the run command in the change that adds the file; drop both in the change that removes it.
|
|
19
|
+
|
|
20
|
+
`claude_md_orphan_file_blocker.py` (PreToolUse on Write|Edit|MultiEdit of any `CLAUDE.md`) reads the content the tool would leave on disk. For an Edit or MultiEdit it reconstructs the post-edit file and notes which orphans the file already held, so a pre-existing orphan on an untouched line is excluded and only an orphan the edit introduces is reported; when the existing file cannot be read it scans the raw `new_string` fragments instead.
|
|
21
|
+
|
|
22
|
+
It collects two kinds of reference:
|
|
23
|
+
|
|
24
|
+
- **Table cells** — the first column of each markdown table row **outside** a fenced code block, keeping cells that name a bare filename in backticks, with no path separator, not a slash-command, ending in a known extension (`.py`, `.md`, `.json`, `.mjs`, `.js`, `.ts`, `.ps1`, `.cmd`, `.ahk`, `.yml`, `.yaml`, `.sh`, `.txt`, `.cfg`, `.toml`, `.ini`).
|
|
25
|
+
- **Run commands** — each line **inside** a fenced code block that invokes an interpreter (`python`, `python.exe`, `python3`, `node`, `pwsh`, `powershell`, `bash`, `sh`, `ruby`, `perl`) on a script, taking that script's basename when it ends in `.py`, `.mjs`, `.js`, `.ts`, `.ps1`, `.sh`, `.rb`, or `.pl`.
|
|
26
|
+
|
|
27
|
+
A fenced *table row* is an example and contributes nothing; a fenced *run command* is a contract the reader runs and is checked. The write is blocked when a collected filename exists nowhere under the scan root — the `CLAUDE.md` directory's parent, covering the directory, its subdirectories, and its siblings. A filesystem error that halts the subtree walk fails open.
|
|
28
|
+
|
|
29
|
+
The check stays quiet for a target that is not a `CLAUDE.md`, for a cell holding a path, a subdirectory ending in `/`, or a slash-command, for a table row inside a fence, for an inline `python x.py` mention outside a fence, and for a table naming an explicit relative-path source (a `../` token), which documents files outside the subtree by design.
|
|
30
|
+
|
|
31
|
+
## 2. A package inventory names each new production file
|
|
32
|
+
|
|
33
|
+
A package directory that documents its own files in a `README.md` Layout table, a `CLAUDE.md` "Key files" list, or a skill `SKILL.md` Layout table keeps that inventory in step with the directory. A new production file in such a directory gets its entry — a table row or a list bullet naming the file in backticks and saying what it does — in the same change.
|
|
34
|
+
|
|
35
|
+
`package_inventory_stale_blocker.py` (PreToolUse on Write) blocks a new production file whose basename appears in no present inventory and names the fix. A skill `SKILL.md` Layout table that maps `scripts/` counts as the inventory for files in that subdirectory.
|
|
36
|
+
|
|
37
|
+
Two free-prose slices stay with judgment and belong in the same change:
|
|
38
|
+
|
|
39
|
+
1. **Purpose / scope sentence.** When the new module adds a responsibility the package `## Purpose` (or the parent inventory's one-line summary of the subdirectory) omits, broaden that sentence to name it. A hook cannot derive a module's responsibility from its filename.
|
|
40
|
+
2. **Per-file description clause.** When a file gains a responsibility the inventory's em-dash description omits — a new public function, a new module-level constant — broaden the clause to name it. The gate checks only that the basename appears once and never reads the description. Constants modules (`*_constants.py`, or any `.py` directly inside `config/`) are the common shape: the clause that lands in the module docstring lands in the inventory description in the same change. The gate fires on Write of a new file and skips files directly inside `config/`, so an Edit adding a constant to an existing config module matches neither path.
|
|
41
|
+
|
|
42
|
+
This is the `category-o-docstring-vs-impl-drift` (O8) orphaned-doc-claim shape applied to a package inventory.
|
|
43
|
+
|
|
44
|
+
## 3. An env-var table row names a file that reads the variable
|
|
45
|
+
|
|
46
|
+
Every row in an env-var summary table pairs an UPPER_SNAKE variable with a code-file path that reads it — written as `` | `GOOGLE_APPLICATION_CREDENTIALS` | `auth/google_auth.py` | … | ``. When a code change removes the last read of a variable from a file, the same change drops or corrects the row naming that file.
|
|
47
|
+
|
|
48
|
+
`env_var_table_code_drift_blocker.py` (PreToolUse on Write|Edit|MultiEdit of `.md`) blocks a row whose named code file exists yet never references the variable, and names the fix. For an Edit, drift a file already held on an untouched row is excluded; a row whose code file resolves nowhere stays quiet, since the hook cannot prove the drift.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Documentation Prose Cuts
|
|
2
|
+
|
|
3
|
+
Prose a reader acts on states settled facts, decisions, and behavior directly. Four sentence shapes carry no fact the reader can act on. Cut each on sight, in `.md` files, in code comments, and in docstrings alike.
|
|
4
|
+
|
|
5
|
+
A detail earns its place by mattering now, or by being timeless enough to matter for future work. Everything else is noise.
|
|
6
|
+
|
|
7
|
+
## The four cut shapes
|
|
8
|
+
|
|
9
|
+
### 1. Exclusion claims — state the claim itself
|
|
10
|
+
|
|
11
|
+
Establishing a claim by naming what it is not, or by walking rejected candidates to reach the answer. Replace with the claim and the evidence behind it.
|
|
12
|
+
|
|
13
|
+
> This is not Option A from the original framing.
|
|
14
|
+
|
|
15
|
+
Cut it. Write what the approach is.
|
|
16
|
+
|
|
17
|
+
### 2. Justification sentences — state the fact, drop the payoff
|
|
18
|
+
|
|
19
|
+
A sentence whose only job is to say why a stated choice is good, or to restate a gain the reader already works out from the behavior around it or from a rule enforced elsewhere.
|
|
20
|
+
|
|
21
|
+
> The lenses point at this file and read it when they run; they do not carry its text in their spawn prompts, so the checklist stays out of the per-round token budget.
|
|
22
|
+
|
|
23
|
+
Keep the first clause. Cut the tail — the reader reaches it alone.
|
|
24
|
+
|
|
25
|
+
For each sentence, ask: **does it state a fact the reader can act on that they could not already work out from the behavior around it?** If no, cut it.
|
|
26
|
+
|
|
27
|
+
A rule's one-line reason stated in present behavior stays — `--jq` runs per page, so cross-page sorts give wrong results — because that reason names a fact the reader needs to pick the right call. A tradeoff or constraint the reader weighs before choosing a path stays too.
|
|
28
|
+
|
|
29
|
+
### 3. Conversation references — write for a reader who saw nothing
|
|
30
|
+
|
|
31
|
+
Every document stands alone. A reader with zero prior context understands every statement without the conversation that produced it.
|
|
32
|
+
|
|
33
|
+
| Shape | Example | Fix |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Options discussed in chat | "This is not Option A from the original framing" | State the decision on its own terms |
|
|
36
|
+
| "As discussed" / "as we decided" | "As discussed, we'll use embeddings" | "Sref matching uses sentence-transformer embeddings" |
|
|
37
|
+
| Pronouns pointing at chat | "This addresses the concerns raised earlier" | Name the concerns inline, or cut |
|
|
38
|
+
| Relative framing on unseen alternatives | "Instead of the three options considered" | State the chosen approach directly |
|
|
39
|
+
| Session sequencing | "After Round 3 we decided…" | State the decision as a fact |
|
|
40
|
+
|
|
41
|
+
Obsidian session logs are the exception — they are conversation-scoped on purpose.
|
|
42
|
+
|
|
43
|
+
### 4. Time references — describe current state only
|
|
44
|
+
|
|
45
|
+
Never reference removed implementations, old defaults, prior behaviors, or earlier contracts. A module or function docstring carries the same current-state-only contract as a `.md` file.
|
|
46
|
+
|
|
47
|
+
Comparisons to alternatives that still exist stay allowed ("use `--paginate --slurp | jq`, not `--jq` alone"), as do references to external defects that still exist (gh CLI #10459).
|
|
48
|
+
|
|
49
|
+
After writing, ask: read a year from now by someone who knew no earlier state, does every sentence still make sense? If a sentence only helps someone who knew an earlier state, cut it.
|
|
50
|
+
|
|
51
|
+
## Enforcement
|
|
52
|
+
|
|
53
|
+
- **Write-time.** `state_description_blocker` (PreToolUse on Write|Edit) blocks historical and comparative phrases in `.md` prose, code comments, and Python docstrings. A phrase wrapped in double quotes or backticks inside a docstring counts as a mention and is skipped. The denial names the matched phrases and shows a rewrite.
|
|
54
|
+
- **AI review.** The claude-dev-env repository's root `AGENTS.md`, the AI-review fan-out source, names the other three shapes as findings an agent applies to the `.md` lines a PR changes. No hook backs them: telling a justification sentence from a load-bearing one needs meaning a regex cannot read.
|
|
55
|
+
|
|
56
|
+
## Sibling rule
|
|
57
|
+
|
|
58
|
+
[`plain-language.md`](plain-language.md) governs word choice — heavy words swapped for everyday ones. This rule governs which sentences survive at all.
|