claude-dev-env 2.13.0 → 2.14.1
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.md +135 -0
- package/CLAUDE.md +1 -57
- package/_shared/AGENTS.md +15 -0
- package/_shared/CLAUDE.md +1 -15
- package/_shared/advisor/AGENTS.md +22 -0
- package/_shared/advisor/CLAUDE.md +1 -22
- package/_shared/pr-loop/AGENTS.md +27 -0
- package/_shared/pr-loop/CLAUDE.md +1 -27
- package/_shared/pr-loop/scripts/AGENTS.md +35 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -35
- package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +42 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +1 -42
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +26 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -26
- package/_shared/pr-loop/scripts/tests/AGENTS.md +44 -0
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -44
- package/_shared/process-tree/AGENTS.md +41 -0
- package/_shared/process-tree/CLAUDE.md +1 -41
- package/agents/AGENTS.md +26 -0
- package/agents/CLAUDE.md +1 -31
- package/agents/clean-coder.md +6 -6
- package/agents/test_agent_frontmatter.py +11 -11
- package/audit-rubrics/AGENTS.md +43 -0
- package/audit-rubrics/CLAUDE.md +1 -43
- package/audit-rubrics/category_rubrics/AGENTS.md +37 -0
- package/audit-rubrics/category_rubrics/CLAUDE.md +1 -37
- package/audit-rubrics/prompts/AGENTS.md +37 -0
- package/audit-rubrics/prompts/CLAUDE.md +1 -37
- package/bin/AGENTS.md +112 -0
- package/bin/CLAUDE.md +1 -112
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +6 -7
- package/bin/install.mjs +24 -5
- package/bin/install.test.mjs +34 -0
- package/commands/AGENTS.md +13 -0
- package/commands/CLAUDE.md +1 -23
- package/docs/AGENTS.md +32 -0
- package/docs/CLAUDE.md +1 -32
- package/docs/CODE_RULES.md +1 -1
- package/docs/imagegen.md +22 -0
- package/docs/references/AGENTS.md +17 -0
- package/docs/references/CLAUDE.md +1 -17
- package/hooks/AGENTS.md +31 -0
- package/hooks/CLAUDE.md +1 -31
- package/hooks/_gh_pr_author_swap_utils.py +14 -0
- package/hooks/advisory/AGENTS.md +16 -0
- package/hooks/advisory/CLAUDE.md +1 -16
- package/hooks/blocking/AGENTS.md +123 -0
- package/hooks/blocking/CLAUDE.md +1 -123
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +1 -28
- package/hooks/blocking/code_rules_banned_identifiers.py +1 -1
- package/hooks/blocking/code_rules_dead_module_constant.py +149 -37
- package/hooks/blocking/code_rules_probe_chains.py +6 -2
- package/hooks/blocking/config/AGENTS.md +10 -0
- package/hooks/blocking/config/CLAUDE.md +1 -10
- package/hooks/blocking/gh_pr_author_restore.py +7 -2
- package/hooks/blocking/intent_only_ending_blocker.py +11 -16
- package/hooks/blocking/inventory_intent_records/AGENTS.md +26 -0
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +1 -26
- package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +1 -26
- package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +1 -24
- package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +1 -30
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +171 -0
- package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
- package/hooks/blocking/test_gh_pr_author_restore.py +2 -1
- package/hooks/blocking/test_intent_only_ending_blocker.py +4 -0
- package/hooks/diagnostic/AGENTS.md +75 -0
- package/hooks/diagnostic/CLAUDE.md +1 -43
- package/hooks/diagnostic/hook_log_extractor.py +2 -2
- package/hooks/diagnostic/hook_log_init.py +2 -2
- package/hooks/diagnostic/migrations/AGENTS.md +16 -0
- package/hooks/diagnostic/migrations/CLAUDE.md +1 -16
- package/hooks/diagnostic/queries/AGENTS.md +19 -0
- package/hooks/diagnostic/queries/CLAUDE.md +1 -19
- package/hooks/diagnostic/test_hook_log_extractor.py +2 -2
- package/hooks/git-hooks/AGENTS.md +31 -0
- package/hooks/git-hooks/CLAUDE.md +1 -31
- package/hooks/git-hooks/git_hooks_constants/AGENTS.md +21 -0
- package/hooks/git-hooks/git_hooks_constants/CLAUDE.md +1 -21
- package/hooks/git-hooks/git_hooks_constants/__init__.py +4 -6
- package/hooks/git-hooks/pre_push.py +47 -34
- package/hooks/git-hooks/test_pre_push.py +111 -7
- package/hooks/hooks_constants/AGENTS.md +104 -0
- package/hooks/hooks_constants/CLAUDE.md +1 -104
- package/hooks/hooks_constants/hook_log_extractor_constants.py +1 -2
- package/hooks/hooks_constants/task_list_loop_starter_constants.py +4 -3
- package/hooks/hooks_constants/working_style_prompt_constants.py +29 -20
- package/hooks/lifecycle/AGENTS.md +18 -0
- package/hooks/lifecycle/CLAUDE.md +1 -18
- package/hooks/observability/AGENTS.md +20 -0
- package/hooks/observability/CLAUDE.md +1 -20
- package/hooks/session/AGENTS.md +33 -0
- package/hooks/session/CLAUDE.md +1 -33
- package/hooks/session/gh_pr_author_session_cleanup.py +7 -2
- package/hooks/session/task_list_loop_starter.py +3 -3
- package/hooks/session/test_gh_pr_author_session_cleanup.py +2 -1
- package/hooks/session/test_task_list_loop_starter.py +10 -2
- package/hooks/session/test_working_style_prompt.py +9 -4
- package/hooks/validation/AGENTS.md +20 -0
- package/hooks/validation/CLAUDE.md +1 -20
- package/hooks/validators/AGENTS.md +51 -0
- package/hooks/validators/CLAUDE.md +1 -51
- package/hooks/validators/health_check.py +52 -19
- package/hooks/validators/python_style_checks.py +1 -1
- package/hooks/validators/test_health_check.py +112 -27
- package/hooks/workflow/AGENTS.md +16 -0
- package/hooks/workflow/CLAUDE.md +1 -16
- package/installable-surfaces.manifest.json +1 -0
- package/output-styles/AGENTS.md +15 -0
- package/output-styles/CLAUDE.md +1 -15
- package/package.json +2 -1
- package/rules/AGENTS.md +61 -0
- package/rules/CLAUDE.md +1 -61
- package/rules/code-standards.md +5 -9
- package/rules/doc-prose-cuts.md +1 -1
- package/scripts/AGENTS.md +62 -0
- package/scripts/CLAUDE.md +1 -62
- package/scripts/codex_compat_materializer.py +4 -0
- package/scripts/dev_env_scripts_constants/AGENTS.md +21 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -21
- package/scripts/sync_to_cursor/AGENTS.md +23 -0
- package/scripts/sync_to_cursor/CLAUDE.md +1 -23
- package/scripts/test_active_capability_references.py +2 -2
- package/scripts/test_validate_instruction_pairs.py +90 -0
- package/scripts/test_verify_installable_package.py +5 -1
- package/scripts/tests/AGENTS.md +33 -0
- package/scripts/tests/CLAUDE.md +1 -33
- package/scripts/tests/test_codex_compat_materializer.py +36 -1
- package/scripts/validate_instruction_pairs.py +183 -0
- package/skills/AGENTS.md +72 -0
- package/skills/CLAUDE.md +1 -72
- package/skills/_shared/AGENTS.md +44 -0
- package/skills/_shared/CLAUDE.md +1 -44
- package/skills/_shared/advisor/AGENTS.md +9 -0
- package/skills/_shared/advisor/CLAUDE.md +1 -9
- package/skills/_shared/pr-loop/AGENTS.md +50 -0
- package/skills/_shared/pr-loop/CLAUDE.md +1 -50
- package/skills/_shared/pr-loop/prompts/AGENTS.md +9 -0
- package/skills/_shared/pr-loop/prompts/CLAUDE.md +1 -9
- package/skills/_shared/pr-loop/scripts/AGENTS.md +35 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -35
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +24 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -24
- package/skills/anthropic-plan/AGENTS.md +34 -0
- package/skills/anthropic-plan/CLAUDE.md +1 -34
- package/skills/anthropic-plan/SKILL.md +1 -1
- package/skills/anthropic-plan/scripts/AGENTS.md +11 -0
- package/skills/anthropic-plan/scripts/CLAUDE.md +1 -11
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +16 -0
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/CLAUDE.md +1 -16
- package/skills/anthropic-plan/templates/AGENTS.md +13 -0
- package/skills/anthropic-plan/templates/CLAUDE.md +1 -13
- package/skills/anthropic-plan/test_skill_contract.py +8 -6
- package/skills/anthropic-plan/workflow/AGENTS.md +14 -0
- package/skills/anthropic-plan/workflow/CLAUDE.md +1 -14
- package/skills/auditing-claude-config/AGENTS.md +21 -0
- package/skills/auditing-claude-config/CLAUDE.md +1 -21
- package/skills/autoconverge/AGENTS.md +36 -0
- package/skills/autoconverge/CLAUDE.md +1 -36
- package/skills/autoconverge/reference/AGENTS.md +16 -0
- package/skills/autoconverge/reference/CLAUDE.md +1 -16
- package/skills/autoconverge/workflow/AGENTS.md +26 -0
- package/skills/autoconverge/workflow/CLAUDE.md +1 -26
- package/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +16 -0
- package/skills/autoconverge/workflow/autoconverge_report_constants/CLAUDE.md +1 -16
- package/skills/bugteam/AGENTS.md +30 -0
- package/skills/bugteam/CLAUDE.md +1 -30
- package/skills/bugteam/reference/AGENTS.md +20 -0
- package/skills/bugteam/reference/CLAUDE.md +1 -20
- package/skills/bugteam/reference/obstacles/AGENTS.md +24 -0
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -24
- package/skills/bugteam/scripts/AGENTS.md +30 -0
- package/skills/bugteam/scripts/CLAUDE.md +1 -30
- package/skills/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +18 -0
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +1 -18
- package/skills/closeout/reference/handoff-prompt-template.md +1 -1
- package/skills/codex-review/AGENTS.md +46 -0
- package/skills/codex-review/CLAUDE.md +1 -46
- package/skills/codex-review/reference/AGENTS.md +15 -0
- package/skills/codex-review/reference/CLAUDE.md +1 -15
- package/skills/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +1 -18
- package/skills/codex-review/test_skill_scaffold.py +3 -3
- package/skills/copilot-review/AGENTS.md +18 -0
- package/skills/copilot-review/CLAUDE.md +1 -18
- package/skills/everything-search/AGENTS.md +17 -0
- package/skills/everything-search/CLAUDE.md +1 -17
- package/skills/fresh-branch/AGENTS.md +14 -0
- package/skills/fresh-branch/CLAUDE.md +1 -14
- package/skills/grok-spawn/AGENTS.md +28 -0
- package/skills/grok-spawn/CLAUDE.md +1 -28
- package/skills/pr-converge/AGENTS.md +32 -0
- package/skills/pr-converge/CLAUDE.md +1 -32
- package/skills/pr-converge/pr_converge_skill_constants/AGENTS.md +26 -0
- package/skills/pr-converge/pr_converge_skill_constants/CLAUDE.md +1 -26
- package/skills/pr-converge/reference/AGENTS.md +28 -0
- package/skills/pr-converge/reference/CLAUDE.md +1 -28
- package/skills/pr-converge/reference/obstacles/AGENTS.md +23 -0
- package/skills/pr-converge/reference/obstacles/CLAUDE.md +1 -23
- package/skills/pr-converge/scripts/AGENTS.md +46 -0
- package/skills/pr-converge/scripts/CLAUDE.md +1 -46
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -18
- package/skills/pr-converge/workflows/AGENTS.md +16 -0
- package/skills/pr-converge/workflows/CLAUDE.md +1 -16
- package/skills/prototype/SKILL.md +1 -2
- package/skills/prototype/reference/honest-limitations.md +1 -1
- package/skills/prototype/reference/promotion-tasks.md +1 -1
- package/skills/prototype/workflows/promotion.md +1 -1
- package/skills/rebase/AGENTS.md +32 -0
- package/skills/rebase/CLAUDE.md +1 -32
- package/skills/recall/AGENTS.md +30 -0
- package/skills/recall/CLAUDE.md +1 -30
- package/skills/remember/AGENTS.md +31 -0
- package/skills/remember/CLAUDE.md +1 -31
- package/skills/reviews/SKILL.md +1 -1
- package/skills/session-log/AGENTS.md +32 -0
- package/skills/session-log/CLAUDE.md +1 -32
- package/skills/session-tidy/AGENTS.md +36 -0
- package/skills/session-tidy/CLAUDE.md +1 -36
- package/skills/skill-builder/AGENTS.md +49 -0
- package/skills/skill-builder/CLAUDE.md +1 -49
- package/skills/skill-builder/references/AGENTS.md +25 -0
- package/skills/skill-builder/references/CLAUDE.md +1 -25
- package/skills/skill-builder/references/deterministic-elements.md +1 -1
- package/skills/skill-builder/templates/AGENTS.md +14 -0
- package/skills/skill-builder/templates/CLAUDE.md +1 -14
- package/skills/skill-builder/workflows/AGENTS.md +19 -0
- package/skills/skill-builder/workflows/CLAUDE.md +1 -19
- package/skills/task-build/AGENTS.md +29 -0
- package/skills/task-build/CLAUDE.md +1 -29
- package/skills/update/AGENTS.md +38 -0
- package/skills/update/CLAUDE.md +1 -38
- package/system-prompts/AGENTS.md +17 -0
- package/system-prompts/CLAUDE.md +1 -17
- package/system-prompts/software-engineer.xml +3 -3
- package/agents/caveman.md +0 -73
- package/agents/clasp-deployment-orchestrator.md +0 -608
- package/agents/code-advisor.md +0 -23
- package/agents/deep-research.md +0 -152
- package/agents/docs-agent.md +0 -85
- package/commands/commit.md +0 -28
- package/commands/docupdate.md +0 -322
- package/commands/hook-log-extract.md +0 -70
- package/commands/hook-log-init.md +0 -76
- package/commands/implement.md +0 -102
- package/commands/plan.md +0 -14
- package/commands/pr-comments.md +0 -47
- package/commands/review-plan.md +0 -5
- package/commands/right-size.md +0 -15
- package/commands/sum.md +0 -30
- package/skills/imagegen/SKILL.md +0 -21
- package/skills/imagegen/scripts/config/__init__.py +0 -1
- package/skills/imagegen/scripts/config/constants.py +0 -28
- package/skills/imagegen/scripts/imagegen.py +0 -45
- package/skills/imagegen/scripts/imagegen_core.py +0 -528
- package/skills/imagegen/scripts/test_imagegen.py +0 -204
|
@@ -7,24 +7,33 @@ __all__ = [
|
|
|
7
7
|
]
|
|
8
8
|
|
|
9
9
|
WORKING_STYLE_PROMPT = (
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
10
|
+
"Document each task in a location that remains easy to find later. Keep a "
|
|
11
|
+
"running scratch text ledger as you work. Write in plain English. Keep responses "
|
|
12
|
+
"focused, brief, and concise. Keep disclaimers and caveats short while giving "
|
|
13
|
+
"the main answer most of the response. Give a high-level explanation by default "
|
|
14
|
+
"and provide depth when the request calls for it. "
|
|
15
|
+
"Before your first tool call, state your next action in one sentence. While "
|
|
16
|
+
"working, give brief updates when you find important information or change "
|
|
17
|
+
"direction. Finish with the outcome in the first sentence, then provide "
|
|
18
|
+
"supporting detail for readers who want it. "
|
|
19
|
+
"Match written-document length to the task. Cover the substance and keep every "
|
|
20
|
+
"section, summary, and phrase useful. "
|
|
21
|
+
"Deliver the requested work at its intended scope. Make routine judgment calls "
|
|
22
|
+
"yourself. Ask for direction when different interpretations would produce "
|
|
23
|
+
"materially different work. When a request seems mistaken or a better approach "
|
|
24
|
+
"exists, state the concern briefly and continue with the requested task. Finish "
|
|
25
|
+
"the complete task and keep actions within the requested scope. "
|
|
26
|
+
"When a request has multiple reasonable interpretations, state your understanding "
|
|
27
|
+
"and the assumptions that shape the work. Ask one focused clarification question "
|
|
28
|
+
"when the ambiguity changes the outcome, scope, audience, format, or risk. Use a "
|
|
29
|
+
"clearly stated low-risk assumption when the intended result remains stable. "
|
|
30
|
+
"Pause for the user's choice before making a high-impact decision. "
|
|
31
|
+
"Use positive prose throughout every generated text surface, including "
|
|
32
|
+
"documentation, comments, user-facing messages, sub-agent prompts, labels, "
|
|
33
|
+
"plans, and instructions. Write each point as one direct affirmative statement "
|
|
34
|
+
"that presents only the intended action, fact, reason, or outcome. Use current, "
|
|
35
|
+
"immediately relevant context and omit historical clutter. Use plain language, "
|
|
36
|
+
"full terms, and simple descriptive names that communicate their meaning "
|
|
37
|
+
"immediately. Keep all text concise, clear, direct, and focused entirely on "
|
|
38
|
+
"useful information."
|
|
30
39
|
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# hooks/lifecycle
|
|
2
|
+
|
|
3
|
+
Hooks that run at session or config-change boundaries rather than on individual tool calls.
|
|
4
|
+
|
|
5
|
+
## Key files
|
|
6
|
+
|
|
7
|
+
| File | Event | What it does |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `config_change_guard.py` | PostToolUse (Write/Edit on `settings.json`) | Counts hooks in the edited `settings.json` and logs any change to `~/.claude/cache/config-change-audit.log`; alerts when the hook count drops below the last known value |
|
|
10
|
+
| `enter_worktree_origin_prefetch.py` | PreToolUse (EnterWorktree) | Fetches origin's default branch before a worktree creation call, keeping the `refs/remotes/origin/<default-branch>` ref that `fresh` mode reads current; never blocks on fetch failure |
|
|
11
|
+
| `session_end_cleanup.py` | SessionEnd | Purges stale cache entries from `~/.claude/cache/` (entries older than the configured threshold) and old backup files |
|
|
12
|
+
| `test_config_change_guard.py` | — | Tests for `config_change_guard.py` |
|
|
13
|
+
| `test_enter_worktree_origin_prefetch.py` | — | Tests for `enter_worktree_origin_prefetch.py` |
|
|
14
|
+
|
|
15
|
+
## Conventions
|
|
16
|
+
|
|
17
|
+
- Constants for these hooks (stale-age threshold, cache directory, known-hook-count file, EnterWorktree prefetch tuning) live in `hooks_constants/session_env_cleanup_constants.py` and `hooks_constants/enter_worktree_prefetch_constants.py`.
|
|
18
|
+
- Tests run with `python -m pytest lifecycle/test_<name>.py`.
|
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Hooks that run at session or config-change boundaries rather than on individual tool calls.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Event | What it does |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `config_change_guard.py` | PostToolUse (Write/Edit on `settings.json`) | Counts hooks in the edited `settings.json` and logs any change to `~/.claude/cache/config-change-audit.log`; alerts when the hook count drops below the last known value |
|
|
10
|
-
| `enter_worktree_origin_prefetch.py` | PreToolUse (EnterWorktree) | Fetches origin's default branch before a worktree creation call, keeping the `refs/remotes/origin/<default-branch>` ref that `fresh` mode reads current; never blocks on fetch failure |
|
|
11
|
-
| `session_end_cleanup.py` | SessionEnd | Purges stale cache entries from `~/.claude/cache/` (entries older than the configured threshold) and old backup files |
|
|
12
|
-
| `test_config_change_guard.py` | — | Tests for `config_change_guard.py` |
|
|
13
|
-
| `test_enter_worktree_origin_prefetch.py` | — | Tests for `enter_worktree_origin_prefetch.py` |
|
|
14
|
-
|
|
15
|
-
## Conventions
|
|
16
|
-
|
|
17
|
-
- Constants for these hooks (stale-age threshold, cache directory, known-hook-count file, EnterWorktree prefetch tuning) live in `hooks_constants/session_env_cleanup_constants.py` and `hooks_constants/enter_worktree_prefetch_constants.py`.
|
|
18
|
-
- Tests run with `python -m pytest lifecycle/test_<name>.py`.
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# hooks/observability
|
|
2
|
+
|
|
3
|
+
PostToolUse hooks that record agent behavior for later review. These hooks do not block tool calls; they log or annotate them.
|
|
4
|
+
|
|
5
|
+
## Key files
|
|
6
|
+
|
|
7
|
+
| File | Event | What it does |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `instructions_loaded_logger.py` | PostToolUse (file load events) | Appends a JSONL record to `~/.claude/logs/instructions_loaded.jsonl` each time Claude Code loads a context file (CLAUDE.md, rules, skills), capturing the file path, load reason, memory type, and session ID |
|
|
10
|
+
| `session_file_edit_tracker.py` | PostToolUse (Write/Edit/MultiEdit) | Records the resolved absolute path of each file edited this session into a per-session JSON file under the system temp directory, for the session edit stage gate to read at commit time |
|
|
11
|
+
| `prose_matcher_advisory.py` | Advisory emit from prose matchers | Privacy-safe JSONL candidates (`matcher_id`, surface, context fingerprint, label) plus keep/narrow/drop/advisory classification against the sample floor |
|
|
12
|
+
| `test_instructions_loaded_logger.py` | — | Tests for `instructions_loaded_logger.py` |
|
|
13
|
+
| `test_session_file_edit_tracker.py` | — | Tests for `session_file_edit_tracker.py` |
|
|
14
|
+
| `test_prose_matcher_advisory.py` | — | Tests for advisory emit and precision classification |
|
|
15
|
+
|
|
16
|
+
## Conventions
|
|
17
|
+
|
|
18
|
+
- The log file is append-only; the hook creates the parent directory if needed.
|
|
19
|
+
- Errors during logging are caught and written as error records rather than propagated — the hook never blocks the tool call.
|
|
20
|
+
- Tests run with `python -m pytest observability/test_instructions_loaded_logger.py`.
|
|
@@ -1,20 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
PostToolUse hooks that record agent behavior for later review. These hooks do not block tool calls; they log or annotate them.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Event | What it does |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `instructions_loaded_logger.py` | PostToolUse (file load events) | Appends a JSONL record to `~/.claude/logs/instructions_loaded.jsonl` each time Claude Code loads a context file (CLAUDE.md, rules, skills), capturing the file path, load reason, memory type, and session ID |
|
|
10
|
-
| `session_file_edit_tracker.py` | PostToolUse (Write/Edit/MultiEdit) | Records the resolved absolute path of each file edited this session into a per-session JSON file under the system temp directory, for the session edit stage gate to read at commit time |
|
|
11
|
-
| `prose_matcher_advisory.py` | Advisory emit from prose matchers | Privacy-safe JSONL candidates (`matcher_id`, surface, context fingerprint, label) plus keep/narrow/drop/advisory classification against the sample floor |
|
|
12
|
-
| `test_instructions_loaded_logger.py` | — | Tests for `instructions_loaded_logger.py` |
|
|
13
|
-
| `test_session_file_edit_tracker.py` | — | Tests for `session_file_edit_tracker.py` |
|
|
14
|
-
| `test_prose_matcher_advisory.py` | — | Tests for advisory emit and precision classification |
|
|
15
|
-
|
|
16
|
-
## Conventions
|
|
17
|
-
|
|
18
|
-
- The log file is append-only; the hook creates the parent directory if needed.
|
|
19
|
-
- Errors during logging are caught and written as error records rather than propagated — the hook never blocks the tool call.
|
|
20
|
-
- Tests run with `python -m pytest observability/test_instructions_loaded_logger.py`.
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# hooks/session
|
|
2
|
+
|
|
3
|
+
SessionStart and SessionEnd hooks for per-session setup and cleanup: removing stale session and plugin-data directories at startup, detecting unregistered repositories, starting the session's task-list maintenance loop, injecting working-style guidance, and clearing PR-author swap state at shutdown.
|
|
4
|
+
|
|
5
|
+
## Key files
|
|
6
|
+
|
|
7
|
+
| File | Event | What it does |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `session_env_cleanup.py` | SessionStart | Removes the current session's pre-existing `~/.claude/session-env/<session_id>/` directory and prunes sibling entries older than the stale-age threshold. Prevents `EEXIST` errors from non-recursive `mkdir` calls in the Bash tool on Windows. |
|
|
10
|
+
| `gh_pr_author_session_cleanup.py` | SessionEnd | Clears any PR-author swap state left over from the current session |
|
|
11
|
+
| `session_edit_tracker_cleanup.py` | SessionStart, SessionEnd | Deletes the tracker file for the running Claude Code conversation from the system temp directory — at start for a clean slate and at end for a clean exit. A tracker is read only by the conversation that wrote it, so a live idle tracker is kept while a peer cleans up |
|
|
12
|
+
| `plugin_data_dir_cleanup.py` | SessionStart | Removes empty plugin data directories at startup to prevent `EEXIST` when Claude Code recreates them |
|
|
13
|
+
| `untracked_repo_detector.py` | SessionStart | Detects when the session cwd is inside a git repository that is not registered in `~/.claude/project-paths.json` and logs a warning |
|
|
14
|
+
| `task_list_loop_starter.py` | SessionStart | Emits an `additionalContext` directive telling Claude to keep the task list current on a 10-minute cadence, starting the `/loop` skill when one is not already running. Writes nothing and runs no tools itself. |
|
|
15
|
+
| `orchestrator_auto_starter.py` | SessionStart | Opt-in (`CLAUDE_ORCHESTRATOR_AUTO_STARTER_ENABLED`) consumer of the shared SessionStart injector; emits orchestrator skill context when enabled. Manual `/orchestrator` unchanged. |
|
|
16
|
+
| `issue_tracker_session_starter.py` | SessionStart | Opt-in (`CLAUDE_ISSUE_TRACKER_SESSION_STARTER_ENABLED`) and repository-gated (git root in `~/.claude/project-paths.json`) issue-tracker skill context. |
|
|
17
|
+
| `_path_setup.py` | — | Inserts the hooks directory on `sys.path` so SessionStart scripts import `hooks_constants` with top-level imports. |
|
|
18
|
+
| `test_orchestrator_auto_starter.py` | — | Tests for `orchestrator_auto_starter.py` |
|
|
19
|
+
| `test_issue_tracker_session_starter.py` | — | Tests for `issue_tracker_session_starter.py` |
|
|
20
|
+
| `working_style_prompt.py` | SessionStart | Emits an `additionalContext` block with the fixed working-style prompt (running ledger, plain English, outcome-first finish, scope discipline). Writes nothing and runs no tools itself. |
|
|
21
|
+
| `test_gh_pr_author_session_cleanup.py` | — | Tests for `gh_pr_author_session_cleanup.py` |
|
|
22
|
+
| `test_session_edit_tracker_cleanup.py` | — | Tests for `session_edit_tracker_cleanup.py` |
|
|
23
|
+
| `test_session_env_cleanup.py` | — | Tests for `session_env_cleanup.py` |
|
|
24
|
+
| `test_untracked_repo_detector.py` | — | Tests for `untracked_repo_detector.py` |
|
|
25
|
+
| `test_task_list_loop_starter.py` | — | Tests for `task_list_loop_starter.py` |
|
|
26
|
+
| `test__path_setup.py` | — | Tests for `_path_setup.py` |
|
|
27
|
+
| `test_working_style_prompt.py` | — | Tests for `working_style_prompt.py` |
|
|
28
|
+
|
|
29
|
+
## Conventions
|
|
30
|
+
|
|
31
|
+
- `session_env_cleanup.py` is Windows-specific in effect but safe to run on all platforms; it exits 0 when the target directory does not exist.
|
|
32
|
+
- Constants (stale-age threshold, directory names) live in `hooks_constants/session_env_cleanup_constants.py`.
|
|
33
|
+
- Tests run with `python -m pytest session/test_<name>.py`.
|
package/hooks/session/CLAUDE.md
CHANGED
|
@@ -1,33 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
SessionStart and SessionEnd hooks for per-session setup and cleanup: removing stale session and plugin-data directories at startup, detecting unregistered repositories, starting the session's task-list maintenance loop, injecting working-style guidance, and clearing PR-author swap state at shutdown.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Event | What it does |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `session_env_cleanup.py` | SessionStart | Removes the current session's pre-existing `~/.claude/session-env/<session_id>/` directory and prunes sibling entries older than the stale-age threshold. Prevents `EEXIST` errors from non-recursive `mkdir` calls in the Bash tool on Windows. |
|
|
10
|
-
| `gh_pr_author_session_cleanup.py` | SessionEnd | Clears any PR-author swap state left over from the current session |
|
|
11
|
-
| `session_edit_tracker_cleanup.py` | SessionStart, SessionEnd | Deletes the tracker file for the running Claude Code conversation from the system temp directory — at start for a clean slate and at end for a clean exit. A tracker is read only by the conversation that wrote it, so a live idle tracker is kept while a peer cleans up |
|
|
12
|
-
| `plugin_data_dir_cleanup.py` | SessionStart | Removes empty plugin data directories at startup to prevent `EEXIST` when Claude Code recreates them |
|
|
13
|
-
| `untracked_repo_detector.py` | SessionStart | Detects when the session cwd is inside a git repository that is not registered in `~/.claude/project-paths.json` and logs a warning |
|
|
14
|
-
| `task_list_loop_starter.py` | SessionStart | Emits an `additionalContext` directive telling Claude to keep the task list current on a 10-minute cadence, starting the `/loop` skill when one is not already running. Writes nothing and runs no tools itself. |
|
|
15
|
-
| `orchestrator_auto_starter.py` | SessionStart | Opt-in (`CLAUDE_ORCHESTRATOR_AUTO_STARTER_ENABLED`) consumer of the shared SessionStart injector; emits orchestrator skill context when enabled. Manual `/orchestrator` unchanged. |
|
|
16
|
-
| `issue_tracker_session_starter.py` | SessionStart | Opt-in (`CLAUDE_ISSUE_TRACKER_SESSION_STARTER_ENABLED`) and repository-gated (git root in `~/.claude/project-paths.json`) issue-tracker skill context. |
|
|
17
|
-
| `_path_setup.py` | — | Inserts the hooks directory on `sys.path` so SessionStart scripts import `hooks_constants` with top-level imports. |
|
|
18
|
-
| `test_orchestrator_auto_starter.py` | — | Tests for `orchestrator_auto_starter.py` |
|
|
19
|
-
| `test_issue_tracker_session_starter.py` | — | Tests for `issue_tracker_session_starter.py` |
|
|
20
|
-
| `working_style_prompt.py` | SessionStart | Emits an `additionalContext` block with the fixed working-style prompt (running ledger, plain English, outcome-first finish, scope discipline). Writes nothing and runs no tools itself. |
|
|
21
|
-
| `test_gh_pr_author_session_cleanup.py` | — | Tests for `gh_pr_author_session_cleanup.py` |
|
|
22
|
-
| `test_session_edit_tracker_cleanup.py` | — | Tests for `session_edit_tracker_cleanup.py` |
|
|
23
|
-
| `test_session_env_cleanup.py` | — | Tests for `session_env_cleanup.py` |
|
|
24
|
-
| `test_untracked_repo_detector.py` | — | Tests for `untracked_repo_detector.py` |
|
|
25
|
-
| `test_task_list_loop_starter.py` | — | Tests for `task_list_loop_starter.py` |
|
|
26
|
-
| `test__path_setup.py` | — | Tests for `_path_setup.py` |
|
|
27
|
-
| `test_working_style_prompt.py` | — | Tests for `working_style_prompt.py` |
|
|
28
|
-
|
|
29
|
-
## Conventions
|
|
30
|
-
|
|
31
|
-
- `session_env_cleanup.py` is Windows-specific in effect but safe to run on all platforms; it exits 0 when the target directory does not exist.
|
|
32
|
-
- Constants (stale-age threshold, directory names) live in `hooks_constants/session_env_cleanup_constants.py`.
|
|
33
|
-
- Tests run with `python -m pytest session/test_<name>.py`.
|
|
1
|
+
@AGENTS.md
|
|
@@ -34,6 +34,7 @@ if hooks_parent_directory not in sys.path:
|
|
|
34
34
|
sys.path.insert(0, hooks_parent_directory)
|
|
35
35
|
|
|
36
36
|
from _gh_pr_author_swap_utils import ( # noqa: E402
|
|
37
|
+
_build_restore_failure_message,
|
|
37
38
|
_delete_state_file,
|
|
38
39
|
_lstat_indicates_attacker_planted,
|
|
39
40
|
_read_original_account,
|
|
@@ -142,8 +143,12 @@ def _restore_stale_state_file(state_file: Path) -> None:
|
|
|
142
143
|
_delete_state_file(state_file)
|
|
143
144
|
else:
|
|
144
145
|
_write_line(
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
_build_restore_failure_message(
|
|
147
|
+
"gh-pr-author-cleanup",
|
|
148
|
+
original_account,
|
|
149
|
+
state_file,
|
|
150
|
+
"stale state file",
|
|
151
|
+
),
|
|
147
152
|
sys.stderr,
|
|
148
153
|
)
|
|
149
154
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"""SessionStart hook — start a task-list maintenance loop for the session.
|
|
3
3
|
|
|
4
4
|
At session start this hook emits an ``additionalContext`` directive asking Claude
|
|
5
|
-
to keep the session's task list current on a 10-minute cadence
|
|
6
|
-
``/loop`` skill when
|
|
7
|
-
|
|
5
|
+
to keep the session's task list current on a 10-minute cadence. Claude reuses the
|
|
6
|
+
active ``/loop`` skill when available and starts the skill for the session's first
|
|
7
|
+
task-list maintenance loop.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
@@ -343,7 +343,8 @@ def test_restore_stale_state_file_logs_when_switch_fails(
|
|
|
343
343
|
|
|
344
344
|
captured_streams = capsys.readouterr()
|
|
345
345
|
assert state_file.exists()
|
|
346
|
-
assert "[gh-pr-author-cleanup]
|
|
346
|
+
assert "[gh-pr-author-cleanup] Restore the gh account" in captured_streams.err
|
|
347
|
+
assert "gh account switch requires attention" in captured_streams.err
|
|
347
348
|
assert "'jl-cmd'" in captured_streams.err
|
|
348
349
|
assert str(state_file) in captured_streams.err
|
|
349
350
|
|
|
@@ -45,9 +45,17 @@ class TestSessionDirective:
|
|
|
45
45
|
emitted = json.loads(_run_main())
|
|
46
46
|
assert "/loop 10m" in emitted["additionalContext"]
|
|
47
47
|
|
|
48
|
-
def
|
|
48
|
+
def test_directive_runs_the_instruction_immediately(self) -> None:
|
|
49
49
|
emitted = json.loads(_run_main())
|
|
50
|
-
assert "
|
|
50
|
+
assert "Run that instruction once immediately when starting the loop, then" in emitted[
|
|
51
|
+
"additionalContext"
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
def test_directive_reuses_an_existing_loop(self) -> None:
|
|
55
|
+
emitted = json.loads(_run_main())
|
|
56
|
+
assert "Reuse the active task-list maintenance loop when available." in emitted[
|
|
57
|
+
"additionalContext"
|
|
58
|
+
]
|
|
51
59
|
|
|
52
60
|
def test_build_session_directive_returns_the_shared_constant(self) -> None:
|
|
53
61
|
assert starter.build_session_directive() == TASK_LIST_LOOP_DIRECTIVE
|
|
@@ -36,12 +36,17 @@ class TestWorkingStylePrompt:
|
|
|
36
36
|
emitted = json.loads(_run_main())
|
|
37
37
|
assert emitted["additionalContext"] == WORKING_STYLE_PROMPT
|
|
38
38
|
|
|
39
|
-
def
|
|
39
|
+
def test_emitted_prompt_preserves_existing_guidance_and_adds_policy(self) -> None:
|
|
40
40
|
emitted = json.loads(_run_main())
|
|
41
41
|
prompt_text = emitted["additionalContext"]
|
|
42
|
-
assert "
|
|
43
|
-
assert "Deliver
|
|
44
|
-
assert
|
|
42
|
+
assert "Document each task in a location that remains easy to find later." in prompt_text
|
|
43
|
+
assert "Deliver the requested work at its intended scope." in prompt_text
|
|
44
|
+
assert "Use positive prose throughout every generated text surface" in prompt_text
|
|
45
|
+
assert "Write each point as one direct affirmative statement" in prompt_text
|
|
46
|
+
assert "Use plain language, full terms, and simple descriptive names" in prompt_text
|
|
47
|
+
assert "When a request has multiple reasonable interpretations" in prompt_text
|
|
48
|
+
assert "Ask one focused clarification question" in prompt_text
|
|
49
|
+
assert "Pause for the user's choice before making a high-impact decision." in prompt_text
|
|
45
50
|
|
|
46
51
|
def test_build_session_directive_returns_the_shared_constant(self) -> None:
|
|
47
52
|
assert starter.build_session_directive() == WORKING_STYLE_PROMPT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# hooks/validation
|
|
2
|
+
|
|
3
|
+
PostToolUse hooks that validate code quality after Claude writes or edits a file. Unlike the blocking hooks (which fire PreToolUse and can deny the write), these hooks run after the write and report errors that need a follow-up fix.
|
|
4
|
+
|
|
5
|
+
## Key files
|
|
6
|
+
|
|
7
|
+
| File | Event | What it does |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `mypy_validator.py` | PostToolUse (Write/Edit on `.py` files) | Runs mypy on the written file and blocks (via PostToolUse block decision) when type errors are found — catches missing attributes, wrong signatures, type mismatches, and import errors |
|
|
10
|
+
| `hook_format_validator.py` | PostToolUse | Validates that a hook script's output JSON matches the expected Claude Code hook-output schema |
|
|
11
|
+
| `test_mypy_validator.py` | — | Tests for `mypy_validator.py` |
|
|
12
|
+
| `test_hook_format_validator.py` | — | Tests for `hook_format_validator.py` |
|
|
13
|
+
|
|
14
|
+
## Conventions
|
|
15
|
+
|
|
16
|
+
- `mypy_validator.py` resolves the project root via `CLAUDE_PROJECT_ROOT` or `git rev-parse --show-toplevel`.
|
|
17
|
+
- It works on both WSL and Windows.
|
|
18
|
+
- Constants (timeouts, max displayed errors) are inline in `mypy_validator.py`; longer tunables go in `hooks_constants/`.
|
|
19
|
+
- The `eval_*.txt` files in this directory are evaluation exports used during development — not runtime artifacts.
|
|
20
|
+
- Tests run with `python -m pytest validation/test_<name>.py`.
|
|
@@ -1,20 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
PostToolUse hooks that validate code quality after Claude writes or edits a file. Unlike the blocking hooks (which fire PreToolUse and can deny the write), these hooks run after the write and report errors that need a follow-up fix.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Event | What it does |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `mypy_validator.py` | PostToolUse (Write/Edit on `.py` files) | Runs mypy on the written file and blocks (via PostToolUse block decision) when type errors are found — catches missing attributes, wrong signatures, type mismatches, and import errors |
|
|
10
|
-
| `hook_format_validator.py` | PostToolUse | Validates that a hook script's output JSON matches the expected Claude Code hook-output schema |
|
|
11
|
-
| `test_mypy_validator.py` | — | Tests for `mypy_validator.py` |
|
|
12
|
-
| `test_hook_format_validator.py` | — | Tests for `hook_format_validator.py` |
|
|
13
|
-
|
|
14
|
-
## Conventions
|
|
15
|
-
|
|
16
|
-
- `mypy_validator.py` resolves the project root via `CLAUDE_PROJECT_ROOT` or `git rev-parse --show-toplevel`.
|
|
17
|
-
- It works on both WSL and Windows.
|
|
18
|
-
- Constants (timeouts, max displayed errors) are inline in `mypy_validator.py`; longer tunables go in `hooks_constants/`.
|
|
19
|
-
- The `eval_*.txt` files in this directory are evaluation exports used during development — not runtime artifacts.
|
|
20
|
-
- Tests run with `python -m pytest validation/test_<name>.py`.
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# hooks/validators
|
|
2
|
+
|
|
3
|
+
A library of check modules used by the validation hooks. Each module focuses on one concern; `run_all_validators.py` runs them all and collects results. These modules do not hook into Claude Code directly — they are imported by the validation hooks.
|
|
4
|
+
|
|
5
|
+
## Core infrastructure
|
|
6
|
+
|
|
7
|
+
| File | Role |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `validator_base.py` | Defines the `Violation` dataclass (`file`, `line`, `message`) used by every check module |
|
|
10
|
+
| `validator_defaults.py` | Default configuration values shared across check modules |
|
|
11
|
+
| `exempt_paths.py` | Path exemption logic — paths that checks skip (e.g. vendored code) |
|
|
12
|
+
| `output_formatter.py` | Formats `Violation` lists into human-readable output |
|
|
13
|
+
| `run_all_validators.py` | Entry point — runs every check module and aggregates results |
|
|
14
|
+
| `health_check.py` | Verifies that all validator dependencies (ruff, mypy) are reachable |
|
|
15
|
+
| `pyproject_config_discovery.py` | Shared walk-up primitive that resolves a tool's pyproject.toml config from an original target path, matching the `[tool.<name>]` table the tool owns |
|
|
16
|
+
| `python_style_helpers.py` | Shared source-line splitting and function-discovery helpers imported by `python_style_checks.py` |
|
|
17
|
+
|
|
18
|
+
## Check modules
|
|
19
|
+
|
|
20
|
+
| Module | What it checks |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `abbreviation_checks.py` | Abbreviated names in Python code |
|
|
23
|
+
| `code_quality_checks.py` | General code quality concerns (dead code, stub bodies, etc.) |
|
|
24
|
+
| `comment_checks.py` | Inline comment presence and content |
|
|
25
|
+
| `file_structure_checks.py` | File-level structural rules (line count, module layout) |
|
|
26
|
+
| `git_checks.py` | Git-state checks (untracked files, merge conflicts) |
|
|
27
|
+
| `magic_value_checks.py` | Magic numbers and strings |
|
|
28
|
+
| `mypy_integration.py` | Runs mypy and converts its output to `Violation` objects |
|
|
29
|
+
| `pr_reference_checks.py` | PR references in commit messages and changelogs |
|
|
30
|
+
| `python_antipattern_checks.py` | Python-specific anti-patterns (bare `except`, `Any`, etc.) |
|
|
31
|
+
| `python_style_checks.py` | Python style rules (naming, imports, type hints) |
|
|
32
|
+
| `react_checks.py` | React/TSX-specific checks (class component patterns, PureComponent usage) |
|
|
33
|
+
| `ruff_integration.py` | Runs ruff and converts its output to `Violation` objects |
|
|
34
|
+
| `security_checks.py` | Security anti-patterns (hardcoded secrets, unsafe calls) |
|
|
35
|
+
| `todo_checks.py` | TODO/FIXME markers without an associated issue reference |
|
|
36
|
+
| `type_safety_checks.py` | Type-safety rules (no `Any`, no `cast`, no `# type: ignore`) |
|
|
37
|
+
| `useless_test_checks.py` | Tests that check only existence or constant values |
|
|
38
|
+
|
|
39
|
+
## Subdirectory
|
|
40
|
+
|
|
41
|
+
| Directory | Role |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `config/` | Constants module (`directory_exemption_constants.py`) holding the exemption directory-segment names that `validate_proposed_file` staging keeps |
|
|
44
|
+
| `test_files/` | Fixture files used by the validator tests — not checked-in test code |
|
|
45
|
+
|
|
46
|
+
## Conventions
|
|
47
|
+
|
|
48
|
+
- Every check module exposes one or more functions that take file content or an AST and return a list of `Violation` objects.
|
|
49
|
+
- Test files live beside the modules they test: `test_<module>.py`. Run with `python -m pytest validators/test_<name>.py`.
|
|
50
|
+
- `conftest.py` provides shared test fixtures (sample files, fixture paths).
|
|
51
|
+
- `README.md` in this directory documents the validator design and how to add a new check.
|
|
@@ -1,51 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
A library of check modules used by the validation hooks. Each module focuses on one concern; `run_all_validators.py` runs them all and collects results. These modules do not hook into Claude Code directly — they are imported by the validation hooks.
|
|
4
|
-
|
|
5
|
-
## Core infrastructure
|
|
6
|
-
|
|
7
|
-
| File | Role |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `validator_base.py` | Defines the `Violation` dataclass (`file`, `line`, `message`) used by every check module |
|
|
10
|
-
| `validator_defaults.py` | Default configuration values shared across check modules |
|
|
11
|
-
| `exempt_paths.py` | Path exemption logic — paths that checks skip (e.g. vendored code) |
|
|
12
|
-
| `output_formatter.py` | Formats `Violation` lists into human-readable output |
|
|
13
|
-
| `run_all_validators.py` | Entry point — runs every check module and aggregates results |
|
|
14
|
-
| `health_check.py` | Verifies that all validator dependencies (ruff, mypy) are reachable |
|
|
15
|
-
| `pyproject_config_discovery.py` | Shared walk-up primitive that resolves a tool's pyproject.toml config from an original target path, matching the `[tool.<name>]` table the tool owns |
|
|
16
|
-
| `python_style_helpers.py` | Shared source-line splitting and function-discovery helpers imported by `python_style_checks.py` |
|
|
17
|
-
|
|
18
|
-
## Check modules
|
|
19
|
-
|
|
20
|
-
| Module | What it checks |
|
|
21
|
-
|---|---|
|
|
22
|
-
| `abbreviation_checks.py` | Abbreviated names in Python code |
|
|
23
|
-
| `code_quality_checks.py` | General code quality concerns (dead code, stub bodies, etc.) |
|
|
24
|
-
| `comment_checks.py` | Inline comment presence and content |
|
|
25
|
-
| `file_structure_checks.py` | File-level structural rules (line count, module layout) |
|
|
26
|
-
| `git_checks.py` | Git-state checks (untracked files, merge conflicts) |
|
|
27
|
-
| `magic_value_checks.py` | Magic numbers and strings |
|
|
28
|
-
| `mypy_integration.py` | Runs mypy and converts its output to `Violation` objects |
|
|
29
|
-
| `pr_reference_checks.py` | PR references in commit messages and changelogs |
|
|
30
|
-
| `python_antipattern_checks.py` | Python-specific anti-patterns (bare `except`, `Any`, etc.) |
|
|
31
|
-
| `python_style_checks.py` | Python style rules (naming, imports, type hints) |
|
|
32
|
-
| `react_checks.py` | React/TSX-specific checks (class component patterns, PureComponent usage) |
|
|
33
|
-
| `ruff_integration.py` | Runs ruff and converts its output to `Violation` objects |
|
|
34
|
-
| `security_checks.py` | Security anti-patterns (hardcoded secrets, unsafe calls) |
|
|
35
|
-
| `todo_checks.py` | TODO/FIXME markers without an associated issue reference |
|
|
36
|
-
| `type_safety_checks.py` | Type-safety rules (no `Any`, no `cast`, no `# type: ignore`) |
|
|
37
|
-
| `useless_test_checks.py` | Tests that check only existence or constant values |
|
|
38
|
-
|
|
39
|
-
## Subdirectory
|
|
40
|
-
|
|
41
|
-
| Directory | Role |
|
|
42
|
-
|---|---|
|
|
43
|
-
| `config/` | Constants module (`directory_exemption_constants.py`) holding the exemption directory-segment names that `validate_proposed_file` staging keeps |
|
|
44
|
-
| `test_files/` | Fixture files used by the validator tests — not checked-in test code |
|
|
45
|
-
|
|
46
|
-
## Conventions
|
|
47
|
-
|
|
48
|
-
- Every check module exposes one or more functions that take file content or an AST and return a list of `Violation` objects.
|
|
49
|
-
- Test files live beside the modules they test: `test_<module>.py`. Run with `python -m pytest validators/test_<name>.py`.
|
|
50
|
-
- `conftest.py` provides shared test fixtures (sample files, fixture paths).
|
|
51
|
-
- `README.md` in this directory documents the validator design and how to add a new check.
|
|
1
|
+
@AGENTS.md
|
|
@@ -7,14 +7,15 @@ Provides:
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import hashlib
|
|
10
|
+
import stat
|
|
10
11
|
import subprocess
|
|
11
12
|
import sys
|
|
12
13
|
from dataclasses import dataclass
|
|
13
|
-
from datetime import datetime
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
|
+
from enum import StrEnum
|
|
14
16
|
from pathlib import Path
|
|
15
17
|
from typing import Dict, Optional
|
|
16
18
|
|
|
17
|
-
|
|
18
19
|
VALIDATOR_FILES = [
|
|
19
20
|
"python_style_checks.py",
|
|
20
21
|
"test_safety_checks.py",
|
|
@@ -25,15 +26,38 @@ VALIDATOR_FILES = [
|
|
|
25
26
|
]
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
class ValidatorStatus(StrEnum):
|
|
30
|
+
"""Displayed state for one required validator."""
|
|
31
|
+
|
|
32
|
+
READY = "[READY]"
|
|
33
|
+
FILE_REQUIRED = "[FILE REQUIRED]"
|
|
34
|
+
ACCESS_REQUIRED = "[ACCESS REQUIRED]"
|
|
35
|
+
|
|
36
|
+
|
|
28
37
|
@dataclass(frozen=True)
|
|
29
38
|
class ValidatorHealth:
|
|
30
39
|
"""Health status of a single validator."""
|
|
31
40
|
|
|
32
41
|
name: str
|
|
33
|
-
|
|
42
|
+
status: ValidatorStatus
|
|
34
43
|
error: Optional[str] = None
|
|
35
44
|
last_modified: Optional[datetime] = None
|
|
36
45
|
|
|
46
|
+
@property
|
|
47
|
+
def is_healthy(self) -> bool:
|
|
48
|
+
"""Return whether the validator is ready for use."""
|
|
49
|
+
return self.status is ValidatorStatus.READY
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def healthy(self) -> bool:
|
|
53
|
+
"""Return the readiness state for compatibility readers."""
|
|
54
|
+
return self.is_healthy
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def is_present(self) -> bool:
|
|
58
|
+
"""Return whether the validator file is present on disk."""
|
|
59
|
+
return self.status is not ValidatorStatus.FILE_REQUIRED
|
|
60
|
+
|
|
37
61
|
|
|
38
62
|
@dataclass(frozen=True)
|
|
39
63
|
class SystemHealth:
|
|
@@ -56,26 +80,33 @@ def check_validator_exists(validator_path: Path) -> ValidatorHealth:
|
|
|
56
80
|
"""
|
|
57
81
|
name = validator_path.stem
|
|
58
82
|
|
|
59
|
-
|
|
83
|
+
try:
|
|
84
|
+
validator_stat = validator_path.stat()
|
|
85
|
+
if not stat.S_ISREG(validator_stat.st_mode):
|
|
86
|
+
return ValidatorHealth(
|
|
87
|
+
name=name,
|
|
88
|
+
status=ValidatorStatus.FILE_REQUIRED,
|
|
89
|
+
error=f"Validator file required: {validator_path}",
|
|
90
|
+
)
|
|
91
|
+
validator_path.read_text(encoding="utf-8")
|
|
60
92
|
return ValidatorHealth(
|
|
61
93
|
name=name,
|
|
62
|
-
|
|
63
|
-
|
|
94
|
+
status=ValidatorStatus.READY,
|
|
95
|
+
last_modified=datetime.fromtimestamp(
|
|
96
|
+
validator_stat.st_mtime, tz=timezone.utc
|
|
97
|
+
),
|
|
64
98
|
)
|
|
65
|
-
|
|
66
|
-
try:
|
|
67
|
-
validator_path.read_text(encoding="utf-8")
|
|
68
|
-
mtime = datetime.fromtimestamp(validator_path.stat().st_mtime)
|
|
99
|
+
except FileNotFoundError:
|
|
69
100
|
return ValidatorHealth(
|
|
70
101
|
name=name,
|
|
71
|
-
|
|
72
|
-
|
|
102
|
+
status=ValidatorStatus.FILE_REQUIRED,
|
|
103
|
+
error=f"Validator file required: {validator_path}",
|
|
73
104
|
)
|
|
74
105
|
except (IOError, OSError, PermissionError) as error:
|
|
75
106
|
return ValidatorHealth(
|
|
76
107
|
name=name,
|
|
77
|
-
|
|
78
|
-
error=f"
|
|
108
|
+
status=ValidatorStatus.ACCESS_REQUIRED,
|
|
109
|
+
error=f"Validator read access requires attention: {error}",
|
|
79
110
|
)
|
|
80
111
|
|
|
81
112
|
|
|
@@ -135,7 +166,10 @@ def get_validator_version(validators_dir: Optional[Path] = None) -> str:
|
|
|
135
166
|
for validator_file in sorted(VALIDATOR_FILES):
|
|
136
167
|
validator_path = validators_dir / validator_file
|
|
137
168
|
if validator_path.exists():
|
|
138
|
-
|
|
169
|
+
try:
|
|
170
|
+
content = validator_path.read_bytes()
|
|
171
|
+
except (FileNotFoundError, OSError):
|
|
172
|
+
continue
|
|
139
173
|
hasher.update(content)
|
|
140
174
|
|
|
141
175
|
return hasher.hexdigest()[:8]
|
|
@@ -154,7 +188,7 @@ def get_system_health(validators_dir: Optional[Path] = None) -> SystemHealth:
|
|
|
154
188
|
validators_dir = Path(__file__).parent
|
|
155
189
|
|
|
156
190
|
validators = check_all_validators(validators_dir)
|
|
157
|
-
all_healthy = all(v.
|
|
191
|
+
all_healthy = all(v.is_healthy for v in validators.values())
|
|
158
192
|
|
|
159
193
|
optional_tools = {
|
|
160
194
|
"ruff": check_optional_tool("ruff"),
|
|
@@ -187,15 +221,14 @@ def print_health_report(health: SystemHealth) -> None:
|
|
|
187
221
|
|
|
188
222
|
print("Required Validators:")
|
|
189
223
|
for name, validator in sorted(health.validators.items()):
|
|
190
|
-
|
|
191
|
-
print(f" {status} {name}")
|
|
224
|
+
print(f" {validator.status.value} {name}")
|
|
192
225
|
if validator.error:
|
|
193
226
|
print(f" Error: {validator.error}")
|
|
194
227
|
print()
|
|
195
228
|
|
|
196
229
|
print("Optional Tools:")
|
|
197
230
|
for tool, available in sorted(health.optional_tools.items()):
|
|
198
|
-
status =
|
|
231
|
+
status = ValidatorStatus.READY.value if available else "[OPTIONAL]"
|
|
199
232
|
print(f" {status} {tool}")
|
|
200
233
|
print()
|
|
201
234
|
|