capt-hook 7.0.0__tar.gz → 8.0.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.
- {capt_hook-7.0.0 → capt_hook-8.0.0}/PKG-INFO +1 -1
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/capt-hook.toml +2 -2
- capt_hook-8.0.0/captain_hook/packs/general/comments.py +160 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/commands.py +1 -1
- {capt_hook-7.0.0 → capt_hook-8.0.0}/pyproject.toml +1 -1
- {capt_hook-7.0.0 → capt_hook-8.0.0}/LICENSE +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/README.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/.claude-plugin/plugin.json +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/__main__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/app.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/ast_grep.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/classifiers/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/classifiers/conductor.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/classifiers/droid.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/classifiers/native.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/cli.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/conditions.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/context.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/contexts.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/decisions.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/dispatch.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/durable.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/events.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/fields.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/file.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/loader.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/log.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/fixes/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/fixes/capt-hook.toml +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/fixes/teammate_permissions.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/_lib.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/commands.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/detours.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/docs.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/models.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/plans.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/prompts.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/review.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/tasks.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/general/tombstones.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/go/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/go/capt-hook.toml +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/go/testing.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/go/toolchain.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/manager.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/python/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/python/capt-hook.toml +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/python/style.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/python/testing.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/python/toolchain.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/steering/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/steering/capt-hook.toml +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/packs/steering/steering.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/lint.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/llm.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/nudge.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/permissions.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/rewrite.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/primitives/workflow.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/prompt.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/py.typed +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/cli.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/dashboard.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/fix.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/formats.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/judge.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/pipeline.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/repo.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/scan.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/settings.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/store.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/review/sync.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/session.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/settings.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/signals/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/signals/nlp.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/bootstrapping-hooks/SKILL.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/state.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/style/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/style/ast_grep.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/style/matchers.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/style/scope.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/style/types.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/tasks.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/templates/example_hook.py.tmpl +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/testing/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/testing/helpers.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/testing/session_cache.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/testing/types.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/types.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/__init__.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/automode.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/fs.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/http.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/model_cache.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/proc.py +0 -0
- {capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/util/text.py +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name = "general"
|
|
2
|
-
version = "0.
|
|
3
|
-
description = "Language-agnostic guards: git/command safety, doc & prompt nudges, task & plan discipline, code-review + docs-freshness gates,
|
|
2
|
+
version = "0.11.0"
|
|
3
|
+
description = "Language-agnostic guards: git/command safety, doc & prompt nudges, task & plan discipline, code-review + docs-freshness gates, a detour check-in nudge, and a verbose-comment nudge."
|
|
4
4
|
hooks = "."
|
|
5
5
|
nlp = true
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Discourage verbose comments: warn when an edit introduces a long comment or line-comment run.
|
|
2
|
+
|
|
3
|
+
Comments should be terse and used sparingly — names, types, and organization carry the
|
|
4
|
+
meaning. The one legitimate exception is documentation-generation comments (godoc, rustdoc,
|
|
5
|
+
docstrings), and even a long doc run trips this warn by design: the threshold is deliberately
|
|
6
|
+
strict so verbosity of any kind gets a nudge. Language-agnostic via the tree-sitter comment
|
|
7
|
+
kinds in :data:`~captain_hook.ast_grep.COMMENT_TYPES`; diff-based, so only comments the edit
|
|
8
|
+
*introduces* count — a pre-existing long comment re-saved unchanged never fires.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
from captain_hook import (
|
|
16
|
+
Allow,
|
|
17
|
+
BaseHookEvent,
|
|
18
|
+
CustomCondition,
|
|
19
|
+
Event,
|
|
20
|
+
FileFixture,
|
|
21
|
+
Input,
|
|
22
|
+
Tool,
|
|
23
|
+
Warn,
|
|
24
|
+
nudge,
|
|
25
|
+
)
|
|
26
|
+
from captain_hook.ast_grep import introduced_comments, lang_for_path
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from collections.abc import Iterable
|
|
30
|
+
|
|
31
|
+
from captain_hook.ast_grep import Match
|
|
32
|
+
|
|
33
|
+
MAX_COMMENT_LINES = 4
|
|
34
|
+
MAX_COMMENT_CHARS = 300
|
|
35
|
+
|
|
36
|
+
# Fixtures for the inline tests — kept as module constants so each physical line stays short.
|
|
37
|
+
GO_NO_COMMENT = "package p\n\nfunc F() {}\n"
|
|
38
|
+
GO_ONE_LINE_DOC = "package p\n\n// F does a thing.\nfunc F() {}\n"
|
|
39
|
+
GO_THREE_LINE_DOC = (
|
|
40
|
+
"package p\n\n// F does a thing.\n// It handles the empty case.\n// Returns an error otherwise.\nfunc F() {}\n"
|
|
41
|
+
)
|
|
42
|
+
GO_LONG_RUN = (
|
|
43
|
+
"package p\n\nfunc F() {\n"
|
|
44
|
+
"\t// explanation line one goes here\n"
|
|
45
|
+
"\t// explanation line two goes here\n"
|
|
46
|
+
"\t// explanation line three is here\n"
|
|
47
|
+
"\t// explanation line four is here\n"
|
|
48
|
+
"\t// explanation line five is here\n"
|
|
49
|
+
"\t// explanation line six is here ok\n"
|
|
50
|
+
"\tx := 1\n}\n"
|
|
51
|
+
)
|
|
52
|
+
GO_EXISTING_OLD = "func F() {\n\t// aaa\n\t// bbb\n\t// ccc\n\t// ddd\n\t// eee\n\t// fff\n\tx := 1\n}\n"
|
|
53
|
+
GO_EXISTING_NEW = "func F() {\n\t// aaa\n\t// bbb\n\t// ccc\n\t// ddd\n\t// eee\n\t// fff\n\tx := 2\n}\n"
|
|
54
|
+
GO_WRITE_PLAIN = "package p\n\nvar x = 1\n"
|
|
55
|
+
GO_WRITE_RUN = (
|
|
56
|
+
"package p\n\n"
|
|
57
|
+
"// note one here\n// note two here\n// note three here\n"
|
|
58
|
+
"// note four here\n// note five here\n// note six here\n"
|
|
59
|
+
"var x = 1\n"
|
|
60
|
+
)
|
|
61
|
+
GO_WRITE_EXISTING_OLD = "package p\n\n// aaa\n// bbb\n// ccc\n// ddd\n// eee\n// fff\nvar x = 1\n"
|
|
62
|
+
GO_WRITE_EXISTING_NEW = "package p\n\n// aaa\n// bbb\n// ccc\n// ddd\n// eee\n// fff\nvar x = 2\n"
|
|
63
|
+
RS_LONG_BLOCK = "/*\n one\n two\n three\n four\n five\n six\n seven\n eight\n*/\nfn f() {}\n"
|
|
64
|
+
RS_LONG_DOC = (
|
|
65
|
+
"/// line one of the rustdoc here\n"
|
|
66
|
+
"/// line two of the rustdoc here\n"
|
|
67
|
+
"/// line three of the rustdoc\n"
|
|
68
|
+
"/// line four of the rustdoc\n"
|
|
69
|
+
"/// line five of the rustdoc\n"
|
|
70
|
+
"/// line six of the rustdoc ok\n"
|
|
71
|
+
"pub fn f() {}\n"
|
|
72
|
+
)
|
|
73
|
+
PY_LONG_RUN = (
|
|
74
|
+
"# note one here\n# note two here\n# note three here\n# note four here\n# note five here\n# note six here\nx = 1\n"
|
|
75
|
+
)
|
|
76
|
+
PY_LONG_DOCSTRING = (
|
|
77
|
+
'def f():\n """\n'
|
|
78
|
+
" line one\n line two\n line three\n"
|
|
79
|
+
" line four\n line five\n line six\n"
|
|
80
|
+
' """\n return 1\n'
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def runs(matches: Iterable[Match]) -> list[list[Match]]:
|
|
85
|
+
"""Group comments into runs of adjacent lines — a block comment, or consecutive line
|
|
86
|
+
comments with no code between — each a list of :class:`Match` in document order."""
|
|
87
|
+
grouped: list[list[Match]] = []
|
|
88
|
+
for m in sorted(matches, key=lambda c: c.line):
|
|
89
|
+
if grouped and m.line <= grouped[-1][-1].end_line + 1:
|
|
90
|
+
grouped[-1].append(m)
|
|
91
|
+
else:
|
|
92
|
+
grouped.append([m])
|
|
93
|
+
return grouped
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def too_long(run: list[Match]) -> bool:
|
|
97
|
+
"""Whether a comment run exceeds the line or character budget."""
|
|
98
|
+
lines = run[-1].end_line - run[0].line + 1
|
|
99
|
+
chars = sum(len(m.text) for m in run)
|
|
100
|
+
return lines > MAX_COMMENT_LINES or chars > MAX_COMMENT_CHARS
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class LongCommentIntroduced(CustomCondition):
|
|
104
|
+
"""True when the pending edit introduces a comment (or adjacent line-comment run) past
|
|
105
|
+
the length budget. Diffs the edit's pre-image against its new text, so a comment already
|
|
106
|
+
present before the edit never counts; files whose language ast-grep can't parse yield False."""
|
|
107
|
+
|
|
108
|
+
def check(self, evt: BaseHookEvent) -> bool:
|
|
109
|
+
if (
|
|
110
|
+
not (file := evt.file)
|
|
111
|
+
or not (lang := lang_for_path(file.path))
|
|
112
|
+
or (old := evt.replaced) is None
|
|
113
|
+
or (new := evt.content) is None
|
|
114
|
+
):
|
|
115
|
+
return False
|
|
116
|
+
return any(too_long(run) for run in runs(introduced_comments(old, new, lang)))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
nudge(
|
|
120
|
+
"Verbose comment introduced. Comments should be terse and used sparingly — let names, "
|
|
121
|
+
"types, and organization document the code. Keep documentation-generation comments "
|
|
122
|
+
"(godoc / rustdoc / docstrings) to a real description, and drop long inline commentary "
|
|
123
|
+
"or anything that restates the code. See: STYLEGUIDE.md § Comments.",
|
|
124
|
+
only_if=[Tool("Edit", "Write", "MultiEdit"), LongCommentIntroduced()],
|
|
125
|
+
events=Event.PreToolUse,
|
|
126
|
+
tests={
|
|
127
|
+
# Short / within-budget comments — allowed.
|
|
128
|
+
Input(file="doc.go", old=GO_NO_COMMENT, content=GO_ONE_LINE_DOC): Allow(),
|
|
129
|
+
Input(file="doc.go", old=GO_NO_COMMENT, content=GO_THREE_LINE_DOC): Allow(),
|
|
130
|
+
Input(file="m.py", old="x = 1\n", content="# set x\nx = 1\n"): Allow(),
|
|
131
|
+
Input(file="lib.rs", old="pub fn f() {}\n", content="/// Builds a widget.\npub fn f() {}\n"): Allow(),
|
|
132
|
+
# Long introduced comment runs — warned.
|
|
133
|
+
Input(file="svc.go", old="package p\n\nfunc F() {\n\tx := 1\n}\n", content=GO_LONG_RUN): Warn(
|
|
134
|
+
pattern="Verbose comment"
|
|
135
|
+
), # 6-line // run > 4 lines
|
|
136
|
+
Input(file="big.go", old=GO_NO_COMMENT, content="package p\n\n// " + "x" * 320 + "\nfunc F() {}\n"): Warn(
|
|
137
|
+
pattern="Verbose comment"
|
|
138
|
+
), # single line, > 300 chars
|
|
139
|
+
Input(file="lib.rs", old="fn f() {}\n", content=RS_LONG_BLOCK): Warn(pattern="Verbose comment"),
|
|
140
|
+
Input(file="lib.rs", old="pub fn f() {}\n", content=RS_LONG_DOC): Warn(
|
|
141
|
+
pattern="Verbose comment"
|
|
142
|
+
), # long rustdoc run also warns (strict threshold, by design)
|
|
143
|
+
Input(file="m.py", old="x = 1\n", content=PY_LONG_RUN): Warn(pattern="Verbose comment"),
|
|
144
|
+
# Diff-gating: a pre-existing long run re-saved unchanged does not fire.
|
|
145
|
+
Input(file="svc.go", old=GO_EXISTING_OLD, content=GO_EXISTING_NEW): Allow(),
|
|
146
|
+
# Python docstrings are string nodes, not comments — never trip this.
|
|
147
|
+
Input(file="m.py", old="def f():\n return 1\n", content=PY_LONG_DOCSTRING): Allow(),
|
|
148
|
+
# Write tool: pre-image comes from disk at PreToolUse.
|
|
149
|
+
Input(
|
|
150
|
+
tool="Write",
|
|
151
|
+
file=FileFixture(name="cmt_new.go", content=GO_WRITE_PLAIN),
|
|
152
|
+
content=GO_WRITE_RUN,
|
|
153
|
+
): Warn(pattern="Verbose comment"), # long run added to existing file
|
|
154
|
+
Input(
|
|
155
|
+
tool="Write",
|
|
156
|
+
file=FileFixture(name="cmt_pre.go", content=GO_WRITE_EXISTING_OLD),
|
|
157
|
+
content=GO_WRITE_EXISTING_NEW,
|
|
158
|
+
): Allow(), # run already on disk: not introduced
|
|
159
|
+
},
|
|
160
|
+
)
|
|
@@ -97,7 +97,7 @@ def rewrite_command(
|
|
|
97
97
|
|
|
98
98
|
- **(pattern, replace)** — polymorphic by the shape of ``pattern``. When it carries an ast-grep
|
|
99
99
|
metavariable (``$NAME`` / ``$$$NAME``), the command is rewritten *structurally* over
|
|
100
|
-
tree-sitter-bash via [`
|
|
100
|
+
tree-sitter-bash via [`ast_grep.rewrite`][captain_hook.ast_grep.rewrite]; otherwise it is
|
|
101
101
|
a regex rewritten via ``re.sub``. So ``rewrite_command("cat $$$ARGS", "bat $$$ARGS")`` is
|
|
102
102
|
structural and ``rewrite_command(r"^cat\s+(\S+)$", r"bat \1")`` is regex. ``note`` surfaces as
|
|
103
103
|
``additionalContext`` so the model sees the substitution.
|
|
@@ -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 = "
|
|
4
|
+
version = "8.0.0"
|
|
5
5
|
description = "Stop repeating yourself to Claude."
|
|
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
|
|
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
|
|
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-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md
RENAMED
|
File without changes
|
|
File without changes
|
{capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/pitfalls.md
RENAMED
|
File without changes
|
{capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/authoring-hooks/references/testing-hooks.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{capt_hook-7.0.0 → capt_hook-8.0.0}/captain_hook/skills/scanning-sessions/references/pr-workflow.md
RENAMED
|
File without changes
|
{capt_hook-7.0.0 → capt_hook-8.0.0}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|