capt-hook 6.2.0__tar.gz → 6.3.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 (107) hide show
  1. {capt_hook-6.2.0 → capt_hook-6.3.0}/PKG-INFO +1 -1
  2. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/capt-hook.toml +1 -1
  3. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/models.py +99 -30
  4. {capt_hook-6.2.0 → capt_hook-6.3.0}/pyproject.toml +1 -1
  5. {capt_hook-6.2.0 → capt_hook-6.3.0}/LICENSE +0 -0
  6. {capt_hook-6.2.0 → capt_hook-6.3.0}/README.md +0 -0
  7. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/.claude-plugin/plugin.json +0 -0
  8. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/__init__.py +0 -0
  9. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/__main__.py +0 -0
  10. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/app.py +0 -0
  11. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/ast_grep.py +0 -0
  12. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/classifiers/__init__.py +0 -0
  13. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/classifiers/conductor.py +0 -0
  14. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/classifiers/droid.py +0 -0
  15. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/classifiers/native.py +0 -0
  16. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/cli.py +0 -0
  17. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/command.py +0 -0
  18. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/conditions.py +0 -0
  19. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/context.py +0 -0
  20. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/contexts.py +0 -0
  21. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/decisions.py +0 -0
  22. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/dispatch.py +0 -0
  23. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/durable.py +0 -0
  24. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/events.py +0 -0
  25. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/fields.py +0 -0
  26. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/file.py +0 -0
  27. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/loader.py +0 -0
  28. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/log.py +0 -0
  29. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/__init__.py +0 -0
  30. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/__init__.py +0 -0
  31. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/_lib.py +0 -0
  32. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/commands.py +0 -0
  33. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/docs.py +0 -0
  34. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/plans.py +0 -0
  35. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/prompts.py +0 -0
  36. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/review.py +0 -0
  37. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/tasks.py +0 -0
  38. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/general/tombstones.py +0 -0
  39. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/go/__init__.py +0 -0
  40. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/go/capt-hook.toml +0 -0
  41. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/go/testing.py +0 -0
  42. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/go/toolchain.py +0 -0
  43. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/manager.py +0 -0
  44. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/python/__init__.py +0 -0
  45. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/python/capt-hook.toml +0 -0
  46. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/python/style.py +0 -0
  47. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/python/testing.py +0 -0
  48. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/python/toolchain.py +0 -0
  49. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/steering/__init__.py +0 -0
  50. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/steering/capt-hook.toml +0 -0
  51. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/packs/steering/steering.py +0 -0
  52. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/__init__.py +0 -0
  53. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/commands.py +0 -0
  54. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/lint.py +0 -0
  55. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/llm.py +0 -0
  56. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/nudge.py +0 -0
  57. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/rewrite.py +0 -0
  58. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/primitives/workflow.py +0 -0
  59. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/prompt.py +0 -0
  60. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/py.typed +0 -0
  61. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/__init__.py +0 -0
  62. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/cli.py +0 -0
  63. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/dashboard.py +0 -0
  64. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/fix.py +0 -0
  65. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/formats.py +0 -0
  66. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/judge.py +0 -0
  67. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/pipeline.py +0 -0
  68. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/repo.py +0 -0
  69. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/scan.py +0 -0
  70. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/settings.py +0 -0
  71. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/store.py +0 -0
  72. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/review/sync.py +0 -0
  73. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/session.py +0 -0
  74. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/settings.py +0 -0
  75. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/signals/__init__.py +0 -0
  76. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/signals/nlp.py +0 -0
  77. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
  78. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
  79. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
  80. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
  81. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
  82. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/bootstrapping-hooks/SKILL.md +0 -0
  83. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
  84. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
  85. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
  86. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
  87. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
  88. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
  89. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
  90. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/state.py +0 -0
  91. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/style/__init__.py +0 -0
  92. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/style/ast_grep.py +0 -0
  93. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/style/matchers.py +0 -0
  94. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/style/scope.py +0 -0
  95. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/style/types.py +0 -0
  96. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/tasks.py +0 -0
  97. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/templates/example_hook.py.tmpl +0 -0
  98. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/testing/__init__.py +0 -0
  99. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/testing/helpers.py +0 -0
  100. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/testing/session_cache.py +0 -0
  101. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/testing/types.py +0 -0
  102. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/types.py +0 -0
  103. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/util/__init__.py +0 -0
  104. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/util/fs.py +0 -0
  105. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/util/http.py +0 -0
  106. {capt_hook-6.2.0 → capt_hook-6.3.0}/captain_hook/util/model_cache.py +0 -0
  107. {capt_hook-6.2.0 → capt_hook-6.3.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: 6.2.0
3
+ Version: 6.3.0
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
@@ -1,5 +1,5 @@
1
1
  name = "general"
2
- version = "0.7.0"
2
+ version = "0.8.0"
3
3
  description = "Language-agnostic guards: git/command safety, doc & prompt nudges, task & plan discipline, and code-review + docs-freshness gates."
4
4
  hooks = "."
5
5
  nlp = true
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
3
4
  from dataclasses import dataclass
4
5
 
5
6
  from captain_hook import (
@@ -7,8 +8,10 @@ from captain_hook import (
7
8
  Allow,
8
9
  BaseHookEvent,
9
10
  Block,
11
+ Clause,
10
12
  Event,
11
13
  Input,
14
+ Phrase,
12
15
  Rewrite,
13
16
  TaskCall,
14
17
  Tool,
@@ -23,6 +26,7 @@ from captain_hook import (
23
26
  workflow_opt_values,
24
27
  workflow_script_source,
25
28
  )
29
+ from captain_hook.signals.nlp import nlp_scan
26
30
 
27
31
 
28
32
  @dataclass(frozen=True, slots=True)
@@ -39,12 +43,84 @@ class DelegatedSpawn:
39
43
  return f"model: {model}\nagent_type: {call.agent_type or '(default)'}\nprompt:\n{call.prompt}"
40
44
 
41
45
 
42
- WORKFLOW_SCRIPT_CAP = 14_000 # below the workflow prose nudge's max_context=16_000, so truncation stays ours
46
+ WORKFLOW_SCRIPT_CAP = 14_000 # below the prose hooks' max_context=16_000, so truncation stays ours
47
+
48
+
49
+ def prose_deliverable_sentences(text: str) -> list[str]:
50
+ """Sentences where a writing verb governs a prose artifact, minus negated asks ("do NOT edit CHANGELOG.md").
51
+
52
+ The text is de-noised for the tagger first: path/URL tokens dropped, brackets and
53
+ word-edge quotes blanked (so `agent('write …')` doesn't glue into one token; intra-word
54
+ apostrophes survive for "n't" negations), readme/changelog extensions stripped, intra-word
55
+ hyphens split, writing verbs lowercased, and imperative writing verbs given a determiner —
56
+ "Update CHANGELOG.md" otherwise parses as a noun compound.
57
+ """
58
+ verbs = r"write|draft|redraft|rewrite|revise|reword|polish|copyedit|compose|author|update|edit"
59
+ text = re.sub(r"\S+/\S+", " ", text)
60
+ text = re.sub(r"[(){}\[\]<>]|(?<![A-Za-z])['\"`]|['\"`](?![A-Za-z])", " ", text)
61
+ text = re.sub(r"(?i)\b(readme|changelog)\.(?:md|rst|txt)\b", r"\1", text)
62
+ text = re.sub(r"(?<=\w)-(?=\w)", " ", text)
63
+ text = re.sub(rf"(?i)\b({verbs})\b", lambda m: m.group(1).lower(), text)
64
+ text = re.sub(rf"\b({verbs})[ \t]+((?i:readme|changelog|docs))\b", r"\1 the \2", text)
65
+ artifact = Phrase(
66
+ "readme",
67
+ "readme.md",
68
+ "doc",
69
+ "docs",
70
+ "documentation",
71
+ "changelog",
72
+ "changelog.md",
73
+ "blog",
74
+ "blog post",
75
+ "release note",
76
+ "announcement",
77
+ "tutorial",
78
+ "guide",
79
+ "prose",
80
+ "marketing copy",
81
+ "article",
82
+ "newsletter",
83
+ "email",
84
+ "pr description",
85
+ "commit message",
86
+ )
87
+ writing = Phrase(
88
+ "write",
89
+ "draft",
90
+ "redraft",
91
+ "rewrite",
92
+ "revise",
93
+ "reword",
94
+ "polish",
95
+ "copyedit",
96
+ "compose",
97
+ "author",
98
+ "update",
99
+ "edit",
100
+ )
101
+ if not (matched := nlp_scan([Clause(noun=artifact, verb=writing)], text)):
102
+ return []
103
+ negated = set(nlp_scan([Clause(noun=artifact, verb=writing, negated=True)], text))
104
+ return [s for s in matched if s not in negated]
105
+
106
+
107
+ @dataclass(frozen=True, slots=True)
108
+ class ProseSpawn(DelegatedSpawn):
109
+ """Gating context: the pending spawn, present only when its prompt asks to produce a prose artifact."""
110
+
111
+ def content(self, evt: BaseHookEvent) -> str | None:
112
+ # zero-arg super() breaks under @dataclass(slots=True) — the decorator rebuilds the class
113
+ if (base := DelegatedSpawn.content(self, evt)) is None or (call := evt.as_input(TaskCall)) is None:
114
+ return None
115
+ if not (sentences := prose_deliverable_sentences((call.prompt or "")[:WORKFLOW_SCRIPT_CAP])):
116
+ return None
117
+ matched = "\n".join(f" {s[:300]}" for s in sentences)
118
+ return f"{base}\n\nsentences the prose prefilter matched:\n{matched}"
43
119
 
44
120
 
45
121
  @dataclass(frozen=True, slots=True)
46
122
  class WorkflowScriptSource:
47
- """Gating context: the pending Workflow call's script source, headed by its model pins."""
123
+ """Gating context: the pending Workflow call's script source, headed by its model pins and prose asks."""
48
124
 
49
125
  tag: str = "workflow_script"
50
126
  required: bool = True
@@ -62,9 +138,13 @@ class WorkflowScriptSource:
62
138
  f"… [script truncated: {len(source):,} chars total; every model pin is quoted above] …\n"
63
139
  f"{source[-tail:]}"
64
140
  )
141
+ if not (sentences := prose_deliverable_sentences(source)):
142
+ return None
143
+ matched = "\n".join(f" {s[:300]}" for s in sentences)
65
144
  return (
66
145
  "lines that pin a model in this script (a stage not quoted here inherits the "
67
- f"session model, fable):\n{pin_lines or ' (none)'}\n\n{source}"
146
+ f"session model, fable):\n{pin_lines or ' (none)'}\n\n"
147
+ f"sentences the prose prefilter matched:\n{matched}\n\n{source}"
68
148
  )
69
149
 
70
150
 
@@ -103,9 +183,10 @@ llm_gate(
103
183
  deliverable is prose.
104
184
 
105
185
  <delegated_spawn> holds the pending Agent/Task call: its model pin, agent type, and
106
- prompt. A regex prefilter already found a haiku/sonnet/opus pin plus a writing verb
107
- near a prose noun in the prompt; your job is precision: is a prose artifact what this
108
- subagent is asked to PRODUCE?
186
+ prompt, ending with the sentences a clause prefilter matched — each asks a writing
187
+ verb of a prose artifact, with negated asks ("do NOT edit the docs") already
188
+ screened out. Your job is precision: is a prose artifact what this subagent is
189
+ asked to PRODUCE?
109
190
 
110
191
  The Models rubric: all writing a user reads — READMEs, docs, changelogs, release
111
192
  notes, blog posts, PR descriptions, any user-facing text — routes to fable. Work
@@ -145,22 +226,11 @@ Review findings go to the orchestrator; the subagent writes no user-facing prose
145
226
  "to fable: drop model to inherit the session model, or pass model='fable'. "
146
227
  "See CLAUDE.md § Plan Execution & Orchestration (Models)."
147
228
  ),
148
- contexts=[DelegatedSpawn()],
229
+ contexts=[ProseSpawn()],
149
230
  events=Event.PreToolUse,
150
231
  only_if=[
151
232
  Tool("Agent|Task"),
152
233
  ToolInput("model", r"(?i)\b(haiku|sonnet|opus)\b"),
153
- ToolInput(
154
- "prompt",
155
- r"(?i)\b(?:writ(?:e|es|ing|ten)|draft|redraft|rewrit|revis|polish|copyedit|compose|author|update|edit)\w*"
156
- r"\W+(?:\w+\W+){0,4}(?:readme|docs?|documentation|blog|changelog|release notes|announcement"
157
- r"|tutorial|guide|prose|copywrit|marketing copy|article|newsletter|email"
158
- r"|pr description|commit message)\b"
159
- r"|\b(?:readme|docs?|documentation|blog|changelog|release notes|announcement"
160
- r"|tutorial|guide|prose|copywrit|marketing copy|article|newsletter|email"
161
- r"|pr description|commit message)\b"
162
- r"\W+(?:\w+\W+){0,4}(?:writ(?:e|es|ing|ten)|draft|redraft|rewrit|revis|polish|copyedit|compose|author|update|edit)",
163
- ),
164
234
  ],
165
235
  skip_if=[
166
236
  ToolInput("prompt", r"(?i)\b(classif|label|tag|categoriz|count|extract|mechanical)"),
@@ -168,6 +238,7 @@ Review findings go to the orchestrator; the subagent writes no user-facing prose
168
238
  ],
169
239
  agent=False,
170
240
  transcript=False,
241
+ max_context=16_000,
171
242
  tests={
172
243
  Input(model="sonnet", prompt="Write the README quickstart for this repo"): Block(),
173
244
  Input(model="opus", prompt="draft the release notes for v2"): Block(),
@@ -183,6 +254,10 @@ Review findings go to the orchestrator; the subagent writes no user-facing prose
183
254
  Input(
184
255
  model="opus",
185
256
  prompt="Fix the failing test in cli.py. Do NOT edit the CHANGELOG — a sibling owns updating it",
257
+ ): Allow(),
258
+ Input(
259
+ model="opus",
260
+ prompt="Fix the failing test in cli.py; do NOT edit CHANGELOG.md. Then draft the release notes.",
186
261
  llm={"block": False},
187
262
  ): Allow(),
188
263
  },
@@ -289,11 +364,12 @@ llm_nudge(
289
364
  deliverable is prose.
290
365
 
291
366
  <workflow_script> holds the pending Workflow call's script source. Its header quotes
292
- every line of the script that pins a model; a stage that is not quoted there carries
367
+ every line of the script that pins a model a stage that is not quoted there carries
293
368
  no pin and inherits the session model, fable — already correctly routed, whatever it
294
- writes. A regex prefilter already found both a haiku/sonnet/opus pin and a writing
295
- verb near a prose noun somewhere in the script; your job is precision: does a
296
- PINNED stage have prose as its own deliverable?
369
+ writes followed by the sentences a clause prefilter matched: each asks a writing
370
+ verb of a prose artifact, with negated asks ("do NOT edit CHANGELOG.md") already
371
+ screened out. Your job is precision: does a PINNED stage have prose as its own
372
+ deliverable?
297
373
 
298
374
  The Models rubric: all writing a user reads — READMEs, docs, changelogs, release
299
375
  notes, blog posts, announcements, any user-facing text — routes to fable. A stage's
@@ -350,13 +426,7 @@ Reading and classifying docs is analysis, not a prose deliverable.
350
426
  events=Event.PreToolUse,
351
427
  only_if=[
352
428
  Tool("Workflow"),
353
- WorkflowScript(
354
- pattern=r"(?i)\b(?:writ(?:e|es|ing|ten)|draft|redraft|rewrit|revis|polish|copyedit|compose|author|update|edit)\w*"
355
- r"\W+(?:\w+\W+){0,4}(?:readme|docs?|documentation|blog|changelog|release notes|prose)\b"
356
- r"|\b(?:readme|docs?|documentation|blog|changelog|release notes|prose)\b"
357
- r"\W+(?:\w+\W+){0,4}(?:writ(?:e|es|ing|ten)|draft|redraft|rewrit|revis|polish|copyedit|compose|author|update|edit)",
358
- model="haiku|sonnet|opus",
359
- ),
429
+ WorkflowScript(model="haiku|sonnet|opus"),
360
430
  ],
361
431
  max_fires=2,
362
432
  max_context=16_000,
@@ -372,7 +442,6 @@ Reading and classifying docs is analysis, not a prose deliverable.
372
442
  ): Allow(),
373
443
  Input(
374
444
  script="agent('Fix the import in cli.py. Do NOT edit CHANGELOG.md — a sibling owns it', {model: 'opus'})",
375
- llm={"fire": False},
376
445
  ): Allow(),
377
446
  },
378
447
  )
@@ -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 = "6.2.0"
4
+ version = "6.3.0"
5
5
  description = "Declarative hook framework for Claude Code"
6
6
  readme = "README.md"
7
7
  license = "PolyForm-Noncommercial-1.0.0"
File without changes
File without changes
File without changes
File without changes
File without changes