aiwcli 0.12.6 → 0.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/bin/dev.cmd +3 -3
  2. package/bin/dev.js +16 -16
  3. package/bin/run.cmd +3 -3
  4. package/bin/run.js +21 -21
  5. package/dist/commands/branch.js +7 -2
  6. package/dist/lib/bmad-installer.js +37 -37
  7. package/dist/lib/terminal.d.ts +2 -0
  8. package/dist/lib/terminal.js +57 -7
  9. package/dist/templates/CLAUDE.md +205 -205
  10. package/dist/templates/_shared/.claude/commands/handoff-resume.md +12 -12
  11. package/dist/templates/_shared/.claude/commands/handoff.md +12 -12
  12. package/dist/templates/_shared/.claude/settings.json +65 -65
  13. package/dist/templates/_shared/.codex/workflows/handoff.md +226 -226
  14. package/dist/templates/_shared/.windsurf/workflows/handoff.md +226 -226
  15. package/dist/templates/_shared/handoff-system/CLAUDE.md +421 -421
  16. package/dist/templates/_shared/handoff-system/lib/document-generator.ts +215 -215
  17. package/dist/templates/_shared/handoff-system/lib/handoff-reader.ts +158 -158
  18. package/dist/templates/_shared/handoff-system/scripts/resume_handoff.ts +373 -373
  19. package/dist/templates/_shared/handoff-system/scripts/save_handoff.ts +469 -469
  20. package/dist/templates/_shared/handoff-system/workflows/handoff-resume.md +66 -66
  21. package/dist/templates/_shared/handoff-system/workflows/handoff.md +254 -254
  22. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -2
  23. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -159
  24. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -147
  25. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +128 -128
  26. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -49
  27. package/dist/templates/_shared/hooks-ts/session_end.ts +196 -196
  28. package/dist/templates/_shared/hooks-ts/session_start.ts +163 -163
  29. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -48
  30. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -74
  31. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +93 -93
  32. package/dist/templates/_shared/lib-ts/CLAUDE.md +367 -367
  33. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -138
  34. package/dist/templates/_shared/lib-ts/base/constants.ts +303 -303
  35. package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -58
  36. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +582 -582
  37. package/dist/templates/_shared/lib-ts/base/inference.ts +301 -301
  38. package/dist/templates/_shared/lib-ts/base/logger.ts +247 -247
  39. package/dist/templates/_shared/lib-ts/base/state-io.ts +202 -202
  40. package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -184
  41. package/dist/templates/_shared/lib-ts/base/utils.ts +184 -184
  42. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +566 -566
  43. package/dist/templates/_shared/lib-ts/context/context-selector.ts +524 -524
  44. package/dist/templates/_shared/lib-ts/context/context-store.ts +712 -712
  45. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +312 -312
  46. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -185
  47. package/dist/templates/_shared/lib-ts/package.json +20 -20
  48. package/dist/templates/_shared/lib-ts/templates/formatters.ts +102 -102
  49. package/dist/templates/_shared/lib-ts/templates/plan-context.ts +58 -58
  50. package/dist/templates/_shared/lib-ts/tsconfig.json +13 -13
  51. package/dist/templates/_shared/lib-ts/types.ts +186 -186
  52. package/dist/templates/_shared/scripts/resolve_context.ts +33 -33
  53. package/dist/templates/_shared/scripts/status_line.ts +690 -690
  54. package/dist/templates/cc-native/.claude/commands/cc-native/rlm/ask.md +136 -136
  55. package/dist/templates/cc-native/.claude/commands/cc-native/rlm/index.md +21 -21
  56. package/dist/templates/cc-native/.claude/commands/cc-native/rlm/overview.md +56 -56
  57. package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -10
  58. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -8
  59. package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -8
  60. package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -8
  61. package/dist/templates/cc-native/CC-NATIVE-README.md +189 -189
  62. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +304 -304
  63. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +143 -143
  64. package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +213 -213
  65. package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -70
  66. package/dist/templates/cc-native/_cc-native/cc-native.config.json +96 -96
  67. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +247 -247
  68. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +76 -76
  69. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -54
  70. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +51 -51
  71. package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -53
  72. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -61
  73. package/dist/templates/cc-native/_cc-native/lib-ts/agent-selection.ts +163 -163
  74. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -156
  75. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/format.ts +597 -597
  76. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/index.ts +26 -26
  77. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/tracker.ts +107 -107
  78. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/write.ts +119 -119
  79. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +21 -21
  80. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +319 -319
  81. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -144
  82. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -57
  83. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -83
  84. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +119 -119
  85. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +79 -79
  86. package/dist/templates/cc-native/_cc-native/lib-ts/graduation.ts +132 -132
  87. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +116 -116
  88. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -168
  89. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +70 -70
  90. package/dist/templates/cc-native/_cc-native/lib-ts/output-builder.ts +130 -130
  91. package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +80 -80
  92. package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -41
  93. package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +101 -101
  94. package/dist/templates/cc-native/_cc-native/lib-ts/review-pipeline.ts +511 -511
  95. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +71 -71
  96. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -217
  97. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +12 -12
  98. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +66 -66
  99. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +184 -184
  100. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -39
  101. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +196 -196
  102. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -201
  103. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +21 -21
  104. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/CLAUDE.md +480 -480
  105. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/embedding-indexer.ts +287 -287
  106. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/hyde.ts +148 -148
  107. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/index.ts +54 -54
  108. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +58 -58
  109. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/ollama-client.ts +208 -208
  110. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +460 -460
  111. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-indexer.ts +446 -446
  112. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-loader.ts +280 -280
  113. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-searcher.ts +274 -274
  114. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +201 -201
  115. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/vector-store.ts +278 -278
  116. package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +184 -184
  117. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +275 -275
  118. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -18
  119. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +329 -329
  120. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -72
  121. package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -9
  122. package/oclif.manifest.json +1 -1
  123. package/package.json +108 -108
  124. package/dist/templates/cc-native/_cc-native/lib-ts/nul +0 -3
