capt-hook 9.0.0__tar.gz → 9.2.0__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 (131) hide show
  1. {capt_hook-9.0.0 → capt_hook-9.2.0}/PKG-INFO +2 -2
  2. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/cli.py +1 -1
  3. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/hooks/hooks.json +2 -1
  4. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/once.py +9 -5
  5. capt_hook-9.2.0/captain_hook/packs/general/capt-hook.toml +5 -0
  6. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/models.py +9 -9
  7. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/implementation_spawn_nudge.md +2 -2
  8. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/inline_edit_nudge.md +1 -1
  9. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/review_routing_spawn_nudge.md +7 -7
  10. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/review_routing_workflow_nudge.md +2 -2
  11. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/cli.py +2 -33
  12. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/fix.py +12 -18
  13. {capt_hook-9.0.0 → capt_hook-9.2.0}/pyproject.toml +2 -2
  14. capt_hook-9.0.0/captain_hook/packs/general/capt-hook.toml +0 -5
  15. {capt_hook-9.0.0 → capt_hook-9.2.0}/LICENSE +0 -0
  16. {capt_hook-9.0.0 → capt_hook-9.2.0}/README.md +0 -0
  17. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/.claude-plugin/plugin.json +0 -0
  18. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/__init__.py +0 -0
  19. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/__main__.py +0 -0
  20. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/app.py +0 -0
  21. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/ast_grep.py +0 -0
  22. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/classifiers/__init__.py +0 -0
  23. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/classifiers/conductor.py +0 -0
  24. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/classifiers/droid.py +0 -0
  25. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/classifiers/native.py +0 -0
  26. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/conditions.py +0 -0
  27. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/context.py +0 -0
  28. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/contexts.py +0 -0
  29. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/decisions.py +0 -0
  30. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/dispatch.py +0 -0
  31. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/durable.py +0 -0
  32. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/events.py +0 -0
  33. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/fields.py +0 -0
  34. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/file.py +0 -0
  35. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/loader.py +0 -0
  36. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/log.py +0 -0
  37. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/__init__.py +0 -0
  38. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/fixes/__init__.py +0 -0
  39. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/fixes/capt-hook.toml +0 -0
  40. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/fixes/teammate_permissions.py +0 -0
  41. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/__init__.py +0 -0
  42. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/_lib.py +0 -0
  43. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/commands.py +0 -0
  44. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/comments.py +0 -0
  45. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/detours.py +0 -0
  46. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/docs.py +0 -0
  47. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/plans.py +0 -0
  48. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/fragments/deliverable_rubric.md +0 -0
  49. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/fragments/workflow_script_header.md +0 -0
  50. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/prose_spawn_gate.md +0 -0
  51. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/prose_workflow_nudge.md +0 -0
  52. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/writing_docs_spawn_nudge.md +0 -0
  53. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts/models/writing_docs_workflow_nudge.md +0 -0
  54. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/prompts.py +0 -0
  55. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/review.py +0 -0
  56. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/tasks.py +0 -0
  57. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/general/tombstones.py +0 -0
  58. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/go/__init__.py +0 -0
  59. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/go/capt-hook.toml +0 -0
  60. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/go/testing.py +0 -0
  61. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/go/toolchain.py +0 -0
  62. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/manager.py +0 -0
  63. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/python/__init__.py +0 -0
  64. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/python/capt-hook.toml +0 -0
  65. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/python/style.py +0 -0
  66. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/python/testing.py +0 -0
  67. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/python/toolchain.py +0 -0
  68. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/steering/__init__.py +0 -0
  69. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/steering/capt-hook.toml +0 -0
  70. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/steering/steering.py +0 -0
  71. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/packs/steering/teammates.py +0 -0
  72. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/__init__.py +0 -0
  73. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/commands.py +0 -0
  74. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/lint.py +0 -0
  75. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/llm.py +0 -0
  76. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/nudge.py +0 -0
  77. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/permissions.py +0 -0
  78. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/rewrite.py +0 -0
  79. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/primitives/workflow.py +0 -0
  80. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/prompt.py +0 -0
  81. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/py.typed +0 -0
  82. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/__init__.py +0 -0
  83. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/dashboard.py +0 -0
  84. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/formats.py +0 -0
  85. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/judge.py +0 -0
  86. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/pipeline.py +0 -0
  87. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/prompts.py +0 -0
  88. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/repo.py +0 -0
  89. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/scan.py +0 -0
  90. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/settings.py +0 -0
  91. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/store.py +0 -0
  92. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/review/sync.py +0 -0
  93. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/session.py +0 -0
  94. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/settings.py +0 -0
  95. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/signals/__init__.py +0 -0
  96. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/signals/nlp.py +0 -0
  97. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
  98. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
  99. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
  100. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
  101. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
  102. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/bootstrapping-hooks/SKILL.md +0 -0
  103. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
  104. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
  105. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
  106. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
  107. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
  108. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
  109. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
  110. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/state.py +0 -0
  111. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/style/__init__.py +0 -0
  112. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/style/ast_grep.py +0 -0
  113. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/style/matchers.py +0 -0
  114. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/style/scope.py +0 -0
  115. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/style/types.py +0 -0
  116. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/tasks.py +0 -0
  117. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/templates/example_hook.py.tmpl +0 -0
  118. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/testing/__init__.py +0 -0
  119. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/testing/helpers.py +0 -0
  120. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/testing/session_cache.py +0 -0
  121. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/testing/types.py +0 -0
  122. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/transcripts.py +0 -0
  123. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/types.py +0 -0
  124. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/__init__.py +0 -0
  125. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/automode.py +0 -0
  126. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/fs.py +0 -0
  127. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/http.py +0 -0
  128. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/model_cache.py +0 -0
  129. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/paths.py +0 -0
  130. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/proc.py +0 -0
  131. {capt_hook-9.0.0 → capt_hook-9.2.0}/captain_hook/util/text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capt-hook
