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.
Files changed (189) hide show
  1. package/bin/run.js +1 -1
  2. package/dist/commands/clear.js +28 -131
  3. package/dist/commands/init/index.js +3 -3
  4. package/dist/lib/gitignore-manager.d.ts +32 -0
  5. package/dist/lib/gitignore-manager.js +141 -2
  6. package/dist/templates/CLAUDE.md +8 -8
  7. package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
  8. package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
  9. package/dist/templates/_shared/.claude/settings.json +7 -7
  10. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
  11. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
  12. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
  13. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
  14. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
  15. package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
  16. package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
  17. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
  18. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
  19. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
  20. package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
  21. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
  22. package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
  23. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
  24. package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
  25. package/dist/templates/_shared/lib-ts/base/logger.ts +31 -15
  26. package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
  27. package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
  28. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +139 -0
  29. package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
  30. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
  31. package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
  32. package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
  33. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +61 -37
  34. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
  35. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
  36. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
  37. package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
  38. package/dist/templates/_shared/lib-ts/types.ts +68 -55
  39. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  40. package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
  41. package/dist/templates/_shared/scripts/save_handoff.ts +21 -21
  42. package/dist/templates/_shared/scripts/status_line.ts +733 -0
  43. package/dist/templates/cc-native/.claude/settings.json +175 -185
  44. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  45. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
  46. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  47. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  48. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
  49. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  50. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
  51. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
  52. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  53. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
  54. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  55. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  56. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  57. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
  58. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
  59. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  60. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
  61. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
  62. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  63. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
  64. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  65. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  66. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
  67. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  68. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
  69. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  70. package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -9
  71. package/oclif.manifest.json +1 -1
  72. package/package.json +1 -1
  73. package/dist/templates/_shared/hooks/__init__.py +0 -16
  74. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  75. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  76. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  77. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  78. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  79. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  80. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  81. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  82. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  83. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  84. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  85. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  86. package/dist/templates/_shared/hooks/archive_plan.py +0 -177
  87. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  88. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  89. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  90. package/dist/templates/_shared/hooks/session_end.py +0 -173
  91. package/dist/templates/_shared/hooks/session_start.py +0 -206
  92. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  93. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  94. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  95. package/dist/templates/_shared/lib/__init__.py +0 -1
  96. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  97. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  98. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  99. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  100. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  101. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  102. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  103. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  104. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  107. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  108. package/dist/templates/_shared/lib/base/constants.py +0 -358
  109. package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
  110. package/dist/templates/_shared/lib/base/inference.py +0 -307
  111. package/dist/templates/_shared/lib/base/logger.py +0 -305
  112. package/dist/templates/_shared/lib/base/stop_words.py +0 -221
  113. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  114. package/dist/templates/_shared/lib/base/utils.py +0 -263
  115. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  116. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  117. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  118. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  130. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  131. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  132. package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
  133. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  134. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  135. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  136. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  137. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  138. package/dist/templates/_shared/lib/templates/README.md +0 -206
  139. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  140. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  141. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  142. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  145. package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
  146. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  147. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  148. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  149. package/dist/templates/_shared/scripts/status_line.py +0 -716
  150. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  151. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  152. package/dist/templates/cc-native/MIGRATION.md +0 -86
  153. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  154. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  155. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  156. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  157. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  158. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  159. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  160. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
  161. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  162. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  163. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  164. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  165. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  166. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  167. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  171. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  172. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  173. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  174. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  175. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  176. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  177. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  178. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  179. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  180. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  181. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  185. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  186. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
  187. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  188. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  189. 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.py` | PreToolUse: ExitPlanMode | Review plans before user approval |
12
- | `add_plan_context.py` | PostToolUse: AskUserQuestion, PreToolUse: Task | Mark questions asked; nudge Plan subagent to ask questions first |
13
- | `suggest-fresh-perspective.py` | PostToolUse | Suggest fresh perspective workflow |
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
- Hooks run from arbitrary working directories. Always set up sys.path explicitly.
19
+ CC-native hooks are TypeScript, run via `bun`. Use relative imports from the hook file location.
20
20
 
21
- ```python
22
- # CORRECT - works in hook context
23
- from pathlib import Path
24
- import sys
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
- _lib = Path(__file__).parent.parent / "lib"
27
- sys.path.insert(0, str(_lib))
28
-
29
- # For shared library access
30
- _shared = Path(__file__).parent.parent.parent / "_shared"
31
- sys.path.insert(0, str(_shared))
32
-
33
- from utils import eprint, ReviewerResult
34
- from lib.base.subprocess_utils import is_internal_call
35
- 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
- ```python
39
- # WRONG - relative imports fail in hook context
40
- from ..lib import utils # ModuleNotFoundError
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
- ```python
50
- def main() -> int:
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
- # Rest of hook logic...
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
- ```python
69
- # CORRECT - current API format
70
- import json
64
+ Use the shared hook utilities — never construct JSON manually:
71
65
 
72
- out = {
73
- "hookSpecificOutput": {
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
- # To block the tool call:
79
- out["hookSpecificOutput"]["permissionDecision"] = "deny"
80
- out["hookSpecificOutput"]["permissionDecisionReason"] = "Reason shown to Claude"
69
+ // Inject context without blocking:
70
+ emitContext("Information for Claude to see...");
81
71
 
82
- print(json.dumps(out, ensure_ascii=False))
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
- ```python
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
- **Key insight:** The old `decision`/`reason` format fails silently. If your hook isn't affecting Claude's behavior, check the output format first.
81
+ ---
82
+
83
+ ## Debugging Output
92
84
 
