aiwcli 0.12.3 → 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 (125) 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 -64
  11. package/dist/templates/_shared/.claude/commands/handoff.md +12 -198
  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 -0
  16. package/dist/templates/_shared/{lib-ts/handoff → handoff-system/lib}/document-generator.ts +215 -216
  17. package/dist/templates/_shared/{lib-ts/handoff → handoff-system/lib}/handoff-reader.ts +157 -158
  18. package/dist/templates/_shared/{scripts → handoff-system/scripts}/resume_handoff.ts +373 -373
  19. package/dist/templates/_shared/{scripts → handoff-system/scripts}/save_handoff.ts +469 -358
  20. package/dist/templates/_shared/handoff-system/workflows/handoff-resume.md +66 -0
  21. package/dist/templates/_shared/{workflows → 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 -183
  28. package/dist/templates/_shared/hooks-ts/session_start.ts +163 -151
  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 -130
  40. package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -184
  41. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +56 -0
  42. package/dist/templates/_shared/lib-ts/base/utils.ts +184 -184
  43. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +566 -560
  44. package/dist/templates/_shared/lib-ts/context/context-selector.ts +524 -515
  45. package/dist/templates/_shared/lib-ts/context/context-store.ts +712 -668
  46. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +312 -312
  47. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -185
  48. package/dist/templates/_shared/lib-ts/package.json +20 -20
  49. package/dist/templates/_shared/lib-ts/templates/formatters.ts +102 -102
  50. package/dist/templates/_shared/lib-ts/templates/plan-context.ts +58 -58
  51. package/dist/templates/_shared/lib-ts/tsconfig.json +13 -13
  52. package/dist/templates/_shared/lib-ts/types.ts +186 -180
  53. package/dist/templates/_shared/scripts/resolve_context.ts +33 -33
  54. package/dist/templates/_shared/scripts/status_line.ts +690 -690
  55. package/dist/templates/cc-native/.claude/commands/{rlm → cc-native/rlm}/ask.md +136 -136
  56. package/dist/templates/cc-native/.claude/commands/{rlm → cc-native/rlm}/index.md +21 -21
  57. package/dist/templates/cc-native/.claude/commands/{rlm → cc-native/rlm}/overview.md +56 -56
  58. package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -10
  59. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -8
  60. package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -8
  61. package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -8
  62. package/dist/templates/cc-native/CC-NATIVE-README.md +189 -189
  63. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +304 -304
  64. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +143 -143
  65. package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +213 -213
  66. package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -70
  67. package/dist/templates/cc-native/_cc-native/cc-native.config.json +96 -96
  68. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +247 -247
  69. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +76 -76
  70. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -54
  71. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +51 -51
  72. package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -53
  73. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -61
  74. package/dist/templates/cc-native/_cc-native/lib-ts/agent-selection.ts +163 -163
  75. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -156
  76. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/format.ts +597 -597
  77. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/index.ts +26 -26
  78. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/tracker.ts +107 -107
  79. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/write.ts +119 -119
  80. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +21 -21
  81. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +319 -319
  82. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -144
  83. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -57
  84. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -83
  85. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +119 -119
  86. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +79 -79
  87. package/dist/templates/cc-native/_cc-native/lib-ts/graduation.ts +132 -132
  88. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +116 -116
  89. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -168
  90. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +70 -70
  91. package/dist/templates/cc-native/_cc-native/lib-ts/output-builder.ts +130 -130
  92. package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +80 -80
  93. package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -41
  94. package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +101 -101
  95. package/dist/templates/cc-native/_cc-native/lib-ts/review-pipeline.ts +511 -511
  96. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +71 -71
  97. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -217
  98. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +12 -12
  99. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +66 -65
  100. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +184 -184
  101. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -39
  102. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +196 -195
  103. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -201
  104. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +21 -21
  105. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/CLAUDE.md +480 -480
  106. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/embedding-indexer.ts +287 -287
  107. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/hyde.ts +148 -148
  108. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/index.ts +54 -54
  109. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +58 -58
  110. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/ollama-client.ts +208 -208
  111. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +460 -460
  112. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-indexer.ts +446 -447
  113. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-loader.ts +280 -280
  114. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-searcher.ts +274 -274
  115. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +201 -201
  116. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/vector-store.ts +278 -278
  117. package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +184 -184
  118. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +275 -275
  119. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -18
  120. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +329 -329
  121. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -72
  122. package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -9
  123. package/oclif.manifest.json +1 -1
  124. package/package.json +108 -108
  125. 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 |