3
- Version: 9.0.0
3
+ Version: 9.2.0
4
4
  Summary: Stop repeating yourself to Claude.
5
5
  Keywords: claude,claude-code,hooks,llm,agents,guardrails,cli
6
6
  Author: Yasyf Mohamedali
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
16
16
  Classifier: Topic :: Software Development :: Quality Assurance
17
17
  Classifier: Topic :: Software Development :: Testing
18
18
  Classifier: Typing :: Typed
19
- Requires-Dist: cc-transcript[judge]>=10.6,<11
19
+ Requires-Dist: cc-transcript[judge]>=11,<12
20
20
  Requires-Dist: pydantic>=2.0
21
21
  Requires-Dist: pydantic-settings>=2.0
22
22
  Requires-Dist: ast-grep-py>=0.39,<1
@@ -209,7 +209,7 @@ def run_event(state: CliState, event_name: str, *, async_: bool = False) -> None
209
209
  # Collapse the N byte-identical siblings Claude Code spawns per event to one
210
210
  # dispatch. Decision-capable events (DECISION_EVENTS) are exempt: swallowing a
211
211
  # sibling there could bypass a gate, which outweighs a duplicated side effect.
212
- if event not in DECISION_EVENTS and not claim_once(event_name, raw_text.encode()):
212
+ if event not in DECISION_EVENTS and not claim_once(event_name, raw_text.encode(), async_=async_):
213
213
  return
214
214
 
215
215
  try:
@@ -80,7 +80,8 @@
80
80
  "SessionEnd": [
81
81
  {
82
82
  "hooks": [
83
- { "type": "command", "command": "uvx capt-hook review run", "async": true }
83
+ { "type": "command", "command": "uvx capt-hook review run", "async": true },
84
+ { "type": "command", "command": "uvx capt-hook run SessionEnd --async", "async": true }
84
85
  ]
85
86
  }
86
87
  ]
@@ -26,12 +26,15 @@ DEFAULT_TTL = 10.0
26
26
  TTL_ENV = "CAPT_HOOK_ONCE_TTL"
27
27
 
28
28
 
29
- def claim_once(event_name: str, payload: bytes) -> bool:
30
- """Claim the one-time dispatch of ``payload`` under ``event_name``.
29
+ def claim_once(event_name: str, payload: bytes, *, async_: bool) -> bool:
30
+ """Claim the one-time dispatch of ``payload`` under ``event_name``'s ``async_`` variant.
31
31
 
32
32
  Returns True when this process won the claim and should dispatch, False when a
