aiwcli 0.10.3 → 0.11.1

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 (191) 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 +107 -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/git-state.ts +1 -1
  24. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
  25. package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
  26. package/dist/templates/_shared/lib-ts/base/logger.ts +15 -2
  27. package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
  28. package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
  29. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +142 -0
  30. package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
  31. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
  32. package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
  33. package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
  34. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +43 -23
  35. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
  36. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
  37. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +158 -0
  38. package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
  39. package/dist/templates/_shared/lib-ts/types.ts +68 -55
  40. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  41. package/dist/templates/_shared/scripts/resume_handoff.ts +345 -0
  42. package/dist/templates/_shared/scripts/save_handoff.ts +3 -3
  43. package/dist/templates/_shared/scripts/status_line.ts +687 -0
  44. package/dist/templates/cc-native/.claude/settings.json +175 -185
  45. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  46. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
  47. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  48. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  49. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1027 -0
  50. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  51. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -0
  52. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +792 -0
  53. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  54. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -0
  55. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  56. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  57. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +115 -0
  58. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  59. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +120 -0
  60. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -0
  61. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  62. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +250 -0
  63. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +275 -0
  64. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  65. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +107 -0
  66. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  67. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  68. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +240 -0
  69. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  70. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +385 -0
  71. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  72. package/dist/templates/cc-native/_cc-native/plan-review.config.json +14 -1
  73. package/oclif.manifest.json +1 -1
  74. package/package.json +2 -2
  75. package/dist/templates/_shared/hooks/__init__.py +0 -16
  76. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  77. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  78. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  79. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  80. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  81. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  82. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  83. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  84. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  85. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  86. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  87. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  88. package/dist/templates/_shared/hooks/archive_plan.py +0 -177
  89. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  90. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  91. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  92. package/dist/templates/_shared/hooks/session_end.py +0 -173
  93. package/dist/templates/_shared/hooks/session_start.py +0 -206
  94. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  95. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  96. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  97. package/dist/templates/_shared/lib/__init__.py +0 -1
  98. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  99. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  100. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  102. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  103. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  104. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  107. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  108. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  109. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  110. package/dist/templates/_shared/lib/base/constants.py +0 -358
  111. package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
  112. package/dist/templates/_shared/lib/base/inference.py +0 -307
  113. package/dist/templates/_shared/lib/base/logger.py +0 -305
  114. package/dist/templates/_shared/lib/base/stop_words.py +0 -221
  115. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  116. package/dist/templates/_shared/lib/base/utils.py +0 -263
  117. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  118. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  130. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  131. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  132. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  133. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  134. package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
  135. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  136. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  137. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  138. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  139. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  140. package/dist/templates/_shared/lib/templates/README.md +0 -206
  141. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  142. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  145. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  146. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  147. package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
  148. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  149. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  150. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  151. package/dist/templates/_shared/scripts/status_line.py +0 -716
  152. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  153. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  154. package/dist/templates/cc-native/MIGRATION.md +0 -86
  155. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  156. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  157. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  158. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  159. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  160. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  161. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  162. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
  163. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  164. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  165. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  166. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  167. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  171. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  172. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  173. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  174. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  175. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  176. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  177. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  178. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  179. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  180. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  181. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  185. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  186. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  187. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  188. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
  189. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  190. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  191. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * SessionStart hook: Restore context after /clear (plan/handoff) or compaction.
