@danya-ai/cli 0.1.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 (184) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +336 -0
  3. package/cli-acp.js +82 -0
  4. package/cli.js +105 -0
  5. package/dist/REPL-EYUOXCEC.js +42 -0
  6. package/dist/REPL-EYUOXCEC.js.map +7 -0
  7. package/dist/acp-S5WNCLMD.js +1372 -0
  8. package/dist/acp-S5WNCLMD.js.map +7 -0
  9. package/dist/agentsValidate-RQ2QDGNY.js +373 -0
  10. package/dist/agentsValidate-RQ2QDGNY.js.map +7 -0
  11. package/dist/ask-TX526UBD.js +129 -0
  12. package/dist/ask-TX526UBD.js.map +7 -0
  13. package/dist/autoUpdater-63RAZ24N.js +17 -0
  14. package/dist/autoUpdater-63RAZ24N.js.map +7 -0
  15. package/dist/chunk-2VQWLLDU.js +16 -0
  16. package/dist/chunk-2VQWLLDU.js.map +7 -0
  17. package/dist/chunk-4CLHMO4I.js +656 -0
  18. package/dist/chunk-4CLHMO4I.js.map +7 -0
  19. package/dist/chunk-4ZNNWJZU.js +5696 -0
  20. package/dist/chunk-4ZNNWJZU.js.map +7 -0
  21. package/dist/chunk-66EZC7Y7.js +149 -0
  22. package/dist/chunk-66EZC7Y7.js.map +7 -0
  23. package/dist/chunk-6EPQRP3S.js +96 -0
  24. package/dist/chunk-6EPQRP3S.js.map +7 -0
  25. package/dist/chunk-77IRSDFR.js +195 -0
  26. package/dist/chunk-77IRSDFR.js.map +7 -0
  27. package/dist/chunk-7RZNLBEK.js +136 -0
  28. package/dist/chunk-7RZNLBEK.js.map +7 -0
  29. package/dist/chunk-BNBV2FXC.js +19 -0
  30. package/dist/chunk-BNBV2FXC.js.map +7 -0
  31. package/dist/chunk-CQCREBDO.js +248 -0
  32. package/dist/chunk-CQCREBDO.js.map +7 -0
  33. package/dist/chunk-D77XS6TB.js +74 -0
  34. package/dist/chunk-D77XS6TB.js.map +7 -0
  35. package/dist/chunk-DHYBJN3V.js +474 -0
  36. package/dist/chunk-DHYBJN3V.js.map +7 -0
  37. package/dist/chunk-DLSLSLTR.js +842 -0
  38. package/dist/chunk-DLSLSLTR.js.map +7 -0
  39. package/dist/chunk-ELAE6Z4H.js +514 -0
  40. package/dist/chunk-ELAE6Z4H.js.map +7 -0
  41. package/dist/chunk-ELZQD7ZR.js +531 -0
  42. package/dist/chunk-ELZQD7ZR.js.map +7 -0
  43. package/dist/chunk-F6DEGMX6.js +31269 -0
  44. package/dist/chunk-F6DEGMX6.js.map +7 -0
  45. package/dist/chunk-GDF2AON2.js +124 -0
  46. package/dist/chunk-GDF2AON2.js.map +7 -0
  47. package/dist/chunk-H7BGBV4P.js +498 -0
  48. package/dist/chunk-H7BGBV4P.js.map +7 -0
  49. package/dist/chunk-HIIHGKXP.js +24 -0
  50. package/dist/chunk-HIIHGKXP.js.map +7 -0
  51. package/dist/chunk-HJCCXED7.js +17 -0
  52. package/dist/chunk-HJCCXED7.js.map +7 -0
  53. package/dist/chunk-IQ6VZB2Y.js +139 -0
  54. package/dist/chunk-IQ6VZB2Y.js.map +7 -0
  55. package/dist/chunk-J4D7AELD.js +518 -0
  56. package/dist/chunk-J4D7AELD.js.map +7 -0
  57. package/dist/chunk-JVGG2YQR.js +23 -0
  58. package/dist/chunk-JVGG2YQR.js.map +7 -0
  59. package/dist/chunk-LGEK2NV7.js +939 -0
  60. package/dist/chunk-LGEK2NV7.js.map +7 -0
  61. package/dist/chunk-LWXT5RGE.js +95 -0
  62. package/dist/chunk-LWXT5RGE.js.map +7 -0
  63. package/dist/chunk-M3TKNAUR.js +35 -0
  64. package/dist/chunk-M3TKNAUR.js.map +7 -0
  65. package/dist/chunk-MRFO7QO5.js +170 -0
  66. package/dist/chunk-MRFO7QO5.js.map +7 -0
  67. package/dist/chunk-MVN3DHQF.js +95 -0
  68. package/dist/chunk-MVN3DHQF.js.map +7 -0
  69. package/dist/chunk-O25PXGOC.js +772 -0
  70. package/dist/chunk-O25PXGOC.js.map +7 -0
  71. package/dist/chunk-OBGVKM3N.js +1618 -0
  72. package/dist/chunk-OBGVKM3N.js.map +7 -0
  73. package/dist/chunk-OV5HJXXQ.js +198 -0
  74. package/dist/chunk-OV5HJXXQ.js.map +7 -0
  75. package/dist/chunk-P5VWDMRD.js +249 -0
  76. package/dist/chunk-P5VWDMRD.js.map +7 -0
  77. package/dist/chunk-PDSAJX7G.js +49 -0
  78. package/dist/chunk-PDSAJX7G.js.map +7 -0
  79. package/dist/chunk-RHNEZOPO.js +739 -0
  80. package/dist/chunk-RHNEZOPO.js.map +7 -0
  81. package/dist/chunk-SQGAHZPM.js +3004 -0
  82. package/dist/chunk-SQGAHZPM.js.map +7 -0
  83. package/dist/chunk-U7Z4MXY4.js +21 -0
  84. package/dist/chunk-U7Z4MXY4.js.map +7 -0
  85. package/dist/chunk-UNCTVIS7.js +146 -0
  86. package/dist/chunk-UNCTVIS7.js.map +7 -0
  87. package/dist/chunk-VMEOI6MH.js +1103 -0
  88. package/dist/chunk-VMEOI6MH.js.map +7 -0
  89. package/dist/chunk-WAY3DKFO.js +47 -0
  90. package/dist/chunk-WAY3DKFO.js.map +7 -0
  91. package/dist/chunk-XEYEKVFT.js +24 -0
  92. package/dist/chunk-XEYEKVFT.js.map +7 -0
  93. package/dist/chunk-Y4BQ36T4.js +796 -0
  94. package/dist/chunk-Y4BQ36T4.js.map +7 -0
  95. package/dist/chunk-Y5LQPJWK.js +12 -0
  96. package/dist/chunk-Y5LQPJWK.js.map +7 -0
  97. package/dist/chunk-YIJWUNWF.js +1260 -0
  98. package/dist/chunk-YIJWUNWF.js.map +7 -0
  99. package/dist/chunk-YMIWYEZ7.js +34 -0
  100. package/dist/chunk-YMIWYEZ7.js.map +7 -0
  101. package/dist/cli-PQNZWJX4.js +3952 -0
  102. package/dist/cli-PQNZWJX4.js.map +7 -0
  103. package/dist/commands-HOBCZ3VQ.js +46 -0
  104. package/dist/commands-HOBCZ3VQ.js.map +7 -0
  105. package/dist/config-MLH7ZTFA.js +81 -0
  106. package/dist/config-MLH7ZTFA.js.map +7 -0
  107. package/dist/context-FZ6G4J63.js +30 -0
  108. package/dist/context-FZ6G4J63.js.map +7 -0
  109. package/dist/costTracker-5WKZXN5S.js +19 -0
  110. package/dist/costTracker-5WKZXN5S.js.map +7 -0
  111. package/dist/customCommands-EB4MMZSS.js +25 -0
  112. package/dist/customCommands-EB4MMZSS.js.map +7 -0
  113. package/dist/env-VMEIP4EW.js +28 -0
  114. package/dist/env-VMEIP4EW.js.map +7 -0
  115. package/dist/index.js +36 -0
  116. package/dist/index.js.map +7 -0
  117. package/dist/kodeAgentSessionId-WUT74FSH.js +16 -0
  118. package/dist/kodeAgentSessionId-WUT74FSH.js.map +7 -0
  119. package/dist/kodeAgentSessionLoad-KR4JSD6D.js +21 -0
  120. package/dist/kodeAgentSessionLoad-KR4JSD6D.js.map +7 -0
  121. package/dist/kodeAgentSessionResume-BCD6UV74.js +18 -0
  122. package/dist/kodeAgentSessionResume-BCD6UV74.js.map +7 -0
  123. package/dist/kodeAgentStreamJson-EDHHWNNX.js +15 -0
  124. package/dist/kodeAgentStreamJson-EDHHWNNX.js.map +7 -0
  125. package/dist/kodeAgentStreamJsonSession-G4RBNZRN.js +133 -0
  126. package/dist/kodeAgentStreamJsonSession-G4RBNZRN.js.map +7 -0
  127. package/dist/kodeAgentStructuredStdio-UA5P5UNU.js +11 -0
  128. package/dist/kodeAgentStructuredStdio-UA5P5UNU.js.map +7 -0
  129. package/dist/kodeHooks-EHM6GSIQ.js +37 -0
  130. package/dist/kodeHooks-EHM6GSIQ.js.map +7 -0
  131. package/dist/llm-SJXCV7DA.js +3138 -0
  132. package/dist/llm-SJXCV7DA.js.map +7 -0
  133. package/dist/llmLazy-2QYJVD6K.js +15 -0
  134. package/dist/llmLazy-2QYJVD6K.js.map +7 -0
  135. package/dist/loader-LJX77EFL.js +28 -0
  136. package/dist/loader-LJX77EFL.js.map +7 -0
  137. package/dist/mcp-DOROSLPN.js +49 -0
  138. package/dist/mcp-DOROSLPN.js.map +7 -0
  139. package/dist/mentionProcessor-5UZRHCGH.js +215 -0
  140. package/dist/mentionProcessor-5UZRHCGH.js.map +7 -0
  141. package/dist/messages-N5KBI53P.js +65 -0
  142. package/dist/messages-N5KBI53P.js.map +7 -0
  143. package/dist/model-HPLBR53R.js +30 -0
  144. package/dist/model-HPLBR53R.js.map +7 -0
  145. package/dist/openai-YP4OJYKF.js +29 -0
  146. package/dist/openai-YP4OJYKF.js.map +7 -0
  147. package/dist/outputStyles-NNALI5D7.js +28 -0
  148. package/dist/outputStyles-NNALI5D7.js.map +7 -0
  149. package/dist/package.json +4 -0
  150. package/dist/pluginRuntime-JYYI5BSQ.js +220 -0
  151. package/dist/pluginRuntime-JYYI5BSQ.js.map +7 -0
  152. package/dist/pluginValidation-JWUFPZUE.js +17 -0
  153. package/dist/pluginValidation-JWUFPZUE.js.map +7 -0
  154. package/dist/prompts-B2SS7CWI.js +50 -0
  155. package/dist/prompts-B2SS7CWI.js.map +7 -0
  156. package/dist/query-HIK457UU.js +50 -0
  157. package/dist/query-HIK457UU.js.map +7 -0
  158. package/dist/responsesStreaming-L2BSN37C.js +10 -0
  159. package/dist/responsesStreaming-L2BSN37C.js.map +7 -0
  160. package/dist/ripgrep-GCKI4UTL.js +17 -0
  161. package/dist/ripgrep-GCKI4UTL.js.map +7 -0
  162. package/dist/skillMarketplace-PCTUUX46.js +37 -0
  163. package/dist/skillMarketplace-PCTUUX46.js.map +7 -0
  164. package/dist/state-XJICGOUA.js +18 -0
  165. package/dist/state-XJICGOUA.js.map +7 -0
  166. package/dist/theme-DP7O4SGH.js +14 -0
  167. package/dist/theme-DP7O4SGH.js.map +7 -0
  168. package/dist/toolPermissionContext-DHAGUPEW.js +17 -0
  169. package/dist/toolPermissionContext-DHAGUPEW.js.map +7 -0
  170. package/dist/toolPermissionSettings-PT65MQIQ.js +18 -0
  171. package/dist/toolPermissionSettings-PT65MQIQ.js.map +7 -0
  172. package/dist/tools-BHW37PCF.js +47 -0
  173. package/dist/tools-BHW37PCF.js.map +7 -0
  174. package/dist/userInput-XDRYT5TI.js +316 -0
  175. package/dist/userInput-XDRYT5TI.js.map +7 -0
  176. package/dist/uuid-QUYJMIUV.js +9 -0
  177. package/dist/uuid-QUYJMIUV.js.map +7 -0
  178. package/dist/yoga.wasm +0 -0
  179. package/package.json +115 -0
  180. package/scripts/binary-utils.cjs +62 -0
  181. package/scripts/cli-acp-wrapper.cjs +82 -0
  182. package/scripts/cli-wrapper.cjs +105 -0
  183. package/scripts/postinstall.js +144 -0
  184. package/yoga.wasm +0 -0
