aiwcli 0.9.1 → 0.9.2
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.
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/context_enforcer.py +65 -15
- package/dist/templates/_shared/hooks/session_start.py +108 -0
- package/dist/templates/_shared/hooks/task_create_atomicity.py +199 -0
- package/dist/templates/_shared/hooks/task_create_capture.py +2 -2
- package/dist/templates/_shared/hooks/task_update_capture.py +2 -2
- package/dist/templates/_shared/hooks/user_prompt_submit.py +58 -13
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/inference.py +20 -35
- package/dist/templates/_shared/lib/base/stop_words.py +158 -0
- package/dist/templates/_shared/lib/base/utils.py +3 -2
- package/dist/templates/_shared/lib/context/__init__.py +0 -2
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_manager.py +2 -2
- package/dist/templates/_shared/lib/context/task_sync.py +5 -82
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/persona_questions.py +113 -0
- package/dist/templates/_shared/lib/templates/plan_context.py +13 -27
- package/dist/templates/cc-native/.claude/agents/cc-native/ACCESSIBILITY-TESTER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/CODE-REVIEWER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/CONTEXT-EXTRACTOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PENETRATION-TESTER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PERFORMANCE-ENGINEER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PLAN-ORCHESTRATOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/TRADE-OFF-ILLUMINATOR.md +1 -1
- package/dist/templates/cc-native/.claude/settings.json +21 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +211 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +48 -9
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +240 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +1 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +1 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +6 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/cc-native/_cc-native/docs/PERMISSION_REQUEST_VERIFICATION.md +0 -147
|
@@ -3,7 +3,7 @@ name: context-extractor
|
|
|
3
3
|
description: Extracts abstracted problem context from conversation for fresh perspective analysis. Strips implementation details, preserves problem essence.
|
|
4
4
|
model: haiku
|
|
5
5
|
focus: context abstraction
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: devils-advocate
|
|
|
3
3
|
description: Takes the contrarian position and pushes logic to uncomfortable extremes. If a plan can't survive its antithesis, it's not robust. This agent asks "what if the exact opposite is true?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: contrarian analysis and reductio ad absurdum
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: feasibility-analyst
|
|
|
3
3
|
description: Evaluates whether plans are achievable given available resources, time, expertise, and technical constraints. Identifies gaps between what's planned and what's realistically possible.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: resource constraints and technical viability
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: fresh-perspective
|
|
|
3
3
|
description: Provides unbiased problem-solving perspective without code context. Analyzes from first principles to combat code-anchored thinking.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: first-principles problem analysis
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: handoff-readiness
|
|
|
3
3
|
description: Tests whether plans contain sufficient context for execution by a fresh context window with zero prior knowledge. Simulates receiving the plan cold and identifies every point where clarification would be needed—because that question can never be answered. Detects undefined references, missing big-picture goals, implicit assumptions, and context-dependent gaps.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: fresh context execution readiness
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: hidden-complexity-detector
|
|
|
3
3
|
description: Surfaces understated difficulty and implementation nightmares hiding behind simple-sounding requirements. Simple plans hide complex reality. This agent asks "what makes this harder than it sounds?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: understated complexity and hidden difficulty
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: incentive-mapper
|
|
|
3
3
|
description: Examines who wins, who loses, and whether incentives align with desired outcomes. Plans fail when people's motivations don't match goals. This agent asks "who benefits from this being true?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: incentive alignment and motivation structures
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: penetration-tester
|
|
|
3
3
|
description: Expert penetration tester specializing in ethical hacking, vulnerability assessment, and security testing. Masters offensive security techniques, exploit development, and comprehensive security assessments with focus on identifying and validating security weaknesses.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: security vulnerabilities and attack vectors
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: performance-engineer
|
|
|
3
3
|
description: Expert performance engineer specializing in system optimization, bottleneck identification, and scalability engineering. Masters performance testing, profiling, and tuning across applications, databases, and infrastructure with focus on achieving optimal response times and resource efficiency.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: performance bottlenecks and optimization
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: plan-orchestrator
|
|
|
3
3
|
description: Intelligent plan analyzer that determines complexity and routes to appropriate reviewers. Uses fast inference to minimize latency while maximizing review accuracy through targeted agent selection.
|
|
4
4
|
model: haiku
|
|
5
5
|
focus: plan complexity analysis and agent routing
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- orchestration
|
|
9
9
|
tools: Read, Glob, Grep
|
|
@@ -3,7 +3,7 @@ name: precedent-finder
|
|
|
3
3
|
description: Pattern-matches to historical precedents and their results. History predicts plan outcomes. This agent asks "when has this been tried before, and what happened?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: historical patterns and precedent analysis
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: reversibility-analyst
|
|
|
3
3
|
description: Identifies one-way doors, lock-in, and path dependencies that foreclose future options. Some decisions close doors permanently. This agent asks "can you undo this if you're wrong?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: one-way doors and irreversible decisions
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: risk-assessor
|
|
|
3
3
|
description: Identifies potential failure modes, external dependencies, reversibility concerns, and mitigation strategies. Focuses on what could go wrong and how to prepare for it.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: failure modes and mitigation strategies
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: second-order-analyst
|
|
|
3
3
|
description: Traces consequences 2-3 steps beyond immediate effects. Plans that look safe in isolation often trigger cascading failures. This agent maps the domino chain and asks "what breaks downstream?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: cascading effects and downstream consequences
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: simplicity-guardian
|
|
|
3
3
|
description: Detects over-engineering, unnecessary complexity, scope creep, premature abstraction, and YAGNI violations. Advocates for the simplest solution that meets requirements.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: complexity reduction and scope control
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: skeptic
|
|
|
3
3
|
description: Adversarial reviewer specializing in problem-solution alignment and assumption validation. Questions whether the plan solves the right problem, challenges hidden assumptions, and identifies over-engineering. Uses Socratic questioning to surface fundamental flaws.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: problem-solution alignment and assumption validation
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -3,7 +3,7 @@ name: stakeholder-advocate
|
|
|
3
3
|
description: Ensures plans actually serve user and business needs, not just technical elegance. Evaluates who benefits, who bears costs, and whether the plan aligns with stakeholder priorities.
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: user value and business alignment
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- design
|
|
@@ -3,7 +3,7 @@ name: trade-off-illuminator
|
|
|
3
3
|
description: Forces explicit acknowledgment of what's being sacrificed. Every decision has a price. Plans hide their costs. This agent drags hidden trade-offs into the light and asks "what are you giving up?"
|
|
4
4
|
model: sonnet
|
|
5
5
|
focus: hidden costs and sacrificed alternatives
|
|
6
|
-
enabled:
|
|
6
|
+
enabled: false
|
|
7
7
|
categories:
|
|
8
8
|
- code
|
|
9
9
|
- infrastructure
|
|
@@ -18,6 +18,17 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"hooks": {
|
|
21
|
+
"SessionStart": [
|
|
22
|
+
{
|
|
23
|
+
"hooks": [
|
|
24
|
+
{
|
|
25
|
+
"type": "command",
|
|
26
|
+
"command": "python .aiwcli/_shared/hooks/session_start.py",
|
|
27
|
+
"timeout": 5000
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
],
|
|
21
32
|
"UserPromptSubmit": [
|
|
22
33
|
{
|
|
23
34
|
"hooks": [
|
|
@@ -94,6 +105,16 @@
|
|
|
94
105
|
}
|
|
95
106
|
],
|
|
96
107
|
"PreToolUse": [
|
|
108
|
+
{
|
|
109
|
+
"matcher": "TaskCreate",
|
|
110
|
+
"hooks": [
|
|
111
|
+
{
|
|
112
|
+
"type": "command",
|
|
113
|
+
"command": "python .aiwcli/_shared/hooks/task_create_atomicity.py",
|
|
114
|
+
"timeout": 15000
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
},
|
|
97
118
|
{
|
|
98
119
|
"matcher": "Write",
|
|
99
120
|
"hooks": [
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# CC-Native Hooks Development Guide
|
|
2
|
+
|
|
3
|
+
> **Keep this document updated.** When you solve an issue related to hooks, add the solution to the relevant section and log it in the Changelog. This document should grow with discovered patterns and fixes—don't wait to be asked.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Quick Reference
|
|
8
|
+
|
|
9
|
+
| Hook | Trigger | Purpose |
|
|
10
|
+
|------|---------|---------|
|
|
11
|
+
| `cc-native-plan-review.py` | PreToolUse: ExitPlanMode | Review plans before user approval |
|
|
12
|
+
| `add_plan_context.py` | PostToolUse: EnterPlanMode | Add context when entering plan mode |
|
|
13
|
+
| `suggest-fresh-perspective.py` | PostToolUse | Suggest fresh perspective workflow |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Import Pattern
|
|
18
|
+
|
|
19
|
+
Hooks run from arbitrary working directories. Always set up sys.path explicitly.
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
# CORRECT - works in hook context
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
_lib = Path(__file__).parent.parent / "lib"
|
|
27
|
+
sys.path.insert(0, str(_lib))
|
|
28
|
+
|
|
29
|
+
# For shared library access
|
|
30
|
+
_shared = Path(__file__).parent.parent.parent / "_shared"
|
|
31
|
+
sys.path.insert(0, str(_shared))
|
|
32
|
+
|
|
33
|
+
from utils import eprint, ReviewerResult
|
|
34
|
+
from lib.base.subprocess_utils import is_internal_call
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# WRONG - relative imports fail in hook context
|
|
39
|
+
from ..lib import utils # ModuleNotFoundError
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Internal Call Detection
|
|
45
|
+
|
|
46
|
+
Hooks can be invoked recursively when spawning subprocesses (agents, orchestrator). Always check and skip:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
def main() -> int:
|
|
50
|
+
# FIRST LINE of main - before any other logic
|
|
51
|
+
if is_internal_call():
|
|
52
|
+
return 0 # Skip for subprocess calls
|
|
53
|
+
|
|
54
|
+
# Rest of hook logic...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Without this check, the hook runs multiple times per plan review, causing duplicate reviews and state corruption.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Hook Output Format
|
|
62
|
+
|
|
63
|
+
Claude Code hooks return JSON to stdout. The format is specific to each hook type.
|
|
64
|
+
|
|
65
|
+
### PreToolUse Output
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# CORRECT - current API format
|
|
69
|
+
import json
|
|
70
|
+
|
|
71
|
+
out = {
|
|
72
|
+
"hookSpecificOutput": {
|
|
73
|
+
"hookEventName": "PreToolUse",
|
|
74
|
+
"additionalContext": "Information for Claude to see...",
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# To block the tool call:
|
|
79
|
+
out["hookSpecificOutput"]["permissionDecision"] = "deny"
|
|
80
|
+
out["hookSpecificOutput"]["permissionDecisionReason"] = "Reason shown to Claude"
|
|
81
|
+
|
|
82
|
+
print(json.dumps(out, ensure_ascii=False))
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# WRONG - old format, silently ignored
|
|
87
|
+
{"decision": "block", "reason": "..."} # Does nothing
|
|
88
|
+
{"continue": False, "message": "..."} # Does nothing
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Key insight:** The old `decision`/`reason` format fails silently. If your hook isn't affecting Claude's behavior, check the output format first.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Debugging Output
|
|
96
|
+
|
|
97
|
+
Hooks communicate via stdout (JSON) and stderr (logs). Use them correctly:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# CORRECT - logs go to stderr, visible in terminal
|
|
101
|
+
def eprint(*args):
|
|
102
|
+
print(*args, file=sys.stderr)
|
|
103
|
+
|
|
104
|
+
eprint("[hook-name] Starting hook...")
|
|
105
|
+
eprint(f"[hook-name] Found {len(items)} items")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# WRONG - print() goes to stdout, corrupts JSON output
|
|
110
|
+
print("Debug info") # Breaks JSON parsing
|
|
111
|
+
print(json.dumps(output)) # Now invalid because of previous print
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Context System Integration
|
|
117
|
+
|
|
118
|
+
Plan review hooks integrate with the shared context system for state management:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from lib.context.context_manager import (
|
|
122
|
+
get_context_by_session_id,
|
|
123
|
+
get_all_in_flight_contexts,
|
|
124
|
+
)
|
|
125
|
+
from lib.base.constants import get_context_reviews_dir
|
|
126
|
+
|
|
127
|
+
# Find active context
|
|
128
|
+
context = get_context_by_session_id(session_id, project_root)
|
|
129
|
+
if not context:
|
|
130
|
+
# Fallback: find single planning context
|
|
131
|
+
in_flight = get_all_in_flight_contexts(project_root)
|
|
132
|
+
planning = [c for c in in_flight if c.in_flight and c.in_flight.mode == "planning"]
|
|
133
|
+
if len(planning) == 1:
|
|
134
|
+
context = planning[0]
|
|
135
|
+
|
|
136
|
+
# Get reviews directory for this context
|
|
137
|
+
reviews_dir = get_context_reviews_dir(context.id, project_root)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
If context isn't found, add diagnostic logging:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
eprint(f"[hook] Session ID: {session_id}")
|
|
144
|
+
eprint(f"[hook] In-flight contexts: {len(in_flight)}")
|
|
145
|
+
eprint(f"[hook] Modes: {[c.in_flight.mode for c in in_flight]}")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Error Handling
|
|
151
|
+
|
|
152
|
+
Hooks should fail gracefully - a broken hook shouldn't break the user's workflow:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
def main() -> int:
|
|
156
|
+
try:
|
|
157
|
+
# Hook logic...
|
|
158
|
+
return 0
|
|
159
|
+
except Exception as e:
|
|
160
|
+
eprint(f"[hook-name] Error: {e}")
|
|
161
|
+
# Return 0 to not block the user
|
|
162
|
+
return 0
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
try:
|
|
166
|
+
raise SystemExit(main())
|
|
167
|
+
except Exception as e:
|
|
168
|
+
import traceback
|
|
169
|
+
eprint(f"[hook-name] FATAL: {e}")
|
|
170
|
+
traceback.print_exc(file=sys.stderr)
|
|
171
|
+
# Still exit 0 to not block - or exit 1 if blocking is intentional
|
|
172
|
+
raise SystemExit(0)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Use `sys.exit(1)` only for intentional blocking (e.g., `blockOnFail: true` configured).
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## DO NOT
|
|
180
|
+
|
|
181
|
+
These are reminders based on past issues. Not enforcement rules.
|
|
182
|
+
|
|
183
|
+
- **Don't modify hook output format** without verifying the current Claude Code hook API (it changes between versions)
|
|
184
|
+
- **Don't use `sys.exit(1)`** for non-fatal errors - it blocks the user's workflow
|
|
185
|
+
- **Don't forget template sync** after modifying hooks in `.aiwcli/` - changes should also go to `packages/cli/src/templates/cc-native/`
|
|
186
|
+
- **Don't use `print()`** for anything except the final JSON output
|
|
187
|
+
- **Don't assume session_id format** - it can be UUID, path-like, or other formats
|
|
188
|
+
- **Don't skip `is_internal_call()` check** - recursive hook execution causes state corruption
|
|
189
|
+
- **Don't hardcode paths** - use `Path(__file__)` and environment variables
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Verification After Changes
|
|
194
|
+
|
|
195
|
+
Always validate Python syntax after editing hooks:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
python -m py_compile packages/cli/src/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Hooks fail silently on syntax errors - this catches them before they reach production.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Changelog
|
|
206
|
+
|
|
207
|
+
<!-- Add dated entries as new issues are discovered -->
|
|
208
|
+
|
|
209
|
+
| Date | Change |
|
|
210
|
+
|------|--------|
|
|
211
|
+
| 2026-02-03 | Initial creation |
|
package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc
CHANGED
|
Binary file
|
|
@@ -29,6 +29,7 @@ Output: _output/cc-native/plans/{YYYY-MM-DD}/{slug}/reviews/
|
|
|
29
29
|
|
|
30
30
|
import json
|
|
31
31
|
import os
|
|
32
|
+
import random
|
|
32
33
|
import sys
|
|
33
34
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
34
35
|
from datetime import datetime
|
|
@@ -522,7 +523,9 @@ def main() -> int:
|
|
|
522
523
|
gemini_enabled = plan_review_enabled and reviewers_config.get("gemini", {}).get("enabled", False)
|
|
523
524
|
|
|
524
525
|
agent_library = load_agent_library(base, agent_settings) if agent_review_enabled else []
|
|
525
|
-
|
|
526
|
+
# Load all agents regardless of enabled status - enabled:false only prevents
|
|
527
|
+
# Claude Code auto-suggestion, not plan-review usage
|
|
528
|
+
enabled_agents = agent_library
|
|
526
529
|
timeout = agent_settings.get("timeout", 120)
|
|
527
530
|
legacy_mode = agent_settings.get("legacyMode", False)
|
|
528
531
|
|
|
@@ -585,19 +588,42 @@ def main() -> int:
|
|
|
585
588
|
|
|
586
589
|
selected_agents: List[AgentConfig] = []
|
|
587
590
|
|
|
591
|
+
# Load mandatory and fallback config
|
|
592
|
+
mandatory_names = set(agent_settings.get("mandatoryAgents", [
|
|
593
|
+
"handoff-readiness", "clarity-auditor", "skeptic"
|
|
594
|
+
]))
|
|
595
|
+
fallback_by_complexity = agent_settings.get("fallbackByComplexity", {
|
|
596
|
+
"simple": 0, "medium": 5, "high": 9
|
|
597
|
+
})
|
|
598
|
+
|
|
588
599
|
if enabled_agents:
|
|
600
|
+
# Split into mandatory and non-mandatory pools
|
|
601
|
+
mandatory_agents = [a for a in enabled_agents if a.name in mandatory_names]
|
|
602
|
+
non_mandatory = [a for a in enabled_agents if a.name not in mandatory_names]
|
|
603
|
+
|
|
604
|
+
eprint(f"[cc-native-plan-review] Mandatory agents: {[a.name for a in mandatory_agents]}")
|
|
605
|
+
eprint(f"[cc-native-plan-review] Non-mandatory pool: {len(non_mandatory)} agents")
|
|
606
|
+
|
|
589
607
|
if orch_result and not legacy_mode:
|
|
590
|
-
# Use orchestrator result from phase 1
|
|
591
608
|
detected_complexity = orch_result.complexity
|
|
592
609
|
|
|
593
|
-
|
|
594
|
-
|
|
610
|
+
# Get orchestrator's additional selections (excluding mandatory since they always run)
|
|
611
|
+
orch_selected_names = set(orch_result.selected_agents) - mandatory_names
|
|
612
|
+
orch_selected = [a for a in non_mandatory if a.name in orch_selected_names]
|
|
595
613
|
|
|
596
|
-
|
|
597
|
-
eprint(f"[cc-native-plan-review] Warning: orchestrator selected unknown agents: {selected_names}")
|
|
598
|
-
selected_agents = [a for a in enabled_agents if orch_result.category in a.categories]
|
|
614
|
+
eprint(f"[cc-native-plan-review] Orchestrator selected (non-mandatory): {[a.name for a in orch_selected]}")
|
|
599
615
|
|
|
600
|
-
|
|
616
|
+
# Random fallback if orchestrator selected zero additional agents
|
|
617
|
+
if not orch_selected and non_mandatory:
|
|
618
|
+
fallback_count = fallback_by_complexity.get(detected_complexity, 5)
|
|
619
|
+
fallback_count = min(fallback_count, len(non_mandatory))
|
|
620
|
+
if fallback_count > 0:
|
|
621
|
+
orch_selected = random.sample(non_mandatory, fallback_count)
|
|
622
|
+
eprint(f"[cc-native-plan-review] Random fallback ({detected_complexity}): {[a.name for a in orch_selected]}")
|
|
623
|
+
|
|
624
|
+
# Combine: mandatory + orchestrator/fallback selection
|
|
625
|
+
selected_agents = mandatory_agents + orch_selected
|
|
626
|
+
eprint(f"[cc-native-plan-review] Final selection: {len(selected_agents)} agents ({len(mandatory_agents)} mandatory + {len(orch_selected)} additional)")
|
|
601
627
|
else:
|
|
602
628
|
eprint("[cc-native-plan-review] Running in legacy mode (all enabled agents)")
|
|
603
629
|
selected_agents = enabled_agents
|
|
@@ -764,4 +790,17 @@ def main() -> int:
|
|
|
764
790
|
|
|
765
791
|
|
|
766
792
|
if __name__ == "__main__":
|
|
767
|
-
|
|
793
|
+
try:
|
|
794
|
+
raise SystemExit(main())
|
|
795
|
+
except Exception as e:
|
|
796
|
+
import traceback
|
|
797
|
+
print(f"[cc-native-plan-review] FATAL ERROR: {e}", file=sys.stderr)
|
|
798
|
+
traceback.print_exc(file=sys.stderr)
|
|
799
|
+
# Output error to Claude via hook format so it's visible
|
|
800
|
+
print(json.dumps({
|
|
801
|
+
"hookSpecificOutput": {
|
|
802
|
+
"hookEventName": "PreToolUse",
|
|
803
|
+
"additionalContext": f"**CC-Native Plan Review Hook Error**\n\nThe hook encountered an error:\n```\n{traceback.format_exc()}\n```\n\nPlease report this issue.",
|
|
804
|
+
}
|
|
805
|
+
}))
|
|
806
|
+
raise SystemExit(1)
|