aiwcli 0.10.3 → 0.11.0
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/bin/run.js +1 -1
- package/dist/commands/clear.js +28 -131
- package/dist/commands/init/index.js +3 -3
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
- package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
- package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
- package/dist/templates/_shared/lib-ts/base/logger.ts +31 -15
- package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +139 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
- package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +61 -37
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
- package/dist/templates/_shared/lib-ts/types.ts +68 -55
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +21 -21
- package/dist/templates/_shared/scripts/status_line.ts +733 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -9
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.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__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -177
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.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__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
- package/dist/templates/_shared/lib/base/inference.py +0 -307
- package/dist/templates/_shared/lib/base/logger.py +0 -305
- package/dist/templates/_shared/lib/base/stop_words.py +0 -221
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -263
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.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__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_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/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- 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/formatters.py +0 -146
- package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -716
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -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/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -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/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -8,37 +8,31 @@
|
|
|
8
8
|
|
|
9
9
|
| Hook | Trigger | Purpose |
|
|
10
10
|
|------|---------|---------|
|
|
11
|
-
| `cc-native-plan-review.
|
|
12
|
-
| `add_plan_context.
|
|
13
|
-
| `
|
|
11
|
+
| `cc-native-plan-review.ts` | PreToolUse: ExitPlanMode | Review plans before user approval |
|
|
12
|
+
| `add_plan_context.ts` | PostToolUse: AskUserQuestion, PreToolUse: Task | Mark questions asked; nudge Plan subagent to ask questions first |
|
|
13
|
+
| `plan_questions_early.ts` | UserPromptSubmit | Inject Phase A clarification prompt in plan mode |
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
## Import Pattern
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
CC-native hooks are TypeScript, run via `bun`. Use relative imports from the hook file location.
|
|
20
20
|
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import
|
|
21
|
+
```typescript
|
|
22
|
+
// Shared library imports (via _shared/lib-ts/)
|
|
23
|
+
import { loadHookInput, runHook, logInfo, emitContext } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
24
|
+
import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
|
|
25
|
+
import { getProjectRoot } from "../../_shared/lib-ts/base/constants.js";
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
from debug import log_debug # Context-folder debug logging
|
|
27
|
+
// CC-native library imports (via ../lib-ts/)
|
|
28
|
+
import { wasQuestionsAsked, markQuestionsAsked } from "../lib-ts/cc-native-state.js";
|
|
29
|
+
import { loadConfig } from "../lib-ts/config.js";
|
|
30
|
+
import type { AgentConfig } from "../lib-ts/types.js";
|
|
36
31
|
```
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```
|
|
33
|
+
**Important:** Always use `.js` extensions in import paths — Bun resolves `.ts` files from `.js` imports.
|
|
34
|
+
|
|
35
|
+
**Import direction:** Hooks → `_cc-native/lib-ts/` → `_shared/lib-ts/`. Never reverse.
|
|
42
36
|
|
|
43
37
|
---
|
|
44
38
|
|
|
@@ -46,13 +40,15 @@ from ..lib import utils # ModuleNotFoundError
|
|
|
46
40
|
|
|
47
41
|
Hooks can be invoked recursively when spawning subprocesses (agents, orchestrator). Always check and skip:
|
|
48
42
|
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
# FIRST LINE of main - before any other logic
|
|
52
|
-
if is_internal_call():
|
|
53
|
-
return 0 # Skip for subprocess calls
|
|
43
|
+
```typescript
|
|
44
|
+
import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
|
|
54
45
|
|
|
55
|
-
|
|
46
|
+
function main(): void {
|
|
47
|
+
// FIRST LINE of main - before any other logic
|
|
48
|
+
if (isInternalCall()) return;
|
|
49
|
+
|
|
50
|
+
// Rest of hook logic...
|
|
51
|
+
}
|
|
56
52
|
```
|
|
57
53
|
|
|
58
54
|
Without this check, the hook runs multiple times per plan review, causing duplicate reviews and state corruption.
|
|
@@ -65,138 +61,115 @@ Claude Code hooks return JSON to stdout. The format is specific to each hook typ
|
|
|
65
61
|
|
|
66
62
|
### PreToolUse Output
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
# CORRECT - current API format
|
|
70
|
-
import json
|
|
64
|
+
Use the shared hook utilities — never construct JSON manually:
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"additionalContext": "Information for Claude to see...",
|
|
75
|
-
}
|
|
76
|
-
}
|
|
66
|
+
```typescript
|
|
67
|
+
import { emitContext, emitContextAndBlock } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
out["hookSpecificOutput"]["permissionDecisionReason"] = "Reason shown to Claude"
|
|
69
|
+
// Inject context without blocking:
|
|
70
|
+
emitContext("Information for Claude to see...");
|
|
81
71
|
|
|
82
|
-
|
|
72
|
+
// Block the tool call with context and reason:
|
|
73
|
+
emitContextAndBlock(
|
|
74
|
+
"Review feedback for Claude to see...",
|
|
75
|
+
"Reason shown to Claude for the denial",
|
|
76
|
+
);
|
|
83
77
|
```
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
# WRONG - old format, silently ignored
|
|
87
|
-
{"decision": "block", "reason": "..."} # Does nothing
|
|
88
|
-
{"continue": False, "message": "..."} # Does nothing
|
|
89
|
-
```
|
|
79
|
+
**Key insight:** The old `decision`/`reason` format fails silently. If your hook isn't affecting Claude's behavior, check the output format first. Only `hookSpecificOutput` with `additionalContext`, `permissionDecision`, and `permissionDecisionReason` fields are recognized.
|
|
90
80
|
|
|
91
|
-
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Debugging Output
|
|
92
84
|
|
|
93
|
-
|
|
85
|
+
For logging tiers, visibility rules, and stderr behavior, see **`_shared/lib-ts/CLAUDE.md`** (the shared library guide). The key rules:
|
|
94
86
|
|
|
95
|
-
|
|
87
|
+
- **stderr is opt-in.** `log_debug/log_info/log_warn/log_error` write to file only (no UI noise)
|
|
88
|
+
- **`logBlocking()` / `log_hook_error()`** for problems that must be visible
|
|
89
|
+
- **`eprint()`** for terminal-only UX (not logged to JSONL)
|
|
90
|
+
- **`print()` corrupts stdout** — never use for diagnostics
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
from base.hook_utils import emit_context, emit_context_and_block
|
|
92
|
+
TypeScript hooks use re-exported logger functions from `hook-utils.ts`:
|
|
99
93
|
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
```typescript
|
|
95
|
+
import { logDebug, logInfo, logWarn, logError } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
102
96
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"Reason shown to Claude for the denial"
|
|
107
|
-
)
|
|
97
|
+
logDebug("hook-name", `Found ${items.length} items`); // file only
|
|
98
|
+
logInfo("hook-name", "Starting hook..."); // file only
|
|
99
|
+
logError("hook-name", `Failed: ${e}`); // file only
|
|
108
100
|
```
|
|
109
101
|
|
|
110
|
-
These handle the JSON serialization and stdout printing. `emit_context` defaults to `ensure_ascii=False`; `emit_context_and_block` defaults to `ensure_ascii=True` (safe for Windows cp1252).
|
|
111
|
-
|
|
112
102
|
---
|
|
113
103
|
|
|
114
|
-
##
|
|
115
|
-
|
|
116
|
-
Hooks communicate via stdout (JSON) and stderr (logs). Use the unified logger for all diagnostic output:
|
|
104
|
+
## Context System Integration
|
|
117
105
|
|
|
118
|
-
|
|
119
|
-
from base.hook_utils import log_debug, log_info, log_warn, log_error
|
|
106
|
+
Plan review hooks integrate with the shared context system for state management:
|
|
120
107
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
108
|
+
```typescript
|
|
109
|
+
import { getContextBySessionId, getAllContexts } from "../../_shared/lib-ts/context/context-store.js";
|
|
110
|
+
import { getContextReviewsDir } from "../../_shared/lib-ts/base/constants.js";
|
|
111
|
+
|
|
112
|
+
// Find active context
|
|
113
|
+
const context = getContextBySessionId(sessionId, projectRoot);
|
|
114
|
+
if (!context) {
|
|
115
|
+
// Fallback: find single planning context
|
|
116
|
+
const allActive = getAllContexts("active", projectRoot);
|
|
117
|
+
const planning = allActive.filter((c: any) => c.mode === "active" || c.mode === "has_plan");
|
|
118
|
+
if (planning.length === 1) {
|
|
119
|
+
context = planning[0];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
eprint("Usage: python hook.py <args>")
|
|
123
|
+
// Get reviews directory for this context
|
|
124
|
+
const reviewsDir = getContextReviewsDir(context.id, projectRoot);
|
|
131
125
|
```
|
|
132
126
|
|
|
133
|
-
|
|
134
|
-
# WRONG - print() goes to stdout, corrupts JSON output
|
|
135
|
-
print("Debug info") # Breaks JSON parsing
|
|
127
|
+
CC-native specific state is accessed via `cc-native-state.ts`:
|
|
136
128
|
|
|
137
|
-
|
|
138
|
-
|
|
129
|
+
```typescript
|
|
130
|
+
import { isPlanAlreadyReviewed, markPlanReviewed, wasQuestionsAsked } from "../lib-ts/cc-native-state.js";
|
|
139
131
|
```
|
|
140
132
|
|
|
141
133
|
---
|
|
142
134
|
|
|
143
|
-
##
|
|
135
|
+
## Error Handling
|
|
144
136
|
|
|
145
|
-
|
|
137
|
+
Hooks should fail gracefully — a broken hook shouldn't break the user's workflow. `runHook()` and `runHookAsync()` handle this automatically: uncaught errors log to file and exit 0 (non-blocking).
|
|
146
138
|
|
|
147
|
-
```
|
|
148
|
-
from lib.
|
|
149
|
-
get_context_by_session_id,
|
|
150
|
-
get_all_in_flight_contexts,
|
|
151
|
-
)
|
|
152
|
-
from lib.base.constants import get_context_reviews_dir
|
|
153
|
-
|
|
154
|
-
# Find active context
|
|
155
|
-
context = get_context_by_session_id(session_id, project_root)
|
|
156
|
-
if not context:
|
|
157
|
-
# Fallback: find single planning context
|
|
158
|
-
in_flight = get_all_in_flight_contexts(project_root)
|
|
159
|
-
planning = [c for c in in_flight if c.in_flight and c.in_flight.mode == "planning"]
|
|
160
|
-
if len(planning) == 1:
|
|
161
|
-
context = planning[0]
|
|
162
|
-
|
|
163
|
-
# Get reviews directory for this context
|
|
164
|
-
reviews_dir = get_context_reviews_dir(context.id, project_root)
|
|
165
|
-
```
|
|
139
|
+
```typescript
|
|
140
|
+
import { runHook, logInfo } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
166
141
|
|
|
167
|
-
|
|
142
|
+
function main(): void {
|
|
143
|
+
// Hook logic — uncaught errors are handled by runHook
|
|
144
|
+
logInfo("hook-name", "Starting...");
|
|
145
|
+
}
|
|
168
146
|
|
|
169
|
-
|
|
170
|
-
log_debug("hook", f"Session ID: {session_id}")
|
|
171
|
-
log_debug("hook", f"In-flight contexts: {len(in_flight)}")
|
|
172
|
-
log_debug("hook", f"Modes: {[c.in_flight.mode for c in in_flight]}")
|
|
147
|
+
runHook(main, "hook_name");
|
|
173
148
|
```
|
|
174
149
|
|
|
175
|
-
|
|
150
|
+
For async hooks (plan review with parallel agents):
|
|
176
151
|
|
|
177
|
-
|
|
152
|
+
```typescript
|
|
153
|
+
import { runHookAsync } from "../../_shared/lib-ts/base/hook-utils.js";
|
|
178
154
|
|
|
179
|
-
|
|
155
|
+
async function main(): Promise<void> {
|
|
156
|
+
// Async hook logic with Promise.all() etc.
|
|
157
|
+
}
|
|
180
158
|
|
|
181
|
-
|
|
182
|
-
|
|
159
|
+
runHookAsync(main, "hook_name");
|
|
160
|
+
```
|
|
183
161
|
|
|
184
|
-
|
|
185
|
-
try:
|
|
186
|
-
# Hook logic...
|
|
187
|
-
return 0
|
|
188
|
-
except Exception as e:
|
|
189
|
-
import traceback
|
|
190
|
-
tb = traceback.format_exc()
|
|
191
|
-
log_error("hook-name", str(e), traceback_str=tb)
|
|
192
|
-
# Return 0 to not block the user
|
|
193
|
-
return 0
|
|
162
|
+
Use `emitContextAndBlock()` for intentional blocking (e.g., plan review denial). `hookEventName` is auto-detected.
|
|
194
163
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Error Handling: Non-Critical Operations
|
|
198
167
|
|
|
199
|
-
|
|
168
|
+
Wrap non-critical shared library calls in try/catch to prevent false "hook error" UI display. See **`_shared/lib-ts/CLAUDE.md`** > Context Store for the pattern and rationale.
|
|
169
|
+
|
|
170
|
+
**When to catch locally vs let bubble:**
|
|
171
|
+
- **Catch locally:** Side effects like mode transitions, state saves — the hook's primary purpose can still succeed without them
|
|
172
|
+
- **Let bubble:** Core operations where failure means the hook genuinely can't do its job
|
|
200
173
|
|
|
201
174
|
---
|
|
202
175
|
|
|
@@ -205,31 +178,29 @@ Use `sys.exit(1)` only for intentional blocking (e.g., two-stage review decision
|
|
|
205
178
|
These are reminders based on past issues. Not enforcement rules.
|
|
206
179
|
|
|
207
180
|
- **Don't modify hook output format** without verifying the current Claude Code hook API (it changes between versions)
|
|
208
|
-
- **Don't use `
|
|
181
|
+
- **Don't use `process.exit(1)` or `process.exit(2)`** for non-fatal errors - it blocks the user's workflow
|
|
209
182
|
- **Don't forget template sync** after modifying hooks in `.aiwcli/` - changes should also go to `packages/cli/src/templates/cc-native/`
|
|
210
|
-
- **Don't use `
|
|
183
|
+
- **Don't use `console.log()`** for anything — it corrupts stdout. Use `emitContext()` for hook output
|
|
211
184
|
- **Don't assume session_id format** - it can be UUID, path-like, or other formats
|
|
212
185
|
- **Don't skip `is_internal_call()` check** - recursive hook execution causes state corruption
|
|
213
|
-
- **Don't hardcode paths** - use `
|
|
186
|
+
- **Don't hardcode paths** - use `getProjectRoot()` and relative imports
|
|
187
|
+
- **Don't let non-critical operations bubble to `runHook`** - catch locally to prevent stderr "hook error" display
|
|
214
188
|
|
|
215
189
|
---
|
|
216
190
|
|
|
217
191
|
## Verification After Changes
|
|
218
192
|
|
|
219
|
-
|
|
193
|
+
Validate TypeScript syntax after editing hooks:
|
|
220
194
|
|
|
221
195
|
```bash
|
|
222
|
-
#
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
# Validate template copy (after sync)
|
|
226
|
-
python -m py_compile packages/cli/src/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py
|
|
196
|
+
# Quick syntax check via bun
|
|
197
|
+
bun --print "import('.aiwcli/_cc-native/hooks/cc-native-plan-review.ts')" 2>&1 | head -5
|
|
227
198
|
|
|
228
|
-
#
|
|
229
|
-
|
|
199
|
+
# Or check imports resolve (dry run)
|
|
200
|
+
bun build --no-bundle .aiwcli/_cc-native/hooks/add_plan_context.ts --outdir /dev/null 2>&1
|
|
230
201
|
```
|
|
231
202
|
|
|
232
|
-
Hooks fail silently on
|
|
203
|
+
Hooks fail silently on import errors — verify after any import path changes.
|
|
233
204
|
|
|
234
205
|
---
|
|
235
206
|
|
|
@@ -239,6 +210,9 @@ Hooks fail silently on syntax errors - this catches them before they reach produ
|
|
|
239
210
|
|
|
240
211
|
| Date | Change |
|
|
241
212
|
|------|--------|
|
|
213
|
+
| 2026-02-10 | **Migrated cc-native hooks from Python to TypeScript.** `cc-native-plan-review.ts` (async, parallel agent reviews via `Promise.all()`), `add_plan_context.ts`, `plan_questions_early.ts`. All hooks use `runHook()`/`runHookAsync()` entry points. Library code in `_cc-native/lib-ts/` (18 files). Settings.json updated to use `bun` runner. Python `.py` files kept as fallback until TS hooks verified. |
|
|
214
|
+
| 2026-02-10 | Flipped TS logger stderr default to opt-in (`opts?.stderr === true`). Added `logBlocking()` for intentional stderr visibility. Removed redundant `{stderr: false}` from hook-utils.ts, user_prompt_submit.ts, context_monitor.ts. Added "Hook Error Visibility" section documenting visibility tiers and exit code behavior. |
|
|
215
|
+
| 2026-02-10 | Fixed `debug.py` `context_path` crash. Added local try/catch around `maybeActivate` in `user_prompt_submit.ts` and `context_monitor.ts` to prevent stderr error display on non-critical I/O failures. Removed dead `context_path` from `_emitHookEnd` in `hook-utils.ts`. Added "Error Handling" section to CLAUDE.md. |
|
|
242
216
|
| 2026-02-07 | Handoff staging lifecycle: `has_handoff` mode + `handoff_consumed` flag mirrors plan lifecycle. `save_handoff.py` no longer transitions to idle — stays active for session_end staging. `session_end.py` stages `active→has_handoff` when handoff_path set and not consumed. `session_start.py` restores `has_handoff→active` on /clear. `context_selector.py` has fallback Case 3b for has_handoff. PostToolUse context_monitor matcher simplified from specific tool list to `*`. |
|
|
243
217
|
| 2026-02-07 | Removed PreToolUse:Write matcher from `add_plan_context.py`. Write-time plan nudges were redundant after consolidating enforcement to PreToolUse:Task. Removed `is_plan_file_write()`, `load_plan_context_config()`, `PHASE_B_ENFORCEMENT`, `nudge_write_questions()`, and `project_dir` import. |
|
|
244
218
|
| 2026-02-07 | Question enforcement is now advisory-only (never blocks). `add_plan_context.py` uses `emit_context()` for all question nudges — no `permissionDecision:deny` anywhere. Removed `emit_context_and_block` import and `TASK_ENFORCEMENT_REASON` constant. |
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Plan context hook — handles question marking and advisory question nudges.
|
|
4
|
+
*
|
|
5
|
+
* Registered for two events:
|
|
6
|
+
* - PostToolUse: AskUserQuestion — marks that questions were asked this session.
|
|
7
|
+
* - PreToolUse: Task — nudges Plan subagent to ask questions first (advisory, never blocks).
|
|
8
|
+
*
|
|
9
|
+
* All enforcement is advisory: injects additionalContext to guide Claude toward asking
|
|
10
|
+
* non-obvious questions, but never blocks the tool call. Claude can proceed regardless.
|
|
11
|
+
*
|
|
12
|
+
* Fail-safe: Any error allows the action silently.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { getProjectRoot } from "../../_shared/lib-ts/base/constants.js";
|
|
16
|
+
import {
|
|
17
|
+
emitContext,
|
|
18
|
+
loadHookInput,
|
|
19
|
+
logDebug,
|
|
20
|
+
logDiagnostic,
|
|
21
|
+
logInfo,
|
|
22
|
+
runHook,
|
|
23
|
+
} from "../../_shared/lib-ts/base/hook-utils.js";
|
|
24
|
+
import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
|
|
25
|
+
import { getEvaluationContextReminder } from "../../_shared/lib-ts/templates/plan-context.js";
|
|
26
|
+
import { markQuestionsAsked, wasQuestionsAsked } from "../lib-ts/cc-native-state.js";
|
|
27
|
+
|
|
28
|
+
const CONTEXT_REMINDER = getEvaluationContextReminder();
|
|
29
|
+
|
|
30
|
+
const TASK_ENFORCEMENT_CONTEXT =
|
|
31
|
+
"Before spawning a Plan agent, consider asking the user non-obvious questions " +
|
|
32
|
+
"via AskUserQuestion. Code exploration reveals WHAT exists — questions reveal WHAT MATTERS.\n\n" +
|
|
33
|
+
"Generate 5+ candidate questions across these categories, then keep only 3-4 where " +
|
|
34
|
+
"different answers would lead to meaningfully different plans:\n\n" +
|
|
35
|
+
"1. INTENT & SUCCESS CRITERIA — What does 'done well' look like? Are there multiple " +
|
|
36
|
+
"interpretations of this request? What's a 10 vs a 6?\n\n" +
|
|
37
|
+
"2. CONSTRAINTS & HISTORY — Has this been attempted before? Are there off-limits areas, " +
|
|
38
|
+
"performance requirements, or security considerations not visible in the code?\n\n" +
|
|
39
|
+
"3. TRADE-OFF PREFERENCES — Speed vs thoroughness? Minimal change vs clean architecture? " +
|
|
40
|
+
"Backward compatibility vs clean break?\n\n" +
|
|
41
|
+
"Frame each question with 2-3 concrete options so the user can choose rather than compose. " +
|
|
42
|
+
"Use AskUserQuestion with structured options — never ask questions as inline text.";
|
|
43
|
+
|
|
44
|
+
function isPlanTask(payload: Record<string, unknown>): boolean {
|
|
45
|
+
const toolInput = payload.tool_input;
|
|
46
|
+
if (!toolInput || typeof toolInput !== "object") return false;
|
|
47
|
+
return String((toolInput as Record<string, unknown>).subagent_type ?? "") === "Plan";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function main(): void {
|
|
51
|
+
// Guard: skip for internal subprocess calls (prevents recursive hook execution)
|
|
52
|
+
if (isInternalCall()) return;
|
|
53
|
+
|
|
54
|
+
const payload = loadHookInput();
|
|
55
|
+
if (!payload) return;
|
|
56
|
+
|
|
57
|
+
const toolName = payload.tool_name;
|
|
58
|
+
const hookEvent = payload.hook_event_name ?? "unknown";
|
|
59
|
+
logDiagnostic(
|
|
60
|
+
"add_plan_context",
|
|
61
|
+
"receive",
|
|
62
|
+
`tool=${toolName}, event=${hookEvent}`,
|
|
63
|
+
{ inputs: { tool_name: toolName, hook_event: hookEvent } },
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const projectRoot = getProjectRoot(payload.cwd);
|
|
67
|
+
|
|
68
|
+
// PostToolUse: AskUserQuestion — mark that questions were asked
|
|
69
|
+
if (toolName === "AskUserQuestion") {
|
|
70
|
+
const sessionId = String(payload.session_id ?? "");
|
|
71
|
+
if (sessionId) {
|
|
72
|
+
markQuestionsAsked(sessionId, projectRoot);
|
|
73
|
+
logInfo("add_plan_context", `Marked questions asked for session ${sessionId.slice(0, 8)}...`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// PreToolUse: Task — nudge Plan subagent to ask questions first (advisory)
|
|
80
|
+
if (toolName === "Task") {
|
|
81
|
+
if (!isPlanTask(payload)) return; // Only gate Plan subagent spawns
|
|
82
|
+
|
|
83
|
+
const permissionMode = payload.permission_mode ?? "";
|
|
84
|
+
if (permissionMode !== "plan") return; // Only enforce during plan mode
|
|
85
|
+
|
|
86
|
+
const sessionId = payload.session_id;
|
|
87
|
+
if (!sessionId) {
|
|
88
|
+
logDebug("add_plan_context", "No session_id for Task gate, skipping enforcement");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const sessionIdStr = String(sessionId);
|
|
93
|
+
|
|
94
|
+
if (wasQuestionsAsked(sessionIdStr, projectRoot)) {
|
|
95
|
+
logInfo("add_plan_context", "Questions asked, allowing Plan Task with eval context");
|
|
96
|
+
logDiagnostic(
|
|
97
|
+
"add_plan_context",
|
|
98
|
+
"decide",
|
|
99
|
+
"Questions asked, allowing Plan Task",
|
|
100
|
+
{ decision: "allow_with_context", reasoning: "was_questions_asked=True" },
|
|
101
|
+
);
|
|
102
|
+
emitContext(CONTEXT_REMINDER);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Questions NOT asked: nudge toward asking questions (advisory only)
|
|
107
|
+
logInfo("add_plan_context", "Questions not asked - nudging Plan Task to ask first");
|
|
108
|
+
logDiagnostic(
|
|
109
|
+
"add_plan_context",
|
|
110
|
+
"decide",
|
|
111
|
+
"Questions not asked, nudging Plan Task",
|
|
112
|
+
{ decision: "nudge", reasoning: "was_questions_asked=False, advisory context" },
|
|
113
|
+
);
|
|
114
|
+
emitContext(TASK_ENFORCEMENT_CONTEXT);
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
runHook(main, "add_plan_context");
|