claude-dev-env 1.93.1 → 1.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +35 -27
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -2
- package/_shared/advisor/scripts/model_tier_run_validator.py +23 -15
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +81 -17
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/audit-contract.md +1 -1
- package/_shared/pr-loop/gh-payloads.md +3 -3
- package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
- package/_shared/pr-loop/scripts/README.md +2 -2
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
- package/_shared/pr-loop/state-schema.md +3 -22
- package/bin/CLAUDE.md +11 -2
- package/bin/ever-shipped-skills.mjs +70 -0
- package/bin/install.mjs +138 -13
- package/bin/install.prune.test.mjs +457 -0
- package/bin/install.test.mjs +10 -4
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_shared.py +82 -0
- package/hooks/blocking/code_rules_test_layout.py +9 -3
- package/hooks/blocking/pii_commit_command.py +398 -0
- package/hooks/blocking/pii_payload_scan.py +133 -0
- package/hooks/blocking/pii_prevention_blocker.py +151 -449
- package/hooks/blocking/plain_language_blocker.py +138 -4
- package/hooks/blocking/sensitive_file_protector.py +114 -48
- package/hooks/blocking/tdd_enforcer.py +9 -2
- package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_code_rules_shared.py +181 -0
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
- package/hooks/blocking/test_sensitive_file_protector.py +185 -0
- package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +4 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
- package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/hooks/pyproject.toml +75 -4
- package/hooks/validators/CLAUDE.md +1 -1
- package/hooks/validators/README.md +2 -0
- package/hooks/validators/python_style_checks.py +114 -136
- package/hooks/validators/python_style_helpers.py +95 -0
- package/hooks/validators/test_python_style_checks.py +0 -164
- package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
- package/hooks/validators/test_python_style_fixes.py +251 -0
- package/hooks/validators/test_python_style_helpers.py +125 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/anti-corollary-tests.md +69 -0
- package/rules/bdd.md +1 -3
- package/rules/code-reviews.md +1 -1
- package/rules/gh-paginate.md +1 -1
- package/rules/plain-language.md +2 -0
- package/scripts/CLAUDE.md +10 -1
- package/scripts/Get-SessionAccount.ps1 +210 -0
- package/scripts/tests/CLAUDE.md +15 -1
- package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
- package/skills/CLAUDE.md +6 -27
- package/skills/anthropic-plan/SKILL.md +1 -13
- package/skills/autoconverge/CLAUDE.md +5 -2
- package/skills/autoconverge/SKILL.md +112 -387
- package/skills/autoconverge/reference/CLAUDE.md +4 -1
- package/skills/autoconverge/reference/closing-report.md +9 -2
- package/skills/autoconverge/reference/convergence.md +26 -22
- package/skills/autoconverge/reference/headless-safety.md +44 -0
- package/skills/autoconverge/reference/multi-pr.md +88 -0
- package/skills/autoconverge/reference/self-closing-loop.md +84 -0
- package/skills/autoconverge/reference/stop-conditions.md +21 -13
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
- package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +286 -96
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/CONSTRAINTS.md +3 -3
- package/skills/bugteam/PROMPTS.md +8 -19
- package/skills/bugteam/SKILL.md +13 -14
- package/skills/bugteam/reference/CLAUDE.md +1 -1
- package/skills/bugteam/reference/README.md +2 -3
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
- package/skills/bugteam/reference/github-pr-reviews.md +56 -38
- package/skills/bugteam/reference/team-setup.md +11 -11
- package/skills/bugteam/scripts/CLAUDE.md +6 -6
- package/skills/bugteam/scripts/README.md +20 -8
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
- package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
- package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
- package/skills/closeout/SKILL.md +153 -0
- package/skills/closeout/reference/handoff-prompt-template.md +72 -0
- package/skills/closeout/reference/issue-body-templates.md +108 -0
- package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
- package/skills/copilot-finding-triage/SKILL.md +80 -28
- package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
- package/skills/copilot-finding-triage/templates/notification.md +8 -3
- package/skills/everything-search/SKILL.md +1 -2
- package/skills/grokify/SKILL.md +58 -0
- package/skills/grokify/templates/handoff-template.md +92 -0
- package/skills/orchestrator/SKILL.md +35 -25
- package/skills/orchestrator-refresh/SKILL.md +15 -11
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +132 -233
- package/skills/pr-converge/reference/convergence-gates.md +102 -66
- package/skills/pr-converge/reference/examples.md +37 -25
- package/skills/pr-converge/reference/fix-protocol.md +7 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
- package/skills/pr-converge/reference/per-tick.md +64 -41
- package/skills/pr-converge/reference/state-schema.md +27 -2
- package/skills/pr-converge/scripts/CLAUDE.md +8 -0
- package/skills/pr-converge/scripts/README.md +4 -3
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
- package/skills/pr-converge/scripts/check_convergence.py +189 -645
- package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
- package/skills/pr-converge/scripts/conftest.py +8 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
- package/skills/pr-fix-protocol/SKILL.md +20 -20
- package/skills/privacy-hygiene/SKILL.md +4 -3
- package/skills/reviewer-gates/SKILL.md +26 -8
- package/skills/session-log/SKILL.md +11 -12
- package/skills/session-tidy/SKILL.md +40 -30
- package/skills/skill-builder/CLAUDE.md +13 -10
- package/skills/skill-builder/SKILL.md +31 -31
- package/skills/skill-builder/references/CLAUDE.md +10 -6
- package/skills/skill-builder/references/delegation-map.md +13 -4
- package/skills/skill-builder/references/description-field.md +113 -0
- package/skills/skill-builder/references/self-audit-checklist.md +21 -4
- package/skills/skill-builder/references/skill-modularity.md +113 -0
- package/skills/skill-builder/references/skill-types.md +4 -0
- package/skills/skill-builder/templates/CLAUDE.md +4 -4
- package/skills/skill-builder/templates/gap-analysis.md +25 -1
- package/skills/skill-builder/workflows/CLAUDE.md +6 -4
- package/skills/skill-builder/workflows/improve-skill.md +17 -16
- package/skills/skill-builder/workflows/new-skill.md +34 -10
- package/skills/skill-builder/workflows/polish-skill.md +59 -43
- package/skills/team-advisor/SKILL.md +15 -11
- package/skills/usage-pause/SKILL.md +10 -14
- package/system-prompts/software-engineer.xml +7 -6
- package/hooks/validators/test_verify_paths.py +0 -32
- package/hooks/validators/verify_paths.py +0 -57
- package/skills/bdd-protocol/CLAUDE.md +0 -26
- package/skills/bdd-protocol/SKILL.md +0 -30
- package/skills/bdd-protocol/references/CLAUDE.md +0 -10
- package/skills/bdd-protocol/references/anti-patterns.md +0 -26
- package/skills/bdd-protocol/references/example-mapping.md +0 -23
- package/skills/bg-agent/CLAUDE.md +0 -17
- package/skills/bg-agent/SKILL.md +0 -69
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
- package/skills/caveman/CLAUDE.md +0 -15
- package/skills/caveman/SKILL.md +0 -38
- package/skills/code/CLAUDE.md +0 -17
- package/skills/code/SKILL.md +0 -178
- package/skills/deep-research/CLAUDE.md +0 -17
- package/skills/deep-research/SKILL.md +0 -80
- package/skills/findbugs/CLAUDE.md +0 -20
- package/skills/findbugs/SKILL.md +0 -241
- package/skills/fixbugs/CLAUDE.md +0 -19
- package/skills/fixbugs/SKILL.md +0 -142
- package/skills/gh-paginate/CLAUDE.md +0 -18
- package/skills/gh-paginate/SKILL.md +0 -84
- package/skills/gotcha/CLAUDE.md +0 -33
- package/skills/gotcha/SKILL.md +0 -73
- package/skills/implement/CLAUDE.md +0 -27
- package/skills/implement/SKILL.md +0 -66
- package/skills/implement/scripts/CLAUDE.md +0 -22
- package/skills/implement/scripts/append_note.py +0 -133
- package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
- package/skills/implement/scripts/test_append_note.py +0 -191
- package/skills/log-audit/CLAUDE.md +0 -20
- package/skills/log-audit/SKILL.md +0 -66
- package/skills/log-audit/reference/CLAUDE.md +0 -9
- package/skills/log-audit/reference/charter.md +0 -52
- package/skills/log-audit/scripts/CLAUDE.md +0 -27
- package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
- package/skills/log-audit/scripts/collect_log_window.py +0 -199
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
- package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
- package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
- package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
- package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
- package/skills/logifix/CLAUDE.md +0 -36
- package/skills/logifix/SKILL.md +0 -69
- package/skills/logifix/scripts/CLAUDE.md +0 -16
- package/skills/logifix/scripts/logifix.ps1 +0 -205
- package/skills/monitor-open-prs/CLAUDE.md +0 -34
- package/skills/monitor-open-prs/SKILL.md +0 -88
- package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
- package/skills/monitor-open-prs/test_skill_contract.py +0 -27
- package/skills/post-audit-findings/SKILL.md +0 -80
- package/skills/pr-consistency-audit/CLAUDE.md +0 -34
- package/skills/pr-consistency-audit/SKILL.md +0 -116
- package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
- package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
- package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
- package/skills/pr-review-responder/CLAUDE.md +0 -35
- package/skills/pr-review-responder/EXAMPLES.md +0 -590
- package/skills/pr-review-responder/PRINCIPLES.md +0 -533
- package/skills/pr-review-responder/README.md +0 -168
- package/skills/pr-review-responder/SKILL.md +0 -203
- package/skills/pr-review-responder/TESTING.md +0 -350
- package/skills/pr-scope-resolve/SKILL.md +0 -45
- package/skills/pre-compact/CLAUDE.md +0 -24
- package/skills/pre-compact/SKILL.md +0 -134
- package/skills/qbug/CLAUDE.md +0 -40
- package/skills/qbug/SKILL.md +0 -387
- package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
- package/skills/refine/CLAUDE.md +0 -44
- package/skills/refine/SKILL.md +0 -262
- package/skills/refine/templates/CLAUDE.md +0 -17
- package/skills/refine/templates/implementation-notes-template.html +0 -56
- package/skills/refine/templates/plan-template.md +0 -60
- package/skills/research-mode/CLAUDE.md +0 -35
- package/skills/research-mode/SKILL.md +0 -53
- package/skills/structure-prompt/CLAUDE.md +0 -42
- package/skills/structure-prompt/SKILL.md +0 -44
- package/skills/structure-prompt/reference/CLAUDE.md +0 -28
- package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
- package/skills/structure-prompt/reference/block-classification.md +0 -27
- package/skills/structure-prompt/reference/canonical-case.md +0 -48
- package/skills/structure-prompt/reference/citation-depth.md +0 -70
- package/skills/structure-prompt/reference/cleanup.md +0 -33
- package/skills/structure-prompt/reference/constraints.md +0 -33
- package/skills/structure-prompt/reference/directives.md +0 -37
- package/skills/structure-prompt/reference/examples.md +0 -72
- package/skills/structure-prompt/reference/instantiation.md +0 -51
- package/skills/structure-prompt/reference/output-contract.md +0 -72
- package/skills/structure-prompt/reference/per-category.md +0 -23
- package/skills/structure-prompt/reference/persona.md +0 -38
- package/skills/structure-prompt/reference/research.md +0 -33
- package/skills/structure-prompt/reference/structure.md +0 -28
- package/skills/verified-build/CLAUDE.md +0 -33
- package/skills/verified-build/SKILL.md +0 -33
|
@@ -1,316 +1,41 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Smoke tests for the skill-path preflight wrap.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
-
|
|
5
|
-
- core.hooksPath pointing to the correct claude hooks dir: exits zero
|
|
6
|
-
- core.hooksPath pointing elsewhere (husky override): exits non-zero
|
|
7
|
-
- core.hooksPath with trailing slash: must still pass after normalization
|
|
3
|
+
Behavioral coverage lives at
|
|
4
|
+
``_shared/pr-loop/scripts/tests/test_preflight.py``.
|
|
8
5
|
"""
|
|
9
6
|
|
|
10
7
|
from __future__ import annotations
|
|
11
8
|
|
|
12
9
|
import importlib.util
|
|
13
|
-
import subprocess
|
|
14
|
-
import sys
|
|
15
10
|
from pathlib import Path
|
|
16
11
|
from types import ModuleType
|
|
17
|
-
from unittest.mock import MagicMock, patch
|
|
18
12
|
|
|
19
|
-
import pytest
|
|
20
13
|
|
|
21
|
-
|
|
22
|
-
def _load_preflight_module() -> ModuleType:
|
|
14
|
+
def _load_wrap_module() -> ModuleType:
|
|
23
15
|
module_path = Path(__file__).parent / "bugteam_preflight.py"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
assert "core.hooksPath" in captured.err
|
|
51
|
-
assert "npx claude-dev-env" in captured.err or "git config" in captured.err
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def test_should_exit_zero_when_core_hooks_path_points_to_claude_hooks(tmp_path: Path) -> None:
|
|
55
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
56
|
-
claude_hooks_path.mkdir(parents=True)
|
|
57
|
-
with patch("subprocess.run") as mock_run:
|
|
58
|
-
mock_run.return_value = _make_completed_process(
|
|
59
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
60
|
-
)
|
|
61
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
62
|
-
assert exit_code == 0
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def test_should_exit_nonzero_when_core_hooks_path_points_elsewhere(capsys: pytest.CaptureFixture[str]) -> None:
|
|
66
|
-
with patch("subprocess.run") as mock_run:
|
|
67
|
-
mock_run.return_value = _make_completed_process(
|
|
68
|
-
"/some/other/path/.husky\n", returncode=0
|
|
69
|
-
)
|
|
70
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
71
|
-
assert exit_code != 0
|
|
72
|
-
captured = capsys.readouterr()
|
|
73
|
-
assert "core.hooksPath" in captured.err
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def test_should_include_correction_commands_in_error_message(capsys: pytest.CaptureFixture[str]) -> None:
|
|
77
|
-
with patch("subprocess.run") as mock_run:
|
|
78
|
-
mock_run.return_value = _make_completed_process("", returncode=1)
|
|
79
|
-
bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
80
|
-
captured = capsys.readouterr()
|
|
81
|
-
assert (
|
|
82
|
-
"npx claude-dev-env" in captured.err
|
|
83
|
-
or "git config --global core.hooksPath" in captured.err
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def test_main_should_exit_nonzero_when_hooks_path_unset() -> None:
|
|
88
|
-
with patch("subprocess.run") as mock_run:
|
|
89
|
-
mock_run.return_value = _make_completed_process("", returncode=1)
|
|
90
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
91
|
-
assert exit_code != 0
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_main_should_continue_when_hooks_path_valid(tmp_path: Path) -> None:
|
|
95
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
96
|
-
claude_hooks_path.mkdir(parents=True)
|
|
97
|
-
with patch("subprocess.run") as mock_run:
|
|
98
|
-
mock_run.return_value = _make_completed_process(
|
|
99
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
100
|
-
)
|
|
101
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
102
|
-
assert exit_code == 0
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def test_should_accept_hooks_path_with_trailing_slash() -> None:
|
|
106
|
-
with patch("subprocess.run") as mock_run:
|
|
107
|
-
mock_run.return_value = _make_completed_process(
|
|
108
|
-
"/home/user/.claude/hooks/git-hooks/\n", returncode=0
|
|
109
|
-
)
|
|
110
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
111
|
-
assert exit_code == 0, (
|
|
112
|
-
"hooksPath with trailing slash must pass verification after normalization"
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def test_should_exit_zero_when_hooks_path_set_at_repo_scope(tmp_path: Path) -> None:
|
|
117
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
118
|
-
claude_hooks_path.mkdir(parents=True)
|
|
119
|
-
repo_root = tmp_path / "my-repo"
|
|
120
|
-
repo_root.mkdir()
|
|
121
|
-
with patch("subprocess.run") as mock_run:
|
|
122
|
-
mock_run.return_value = _make_completed_process(
|
|
123
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
124
|
-
)
|
|
125
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(repo_root)
|
|
126
|
-
assert exit_code == 0, (
|
|
127
|
-
"verify_git_hooks_path must accept a valid path returned by effective "
|
|
128
|
-
"config query (not restricted to --global scope)"
|
|
129
|
-
)
|
|
130
|
-
called_command = mock_run.call_args[0][0]
|
|
131
|
-
assert "--global" not in called_command, (
|
|
132
|
-
"verify_git_hooks_path must query effective config, not --global only"
|
|
133
|
-
)
|
|
134
|
-
assert "-C" in called_command, (
|
|
135
|
-
"verify_git_hooks_path must use git -C <repo_root> for repo-effective config"
|
|
136
|
-
)
|
|
137
|
-
dash_c_index = called_command.index("-C")
|
|
138
|
-
assert called_command[dash_c_index + 1] == str(repo_root), (
|
|
139
|
-
"git -C must receive the resolved repository root path"
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def test_verify_git_hooks_path_accepts_none_repository_root(tmp_path: Path) -> None:
|
|
144
|
-
"""When repository_root is None, the call must use git's cwd-effective config.
|
|
145
|
-
|
|
146
|
-
Binds the documented optional contract: passing None must not raise and must
|
|
147
|
-
omit the `-C <root>` arguments so git falls back to the working directory.
|
|
148
|
-
"""
|
|
149
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
150
|
-
claude_hooks_path.mkdir(parents=True)
|
|
151
|
-
with patch("subprocess.run") as mock_run:
|
|
152
|
-
mock_run.return_value = _make_completed_process(
|
|
153
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
154
|
-
)
|
|
155
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(None)
|
|
156
|
-
assert exit_code == 0
|
|
157
|
-
called_command = mock_run.call_args[0][0]
|
|
158
|
-
assert "-C" not in called_command, (
|
|
159
|
-
"verify_git_hooks_path(None) must omit -C so git uses cwd-effective config"
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def test_should_accept_hooks_path_with_backslash_and_trailing_slash() -> None:
|
|
164
|
-
with patch("subprocess.run") as mock_run:
|
|
165
|
-
mock_run.return_value = _make_completed_process(
|
|
166
|
-
"C:\\Users\\user\\.claude\\hooks\\git-hooks\\\n", returncode=0
|
|
167
|
-
)
|
|
168
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
169
|
-
assert exit_code == 0, (
|
|
170
|
-
"Windows hooksPath with trailing backslash must pass after normalization"
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def test_should_exit_nonzero_when_git_executable_not_found(
|
|
175
|
-
capsys: pytest.CaptureFixture[str],
|
|
176
|
-
) -> None:
|
|
177
|
-
"""Preflight must not crash with a traceback when git is missing from PATH."""
|
|
178
|
-
with patch("subprocess.run", side_effect=FileNotFoundError()):
|
|
179
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
180
|
-
assert exit_code != 0, (
|
|
181
|
-
"FileNotFoundError from subprocess.run must produce a non-zero exit, "
|
|
182
|
-
"not a propagated traceback"
|
|
183
|
-
)
|
|
184
|
-
captured = capsys.readouterr()
|
|
185
|
-
assert "git" in captured.err.lower(), (
|
|
186
|
-
"Error message must mention git so the user knows what is missing"
|
|
187
|
-
)
|
|
188
|
-
assert (
|
|
189
|
-
"npx claude-dev-env" in captured.err
|
|
190
|
-
or "git config --global core.hooksPath" in captured.err
|
|
191
|
-
), "Error message must include the enforcement-absent remediation hints"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def test_should_exit_nonzero_when_subprocess_run_raises_os_error(
|
|
195
|
-
capsys: pytest.CaptureFixture[str],
|
|
196
|
-
) -> None:
|
|
197
|
-
"""Preflight must surface a clean error for other OS-level git launch failures."""
|
|
198
|
-
with patch("subprocess.run", side_effect=OSError("permission denied")):
|
|
199
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
200
|
-
assert exit_code != 0, (
|
|
201
|
-
"OSError from subprocess.run must produce a non-zero exit, "
|
|
202
|
-
"not a propagated traceback"
|
|
203
|
-
)
|
|
204
|
-
captured = capsys.readouterr()
|
|
205
|
-
assert "bugteam_preflight" in captured.err, (
|
|
206
|
-
"Error message must be prefixed with the preflight tool name for context"
|
|
207
|
-
)
|
|
208
|
-
assert "permission denied" in captured.err, (
|
|
209
|
-
"Error message must include the underlying OSError detail for diagnosis"
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
def test_has_pytest_configuration_finds_pytest_ini(tmp_path: Path) -> None:
|
|
214
|
-
"""has_pytest_configuration must detect pytest.ini at the repo root.
|
|
215
|
-
|
|
216
|
-
Regression for loop1-17/loop1-18: the literals "pytest.ini",
|
|
217
|
-
"pyproject.toml", and "[tool.pytest" were inlined in production function
|
|
218
|
-
bodies; centralizing them in bugteam_scripts_constants and importing here
|
|
219
|
-
pins the contract.
|
|
220
|
-
"""
|
|
221
|
-
repository_root = tmp_path / "repo"
|
|
222
|
-
repository_root.mkdir()
|
|
223
|
-
(repository_root / "pytest.ini").write_text("[pytest]\n", encoding="utf-8")
|
|
224
|
-
assert bugteam_preflight.has_pytest_configuration(repository_root) is True
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def test_has_pytest_configuration_finds_pyproject_pytest_section(
|
|
228
|
-
tmp_path: Path,
|
|
229
|
-
) -> None:
|
|
230
|
-
repository_root = tmp_path / "repo"
|
|
231
|
-
repository_root.mkdir()
|
|
232
|
-
(repository_root / "pyproject.toml").write_text(
|
|
233
|
-
"[tool.pytest.ini_options]\nminversion = '6.0'\n", encoding="utf-8"
|
|
234
|
-
)
|
|
235
|
-
assert bugteam_preflight.has_pytest_configuration(repository_root) is True
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
def test_has_pytest_configuration_returns_false_without_either_file(
|
|
239
|
-
tmp_path: Path,
|
|
240
|
-
) -> None:
|
|
241
|
-
repository_root = tmp_path / "repo"
|
|
242
|
-
repository_root.mkdir()
|
|
243
|
-
assert bugteam_preflight.has_pytest_configuration(repository_root) is False
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def test_main_should_halt_when_env_var_lists_bugteam(
|
|
247
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
248
|
-
capsys: pytest.CaptureFixture[str],
|
|
249
|
-
) -> None:
|
|
250
|
-
"""CLAUDE_REVIEWS_DISABLED=bugteam must halt preflight with the dedicated exit code."""
|
|
251
|
-
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugteam")
|
|
252
|
-
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
253
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
254
|
-
assert exit_code == bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
255
|
-
captured = capsys.readouterr()
|
|
256
|
-
assert "CLAUDE_REVIEWS_DISABLED" in captured.err
|
|
257
|
-
assert "bugteam" in captured.err
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def test_main_should_continue_when_env_var_omits_bugteam(
|
|
261
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
262
|
-
tmp_path: Path,
|
|
263
|
-
) -> None:
|
|
264
|
-
"""CLAUDE_REVIEWS_DISABLED without the bugteam token must not halt preflight."""
|
|
265
|
-
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "copilot,bugbot")
|
|
266
|
-
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
267
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
268
|
-
claude_hooks_path.mkdir(parents=True)
|
|
269
|
-
with patch("subprocess.run") as mock_run:
|
|
270
|
-
mock_run.return_value = _make_completed_process(
|
|
271
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
272
|
-
)
|
|
273
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
274
|
-
assert exit_code != bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def test_main_should_halt_when_env_var_contains_uppercase_or_whitespace_bugteam_token(
|
|
278
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
279
|
-
) -> None:
|
|
280
|
-
"""Token matching must be case-insensitive and whitespace-tolerant."""
|
|
281
|
-
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", " BugTeam , copilot ")
|
|
282
|
-
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
283
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
284
|
-
assert exit_code == bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def _was_called_with_argument(
|
|
288
|
-
mock_subprocess_run: MagicMock, argument_token: str
|
|
289
|
-
) -> bool:
|
|
290
|
-
return any(
|
|
291
|
-
argument_token in each_call_args[0][0]
|
|
292
|
-
for each_call_args in mock_subprocess_run.call_args_list
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
def test_verify_git_hooks_path_invokes_self_heal_before_effective_query(
|
|
297
|
-
tmp_path: Path,
|
|
298
|
-
) -> None:
|
|
299
|
-
"""verify_git_hooks_path must delegate to the shared self-heal helper before --get."""
|
|
300
|
-
canonical_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
301
|
-
canonical_hooks_path.mkdir(parents=True)
|
|
302
|
-
with patch("subprocess.run") as mock_run:
|
|
303
|
-
mock_run.return_value = _make_completed_process(
|
|
304
|
-
str(canonical_hooks_path) + "\n", returncode=0
|
|
305
|
-
)
|
|
306
|
-
bugteam_preflight.verify_git_hooks_path(tmp_path)
|
|
307
|
-
assert _was_called_with_argument(mock_run, "--get-all"), (
|
|
308
|
-
"verify_git_hooks_path must run the --local --get-all read for self-heal"
|
|
309
|
-
)
|
|
310
|
-
assert _was_called_with_argument(mock_run, "--get"), (
|
|
311
|
-
"verify_git_hooks_path must still run the effective --get verification"
|
|
312
|
-
)
|
|
313
|
-
first_called_command = mock_run.call_args_list[0][0][0]
|
|
314
|
-
assert "--get-all" in first_called_command, (
|
|
315
|
-
"Self-heal must run BEFORE the effective config query, not after"
|
|
316
|
-
)
|
|
16
|
+
specification = importlib.util.spec_from_file_location(
|
|
17
|
+
"bugteam_preflight",
|
|
18
|
+
module_path,
|
|
19
|
+
)
|
|
20
|
+
assert specification is not None
|
|
21
|
+
assert specification.loader is not None
|
|
22
|
+
wrap_module = importlib.util.module_from_spec(specification)
|
|
23
|
+
specification.loader.exec_module(wrap_module)
|
|
24
|
+
return wrap_module
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_wrap_main_delegates_to_shared_module() -> None:
|
|
28
|
+
wrap_module = _load_wrap_module()
|
|
29
|
+
assert wrap_module.main.__module__ == "preflight"
|
|
30
|
+
shared_filename = Path(wrap_module.main.__code__.co_filename).name
|
|
31
|
+
assert shared_filename == "preflight.py"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_wrap_main_help_exits_cleanly() -> None:
|
|
35
|
+
wrap_module = _load_wrap_module()
|
|
36
|
+
try:
|
|
37
|
+
wrap_module.main(["--help"])
|
|
38
|
+
except SystemExit as exit_signal:
|
|
39
|
+
assert exit_signal.code in (0, None)
|
|
40
|
+
return
|
|
41
|
+
raise AssertionError("main(['--help']) must raise SystemExit")
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: closeout
|
|
3
|
+
description: >-
|
|
4
|
+
Harvests session obstacles into GitHub issues backed by quoted evidence at
|
|
5
|
+
session end, validates each draft with the user, dedupes against open and
|
|
6
|
+
closed issues, routes each to its repo, files children then a parent
|
|
7
|
+
checklist, and prints a computed cloud handoff prompt. Triggers: /closeout,
|
|
8
|
+
close out this session, close out the session, file the session obstacles,
|
|
9
|
+
session closeout, harvest session obstacles, end-of-session issue filing.
|
|
10
|
+
Near-miss: not session-log, which journals the session to the vault; closeout
|
|
11
|
+
files GitHub issues and prints a handoff prompt, and writes no session
|
|
12
|
+
journal.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Closeout
|
|
16
|
+
|
|
17
|
+
**Core principle:** At the end of a working session, turn the obstacles the session hit into user-approved GitHub issues — each backed by a quoted line — plus a computed cloud handoff prompt, never guessing, never filing without approval, never touching the host repo's live pipeline.
|
|
18
|
+
|
|
19
|
+
## Gotchas
|
|
20
|
+
|
|
21
|
+
Highest-signal content. Append a bullet each time a run fails in a new way.
|
|
22
|
+
|
|
23
|
+
- An obstacle stated from memory drifts. File only what the session can quote word for word — the actual error text, the exact command, the exact log line. A candidate that cannot be quoted goes under "Unverified candidates" for the user to judge, never into a filed issue as fact.
|
|
24
|
+
- Skipping the confirmation gate files noise to a shared server that other people read. Every parent and child draft passes the AskUserQuestion gate before any write.
|
|
25
|
+
- A body that leans on chat context reads as a puzzle to anyone who opens the issue cold. Write each body so a reader with zero session context acts on it: name the failure, the count, and the quoted line.
|
|
26
|
+
- A `--body` string mangles backticks on GitHub — they land as literal `\``. Every `gh` create and comment uses `--body-file <path>`.
|
|
27
|
+
- A dedupe search that skips closed issues re-files a twin the team already resolved. The search covers `--state all`.
|
|
28
|
+
- A volatile path in an issue body breaks the moment the job scratch is cleaned. Keep temp dirs, worktrees, and `$CLAUDE_JOB_DIR` out of every body.
|
|
29
|
+
|
|
30
|
+
## When this skill applies
|
|
31
|
+
|
|
32
|
+
Run this skill **at the end** of a working session, from inside that same session, when the session hit obstacles worth filing — hook blocks, gates that fired wrongly, tools that failed, forced workarounds, dead ends.
|
|
33
|
+
|
|
34
|
+
Triggers: `/closeout`, "close out this session", "file the session obstacles", "session closeout", "harvest session obstacles", "end-of-session issue filing".
|
|
35
|
+
|
|
36
|
+
**Refusal cases — first match wins:**
|
|
37
|
+
|
|
38
|
+
- **Mid-session, work still open.** Respond: `Closeout runs at session end. Keep working, and run /closeout once the session's work is done.`
|
|
39
|
+
- **Asked to journal the session.** Respond: `Closeout files GitHub issues; it does not write a session journal. For a session report to the vault, use /session-log.`
|
|
40
|
+
- **No obstacles this session.** Respond: `No obstacles to file — the session hit no hook blocks, tool failures, or dead ends worth an issue. Nothing to close out.`
|
|
41
|
+
|
|
42
|
+
## The process
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
- [ ] Phase 1 — Harvest obstacles from the three sources; quote verbatim evidence
|
|
46
|
+
- [ ] Phase 1 — Run the PII pass over every candidate
|
|
47
|
+
- [ ] Phase 2 — Draft the parent + children set
|
|
48
|
+
- [ ] Phase 2 — Confirmation gate: AskUserQuestion, file only on approval
|
|
49
|
+
- [ ] Phase 3 — Dedupe each candidate against open and closed issues
|
|
50
|
+
- [ ] Phase 4 — Route each issue to its repo
|
|
51
|
+
- [ ] Phase 5 — File children, then the parent checklist
|
|
52
|
+
- [ ] Phase 6 — Print the computed cloud handoff prompt in chat
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Track the phases on the task list
|
|
56
|
+
|
|
57
|
+
At invocation, copy the six phases onto the session task list — one task each via TaskCreate: harvest, draft + user validation, dedupe, repo routing, filing, handoff prompt. Mark a task `in_progress` with TaskUpdate when its phase starts and `completed` when the phase finishes.
|
|
58
|
+
|
|
59
|
+
Hold one line on the filing task: never mark it `completed` while any planned issue is still uncreated. A filing phase that lands fewer issues than the approved set keeps the filing task open, with the missing issues named on it.
|
|
60
|
+
|
|
61
|
+
### Phase 1 — Harvest obstacles
|
|
62
|
+
|
|
63
|
+
Read three sources, in order:
|
|
64
|
+
|
|
65
|
+
1. **This session's conversation** — the chat log visible in context.
|
|
66
|
+
2. **The session task list** — TaskCreate/TaskUpdate records read through TaskList and TaskGet.
|
|
67
|
+
3. **Tool results still in the context window** — hook denials, command output, log tails.
|
|
68
|
+
|
|
69
|
+
An obstacle is a hook block, a gate that fired wrongly, a tool that failed, a forced workaround, or a dead end.
|
|
70
|
+
|
|
71
|
+
**Non-negotiable evidence rule:** every filed issue quotes verbatim evidence captured this session — the actual error text, the exact command, the exact log line. An obstacle you cannot quote is dropped, or listed under a "Unverified candidates" section of the drafts for the user to decide. It is never filed as fact.
|
|
72
|
+
|
|
73
|
+
**PII pass (runs on every run):** strip personal data from every issue body and from the handoff prompt — emails, real names, home paths, private hosts and IPs, account ids, tokens. The pass runs whether the target repo is public or private; repository visibility changes only how aggressive the redaction is (public repos get the strictest pass), never whether the pass runs. Checklist and swaps: [reference/pii-redaction-checklist.md](reference/pii-redaction-checklist.md).
|
|
74
|
+
|
|
75
|
+
### Phase 2 — Draft and validate with the user
|
|
76
|
+
|
|
77
|
+
Build the parent → children issue set as drafts. Body shapes and worked examples: [reference/issue-body-templates.md](reference/issue-body-templates.md).
|
|
78
|
+
|
|
79
|
+
Then the **mandatory confirmation gate**. Present through AskUserQuestion:
|
|
80
|
+
|
|
81
|
+
- Each drafted parent and child — title, target repo, one line of scope each.
|
|
82
|
+
- Any PII concern the pass found.
|
|
83
|
+
- Any closed twin a dedupe search surfaced (see Phase 3), as a reopen/comment/file-new choice.
|
|
84
|
+
|
|
85
|
+
Filing to GitHub is an irreversible write to a shared server that other people read. File only on explicit user approval. The user validates every finding before anything is posted.
|
|
86
|
+
|
|
87
|
+
### Phase 3 — Dedupe
|
|
88
|
+
|
|
89
|
+
Before filing each candidate, search open and closed issues on the target repo:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
gh issue list --search "<terms>" --state all
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **Open twin exists** → comment on it, rather than filing a new issue.
|
|
96
|
+
- **Closed twin exists** → do not silently file or comment. Surface it in the Phase 2 gate as "previously closed twin — reopen, comment, or file new" for the user to decide.
|
|
97
|
+
|
|
98
|
+
`gh issue list` needs no pagination flags. If you show a `gh api` read of a paginated list endpoint anywhere, show `--paginate --slurp` piped to external `jq` — `gh`'s built-in `--jq` runs per page and gives wrong cross-page results.
|
|
99
|
+
|
|
100
|
+
### Phase 4 — Repo routing
|
|
101
|
+
|
|
102
|
+
Route each issue by a deterministic rule:
|
|
103
|
+
|
|
104
|
+
- The evidence names a file under the dev-env tree — `packages/claude-dev-env/hooks/`, `rules/`, `skills/`, `commands/`, `agents/`, `bin/`, or `docs/` — or an installed copy of those under `~/.claude/` (hooks, rules, skills, commands, agents) → file against **claude-dev-env**.
|
|
105
|
+
- Otherwise → the **working repo**, read live:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
gh repo view --json nameWithOwner
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- **Cross-repo case** — a hook shipped by repo B blocked work in repo A → file against **B** and reference A in the body.
|
|
112
|
+
|
|
113
|
+
### Phase 5 — File
|
|
114
|
+
|
|
115
|
+
File **children first**, then the parent. The parent body is a checklist of `- [ ] owner/repo#N` lines, one per child created.
|
|
116
|
+
|
|
117
|
+
- Every `gh issue create` and `gh issue comment` uses `--body-file <path>`, never `--body`.
|
|
118
|
+
- No volatile paths in any body: no temp dirs, no worktrees, no `$CLAUDE_JOB_DIR`, no `.claude-editor/jobs` or `.claude/worktrees` paths.
|
|
119
|
+
- Bodies are self-contained and specific: the failure mode, the count, and the quoted line — not "improve error handling".
|
|
120
|
+
|
|
121
|
+
### Phase 6 — Computed handoff prompt
|
|
122
|
+
|
|
123
|
+
Print **in chat** (not a file) a prompt the user pastes into a cloud session that opens PRs for the filed issues and drives them to convergence. Template and worked example: [reference/handoff-prompt-template.md](reference/handoff-prompt-template.md).
|
|
124
|
+
|
|
125
|
+
The prompt is computed, not a bare list. It carries:
|
|
126
|
+
|
|
127
|
+
1. **Safety boundaries** — what must never be run, merged, deployed, or synced. The working repo's pipeline is live in production.
|
|
128
|
+
2. **Base branch and verification commands** — read the base branch and the per-package verification commands from the target repo's CLAUDE.md and docs at runtime.
|
|
129
|
+
3. **Dependency order** — which issue must land before which, and which issues touch the same files and so must stack on one branch rather than run in parallel.
|
|
130
|
+
|
|
131
|
+
If any issue cannot be done from a cloud session — it needs a local environment, physical devices, or a private network — the prompt says so per issue and scopes the cloud work to what a cloud session can reach.
|
|
132
|
+
|
|
133
|
+
## Skill boundaries
|
|
134
|
+
|
|
135
|
+
This skill runs inside a repo whose pipeline is live in production. Hold these lines:
|
|
136
|
+
|
|
137
|
+
- It never runs that pipeline, never merges, never deploys, never syncs.
|
|
138
|
+
- It never runs the host repo's automations.
|
|
139
|
+
- It creates issues and comments and prints text. Nothing else.
|
|
140
|
+
|
|
141
|
+
## File index
|
|
142
|
+
|
|
143
|
+
| File | Purpose |
|
|
144
|
+
|------|---------|
|
|
145
|
+
| `SKILL.md` | This hub — core principle, gotchas, refusal cases, six-phase process, boundaries |
|
|
146
|
+
| `reference/issue-body-templates.md` | Parent and child issue body shapes with worked examples |
|
|
147
|
+
| `reference/pii-redaction-checklist.md` | The PII pass: categories, swaps, public-versus-private aggression |
|
|
148
|
+
| `reference/handoff-prompt-template.md` | The computed cloud handoff prompt shape with a worked example |
|
|
149
|
+
|
|
150
|
+
## Folder map
|
|
151
|
+
|
|
152
|
+
- `SKILL.md` — hub: principle, gotchas, refusal, six-phase process, boundaries.
|
|
153
|
+
- `reference/` — issue body templates, PII redaction checklist, handoff prompt template.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Handoff prompt template
|
|
2
|
+
|
|
3
|
+
The closing session prints this prompt in chat. The user pastes it into a cloud session that opens PRs for the filed issues and drives them to convergence. The prompt is computed from the filed set — the safety lines, base branch, verification commands, and dependency order are read at runtime, not guessed.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Shape](#shape)
|
|
8
|
+
- [How to compute each block](#how-to-compute-each-block)
|
|
9
|
+
- [Worked example](#worked-example)
|
|
10
|
+
|
|
11
|
+
## Shape
|
|
12
|
+
|
|
13
|
+
```markdown
|
|
14
|
+
# Cloud handoff — <parent issue owner/repo#N>
|
|
15
|
+
|
|
16
|
+
## Task
|
|
17
|
+
Open a PR per filed issue below and drive each to convergence.
|
|
18
|
+
|
|
19
|
+
## Safety — never do these
|
|
20
|
+
- Never run, merge, deploy, or sync the <working repo> pipeline. It is live in production.
|
|
21
|
+
- Never run the host repo's automations.
|
|
22
|
+
- <any repo-specific never line read from the target CLAUDE.md>
|
|
23
|
+
|
|
24
|
+
## Base branch and verification
|
|
25
|
+
- Base branch: <base>
|
|
26
|
+
- Per-package verification commands:
|
|
27
|
+
- <package A>: <command>
|
|
28
|
+
- <package B>: <command>
|
|
29
|
+
|
|
30
|
+
## Issues, in dependency order
|
|
31
|
+
1. owner/repo#<N> — <title>. <cloud-doable? yes / no + why>.
|
|
32
|
+
2. owner/repo#<N> — <title>. Depends on #<N> landing first.
|
|
33
|
+
3. owner/repo#<N> — <title>. Touches the same files as #<N>; stack on one branch, do not run in parallel.
|
|
34
|
+
|
|
35
|
+
## Cannot be done from a cloud session
|
|
36
|
+
- owner/repo#<N> — needs <local environment / physical device / private network>. Scope the cloud work to <what a cloud session can reach>.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## How to compute each block
|
|
40
|
+
|
|
41
|
+
- **Safety lines** — read the working repo's CLAUDE.md and docs for the pipeline, deploy, and sync commands the cloud session must never run. Name each one.
|
|
42
|
+
- **Base branch** — read it live from the target repo (its default branch), not from memory.
|
|
43
|
+
- **Verification commands** — read the per-package test and check commands from the target repo's CLAUDE.md or docs. List one line per package a filed issue touches.
|
|
44
|
+
- **Dependency order** — for each pair of issues, decide: does one need the other's change to land first? Do they touch the same files? Same-file issues stack on one branch; independent issues run in parallel.
|
|
45
|
+
- **Cloud reachability** — for each issue, decide whether a cloud session can do the work. An issue needing a local environment, physical devices, or a private network is marked, and the cloud work is scoped to the reachable part.
|
|
46
|
+
|
|
47
|
+
## Worked example
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
# Cloud handoff — jl-cmd/claude-dev-env#100
|
|
51
|
+
|
|
52
|
+
## Task
|
|
53
|
+
Open a PR per filed issue below and drive each to convergence.
|
|
54
|
+
|
|
55
|
+
## Safety — never do these
|
|
56
|
+
- Never run, merge, deploy, or sync the claude-dev-env publish pipeline. It is live in production (publishes to npm).
|
|
57
|
+
- Never run the host repo's automations.
|
|
58
|
+
- Never hand-edit .cursor/BUGBOT.md; it is generated from AGENTS.md.
|
|
59
|
+
|
|
60
|
+
## Base branch and verification
|
|
61
|
+
- Base branch: main
|
|
62
|
+
- Per-package verification commands:
|
|
63
|
+
- claude-dev-env (JS): cd packages/claude-dev-env && npm test
|
|
64
|
+
- claude-dev-env (Python): python -m pytest packages/claude-dev-env
|
|
65
|
+
|
|
66
|
+
## Issues, in dependency order
|
|
67
|
+
1. jl-cmd/claude-dev-env#101 — inline-collection gate fires in exempt test files. Cloud-doable: yes.
|
|
68
|
+
2. jl-cmd/claude-dev-env#102 — boolean-naming gate flags a fixture variable. Touches the same file as #101 (code_rules_enforcer.py); stack on one branch with #101, do not run in parallel.
|
|
69
|
+
|
|
70
|
+
## Cannot be done from a cloud session
|
|
71
|
+
- None. Both issues are pure hook-logic changes with local pytest coverage a cloud session can run.
|
|
72
|
+
```
|