@@ -0,0 +1,1260 @@
1
+ import { createRequire as __kodeCreateRequire } from "node:module";
2
+ const require = __kodeCreateRequire(import.meta.url);
3
+ import {
4
+ getDanyaAgentSessionId
5
+ } from "./chunk-U7Z4MXY4.js";
6
+ import {
7
+ loadSettingsWithLegacyFallback
8
+ } from "./chunk-66EZC7Y7.js";
9
+ import {
10
+ getSessionPlugins
11
+ } from "./chunk-2VQWLLDU.js";
12
+ import {
13
+ getCwd,
14
+ init_log,
15
+ init_state,
16
+ logError
17
+ } from "./chunk-SQGAHZPM.js";
18
+
19
+ // src/utils/session/kodeHooks.ts
20
+ init_log();
21
+ init_state();
22
+ import { spawn } from "child_process";
23
+ import { readFileSync, statSync } from "fs";
24
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
25
+ import { tmpdir } from "os";
26
+ import { join } from "path";
27
+ import { minimatch } from "minimatch";
28
+ var cache = /* @__PURE__ */ new Map();
29
+ var pluginHooksCache = /* @__PURE__ */ new Map();
30
+ var sessionStartCache = /* @__PURE__ */ new Map();
31
+ var HOOK_RUNTIME_STATE_KEY = "__danyaHookRuntimeState";
32
+ function getHookRuntimeState(toolUseContext) {
33
+ const existing = toolUseContext?.[HOOK_RUNTIME_STATE_KEY];
34
+ if (existing && typeof existing === "object" && Array.isArray(existing.queuedSystemMessages) && Array.isArray(existing.queuedAdditionalContexts)) {
35
+ return existing;
36
+ }
37
+ const created = {
38
+ transcriptPath: void 0,
39
+ queuedSystemMessages: [],
40
+ queuedAdditionalContexts: []
41
+ };
42
+ if (toolUseContext && typeof toolUseContext === "object") {
43
+ ;
44
+ toolUseContext[HOOK_RUNTIME_STATE_KEY] = created;
45
+ }
46
+ return created;
47
+ }
48
+ function updateHookTranscriptForMessages(toolUseContext, messages) {
49
+ const state = getHookRuntimeState(toolUseContext);
50
+ const sessionId = getDanyaAgentSessionId();
51
+ const dir = join(tmpdir(), "kode-hooks-transcripts");
52
+ try {
53
+ mkdirSync(dir, { recursive: true });
54
+ } catch {
55
+ }
56
+ if (!state.transcriptPath) {
57
+ state.transcriptPath = join(dir, `${sessionId}.transcript.txt`);
58
+ }
59
+ const lines = [];
60
+ for (const msg of Array.isArray(messages) ? messages : []) {
61
+ if (!msg || typeof msg !== "object") continue;
62
+ if (msg.type !== "user" && msg.type !== "assistant") continue;
63
+ if (msg.type === "user") {
64
+ const content2 = msg?.message?.content;
65
+ if (typeof content2 === "string") {
66
+ lines.push(`user: ${content2}`);
67
+ continue;
68
+ }
69
+ if (Array.isArray(content2)) {
70
+ const parts2 = [];
71
+ for (const block of content2) {
72
+ if (!block || typeof block !== "object") continue;
73
+ if (block.type === "text") parts2.push(String(block.text ?? ""));
74
+ if (block.type === "tool_result")
75
+ parts2.push(`[tool_result] ${String(block.content ?? "")}`);
76
+ }
77
+ lines.push(`user: ${parts2.join("")}`);
78
+ }
79
+ continue;
80
+ }
81
+ const content = msg?.message?.content;
82
+ if (typeof content === "string") {
83
+ lines.push(`assistant: ${content}`);
84
+ continue;
85
+ }
86
+ if (!Array.isArray(content)) continue;
87
+ const parts = [];
88
+ for (const block of content) {
89
+ if (!block || typeof block !== "object") continue;
90
+ if (block.type === "text") parts.push(String(block.text ?? ""));
91
+ if (block.type === "tool_use" || block.type === "server_tool_use") {
92
+ parts.push(
93
+ `[tool_use:${String(block.name ?? "")}] ${hookValueForPrompt(block.input)}`
94
+ );
95
+ }
96
+ if (block.type === "mcp_tool_use") {
97
+ parts.push(
98
+ `[mcp_tool_use:${String(block.name ?? "")}] ${hookValueForPrompt(block.input)}`
99
+ );
100
+ }
101
+ }
102
+ lines.push(`assistant: ${parts.join("")}`);
103
+ }
104
+ try {
105
+ writeFileSync(state.transcriptPath, lines.join("\n") + "\n", "utf8");
106
+ } catch {
107
+ }
108
+ }
109
+ function drainHookSystemPromptAdditions(toolUseContext) {
110
+ const state = getHookRuntimeState(toolUseContext);
111
+ const systemMessages = state.queuedSystemMessages.splice(
112
+ 0,
113
+ state.queuedSystemMessages.length
114
+ );
115
+ const contexts = state.queuedAdditionalContexts.splice(
116
+ 0,
117
+ state.queuedAdditionalContexts.length
118
+ );
119
+ const additions = [];
120
+ if (systemMessages.length > 0) {
121
+ additions.push(
122
+ ["\n# Hook system messages", ...systemMessages.map((m) => m.trim())].filter(Boolean).join("\n\n")
123
+ );
124
+ }
125
+ if (contexts.length > 0) {
126
+ additions.push(
127
+ ["\n# Hook additional context", ...contexts.map((m) => m.trim())].filter(Boolean).join("\n\n")
128
+ );
129
+ }
130
+ return additions;
131
+ }
132
+ function getHookTranscriptPath(toolUseContext) {
133
+ return getHookRuntimeState(toolUseContext).transcriptPath;
134
+ }
135
+ function queueHookSystemMessages(toolUseContext, messages) {
136
+ const state = getHookRuntimeState(toolUseContext);
137
+ for (const msg of messages) {
138
+ const trimmed = String(msg ?? "").trim();
139
+ if (trimmed) state.queuedSystemMessages.push(trimmed);
140
+ }
141
+ }
142
+ function queueHookAdditionalContexts(toolUseContext, contexts) {
143
+ const state = getHookRuntimeState(toolUseContext);
144
+ for (const ctx of contexts) {
145
+ const trimmed = String(ctx ?? "").trim();
146
+ if (trimmed) state.queuedAdditionalContexts.push(trimmed);
147
+ }
148
+ }
149
+ function isCommandHook(value) {
150
+ return value !== null && typeof value === "object" && value.type === "command" && typeof value.command === "string" && Boolean(value.command.trim());
151
+ }
152
+ function isPromptHook(value) {
153
+ return value !== null && typeof value === "object" && value.type === "prompt" && typeof value.prompt === "string" && Boolean(value.prompt.trim());
154
+ }
155
+ function isHook(value) {
156
+ return isCommandHook(value) || isPromptHook(value);
157
+ }
158
+ function parseHookMatchers(value) {
159
+ if (!Array.isArray(value)) return [];
160
+ const out = [];
161
+ for (const item of value) {
162
+ if (!item || typeof item !== "object") continue;
163
+ const matcher = typeof item.matcher === "string" ? item.matcher.trim() : "";
164
+ const effectiveMatcher = matcher || "*";
165
+ const hooksRaw = item.hooks;
166
+ const hooks = Array.isArray(hooksRaw) ? hooksRaw.filter(isHook) : [];
167
+ if (hooks.length === 0) continue;
168
+ out.push({ matcher: effectiveMatcher, hooks });
169
+ }
170
+ return out;
171
+ }
172
+ function parseHooksByEvent(rawHooks) {
173
+ if (!rawHooks || typeof rawHooks !== "object") return {};
174
+ const hooks = rawHooks;
175
+ return {
176
+ PreToolUse: parseHookMatchers(hooks.PreToolUse),
177
+ PostToolUse: parseHookMatchers(hooks.PostToolUse),
178
+ Stop: parseHookMatchers(hooks.Stop),
179
+ SubagentStop: parseHookMatchers(hooks.SubagentStop),
180
+ UserPromptSubmit: parseHookMatchers(hooks.UserPromptSubmit),
181
+ SessionStart: parseHookMatchers(hooks.SessionStart),
182
+ SessionEnd: parseHookMatchers(hooks.SessionEnd)
183
+ };
184
+ }
185
+ function loadInlinePluginHooksByEvent(plugin) {
186
+ const manifestHooks = plugin.manifest?.hooks;
187
+ if (!manifestHooks || typeof manifestHooks !== "object" || Array.isArray(manifestHooks))
188
+ return null;
189
+ const hookObj = manifestHooks.hooks && typeof manifestHooks.hooks === "object" && !Array.isArray(manifestHooks.hooks) ? manifestHooks.hooks : manifestHooks;
190
+ const cacheKey = `${plugin.manifestPath}#inlineHooks`;
191
+ try {
192
+ const stat = statSync(plugin.manifestPath);
193
+ const cached = pluginHooksCache.get(cacheKey);
194
+ if (cached && cached.mtimeMs === stat.mtimeMs) return cached.byEvent;
195
+ const byEvent = parseHooksByEvent(hookObj);
196
+ pluginHooksCache.set(cacheKey, { mtimeMs: stat.mtimeMs, byEvent });
197
+ return byEvent;
198
+ } catch (err) {
199
+ logError(err);
200
+ pluginHooksCache.delete(cacheKey);
201
+ return null;
202
+ }
203
+ }
204
+ function loadSettingsMatchers(projectDir, event) {
205
+ const loaded = loadSettingsWithLegacyFallback({
206
+ destination: "projectSettings",
207
+ projectDir,
208
+ migrateToPrimary: true
209
+ });
210
+ const settingsPath = loaded.usedPath;
211
+ if (!settingsPath) return [];
212
+ try {
213
+ const stat = statSync(settingsPath);
214
+ const cached = cache.get(settingsPath);
215
+ if (cached && cached.mtimeMs === stat.mtimeMs)
216
+ return cached.byEvent[event] ?? [];
217
+ const parsed = loaded.settings;
218
+ const byEvent = parseHooksByEvent(parsed?.hooks);
219
+ cache.set(settingsPath, { mtimeMs: stat.mtimeMs, byEvent });
220
+ return byEvent[event] ?? [];
221
+ } catch {
222
+ cache.delete(settingsPath);
223
+ return [];
224
+ }
225
+ }
226
+ function matcherMatchesTool(matcher, toolName) {
227
+ if (!matcher) return false;
228
+ if (matcher === "*" || matcher === "all") return true;
229
+ if (matcher === toolName) return true;
230
+ try {
231
+ if (minimatch(toolName, matcher, { dot: true, nocase: false })) return true;
232
+ } catch {
233
+ }
234
+ try {
235
+ if (new RegExp(matcher).test(toolName)) return true;
236
+ } catch {
237
+ }
238
+ return false;
239
+ }
240
+ function buildShellCommand(command) {
241
+ if (process.platform === "win32") {
242
+ return ["cmd.exe", "/d", "/s", "/c", command];
243
+ }
244
+ return ["/bin/sh", "-c", command];
245
+ }
246
+ async function runCommandHook(args) {
247
+ const cmd = buildShellCommand(args.command);
248
+ const proc = spawn(cmd[0], cmd.slice(1), {
249
+ cwd: args.cwd,
250
+ env: { ...process.env, ...args.env ?? {} },
251
+ stdio: ["pipe", "pipe", "pipe"],
252
+ windowsHide: true
253
+ });
254
+ let wasAborted = false;
255
+ const onAbort = () => {
256
+ wasAborted = true;
257
+ try {
258
+ proc.kill();
259
+ } catch {
260
+ }
261
+ };
262
+ if (args.signal) {
263
+ if (args.signal.aborted) onAbort();
264
+ args.signal.addEventListener("abort", onAbort, { once: true });
265
+ }
266
+ try {
267
+ const input = JSON.stringify(args.stdinJson);
268
+ try {
269
+ proc.stdin?.write(input);
270
+ proc.stdin?.end();
271
+ } catch {
272
+ }
273
+ let stdout = "";
274
+ let stderr = "";
275
+ const collect = (stream, append) => {
276
+ if (!stream) {
277
+ return { done: Promise.resolve(), cleanup: () => {
278
+ } };
279
+ }
280
+ try {
281
+ ;
282
+ stream.setEncoding?.("utf8");
283
+ } catch {
284
+ }
285
+ let resolveDone = null;
286
+ const done = new Promise((resolve) => {
287
+ resolveDone = resolve;
288
+ });
289
+ const finish = () => {
290
+ cleanup();
291
+ if (!resolveDone) return;
292
+ resolveDone();
293
+ resolveDone = null;
294
+ };
295
+ const onData = (chunk) => {
296
+ append(
297
+ typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk)
298
+ );
299
+ };
300
+ const onError = () => finish();
301
+ const cleanup = () => {
302
+ stream.off("data", onData);
303
+ stream.off("end", finish);
304
+ stream.off("close", finish);
305
+ stream.off("error", onError);
306
+ };
307
+ stream.on("data", onData);
308
+ stream.once("end", finish);
309
+ stream.once("close", finish);
310
+ stream.once("error", onError);
311
+ return { done, cleanup };
312
+ };
313
+ const stdoutCollector = collect(proc.stdout, (chunk) => {
314
+ stdout += chunk;
315
+ });
316
+ const stderrCollector = collect(proc.stderr, (chunk) => {
317
+ stderr += chunk;
318
+ });
319
+ const exitCode = await new Promise((resolve) => {
320
+ proc.once("exit", (code, signal) => {
321
+ if (typeof code === "number") return resolve(code);
322
+ if (signal) return resolve(143);
323
+ return resolve(0);
324
+ });
325
+ proc.once("error", () => resolve(1));
326
+ });
327
+ await Promise.race([
328
+ Promise.allSettled([stdoutCollector.done, stderrCollector.done]),
329
+ new Promise((resolve) => setTimeout(resolve, 250))
330
+ ]);
331
+ stdoutCollector.cleanup();
332
+ stderrCollector.cleanup();
333
+ return {
334
+ exitCode: wasAborted && exitCode === 0 ? 143 : exitCode,
335
+ stdout,
336
+ stderr
337
+ };
338
+ } finally {
339
+ if (args.signal) {
340
+ try {
341
+ args.signal.removeEventListener("abort", onAbort);
342
+ } catch {
343
+ }
344
+ }
345
+ }
346
+ }
347
+ function mergeAbortSignals(signals) {
348
+ const controller = new AbortController();
349
+ const onAbort = () => controller.abort();
350
+ const cleanups = [];
351
+ for (const signal of signals) {
352
+ if (!signal) continue;
353
+ if (signal.aborted) {
354
+ controller.abort();
355
+ continue;
356
+ }
357
+ signal.addEventListener("abort", onAbort, { once: true });
358
+ cleanups.push(() => {
359
+ try {
360
+ signal.removeEventListener("abort", onAbort);
361
+ } catch {
362
+ }
363
+ });
364
+ }
365
+ return {
366
+ signal: controller.signal,
367
+ cleanup: () => cleanups.forEach((fn) => fn())
368
+ };
369
+ }
370
+ function withHookTimeout(args) {
371
+ const timeoutMs = typeof args.timeoutSeconds === "number" && Number.isFinite(args.timeoutSeconds) ? Math.max(0, Math.floor(args.timeoutSeconds * 1e3)) : args.fallbackTimeoutMs;
372
+ const timeoutSignal = typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(timeoutMs) : (() => {
373
+ const controller = new AbortController();
374
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
375
+ const signal = controller.signal;
376
+ signal.__cleanup = () => clearTimeout(timer);
377
+ return signal;
378
+ })();
379
+ const merged = mergeAbortSignals([args.parentSignal, timeoutSignal]);
380
+ const timeoutCleanup = typeof timeoutSignal.__cleanup === "function" ? timeoutSignal.__cleanup : () => {
381
+ };
382
+ return {
383
+ signal: merged.signal,
384
+ cleanup: () => {
385
+ merged.cleanup();
386
+ timeoutCleanup();
387
+ }
388
+ };
389
+ }
390
+ function coerceHookMessage(stdout, stderr) {
391
+ const s = (stderr || "").trim();
392
+ if (s) return s;
393
+ const o = (stdout || "").trim();
394
+ if (o) return o;
395
+ return "Hook blocked the tool call.";
396
+ }
397
+ function coerceHookPermissionMode(mode) {
398
+ if (mode === "acceptEdits" || mode === "bypassPermissions") return "allow";
399
+ return "ask";
400
+ }
401
+ function extractFirstJsonObject(text) {
402
+ let start = -1;
403
+ let depth = 0;
404
+ let inString = false;
405
+ let escaped = false;
406
+ for (let i = 0; i < text.length; i++) {
407
+ const ch = text[i];
408
+ if (start === -1) {
409
+ if (ch === "{") {
410
+ start = i;
411
+ depth = 1;
412
+ }
413
+ continue;
414
+ }
415
+ if (inString) {
416
+ if (escaped) {
417
+ escaped = false;
418
+ continue;
419
+ }
420
+ if (ch === "\\") {
421
+ escaped = true;
422
+ continue;
423
+ }
424
+ if (ch === '"') {
425
+ inString = false;
426
+ }
427
+ continue;
428
+ }
429
+ if (ch === '"') {
430
+ inString = true;
431
+ continue;
432
+ }
433
+ if (ch === "{") {
434
+ depth++;
435
+ continue;
436
+ }
437
+ if (ch === "}") {
438
+ depth--;
439
+ if (depth === 0) return text.slice(start, i + 1);
440
+ }
441
+ }
442
+ return null;
443
+ }
444
+ function parseSessionStartAdditionalContext(stdout) {
445
+ const trimmed = String(stdout ?? "").trim();
446
+ if (!trimmed) return null;
447
+ const jsonStr = extractFirstJsonObject(trimmed) ?? trimmed;
448
+ try {
449
+ const parsed = JSON.parse(jsonStr);
450
+ const additional = parsed && typeof parsed === "object" && parsed.hookSpecificOutput && typeof parsed.hookSpecificOutput.additionalContext === "string" ? String(parsed.hookSpecificOutput.additionalContext) : null;
451
+ return additional && additional.trim() ? additional : null;
452
+ } catch {
453
+ return null;
454
+ }
455
+ }
456
+ function tryParseHookJson(stdout) {
457
+ const trimmed = String(stdout ?? "").trim();
458
+ if (!trimmed) return null;
459
+ const jsonStr = extractFirstJsonObject(trimmed) ?? trimmed;
460
+ try {
461
+ const parsed = JSON.parse(jsonStr);
462
+ return parsed && typeof parsed === "object" ? parsed : null;
463
+ } catch {
464
+ return null;
465
+ }
466
+ }
467
+ function normalizePermissionDecision(value) {
468
+ if (typeof value !== "string") return null;
469
+ const normalized = value.trim().toLowerCase();
470
+ if (normalized === "allow" || normalized === "approve") return "allow";
471
+ if (normalized === "deny" || normalized === "block") return "deny";
472
+ if (normalized === "ask") return "ask";
473
+ if (normalized === "passthrough" || normalized === "continue")
474
+ return "passthrough";
475
+ return null;
476
+ }
477
+ function normalizeStopDecision(value) {
478
+ if (typeof value !== "string") return null;
479
+ const normalized = value.trim().toLowerCase();
480
+ if (normalized === "approve" || normalized === "allow") return "approve";
481
+ if (normalized === "block" || normalized === "deny") return "block";
482
+ return null;
483
+ }
484
+ function hookValueForPrompt(value) {
485
+ if (value === null || value === void 0) return "";
486
+ if (typeof value === "string") return value;
487
+ try {
488
+ return JSON.stringify(value, null, 2);
489
+ } catch {
490
+ return String(value);
491
+ }
492
+ }
493
+ function interpolatePromptHookTemplate(template, hookInput) {
494
+ return String(template ?? "").replaceAll("$TOOL_INPUT", hookValueForPrompt(hookInput.tool_input)).replaceAll("$TOOL_RESULT", hookValueForPrompt(hookInput.tool_result)).replaceAll("$TOOL_RESPONSE", hookValueForPrompt(hookInput.tool_response)).replaceAll("$USER_PROMPT", hookValueForPrompt(hookInput.user_prompt)).replaceAll("$PROMPT", hookValueForPrompt(hookInput.prompt)).replaceAll("$REASON", hookValueForPrompt(hookInput.reason));
495
+ }
496
+ function extractAssistantText(message) {
497
+ const content = message?.message?.content;
498
+ if (typeof content === "string") return content;
499
+ if (!Array.isArray(content)) return "";
500
+ return content.filter((b) => b && typeof b === "object" && b.type === "text").map((b) => String(b.text ?? "")).join("");
501
+ }
502
+ async function runPromptHook(args) {
503
+ const { signal, cleanup } = withHookTimeout({
504
+ timeoutSeconds: args.hook.timeout,
505
+ parentSignal: args.parentSignal,
506
+ fallbackTimeoutMs: args.fallbackTimeoutMs
507
+ });
508
+ try {
509
+ const { queryQuick } = await import("./llmLazy-2QYJVD6K.js");
510
+ const systemPrompt = [
511
+ "You are executing a Danya prompt hook.",
512
+ "Return a single JSON object only (no markdown, no prose).",
513
+ `hook_event_name: ${args.hookEvent}`,
514
+ "Valid fields include:",
515
+ "- systemMessage: string",
516
+ '- decision: "approve" | "block" (Stop/SubagentStop only)',
517
+ "- reason: string (Stop/SubagentStop only)",
518
+ '- hookSpecificOutput.permissionDecision: "allow" | "deny" | "ask" | "passthrough" (PreToolUse only)',
519
+ "- hookSpecificOutput.updatedInput: object (PreToolUse only)",
520
+ "- hookSpecificOutput.additionalContext: string (SessionStart/any)"
521
+ ];
522
+ const promptText = interpolatePromptHookTemplate(
523
+ args.hook.prompt,
524
+ args.hookInput
525
+ );
526
+ const userPrompt = `${promptText}
527
+
528
+ # Hook input JSON
529
+ ${hookValueForPrompt(args.hookInput)}`;
530
+ const response = await queryQuick({
531
+ systemPrompt,
532
+ userPrompt,
533
+ signal
534
+ });
535
+ return { exitCode: 0, stdout: extractAssistantText(response), stderr: "" };
536
+ } catch (err) {
537
+ return {
538
+ exitCode: 1,
539
+ stdout: "",
540
+ stderr: err instanceof Error ? err.message : String(err)
541
+ };
542
+ } finally {
543
+ cleanup();
544
+ }
545
+ }
546
+ function applyEnvFileToProcessEnv(envFilePath) {
547
+ let raw;
548
+ try {
549
+ raw = readFileSync(envFilePath, "utf8");
550
+ } catch {
551
+ return;
552
+ }
553
+ const lines = raw.split(/\r?\n/);
554
+ for (const line of lines) {
555
+ const trimmed = line.trim();
556
+ if (!trimmed || trimmed.startsWith("#")) continue;
557
+ const withoutExport = trimmed.startsWith("export ") ? trimmed.slice("export ".length).trim() : trimmed;
558
+ const eq = withoutExport.indexOf("=");
559
+ if (eq <= 0) continue;
560
+ const key = withoutExport.slice(0, eq).trim();
561
+ let value = withoutExport.slice(eq + 1).trim();
562
+ if (!key) continue;
563
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
564
+ value = value.slice(1, -1);
565
+ }
566
+ process.env[key] = value;
567
+ }
568
+ }
569
+ function loadPluginMatchers(projectDir, event) {
570
+ const plugins = getSessionPlugins();
571
+ if (plugins.length === 0) return [];
572
+ const out = [];
573
+ for (const plugin of plugins) {
574
+ for (const hookPath of plugin.hooksFiles ?? []) {
575
+ try {
576
+ const stat = statSync(hookPath);
577
+ const cached = pluginHooksCache.get(hookPath);
578
+ if (cached && cached.mtimeMs === stat.mtimeMs) {
579
+ out.push(
580
+ ...(cached.byEvent[event] ?? []).map((m) => ({
581
+ matcher: m.matcher,
582
+ hooks: m.hooks.map((h) => ({ ...h, pluginRoot: plugin.rootDir }))
583
+ }))
584
+ );
585
+ continue;
586
+ }
587
+ const raw = readFileSync(hookPath, "utf8");
588
+ const parsed = JSON.parse(raw);
589
+ const hookObj = parsed && typeof parsed === "object" && parsed.hooks ? parsed.hooks : parsed;
590
+ const byEvent = parseHooksByEvent(hookObj);
591
+ pluginHooksCache.set(hookPath, { mtimeMs: stat.mtimeMs, byEvent });
592
+ out.push(
593
+ ...(byEvent[event] ?? []).map((m) => ({
594
+ matcher: m.matcher,
595
+ hooks: m.hooks.map((h) => ({ ...h, pluginRoot: plugin.rootDir }))
596
+ }))
597
+ );
598
+ } catch (err) {
599
+ logError(err);
600
+ continue;
601
+ }
602
+ }
603
+ const inlineByEvent = loadInlinePluginHooksByEvent(plugin);
604
+ if (inlineByEvent?.[event]) {
605
+ out.push(
606
+ ...(inlineByEvent[event] ?? []).map((m) => ({
607
+ matcher: m.matcher,
608
+ hooks: m.hooks.map((h) => ({ ...h, pluginRoot: plugin.rootDir }))
609
+ }))
610
+ );
611
+ }
612
+ }
613
+ return out;
614
+ }
615
+ function parseSessionStartHooks(value) {
616
+ if (!Array.isArray(value)) return [];
617
+ const out = [];
618
+ for (const item of value) {
619
+ if (!item || typeof item !== "object") continue;
620
+ const hooksRaw = item.hooks;
621
+ const hooks = Array.isArray(hooksRaw) ? hooksRaw.filter(isCommandHook) : [];
622
+ out.push(...hooks);
623
+ }
624
+ return out;
625
+ }
626
+ async function getSessionStartAdditionalContext(args) {
627
+ const sessionId = getDanyaAgentSessionId();
628
+ const cached = sessionStartCache.get(sessionId);
629
+ if (cached) return cached.additionalContext;
630
+ const projectDir = args?.cwd ?? getCwd();
631
+ const plugins = getSessionPlugins();
632
+ if (plugins.length === 0) {
633
+ sessionStartCache.set(sessionId, { additionalContext: "" });
634
+ return "";
635
+ }
636
+ const envFileDir = mkdtempSync(join(tmpdir(), "kode-env-"));
637
+ const envFilePath = join(envFileDir, `${sessionId}.env`);
638
+ try {
639
+ writeFileSync(envFilePath, "", "utf8");
640
+ } catch {
641
+ }
642
+ const additionalContexts = [];
643
+ try {
644
+ for (const plugin of plugins) {
645
+ for (const hookPath of plugin.hooksFiles ?? []) {
646
+ let hookObj;
647
+ try {
648
+ const raw = readFileSync(hookPath, "utf8");
649
+ const parsed = JSON.parse(raw);
650
+ hookObj = parsed && typeof parsed === "object" && parsed.hooks ? parsed.hooks : parsed;
651
+ } catch {
652
+ continue;
653
+ }
654
+ const hooks = parseSessionStartHooks(hookObj?.SessionStart).map((h) => ({
655
+ ...h,
656
+ pluginRoot: plugin.rootDir
657
+ }));
658
+ if (hooks.length === 0) continue;
659
+ for (const hook of hooks) {
660
+ const payload = {
661
+ session_id: sessionId,
662
+ cwd: projectDir,
663
+ hook_event_name: "SessionStart",
664
+ permission_mode: coerceHookPermissionMode(args?.permissionMode)
665
+ };
666
+ const result = await runCommandHook({
667
+ command: hook.command,
668
+ stdinJson: payload,
669
+ cwd: projectDir,
670
+ env: {
671
+ CLAUDE_PROJECT_DIR: projectDir,
672
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {},
673
+ CLAUDE_ENV_FILE: envFilePath
674
+ },
675
+ signal: args?.signal
676
+ });
677
+ if (result.exitCode !== 0) continue;
678
+ const injected = parseSessionStartAdditionalContext(result.stdout);
679
+ if (injected) additionalContexts.push(injected);
680
+ }
681
+ }
682
+ const inlineHooks = plugin.manifest?.hooks;
683
+ if (inlineHooks && typeof inlineHooks === "object" && !Array.isArray(inlineHooks)) {
684
+ const hookObj = inlineHooks.hooks && typeof inlineHooks.hooks === "object" && !Array.isArray(inlineHooks.hooks) ? inlineHooks.hooks : inlineHooks;
685
+ const hooks = parseSessionStartHooks(
686
+ hookObj?.SessionStart
687
+ ).map((h) => ({
688
+ ...h,
689
+ pluginRoot: plugin.rootDir
690
+ }));
691
+ if (hooks.length > 0) {
692
+ for (const hook of hooks) {
693
+ const payload = {
694
+ session_id: sessionId,
695
+ cwd: projectDir,
696
+ hook_event_name: "SessionStart",
697
+ permission_mode: coerceHookPermissionMode(args?.permissionMode)
698
+ };
699
+ const result = await runCommandHook({
700
+ command: hook.command,
701
+ stdinJson: payload,
702
+ cwd: projectDir,
703
+ env: {
704
+ CLAUDE_PROJECT_DIR: projectDir,
705
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {},
706
+ CLAUDE_ENV_FILE: envFilePath
707
+ },
708
+ signal: args?.signal
709
+ });
710
+ if (result.exitCode !== 0) continue;
711
+ const injected = parseSessionStartAdditionalContext(result.stdout);
712
+ if (injected) additionalContexts.push(injected);
713
+ }
714
+ }
715
+ }
716
+ }
717
+ } finally {
718
+ applyEnvFileToProcessEnv(envFilePath);
719
+ try {
720
+ rmSync(envFileDir, { recursive: true, force: true });
721
+ } catch {
722
+ }
723
+ }
724
+ const additionalContext = additionalContexts.filter(Boolean).join("\n\n");
725
+ sessionStartCache.set(sessionId, { additionalContext });
726
+ return additionalContext;
727
+ }
728
+ async function runPreToolUseHooks(args) {
729
+ const projectDir = args.cwd ?? getCwd();
730
+ const matchers = [
731
+ ...loadSettingsMatchers(projectDir, "PreToolUse"),
732
+ ...loadPluginMatchers(projectDir, "PreToolUse")
733
+ ];
734
+ if (matchers.length === 0) return { kind: "allow", warnings: [] };
735
+ const applicable = matchers.filter(
736
+ (m) => matcherMatchesTool(m.matcher, args.toolName)
737
+ );
738
+ if (applicable.length === 0) return { kind: "allow", warnings: [] };
739
+ const hookInput = {
740
+ session_id: getDanyaAgentSessionId(),
741
+ transcript_path: args.transcriptPath,
742
+ cwd: projectDir,
743
+ hook_event_name: "PreToolUse",
744
+ permission_mode: coerceHookPermissionMode(args.permissionMode),
745
+ tool_name: args.toolName,
746
+ tool_input: args.toolInput,
747
+ tool_use_id: args.toolUseId
748
+ };
749
+ const warnings = [];
750
+ const systemMessages = [];
751
+ const additionalContexts = [];
752
+ let mergedUpdatedInput;
753
+ let permissionDecision = null;
754
+ const executions = [];
755
+ for (const entry of applicable) {
756
+ for (const hook of entry.hooks) {
757
+ if (hook.type === "prompt") {
758
+ executions.push(
759
+ runPromptHook({
760
+ hook,
761
+ hookEvent: "PreToolUse",
762
+ hookInput,
763
+ safeMode: args.safeMode ?? false,
764
+ parentSignal: args.signal,
765
+ fallbackTimeoutMs: 3e4
766
+ }).then((result) => ({ hook, result }))
767
+ );
768
+ continue;
769
+ }
770
+ const { signal, cleanup } = withHookTimeout({
771
+ timeoutSeconds: hook.timeout,
772
+ parentSignal: args.signal,
773
+ fallbackTimeoutMs: 6e4
774
+ });
775
+ executions.push(
776
+ runCommandHook({
777
+ command: hook.command,
778
+ stdinJson: hookInput,
779
+ cwd: projectDir,
780
+ env: {
781
+ CLAUDE_PROJECT_DIR: projectDir,
782
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {}
783
+ },
784
+ signal
785
+ }).then((result) => ({ hook, result })).finally(cleanup)
786
+ );
787
+ }
788
+ }
789
+ const settled = await Promise.allSettled(executions);
790
+ for (const item of settled) {
791
+ if (item.status === "rejected") {
792
+ logError(item.reason);
793
+ warnings.push(`Hook failed to run: ${String(item.reason ?? "")}`);
794
+ continue;
795
+ }
796
+ const { hook, result } = item.value;
797
+ if (result.exitCode === 2) {
798
+ return {
799
+ kind: "block",
800
+ message: coerceHookMessage(result.stdout, result.stderr)
801
+ };
802
+ }
803
+ if (result.exitCode !== 0) {
804
+ warnings.push(coerceHookMessage(result.stdout, result.stderr));
805
+ continue;
806
+ }
807
+ const json = tryParseHookJson(result.stdout);
808
+ if (!json) continue;
809
+ if (typeof json.systemMessage === "string" && json.systemMessage.trim()) {
810
+ systemMessages.push(json.systemMessage.trim());
811
+ }
812
+ const additional = json.hookSpecificOutput && typeof json.hookSpecificOutput === "object" && typeof json.hookSpecificOutput.additionalContext === "string" ? String(json.hookSpecificOutput.additionalContext) : null;
813
+ if (additional && additional.trim()) {
814
+ additionalContexts.push(additional.trim());
815
+ }
816
+ const decision = normalizePermissionDecision(
817
+ json.hookSpecificOutput?.permissionDecision
818
+ );
819
+ if (decision === "deny") {
820
+ const msg = systemMessages.length > 0 ? systemMessages.join("\n\n") : coerceHookMessage(result.stdout, result.stderr);
821
+ return {
822
+ kind: "block",
823
+ message: msg,
824
+ systemMessages,
825
+ additionalContexts
826
+ };
827
+ }
828
+ if (decision === "ask") {
829
+ permissionDecision = "ask";
830
+ } else if (decision === "allow") {
831
+ if (!permissionDecision) permissionDecision = "allow";
832
+ }
833
+ const updated = json.hookSpecificOutput && typeof json.hookSpecificOutput === "object" && json.hookSpecificOutput.updatedInput && typeof json.hookSpecificOutput.updatedInput === "object" ? json.hookSpecificOutput.updatedInput : null;
834
+ if (updated) {
835
+ mergedUpdatedInput = { ...mergedUpdatedInput ?? {}, ...updated };
836
+ }
837
+ }
838
+ return {
839
+ kind: "allow",
840
+ warnings,
841
+ permissionDecision: permissionDecision === "allow" ? "allow" : permissionDecision === "ask" ? "ask" : void 0,
842
+ updatedInput: permissionDecision === "allow" ? mergedUpdatedInput : void 0,
843
+ systemMessages,
844
+ additionalContexts
845
+ };
846
+ }
847
+ async function runPostToolUseHooks(args) {
848
+ const projectDir = args.cwd ?? getCwd();
849
+ const matchers = [
850
+ ...loadSettingsMatchers(projectDir, "PostToolUse"),
851
+ ...loadPluginMatchers(projectDir, "PostToolUse")
852
+ ];
853
+ if (matchers.length === 0) {
854
+ return { warnings: [], systemMessages: [], additionalContexts: [] };
855
+ }
856
+ const applicable = matchers.filter(
857
+ (m) => matcherMatchesTool(m.matcher, args.toolName)
858
+ );
859
+ if (applicable.length === 0) {
860
+ return { warnings: [], systemMessages: [], additionalContexts: [] };
861
+ }
862
+ const hookInput = {
863
+ session_id: getDanyaAgentSessionId(),
864
+ transcript_path: args.transcriptPath,
865
+ cwd: projectDir,
866
+ hook_event_name: "PostToolUse",
867
+ permission_mode: coerceHookPermissionMode(args.permissionMode),
868
+ tool_name: args.toolName,
869
+ tool_input: args.toolInput,
870
+ tool_result: args.toolResult,
871
+ tool_response: args.toolResult,
872
+ tool_use_id: args.toolUseId
873
+ };
874
+ const warnings = [];
875
+ const systemMessages = [];
876
+ const additionalContexts = [];
877
+ const executions = [];
878
+ for (const entry of applicable) {
879
+ for (const hook of entry.hooks) {
880
+ if (hook.type === "prompt") {
881
+ executions.push(
882
+ runPromptHook({
883
+ hook,
884
+ hookEvent: "PostToolUse",
885
+ hookInput,
886
+ safeMode: args.safeMode ?? false,
887
+ parentSignal: args.signal,
888
+ fallbackTimeoutMs: 3e4
889
+ }).then((result) => ({ hook, result }))
890
+ );
891
+ continue;
892
+ }
893
+ const { signal, cleanup } = withHookTimeout({
894
+ timeoutSeconds: hook.timeout,
895
+ parentSignal: args.signal,
896
+ fallbackTimeoutMs: 6e4
897
+ });
898
+ executions.push(
899
+ runCommandHook({
900
+ command: hook.command,
901
+ stdinJson: hookInput,
902
+ cwd: projectDir,
903
+ env: {
904
+ CLAUDE_PROJECT_DIR: projectDir,
905
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {}
906
+ },
907
+ signal
908
+ }).then((result) => ({ hook, result })).finally(cleanup)
909
+ );
910
+ }
911
+ }
912
+ const settled = await Promise.allSettled(executions);
913
+ for (const item of settled) {
914
+ if (item.status === "rejected") {
915
+ logError(item.reason);
916
+ warnings.push(`Hook failed to run: ${String(item.reason ?? "")}`);
917
+ continue;
918
+ }
919
+ const { result } = item.value;
920
+ if (result.exitCode !== 0) {
921
+ warnings.push(coerceHookMessage(result.stdout, result.stderr));
922
+ continue;
923
+ }
924
+ const json = tryParseHookJson(result.stdout);
925
+ if (!json) continue;
926
+ if (typeof json.systemMessage === "string" && json.systemMessage.trim()) {
927
+ systemMessages.push(json.systemMessage.trim());
928
+ }
929
+ const additional = json.hookSpecificOutput && typeof json.hookSpecificOutput === "object" && typeof json.hookSpecificOutput.additionalContext === "string" ? String(json.hookSpecificOutput.additionalContext) : null;
930
+ if (additional && additional.trim()) {
931
+ additionalContexts.push(additional.trim());
932
+ }
933
+ }
934
+ return { warnings, systemMessages, additionalContexts };
935
+ }
936
+ async function runStopHooks(args) {
937
+ const projectDir = args.cwd ?? getCwd();
938
+ const matchers = [
939
+ ...loadSettingsMatchers(projectDir, args.hookEvent),
940
+ ...loadPluginMatchers(projectDir, args.hookEvent)
941
+ ];
942
+ if (matchers.length === 0) {
943
+ return {
944
+ decision: "approve",
945
+ warnings: [],
946
+ systemMessages: [],
947
+ additionalContexts: []
948
+ };
949
+ }
950
+ const applicable = matchers.filter((m) => matcherMatchesTool(m.matcher, "*"));
951
+ if (applicable.length === 0) {
952
+ return {
953
+ decision: "approve",
954
+ warnings: [],
955
+ systemMessages: [],
956
+ additionalContexts: []
957
+ };
958
+ }
959
+ const hookInput = {
960
+ session_id: getDanyaAgentSessionId(),
961
+ transcript_path: args.transcriptPath,
962
+ cwd: projectDir,
963
+ hook_event_name: args.hookEvent,
964
+ permission_mode: coerceHookPermissionMode(args.permissionMode),
965
+ reason: args.reason,
966
+ stop_hook_active: args.stopHookActive === true,
967
+ ...args.hookEvent === "SubagentStop" ? { agent_id: args.agentId, agent_transcript_path: args.transcriptPath } : {}
968
+ };
969
+ const warnings = [];
970
+ const systemMessages = [];
971
+ const additionalContexts = [];
972
+ const executions = [];
973
+ for (const entry of applicable) {
974
+ for (const hook of entry.hooks) {
975
+ if (hook.type === "prompt") {
976
+ executions.push(
977
+ runPromptHook({
978
+ hook,
979
+ hookEvent: args.hookEvent,
980
+ hookInput,
981
+ safeMode: args.safeMode ?? false,
982
+ parentSignal: args.signal,
983
+ fallbackTimeoutMs: 3e4
984
+ }).then((result) => ({ hook, result }))
985
+ );
986
+ continue;
987
+ }
988
+ const { signal, cleanup } = withHookTimeout({
989
+ timeoutSeconds: hook.timeout,
990
+ parentSignal: args.signal,
991
+ fallbackTimeoutMs: 6e4
992
+ });
993
+ executions.push(
994
+ runCommandHook({
995
+ command: hook.command,
996
+ stdinJson: hookInput,
997
+ cwd: projectDir,
998
+ env: {
999
+ CLAUDE_PROJECT_DIR: projectDir,
1000
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {}
1001
+ },
1002
+ signal
1003
+ }).then((result) => ({ hook, result })).finally(cleanup)
1004
+ );
1005
+ }
1006
+ }
1007
+ const settled = await Promise.allSettled(executions);
1008
+ for (const item of settled) {
1009
+ if (item.status === "rejected") {
1010
+ logError(item.reason);
1011
+ warnings.push(`Hook failed to run: ${String(item.reason ?? "")}`);
1012
+ continue;
1013
+ }
1014
+ const { result } = item.value;
1015
+ if (result.exitCode === 2) {
1016
+ return {
1017
+ decision: "block",
1018
+ message: coerceHookMessage(result.stdout, result.stderr),
1019
+ warnings,
1020
+ systemMessages,
1021
+ additionalContexts
1022
+ };
1023
+ }
1024
+ if (result.exitCode !== 0) {
1025
+ warnings.push(coerceHookMessage(result.stdout, result.stderr));
1026
+ continue;
1027
+ }
1028
+ const json = tryParseHookJson(result.stdout);
1029
+ if (!json) continue;
1030
+ if (typeof json.systemMessage === "string" && json.systemMessage.trim()) {
1031
+ systemMessages.push(json.systemMessage.trim());
1032
+ }
1033
+ const additional = json.hookSpecificOutput && typeof json.hookSpecificOutput === "object" && typeof json.hookSpecificOutput.additionalContext === "string" ? String(json.hookSpecificOutput.additionalContext) : null;
1034
+ if (additional && additional.trim()) {
1035
+ additionalContexts.push(additional.trim());
1036
+ }
1037
+ const stopDecision = normalizeStopDecision(json.decision);
1038
+ if (stopDecision === "block") {
1039
+ const reason = typeof json.reason === "string" && json.reason.trim() ? json.reason.trim() : null;
1040
+ const msg = reason || (systemMessages.length > 0 ? systemMessages.join("\n\n") : coerceHookMessage(result.stdout, result.stderr));
1041
+ return {
1042
+ decision: "block",
1043
+ message: msg,
1044
+ warnings,
1045
+ systemMessages,
1046
+ additionalContexts
1047
+ };
1048
+ }
1049
+ }
1050
+ return { decision: "approve", warnings, systemMessages, additionalContexts };
1051
+ }
1052
+ async function runUserPromptSubmitHooks(args) {
1053
+ const projectDir = args.cwd ?? getCwd();
1054
+ const matchers = [
1055
+ ...loadSettingsMatchers(projectDir, "UserPromptSubmit"),
1056
+ ...loadPluginMatchers(projectDir, "UserPromptSubmit")
1057
+ ];
1058
+ if (matchers.length === 0) {
1059
+ return {
1060
+ decision: "allow",
1061
+ warnings: [],
1062
+ systemMessages: [],
1063
+ additionalContexts: []
1064
+ };
1065
+ }
1066
+ const applicable = matchers.filter((m) => matcherMatchesTool(m.matcher, "*"));
1067
+ if (applicable.length === 0) {
1068
+ return {
1069
+ decision: "allow",
1070
+ warnings: [],
1071
+ systemMessages: [],
1072
+ additionalContexts: []
1073
+ };
1074
+ }
1075
+ const hookInput = {
1076
+ session_id: getDanyaAgentSessionId(),
1077
+ transcript_path: args.transcriptPath,
1078
+ cwd: projectDir,
1079
+ hook_event_name: "UserPromptSubmit",
1080
+ permission_mode: coerceHookPermissionMode(args.permissionMode),
1081
+ user_prompt: args.prompt,
1082
+ prompt: args.prompt
1083
+ };
1084
+ const warnings = [];
1085
+ const systemMessages = [];
1086
+ const additionalContexts = [];
1087
+ const executions = [];
1088
+ for (const entry of applicable) {
1089
+ for (const hook of entry.hooks) {
1090
+ if (hook.type === "prompt") {
1091
+ executions.push(
1092
+ runPromptHook({
1093
+ hook,
1094
+ hookEvent: "UserPromptSubmit",
1095
+ hookInput,
1096
+ safeMode: args.safeMode ?? false,
1097
+ parentSignal: args.signal,
1098
+ fallbackTimeoutMs: 3e4
1099
+ }).then((result) => ({ hook, result }))
1100
+ );
1101
+ continue;
1102
+ }
1103
+ const { signal, cleanup } = withHookTimeout({
1104
+ timeoutSeconds: hook.timeout,
1105
+ parentSignal: args.signal,
1106
+ fallbackTimeoutMs: 6e4
1107
+ });
1108
+ executions.push(
1109
+ runCommandHook({
1110
+ command: hook.command,
1111
+ stdinJson: hookInput,
1112
+ cwd: projectDir,
1113
+ env: {
1114
+ CLAUDE_PROJECT_DIR: projectDir,
1115
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {}
1116
+ },
1117
+ signal
1118
+ }).then((result) => ({ hook, result })).finally(cleanup)
1119
+ );
1120
+ }
1121
+ }
1122
+ const settled = await Promise.allSettled(executions);
1123
+ for (const item of settled) {
1124
+ if (item.status === "rejected") {
1125
+ logError(item.reason);
1126
+ warnings.push(`Hook failed to run: ${String(item.reason ?? "")}`);
1127
+ continue;
1128
+ }
1129
+ const { result } = item.value;
1130
+ if (result.exitCode === 2) {
1131
+ return {
1132
+ decision: "block",
1133
+ message: coerceHookMessage(result.stdout, result.stderr),
1134
+ warnings,
1135
+ systemMessages,
1136
+ additionalContexts
1137
+ };
1138
+ }
1139
+ if (result.exitCode !== 0) {
1140
+ warnings.push(coerceHookMessage(result.stdout, result.stderr));
1141
+ continue;
1142
+ }
1143
+ const json = tryParseHookJson(result.stdout);
1144
+ if (!json) continue;
1145
+ if (typeof json.systemMessage === "string" && json.systemMessage.trim()) {
1146
+ systemMessages.push(json.systemMessage.trim());
1147
+ }
1148
+ const additional = json.hookSpecificOutput && typeof json.hookSpecificOutput === "object" && typeof json.hookSpecificOutput.additionalContext === "string" ? String(json.hookSpecificOutput.additionalContext) : null;
1149
+ if (additional && additional.trim()) {
1150
+ additionalContexts.push(additional.trim());
1151
+ }
1152
+ const stopDecision = normalizeStopDecision(json.decision);
1153
+ if (stopDecision === "block") {
1154
+ const reason = typeof json.reason === "string" && json.reason.trim() ? json.reason.trim() : null;
1155
+ const msg = reason || (systemMessages.length > 0 ? systemMessages.join("\n\n") : coerceHookMessage(result.stdout, result.stderr));
1156
+ return {
1157
+ decision: "block",
1158
+ message: msg,
1159
+ warnings,
1160
+ systemMessages,
1161
+ additionalContexts
1162
+ };
1163
+ }
1164
+ }
1165
+ return { decision: "allow", warnings, systemMessages, additionalContexts };
1166
+ }
1167
+ async function runSessionEndHooks(args) {
1168
+ const projectDir = args.cwd ?? getCwd();
1169
+ const matchers = [
1170
+ ...loadSettingsMatchers(projectDir, "SessionEnd"),
1171
+ ...loadPluginMatchers(projectDir, "SessionEnd")
1172
+ ];
1173
+ if (matchers.length === 0) return { warnings: [], systemMessages: [] };
1174
+ const applicable = matchers.filter((m) => matcherMatchesTool(m.matcher, "*"));
1175
+ if (applicable.length === 0) return { warnings: [], systemMessages: [] };
1176
+ const hookInput = {
1177
+ session_id: getDanyaAgentSessionId(),
1178
+ transcript_path: args.transcriptPath,
1179
+ cwd: projectDir,
1180
+ hook_event_name: "SessionEnd",
1181
+ permission_mode: coerceHookPermissionMode(args.permissionMode),
1182
+ reason: args.reason
1183
+ };
1184
+ const warnings = [];
1185
+ const systemMessages = [];
1186
+ const executions = [];
1187
+ for (const entry of applicable) {
1188
+ for (const hook of entry.hooks) {
1189
+ if (hook.type === "prompt") {
1190
+ executions.push(
1191
+ runPromptHook({
1192
+ hook,
1193
+ hookEvent: "SessionEnd",
1194
+ hookInput,
1195
+ safeMode: args.safeMode ?? false,
1196
+ parentSignal: args.signal,
1197
+ fallbackTimeoutMs: 3e4
1198
+ }).then((result) => ({ hook, result }))
1199
+ );
1200
+ continue;
1201
+ }
1202
+ const { signal, cleanup } = withHookTimeout({
1203
+ timeoutSeconds: hook.timeout,
1204
+ parentSignal: args.signal,
1205
+ fallbackTimeoutMs: 6e4
1206
+ });
1207
+ executions.push(
1208
+ runCommandHook({
1209
+ command: hook.command,
1210
+ stdinJson: hookInput,
1211
+ cwd: projectDir,
1212
+ env: {
1213
+ CLAUDE_PROJECT_DIR: projectDir,
1214
+ ...hook.pluginRoot ? { CLAUDE_PLUGIN_ROOT: hook.pluginRoot } : {}
1215
+ },
1216
+ signal
1217
+ }).then((result) => ({ hook, result })).finally(cleanup)
1218
+ );
1219
+ }
1220
+ }
1221
+ const settled = await Promise.allSettled(executions);
1222
+ for (const item of settled) {
1223
+ if (item.status === "rejected") {
1224
+ logError(item.reason);
1225
+ warnings.push(`Hook failed to run: ${String(item.reason ?? "")}`);
1226
+ continue;
1227
+ }
1228
+ const { result } = item.value;
1229
+ if (result.exitCode !== 0) {
1230
+ warnings.push(coerceHookMessage(result.stdout, result.stderr));
1231
+ continue;
1232
+ }
1233
+ const json = tryParseHookJson(result.stdout);
1234
+ if (!json) continue;
1235
+ if (typeof json.systemMessage === "string" && json.systemMessage.trim()) {
1236
+ systemMessages.push(json.systemMessage.trim());
1237
+ }
1238
+ }
1239
+ return { warnings, systemMessages };
1240
+ }
1241
+ function __resetDanyaHooksCacheForTests() {
1242
+ cache.clear();
1243
+ pluginHooksCache.clear();
1244
+ sessionStartCache.clear();
1245
+ }
1246
+
1247
+ export {
1248
+ updateHookTranscriptForMessages,
1249
+ drainHookSystemPromptAdditions,
1250
+ getHookTranscriptPath,
1251
+ queueHookSystemMessages,
1252
+ queueHookAdditionalContexts,
1253
+ getSessionStartAdditionalContext,
1254
+ runPreToolUseHooks,
1255
+ runPostToolUseHooks,
1256
+ runStopHooks,
1257
+ runUserPromptSubmitHooks,
1258
+ runSessionEndHooks,
1259
+ __resetDanyaHooksCacheForTests
1260
+ };