capt-hook 3.3.0__tar.gz → 3.3.1__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.
Files changed (90) hide show
  1. {capt_hook-3.3.0 → capt_hook-3.3.1}/PKG-INFO +1 -1
  2. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/cli.py +19 -12
  3. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/cli.py +3 -1
  4. {capt_hook-3.3.0 → capt_hook-3.3.1}/pyproject.toml +1 -1
  5. {capt_hook-3.3.0 → capt_hook-3.3.1}/LICENSE +0 -0
  6. {capt_hook-3.3.0 → capt_hook-3.3.1}/README.md +0 -0
  7. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/.claude-plugin/plugin.json +0 -0
  8. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/__init__.py +0 -0
  9. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/__main__.py +0 -0
  10. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/app.py +0 -0
  11. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/classifiers/__init__.py +0 -0
  12. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/classifiers/conductor.py +0 -0
  13. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/classifiers/droid.py +0 -0
  14. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/classifiers/native.py +0 -0
  15. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/command.py +0 -0
  16. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/conditions.py +0 -0
  17. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/context.py +0 -0
  18. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/decisions.py +0 -0
  19. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/dispatch.py +0 -0
  20. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/events.py +0 -0
  21. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/file.py +0 -0
  22. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/llm/__init__.py +0 -0
  23. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/loader.py +0 -0
  24. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/log.py +0 -0
  25. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/__init__.py +0 -0
  26. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/capt-hook.toml +0 -0
  27. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/commands.py +0 -0
  28. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/docs.py +0 -0
  29. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/plans.py +0 -0
  30. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/prompts.py +0 -0
  31. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/review.py +0 -0
  32. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/stewardship.py +0 -0
  33. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/general/tasks.py +0 -0
  34. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/manager.py +0 -0
  35. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/python/capt-hook.toml +0 -0
  36. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/python/style.py +0 -0
  37. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/python/testing.py +0 -0
  38. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/packs/python/toolchain.py +0 -0
  39. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/primitives/__init__.py +0 -0
  40. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/primitives/commands.py +0 -0
  41. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/primitives/lint.py +0 -0
  42. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/primitives/llm.py +0 -0
  43. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/primitives/nudge.py +0 -0
  44. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/primitives/workflow.py +0 -0
  45. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/prompt.py +0 -0
  46. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/py.typed +0 -0
  47. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/__init__.py +0 -0
  48. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/fix.py +0 -0
  49. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/formats.py +0 -0
  50. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/judge.py +0 -0
  51. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/pipeline.py +0 -0
  52. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/repo.py +0 -0
  53. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/scan.py +0 -0
  54. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/settings.py +0 -0
  55. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/store.py +0 -0
  56. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/review/sync.py +0 -0
  57. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/session.py +0 -0
  58. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/settings.py +0 -0
  59. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/signals/__init__.py +0 -0
  60. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/signals/nlp.py +0 -0
  61. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
  62. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
  63. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
  64. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
  65. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
  66. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/bootstrapping-hooks/SKILL.md +0 -0
  67. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
  68. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
  69. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
  70. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
  71. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
  72. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
  73. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
  74. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/state.py +0 -0
  75. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/style/__init__.py +0 -0
  76. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/style/matchers.py +0 -0
  77. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/style/scope.py +0 -0
  78. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/style/types.py +0 -0
  79. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/tasks.py +0 -0
  80. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/templates/example_hook.py.tmpl +0 -0
  81. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/testing/__init__.py +0 -0
  82. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/testing/helpers.py +0 -0
  83. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/testing/session_cache.py +0 -0
  84. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/testing/types.py +0 -0
  85. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/tests/__init__.py +0 -0
  86. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/tests/helpers.py +0 -0
  87. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/types.py +0 -0
  88. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/util/__init__.py +0 -0
  89. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/util/model_cache.py +0 -0
  90. {capt_hook-3.3.0 → capt_hook-3.3.1}/captain_hook/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capt-hook
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: Declarative hook framework for Claude Code
5
5
  Keywords: claude,claude-code,hooks,llm,agents,guardrails,cli
6
6
  Author: Yasyf Mohamedali
@@ -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 fresh_own:
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
- settings = [p for name in ("settings.json", "settings.local.json") if (p := root / ".claude" / name).exists()]
222
- if not settings:
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(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "capt-hook"
3
- version = "3.3.0"
3
+ version = "3.3.1"
4
4
  description = "Declarative hook framework for Claude Code"
5
5
  readme = "README.md"
6
6
  license = "PolyForm-Noncommercial-1.0.0"
File without changes
File without changes
File without changes
File without changes