@@ -1,247 +1,247 @@
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.ts` | PreToolUse: ExitPlanMode | Questions gate + plan review 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
-
15
- ### Plan Review Architecture
16
-
17
- The hook is a thin coordinator (~70 lines) that delegates to `lib-ts/review-pipeline.ts`. The pipeline wires together focused modules:
18
-
19
- | Module | Responsibility |
20
- |--------|----------------|
21
- | `plan-discovery.ts` | Find plan file, read content, compute hash |
22
- | `settings.ts` | Load + merge config with defaults, load agent library |
23
- | `agent-selection.ts` | Mandatory agent resolution, orchestrator-based selection, model assignment |
24
- | `graduation.ts` | Pass eligibility, pass streaks, graduation threshold, iteration advancement |
25
- | `output-builder.ts` | Issue truncation, verdict override, context/block message construction |
26
- | `review-pipeline.ts` | Pipeline orchestrator wiring all modules together |
27
- | `artifacts/format.ts` | Pure formatting (markdown, JSON, inline summaries) |
28
- | `artifacts/write.ts` | File I/O for review artifacts |
29
- | `artifacts/tracker.ts` | Review tracker management |
30
-
31
- ### Questions Gate (in review-pipeline.ts)
32
-
33
- Before running plan review agents, the pipeline checks `wasQuestionsAsked()`. If the user hasn't been asked questions yet, it runs a fresh-context plan-questions agent (from `agents/plan-questions/PLAN-QUESTIONER.md`) that independently reviews the plan and generates questions, assumptions, and ambiguities. If questions are found, ExitPlanMode is denied with the question list injected as context. After the user answers via AskUserQuestion (which triggers `mark_questions_asked.ts`), the next ExitPlanMode attempt passes the gate and proceeds to normal plan review.
34
-
35
- ---
36
-
37
- ## Import Pattern
38
-
39
- CC-native hooks are TypeScript, run via `bun`. Use relative imports from the hook file location.
40
-
41
- ```typescript
42
- // Shared library imports (via _shared/lib-ts/)
43
- import { loadHookInput, runHook, logInfo, emitContext } from "../../_shared/lib-ts/base/hook-utils.js";
44
- import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
45
- import { getProjectRoot } from "../../_shared/lib-ts/base/constants.js";
46
-
47
- // CC-native library imports (via ../lib-ts/)
48
- import { wasQuestionsAsked, markQuestionsAsked } from "../lib-ts/cc-native-state.js";
49
- import { loadConfig } from "../lib-ts/config.js";
50
- import type { AgentConfig } from "../lib-ts/types.js";
51
- ```
52
-
53
- **Important:** Always use `.js` extensions in import paths — Bun resolves `.ts` files from `.js` imports.
54
-
55
- **Import direction:** Hooks → `_cc-native/lib-ts/` → `_shared/lib-ts/`. Never reverse.
56
-
57
- ---
58
-
59
- ## Internal Call Detection
60
-
61
- Hooks can be invoked recursively when spawning subprocesses (agents, orchestrator). Always check and skip:
62
-
63
- ```typescript
64
- import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
65
-
66
- function main(): void {
67
- // FIRST LINE of main - before any other logic
68
- if (isInternalCall()) return;
69
-
70
- // Rest of hook logic...
71
- }
72
- ```
73
-
74
- Without this check, the hook runs multiple times per plan review, causing duplicate reviews and state corruption.
75
-
76
- ---
77
-
78
- ## Hook Output Format
79
-
80
- Claude Code hooks return JSON to stdout. The format is specific to each hook type.
81
-
82
- ### PreToolUse Output
83
-
84
- Use the shared hook utilities — never construct JSON manually:
85
-
86
- ```typescript
87
- import { emitContext, emitContextAndBlock } from "../../_shared/lib-ts/base/hook-utils.js";
88
-
89
- // Inject context without blocking:
90
- emitContext("Information for Claude to see...");
91
-
92
- // Block the tool call with context and reason:
93
- emitContextAndBlock(
94
- "Review feedback for Claude to see...",
95
- "Reason shown to Claude for the denial",
96
- );
97
- ```
98
-
99
- **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.
100
-
101
- ---
102
-
103
- ## Debugging Output
104
-
105
- For logging tiers, visibility rules, and stderr behavior, see **`_shared/lib-ts/CLAUDE.md`** (the shared library guide). The key rules:
106
-
107
- - **stderr is opt-in.** `log_debug/log_info/log_warn/log_error` write to file only (no UI noise)
108
- - **`logBlocking()` / `log_hook_error()`** for problems that must be visible
109
- - **`eprint()`** for terminal-only UX (not logged to JSONL)
110
- - **`print()` corrupts stdout** — never use for diagnostics
111
-
112
- TypeScript hooks use re-exported logger functions from `hook-utils.ts`:
113
-
114
- ```typescript
115
- import { logDebug, logInfo, logWarn, logError } from "../../_shared/lib-ts/base/hook-utils.js";
116
-
117
- logDebug("hook-name", `Found ${items.length} items`); // file only
118
- logInfo("hook-name", "Starting hook..."); // file only
119
- logError("hook-name", `Failed: ${e}`); // file only
120
- ```
121
-
122
- ---
123
-
124
- ## Context System Integration
125
-
126
- Plan review hooks integrate with the shared context system for state management:
127
-
128
- ```typescript
129
- import { getContextBySessionId, getAllContexts } from "../../_shared/lib-ts/context/context-store.js";
130
- import { getContextReviewsDir } from "../../_shared/lib-ts/base/constants.js";
131
-
132
- // Find active context
133
- const context = getContextBySessionId(sessionId, projectRoot);
134
- if (!context) {
135
- // Fallback: find single planning context
136
- const allActive = getAllContexts("active", projectRoot);
137
- const planning = allActive.filter((c: any) => c.mode === "active" || c.mode === "has_plan");
138
- if (planning.length === 1) {
139
- context = planning[0];
140
- }
141
- }
142
-
143
- // Get reviews directory for this context
144
- const reviewsDir = getContextReviewsDir(context.id, projectRoot);
145
- ```
146
-
147
- CC-native specific state is accessed via `cc-native-state.ts`:
148
-
149
- ```typescript
150
- import { isPlanAlreadyReviewed, markPlanReviewed, wasQuestionsAsked } from "../lib-ts/cc-native-state.js";
151
- ```
152
-
153
- ---
154
-
155
- ## Error Handling
156
-
157
- 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).
158
-
159
- ```typescript
160
- import { runHook, logInfo } from "../../_shared/lib-ts/base/hook-utils.js";
161
-
162
- function main(): void {
163
- // Hook logic — uncaught errors are handled by runHook
164
- logInfo("hook-name", "Starting...");
165
- }
166
-
167
- runHook(main, "hook_name");
168
- ```
169
-
170
- For async hooks (plan review with parallel agents):
171
-
172
- ```typescript
173
- import { runHookAsync } from "../../_shared/lib-ts/base/hook-utils.js";
174
-
175
- async function main(): Promise<void> {
176
- // Async hook logic with Promise.all() etc.
177
- }
178
-
179
- runHookAsync(main, "hook_name");
180
- ```
181
-
182
- Use `emitContextAndBlock()` for intentional blocking (e.g., plan review denial). `hookEventName` is auto-detected.
183
-
184
- ---
185
-
186
- ## Error Handling: Non-Critical Operations
187
-
188
- 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.
189
-
190
- **When to catch locally vs let bubble:**
191
- - **Catch locally:** Side effects like mode transitions, state saves — the hook's primary purpose can still succeed without them
192
- - **Let bubble:** Core operations where failure means the hook genuinely can't do its job
193
-
194
- ---
195
-
196
- ## DO NOT
197
-
198
- These are reminders based on past issues. Not enforcement rules.
199
-
200
- - **Don't modify hook output format** without verifying the current Claude Code hook API (it changes between versions)
201
- - **Don't use `process.exit(1)` or `process.exit(2)`** for non-fatal errors - it blocks the user's workflow
202
- - **Don't forget template sync** after modifying hooks in `.aiwcli/` - changes should also go to `packages/cli/src/templates/cc-native/`
203
- - **Don't use `console.log()`** for anything — it corrupts stdout. Use `emitContext()` for hook output
204
- - **Don't assume session_id format** - it can be UUID, path-like, or other formats
205
- - **Don't skip `is_internal_call()` check** - recursive hook execution causes state corruption
206
- - **Don't hardcode paths** - use `getProjectRoot()` and relative imports
207
- - **Don't let non-critical operations bubble to `runHook`** - catch locally to prevent stderr "hook error" display
208
-
209
- ---
210
-
211
- ## Verification After Changes
212
-
213
- Validate TypeScript syntax after editing hooks:
214
-
215
- ```bash
216
- # Quick syntax check via bun
217
- bun --print "import('.aiwcli/_cc-native/hooks/cc-native-plan-review.ts')" 2>&1 | head -5
218
-
219
- # Or check imports resolve (dry run)
220
- bun build --no-bundle .aiwcli/_cc-native/hooks/add_plan_context.ts --outdir /dev/null 2>&1
221
- ```
222
-
223
- Hooks fail silently on import errors — verify after any import path changes.
224
-
225
- ---
226
-
227
- ## Changelog
228
-
229
- <!-- Add dated entries as new issues are discovered -->
230
-
231
- | Date | Change |
232
- |------|--------|
233
- | 2026-02-14 | **Plan review hook refactored into focused modules.** `cc-native-plan-review.ts` reduced from 1061 to ~70 lines (thin coordinator). Core logic moved to `review-pipeline.ts`. Extracted: `plan-discovery.ts`, `settings.ts`, `agent-selection.ts`, `graduation.ts`, `output-builder.ts`. Split `artifacts.ts` (822 lines) into `artifacts/format.ts`, `artifacts/write.ts`, `artifacts/tracker.ts` with barrel re-export. Added `loadIterationState()`/`saveIterationState()` to `state.ts`. New pipeline types in `types.ts`. |
234
- | 2026-02-14 | **Questions gate added to plan review.** `cc-native-plan-review.ts` now runs a fresh-context plan-questions agent before plan review. If `wasQuestionsAsked()` returns false, the PLAN-QUESTIONER agent (from `agents/plan-questions/`) generates questions/assumptions/ambiguities using `QUESTIONS_SCHEMA`. On questions found, ExitPlanMode is denied with question list as context. New library module: `lib-ts/plan-questions.ts`. Agent directory reorganized: review agents moved to `agents/plan-review/`, question agents in `agents/plan-questions/`. |
235
- | 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. |
236
- | 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. |
237
- | 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. |
238
- | 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 `*`. |
239
- | 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. |
240
- | 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. |
241
- | 2026-02-07 | Moved question enforcement to PreToolUse:Task (Plan subagent gate). `add_plan_context.py` now handles three events: PostToolUse:AskUserQuestion, PreToolUse:Task (primary gate), PreToolUse:Write (fallback). Added `is_plan_task()`, `is_internal_call()` guard, `TASK_ENFORCEMENT_CONTEXT` constant. Registered `^Task$` command hook in settings.json. |
242
- | 2026-02-07 | Deleted `plan_accepted.py` (dead code — PostToolUse:ExitPlanMode never fires due to /clear race). Plan field assignment handled by `session_end.py` fallback. Added `plan_consumed` flag to prevent infinite plan re-staging. |
243
- | 2026-02-07 | Hook lifecycle diagnostics: all hooks now use `run_hook(main, "hook_name")` entry point. Logs HOOK_START/HOOK_END with template origin, event type, duration_ms, and status. Millisecond timestamps in logger. |
244
- | 2026-02-07 | Unified logger: all diagnostic logging uses `log_debug/log_info/log_warn/log_error` from `_shared/lib/base/logger.py` instead of eprint/print-to-stderr. Updated debugging and error handling docs. |
245
- | 2026-02-06 | Merged mark_questions_asked.py into add_plan_context.py. Hook now handles both PostToolUse:AskUserQuestion and PreToolUse:Write. Deleted standalone mark_questions_asked.py. |
246
- | 2026-02-06 | Fixed add_plan_context.py trigger docs (was PostToolUse: EnterPlanMode, is PreToolUse: Write). Added emit_context/emit_context_and_block utility docs. |
247
- | 2026-02-03 | Initial creation |
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.ts` | PreToolUse: ExitPlanMode | Questions gate + plan review 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
+
15
+ ### Plan Review Architecture
16
+
17
+ The hook is a thin coordinator (~70 lines) that delegates to `lib-ts/review-pipeline.ts`. The pipeline wires together focused modules:
18
+
19
+ | Module | Responsibility |
20
+ |--------|----------------|
21
+ | `plan-discovery.ts` | Find plan file, read content, compute hash |
22
+ | `settings.ts` | Load + merge config with defaults, load agent library |
23
+ | `agent-selection.ts` | Mandatory agent resolution, orchestrator-based selection, model assignment |
24
+ | `graduation.ts` | Pass eligibility, pass streaks, graduation threshold, iteration advancement |
25
+ | `output-builder.ts` | Issue truncation, verdict override, context/block message construction |
26
+ | `review-pipeline.ts` | Pipeline orchestrator wiring all modules together |
27
+ | `artifacts/format.ts` | Pure formatting (markdown, JSON, inline summaries) |
28
+ | `artifacts/write.ts` | File I/O for review artifacts |
29
+ | `artifacts/tracker.ts` | Review tracker management |
30
+
31
+ ### Questions Gate (in review-pipeline.ts)
32
+
33
+ Before running plan review agents, the pipeline checks `wasQuestionsAsked()`. If the user hasn't been asked questions yet, it runs a fresh-context plan-questions agent (from `agents/plan-questions/PLAN-QUESTIONER.md`) that independently reviews the plan and generates questions, assumptions, and ambiguities. If questions are found, ExitPlanMode is denied with the question list injected as context. After the user answers via AskUserQuestion (which triggers `mark_questions_asked.ts`), the next ExitPlanMode attempt passes the gate and proceeds to normal plan review.
34
+
35
+ ---
36
+
37
+ ## Import Pattern
38
+
39
+ CC-native hooks are TypeScript, run via `bun`. Use relative imports from the hook file location.
40
+
41
+ ```typescript
42
+ // Shared library imports (via _shared/lib-ts/)
43
+ import { loadHookInput, runHook, logInfo, emitContext } from "../../_shared/lib-ts/base/hook-utils.js";
44
+ import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
45
+ import { getProjectRoot } from "../../_shared/lib-ts/base/constants.js";
46
+
47
+ // CC-native library imports (via ../lib-ts/)
48
+ import { wasQuestionsAsked, markQuestionsAsked } from "../lib-ts/cc-native-state.js";
49
+ import { loadConfig } from "../lib-ts/config.js";
50
+ import type { AgentConfig } from "../lib-ts/types.js";
51
+ ```
52
+
53
+ **Important:** Always use `.js` extensions in import paths — Bun resolves `.ts` files from `.js` imports.
54
+
55
+ **Import direction:** Hooks → `_cc-native/lib-ts/` → `_shared/lib-ts/`. Never reverse.
56
+
57
+ ---
58
+
59
+ ## Internal Call Detection
60
+
61
+ Hooks can be invoked recursively when spawning subprocesses (agents, orchestrator). Always check and skip:
62
+
63
+ ```typescript
64
+ import { isInternalCall } from "../../_shared/lib-ts/base/subprocess-utils.js";
65
+
66
+ function main(): void {
67
+ // FIRST LINE of main - before any other logic
68
+ if (isInternalCall()) return;
69
+
70
+ // Rest of hook logic...
71
+ }
72
+ ```
73
+
74
+ Without this check, the hook runs multiple times per plan review, causing duplicate reviews and state corruption.
75
+
76
+ ---
77
+
78
+ ## Hook Output Format
79
+
80
+ Claude Code hooks return JSON to stdout. The format is specific to each hook type.
81
+
82
+ ### PreToolUse Output
83
+
84
+ Use the shared hook utilities — never construct JSON manually:
85
+
86
+ ```typescript
87
+ import { emitContext, emitContextAndBlock } from "../../_shared/lib-ts/base/hook-utils.js";
88
+
89
+ // Inject context without blocking:
90
+ emitContext("Information for Claude to see...");
91
+
92
+ // Block the tool call with context and reason:
93
+ emitContextAndBlock(
94
+ "Review feedback for Claude to see...",
95
+ "Reason shown to Claude for the denial",
96
+ );
97
+ ```
98
+
99
+ **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.
100
+
101
+ ---
102
+
103
+ ## Debugging Output
104
+
105
+ For logging tiers, visibility rules, and stderr behavior, see **`_shared/lib-ts/CLAUDE.md`** (the shared library guide). The key rules:
106
+
107
+ - **stderr is opt-in.** `log_debug/log_info/log_warn/log_error` write to file only (no UI noise)
108
+ - **`logBlocking()` / `log_hook_error()`** for problems that must be visible
109
+ - **`eprint()`** for terminal-only UX (not logged to JSONL)
110
+ - **`print()` corrupts stdout** — never use for diagnostics
111
+
112
+ TypeScript hooks use re-exported logger functions from `hook-utils.ts`:
113
+
114
+ ```typescript
115
+ import { logDebug, logInfo, logWarn, logError } from "../../_shared/lib-ts/base/hook-utils.js";
116
+
117
+ logDebug("hook-name", `Found ${items.length} items`); // file only
118
+ logInfo("hook-name", "Starting hook..."); // file only
119
+ logError("hook-name", `Failed: ${e}`); // file only
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Context System Integration
125
+
126
+ Plan review hooks integrate with the shared context system for state management:
127
+
128
+ ```typescript
129
+ import { getContextBySessionId, getAllContexts } from "../../_shared/lib-ts/context/context-store.js";
130
+ import { getContextReviewsDir } from "../../_shared/lib-ts/base/constants.js";
131
+
132
+ // Find active context
133
+ const context = getContextBySessionId(sessionId, projectRoot);
134
+ if (!context) {
135
+ // Fallback: find single planning context
136
+ const allActive = getAllContexts("active", projectRoot);
137
+ const planning = allActive.filter((c: any) => c.mode === "active" || c.mode === "has_plan");
138
+ if (planning.length === 1) {
139
+ context = planning[0];
140
+ }
141
+ }
142
+
143
+ // Get reviews directory for this context
144
+ const reviewsDir = getContextReviewsDir(context.id, projectRoot);
145
+ ```
146
+
147
+ CC-native specific state is accessed via `cc-native-state.ts`:
148
+
149
+ ```typescript
150
+ import { isPlanAlreadyReviewed, markPlanReviewed, wasQuestionsAsked } from "../lib-ts/cc-native-state.js";
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Error Handling
156
+
157
+ 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).
158
+
159
+ ```typescript
160
+ import { runHook, logInfo } from "../../_shared/lib-ts/base/hook-utils.js";
161
+
162
+ function main(): void {
163
+ // Hook logic — uncaught errors are handled by runHook
164
+ logInfo("hook-name", "Starting...");
165
+ }
166
+
167
+ runHook(main, "hook_name");
168
+ ```
169
+
170
+ For async hooks (plan review with parallel agents):
171
+
172
+ ```typescript
173
+ import { runHookAsync } from "../../_shared/lib-ts/base/hook-utils.js";
174
+
175
+ async function main(): Promise<void> {
176
+ // Async hook logic with Promise.all() etc.
177
+ }
178
+
179
+ runHookAsync(main, "hook_name");
180
+ ```
181
+
182
+ Use `emitContextAndBlock()` for intentional blocking (e.g., plan review denial). `hookEventName` is auto-detected.
183
+
184
+ ---
185
+
186
+ ## Error Handling: Non-Critical Operations
187
+
188
+ 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.
189
+
190
+ **When to catch locally vs let bubble:**
191
+ - **Catch locally:** Side effects like mode transitions, state saves — the hook's primary purpose can still succeed without them
192
+ - **Let bubble:** Core operations where failure means the hook genuinely can't do its job
193
+
194
+ ---
195
+
196
+ ## DO NOT
197
+
198
+ These are reminders based on past issues. Not enforcement rules.
199
+
200
+ - **Don't modify hook output format** without verifying the current Claude Code hook API (it changes between versions)
201
+ - **Don't use `process.exit(1)` or `process.exit(2)`** for non-fatal errors - it blocks the user's workflow
202
+ - **Don't forget template sync** after modifying hooks in `.aiwcli/` - changes should also go to `packages/cli/src/templates/cc-native/`
203
+ - **Don't use `console.log()`** for anything — it corrupts stdout. Use `emitContext()` for hook output
204
+ - **Don't assume session_id format** - it can be UUID, path-like, or other formats
205
+ - **Don't skip `is_internal_call()` check** - recursive hook execution causes state corruption
206
+ - **Don't hardcode paths** - use `getProjectRoot()` and relative imports
207
+ - **Don't let non-critical operations bubble to `runHook`** - catch locally to prevent stderr "hook error" display
208
+
209
+ ---
210
+
211
+ ## Verification After Changes
212
+
213
+ Validate TypeScript syntax after editing hooks:
214
+
215
+ ```bash
216
+ # Quick syntax check via bun
217
+ bun --print "import('.aiwcli/_cc-native/hooks/cc-native-plan-review.ts')" 2>&1 | head -5
218
+
219
+ # Or check imports resolve (dry run)
220
+ bun build --no-bundle .aiwcli/_cc-native/hooks/add_plan_context.ts --outdir /dev/null 2>&1
221
+ ```
222
+
223
+ Hooks fail silently on import errors — verify after any import path changes.
224
+
225
+ ---
226
+
227
+ ## Changelog
228
+
229
+ <!-- Add dated entries as new issues are discovered -->
230
+
231
+ | Date | Change |
232
+ |------|--------|
233
+ | 2026-02-14 | **Plan review hook refactored into focused modules.** `cc-native-plan-review.ts` reduced from 1061 to ~70 lines (thin coordinator). Core logic moved to `review-pipeline.ts`. Extracted: `plan-discovery.ts`, `settings.ts`, `agent-selection.ts`, `graduation.ts`, `output-builder.ts`. Split `artifacts.ts` (822 lines) into `artifacts/format.ts`, `artifacts/write.ts`, `artifacts/tracker.ts` with barrel re-export. Added `loadIterationState()`/`saveIterationState()` to `state.ts`. New pipeline types in `types.ts`. |
234
+ | 2026-02-14 | **Questions gate added to plan review.** `cc-native-plan-review.ts` now runs a fresh-context plan-questions agent before plan review. If `wasQuestionsAsked()` returns false, the PLAN-QUESTIONER agent (from `agents/plan-questions/`) generates questions/assumptions/ambiguities using `QUESTIONS_SCHEMA`. On questions found, ExitPlanMode is denied with question list as context. New library module: `lib-ts/plan-questions.ts`. Agent directory reorganized: review agents moved to `agents/plan-review/`, question agents in `agents/plan-questions/`. |
235
+ | 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. |
236
+ | 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. |
237
+ | 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. |
238
+ | 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 `*`. |
239
+ | 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. |
240
+ | 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. |
241
+ | 2026-02-07 | Moved question enforcement to PreToolUse:Task (Plan subagent gate). `add_plan_context.py` now handles three events: PostToolUse:AskUserQuestion, PreToolUse:Task (primary gate), PreToolUse:Write (fallback). Added `is_plan_task()`, `is_internal_call()` guard, `TASK_ENFORCEMENT_CONTEXT` constant. Registered `^Task$` command hook in settings.json. |
242
+ | 2026-02-07 | Deleted `plan_accepted.py` (dead code — PostToolUse:ExitPlanMode never fires due to /clear race). Plan field assignment handled by `session_end.py` fallback. Added `plan_consumed` flag to prevent infinite plan re-staging. |
243
+ | 2026-02-07 | Hook lifecycle diagnostics: all hooks now use `run_hook(main, "hook_name")` entry point. Logs HOOK_START/HOOK_END with template origin, event type, duration_ms, and status. Millisecond timestamps in logger. |
244
+ | 2026-02-07 | Unified logger: all diagnostic logging uses `log_debug/log_info/log_warn/log_error` from `_shared/lib/base/logger.py` instead of eprint/print-to-stderr. Updated debugging and error handling docs. |
245
+ | 2026-02-06 | Merged mark_questions_asked.py into add_plan_context.py. Hook now handles both PostToolUse:AskUserQuestion and PreToolUse:Write. Deleted standalone mark_questions_asked.py. |
246
+ | 2026-02-06 | Fixed add_plan_context.py trigger docs (was PostToolUse: EnterPlanMode, is PreToolUse: Write). Added emit_context/emit_context_and_block utility docs. |
247
+ | 2026-02-03 | Initial creation |