@hailer/mcp 1.0.29 → 1.1.2

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 (233) hide show
  1. package/.claude/.session-checked +1 -0
  2. package/.claude/agents/agent-ada-skill-builder.md +10 -2
  3. package/.claude/agents/agent-alejandro-function-fields.md +104 -37
  4. package/.claude/agents/agent-bjorn-config-audit.md +41 -21
  5. package/.claude/agents/agent-builder-agent-creator.md +13 -3
  6. package/.claude/agents/agent-code-simplifier.md +53 -0
  7. package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
  8. package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
  9. package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
  10. package/.claude/agents/agent-helga-workflow-config.md +75 -10
  11. package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
  12. package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
  13. package/.claude/agents/agent-ivan-monolith.md +154 -0
  14. package/.claude/agents/agent-kenji-data-reader.md +15 -8
  15. package/.claude/agents/agent-lars-code-inspector.md +56 -8
  16. package/.claude/agents/agent-marco-mockup-builder.md +110 -0
  17. package/.claude/agents/agent-marcus-api-documenter.md +323 -0
  18. package/.claude/agents/agent-marketplace-publisher.md +232 -72
  19. package/.claude/agents/agent-marketplace-reviewer.md +255 -79
  20. package/.claude/agents/agent-permissions-handler.md +208 -0
  21. package/.claude/agents/agent-simple-writer.md +48 -0
  22. package/.claude/agents/agent-svetlana-code-review.md +127 -14
  23. package/.claude/agents/agent-tanya-test-runner.md +333 -0
  24. package/.claude/agents/agent-ui-designer.md +100 -0
  25. package/.claude/agents/agent-viktor-sql-insights.md +19 -6
  26. package/.claude/agents/agent-web-search.md +55 -0
  27. package/.claude/agents/agent-yevgeni-discussions.md +7 -1
  28. package/.claude/agents/agent-zara-zapier.md +159 -0
  29. package/.claude/commands/app-squad.md +135 -0
  30. package/.claude/commands/audit-squad.md +158 -0
  31. package/.claude/commands/autoplan.md +563 -0
  32. package/.claude/commands/cleanup-squad.md +98 -0
  33. package/.claude/commands/config-squad.md +106 -0
  34. package/.claude/commands/crud-squad.md +87 -0
  35. package/.claude/commands/data-squad.md +97 -0
  36. package/.claude/commands/debug-squad.md +303 -0
  37. package/.claude/commands/doc-squad.md +65 -0
  38. package/.claude/commands/handoff.md +137 -0
  39. package/.claude/commands/health.md +49 -0
  40. package/.claude/commands/help.md +2 -1
  41. package/.claude/commands/help:agents.md +96 -16
  42. package/.claude/commands/help:commands.md +55 -11
  43. package/.claude/commands/help:faq.md +16 -1
  44. package/.claude/commands/help:skills.md +93 -0
  45. package/.claude/commands/hotfix-squad.md +112 -0
  46. package/.claude/commands/integration-squad.md +82 -0
  47. package/.claude/commands/janitor-squad.md +167 -0
  48. package/.claude/commands/learn-auto.md +120 -0
  49. package/.claude/commands/learn.md +120 -0
  50. package/.claude/commands/mcp-list.md +27 -0
  51. package/.claude/commands/onboard-squad.md +140 -0
  52. package/.claude/commands/plan-workspace.md +732 -0
  53. package/.claude/commands/prd.md +131 -0
  54. package/.claude/commands/project-status.md +82 -0
  55. package/.claude/commands/publish.md +138 -0
  56. package/.claude/commands/recap.md +69 -0
  57. package/.claude/commands/restore.md +64 -0
  58. package/.claude/commands/review-squad.md +152 -0
  59. package/.claude/commands/save.md +24 -0
  60. package/.claude/commands/stats.md +19 -0
  61. package/.claude/commands/swarm.md +210 -0
  62. package/.claude/commands/tool-builder.md +3 -1
  63. package/.claude/commands/ws-pull.md +1 -1
  64. package/.claude/commands/yolo-off.md +17 -0
  65. package/.claude/commands/yolo.md +82 -0
  66. package/.claude/hooks/_shared-memory.cjs +305 -0
  67. package/.claude/hooks/_utils.cjs +134 -0
  68. package/.claude/hooks/agent-failure-detector.cjs +164 -79
  69. package/.claude/hooks/agent-usage-logger.cjs +204 -0
  70. package/.claude/hooks/app-edit-guard.cjs +20 -4
  71. package/.claude/hooks/auto-learn.cjs +316 -0
  72. package/.claude/hooks/bash-guard.cjs +282 -0
  73. package/.claude/hooks/builder-mode-manager.cjs +183 -54
  74. package/.claude/hooks/bulk-activity-guard.cjs +283 -0
  75. package/.claude/hooks/context-watchdog.cjs +292 -0
  76. package/.claude/hooks/delegation-reminder.cjs +478 -0
  77. package/.claude/hooks/design-system-lint.cjs +283 -0
  78. package/.claude/hooks/post-scaffold-hook.cjs +16 -3
  79. package/.claude/hooks/prompt-guard.cjs +366 -0
  80. package/.claude/hooks/publish-template-guard.cjs +16 -0
  81. package/.claude/hooks/session-start.cjs +35 -0
  82. package/.claude/hooks/shared-memory-writer.cjs +147 -0
  83. package/.claude/hooks/skill-injector.cjs +140 -0
  84. package/.claude/hooks/skill-usage-logger.cjs +258 -0
  85. package/.claude/hooks/src-edit-guard.cjs +16 -1
  86. package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
  87. package/.claude/scripts/yolo-toggle.cjs +142 -0
  88. package/.claude/settings.json +141 -14
  89. package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
  90. package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
  91. package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
  92. package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
  93. package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
  94. package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
  95. package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
  96. package/.claude/skills/agent-structure/SKILL.md +98 -0
  97. package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
  98. package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
  99. package/.claude/skills/delegation-routing/SKILL.md +202 -0
  100. package/.claude/skills/frontend-design/SKILL.md +254 -0
  101. package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
  102. package/.claude/skills/hailer-api-client/SKILL.md +518 -0
  103. package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
  104. package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
  105. package/.claude/skills/hailer-design-system/SKILL.md +235 -0
  106. package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
  107. package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
  108. package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
  109. package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
  110. package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
  111. package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
  112. package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
  113. package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
  114. package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
  115. package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
  116. package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
  117. package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
  118. package/.claude/skills/integration-patterns/SKILL.md +421 -0
  119. package/.claude/skills/json-only-output/SKILL.md +52 -12
  120. package/.claude/skills/lsp-setup/SKILL.md +160 -0
  121. package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
  122. package/.claude/skills/optional-parameters/SKILL.md +32 -23
  123. package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
  124. package/.claude/skills/testing-patterns/SKILL.md +630 -0
  125. package/.claude/skills/tool-builder/SKILL.md +250 -0
  126. package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
  127. package/.claude/skills/tool-response-verification/SKILL.md +82 -48
  128. package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
  129. package/.env.example +26 -7
  130. package/CLAUDE.md +290 -224
  131. package/dist/CLAUDE.md +370 -0
  132. package/dist/app.d.ts +1 -1
  133. package/dist/app.js +101 -101
  134. package/dist/bot/bot-config.d.ts +26 -0
  135. package/dist/bot/bot-config.js +135 -0
  136. package/dist/bot/bot-manager.d.ts +40 -0
  137. package/dist/bot/bot-manager.js +137 -0
  138. package/dist/bot/bot.d.ts +127 -0
  139. package/dist/bot/bot.js +1328 -0
  140. package/dist/bot/operation-logger.d.ts +28 -0
  141. package/dist/bot/operation-logger.js +132 -0
  142. package/dist/bot/services/conversation-manager.d.ts +60 -0
  143. package/dist/bot/services/conversation-manager.js +246 -0
  144. package/dist/bot/services/index.d.ts +9 -0
  145. package/dist/bot/services/index.js +18 -0
  146. package/dist/bot/services/message-classifier.d.ts +42 -0
  147. package/dist/bot/services/message-classifier.js +228 -0
  148. package/dist/bot/services/message-formatter.d.ts +88 -0
  149. package/dist/bot/services/message-formatter.js +411 -0
  150. package/dist/bot/services/session-logger.d.ts +162 -0
  151. package/dist/bot/services/session-logger.js +724 -0
  152. package/dist/bot/services/token-billing.d.ts +78 -0
  153. package/dist/bot/services/token-billing.js +233 -0
  154. package/dist/bot/services/types.d.ts +169 -0
  155. package/dist/bot/services/types.js +12 -0
  156. package/dist/bot/services/typing-indicator.d.ts +23 -0
  157. package/dist/bot/services/typing-indicator.js +60 -0
  158. package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
  159. package/dist/bot/services/workspace-schema-cache.js +506 -0
  160. package/dist/bot/tool-executor.d.ts +28 -0
  161. package/dist/bot/tool-executor.js +48 -0
  162. package/dist/bot/workspace-overview.d.ts +12 -0
  163. package/dist/bot/workspace-overview.js +94 -0
  164. package/dist/cli.d.ts +1 -8
  165. package/dist/cli.js +1 -253
  166. package/dist/config.d.ts +96 -3
  167. package/dist/config.js +148 -37
  168. package/dist/core.d.ts +5 -0
  169. package/dist/core.js +61 -8
  170. package/dist/lib/discussion-lock.d.ts +42 -0
  171. package/dist/lib/discussion-lock.js +110 -0
  172. package/dist/lib/logger.d.ts +0 -1
  173. package/dist/lib/logger.js +39 -23
  174. package/dist/lib/request-logger.d.ts +77 -0
  175. package/dist/lib/request-logger.js +147 -0
  176. package/dist/mcp/UserContextCache.js +16 -13
  177. package/dist/mcp/hailer-clients.js +18 -17
  178. package/dist/mcp/signal-handler.js +29 -13
  179. package/dist/mcp/tool-registry.d.ts +4 -15
  180. package/dist/mcp/tool-registry.js +94 -32
  181. package/dist/mcp/tools/activity.js +28 -69
  182. package/dist/mcp/tools/app-core.js +9 -4
  183. package/dist/mcp/tools/app-marketplace.js +22 -12
  184. package/dist/mcp/tools/app-member.js +5 -2
  185. package/dist/mcp/tools/app-scaffold.js +32 -18
  186. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  187. package/dist/mcp/tools/bot-config/constants.js +94 -0
  188. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  189. package/dist/mcp/tools/bot-config/core.js +2456 -0
  190. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  191. package/dist/mcp/tools/bot-config/index.js +59 -0
  192. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  193. package/dist/mcp/tools/bot-config/tools.js +15 -0
  194. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  195. package/dist/mcp/tools/bot-config/types.js +6 -0
  196. package/dist/mcp/tools/discussion.js +107 -77
  197. package/dist/mcp/tools/document.d.ts +11 -0
  198. package/dist/mcp/tools/document.js +741 -0
  199. package/dist/mcp/tools/file.js +5 -2
  200. package/dist/mcp/tools/insight.js +36 -12
  201. package/dist/mcp/tools/investigate.d.ts +9 -0
  202. package/dist/mcp/tools/investigate.js +254 -0
  203. package/dist/mcp/tools/user.d.ts +2 -4
  204. package/dist/mcp/tools/user.js +9 -50
  205. package/dist/mcp/tools/workflow.d.ts +1 -0
  206. package/dist/mcp/tools/workflow.js +164 -52
  207. package/dist/mcp/utils/hailer-api-client.js +26 -17
  208. package/dist/mcp/webhook-handler.d.ts +64 -3
  209. package/dist/mcp/webhook-handler.js +219 -9
  210. package/dist/mcp-server.d.ts +4 -0
  211. package/dist/mcp-server.js +237 -25
  212. package/dist/plugins/bug-fixer/index.d.ts +2 -0
  213. package/dist/plugins/bug-fixer/index.js +18 -0
  214. package/dist/plugins/bug-fixer/tools.d.ts +45 -0
  215. package/dist/plugins/bug-fixer/tools.js +1096 -0
  216. package/package.json +10 -10
  217. package/scripts/test-hal-tools.ts +154 -0
  218. package/.claude/agents/agent-nora-name-functions.md +0 -123
  219. package/.claude/assistant-knowledge.md +0 -23
  220. package/.claude/commands/install-plugin.md +0 -261
  221. package/.claude/commands/list-plugins.md +0 -42
  222. package/.claude/commands/marketplace-setup.md +0 -33
  223. package/.claude/commands/publish-plugin.md +0 -55
  224. package/.claude/commands/uninstall-plugin.md +0 -87
  225. package/.claude/hooks/interactive-mode.cjs +0 -87
  226. package/.claude/hooks/mcp-server-guard.cjs +0 -108
  227. package/.claude/skills/marketplace-publishing.md +0 -155
  228. package/dist/bot/chat-bot.d.ts +0 -31
  229. package/dist/bot/chat-bot.js +0 -357
  230. package/dist/mcp/tools/metrics.d.ts +0 -13
  231. package/dist/mcp/tools/metrics.js +0 -546
  232. package/dist/stdio-server.d.ts +0 -14
  233. package/dist/stdio-server.js +0 -114
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * <hook-name>context-watchdog</hook-name>
4
+ *
5
+ * <purpose>
6
+ * Auto-detect context exhaustion and trigger handoff before context is lost.
7
+ * Three layers:
8
+ * 1. PreCompact (auto) - escalating response by compaction count:
9
+ * 1st: inform (context still good after one compaction)
10
+ * 2nd: strongly urge handoff (summary-of-summary, quality degrades)
11
+ * 3rd+: yolo=auto-handoff, interactive=block until acknowledged
12
+ * 2. PostToolUse (Task) - count agent calls, warn at thresholds
13
+ * 3. Stop - if yolo + compact fired, block stop until handoff written
14
+ * </purpose>
15
+ *
16
+ * <triggers>
17
+ * - PreCompact (matcher: auto)
18
+ * - PostToolUse (matcher: Task)
19
+ * - Stop (matcher: "")
20
+ * </triggers>
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ // --- Config ---
27
+ const EARLY_WARNING_CALLS = 20; // Soft heads-up
28
+ const URGENT_WARNING_CALLS = 35; // Strong warning
29
+ const MAX_STOP_BLOCKS = 2; // Prevent infinite stop-block loops
30
+
31
+ // --- Helpers ---
32
+
33
+ function ordinal(n) {
34
+ const s = ['th', 'st', 'nd', 'rd'];
35
+ const v = n % 100;
36
+ return n + (s[(v - 20) % 10] || s[v] || s[0]);
37
+ }
38
+
39
+ function getProjectRoot() {
40
+ let dir = process.cwd();
41
+ while (dir !== '/' && !fs.existsSync(path.join(dir, '.claude'))) {
42
+ dir = path.dirname(dir);
43
+ }
44
+ return dir;
45
+ }
46
+
47
+ function statePath(projectRoot) {
48
+ return path.join(projectRoot, '.claude', '.context-watchdog.json');
49
+ }
50
+
51
+ function loadState(projectRoot, sessionId) {
52
+ const fp = statePath(projectRoot);
53
+ try {
54
+ if (fs.existsSync(fp)) {
55
+ const state = JSON.parse(fs.readFileSync(fp, 'utf8'));
56
+ if (state.sessionId === sessionId) return state;
57
+ }
58
+ } catch {}
59
+ // New session or corrupted state - reset
60
+ return {
61
+ sessionId,
62
+ toolCalls: 0,
63
+ compactCount: 0,
64
+ lastWarning: 'none', // none | early | urgent | compact
65
+ stopBlocks: 0,
66
+ };
67
+ }
68
+
69
+ function saveState(projectRoot, state) {
70
+ try {
71
+ fs.writeFileSync(statePath(projectRoot), JSON.stringify(state));
72
+ } catch {}
73
+ }
74
+
75
+ function isYoloMode(projectRoot) {
76
+ try {
77
+ const statePath = path.join(projectRoot, '.claude', 'yolo-state.json');
78
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
79
+ return state.mode === 'yolo';
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ function handoffRecentlyWritten(projectRoot) {
86
+ const fp = path.join(projectRoot, 'SESSION-HANDOFF.md');
87
+ try {
88
+ if (fs.existsSync(fp)) {
89
+ const age = Date.now() - fs.statSync(fp).mtimeMs;
90
+ return age < 120_000; // Written within last 2 minutes
91
+ }
92
+ } catch {}
93
+ return false;
94
+ }
95
+
96
+ // Output helpers - schema differs by hook event type
97
+ let _hookEvent = null; // Set during processHook
98
+
99
+ function allow(message) {
100
+ if (_hookEvent === 'Stop') {
101
+ // Stop schema: { continue?, stopReason? }
102
+ console.log(JSON.stringify({}));
103
+ } else if (_hookEvent === 'PreCompact') {
104
+ // PreCompact: just output empty or with message
105
+ if (message) {
106
+ console.log(JSON.stringify({ additionalContext: message }));
107
+ } else {
108
+ console.log(JSON.stringify({}));
109
+ }
110
+ } else {
111
+ // PreToolUse/PostToolUse schema
112
+ const out = { decision: 'allow' };
113
+ if (message) out.message = message;
114
+ console.log(JSON.stringify(out));
115
+ }
116
+ process.exit(0);
117
+ }
118
+
119
+ function block(reason) {
120
+ if (_hookEvent === 'Stop') {
121
+ // Stop schema: continue=true means "don't stop, keep going"
122
+ console.log(JSON.stringify({ continue: true, stopReason: reason }));
123
+ } else {
124
+ console.log(JSON.stringify({ decision: 'block', reason }));
125
+ }
126
+ process.exit(0);
127
+ }
128
+
129
+ // --- Main ---
130
+
131
+ let input = '';
132
+ process.stdin.setEncoding('utf8');
133
+ process.stdin.on('data', chunk => input += chunk);
134
+ process.stdin.on('end', () => {
135
+ try {
136
+ processHook(JSON.parse(input));
137
+ } catch {
138
+ allow();
139
+ }
140
+ });
141
+
142
+ function processHook(data) {
143
+ const projectRoot = getProjectRoot();
144
+ const sessionId = data.session_id || '';
145
+ const state = loadState(projectRoot, sessionId);
146
+ const yolo = isYoloMode(projectRoot);
147
+ const event = data.hook_event_name;
148
+ _hookEvent = event; // Set for allow/block output formatting
149
+
150
+ // ─── Layer 1: PreCompact (auto) ────────────────────────
151
+ // Context IS full. Escalating response by compaction count.
152
+ if (event === 'PreCompact') {
153
+ state.compactCount++;
154
+ state.lastWarning = 'compact';
155
+ saveState(projectRoot, state);
156
+
157
+ // 1st compaction: inform — context is still usable
158
+ if (state.compactCount === 1) {
159
+ if (yolo) {
160
+ allow(
161
+ `📋 Context compacting (1st time). Still usable.\n\n` +
162
+ `Save progress soon:\n` +
163
+ `1. Update DEVELOPMENT.md with current progress\n` +
164
+ `2. Write SESSION-HANDOFF.md\n` +
165
+ `3. Run /save\n\n` +
166
+ `You can continue working after this compaction.`
167
+ );
168
+ } else {
169
+ allow(`📋 Context compacting (1st time). Context is still good — consider running /handoff soon to preserve state.`);
170
+ }
171
+ }
172
+
173
+ // 2nd compaction: strongly urge handoff — quality starts degrading
174
+ if (state.compactCount === 2) {
175
+ if (yolo) {
176
+ allow(
177
+ `⚠️ CONTEXT COMPACTING 2nd TIME — quality will degrade.\n\n` +
178
+ `You MUST save progress NOW before continuing:\n` +
179
+ `1. Update DEVELOPMENT.md with current progress\n` +
180
+ `2. Write SESSION-HANDOFF.md with full current state and next steps\n` +
181
+ `3. Run /save "Handoff: 2nd compaction"\n` +
182
+ `4. Tell user: "Context compacted twice. Recommend starting fresh with /recap."\n\n` +
183
+ `Do this BEFORE any other work. After handoff, you may continue but expect degraded recall.`
184
+ );
185
+ } else {
186
+ allow(
187
+ `⚠️ Context compacting for the 2nd time. Earlier details are now a summary-of-a-summary.\n\n` +
188
+ `Strongly recommended: run /handoff now and start a fresh session with /recap.\n` +
189
+ `Continuing risks: forgetting decisions, re-reading files, repeating mistakes.`
190
+ );
191
+ }
192
+ }
193
+
194
+ // 3rd+ compaction: force handoff
195
+ if (state.compactCount >= 3) {
196
+ if (yolo) {
197
+ allow(
198
+ `🛑 CONTEXT COMPACTING ${ordinal(state.compactCount)} TIME — session is degraded.\n\n` +
199
+ `IMMEDIATELY write handoff and stop:\n` +
200
+ `1. Update DEVELOPMENT.md with ALL current progress\n` +
201
+ `2. Write SESSION-HANDOFF.md with full state, pending tasks, and next steps\n` +
202
+ `3. Run /save "Auto-handoff: ${state.compactCount}x compacted"\n` +
203
+ `4. Tell user: "Context compacted ${state.compactCount} times. Start new conversation with /recap to continue."\n\n` +
204
+ `Do NOT continue working. Quality is too degraded.`
205
+ );
206
+ } else {
207
+ allow(
208
+ `🛑 Context compacting for the ${ordinal(state.compactCount)} time — session is significantly degraded.\n\n` +
209
+ `You should run /handoff and start fresh. Continuing will lead to:\n` +
210
+ `- Forgotten decisions and context\n` +
211
+ `- Re-reading files already read\n` +
212
+ `- Potential mistakes from lost context\n\n` +
213
+ `Please run /handoff now.`
214
+ );
215
+ }
216
+ }
217
+ }
218
+
219
+ // ─── Layer 2: PostToolUse (Task) - agent call counter ──
220
+ if (event === 'PostToolUse' && data.tool_name === 'Task') {
221
+ state.toolCalls++;
222
+ saveState(projectRoot, state);
223
+
224
+ // Skip warnings if compact already fired (more urgent message already sent)
225
+ if (state.lastWarning === 'compact') {
226
+ allow();
227
+ }
228
+
229
+ // Urgent warning
230
+ if (state.toolCalls === URGENT_WARNING_CALLS && state.lastWarning !== 'urgent') {
231
+ state.lastWarning = 'urgent';
232
+ saveState(projectRoot, state);
233
+
234
+ if (yolo) {
235
+ allow(
236
+ `⚠️ Context getting full: ${state.toolCalls} agent calls this session.\n\n` +
237
+ `Save progress NOW before auto-compaction hits:\n` +
238
+ `1. Update DEVELOPMENT.md with progress\n` +
239
+ `2. Write SESSION-HANDOFF.md\n` +
240
+ `3. Run /save\n` +
241
+ `Then continue working (or tell user to start fresh).`
242
+ );
243
+ } else {
244
+ allow(`📊 ${state.toolCalls} agent calls this session. Context may be getting full - consider /handoff.`);
245
+ }
246
+ }
247
+
248
+ // Early warning
249
+ if (state.toolCalls === EARLY_WARNING_CALLS && state.lastWarning === 'none') {
250
+ state.lastWarning = 'early';
251
+ saveState(projectRoot, state);
252
+
253
+ if (yolo) {
254
+ allow(`📊 Context check: ${state.toolCalls} agent calls. Consider saving progress with /save soon.`);
255
+ }
256
+ // Silent in non-yolo mode at early threshold
257
+ }
258
+
259
+ allow();
260
+ }
261
+
262
+ // ─── Layer 3: Stop - safety net ────────────────────────
263
+ // Block stop if context was heavily compacted and no handoff written.
264
+ if (event === 'Stop') {
265
+ // Prevent infinite loop: if stop hook already active, let it through
266
+ if (data.stop_hook_active) {
267
+ allow();
268
+ }
269
+
270
+ // After 2+ compactions (any mode), block stop until handoff is written
271
+ const shouldBlock = state.compactCount >= 2 && state.stopBlocks < MAX_STOP_BLOCKS;
272
+
273
+ if (shouldBlock && !handoffRecentlyWritten(projectRoot)) {
274
+ state.stopBlocks++;
275
+ saveState(projectRoot, state);
276
+ block(
277
+ `Context was compacted ${state.compactCount} time(s) but no handoff found.\n\n` +
278
+ `Before stopping, you MUST:\n` +
279
+ `1. Write SESSION-HANDOFF.md with current state and next steps\n` +
280
+ `2. Update DEVELOPMENT.md with progress\n` +
281
+ `3. Run /save "Handoff: ${state.compactCount}x compacted"\n` +
282
+ `4. Tell user: "Session degraded after ${state.compactCount} compactions. Start fresh with /recap."`
283
+ );
284
+ }
285
+
286
+ allow();
287
+ }
288
+
289
+ // Default - unknown event, output safe empty JSON
290
+ console.log(JSON.stringify({}));
291
+ process.exit(0);
292
+ }