93
- ### Using Hook Utilities (Preferred)
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
- Instead of manually constructing hookSpecificOutput dicts, use the shared utilities from `base.hook_utils`:
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
- ```python
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
- # Inject context without blocking:
101
- emit_context("Information for Claude to see...")
94
+ ```typescript
95
+ import { logDebug, logInfo, logWarn, logError } from "../../_shared/lib-ts/base/hook-utils.js";
102
96
 
103
- # Block the tool call with context and reason:
104
- emit_context_and_block(
105
- "Review feedback for Claude to see...",
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
- ## Debugging Output
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
- ```python
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
- # CORRECT - unified logger: writes to stderr AND _output/hook-log.jsonl
122
- log_debug("hook-name", f"Found {len(items)} items")
123
- log_info("hook-name", "Starting hook...")
124
- log_warn("hook-name", f"Fallback used: {reason}")
125
- log_error("hook-name", f"Failed: {e}", traceback_str=tb)
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
- ```python
129
- # ACCEPTABLE - eprint() for terminal-only UX (usage help, progress)
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
- ```python
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
- # WRONG - raw print to stderr instead of logger
138
- print(f"Error: {e}", file=sys.stderr) # Use log_error() instead
129
+ ```typescript
130
+ import { isPlanAlreadyReviewed, markPlanReviewed, wasQuestionsAsked } from "../lib-ts/cc-native-state.js";
139
131
  ```
140
132
 
141
133
  ---
142
134
 
143
- ## Context System Integration
135
+ ## Error Handling
144
136
 
145
- Plan review hooks integrate with the shared context system for state management:
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
- ```python
148
- from lib.context.context_manager import (
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
- If context isn't found, add diagnostic logging:
142
+ function main(): void {
143
+ // Hook logic — uncaught errors are handled by runHook
144
+ logInfo("hook-name", "Starting...");
145
+ }
168
146
 
169
- ```python
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
- ## Error Handling
152
+ ```typescript
153
+ import { runHookAsync } from "../../_shared/lib-ts/base/hook-utils.js";
178
154
 
179
- Hooks should fail gracefully - a broken hook shouldn't break the user's workflow:
155
+ async function main(): Promise<void> {
156
+ // Async hook logic with Promise.all() etc.
157
+ }
180
158
 
181
- ```python
182
- from base.hook_utils import log_error, run_hook
159
+ runHookAsync(main, "hook_name");
160
+ ```
183
161
 
184
- def main() -> int:
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
- if __name__ == "__main__":
196
- run_hook(main, "hook-name")
197
- ```
164
+ ---
165
+
166
+ ## Error Handling: Non-Critical Operations
198
167
 
199
- Use `sys.exit(1)` only for intentional blocking (e.g., two-stage review decision denies the plan).
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 `sys.exit(1)`** for non-fatal errors - it blocks the user's workflow
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 `print()`** for anything except the final JSON output
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 `Path(__file__)` and environment variables
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
- Always validate Python syntax after editing hooks:
193
+ Validate TypeScript syntax after editing hooks:
220
194
 
221
195
  ```bash
222
- # Validate working copy
223
- python -m py_compile .aiwcli/_cc-native/hooks/cc-native-plan-review.py
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
- # Validate all shared hooks (loop required — py_compile doesn't accept globs)
229
- for f in .aiwcli/_shared/hooks/*.py; do python -m py_compile "$f"; done
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 syntax errors - this catches them before they reach production.
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");