capt-hook 8.0.0__tar.gz → 8.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.
- {capt_hook-8.0.0 → capt_hook-8.2.0}/PKG-INFO +1 -1
- capt_hook-8.2.0/captain_hook/packs/general/capt-hook.toml +5 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/models.py +207 -18
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/prompts.py +10 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/tasks.py +15 -1
- capt_hook-8.2.0/captain_hook/packs/steering/capt-hook.toml +5 -0
- capt_hook-8.2.0/captain_hook/packs/steering/steering.py +731 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/pyproject.toml +1 -1
- capt_hook-8.0.0/captain_hook/packs/general/capt-hook.toml +0 -5
- capt_hook-8.0.0/captain_hook/packs/steering/capt-hook.toml +0 -5
- capt_hook-8.0.0/captain_hook/packs/steering/steering.py +0 -350
- {capt_hook-8.0.0 → capt_hook-8.2.0}/LICENSE +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/README.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/.claude-plugin/plugin.json +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/__main__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/app.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/ast_grep.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/classifiers/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/classifiers/conductor.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/classifiers/droid.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/classifiers/native.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/cli.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/conditions.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/context.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/contexts.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/decisions.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/dispatch.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/durable.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/events.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/fields.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/file.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/loader.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/log.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/fixes/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/fixes/capt-hook.toml +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/fixes/teammate_permissions.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/_lib.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/commands.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/comments.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/detours.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/docs.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/plans.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/review.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/general/tombstones.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/go/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/go/capt-hook.toml +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/go/testing.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/go/toolchain.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/manager.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/python/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/python/capt-hook.toml +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/python/style.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/python/testing.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/python/toolchain.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/packs/steering/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/commands.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/lint.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/llm.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/nudge.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/permissions.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/rewrite.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/primitives/workflow.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/prompt.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/py.typed +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/cli.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/dashboard.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/fix.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/formats.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/judge.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/pipeline.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/repo.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/scan.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/settings.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/store.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/review/sync.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/session.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/settings.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/signals/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/signals/nlp.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/bootstrapping-hooks/SKILL.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/state.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/style/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/style/ast_grep.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/style/matchers.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/style/scope.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/style/types.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/tasks.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/templates/example_hook.py.tmpl +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/testing/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/testing/helpers.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/testing/session_cache.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/testing/types.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/types.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/__init__.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/automode.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/fs.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/http.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/model_cache.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/proc.py +0 -0
- {capt_hook-8.0.0 → capt_hook-8.2.0}/captain_hook/util/text.py +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
name = "general"
|
|
2
|
+
version = "0.13.0"
|
|
3
|
+
description = "Language-agnostic guards: git/command safety, doc & prompt nudges, task & plan discipline, model-routing nudges (review, security audit, and diagnosis route to gpt-5.5), 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
|
|
@@ -321,7 +321,8 @@ The Models rubric: implementation delegates to opus-4.8 at xhigh — opus is ~2x
|
|
|
321
321
|
than fable and nearly as capable. Fable's lanes are orchestration, design/architecture
|
|
322
322
|
review, hard planning, all prose/writing, and implementation that is very sensitive or
|
|
323
323
|
error-prone (auth, migrations, concurrency, data loss, crypto, subtle algorithms).
|
|
324
|
-
Code/diff review and bug diagnosis have their own gpt-5.5
|
|
324
|
+
Code/diff review, security review/audit, and bug diagnosis have their own gpt-5.5
|
|
325
|
+
lanes with separate nudges.
|
|
325
326
|
|
|
326
327
|
Set fire=true only when the prompt is clearly routine implementation — building, fixing,
|
|
327
328
|
wiring, or refactoring code — with no fable-lane signal. A prompt that reviews, plans,
|
|
@@ -476,20 +477,25 @@ Auth plus concurrency is a sensitive surface — fable's inline lane.
|
|
|
476
477
|
)
|
|
477
478
|
|
|
478
479
|
llm_nudge(
|
|
479
|
-
"""Decide whether this delegated subagent runs code review
|
|
480
|
-
|
|
480
|
+
"""Decide whether this delegated subagent runs code review, a security review/audit
|
|
481
|
+
or verification of security-sensitive code, or bug diagnosis that should route to
|
|
482
|
+
gpt-5.5 instead of fable.
|
|
481
483
|
|
|
482
484
|
<delegated_spawn> holds the pending Agent/Task call: its model pin (or that it inherits
|
|
483
485
|
the session model, fable), agent type, and prompt.
|
|
484
486
|
|
|
485
487
|
The Models rubric: code/diff review — sweeping a diff or codebase for bugs,
|
|
486
|
-
correctness, or cleanups; finder and refuter passes over findings —
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
488
|
+
correctness, or cleanups; finder and refuter passes over findings — security
|
|
489
|
+
review/audit and verification of security-sensitive code (auth, input validation,
|
|
490
|
+
crypto, secrets), and bug diagnosis route to gpt-5.5 via the codex skill; fable is
|
|
491
|
+
the escalation target when gpt-5.5's output misses. Fable keeps design/architecture
|
|
492
|
+
review, "is this the right approach" judgment, prose review, the synthesis/
|
|
493
|
+
accept-reject pass over review findings — and security-sensitive implementation,
|
|
494
|
+
which is not review.
|
|
495
|
+
|
|
496
|
+
Set fire=true only when the prompt clearly reviews code or diffs for defects, audits
|
|
497
|
+
or verifies security-sensitive code, or diagnoses a bug, and the spawn would run on
|
|
498
|
+
fable. Design review, approach judgment,
|
|
493
499
|
synthesis over findings, and prose review are fable's lanes: fire=false. When
|
|
494
500
|
uncertain, fire=false — the agent may have chosen fable deliberately, and a false
|
|
495
501
|
alarm teaches it to ignore this nudge. Keep reasoning under 40 words.
|
|
@@ -519,10 +525,23 @@ The accept-reject pass over findings stays on fable.
|
|
|
519
525
|
Review the README draft for factual errors.
|
|
520
526
|
Prose review stays on fable.
|
|
521
527
|
</example>
|
|
528
|
+
<example fire="true">
|
|
529
|
+
Audit the session-token handling in auth/middleware.py for vulnerabilities.
|
|
530
|
+
Security review/audit of code — gpt-5.5's lane via codex.
|
|
531
|
+
</example>
|
|
532
|
+
<example fire="true">
|
|
533
|
+
Verify the new input-validation layer rejects path traversal and injection payloads.
|
|
534
|
+
Verification of security-sensitive code routes to gpt-5.5.
|
|
535
|
+
</example>
|
|
536
|
+
<example fire="false">
|
|
537
|
+
Implement mitigations for the security-audit findings in auth.py.
|
|
538
|
+
Security-sensitive implementation, not review — the implementation lanes apply.
|
|
539
|
+
</example>
|
|
522
540
|
</examples>""",
|
|
523
541
|
message=lambda r: (
|
|
524
542
|
f"This review/diagnosis delegation would run on fable. {r.reasoning} "
|
|
525
|
-
"Code/diff review
|
|
543
|
+
"Code/diff review, security review/audit and verification of security-sensitive code, "
|
|
544
|
+
"and bug diagnosis route to gpt-5.5: run the codex skill (from a "
|
|
526
545
|
"workflow or subagent, spawn a model='sonnet', effort='low' wrapper that writes a "
|
|
527
546
|
"self-contained codex prompt), and escalate to fable only when gpt-5.5's output misses. "
|
|
528
547
|
"Design review and findings synthesis stay on fable. "
|
|
@@ -532,7 +551,11 @@ Prose review stays on fable.
|
|
|
532
551
|
events=Event.PreToolUse,
|
|
533
552
|
only_if=[
|
|
534
553
|
Tool("Agent|Task"),
|
|
535
|
-
ToolInput(
|
|
554
|
+
ToolInput(
|
|
555
|
+
"prompt",
|
|
556
|
+
r"(?i)(\b(review|refut|adversari|audit|correctness|diagnos|root.?caus|secur|vuln|pentest)"
|
|
557
|
+
r"|\bverif\w*[\s\S]{0,160}?\b(auth|crypt|secret|sanitiz|inject|input.?valid|token|session))",
|
|
558
|
+
),
|
|
536
559
|
],
|
|
537
560
|
skip_if=[
|
|
538
561
|
ToolInput("model", r"(?i)\b(opus|sonnet|haiku)\b"),
|
|
@@ -552,6 +575,13 @@ Prose review stays on fable.
|
|
|
552
575
|
prompt="Synthesize the confirmed review findings and decide which to fix",
|
|
553
576
|
llm={"fire": False},
|
|
554
577
|
): Allow(),
|
|
578
|
+
Input(prompt="Audit auth/session.py for security vulnerabilities"): Warn(pattern="gpt-5.5"),
|
|
579
|
+
Input(prompt="Verify the input-validation change blocks path traversal"): Warn(pattern="codex"),
|
|
580
|
+
Input(prompt="Verify the pagination change renders the last page correctly"): Allow(),
|
|
581
|
+
Input(
|
|
582
|
+
prompt="Implement mitigations for the security audit findings in auth.py",
|
|
583
|
+
llm={"fire": False},
|
|
584
|
+
): Allow(),
|
|
555
585
|
},
|
|
556
586
|
)
|
|
557
587
|
|
|
@@ -666,10 +696,13 @@ line that pins a model — a stage not quoted there carries no pin and inherits
|
|
|
666
696
|
session model, fable.
|
|
667
697
|
|
|
668
698
|
The Models rubric: code/diff review stages — finder sweeps over a diff or codebase,
|
|
669
|
-
adversarial refuters over findings —
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
699
|
+
adversarial refuters over findings — security review/audit stages and verification
|
|
700
|
+
of security-sensitive code (auth, input validation, crypto, secrets), and bug
|
|
701
|
+
diagnosis route to gpt-5.5 via the codex skill. A stage does that correctly when it
|
|
702
|
+
pins model 'sonnet' at low effort and its prompt writes a self-contained codex prompt
|
|
703
|
+
and runs the codex skill. Fable keeps the synthesis/accept-reject stage over findings
|
|
704
|
+
and design/architecture judgment — and security-sensitive implementation, which is
|
|
705
|
+
not review.
|
|
673
706
|
|
|
674
707
|
Set fire=true only when at least one review or diagnosis stage would run on fable —
|
|
675
708
|
unpinned, or pinned 'fable'. Stages already wrapped for codex, synthesis stages, and
|
|
@@ -695,10 +728,14 @@ Already the codex wrapper — correctly routed.
|
|
|
695
728
|
agent(`Synthesize the confirmed findings and decide which to fix`)
|
|
696
729
|
Synthesis/accept-reject stays on fable.
|
|
697
730
|
</example>
|
|
731
|
+
<example fire="true">
|
|
732
|
+
agent(`Audit the auth flow for injection and session-fixation issues; return findings as JSON`)
|
|
733
|
+
An unpinned security audit inherits fable; security review/audit is the codex-wrapper lane.
|
|
734
|
+
</example>
|
|
698
735
|
</examples>""",
|
|
699
736
|
message=lambda r: (
|
|
700
737
|
f"This workflow runs review/diagnosis stages on fable. {r.reasoning} "
|
|
701
|
-
"Route finder, refuter, and diagnosis stages to gpt-5.5: make each a model: 'sonnet', "
|
|
738
|
+
"Route finder, refuter, security-audit, and diagnosis stages to gpt-5.5: make each a model: 'sonnet', "
|
|
702
739
|
"effort: 'low' stage that writes a self-contained codex prompt and runs the codex skill; "
|
|
703
740
|
"keep the synthesis/accept-reject stage on fable (inherit the session model). "
|
|
704
741
|
"See CLAUDE.md § Plan Execution & Orchestration (Models)."
|
|
@@ -707,7 +744,10 @@ Synthesis/accept-reject stays on fable.
|
|
|
707
744
|
events=Event.PreToolUse,
|
|
708
745
|
only_if=[
|
|
709
746
|
Tool("Workflow"),
|
|
710
|
-
WorkflowScript(
|
|
747
|
+
WorkflowScript(
|
|
748
|
+
pattern=r"(?i)(\b(review|refut|adversari|audit|correctness|diagnos|root.?caus|secur|vuln|pentest)"
|
|
749
|
+
r"|\bverif\w*[\s\S]{0,160}?\b(auth|crypt|secret|sanitiz|inject|input.?valid|token|session))",
|
|
750
|
+
),
|
|
711
751
|
],
|
|
712
752
|
max_fires=2,
|
|
713
753
|
max_context=16_000,
|
|
@@ -730,5 +770,154 @@ Synthesis/accept-reject stays on fable.
|
|
|
730
770
|
script="agent(`Synthesize the confirmed review findings and decide which to fix`)",
|
|
731
771
|
llm={"fire": False},
|
|
732
772
|
): Allow(),
|
|
773
|
+
Input(
|
|
774
|
+
script="agent(`Audit the login flow for auth bypass and injection; return findings as JSON`)"
|
|
775
|
+
): Warn(pattern="gpt-5.5"),
|
|
776
|
+
Input(script="agent('Verify the CLI renders the last page correctly')"): Allow(),
|
|
777
|
+
},
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
llm_nudge(
|
|
781
|
+
"""Decide whether this delegated subagent call delegates documentation or prose
|
|
782
|
+
writing without directing the subagent to read the writing-docs skill.
|
|
783
|
+
|
|
784
|
+
<delegated_spawn> holds the pending Agent/Task call: its model pin, agent type, and
|
|
785
|
+
prompt, ending with the sentences a clause prefilter matched — each asks a writing verb
|
|
786
|
+
of a prose artifact, with negated asks ("do NOT edit the docs") already screened out.
|
|
787
|
+
|
|
788
|
+
You are watching an orchestrating agent spawn a subagent. Decide whether the pending
|
|
789
|
+
prompt delegates documentation or prose writing — a README, docs page, CHANGELOG,
|
|
790
|
+
tutorial, release notes, or similar deliverable — without directing the subagent to
|
|
791
|
+
read the writing-docs skill. Restated style rules ('technical-builder voice', 'no hype
|
|
792
|
+
adjectives', 'first person, confident') do not count as reading the skill; that
|
|
793
|
+
paraphrase is exactly the failure to catch. Fire only when the prompt's deliverable is
|
|
794
|
+
prose the writing-docs skill governs. Do not fire for code work that incidentally
|
|
795
|
+
mentions a doc file, for reading or reviewing docs without writing them, or for a prompt
|
|
796
|
+
that already tells the agent to read the skill or its references.
|
|
797
|
+
|
|
798
|
+
When uncertain, fire=false — a false alarm teaches the agent to ignore this nudge. Keep
|
|
799
|
+
reasoning under 40 words.
|
|
800
|
+
|
|
801
|
+
<examples>
|
|
802
|
+
<example fire="true">
|
|
803
|
+
Rewrite the README for this repo. You are fable; technical-builder voice, no hype adjectives.
|
|
804
|
+
README prose with the style rules paraphrased in place of the skill — the drift this catches.
|
|
805
|
+
</example>
|
|
806
|
+
<example fire="true">
|
|
807
|
+
Draft the release notes for v2. Keep it first-person and confident, no marketing fluff.
|
|
808
|
+
Release-notes prose; restated voice rules, no pointer to the writing-docs skill.
|
|
809
|
+
</example>
|
|
810
|
+
<example fire="false">
|
|
811
|
+
Fix the failing test in cli.py; the README already documents the new flag.
|
|
812
|
+
Code work that only mentions the README — no prose is produced.
|
|
813
|
+
</example>
|
|
814
|
+
<example fire="false">
|
|
815
|
+
Rewrite the README, but read the doc-writing skill and its references first.
|
|
816
|
+
Already directs the subagent to the skill — nothing to nudge.
|
|
817
|
+
</example>
|
|
818
|
+
</examples>""",
|
|
819
|
+
message=lambda r: (
|
|
820
|
+
"This prompt delegates prose but paraphrases the writing rules instead of pointing at them. "
|
|
821
|
+
f"{r.reasoning} A paraphrase drifts and silently overrides the skill — rewrite the prompt to "
|
|
822
|
+
"direct the agent to READ the writing-docs skill and its references (the installed plugin under "
|
|
823
|
+
"~/.claude/plugins/cache/skills/writing-docs, or plugins/writing-docs in the cc-skills repo) "
|
|
824
|
+
"before it writes."
|
|
825
|
+
),
|
|
826
|
+
contexts=[ProseSpawn()],
|
|
827
|
+
events=Event.PreToolUse,
|
|
828
|
+
only_if=[Tool("Agent|Task")],
|
|
829
|
+
skip_if=[
|
|
830
|
+
ToolInput("prompt", r"(?i)writing-docs"),
|
|
831
|
+
Agent("Explore|claude-code-guide"),
|
|
832
|
+
],
|
|
833
|
+
max_context=16_000,
|
|
834
|
+
agent=False,
|
|
835
|
+
transcript=False,
|
|
836
|
+
tests={
|
|
837
|
+
Input(
|
|
838
|
+
prompt="Rewrite the README of /repo. You are fable; technical-builder voice, no hype "
|
|
839
|
+
"adjectives. Verify commands against the binary."
|
|
840
|
+
): Warn(pattern="writing-docs"),
|
|
841
|
+
Input(
|
|
842
|
+
prompt="Rewrite the README of /repo; technical-builder voice, no hype adjectives. Read "
|
|
843
|
+
"the writing-docs skill at ~/.claude/plugins/cache/skills/writing-docs first."
|
|
844
|
+
): Allow(),
|
|
845
|
+
Input(prompt="Fix the race in daemon.go; update the failing test"): Allow(),
|
|
846
|
+
Input(
|
|
847
|
+
prompt="Rewrite the README, but read the doc-writing skill and its references first",
|
|
848
|
+
llm={"fire": False},
|
|
849
|
+
): Allow(),
|
|
850
|
+
},
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
llm_nudge(
|
|
854
|
+
"""Decide whether this workflow script delegates documentation or prose writing to an
|
|
855
|
+
agent() stage without directing that subagent to read the writing-docs skill.
|
|
856
|
+
|
|
857
|
+
<workflow_script> holds the pending Workflow call's script source, headed by every line
|
|
858
|
+
that pins a model, followed by the sentences a clause prefilter matched — each asks a
|
|
859
|
+
writing verb of a prose artifact, with negated asks ("do NOT edit CHANGELOG.md") already
|
|
860
|
+
screened out.
|
|
861
|
+
|
|
862
|
+
Decide whether an agent() prompt delegates documentation or prose writing — a README,
|
|
863
|
+
docs page, CHANGELOG, tutorial, release notes, or similar deliverable — without
|
|
864
|
+
directing that subagent to read the writing-docs skill. Restated style rules
|
|
865
|
+
('technical-builder voice', 'no hype adjectives', 'first person, confident') do not
|
|
866
|
+
count as reading the skill; that paraphrase is exactly the failure to catch. Fire only
|
|
867
|
+
when a stage's deliverable is prose the writing-docs skill governs. Do not fire for code
|
|
868
|
+
work that incidentally mentions a doc file, for reading or reviewing docs without writing
|
|
869
|
+
them, or for a stage that already tells its subagent to read the skill or its references.
|
|
870
|
+
|
|
871
|
+
When uncertain, fire=false — a false alarm teaches the agent to ignore this nudge. Keep
|
|
872
|
+
reasoning under 40 words and name the offending stage.
|
|
873
|
+
|
|
874
|
+
<examples>
|
|
875
|
+
<example fire="true">
|
|
876
|
+
agent('Rewrite the README for the new CLI. Technical-builder voice, no hype adjectives', {model: 'opus'})
|
|
877
|
+
The stage's deliverable is README prose with the style rules paraphrased in place of the skill.
|
|
878
|
+
</example>
|
|
879
|
+
<example fire="true">
|
|
880
|
+
agent(`Draft the docs-site page for ${feature}. First-person, confident, no marketing fluff`)
|
|
881
|
+
A docs page delegated with restated voice rules and no pointer to the writing-docs skill.
|
|
882
|
+
</example>
|
|
883
|
+
<example fire="false">
|
|
884
|
+
agent('Fix the failing import in cli.py; the README already documents the flag', {model: 'opus'})
|
|
885
|
+
Code work that only mentions the README — no prose is produced.
|
|
886
|
+
</example>
|
|
887
|
+
<example fire="false">
|
|
888
|
+
agent('Rewrite the troubleshooting guide, but read the doc-writing skill and its references first')
|
|
889
|
+
The stage already directs its subagent to the skill — nothing to nudge.
|
|
890
|
+
</example>
|
|
891
|
+
</examples>""",
|
|
892
|
+
message=lambda r: (
|
|
893
|
+
"This workflow script delegates prose but paraphrases the writing rules instead of pointing at "
|
|
894
|
+
f"them. {r.reasoning} A paraphrase drifts and silently overrides the skill — rewrite the offending "
|
|
895
|
+
"agent() prompt to direct its subagent to READ the writing-docs skill and its references (the "
|
|
896
|
+
"installed plugin under ~/.claude/plugins/cache/skills/writing-docs, or plugins/writing-docs in "
|
|
897
|
+
"the cc-skills repo) before it writes."
|
|
898
|
+
),
|
|
899
|
+
contexts=[ProseWorkflowScript()],
|
|
900
|
+
events=Event.PreToolUse,
|
|
901
|
+
only_if=[Tool("Workflow")],
|
|
902
|
+
skip_if=[WorkflowScript(pattern=r"(?i)writing-docs")],
|
|
903
|
+
max_fires=2,
|
|
904
|
+
max_context=16_000,
|
|
905
|
+
agent=False,
|
|
906
|
+
transcript=False,
|
|
907
|
+
tests={
|
|
908
|
+
Input(
|
|
909
|
+
script="agent('Rewrite the README. You are fable; technical-builder voice, no hype "
|
|
910
|
+
"adjectives', {model: 'opus'})"
|
|
911
|
+
): Warn(pattern="writing-docs"),
|
|
912
|
+
Input(
|
|
913
|
+
script="agent('Rewrite the README per the writing-docs skill at "
|
|
914
|
+
"~/.claude/plugins/cache/skills/writing-docs', {model: 'opus'})"
|
|
915
|
+
): Allow(),
|
|
916
|
+
Input(script="agent('Fix the race in daemon.go; update the failing test')"): Allow(),
|
|
917
|
+
Input(
|
|
918
|
+
script="agent('Rewrite the README, but read the doc-writing skill and its references "
|
|
919
|
+
"first', {model: 'opus'})",
|
|
920
|
+
llm={"fire": False},
|
|
921
|
+
): Allow(),
|
|
733
922
|
},
|
|
734
923
|
)
|
|
@@ -24,6 +24,7 @@ nudge(
|
|
|
24
24
|
skip_if=[
|
|
25
25
|
UsedSkill("llm-prompts"),
|
|
26
26
|
UsedSkill("slop-cop-check", "slop-cop-prose"),
|
|
27
|
+
Content(r"\.count_tokens\(", project_only=False),
|
|
27
28
|
],
|
|
28
29
|
tests={
|
|
29
30
|
Input(
|
|
@@ -31,5 +32,14 @@ nudge(
|
|
|
31
32
|
): Warn(),
|
|
32
33
|
Input(file="prompt.md", content="<instruction>\nSummarize the document.\n</instruction>\n"): Warn(),
|
|
33
34
|
Input(file="util.py", content="def add(a, b):\n return a + b\n"): Allow(),
|
|
35
|
+
Input(
|
|
36
|
+
file="tokens.py",
|
|
37
|
+
content=(
|
|
38
|
+
"def api_count(text: str, model: str) -> int:\n"
|
|
39
|
+
" resp = client.messages.count_tokens(\n"
|
|
40
|
+
' model=model,\n messages=[{"role": "user", "content": text or " "}],\n'
|
|
41
|
+
" )\n return int(resp.input_tokens)\n"
|
|
42
|
+
),
|
|
43
|
+
): Allow(),
|
|
34
44
|
},
|
|
35
45
|
)
|
|
@@ -55,6 +55,13 @@ class DriftedFromTasks(CustomCondition):
|
|
|
55
55
|
return since.tool_calls.named("Bash|Grep|Glob|WebSearch|WebFetch|LSP|Skill").count() >= TASK_DRIFT_THRESHOLD
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
class TaskNotification(CustomCondition):
|
|
59
|
+
"""Matches when the prompt is a system-injected background-task report, not free-form user text."""
|
|
60
|
+
|
|
61
|
+
def check(self, evt: BaseHookEvent) -> bool:
|
|
62
|
+
return (evt.user_prompt or "").strip().startswith("<task-notification>")
|
|
63
|
+
|
|
64
|
+
|
|
58
65
|
gate(
|
|
59
66
|
"Open tasks remain. Before stopping, mark each finished task status='completed' via the "
|
|
60
67
|
"TaskUpdate tool (add a note if you're deliberately deferring one), or output "
|
|
@@ -120,7 +127,7 @@ nudge(
|
|
|
120
127
|
nudge(
|
|
121
128
|
"This message has several distinct requests. Use the TaskCreate tool for each item "
|
|
122
129
|
"before starting work, so none gets dropped. See: CLAUDE.md § Task Tracking.",
|
|
123
|
-
skip_if=[InPlanMode()],
|
|
130
|
+
skip_if=[InPlanMode(), TaskNotification()],
|
|
124
131
|
events=Event.UserPromptSubmit,
|
|
125
132
|
signals=Signals(
|
|
126
133
|
[
|
|
@@ -141,5 +148,12 @@ nudge(
|
|
|
141
148
|
Input(prompt="1. add foo\n2. fix bar\n3. update baz"): Warn(),
|
|
142
149
|
Input(prompt="just fix the typo"): Allow(),
|
|
143
150
|
Input(prompt="1. add foo\n2. fix bar\n3. update baz", permission_mode="plan"): Allow(),
|
|
151
|
+
Input(
|
|
152
|
+
prompt=(
|
|
153
|
+
"<task-notification>\n<task-id>abc</task-id>\n<status>completed</status>\n"
|
|
154
|
+
"<summary>Agent finished</summary>\n<result>\n- `cc-transcript` -> version 7.0.1\n"
|
|
155
|
+
"- `spawnllm` -> version 0.5.2\n</result>\n</task-notification>"
|
|
156
|
+
)
|
|
157
|
+
): Allow(),
|
|
144
158
|
},
|
|
145
159
|
)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
name = "steering"
|
|
2
|
+
version = "0.4.0"
|
|
3
|
+
description = "Steer the agent's judgment on what's worth fixing: prefer fundamental, first-principles fixes over band-aids, don't dismiss a real pre-existing issue, don't chase trivial type noise, and don't defer the requested fix by swapping in docs or a follow-up nobody asked for."
|
|
4
|
+
hooks = "."
|
|
5
|
+
nlp = true
|