claude-dev-env 1.92.1 → 1.93.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/CLAUDE.md +6 -2
- package/_shared/CLAUDE.md +2 -1
- package/_shared/advisor/CLAUDE.md +21 -0
- package/_shared/advisor/advisor-protocol.md +142 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/__init__.py +1 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +86 -0
- package/_shared/advisor/scripts/model_tier_run_validator.py +246 -0
- package/_shared/advisor/scripts/pyproject.toml +3 -0
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +302 -0
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +180 -0
- package/_shared/advisor/scripts/tier_model_ids.py +164 -0
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/precatch-rubric.md +65 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +260 -14
- package/_shared/pr-loop/scripts/post_audit_thread.py +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +20 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +2 -1
- package/_shared/pr-loop/scripts/reviewer_availability.py +42 -12
- package/_shared/pr-loop/scripts/reviews_disabled.py +65 -21
- package/_shared/pr-loop/scripts/terminology_sweep.py +69 -21
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/tests/fixtures/{copilot_internal_user_jonecho.json → copilot_internal_user_example.json} +1 -1
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +1 -1
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +292 -2
- package/_shared/pr-loop/scripts/tests/test_copilot_quota.py +8 -8
- package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +18 -5
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +56 -5
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +104 -17
- package/agents/CLAUDE.md +3 -2
- package/agents/clasp-deployment-orchestrator.md +2 -2
- package/agents/code-advisor.md +7 -5
- package/agents/code-verifier.md +6 -2
- package/agents/session-advisor.md +27 -0
- package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +1 -1
- package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +1 -1
- package/audit-rubrics/prompts/category-a-api-contracts.md +2 -2
- package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
- package/audit-rubrics/prompts/category-c-resource-cleanup.md +2 -2
- package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +2 -2
- package/audit-rubrics/prompts/category-e-dead-code.md +2 -2
- package/audit-rubrics/prompts/category-f-silent-failures.md +2 -2
- package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
- package/audit-rubrics/prompts/category-h-security-boundaries.md +2 -2
- package/audit-rubrics/prompts/category-i-concurrency.md +2 -2
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +2 -2
- package/audit-rubrics/prompts/category-k-codebase-conflicts.md +2 -2
- package/audit-rubrics/prompts/category-l-behavior-equivalence.md +2 -2
- package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +2 -2
- package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +2 -2
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +2 -2
- package/bin/CLAUDE.md +1 -0
- package/bin/expand_home_directory_tokens.mjs +75 -0
- package/bin/install.mjs +21 -7
- package/bin/install.test.mjs +220 -10
- package/hooks/CLAUDE.md +1 -1
- package/hooks/blocking/CLAUDE.md +4 -1
- package/hooks/blocking/_path_setup.py +13 -0
- package/hooks/blocking/bash_pre_tool_use_dispatcher.py +281 -0
- package/hooks/blocking/code_rules_dead_module_constant.py +88 -16
- package/hooks/blocking/code_rules_enforcer.py +6 -0
- package/hooks/blocking/code_rules_imports_logging.py +34 -1
- package/hooks/blocking/code_rules_paths_syspath.py +1 -1
- package/hooks/blocking/code_rules_test_layout.py +272 -0
- package/hooks/blocking/nas_ssh_binary_enforcer.py +22 -12
- package/hooks/blocking/pii_prevention_blocker.py +781 -0
- package/hooks/blocking/pii_scanner.py +365 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
- package/hooks/blocking/stop_dispatcher.py +84 -0
- package/hooks/blocking/test__path_setup.py +39 -0
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +61 -0
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +294 -0
- package/hooks/blocking/test_code_rules_enforcer_cross_skill_duplicate.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +2 -2
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +51 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +103 -0
- package/hooks/blocking/test_code_rules_enforcer_dot_test_pattern.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +11 -11
- package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_same_file_inline_duplicate.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_test_layout.py +111 -0
- package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +76 -0
- package/hooks/blocking/test_destructive_command_blocker.py +29 -9
- package/hooks/blocking/test_gh_body_arg_blocker.py +3 -3
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +96 -39
- package/hooks/blocking/test_pii_prevention_blocker.py +905 -0
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +41 -0
- package/hooks/blocking/test_pii_scanner.py +190 -0
- package/hooks/blocking/test_pr_description_enforcer_pr_number.py +1 -1
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +14 -13
- package/hooks/blocking/test_stop_dispatcher.py +187 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +1 -1
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +3 -3
- package/hooks/blocking/volatile_path_in_post_blocker.py +54 -5
- package/hooks/diagnostic/migrations/README.md +25 -26
- package/hooks/diagnostic/test_hook_log_extractor.py +10 -10
- package/hooks/hooks.json +10 -120
- package/hooks/hooks_constants/CLAUDE.md +12 -3
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +78 -0
- package/hooks/hooks_constants/dead_module_constant_constants.py +1 -0
- package/hooks/hooks_constants/hosted_hook_runner.py +73 -0
- package/hooks/hooks_constants/local_identity.py +182 -0
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +4 -37
- package/hooks/hooks_constants/pii_prevention_constants.py +295 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/python_style_checks_constants.py +15 -0
- package/hooks/hooks_constants/stop_dispatcher_constants.py +26 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +87 -0
- package/hooks/hooks_constants/test_hardcoded_user_path_constants.py +2 -2
- package/hooks/hooks_constants/test_hosted_hook_runner.py +101 -0
- package/hooks/hooks_constants/test_layout_constants.py +28 -0
- package/hooks/hooks_constants/test_local_identity.py +88 -0
- package/hooks/hooks_constants/test_session_env_cleanup_constants.py +1 -1
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +32 -0
- package/hooks/validators/README.md +5 -6
- package/hooks/validators/__init__.py +2 -2
- package/hooks/validators/python_style_checks.py +298 -243
- package/hooks/validators/run_all_validators.py +6 -0
- package/hooks/validators/test_python_style_checks.py +278 -163
- package/package.json +2 -2
- package/rules/CLAUDE.md +2 -1
- package/rules/bdd.md +1 -1
- package/rules/nas-ssh-invocation.md +6 -4
- package/rules/no-justification-noise.md +61 -0
- package/scripts/test_setup_project_paths.py +1 -1
- package/skills/CLAUDE.md +6 -3
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +2 -2
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/test__path_resolver.py +42 -3
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +11 -11
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +26 -3
- package/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +49 -25
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +113 -35
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +44 -22
- package/skills/autoconverge/reference/gotchas.md +11 -0
- package/skills/autoconverge/reference/stop-conditions.md +23 -13
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +15 -19
- package/skills/autoconverge/workflow/converge.contract.test.mjs +43 -42
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +141 -16
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +2 -2
- package/skills/autoconverge/workflow/converge.mjs +329 -68
- package/skills/autoconverge/workflow/converge.path-aware.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.precatch.test.mjs +152 -0
- package/skills/autoconverge/workflow/converge.run-input.test.mjs +3 -3
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +15 -15
- package/skills/autoconverge/workflow/test_convergence_summary.py +6 -6
- package/skills/bdd-protocol/SKILL.md +4 -5
- package/skills/bdd-protocol/references/anti-patterns.md +1 -1
- package/skills/bugteam/SKILL.md +8 -0
- package/skills/bugteam/reference/copilot-gap-analysis.md +20 -20
- package/skills/bugteam/reference/obstacles/audit-walk-categories.md +1 -1
- package/skills/bugteam/reference/team-setup.md +7 -5
- package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
- package/skills/copilot-finding-triage/SKILL.md +124 -0
- package/skills/copilot-finding-triage/reference/tier-rubric.md +49 -0
- package/skills/copilot-finding-triage/scripts/conftest.py +8 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +32 -0
- package/skills/copilot-finding-triage/scripts/notify_ntfy.py +192 -0
- package/skills/copilot-finding-triage/scripts/test_notify_ntfy.py +141 -0
- package/skills/copilot-finding-triage/templates/notification.md +34 -0
- package/skills/copilot-review/SKILL.md +8 -6
- package/skills/findbugs/SKILL.md +4 -0
- package/skills/fixbugs/SKILL.md +8 -7
- package/skills/gotcha/CLAUDE.md +2 -2
- package/skills/gotcha/SKILL.md +4 -4
- package/skills/log-audit/SKILL.md +4 -6
- package/skills/monitor-open-prs/CLAUDE.md +1 -1
- package/skills/monitor-open-prs/SKILL.md +6 -1
- package/skills/orchestrator/SKILL.md +74 -78
- package/skills/orchestrator-refresh/SKILL.md +24 -12
- package/skills/post-audit-findings/SKILL.md +5 -9
- package/skills/pr-consistency-audit/SKILL.md +5 -1
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +86 -47
- package/skills/pr-converge/reference/CLAUDE.md +1 -1
- package/skills/pr-converge/reference/convergence-gates.md +20 -17
- package/skills/pr-converge/reference/examples.md +63 -47
- package/skills/pr-converge/reference/fix-protocol.md +15 -15
- package/skills/pr-converge/reference/ground-rules.md +11 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +6 -5
- package/skills/pr-converge/reference/per-tick.md +129 -107
- package/skills/pr-converge/reference/state-schema.md +15 -10
- package/skills/pr-converge/scripts/check_convergence.py +5 -4
- package/skills/pr-converge/scripts/test_check_convergence.py +6 -4
- package/skills/pr-fix-protocol/SKILL.md +3 -8
- package/skills/pr-loop-cloud-transport/SKILL.md +102 -0
- package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +40 -0
- package/skills/pr-loop-cloud-transport/reference/substitution-matrix.md +48 -0
- package/skills/pr-loop-lifecycle/SKILL.md +5 -9
- package/skills/pr-scope-resolve/SKILL.md +3 -6
- package/skills/privacy-hygiene/SKILL.md +114 -0
- package/skills/qbug/SKILL.md +8 -8
- package/skills/rebase/SKILL.md +5 -1
- package/skills/refine/SKILL.md +4 -5
- package/skills/reviewer-gates/SKILL.md +7 -11
- package/skills/session-log/SKILL.md +4 -1
- package/skills/skill-builder/SKILL.md +3 -6
- package/skills/structure-prompt/SKILL.md +4 -5
- package/skills/team-advisor/SKILL.md +56 -0
- package/skills/test_markdown_link_integrity.py +10 -6
- package/skills/update/SKILL.md +5 -1
- package/skills/usage-pause/SKILL.md +14 -5
- package/skills/usage-pause/scripts/resolve_usage_window.py +83 -5
- package/skills/usage-pause/scripts/test_resolve_usage_window.py +185 -17
- package/skills/usage-pause/scripts/usage_pause_constants/resolve_usage_window_constants.py +4 -3
- package/skills/verified-build/SKILL.md +4 -9
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Every case drives the production ``main`` / ``evaluate_copilot_quota`` path with
|
|
4
4
|
only the ``gh`` subprocess boundary (``_run_gh``) stubbed, fed a captured
|
|
5
|
-
``copilot_internal/user`` JSON fixture in the
|
|
5
|
+
``copilot_internal/user`` JSON fixture in the example-account shape.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import importlib.util
|
|
@@ -30,7 +30,7 @@ def _load_copilot_quota_module() -> ModuleType:
|
|
|
30
30
|
|
|
31
31
|
copilot_quota = _load_copilot_quota_module()
|
|
32
32
|
|
|
33
|
-
FIXTURE_PATH = Path(__file__).parent / "fixtures" / "
|
|
33
|
+
FIXTURE_PATH = Path(__file__).parent / "fixtures" / "copilot_internal_user_example.json"
|
|
34
34
|
AVAILABLE_USER_JSON = FIXTURE_PATH.read_text(encoding="utf-8")
|
|
35
35
|
FAKE_TOKEN_RESULT = (0, "ghp_faketoken_value\n")
|
|
36
36
|
|
|
@@ -75,7 +75,7 @@ def _isolate_account_sources(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) ->
|
|
|
75
75
|
def test_main_runs_copilot_when_premium_quota_available(
|
|
76
76
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
77
77
|
) -> None:
|
|
78
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
78
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
79
79
|
monkeypatch.setattr(
|
|
80
80
|
copilot_quota,
|
|
81
81
|
"_run_gh",
|
|
@@ -91,7 +91,7 @@ def test_main_runs_copilot_when_premium_quota_available(
|
|
|
91
91
|
def test_main_exits_out_of_quota_when_premium_exhausted(
|
|
92
92
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
93
93
|
) -> None:
|
|
94
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
94
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
95
95
|
monkeypatch.setattr(
|
|
96
96
|
copilot_quota,
|
|
97
97
|
"_run_gh",
|
|
@@ -106,7 +106,7 @@ def test_main_exits_out_of_quota_when_premium_exhausted(
|
|
|
106
106
|
def test_main_exits_api_down_on_non_json_response(
|
|
107
107
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
108
108
|
) -> None:
|
|
109
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
109
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
110
110
|
monkeypatch.setattr(
|
|
111
111
|
copilot_quota,
|
|
112
112
|
"_run_gh",
|
|
@@ -121,7 +121,7 @@ def test_main_exits_api_down_on_non_json_response(
|
|
|
121
121
|
def test_main_exits_api_down_on_missing_premium_snapshot(
|
|
122
122
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
123
123
|
) -> None:
|
|
124
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
124
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
125
125
|
monkeypatch.setattr(
|
|
126
126
|
copilot_quota,
|
|
127
127
|
"_run_gh",
|
|
@@ -136,7 +136,7 @@ def test_main_exits_api_down_on_missing_premium_snapshot(
|
|
|
136
136
|
def test_main_exits_api_down_when_gh_token_unresolved(
|
|
137
137
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
138
138
|
) -> None:
|
|
139
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
139
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
140
140
|
monkeypatch.setattr(
|
|
141
141
|
copilot_quota,
|
|
142
142
|
"_run_gh",
|
|
@@ -180,7 +180,7 @@ def test_every_gh_backed_skip_writes_a_log_line_naming_the_scenario(
|
|
|
180
180
|
expected_exit_code: int,
|
|
181
181
|
expected_scenario: str,
|
|
182
182
|
) -> None:
|
|
183
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
183
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
184
184
|
monkeypatch.setattr(
|
|
185
185
|
copilot_quota, "_run_gh", _gh_stub(FAKE_TOKEN_RESULT, api_result)
|
|
186
186
|
)
|
|
@@ -33,7 +33,7 @@ def _load_module(module_name: str) -> ModuleType:
|
|
|
33
33
|
copilot_quota = _load_module("copilot_quota")
|
|
34
34
|
reviewer_availability = _load_module("reviewer_availability")
|
|
35
35
|
|
|
36
|
-
FIXTURE_PATH = Path(__file__).parent / "fixtures" / "
|
|
36
|
+
FIXTURE_PATH = Path(__file__).parent / "fixtures" / "copilot_internal_user_example.json"
|
|
37
37
|
AVAILABLE_USER_JSON = FIXTURE_PATH.read_text(encoding="utf-8")
|
|
38
38
|
FAKE_TOKEN_RESULT = (0, "ghp_faketoken_value\n")
|
|
39
39
|
|
|
@@ -67,6 +67,7 @@ def _isolate_reviewer_environment(
|
|
|
67
67
|
) -> None:
|
|
68
68
|
monkeypatch.delenv("COPILOT_QUOTA_ACCOUNT", raising=False)
|
|
69
69
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
70
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
|
|
70
71
|
monkeypatch.setattr(
|
|
71
72
|
reviewer_availability, "COPILOT_QUOTA_DEFAULT_ENV_FILE_PATH", tmp_path / ".env"
|
|
72
73
|
)
|
|
@@ -75,7 +76,7 @@ def _isolate_reviewer_environment(
|
|
|
75
76
|
def test_main_reports_copilot_available_when_quota_confirmed_available(
|
|
76
77
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
77
78
|
) -> None:
|
|
78
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
79
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
79
80
|
monkeypatch.setattr(
|
|
80
81
|
copilot_quota, "_run_gh", _gh_stub(FAKE_TOKEN_RESULT, (0, AVAILABLE_USER_JSON))
|
|
81
82
|
)
|
|
@@ -106,7 +107,7 @@ def test_main_reports_copilot_down_when_opted_out_via_env(
|
|
|
106
107
|
def test_main_reports_copilot_down_when_out_of_quota(
|
|
107
108
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
108
109
|
) -> None:
|
|
109
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
110
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
110
111
|
monkeypatch.setattr(
|
|
111
112
|
copilot_quota,
|
|
112
113
|
"_run_gh",
|
|
@@ -121,7 +122,7 @@ def test_main_reports_copilot_down_when_out_of_quota(
|
|
|
121
122
|
def test_main_reports_copilot_down_when_quota_api_is_down(
|
|
122
123
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
123
124
|
) -> None:
|
|
124
|
-
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "
|
|
125
|
+
monkeypatch.setenv("COPILOT_QUOTA_ACCOUNT", "example-account")
|
|
125
126
|
monkeypatch.setattr(
|
|
126
127
|
copilot_quota, "_run_gh", _gh_stub(FAKE_TOKEN_RESULT, (0, "not json at all"))
|
|
127
128
|
)
|
|
@@ -140,9 +141,20 @@ def test_main_reports_copilot_down_when_no_account_configured(
|
|
|
140
141
|
assert "scenario C" in captured.err
|
|
141
142
|
|
|
142
143
|
|
|
143
|
-
def
|
|
144
|
+
def test_main_reports_bugbot_down_when_no_env_vars_set(
|
|
144
145
|
capsys: pytest.CaptureFixture[str],
|
|
145
146
|
) -> None:
|
|
147
|
+
exit_code = reviewer_availability.main(["--reviewer", "bugbot"])
|
|
148
|
+
captured = capsys.readouterr()
|
|
149
|
+
assert exit_code == reviewer_availability.EXIT_CODE_REVIEWER_DOWN
|
|
150
|
+
assert "CLAUDE_REVIEWS_ENABLED" in captured.err
|
|
151
|
+
assert "CLAUDE_REVIEWS_DISABLED" not in captured.err
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_main_reports_bugbot_available_when_enabled_via_env(
|
|
155
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
156
|
+
) -> None:
|
|
157
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
146
158
|
exit_code = reviewer_availability.main(["--reviewer", "bugbot"])
|
|
147
159
|
captured = capsys.readouterr()
|
|
148
160
|
assert exit_code == reviewer_availability.EXIT_CODE_REVIEWER_AVAILABLE
|
|
@@ -153,6 +165,7 @@ def test_main_reports_bugbot_down_when_opted_out_via_env(
|
|
|
153
165
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
154
166
|
) -> None:
|
|
155
167
|
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugbot")
|
|
168
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
156
169
|
exit_code = reviewer_availability.main(["--reviewer", "bugbot"])
|
|
157
170
|
captured = capsys.readouterr()
|
|
158
171
|
assert exit_code == reviewer_availability.EXIT_CODE_REVIEWER_DOWN
|
|
@@ -43,20 +43,46 @@ def test_is_bugbot_disabled_via_env_returns_true_when_env_lists_bugbot(
|
|
|
43
43
|
assert reviews_disabled.is_bugbot_disabled_via_env() is True
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def
|
|
46
|
+
def test_is_bugbot_disabled_via_env_returns_true_when_no_env_vars_set(
|
|
47
47
|
monkeypatch: pytest.MonkeyPatch,
|
|
48
48
|
) -> None:
|
|
49
49
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
50
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
|
|
51
|
+
assert reviews_disabled.is_bugbot_disabled_via_env() is True
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_is_bugbot_disabled_via_env_returns_false_when_enabled_lists_bugbot(
|
|
55
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
56
|
+
) -> None:
|
|
57
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
58
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
50
59
|
assert reviews_disabled.is_bugbot_disabled_via_env() is False
|
|
51
60
|
|
|
52
61
|
|
|
53
|
-
def
|
|
62
|
+
def test_is_bugbot_disabled_via_env_enabled_parse_is_case_and_space_tolerant(
|
|
54
63
|
monkeypatch: pytest.MonkeyPatch,
|
|
55
64
|
) -> None:
|
|
56
|
-
monkeypatch.
|
|
65
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
66
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", " BugBot ")
|
|
57
67
|
assert reviews_disabled.is_bugbot_disabled_via_env() is False
|
|
58
68
|
|
|
59
69
|
|
|
70
|
+
def test_is_bugbot_disabled_via_env_true_when_disabled_overrides_enabled(
|
|
71
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
72
|
+
) -> None:
|
|
73
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugbot")
|
|
74
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
75
|
+
assert reviews_disabled.is_bugbot_disabled_via_env() is True
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_is_bugbot_disabled_via_env_true_when_bugteam_listed_and_not_enabled(
|
|
79
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
80
|
+
) -> None:
|
|
81
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugteam")
|
|
82
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
|
|
83
|
+
assert reviews_disabled.is_bugbot_disabled_via_env() is True
|
|
84
|
+
|
|
85
|
+
|
|
60
86
|
def test_is_bugbot_disabled_via_env_true_when_both_tokens_listed_mixed_case(
|
|
61
87
|
monkeypatch: pytest.MonkeyPatch,
|
|
62
88
|
) -> None:
|
|
@@ -65,6 +91,29 @@ def test_is_bugbot_disabled_via_env_true_when_both_tokens_listed_mixed_case(
|
|
|
65
91
|
assert reviews_disabled.is_bugteam_disabled_via_env() is True
|
|
66
92
|
|
|
67
93
|
|
|
94
|
+
def test_is_bugbot_opted_out_via_env_returns_true_when_disabled_lists_bugbot(
|
|
95
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
96
|
+
) -> None:
|
|
97
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugbot")
|
|
98
|
+
assert reviews_disabled.is_bugbot_opted_out_via_env() is True
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_is_bugbot_opted_out_via_env_returns_false_when_only_enabled_lists_bugbot(
|
|
102
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
103
|
+
) -> None:
|
|
104
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
105
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
106
|
+
assert reviews_disabled.is_bugbot_opted_out_via_env() is False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_is_bugbot_opted_out_via_env_returns_false_when_no_env_vars_set(
|
|
110
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
111
|
+
) -> None:
|
|
112
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
113
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
|
|
114
|
+
assert reviews_disabled.is_bugbot_opted_out_via_env() is False
|
|
115
|
+
|
|
116
|
+
|
|
68
117
|
def test_is_copilot_disabled_via_env_returns_true_when_env_lists_copilot(
|
|
69
118
|
monkeypatch: pytest.MonkeyPatch,
|
|
70
119
|
) -> None:
|
|
@@ -101,17 +150,19 @@ def test_cli_main_returns_zero_when_named_reviewer_disabled(
|
|
|
101
150
|
assert reviews_disabled.main(["--reviewer", "bugbot"]) == 0
|
|
102
151
|
|
|
103
152
|
|
|
104
|
-
def
|
|
153
|
+
def test_cli_main_returns_one_when_bugbot_enabled_via_env(
|
|
105
154
|
monkeypatch: pytest.MonkeyPatch,
|
|
106
155
|
) -> None:
|
|
107
156
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
157
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
108
158
|
assert reviews_disabled.main(["--reviewer", "bugbot"]) == 1
|
|
109
159
|
|
|
110
160
|
|
|
111
|
-
def
|
|
161
|
+
def test_cli_main_returns_one_for_bugbot_when_enabled_despite_bugteam_optout(
|
|
112
162
|
monkeypatch: pytest.MonkeyPatch,
|
|
113
163
|
) -> None:
|
|
114
164
|
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugteam")
|
|
165
|
+
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
115
166
|
assert reviews_disabled.main(["--reviewer", "bugbot"]) == 1
|
|
116
167
|
|
|
117
168
|
|
|
@@ -49,12 +49,12 @@ CODE_AND_PROSE_DIFF = (
|
|
|
49
49
|
"+++ b/api/quota.py\n"
|
|
50
50
|
"@@ -0,0 +1,2 @@\n"
|
|
51
51
|
"+def read_quota(account):\n"
|
|
52
|
-
'+ return account["
|
|
52
|
+
'+ return account["premium_request_interactions"]\n'
|
|
53
53
|
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
54
54
|
"--- a/docs/README.md\n"
|
|
55
55
|
"+++ b/docs/README.md\n"
|
|
56
56
|
"@@ -0,0 +1,1 @@\n"
|
|
57
|
-
"+The premium-request
|
|
57
|
+
"+The premium-request-budget field gates the run.\n"
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
|
|
@@ -63,7 +63,7 @@ def test_flags_prose_near_miss_of_code_identifier() -> None:
|
|
|
63
63
|
assert len(findings) == 1
|
|
64
64
|
assert "docs/README.md:1" in findings[0]
|
|
65
65
|
assert "premium-request" in findings[0]
|
|
66
|
-
assert "
|
|
66
|
+
assert "premium_request_interactions" in findings[0]
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
def test_does_not_flag_exact_hyphen_variant() -> None:
|
|
@@ -208,7 +208,7 @@ def test_still_flags_non_plural_divergent_tail() -> None:
|
|
|
208
208
|
"--- a/api/quota.py\n"
|
|
209
209
|
"+++ b/api/quota.py\n"
|
|
210
210
|
"@@ -0,0 +1,1 @@\n"
|
|
211
|
-
"+
|
|
211
|
+
"+premium_request_interactions = 5\n"
|
|
212
212
|
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
213
213
|
"--- a/docs/README.md\n"
|
|
214
214
|
"+++ b/docs/README.md\n"
|
|
@@ -226,8 +226,8 @@ def test_flags_near_miss_inside_code_comment() -> None:
|
|
|
226
226
|
"--- a/api/quota.py\n"
|
|
227
227
|
"+++ b/api/quota.py\n"
|
|
228
228
|
"@@ -0,0 +1,2 @@\n"
|
|
229
|
-
"+
|
|
230
|
-
"+# the premium-request path resets the counter\n"
|
|
229
|
+
"+premium_request_interactions = 5\n"
|
|
230
|
+
"+# the premium-request-budget path resets the counter\n"
|
|
231
231
|
)
|
|
232
232
|
findings = sweep_diff(diff)
|
|
233
233
|
assert len(findings) == 1
|
|
@@ -268,9 +268,11 @@ def test_main_exits_zero_when_clean(tmp_path: Path) -> None:
|
|
|
268
268
|
|
|
269
269
|
def test_staged_terminology_findings_flags_staged_prose(tmp_path: Path) -> None:
|
|
270
270
|
_init_git_repository(tmp_path)
|
|
271
|
-
(tmp_path / "quota.py").write_text(
|
|
271
|
+
(tmp_path / "quota.py").write_text(
|
|
272
|
+
"premium_request_interactions = 5\n", encoding="utf-8"
|
|
273
|
+
)
|
|
272
274
|
(tmp_path / "README.md").write_text(
|
|
273
|
-
"The premium-request
|
|
275
|
+
"The premium-request-budget field gates the run.\n", encoding="utf-8"
|
|
274
276
|
)
|
|
275
277
|
subprocess.run(
|
|
276
278
|
["git", "add", "-A"],
|
|
@@ -341,12 +343,12 @@ def test_does_not_flag_tail_word_that_is_identifier_vocabulary() -> None:
|
|
|
341
343
|
"--- a/api/layout.py\n"
|
|
342
344
|
"+++ b/api/layout.py\n"
|
|
343
345
|
"@@ -0,0 +1,1 @@\n"
|
|
344
|
-
"+
|
|
346
|
+
"+target_width_ratio = box_height\n"
|
|
345
347
|
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
346
348
|
"--- a/docs/README.md\n"
|
|
347
349
|
"+++ b/docs/README.md\n"
|
|
348
350
|
"@@ -0,0 +1,1 @@\n"
|
|
349
|
-
"+Scale the art until it covers the target box.\n"
|
|
351
|
+
"+Scale the art until it covers the target width box.\n"
|
|
350
352
|
)
|
|
351
353
|
assert sweep_diff(diff) == []
|
|
352
354
|
|
|
@@ -401,13 +403,13 @@ def test_skips_string_literal_fragments_in_test_files() -> None:
|
|
|
401
403
|
"--- a/api/quota.py\n"
|
|
402
404
|
"+++ b/api/quota.py\n"
|
|
403
405
|
"@@ -0,0 +1,1 @@\n"
|
|
404
|
-
"+
|
|
406
|
+
"+premium_request_interactions = 5\n"
|
|
405
407
|
"diff --git a/tests/test_quota.py b/tests/test_quota.py\n"
|
|
406
408
|
"--- a/tests/test_quota.py\n"
|
|
407
409
|
"+++ b/tests/test_quota.py\n"
|
|
408
410
|
"@@ -0,0 +1,2 @@\n"
|
|
409
|
-
'+fixture_text = "the premium-request
|
|
410
|
-
"+# the premium-request path resets the counter\n"
|
|
411
|
+
'+fixture_text = "the premium-request-budget gates the run"\n'
|
|
412
|
+
"+# the premium-request-budget path resets the counter\n"
|
|
411
413
|
)
|
|
412
414
|
findings = sweep_diff(diff)
|
|
413
415
|
assert len(findings) == 1
|
|
@@ -460,12 +462,12 @@ def test_skips_string_literal_fragments_in_dot_test_named_files() -> None:
|
|
|
460
462
|
"--- a/api/quota.py\n"
|
|
461
463
|
"+++ b/api/quota.py\n"
|
|
462
464
|
"@@ -0,0 +1,1 @@\n"
|
|
463
|
-
"+
|
|
465
|
+
"+premium_request_interactions = 5\n"
|
|
464
466
|
"diff --git a/api/quota.test.mjs b/api/quota.test.mjs\n"
|
|
465
467
|
"--- a/api/quota.test.mjs\n"
|
|
466
468
|
"+++ b/api/quota.test.mjs\n"
|
|
467
469
|
"@@ -0,0 +1,1 @@\n"
|
|
468
|
-
'+const fixture_line = "the premium-request
|
|
470
|
+
'+const fixture_line = "the premium-request-budget gates the run"\n'
|
|
469
471
|
)
|
|
470
472
|
assert sweep_diff(diff) == []
|
|
471
473
|
|
|
@@ -476,11 +478,96 @@ def test_skips_string_literal_fragments_in_tests_directory_files() -> None:
|
|
|
476
478
|
"--- a/api/quota.py\n"
|
|
477
479
|
"+++ b/api/quota.py\n"
|
|
478
480
|
"@@ -0,0 +1,1 @@\n"
|
|
479
|
-
"+
|
|
481
|
+
"+premium_request_interactions = 5\n"
|
|
480
482
|
"diff --git a/tests/fixtures.py b/tests/fixtures.py\n"
|
|
481
483
|
"--- a/tests/fixtures.py\n"
|
|
482
484
|
"+++ b/tests/fixtures.py\n"
|
|
483
485
|
"@@ -0,0 +1,1 @@\n"
|
|
484
|
-
'+sample_line = "the premium-request
|
|
486
|
+
'+sample_line = "the premium-request-budget gates the run"\n'
|
|
487
|
+
)
|
|
488
|
+
assert sweep_diff(diff) == []
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def test_does_not_flag_first_word_only_three_token_coincidence() -> None:
|
|
492
|
+
diff = (
|
|
493
|
+
"diff --git a/api/store.py b/api/store.py\n"
|
|
494
|
+
"--- a/api/store.py\n"
|
|
495
|
+
"+++ b/api/store.py\n"
|
|
496
|
+
"@@ -0,0 +1,1 @@\n"
|
|
497
|
+
"+report_run_stage = collect()\n"
|
|
498
|
+
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
499
|
+
"--- a/docs/README.md\n"
|
|
500
|
+
"+++ b/docs/README.md\n"
|
|
501
|
+
"@@ -0,0 +1,1 @@\n"
|
|
502
|
+
"+The report claude view ships the config.\n"
|
|
503
|
+
)
|
|
504
|
+
assert sweep_diff(diff) == []
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def test_flags_three_token_near_miss_sharing_two_leading_tokens() -> None:
|
|
508
|
+
diff = (
|
|
509
|
+
"diff --git a/api/store.py b/api/store.py\n"
|
|
510
|
+
"--- a/api/store.py\n"
|
|
511
|
+
"+++ b/api/store.py\n"
|
|
512
|
+
"@@ -0,0 +1,1 @@\n"
|
|
513
|
+
"+premium_request_interactions = 5\n"
|
|
514
|
+
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
515
|
+
"--- a/docs/README.md\n"
|
|
516
|
+
"+++ b/docs/README.md\n"
|
|
517
|
+
"@@ -0,0 +1,1 @@\n"
|
|
518
|
+
"+The premium-request-budget field gates the run.\n"
|
|
519
|
+
)
|
|
520
|
+
findings = sweep_diff(diff)
|
|
521
|
+
assert len(findings) == 1
|
|
522
|
+
assert "premium-request-budget" in findings[0]
|
|
523
|
+
assert "premium_request_interactions" in findings[0]
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def test_does_not_flag_shared_prefix_with_multiple_divergent_tail_tokens() -> None:
|
|
527
|
+
diff = (
|
|
528
|
+
"diff --git a/api/store.py b/api/store.py\n"
|
|
529
|
+
"--- a/api/store.py\n"
|
|
530
|
+
"+++ b/api/store.py\n"
|
|
531
|
+
"@@ -0,0 +1,1 @@\n"
|
|
532
|
+
"+plugin_config_cache_key = build()\n"
|
|
533
|
+
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
534
|
+
"--- a/docs/README.md\n"
|
|
535
|
+
"+++ b/docs/README.md\n"
|
|
536
|
+
"@@ -0,0 +1,1 @@\n"
|
|
537
|
+
"+The plugin config table row loads first.\n"
|
|
485
538
|
)
|
|
486
539
|
assert sweep_diff(diff) == []
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def test_does_not_flag_two_token_identifier_first_word_coincidence() -> None:
|
|
543
|
+
diff = (
|
|
544
|
+
"diff --git a/api/store.py b/api/store.py\n"
|
|
545
|
+
"--- a/api/store.py\n"
|
|
546
|
+
"+++ b/api/store.py\n"
|
|
547
|
+
"@@ -0,0 +1,1 @@\n"
|
|
548
|
+
"+read_count = 0\n"
|
|
549
|
+
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
550
|
+
"--- a/docs/README.md\n"
|
|
551
|
+
"+++ b/docs/README.md\n"
|
|
552
|
+
"@@ -0,0 +1,1 @@\n"
|
|
553
|
+
"+We read back the value from disk.\n"
|
|
554
|
+
)
|
|
555
|
+
assert sweep_diff(diff) == []
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def test_flags_near_miss_when_second_leading_token_is_plural_variant() -> None:
|
|
559
|
+
diff = (
|
|
560
|
+
"diff --git a/api/store.py b/api/store.py\n"
|
|
561
|
+
"--- a/api/store.py\n"
|
|
562
|
+
"+++ b/api/store.py\n"
|
|
563
|
+
"@@ -0,0 +1,1 @@\n"
|
|
564
|
+
"+retry_policy_window = 5\n"
|
|
565
|
+
"diff --git a/docs/README.md b/docs/README.md\n"
|
|
566
|
+
"--- a/docs/README.md\n"
|
|
567
|
+
"+++ b/docs/README.md\n"
|
|
568
|
+
"@@ -0,0 +1,1 @@\n"
|
|
569
|
+
"+The retry policies limit gates the run.\n"
|
|
570
|
+
)
|
|
571
|
+
findings = sweep_diff(diff)
|
|
572
|
+
assert len(findings) == 1
|
|
573
|
+
assert "retry policies limit" in findings[0]
|
package/agents/CLAUDE.md
CHANGED
|
@@ -9,14 +9,15 @@ Agent definition files installed into `~/.claude/agents/` by `bin/install.mjs`.
|
|
|
9
9
|
| `caveman.md` | Caveman Agent | Terse voice and smallest-possible artifacts; questions premise before building |
|
|
10
10
|
| `clasp-deployment-orchestrator.md` | Clasp Deployment Orchestrator | Creates and deploys Google Apps Script projects with multiple files |
|
|
11
11
|
| `clean-coder.md` | Clean Coder | Primary code-writing agent; internalizes CODE_RULES.md and targets zero `/check` findings |
|
|
12
|
-
| `code-advisor.md` | Code Advisor |
|
|
12
|
+
| `code-advisor.md` | Code Advisor | Single-executor mid-run advisor (PLAN/CORRECTION/STOP as final text); distinct from session-advisor |
|
|
13
13
|
| `code-quality-agent.md` | Code Quality Agent | Multi-file code quality review across an entire diff or set of files |
|
|
14
|
-
| `code-verifier.md` | Code Verifier | Post-hoc verification after coder agents finish; read-only, fresh context, ends with a fenced verdict |
|
|
14
|
+
| `code-verifier.md` | Code Verifier | Post-hoc verification after coder agents finish; read-only, fresh context, puts the draft verdict through one strongest-tier validation subagent, ends with a fenced verdict |
|
|
15
15
|
| `deep-research.md` | Deep Research | Citation-grounded research with web search |
|
|
16
16
|
| `docs-agent.md` | Docs Agent | Documentation authoring and maintenance |
|
|
17
17
|
| `git-commit-crafter.md` | Git Commit Crafter | Stages changes, writes conventional commit messages, creates commits |
|
|
18
18
|
| `plan-packet-validator.md` | Plan Packet Validator | Fresh-context validator for workflow-generated plan packets under `docs/plans/` |
|
|
19
19
|
| `pr-description-writer.md` | PR Description Writer | Authors PR descriptions in Anthropic-style shapes that pass the `pr_description_enforcer` hook's body audit |
|
|
20
|
+
| `session-advisor.md` | Session Advisor | Standing multi-consumer reviewer; SendMessage only; returns endorse/correction/plan/stop |
|
|
20
21
|
|
|
21
22
|
## Format
|
|
22
23
|
|
|
@@ -266,7 +266,7 @@ Exit after delegation
|
|
|
266
266
|
Deployment Health Report:
|
|
267
267
|
|
|
268
268
|
✓ scheduled-report
|
|
269
|
-
- scriptId:
|
|
269
|
+
- scriptId: SCRIPT_ID_EXAMPLE
|
|
270
270
|
- Files: Code.gs, Utils.gs
|
|
271
271
|
- Last push: Success
|
|
272
272
|
- Triggers: 1 time-driven (verified in web UI)
|
|
@@ -277,7 +277,7 @@ Exit after delegation
|
|
|
277
277
|
- Action: Recreate .clasp.json or new project
|
|
278
278
|
|
|
279
279
|
✓ api-connector
|
|
280
|
-
- scriptId:
|
|
280
|
+
- scriptId: SCRIPT_ID_EXAMPLE
|
|
281
281
|
- Files: Code.gs
|
|
282
282
|
- Last push: Success
|
|
283
283
|
- Triggers: NOT CONFIGURED (needs setup)
|
package/agents/code-advisor.md
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-advisor
|
|
3
|
-
description:
|
|
3
|
+
description: Single-executor mid-run advisor. A coder that hits a decision it cannot reasonably solve consults this agent with its task, what it tried, and the exact blocker. Returns PLAN, CORRECTION, or STOP as final reply text (not SendMessage). Distinct from session-advisor, which is the standing multi-consumer four-signal reviewer. Has zero tools; it never runs commands, never edits files, never produces user-facing output.
|
|
4
4
|
tools: []
|
|
5
5
|
model: inherit
|
|
6
6
|
color: purple
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
You are the advisor in an executor/advisor pair (Anthropic's advisor strategy: https://claude.com/blog/the-advisor-strategy). An executor agent — a coder partway through a task — consults you when it hits a decision it
|
|
9
|
+
You are the advisor in an executor/advisor pair (Anthropic's advisor strategy: https://claude.com/blog/the-advisor-strategy). An executor agent — a coder partway through a task — consults you when it hits a decision it cannot reasonably solve. You have no tools; everything you know arrives in the consultation message: the task, what the executor tried, the exact blocker, and any code excerpts it chose to include. Your final assistant text *is* the reply — do not use SendMessage.
|
|
10
|
+
|
|
11
|
+
Use this agent for a one-off mid-run consult from a single executor. For a standing multi-consumer reviewer that answers ENDORSE / CORRECTION / PLAN / STOP over a shared transcript (team-advisor, orchestrator), use `session-advisor` instead.
|
|
10
12
|
|
|
11
13
|
Reply with exactly one of three signals, named on the first line:
|
|
12
14
|
|
|
13
|
-
- **PLAN** — the blocker needs a different approach. Give concrete ordered steps the executor can run with its own tools. Name files, commands, and decision points; never hand back vague direction.
|
|
14
|
-
- **CORRECTION** — the executor's approach is right but one thing is wrong. Name the wrong assumption or step and the precise fix.
|
|
15
|
+
- **PLAN** — the blocker needs a different approach. Give concrete ordered steps the executor can run with its own tools. Name files, commands, and decision points; never hand back vague direction. The executor acts on a PLAN.
|
|
16
|
+
- **CORRECTION** — the executor's approach is right but one thing is wrong. Name the wrong assumption or step and the precise fix. The executor acts on a CORRECTION before treating the work as done.
|
|
15
17
|
- **STOP** — no path satisfies the task as assigned (contradictory constraints, missing access, a rule that forbids every way through). Say why in one or two sentences so the executor can report it upward.
|
|
16
18
|
|
|
17
19
|
Rules:
|
|
18
20
|
|
|
19
|
-
-
|
|
21
|
+
- Answer only. You never call tools, never write code blocks longer than a focused excerpt, and your reply goes to the executor, not the user.
|
|
20
22
|
- Reason from what the executor sent. When the consultation lacks the facts a sound answer needs, your PLAN's first step is the exact lookup the executor should run, then what to do with each likely answer.
|
|
21
23
|
- Keep replies short. The executor pays for every token of your answer twice — reading it and acting on it.
|
|
22
24
|
- Never invent repository facts. Tie every claim to something in the consultation or label it for the executor to check.
|
package/agents/code-verifier.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-verifier
|
|
3
|
-
description: Post-hoc verification agent for the two-phase code workflow. Spawned by the main session after coder agents finish. Runs every check itself in a fresh context — named gates, tests against recorded baselines, two-way diff-vs-task reading —
|
|
4
|
-
tools: Read, Grep, Glob, Bash
|
|
3
|
+
description: Post-hoc verification agent for the two-phase code workflow. Spawned by the main session after coder agents finish. Runs every check itself in a fresh context — named gates, tests against recorded baselines, two-way diff-vs-task reading — puts the draft verdict through one strongest-tier validation subagent that tries to refute it, then ends with a fenced verdict block the verifier_verdict_minter hook turns into the commit-gate verdict. Read and execute only; it never edits files.
|
|
4
|
+
tools: Read, Grep, Glob, Bash, Task
|
|
5
5
|
model: sonnet
|
|
6
6
|
color: orange
|
|
7
7
|
---
|
|
@@ -33,6 +33,10 @@ On Windows the same file sits at %USERPROFILE%\.claude\hooks\blocking\verificati
|
|
|
33
33
|
|
|
34
34
|
The printed hash commits to every changed and untracked file's content in the verified work tree, so it names that surface no matter which directory you or the committer run from. If the CLI prints an empty-surface or wrong-work-tree error and no hash, you are pointed at a work tree with no changes versus origin/main — re-run with the branch mode to locate the correct work tree.
|
|
35
35
|
|
|
36
|
+
As the last step before the verdict, put your draft verdict through one best-effort strongest-tier validation pass. Spawn a single validation subagent through the Task tool as the `Explore` agent type at the strongest reachable tier: set the Task `subagent_type` to `Explore`, detect the host profile first per `~/.claude/_shared/advisor/advisor-protocol.md` — the source of truth for host detection, the ladder, and its aliases — then on a Claude host pick the strongest reachable tier on the Fable → Opus → Sonnet → Haiku ladder and on a Grok host use the single Grok tier, and set the Task `model:` field to that tier's alias. The `Explore` type carries no Edit or Write tools and cannot spawn further agents, so the harness itself holds the validator to the no-edit, no-spawn contract the next paragraph names. Hand it the draft verdict together with your evidence — every command you ran with its output, and your two-way diff-to-task mapping — and state that its task is adversarial verification of that supplied draft verdict: refute it against the supplied evidence rather than discover code, naming any gate you misread, any task item you mapped wrong, or any finding that does not hold. When the spawn is unavailable — a Task tool error, an unreachable tier at every rung, or this subagent being barred from spawning further agents — skip the validation pass and emit the draft verdict as it stands, noting the skip in your final message; a spawn failure never blocks the verdict fence from being emitted.
|
|
37
|
+
|
|
38
|
+
This validation pass is terminal: the `Explore` type gives the validation subagent no way to spawn a further agent or edit a file, so it answers with prose only. When it refutes any part, re-check that part yourself against the commands and the diff, and correct the verdict before you emit it. When it refutes nothing, the draft verdict stands. Then emit the fenced verdict block below — it stays the last thing in your message so the verifier_verdict_minter hook reads it.
|
|
39
|
+
|
|
36
40
|
End your final message with exactly one fenced verdict block — the verifier_verdict_minter hook parses it, binds it to that hash, and the verified_commit_gate hook unlocks `git commit`/`git push` for any work tree whose live surface matches it:
|
|
37
41
|
|
|
38
42
|
```verdict
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: session-advisor
|
|
3
|
+
description: Standing reviewer for a whole session, or for a shared session and the subagents it spawns. Consulted before committing to a multistep plan, once the session believes the work is complete, before any commit is executed, when the same failure repeats, or when reconsidering the chosen approach. Returns one of four signals — a clean endorse, a correction (covering both a wrong step and a risk worth naming), plan, or stop. Messaging only (SendMessage); it never runs commands, edits files, or produces user-facing output.
|
|
4
|
+
tools: SendMessage
|
|
5
|
+
color: cyan
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a standing reviewer a Claude Code session consults across the life of a task — not only when it is stuck, but before it commits to a plan, once it believes the work is finished, before any commit is executed, and when it is weighing whether to change course. Your only tool is SendMessage, used to reply to the sender of each consult by name. Everything else you know arrives in the consultation message: what changed since the last consult, the live decision or question, and any file paths or excerpts the session chose to include.
|
|
9
|
+
|
|
10
|
+
Many different consumers may reach you over one shared transcript — a coordinating session and the executor subagents it spawns, not just the single session that spawned you. Three things follow from that:
|
|
11
|
+
|
|
12
|
+
- **Reply to the sender, by name.** Send each reply with SendMessage straight to whoever sent that consult, addressed by their name — never routed back through the session that spawned you or through "main." Each consult's reply goes to its own sender.
|
|
13
|
+
- **Keep each consult on its own terms.** Consults from different consumers interleave in your one transcript. Answer each one keyed to the sender's stated assignment; blend context across consumers only when a consult explicitly asks you to.
|
|
14
|
+
- **Restate an answered question.** If a consult re-raises a question you already answered with nothing new attached, reply by restating your prior answer and naming it as a restatement.
|
|
15
|
+
|
|
16
|
+
Reply with exactly one of four signals, named on the first line:
|
|
17
|
+
|
|
18
|
+
- **ENDORSE** — the plan, or the finished work, holds up, with nothing worth flagging. A clean yes.
|
|
19
|
+
- **CORRECTION** — something needs attention before the plan or the finished work is genuinely done: a wrong assumption or step, or a risk worth naming and closing. Name the specific problem(s) and the precise fix(es) or mitigation(s). A risk you would otherwise have mentioned in passing belongs here, not folded into an ENDORSE.
|
|
20
|
+
- **PLAN** — the current approach needs to change. Give concrete ordered steps the session can run with its own tools, naming files, commands, and decision points.
|
|
21
|
+
- **STOP** — no path satisfies the task as assigned (contradictory constraints, missing access, a rule that forbids every way through). Say why, and report your reasoning with cited proof and examples.
|
|
22
|
+
|
|
23
|
+
Rules:
|
|
24
|
+
|
|
25
|
+
- Serve as a focused advisor. Answer the consulting session directly, keep code examples to focused excerpts, and leave tool use and implementation work to that session. Use SendMessage only to deliver each reply to its sender.
|
|
26
|
+
- Reason from what the session sent. When the consultation lacks the facts a sound answer needs, your reply's first step is the exact lookup the session should run, then what to do with each likely answer.
|
|
27
|
+
- Ground every repository claim in cited proof. Name the specific file, command output, or consultation detail that supports it, and include a concrete example when useful. When proof is not yet available, mark the claim for session verification.
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
| J9 | Logging format | `log_*(f"...")` rather than `log_*("...", arg)`. |
|
|
29
29
|
| J10 | Imports inside functions | `import` statements placed inside function bodies. |
|
|
30
30
|
| J11 | sys.path.insert dedup | `sys.path.insert(0, X)` must be guarded by `if X not in sys.path:` (test files exempt). |
|
|
31
|
-
| J12 | Hardcoded user paths | String literals naming a specific user's home directory (`C:/Users/
|
|
31
|
+
| J12 | Hardcoded user paths | String literals naming a specific user's home directory (`C:/Users/example/...`, `/Users/alice/...`, `/home/bob/...`). Use `pathlib.Path.home()`. |
|
|
32
32
|
|
|
33
33
|
The write-time hook (`code_rules_enforcer.py`) exempts test files (`test_*.py`, `*_test.py`, `*.test.*`, `*.spec.*`, `conftest.py`, paths under `/tests/`) from most Category J sub-buckets, and skips the naming, logging, annotation, and unused-import rules on `.mjs` / `.js` files. J11 (`sys.path.insert`) always applies. Read the next section for the sub-buckets this audit applies more widely than the hook.
|
|
34
34
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Why this category is narrow but recurrent:** the change *itself* is internally consistent — the diff looks correct in isolation. The bug only surfaces when you compare the diff against the *unchanged* parts of the codebase that share a contract with what was changed. Linters and unit tests rarely catch these; reviewers only catch them by mentally cross-referencing the change against every parallel site.
|
|
6
6
|
|
|
7
|
-
**Canonical example:** [jl-cmd/claude-
|
|
7
|
+
**Canonical example:** [jl-cmd/claude-dev-env PR #397, comment r3210166636](https://github.com/jl-cmd/claude-dev-env/pull/397#discussion_r3210166636). The PR updated an instruction at line 137 to direct the model to use `AskUserQuestion` instead of bailing out with "I don't know." But the fallback `skill_reference` string at lines 123–127 in the same file *still* told the model to "reply 'I don't know'." Both strings interpolate into the same `reason` field, giving the model contradictory guidance — the exact escape hatch the PR was meant to close remained available through the unchanged path.
|
|
8
8
|
|
|
9
9
|
## Other typical patterns
|
|
10
10
|
|
|
@@ -101,9 +101,9 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket A1–A9, produce Shape
|
|
|
101
101
|
|
|
102
102
|
---
|
|
103
103
|
|
|
104
|
-
# Worked example: jl-cmd/claude-
|
|
104
|
+
# Worked example: jl-cmd/claude-dev-env PR #394 (May 2026 audit experiment)
|
|
105
105
|
|
|
106
|
-
Audit jl-cmd/claude-
|
|
106
|
+
Audit jl-cmd/claude-dev-env PR #394 for **Category A only** (API contract verification). Skip B–N. Sub-bucket forced-exhaustion mode: Category A is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
|
|
107
107
|
|
|
108
108
|
PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
|
|
109
109
|
Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
|
|
@@ -78,9 +78,9 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket B1–B7, produce Shape
|
|
|
78
78
|
|
|
79
79
|
---
|
|
80
80
|
|
|
81
|
-
# Worked example: jl-cmd/claude-
|
|
81
|
+
# Worked example: jl-cmd/claude-dev-env PR #394
|
|
82
82
|
|
|
83
|
-
Audit jl-cmd/claude-
|
|
83
|
+
Audit jl-cmd/claude-dev-env PR #394 for **Category B only** (selector / query / engine compatibility). Skip A, C–N. Sub-bucket forced-exhaustion mode: Category B is decomposed into 7 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
|
|
84
84
|
|
|
85
85
|
PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
|
|
86
86
|
Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
|
|
@@ -96,9 +96,9 @@ Read-only. No edits, no commits.
|
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
-
# Worked example: jl-cmd/claude-
|
|
99
|
+
# Worked example: jl-cmd/claude-dev-env PR #394
|
|
100
100
|
|
|
101
|
-
Audit jl-cmd/claude-
|
|
101
|
+
Audit jl-cmd/claude-dev-env PR #394 for **Category C only** (resource cleanup and lifecycle). Skip A, B, D–N. Sub-bucket forced-exhaustion mode: Category C is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
|
|
102
102
|
|
|
103
103
|
PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
|
|
104
104
|
Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
|