33
- still-fresh sibling already claimed it (a duplicate that must exit silently).
34
- ``CAPT_HOOK_ONCE_TTL=0`` disables the guard, so every call wins.
33
+ still-fresh sibling already claimed it (a duplicate that must exit silently). The sync
34
+ and async passes of one event dispatch disjoint hook sets (``dispatch`` filters on
35
+ ``spec.async_``), so ``async_`` is part of the key: each variant claims its own token and
36
+ both run even on byte-identical stdin. ``CAPT_HOOK_ONCE_TTL=0`` disables the guard, so
37
+ every call wins.
35
38
  """
36
39
  ttl = _ttl()
37
40
  if ttl <= 0:
@@ -40,7 +43,8 @@ def claim_once(event_name: str, payload: bytes) -> bool:
40
43
  if sentinel_dir is None:
41
44
  return True
42
45
  _reap(sentinel_dir, ttl)
43
- key = hashlib.sha256(event_name.encode() + b"\0" + payload).hexdigest()
46
+ variant = b"async" if async_ else b"sync"
47
+ key = hashlib.sha256(event_name.encode() + b"\0" + variant + b"\0" + payload).hexdigest()
44
48
  return _try_claim(sentinel_dir / key, ttl)
45
49
 
46
50
 
@@ -0,0 +1,5 @@
1
+ name = "general"
2
+ version = "0.17.0"
3
+ description = "Language-agnostic guards: git/jj command safety, doc & prompt nudges, task & plan discipline, model-routing nudges (review, security audit, and diagnosis route to gpt-5.6-sol via the codex-wrapper agent), code-review + docs-freshness gates, a detour check-in nudge, a verbose-comment nudge, and delegated-prose nudges that catch doc writing handed to a subagent or workflow without the writing-docs skill."
4
+ hooks = "."
5
+ nlp = true
@@ -263,7 +263,7 @@ llm_nudge(
263
263
  message=lambda r: (
264
264
  f"This delegation would run on fable, but it reads as routine implementation. {r.reasoning} "
265
265
  "Implementation defaults to model='opus' + effort='xhigh' (~2x cheaper, nearly as capable); "
266
- "a well-scoped edit to existing code can also go to gpt-5.5: spawn the codex:codex-wrapper "
266
+ "a well-scoped edit to existing code can also go to gpt-5.6-sol: spawn the codex:codex-wrapper "
267
267
  "agent with a self-contained prompt. Keep fable if this genuinely is sensitive or error-prone. "
268
268
  "See CLAUDE.md § Plan Execution & Orchestration (Models)."
269
269
  ),
@@ -291,7 +291,7 @@ llm_nudge(
291
291
  message=lambda r: (
292
292
  f"This inline edit reads as routine implementation on fable. {r.reasoning} "
293
293
  "Implementation delegates: spawn a model='opus', effort='xhigh' subagent to own the change, "
294
- "or route a well-scoped edit to gpt-5.5 via the codex skill. Keep editing inline only when "
294
+ "or route a well-scoped edit to gpt-5.6-sol via the codex skill. Keep editing inline only when "
295
295
  "the change is small, sensitive, or bound to judgment you just exercised. "
296
296
  "See CLAUDE.md § Plan Execution & Orchestration (Models)."
297
297
  ),
@@ -352,9 +352,9 @@ llm_nudge(
352
352
  message=lambda r: (
353
353
  f"This review/diagnosis delegation would run on fable. {r.reasoning} "
354
354
  "Code/diff review, security review/audit and verification of security-sensitive code, "
355
- "and bug diagnosis route to gpt-5.5: spawn the codex:codex-wrapper agent with the "
355
+ "and bug diagnosis route to gpt-5.6-sol: spawn the codex:codex-wrapper agent with the "
356
356
  "self-contained question as its prompt (from the main conversation, run the codex skill "
357
- "directly), and escalate to fable only when gpt-5.5's output misses. "
357
+ "directly), and escalate to fable only when gpt-5.6-sol's output misses. "
358
358
  "Design review and findings synthesis stay on fable. "
359
359
  "See CLAUDE.md § Plan Execution & Orchestration (Models)."
360
360
  ),
@@ -378,7 +378,7 @@ llm_nudge(
378
378
  agent=False,
379
379
  transcript=False,
380
380
  tests={
381
- Input(prompt="Review the diff for correctness and concurrency issues"): Warn(pattern="gpt-5.5"),
381
+ Input(prompt="Review the diff for correctness and concurrency issues"): Warn(pattern="gpt-5.6"),
382
382
  Input(model="fable", prompt="Adversarially refute this finding: the retry loop is wrong"): Warn(
383
383
  pattern="codex"
384
384
  ),
@@ -395,7 +395,7 @@ llm_nudge(
395
395
  prompt="Synthesize the confirmed review findings and decide which to fix",
396
396
  llm={"fire": False},
397
397
  ): Allow(),
398
- Input(prompt="Audit auth/session.py for security vulnerabilities"): Warn(pattern="gpt-5.5"),
398
+ Input(prompt="Audit auth/session.py for security vulnerabilities"): Warn(pattern="gpt-5.6"),
399
399
  Input(prompt="Verify the input-validation change blocks path traversal"): Warn(pattern="codex"),
400
400
  Input(prompt="Verify the pagination change renders the last page correctly"): Allow(),
401
401
  Input(
@@ -456,7 +456,7 @@ llm_nudge(
456
456
  REVIEW_ROUTING_WORKFLOW_NUDGE,
457
457
  message=lambda r: (
458
458
  f"This workflow runs review/diagnosis stages on fable. {r.reasoning} "
459
- "Route finder, refuter, security-audit, and diagnosis stages to gpt-5.5: give each stage "
459
+ "Route finder, refuter, security-audit, and diagnosis stages to gpt-5.6-sol: give each stage "
460
460
  "agentType: 'codex:codex-wrapper' with the self-contained question as its prompt; "
461
461
  "keep the synthesis/accept-reject stage on fable (inherit the session model). "
462
462
  "See CLAUDE.md § Plan Execution & Orchestration (Models)."
@@ -479,7 +479,7 @@ llm_nudge(
479
479
  pattern="codex"
480
480
  ),
481
481
  Input(script="agent(`Adversarially refute: ${f.title}`, {model: 'fable', effort: 'max'})"): Warn(
482
- pattern="gpt-5.5"
482
+ pattern="gpt-5.6"
483
483
  ),
484
484
  Input(
485
485
  script="agent('Write a self-contained codex prompt reviewing this diff, "
@@ -496,7 +496,7 @@ llm_nudge(
496
496
  llm={"fire": False},
497
497
  ): Allow(),
498
498
  Input(script="agent(`Audit the login flow for auth bypass and injection; return findings as JSON`)"): Warn(
499
- pattern="gpt-5.5"
499
+ pattern="gpt-5.6"
500
500
  ),
501
501
  Input(script="agent('Verify the CLI renders the last page correctly')"): Allow(),
502
502
  },
@@ -7,7 +7,7 @@ The Models rubric: implementation delegates to opus-4.8 at xhigh — opus is ~2x
7
7
  than fable and nearly as capable. Fable's lanes are orchestration, design/architecture
8
8
  review, hard planning, all prose/writing, and implementation that is very sensitive or
9
9
  error-prone (auth, migrations, concurrency, data loss, crypto, subtle algorithms).
10
- Code/diff review, security review/audit, and bug diagnosis have their own gpt-5.5
10
+ Code/diff review, security review/audit, and bug diagnosis have their own gpt-5.6-sol
11
11
  lanes with separate nudges.
12
12
 
13
13
  Set fire=true only when the prompt is clearly routine implementation — building, fixing,
@@ -28,7 +28,7 @@ Well-scoped feature wiring; the default implementation lane.
28
28
  </example>
29
29
  <example fire="false">
30
30
  Review the diff for correctness and concurrency issues.
31
- Not implementation — review routes via its own nudge (gpt-5.5's lane), not to opus.
31
+ Not implementation — review routes via its own nudge (gpt-5.6-sol's lane), not to opus.
32
32
  </example>
33
33
  <example fire="false">
34
34
  Design the migration strategy for the sharded session store.
@@ -7,7 +7,7 @@ and written.
7
7
 
8
8
  The Models rubric: implementation belongs to a delegated opus-4.8 subagent at xhigh —
9
9
  ~2x cheaper than fable and nearly as capable — or, for a well-scoped edit to existing
10
- code, to gpt-5.5 via the codex skill. Fable edits inline when the change is small or
10
+ code, to gpt-5.6-sol via the codex skill. Fable edits inline when the change is small or
11
11
  judgment-bound: a fix-up finishing work it just reasoned through, a subtle algorithm,
12
12
  or a sensitive surface (auth, migrations, concurrency, data loss, crypto).
13
13
 
@@ -1,6 +1,6 @@
1
1
  Decide whether this delegated subagent runs code review, a security review/audit
2
2
  or verification of security-sensitive code, or bug diagnosis that should route to
3
- gpt-5.5 instead of fable.
3
+ gpt-5.6-sol instead of fable.
4
4
 
5
5
  <delegated_spawn> holds the pending Agent/Task call: its model pin (or that it inherits
6
6
  the session model, fable), agent type, and prompt.
@@ -8,9 +8,9 @@ the session model, fable), agent type, and prompt.
8
8
  The Models rubric: code/diff review — sweeping a diff or codebase for bugs,
9
9
  correctness, or cleanups; finder and refuter passes over findings — security
10
10
  review/audit and verification of security-sensitive code (auth, input validation,
11
- crypto, secrets), and bug diagnosis route to gpt-5.5 via the codex-wrapper agent —
11
+ crypto, secrets), and bug diagnosis route to gpt-5.6-sol via the codex-wrapper agent —
12
12
  spawn agent type 'codex:codex-wrapper' with the self-contained question; fable is
13
- the escalation target when gpt-5.5's output misses. Fable keeps design/architecture
13
+ the escalation target when gpt-5.6-sol's output misses. Fable keeps design/architecture
14
14
  review, "is this the right approach" judgment, prose review, the synthesis/
15
15
  accept-reject pass over review findings — and security-sensitive implementation,
16
16
  which is not review.
@@ -26,7 +26,7 @@ shape: fire=true. Keep reasoning under 40 words.
26
26
  <examples>
27
27
  <example fire="true">
28
28
  Review the diff for correctness and concurrency issues; report findings as JSON.
29
- Diff review for defects — gpt-5.5's lane via the codex-wrapper agent.
29
+ Diff review for defects — gpt-5.6-sol's lane via the codex-wrapper agent.
30
30
  </example>
31
31
  <example fire="true">
32
32
  Adversarially refute this finding: the retry loop double-increments the counter.
@@ -34,7 +34,7 @@ A refuter pass over a code finding is review work.
34
34
  </example>
35
35
  <example fire="true">
36
36
  Diagnose why the exporter hangs when two workers flush concurrently.
37
- Bug diagnosis starts on gpt-5.5; fable is the escalation target.
37
+ Bug diagnosis starts on gpt-5.6-sol; fable is the escalation target.
38
38
  </example>
39
39
  <example fire="true">
40
40
  You are a low-cost wrapper: write a self-contained codex prompt reviewing this diff, then run the codex skill.
@@ -54,11 +54,11 @@ Prose review stays on fable.
54
54
  </example>
55
55
  <example fire="true">
56
56
  Audit the session-token handling in auth/middleware.py for vulnerabilities.
57
- Security review/audit of code — gpt-5.5's lane via the codex-wrapper agent.
57
+ Security review/audit of code — gpt-5.6-sol's lane via the codex-wrapper agent.
58
58
  </example>
59
59
  <example fire="true">
60
60
  Verify the new input-validation layer rejects path traversal and injection payloads.
61
- Verification of security-sensitive code routes to gpt-5.5.
61
+ Verification of security-sensitive code routes to gpt-5.6-sol.
62
62
  </example>
63
63
  <example fire="false">
64
64
  Implement mitigations for the security-audit findings in auth.py.
@@ -1,5 +1,5 @@
1
1
  Decide whether this workflow script runs code review or bug diagnosis stages on
2
- fable that should route to gpt-5.5.
2
+ fable that should route to gpt-5.6-sol.
3
3
 
4
4
  <workflow_script> holds the pending Workflow call's script source.
5
5
  {workflow_script_header}
@@ -7,7 +7,7 @@ fable that should route to gpt-5.5.
7
7
  The Models rubric: code/diff review stages — finder sweeps over a diff or codebase,
8
8
  adversarial refuters over findings — security review/audit stages and verification
9
9
  of security-sensitive code (auth, input validation, crypto, secrets), and bug
10
- diagnosis route to gpt-5.5 via the codex-wrapper agent. A stage does that correctly
10
+ diagnosis route to gpt-5.6-sol via the codex-wrapper agent. A stage does that correctly
11
11
  when its agent() call pins agentType 'codex:codex-wrapper' and its prompt is the
12
12
  self-contained question (or pointers to the files/diff to gather plus the questions
13
13
  to answer). A stage that pins a Claude model and asks its agent to run the codex
@@ -8,7 +8,6 @@ machinery lazily once it actually runs, and async store calls bridge with
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- import json
12
11
  import os
13
12
  import sys
14
13
  from datetime import UTC, datetime
@@ -19,7 +18,6 @@ import click
19
18
 
20
19
  if TYPE_CHECKING:
21
20
  from collections.abc import Awaitable, Callable
22
- from typing import Any
23
21
 
24
22
  from cc_transcript.corrections import Correction
25
23
  from cc_transcript.judge.similar import KeyOverlap
@@ -31,7 +29,6 @@ if TYPE_CHECKING:
31
29
  from captain_hook.review.store import ReviewStore, ThresholdStatus
32
30
  from captain_hook.review.sync import SyncReport
33
31
 
34
- REVIEW_RUN_COMMAND = "capt-hook review run"
35
32
  STATUS_CHOICES = ("watching", "pr_open", "stale", "accepted", "rejected")
36
33
 
37
34
 
@@ -51,33 +48,6 @@ def resolve_repo(repo_: str | None, root: Path) -> RepoKey:
51
48
  return RepoKey(repo_) if repo_ else current_repo(root)
52
49
 
53
50
 
54
- def group_commands(group: dict[str, Any]) -> list[str]:
55
- entries: list[dict[str, Any]] = group.get("hooks") or []
56
- return [str(entry.get("command") or "") for entry in entries]
57
-
58
-
59
- def review_wired(hooks: dict[str, Any]) -> bool:
60
- groups: list[dict[str, Any]] = hooks.get("SessionEnd") or []
61
- return any(REVIEW_RUN_COMMAND in command for group in groups for command in group_commands(group))
62
-
63
-
64
- def ensure_review_wiring(settings_path: Path) -> bool:
65
- from captain_hook.cli import sibling_settings, write_settings
66
-
67
- existing: dict[str, Any] = json.loads(settings_path.read_text()) if settings_path.exists() else {}
68
- sibling = sibling_settings(settings_path)
69
- sibling_hooks: dict[str, Any] = (json.loads(sibling.read_text()).get("hooks") or {}) if sibling.exists() else {}
70
- hooks: dict[str, Any] = existing.get("hooks") or {}
71
- if review_wired(hooks) or review_wired(sibling_hooks):
72
- return False
73
- group = {"hooks": [{"type": "command", "command": f"uvx {REVIEW_RUN_COMMAND}", "async": True}]}
74
- write_settings(
75
- settings_path,
76
- existing | {"hooks": hooks | {"SessionEnd": [*(hooks.get("SessionEnd") or []), group]}},
77
- )
78
- return True
79
-
80
-
81
51
  def run_store[T](fn: Callable[[ReviewStore], Awaitable[T]]) -> T:
82
52
  """Open the review store, run ``fn`` against it, and bridge with ``asyncio.run`` at the command boundary."""
83
53
  import asyncio
@@ -136,14 +106,13 @@ def spawn(transcript: Path, cwd: str | None) -> None:
136
106
  @review.command()
137
107
  @click.pass_obj
138
108
  def enable(state: CliState) -> None:
139
- """Watch the current repo, register the captain-hook plugin, and wire the SessionEnd hook."""
109
+ """Watch the current repo and register the captain-hook plugin."""
140
110
  from captain_hook.cli import register_marketplace
141
111
 
142
112
  repo = current_repo(state.root)
143
113
  watch_repo(repo)
144
114
  register_marketplace(state.root)
145
- wired = ensure_review_wiring(state.root / ".claude" / "settings.json")
146
- click.echo(f"watching {repo}" + (" (SessionEnd hook wired into .claude/settings.json)" if wired else ""))
115
+ click.echo(f"watching {repo}")
147
116
 
148
117
 
149
118
  @review.command()
@@ -46,11 +46,17 @@ from cc_transcript.mining.confidence import MEDIUM, VERY_HIGH, CandidateSignal
46
46
  from cc_transcript.mining.signals import MiningSignal
47
47
  from cc_transcript.mining.sourcekind import SourceKind
48
48
  from cc_transcript.mining.spec import CONFIDENCE_STEP, bump
49
- from cc_transcript.models import AssistantEvent, OtherEvent, ToolResultBlock, UserEvent
49
+ from cc_transcript.models import (
50
+ AssistantEvent,
51
+ AttachmentEvent,
52
+ HookAdditionalContext,
53
+ HookBlockingError,
54
+ ToolResultBlock,
55
+ UserEvent,
56
+ )
50
57
 
51
58
  if TYPE_CHECKING:
52
59
  from collections.abc import Iterator, Mapping, Sequence
53
- from typing import Any
54
60
 
55
61
  from cc_transcript.decisions import Decision, DecisionLog
56
62
  from cc_transcript.ids import SessionId, ToolUseId
@@ -137,22 +143,10 @@ def classify_marker(text: str) -> Marker | None:
137
143
 
138
144
  def fingerprint_of(event: TranscriptEvent) -> Fingerprint | None:
139
145
  match event:
140
- case OtherEvent(type="attachment", raw=raw):
141
- attachment: Mapping[str, Any] = raw["attachment"]
142
- match attachment.get("type"):
143
- case "hook_additional_context":
144
- return Fingerprint(
145
- message="\n".join(str(part) for part in attachment["content"]),
146
- tool_use_id=attachment.get("toolUseID"),
147
- event=attachment.get("hookEvent"),
148
- )
149
- case "hook_blocking_error":
150
- return Fingerprint(
151
- message=str(attachment["blockingError"]["blockingError"]),
152
- event=str(attachment.get("hookEvent") or "Stop"),
153
- )
154
- case _:
155
- return None
146
+ case AttachmentEvent(detail=HookAdditionalContext(content=content, tool_use_id=tool_use_id, hook_event=hook_event)):
147
+ return Fingerprint(message="\n".join(content), tool_use_id=tool_use_id, event=hook_event)
148
+ case AttachmentEvent(detail=HookBlockingError(blocking_error={"blockingError": blocking_error}, hook_event=hook_event)):
149
+ return Fingerprint(message=str(blocking_error), event=str(hook_event or "Stop"))
156
150
  case UserEvent(meta=meta, text=text) if meta.is_meta and text.startswith(STOP_FEEDBACK_PREFIX):
157
151
  return Fingerprint(message=text.removeprefix(STOP_FEEDBACK_PREFIX), event="Stop")
158
152
  case UserEvent(blocks=blocks):
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "capt-hook"
3
3
  # Inert sentinel — the real version is stamped from the release tag (uv version --frozen).
4
- version = "9.0.0"
4
+ version = "9.2.0"
5
5
  description = "Stop repeating yourself to Claude."
6
6
  readme = "README.md"
7
7
  license = "PolyForm-Noncommercial-1.0.0"
@@ -29,7 +29,7 @@ classifiers = [
29
29
  ]
30
30
  requires-python = ">=3.13"
31
31
  dependencies = [
32
- "cc-transcript[judge]>=10.6,<11",
32
+ "cc-transcript[judge]>=11,<12",
33
33
  "pydantic>=2.0",
34
34
  "pydantic-settings>=2.0",
35
35
  "ast-grep-py>=0.39,<1",
@@ -1,5 +0,0 @@
1
- name = "general"
2
- version = "0.16.0"
3
- description = "Language-agnostic guards: git/jj command safety, doc & prompt nudges, task & plan discipline, model-routing nudges (review, security audit, and diagnosis route to gpt-5.5 via the codex-wrapper agent), code-review + docs-freshness gates, a detour check-in nudge, a verbose-comment nudge, and delegated-prose nudges that catch doc writing handed to a subagent or workflow without the writing-docs skill."
4
- hooks = "."
5
- nlp = true
File without changes
File without changes
File without changes
File without changes