4
+ * Routes by source field to appropriate handler.
5
+ */
6
+ import { getProjectRoot } from "../lib-ts/base/constants.js";
7
+ import {
8
+ emitContext, loadHookInput, logDebug,
9
+ logDiagnostic, logError as _logError, logInfo, runHook,
10
+ } from "../lib-ts/base/hook-utils.js";
11
+ import {
12
+ buildRestoreSections, formatHandoffContinuation, getModeDisplay,
13
+ } from "../lib-ts/context/context-formatter.js";
14
+ import {
15
+ bindSession, getAllContexts, getContextBySessionId, updateMode,
16
+ } from "../lib-ts/context/context-store.js";
17
+ import type { ContextState as _ContextState } from "../lib-ts/types.js";
18
+
19
+ /**
20
+ * Handle post-compaction restore: re-inject context that was lost during compaction.
21
+ * Plan content is inlined because Claude doesn't auto-paste after compact.
22
+ */
23
+ function handleCompactRestore(sessionId: string, projectRoot: string): void {
24
+ const state = getContextBySessionId(sessionId, projectRoot);
25
+ if (!state) {
26
+ logDebug("session_start", `No context for session ${sessionId} (compact)`);
27
+ return;
28
+ }
29
+
30
+ const sections: string[] = [
31
+ `## Resuming Context After Compaction: ${state.id}`,
32
+ "",
33
+ `**Summary:** ${state.summary}`,
34
+ `**Mode:** ${getModeDisplay(state.mode) || state.mode}`,
35
+ "",
36
+ ];
37
+
38
+ // Inline plan = true (plan not auto-pasted after compact)
39
+ const restore = buildRestoreSections(state, projectRoot, true);
40
+ if (restore) sections.push(restore);
41
+
42
+ sections.push(
43
+ "",
44
+ "---",
45
+ "*Context was compacted to free up space. The above restores your working state.*",
46
+ );
47
+
48
+ emitContext(sections.join("\n"));
49
+ logInfo("session_start", `Compact restore for ${state.id}`);
50
+ }
51
+
52
+ /**
53
+ * Handle post-clear restore: find staged has_plan or has_handoff context,
54
+ * bind session, transition to active, inject context.
55
+ */
56
+ function handleClearRestore(sessionId: string, projectRoot: string): void {
57
+ const allContexts = getAllContexts("active", projectRoot);
58
+
59
+ // Priority 1: has_plan contexts
60
+ const hasPlan = allContexts.filter(c => c.mode === "has_plan");
61
+ if (hasPlan.length > 0) {
62
+ // Pick most recently active (getAllContexts sorts by last_active desc)
63
+ const ctx = hasPlan[0]!;
64
+
65
+ bindSession(ctx.id, sessionId, projectRoot);
66
+ updateMode(ctx.id, "active", projectRoot, { plan_consumed: true });
67
+
68
+ logInfo("session_start", `Clear restore: ${ctx.id} has_plan → active (plan_consumed=true)`);
69
+
70
+ const sections: string[] = [
71
+ `## Resuming Context After Plan Clear: ${ctx.id}`,
72
+ "",
73
+ `**Summary:** ${ctx.summary}`,
74
+ `**Mode:** Active (Plan Restored)`,
75
+ "",
76
+ ];
77
+
78
+ // inline_plan=false — Claude auto-pastes plan content after /clear
79
+ const restore = buildRestoreSections(ctx, projectRoot, false);
80
+ if (restore) sections.push(restore);
81
+
82
+ sections.push(
83
+ "",
84
+ "---",
85
+ "*Plan has been accepted. The plan content was auto-pasted above. Implement according to the plan.*",
86
+ );
87
+
88
+ emitContext(sections.join("\n"));
89
+ return;
90
+ }
91
+
92
+ // Priority 2: has_handoff contexts
93
+ const hasHandoff = allContexts.filter(c => c.mode === "has_handoff");
94
+ if (hasHandoff.length > 0) {
95
+ const ctx = hasHandoff[0]!;
96
+
97
+ bindSession(ctx.id, sessionId, projectRoot);
98
+ updateMode(ctx.id, "active", projectRoot, { handoff_consumed: true });
99
+
100
+ logInfo("session_start", `Clear restore: ${ctx.id} has_handoff → active (handoff_consumed=true)`);
101
+
102
+ const handoffContent = formatHandoffContinuation(ctx, projectRoot);
103
+ emitContext(handoffContent);
104
+ return;
105
+ }
106
+
107
+ // Nothing to restore
108
+ logDebug("session_start", "No has_plan or has_handoff contexts found");
109
+ }
110
+
111
+ function main(): void {
112
+ const payload = loadHookInput();
113
+ if (!payload) return;
114
+
115
+ const sessionId = payload.session_id;
116
+ if (!sessionId) {
117
+ logDebug("session_start", "No session_id");
118
+ return;
119
+ }
120
+
121
+ const projectRoot = getProjectRoot(payload.cwd);
122
+ const source = payload.source ?? "";
123
+
124
+ logDiagnostic("session_start", "entry", `source=${source}, session=${sessionId}`);
125
+
126
+ switch (source) {
127
+ case "clear": {
128
+ handleClearRestore(sessionId, projectRoot);
129
+ break;
130
+ }
131
+
132
+ case "compact": {
133
+ handleCompactRestore(sessionId, projectRoot);
134
+ break;
135
+ }
136
+
137
+ default: {
138
+ logDebug("session_start", `Unhandled source: ${source}`);
139
+ break;
140
+ }
141
+ }
142
+ }
143
+
144
+ runHook(main, "session_start");
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * PostToolUse:TaskCreate hook: Persist Claude's TaskCreate calls to state.json.
4
+ */
5
+ import { getProjectRoot } from "../lib-ts/base/constants.js";
6
+ import {
7
+ checkSkipPersistence, getToolInput, loadHookInput, logDebug,
8
+ logError, logInfo, logWarn, runHook, validateHookEvent,
9
+ } from "../lib-ts/base/hook-utils.js";
10
+ import { getContextBySessionId } from "../lib-ts/context/context-store.js";
11
+ import { addTask } from "../lib-ts/context/task-tracker.js";
12
+
13
+ function main(): void {
14
+ const payload = loadHookInput();
15
+ if (!payload) return;
16
+ if (!validateHookEvent(payload, "PostToolUse", "TaskCreate")) return;
17
+
18
+ const toolInput = getToolInput(payload);
19
+ if (!toolInput) return;
20
+ if (checkSkipPersistence(payload, "task_create_capture")) return;
21
+
22
+ const projectRoot = getProjectRoot(payload.cwd);
23
+ const sessionId = payload.session_id ?? "unknown";
24
+
25
+ const state = getContextBySessionId(sessionId, projectRoot);
26
+ if (!state) {
27
+ logDebug("task_create_capture", `No context for session ${sessionId}`);
28
+ return;
29
+ }
30
+
31
+ const subject = toolInput.subject as string | undefined;
32
+ if (!subject) {
33
+ logWarn("task_create_capture", "TaskCreate missing subject field");
34
+ return;
35
+ }
36
+
37
+ const description = (toolInput.description as string) ?? "";
38
+ const activeForm = (toolInput.activeForm as string) ?? "";
39
+
40
+ const task = addTask(state.id, subject, description, activeForm, sessionId, projectRoot);
41
+ if (task) {
42
+ logInfo("task_create_capture", `Persisted task ${task.id}: ${subject}`);
43
+ } else {
44
+ logError("task_create_capture", `Failed to persist task: ${subject}`);
45
+ }
46
+ }
47
+
48
+ runHook(main, "task_create_capture");
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * PostToolUse:TaskUpdate hook: Persist Claude's TaskUpdate calls to state.json.
4
+ * Maps Claude's ephemeral task IDs to persistent aiw-N IDs.
5
+ */
6
+ import { getProjectRoot } from "../lib-ts/base/constants.js";
7
+ import {
8
+ checkSkipPersistence, getToolInput, loadHookInput, logDebug,
9
+ logError as _logError, logInfo, logWarn, runHook, validateHookEvent,
10
+ } from "../lib-ts/base/hook-utils.js";
11
+ import { getContextBySessionId } from "../lib-ts/context/context-store.js";
12
+ import { deleteTask, updateTask } from "../lib-ts/context/task-tracker.js";
13
+
14
+ function main(): void {
15
+ const payload = loadHookInput();
16
+ if (!payload) return;
17
+ if (!validateHookEvent(payload, "PostToolUse", "TaskUpdate")) return;
18
+
19
+ const toolInput = getToolInput(payload);
20
+ if (!toolInput) return;
21
+ if (checkSkipPersistence(payload, "task_update_capture")) return;
22
+
23
+ const projectRoot = getProjectRoot(payload.cwd);
24
+ const sessionId = payload.session_id ?? "unknown";
25
+
26
+ const state = getContextBySessionId(sessionId, projectRoot);
27
+ if (!state) {
28
+ logDebug("task_update_capture", `No context for session ${sessionId}`);
29
+ return;
30
+ }
31
+
32
+ const claudeTaskId = toolInput.taskId as string | undefined;
33
+ if (!claudeTaskId) {
34
+ logWarn("task_update_capture", "TaskUpdate missing taskId");
35
+ return;
36
+ }
37
+
38
+ // Map Claude's ephemeral ID to persistent ID
39
+ const metadata = (toolInput.metadata ?? {}) as Record<string, any>;
40
+ const persistentId = (metadata.persistent_id as string) ?? `aiw-${claudeTaskId}`;
41
+
42
+ const status = toolInput.status as string | undefined;
43
+
44
+ if (status === "deleted") {
45
+ const ok = deleteTask(state.id, persistentId, projectRoot);
46
+ if (ok) {
47
+ logInfo("task_update_capture", `Deleted task ${persistentId}`);
48
+ } else {
49
+ logWarn("task_update_capture", `Task ${persistentId} not found for deletion`);
50
+ }
51
+
52
+ return;
53
+ }
54
+
55
+ if (status) {
56
+ const opts: Record<string, any> = { status };
57
+ if (metadata.evidence) opts.evidence = metadata.evidence;
58
+ if (metadata.work_summary) opts.work_summary = metadata.work_summary;
59
+ if (metadata.files_changed && Array.isArray(metadata.files_changed)) {
60
+ opts.files_changed = metadata.files_changed;
61
+ }
62
+
63
+ opts.session_id = sessionId;
64
+
65
+ const ok = updateTask(state.id, persistentId, opts, projectRoot);
66
+ if (ok) {
67
+ logInfo("task_update_capture", `Updated task ${persistentId} → ${status}`);
68
+ } else {
69
+ logWarn("task_update_capture", `Task ${persistentId} not found for update`);
70
+ }
71
+ }
72
+ }
73
+
74
+ runHook(main, "task_update_capture");
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * UserPromptSubmit hook: Context enforcement — ensures every prompt belongs
4
+ * to a tracked context. The most complex shared hook.
5
+ *
6
+ * Uses emitContext() for output — context text is passed via hookSpecificOutput JSON.
7
+ * Catches BlockRequest and exits with code 2 to block the prompt.
8
+ */
9
+ import { getProjectRoot } from "../lib-ts/base/constants.js";
10
+ import {
11
+ emitContext, hookLog, loadHookInput, logBlocking, logDebug, logDiagnostic as _logDiagnostic, logInfo, logWarn as _logWarn, runHookAsync,
12
+ } from "../lib-ts/base/hook-utils.js";
13
+ import { BlockRequest, determineContext } from "../lib-ts/context/context-selector.js";
14
+ import {
15
+ bindSession, getContextBySessionId, maybeActivate, saveState,
16
+ } from "../lib-ts/context/context-store.js";
17
+
18
+ async function asyncMain(): Promise<void> {
19
+ const payload = loadHookInput();
20
+ if (!payload) return;
21
+
22
+ const prompt = (payload as any).prompt as string | undefined;
23
+ const sessionId = payload.session_id;
24
+ const permissionMode = payload.permission_mode ?? "";
25
+ const projectRoot = getProjectRoot(payload.cwd);
26
+
27
+ if (!sessionId) {
28
+ logDebug("user_prompt_submit", "No session_id");
29
+ return;
30
+ }
31
+
32
+ const outputs: string[] = [];
33
+
34
+ // Check if session is already bound to a context
35
+ const existingCtx = getContextBySessionId(sessionId, projectRoot);
36
+
37
+ if (existingCtx) {
38
+ // Returning user — context already bound (stderr: false to avoid "hook error" display)
39
+ try {
40
+ maybeActivate(existingCtx.id, permissionMode, projectRoot, "user_prompt_submit");
41
+ } catch (error) {
42
+ hookLog("warn", "user_prompt_submit", `maybeActivate failed (non-critical): ${error}`, { stderr: false });
43
+ }
44
+
45
+ hookLog("debug", "user_prompt_submit", `Session bound to ${existingCtx.id}`, { stderr: false });
46
+ } else if (prompt) {
47
+ // First prompt — need to determine context
48
+ try {
49
+ const [contextId, method, outputText] = await determineContext(prompt, sessionId, projectRoot);
50
+
51
+ if (contextId) {
52
+ bindSession(contextId, sessionId, projectRoot);
53
+ maybeActivate(contextId, permissionMode, projectRoot, "user_prompt_submit");
54
+
55
+ // Clear handoff_path after binding (prevents re-injection)
56
+ const state = getContextBySessionId(sessionId, projectRoot);
57
+ if (state && state.handoff_path) {
58
+ state.handoff_path = null;
59
+ saveState(state.id, state, projectRoot);
60
+ }
61
+
62
+ logInfo("user_prompt_submit", `Context ${contextId} via ${method}`);
63
+ }
64
+
65
+ if (outputText) {
66
+ outputs.push(outputText);
67
+ }
68
+ } catch (error) {
69
+ if (error instanceof BlockRequest) {
70
+ logBlocking("user_prompt_submit", (error as Error).message);
71
+ process.exit(2); // Block the prompt
72
+ }
73
+
74
+ throw error; // Re-throw unexpected errors
75
+ }
76
+ }
77
+
78
+ if (outputs.length > 0) {
79
+ emitContext(outputs.join("\n\n"));
80
+ }
81
+ }
82
+
83
+ runHookAsync(asyncMain, "user_prompt_submit");
@@ -0,0 +1,318 @@
1
+ # Shared TypeScript Library
2
+
3
+ **Location:** `_shared/lib-ts/` — cross-method infrastructure used by ALL templates.
4
+
5
+ **One import gets you started:**
6
+ ```typescript
7
+ import { loadHookInput, runHook, logInfo, emitContext } from "../lib-ts/base/hook-utils.js";
8
+ ```
9
+
10
+ `hook-utils.ts` re-exports the most-used functions from `logger.ts`, `constants.ts`, and `context-store.ts`. Start here. Only import from deeper modules when you need specific capabilities.
11
+
12
+ **Import direction:** Hooks --> method lib --> `_shared/lib-ts/`. Never the reverse.
13
+
14
+ ---
15
+
16
+ ## Critical Rules
17
+
18
+ These cause silent failures or UI noise when violated:
19
+
20
+ - **Entry point:** Every hook MUST use `runHook()` or `runHookAsync()` — never bare `main()` or `process.exit()`
21
+ - **stdout is sacred:** Only hook JSON output goes to stdout. Use logger functions for diagnostics, never `console.log()` or `print()`
22
+ - **stderr is opt-in:** `logDebug/logInfo/logWarn/logError` write to file only. Use `logBlocking()` when you NEED stderr visibility
23
+ - **Catch non-critical errors locally:** Uncaught errors bubble to `runHook` which writes to stderr, showing "hook error" in the UI even on exit 0
24
+ - **No reverse imports:** Never import from method lib (e.g., `_cc-native/lib/`) into shared lib
25
+
26
+ ---
27
+
28
+ ## Hook Skeleton
29
+
30
+ Copy this for new hooks:
31
+
32
+ ```typescript
33
+ #!/usr/bin/env bun
34
+ import { loadHookInput, runHook, logDebug, logInfo, emitContext } from "../lib-ts/base/hook-utils.js";
35
+
36
+ function main(): void {
37
+ const payload = loadHookInput();
38
+ if (!payload) return;
39
+
40
+ const sessionId = payload.session_id;
41
+ if (!sessionId) return;
42
+
43
+ // Your hook logic here...
44
+
45
+ emitContext("Context visible to Claude");
46
+ }
47
+
48
+ runHook(main, "my_hook_name");
49
+ ```
50
+
51
+ For async hooks (AI inference, network calls):
52
+
53
+ ```typescript
54
+ import { runHookAsync } from "../lib-ts/base/hook-utils.js";
55
+
56
+ async function asyncMain(): Promise<void> {
57
+ // await something...
58
+ }
59
+
60
+ runHookAsync(asyncMain, "my_async_hook");
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Logging
66
+
67
+ All logging goes to `_output/hook-log.jsonl`. stderr visibility is opt-in.
68
+
69
+ | Tier | Function | Visible in UI? | Use When |
70
+ |------|----------|---------------|----------|
71
+ | File-only | `logDebug()` / `logInfo()` / `logWarn()` / `logError()` | No | 99% of logging: diagnostics, state changes, non-critical errors |
72
+ | Blocking | `logBlocking()` | Yes (stderr) | The hook found a real problem the user or Claude must see |
73
+ | Unhandled | `logHookError()` | Yes (stderr) | Reserved for `runHook` crash handler — do not call directly |
74
+ | Terminal | `eprint()` | Yes (raw stderr) | Usage help, progress indicators — not logged to JSONL |
75
+
76
+ ```typescript
77
+ import { logDebug, logInfo, logWarn, logBlocking } from "../lib-ts/base/hook-utils.js";
78
+
79
+ logInfo("my_hook", "Session started"); // file only
80
+ logWarn("my_hook", `Fallback used: ${reason}`); // file only
81
+ logBlocking("my_hook", "Critical: state corrupt"); // shows in UI
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Hook Output — Three Communication Channels
87
+
88
+ Hooks have three channels back to the session. Pick the right one:
89
+
90
+ | Want to... | Function | Who sees it |
91
+ |------------|----------|-------------|
92
+ | Block tool + return message | `emitContextAndBlock(context, reason)` | Claude + user (denial reason prominent) |
93
+ | Return message, don't block | `emitContext(context)` | Claude + user (in transcript) |
94
+ | Log only (diagnostics) | `logInfo()` / `logWarn()` / etc. | Nobody in session — file only |
95
+
96
+ **There is no way to show something to the user but hide it from Claude, or vice versa.** Both `emitContext()` and `emitContextAndBlock()` produce output visible to both.
97
+
98
+ ### Channel 1: Block + Context (PreToolUse only)
99
+
100
+ ```typescript
101
+ emitContextAndBlock(
102
+ "Detailed feedback Claude sees", // additionalContext
103
+ "Short reason for the block" // permissionDecisionReason
104
+ );
105
+ // No SystemExit needed — permissionDecision:"deny" with exit 0 is sufficient.
106
+ // runHookAsync drains stdout before exit to ensure pipe consumers receive the JSON.
107
+ ```
108
+
109
+ The tool call is **prevented from executing**. Only works for PreToolUse hooks.
110
+
111
+ ### Channel 2: Non-blocking Context (any hook event)
112
+
113
+ ```typescript
114
+ emitContext("Information added to Claude's context");
115
+ ```
116
+
117
+ The tool call / session continues normally. Works for PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Notification, SubagentStart.
118
+
119
+ ### Channel 3: Log-only (diagnostics)
120
+
121
+ ```typescript
122
+ logInfo("my_hook", "Processing started"); // File only
123
+ logWarn("my_hook", `Fallback used: ${why}`); // File only
124
+ ```
125
+
126
+ Nobody in the session sees this. Written to `_output/hook-log.jsonl` for debugging.
127
+
128
+ ### Hook Output Logging
129
+
130
+ Both `emitContext()` and `emitContextAndBlock()` automatically log their output to `_output/hook-log.jsonl` as `HOOK_OUTPUT` entries. This captures exactly what was sent to Claude via stdout, closing the visibility gap where the agent sees injected context the user doesn't.
131
+
132
+ - **Log level:** `info` — visible unless `HOOK_LOG_LEVEL=warn`
133
+ - **`msg` field:** Scannable summary: `HOOK_OUTPUT [context] 842 chars` or `HOOK_OUTPUT [block] 340 chars, reason="..."`
134
+ - **`data` field:** Full payload including `additionalContext` and `blockReason` (for blocks)
135
+ - **Controlled by:** Existing `HOOK_LOG_LEVEL` and `HOOK_LOG_DISABLE` env vars
136
+ - **No hook changes needed:** Logging happens inside the emit functions themselves
137
+
138
+ ### Exit codes and JSON
139
+
140
+ | Exit Code | JSON Parsed? | Effect |
141
+ |-----------|-------------|--------|
142
+ | **0** | Yes | Normal — `hookSpecificOutput` processed |
143
+ | **2** | No | Blocking error — JSON ignored, stderr fed to Claude |
144
+ | **Other** | No | Non-blocking error — stderr shown in verbose mode |
145
+
146
+ You cannot mix exit 2 with JSON decisions. Pick one: exit 0 + JSON, or exit 2 + stderr.
147
+
148
+ ### hookSpecificOutput fields by event type
149
+
150
+ | Event | `additionalContext` | `permissionDecision` | `permissionDecisionReason` | Other |
151
+ |-------|:--:|:--:|:--:|-------|
152
+ | **PreToolUse** | Y | Y (allow/deny/ask) | Y | `updatedInput` |
153
+ | **PostToolUse** | Y | - | - | `updatedMCPToolOutput` (MCP only) |
154
+ | **UserPromptSubmit** | Y | - | - | top-level `decision: "block"` |
155
+ | **SessionStart** | Y | - | - | — |
156
+ | **Notification** | Y | - | - | — |
157
+ | **SubagentStart** | Y | - | - | — |
158
+ | **Stop** | - | - | - | top-level `decision`, `reason` |
159
+ | **SessionEnd** | - | - | - | — |
160
+
161
+ **Invalid fields cause silent rejection of the entire output.** No error, no feedback. Conversely, **missing `hookEventName` also causes silent rejection** — see "Hook API: Critical Learnings" below.
162
+
163
+ ### Special case: fileSuggestion
164
+
165
+ The `fileSuggestion` settings command is NOT a hook — it uses a different protocol. It outputs a plain JSON array to stdout (e.g., `console.log(JSON.stringify(paths))`). Do not use `emitContext()` for fileSuggestion.
166
+
167
+ ---
168
+
169
+ ## Hook API: Critical Learnings (Verified 2026-02-11)
170
+
171
+ These findings were verified through systematic testing. They document Claude Code's actual behavior, which sometimes differs from what the docs suggest.
172
+
173
+ ### hookEventName is REQUIRED (CC 2.1.39+)
174
+
175
+ Claude Code validates `hookSpecificOutput` using a Zod discriminated union keyed on `hookEventName`. If this field is missing:
176
+
177
+ - The entire hook output is silently rejected — no error, no feedback
178
+ - `permissionDecision: "deny"` is never processed
179
+ - The hook appears to "not work" even though it runs successfully
180
+
181
+ **You don't need to handle this manually.** `emitContext()` and `emitContextAndBlock()` auto-detect `hookEventName` from the stdin payload (via `_lastHookEvent`, set by `loadHookInput()`/`runHook()`). This works because hooks are synchronous single-process executions — each `bun` process has its own memory, so there's no concurrency risk between sessions.
182
+
183
+ **If auto-detection fails** (e.g., `loadHookInput()` wasn't called), `hookEventName` is omitted and the output will be silently rejected. This is why `runHook()`/`runHookAsync()` is mandatory — it calls `_earlyReadInput()` first, guaranteeing `_lastHookEvent` is populated.
184
+
185
+ ### Exit Code Behavior (Tested)
186
+
187
+ | Exit Code | JSON Parsed? | Blocks Tool? | What Claude Sees | Tested? |
188
+ |-----------|-------------|-------------|------------------|---------|
189
+ | **0** + deny JSON | Yes | Yes (PreToolUse only) | `additionalContext` + denial reason | Yes |
190
+ | **0** + context JSON | Yes | No | `additionalContext` in transcript | Yes |
191
+ | **1** | No | No | stderr in verbose mode only | Yes |
192
+ | **2** | No | Yes (any event) | stderr fed as system-reminder | Yes |
193
+
194
+ **Key insight:** Exit 0 + `permissionDecision: "deny"` is the correct way to block a tool. Exit 2 is a blunt instrument — it ignores your JSON and feeds raw stderr to Claude. Use exit 0 + deny for clean blocking with structured feedback.
195
+
196
+ ### ExitPlanMode: Not Special-Cased
197
+
198
+ Early testing suggested ExitPlanMode was "immune" to PreToolUse deny. **This was wrong.** The actual issue was missing `hookEventName` — the Zod validator silently rejected the deny output.
199
+
200
+ **With `hookEventName` included:**
201
+ - PreToolUse `permissionDecision: "deny"` (exit 0) → **blocks ExitPlanMode**, no dialog appears, session stays in plan mode
202
+ - `emitContextAndBlock()` handles this automatically via auto-detection
203
+
204
+ **Without `hookEventName` (the bug):**
205
+ - Deny silently rejected → dialog appeared → looked like ExitPlanMode was special-cased
206
+ - Exit 2 also appeared to "not work" for PreToolUse (JSON was ignored as expected, but the blocking was via stderr, not deny)
207
+ - PostToolUse with exit 2 appeared to work because it used stderr (not JSON), bypassing the Zod issue
208
+
209
+ **Lesson:** When a hook output seems to be "silently ignored," check the JSON schema first. The Zod validator rejects malformed output without any error message.
210
+
211
+ ### Debugging Checklist
212
+
213
+ When a hook's deny/context isn't working:
214
+
215
+ 1. **Is `hookEventName` in the JSON output?** Check `_output/hook-log.jsonl` for `HOOK_OUTPUT` entries
216
+ 2. **Is the hook using `runHook()`/`runHookAsync()`?** Required for auto-detection
217
+ 3. **Is `loadHookInput()` called before `emitContext()`?** It populates `_lastHookEvent`
218
+ 4. **Is the exit code 0?** Exit 1/2 cause JSON to be ignored
219
+ 5. **Are there extra fields in `hookSpecificOutput`?** Invalid fields cause silent rejection of the entire output
220
+
221
+ ---
222
+
223
+ ## Context Store
224
+
225
+ 2-layer CRUD: per-context `state.json` + global `_output/index.json`.
226
+
227
+ ```typescript
228
+ import { getContextBySessionId, bindSession, maybeActivate, saveState } from "../lib-ts/context/context-store.js";
229
+
230
+ const state = getContextBySessionId(sessionId, projectRoot);
231
+ if (state) {
232
+ // ALWAYS wrap non-critical operations — uncaught errors become UI "hook error"
233
+ try {
234
+ maybeActivate(state.id, permissionMode, projectRoot, "hook_name");
235
+ } catch (e) {
236
+ logWarn("hook_name", `maybeActivate failed (non-critical): ${e}`);
237
+ }
238
+ }
239
+ ```
240
+
241
+ **Valid modes:** `idle` | `has_plan` | `has_handoff` | `active`
242
+
243
+ Transitions: `idle`/`has_plan`/`has_handoff` --> `active` (via `maybeActivate`). `active` --> `has_plan`/`has_handoff` (via `session_end`).
244
+
245
+ ---
246
+
247
+ ## Module Reference
248
+
249
+ Use this table to find the right file. Read the source for full API details.
250
+
251
+ ### `base/` — Core Infrastructure
252
+
253
+ | File | Purpose | Key Exports |
254
+ |------|---------|-------------|
255
+ | `hook-utils.ts` | Hook lifecycle, stdin parsing, output emit, re-exports | `runHook`, `runHookAsync`, `loadHookInput`, `emitContext`, `emitContextAndBlock`, `logDebug`...`logBlocking` |
256
+ | `logger.ts` | JSONL logging engine | `hookLog`, `logDebug`, `logInfo`, `logWarn`, `logError`, `logBlocking`, `logHookError`, `logDiagnostic` |
257
+ | `constants.ts` | Path resolution, limits | `getProjectRoot()`, `getContextDir()`, `MAX_FILE_SIZE` |
258
+ | `atomic-write.ts` | Crash-safe file writes | `atomicWriteFileSync()` |
259
+ | `state-io.ts` | State serialization with mode migration | `readState()`, `writeState()` |
260
+ | `inference.ts` | Claude CLI subprocess calls | `inferText()` |
261
+ | `utils.ts` | Formatting, ID generation | `nowIso()`, `generateContextId()`, `slugify()` |
262
+ | `git-state.ts` | Git snapshot | `captureGitState()` |
263
+ | `subprocess-utils.ts` | Recursive call guard | `isInternalCall()` |
264
+ | `stop-words.ts` | Word list for ID generation | Used by `utils.ts` internally |
265
+
266
+ ### `context/` — Context State Management
267
+
268
+ | File | Purpose | Key Exports |
269
+ |------|---------|-------------|
270
+ | `context-store.ts` | CRUD for context state + index | `getContextBySessionId`, `bindSession`, `maybeActivate`, `saveState`, `createContext` |
271
+ | `context-selector.ts` | Route prompts to contexts | `determineContext()`, `BlockRequest` |
272
+ | `context-formatter.ts` | Display formatting | `formatContextSummary()` |
273
+ | `plan-manager.ts` | Plan lifecycle (archive, hash, sign) | `archivePlan()`, `computePlanHash()` |
274
+ | `task-tracker.ts` | Task CRUD on state.json | `addTask()`, `updateTask()`, `getTasks()` |
275
+
276
+ ### `handoff/` and `templates/`
277
+
278
+ | File | Purpose | Key Exports |
279
+ |------|---------|-------------|
280
+ | `handoff/document-generator.ts` | Handoff document generation | `generateHandoffDocument()` |
281
+ | `templates/formatters.ts` | Display constants, mode maps, icons | `MODE_MAP`, `STATUS_ICONS` |
282
+ | `templates/plan-context.ts` | Plan evaluation templates | `PLAN_EVALUATION_REMINDER` |
283
+
284
+ ### Root
285
+
286
+ | File | Purpose |
287
+ |------|---------|
288
+ | `types.ts` | All shared types: `Mode`, `ContextState`, `Task`, `HookInput`, `HookOutput` |
289
+
290
+ ---
291
+
292
+ ## Shared Hooks (`_shared/hooks-ts/`)
293
+
294
+ These run for ALL templates. Method-specific hooks live in `_{method}/hooks/`.
295
+
296
+ | Hook | Event | Purpose |
297
+ |------|-------|---------|
298
+ | `user_prompt_submit.ts` | UserPromptSubmit | Context enforcement — binds prompts to tracked contexts |
299
+ | `context_monitor.ts` | PostToolUse:* | Context window tracking, handoff warnings at 30/20/10% |
300
+ | `session_start.ts` | SessionStart | Restores plan/handoff context after `/clear` or compaction |
301
+ | `session_end.ts` | SessionEnd | Stages `active` --> `has_plan`/`has_handoff` for next session |
302
+ | `archive_plan.ts` | PreToolUse:ExitPlanMode | Archives plan file before accept/reject decision |
303
+ | `pre_compact.ts` | PreToolUse:Compact | Pre-compaction state snapshot |
304
+ | `task_create_capture.ts` | PostToolUse:TaskCreate | Persists task creation to context state |
305
+ | `task_update_capture.ts` | PostToolUse:TaskUpdate | Persists task updates to context state |
306
+ | `file-suggestion.ts` | PostToolUse:Write | Suggests file organization improvements |
307
+
308
+ ---
309
+
310
+ ## Environment Variables
311
+
312
+ | Variable | Effect |
313
+ |----------|--------|
314
+ | `CLAUDE_PROJECT_DIR` | Override project root detection |
315
+ | `HOOK_LOG_DISABLE=1` | Disable all file logging |
316
+ | `HOOK_LOG_LEVEL=warn` | Minimum log level (default: `debug`) |
317
+ | `HOOK_ERROR_LOG_DISABLE=1` | Legacy alias for `HOOK_LOG_DISABLE` |
318
+ | `_CC_INTERNAL=1` | Marks subprocess calls (checked by `isInternalCall()`) |