capt-hook 3.3.0__tar.gz → 3.3.2__tar.gz
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.
- {capt_hook-3.3.0 → capt_hook-3.3.2}/PKG-INFO +1 -1
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/cli.py +19 -12
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/cli.py +3 -1
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/bootstrapping-hooks/SKILL.md +10 -13
- {capt_hook-3.3.0 → capt_hook-3.3.2}/pyproject.toml +1 -1
- {capt_hook-3.3.0 → capt_hook-3.3.2}/LICENSE +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/README.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/.claude-plugin/plugin.json +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/__main__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/app.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/classifiers/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/classifiers/conductor.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/classifiers/droid.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/classifiers/native.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/command.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/conditions.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/context.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/decisions.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/dispatch.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/events.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/file.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/llm/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/loader.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/log.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/capt-hook.toml +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/commands.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/docs.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/plans.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/prompts.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/review.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/stewardship.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/general/tasks.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/manager.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/python/capt-hook.toml +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/python/style.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/python/testing.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/packs/python/toolchain.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/primitives/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/primitives/commands.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/primitives/lint.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/primitives/llm.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/primitives/nudge.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/primitives/workflow.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/prompt.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/py.typed +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/fix.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/formats.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/judge.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/pipeline.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/repo.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/scan.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/settings.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/store.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/review/sync.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/session.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/settings.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/signals/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/signals/nlp.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/state.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/style/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/style/matchers.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/style/scope.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/style/types.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/tasks.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/templates/example_hook.py.tmpl +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/testing/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/testing/helpers.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/testing/session_cache.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/testing/types.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/tests/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/tests/helpers.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/types.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/util/__init__.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/util/model_cache.py +0 -0
- {capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/utils.py +0 -0
|
@@ -157,20 +157,33 @@ def is_captain_hook_group(group: dict[str, Any]) -> bool:
|
|
|
157
157
|
return any("capt-hook" in (h.get("command") or "") for h in group.get("hooks") or [])
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
def capt_hook_events(path: Path) -> set[str]:
|
|
161
|
+
if not path.exists():
|
|
162
|
+
return set()
|
|
163
|
+
return {
|
|
164
|
+
event
|
|
165
|
+
for event, groups in (json.loads(path.read_text()).get("hooks") or {}).items()
|
|
166
|
+
if any(is_captain_hook_group(g) for g in groups)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
160
170
|
def merge_settings(
|
|
161
171
|
hooks_dir: str, settings_path: Path, from_source: str = DIST_NAME
|
|
162
172
|
) -> tuple[dict[str, Any], dict[str, str]]:
|
|
163
173
|
new_hooks: dict[str, list[dict[str, Any]]] = generate_settings(hooks_dir, from_source=from_source)["hooks"]
|
|
164
174
|
existing = json.loads(settings_path.read_text()) if settings_path.exists() else {}
|
|
165
175
|
existing_hooks: dict[str, list[dict[str, Any]]] = existing.get("hooks") or {}
|
|
176
|
+
committed = capt_hook_events(settings_path.parent / "settings.json")
|
|
166
177
|
|
|
167
178
|
summary: dict[str, str] = {}
|
|
168
179
|
merged_hooks: dict[str, list[dict[str, Any]]] = {}
|
|
169
180
|
for event in sorted(existing_hooks.keys() | new_hooks.keys()):
|
|
170
181
|
foreign = [g for g in existing_hooks.get(event, []) if not is_captain_hook_group(g)]
|
|
171
182
|
old_own = [g for g in existing_hooks.get(event, []) if is_captain_hook_group(g)]
|
|
172
|
-
fresh_own = new_hooks.get(event, [])
|
|
173
|
-
if old_own or
|
|
183
|
+
fresh_own = [] if event in committed else new_hooks.get(event, [])
|
|
184
|
+
if event in committed and (old_own or new_hooks.get(event)):
|
|
185
|
+
summary[event] = "deferred"
|
|
186
|
+
elif old_own or fresh_own:
|
|
174
187
|
summary[event] = (
|
|
175
188
|
"unchanged"
|
|
176
189
|
if old_own == fresh_own
|
|
@@ -207,6 +220,8 @@ def print_hook_summary(label: str, summary: dict[str, str]) -> None:
|
|
|
207
220
|
click.echo(f" - removed {event}")
|
|
208
221
|
if unchanged := by_status["unchanged"]:
|
|
209
222
|
click.echo(f" unchanged: {', '.join(unchanged)} (already present)")
|
|
223
|
+
if deferred := by_status["deferred"]:
|
|
224
|
+
click.echo(f" deferred to settings.json: {', '.join(deferred)}")
|
|
210
225
|
|
|
211
226
|
|
|
212
227
|
def regenerate_settings(state: CliState) -> None:
|
|
@@ -218,16 +233,8 @@ def regenerate_settings(state: CliState) -> None:
|
|
|
218
233
|
|
|
219
234
|
|
|
220
235
|
def settings_drift(root: Path) -> set[str]:
|
|
221
|
-
|
|
222
|
-
if
|
|
223
|
-
return set()
|
|
224
|
-
wired = {
|
|
225
|
-
event
|
|
226
|
-
for path in settings
|
|
227
|
-
for event, groups in (json.loads(path.read_text()).get("hooks") or {}).items()
|
|
228
|
-
if any(is_captain_hook_group(g) for g in groups)
|
|
229
|
-
}
|
|
230
|
-
return subscribed_events() - wired
|
|
236
|
+
paths = [p for name in ("settings.json", "settings.local.json") if (p := root / ".claude" / name).exists()]
|
|
237
|
+
return subscribed_events() - {event for p in paths for event in capt_hook_events(p)} if paths else set()
|
|
231
238
|
|
|
232
239
|
|
|
233
240
|
def warn_settings_drift(
|
|
@@ -60,8 +60,10 @@ def ensure_review_wiring(settings_path: Path) -> bool:
|
|
|
60
60
|
from captain_hook.cli import write_settings
|
|
61
61
|
|
|
62
62
|
existing: dict[str, Any] = json.loads(settings_path.read_text()) if settings_path.exists() else {}
|
|
63
|
+
committed = settings_path.parent / "settings.json"
|
|
64
|
+
committed_hooks: dict[str, Any] = (json.loads(committed.read_text()).get("hooks") or {}) if committed.exists() else {}
|
|
63
65
|
hooks: dict[str, Any] = existing.get("hooks") or {}
|
|
64
|
-
if review_wired(hooks):
|
|
66
|
+
if review_wired(hooks) or review_wired(committed_hooks):
|
|
65
67
|
return False
|
|
66
68
|
group = {"hooks": [{"type": "command", "command": f"uvx {REVIEW_RUN_COMMAND}"}]}
|
|
67
69
|
write_settings(
|
|
@@ -29,7 +29,7 @@ Copy this checklist into your response and check off steps as you complete them:
|
|
|
29
29
|
|
|
30
30
|
```
|
|
31
31
|
Bootstrap Progress:
|
|
32
|
-
- [ ] Step 1: Locate + scaffold (init
|
|
32
|
+
- [ ] Step 1: Locate + scaffold (init) + pre-flight
|
|
33
33
|
- [ ] Step 2: Survey the repo (docs, CI, lint configs, git log)
|
|
34
34
|
- [ ] Step 3: Mine candidates onto the taxonomy
|
|
35
35
|
- [ ] Step 4: Propose via AskUserQuestion — nothing written before approval
|
|
@@ -50,16 +50,12 @@ grep -lq 'capt-hook' .claude/settings.json 2>/dev/null && echo COMMITTED || echo
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
Then scaffold up front, so the framework and the session reviewer are live before you propose
|
|
53
|
-
anything
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- **COMMITTED** (a checked-in `.claude/settings.json` already runs `uvx capt-hook run …`) — do
|
|
60
|
-
**not** run `init`; it would duplicate those hooks into `settings.local.json` and double-fire.
|
|
61
|
-
Run `uvx capt-hook review enable` instead — it installs the reviewer skills and arms the session
|
|
62
|
-
reviewer without touching the committed event hooks.
|
|
53
|
+
anything. Run `uvx capt-hook init` in every repo. It scaffolds `.claude/hooks/`,
|
|
54
|
+
wires `.claude/settings.local.json`, installs the skills, and **enables the session reviewer**
|
|
55
|
+
(watching this repo; it mines ended sessions and opens hook PRs — `uvx capt-hook review disable`
|
|
56
|
+
to stop). In a **COMMITTED** repo (a checked-in `.claude/settings.json` already runs
|
|
57
|
+
`uvx capt-hook run …`), `init` defers those events to the committed file instead of re-wiring
|
|
58
|
+
them locally. It prints `deferred to settings.json: …` and never double-fires.
|
|
63
59
|
|
|
64
60
|
Read `.claude/settings.local.json` and `.claude/settings.json`. If capt-hook hooks already exist,
|
|
65
61
|
switch to **additive mode**: never overwrite existing hook files; new categories go in new files,
|
|
@@ -115,8 +111,9 @@ into enforced style rules (runs the translating-styleguides skill)"**.
|
|
|
115
111
|
### 5. Clear the demo example.py
|
|
116
112
|
|
|
117
113
|
Scaffolding already ran in Step 1. If that `init` created the demo `.claude/hooks/example.py`,
|
|
118
|
-
delete it once you've drafted the real hooks (Step 6) — the approved hooks replace it. (In
|
|
119
|
-
|
|
114
|
+
delete it once you've drafted the real hooks (Step 6) — the approved hooks replace it. (In a repo
|
|
115
|
+
that already had its own `.claude/hooks/`, `init` leaves those files untouched and only adds
|
|
116
|
+
`example.py` if it was absent; clear it the same way.)
|
|
120
117
|
|
|
121
118
|
### 6. Write hooks
|
|
122
119
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md
RENAMED
|
File without changes
|
|
File without changes
|
{capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/pitfalls.md
RENAMED
|
File without changes
|
{capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/authoring-hooks/references/testing-hooks.md
RENAMED
|
File without changes
|
|
File without changes
|
{capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/scanning-sessions/references/pr-workflow.md
RENAMED
|
File without changes
|
{capt_hook-3.3.0 → capt_hook-3.3.2}/captain_hook/skills/scanning-sessions/references/review-cli.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|