failproofai 0.0.15 → 1.0.0-beta.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 (136) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  21. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  22. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
  28. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  36. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  38. package/.next/standalone/.next/server/app/index.html +1 -1
  39. package/.next/standalone/.next/server/app/index.rsc +14 -14
  40. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  41. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  42. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  43. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +9 -9
  44. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  46. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +9 -9
  49. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  55. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  56. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  59. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +3 -0
  62. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
  66. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +2 -2
  68. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__06b4trb._.js → [root-of-the-server]__0259h38._.js} +2 -2
  69. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +2 -2
  70. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +2 -2
  71. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +2 -2
  72. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +2 -2
  73. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +2 -2
  74. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +2 -2
  76. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zj-87g._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +2 -2
  78. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0hs_5mh.js → node_modules_html-to-image_es_index_1-upb14.js} +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
  84. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  85. package/.next/standalone/.next/server/pages/404.html +1 -1
  86. package/.next/standalone/.next/server/pages/500.html +1 -1
  87. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/server-reference-manifest.json +11 -11
  89. package/.next/standalone/.next/static/chunks/{1_v8b8mxhk25s.js → 0ip1n8xmgxarv.js} +1 -1
  90. package/.next/standalone/.next/static/chunks/{33rm-y-i4uzvv.js → 0u9hc6oxx-gpj.js} +1 -1
  91. package/.next/standalone/.next/static/chunks/{11qhtpk2cvoxv.js → 1d6ncspjagm53.js} +1 -1
  92. package/.next/standalone/.next/static/chunks/{2f4mxqkkoa_7d.js → 1grd7y94-igz9.js} +1 -1
  93. package/.next/standalone/.next/static/chunks/{3m8wmvlhsy1mx.js → 1s-_swlc80y43.js} +1 -1
  94. package/.next/standalone/.next/static/chunks/{0kf9j0pf_j9-0.js → 29k6ef49zppxj.js} +1 -1
  95. package/.next/standalone/.next/static/chunks/{2h7vw-ojgr9fz.js → 29ydr-8q0zis6.js} +1 -1
  96. package/.next/standalone/.next/static/chunks/{1-ng45379-ztb.js → 2vr7aazw6k2-n.js} +1 -1
  97. package/.next/standalone/.next/static/chunks/{2geh8s7d3ead-.js → 3wrgbih-516uj.js} +1 -1
  98. package/.next/standalone/Cargo.lock +428 -0
  99. package/.next/standalone/Cargo.toml +9 -0
  100. package/.next/standalone/crates/PROTOCOL.md +122 -0
  101. package/.next/standalone/crates/failproofaid/Cargo.toml +18 -0
  102. package/.next/standalone/crates/failproofaid/src/lock.rs +81 -0
  103. package/.next/standalone/crates/failproofaid/src/main.rs +83 -0
  104. package/.next/standalone/crates/failproofaid/src/paths.rs +172 -0
  105. package/.next/standalone/crates/failproofaid/src/server.rs +502 -0
  106. package/.next/standalone/crates/failproofaid/src/worker.rs +400 -0
  107. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +241 -0
  108. package/.next/standalone/crates/fpai-ipc/Cargo.toml +16 -0
  109. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +175 -0
  110. package/.next/standalone/crates/fpai-ipc/src/framing.rs +177 -0
  111. package/.next/standalone/crates/fpai-ipc/src/lib.rs +10 -0
  112. package/.next/standalone/crates/fpai-ipc/src/peer.rs +80 -0
  113. package/.next/standalone/package.json +5 -3
  114. package/.next/standalone/rust-toolchain.toml +4 -0
  115. package/.next/standalone/server.js +1 -1
  116. package/bin/failproofai-worker.mjs +51 -0
  117. package/bin/failproofai.mjs +68 -1
  118. package/bin/failproofaid-shim.mjs +67 -0
  119. package/dist/cli.mjs +1202 -624
  120. package/dist/worker.mjs +8441 -0
  121. package/package.json +5 -3
  122. package/src/hooks/builtin-policies.ts +37 -13
  123. package/src/hooks/configure-wizard.ts +73 -1
  124. package/src/hooks/daemon-client.ts +199 -0
  125. package/src/hooks/daemon-download.ts +152 -0
  126. package/src/hooks/daemon-service.ts +449 -0
  127. package/src/hooks/handler.ts +231 -195
  128. package/src/hooks/normalize-cli-payload.ts +61 -0
  129. package/src/hooks/policy-types.ts +15 -0
  130. package/src/hooks/read-stdin.ts +48 -0
  131. package/src/hooks/worker-server.ts +154 -0
  132. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1is3glr._.js +0 -3
  133. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0fopait._.js +0 -3
  134. /package/.next/standalone/.next/static/{ePKMmXXfXxThNsxwv1EiF → 7Lxtd352rSaHB-pmm6G8M}/_buildManifest.js +0 -0
  135. /package/.next/standalone/.next/static/{ePKMmXXfXxThNsxwv1EiF → 7Lxtd352rSaHB-pmm6G8M}/_clientMiddlewareManifest.js +0 -0
  136. /package/.next/standalone/.next/static/{ePKMmXXfXxThNsxwv1EiF → 7Lxtd352rSaHB-pmm6G8M}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -19,7 +19,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  // package.json
22
- var version2 = "0.0.15";
22
+ var version2 = "1.0.0-beta.0";
23
23
  var init_package = () => {};
24
24
 
25
25
  // src/posthog-key.ts
@@ -155,6 +155,383 @@ var init_telemetry_id = __esm(() => {
155
155
  ID_FILE = path.join(ID_DIR, "instance-id");
156
156
  });
157
157
 
158
+ // src/hooks/hook-logger.ts
159
+ import {
160
+ appendFileSync,
161
+ renameSync,
162
+ mkdirSync,
163
+ existsSync,
164
+ statSync
165
+ } from "node:fs";
166
+ import { join } from "node:path";
167
+ import { homedir } from "node:os";
168
+ function ensureResolved() {
169
+ if (resolved)
170
+ return;
171
+ resolved = true;
172
+ const rawLevel = (process.env.FAILPROOFAI_LOG_LEVEL ?? "").toLowerCase();
173
+ if (rawLevel === "info" || rawLevel === "warn" || rawLevel === "error") {
174
+ currentLevel = rawLevel;
175
+ }
176
+ const rawFile = (process.env.FAILPROOFAI_HOOK_LOG_FILE ?? "").trim();
177
+ if (rawFile) {
178
+ fileLoggingEnabled = true;
179
+ if (rawFile !== "1" && rawFile !== "true") {
180
+ logDir = rawFile;
181
+ }
182
+ }
183
+ }
184
+ function shouldEmit(level) {
185
+ ensureResolved();
186
+ return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];
187
+ }
188
+ function emitStderr(label, msg) {
189
+ process.stderr.write(`[failproofai:hook] ${label} ${msg}
190
+ `);
191
+ }
192
+ function ensureLogDir() {
193
+ if (!existsSync(logDir)) {
194
+ mkdirSync(logDir, { recursive: true });
195
+ }
196
+ }
197
+ function rotateIfNeeded(filePath) {
198
+ try {
199
+ const stats = statSync(filePath);
200
+ if (stats.size >= MAX_FILE_SIZE) {
201
+ const archiveName = `hooks-${Date.now()}.log`;
202
+ renameSync(filePath, join(logDir, archiveName));
203
+ }
204
+ } catch {}
205
+ }
206
+ function appendToFile(label, msg) {
207
+ if (!fileLoggingEnabled)
208
+ return;
209
+ try {
210
+ ensureLogDir();
211
+ const filePath = join(logDir, LOG_FILENAME);
212
+ rotateIfNeeded(filePath);
213
+ const timestamp = new Date().toISOString();
214
+ const line = `[${timestamp}] ${label} ${msg}
215
+ `;
216
+ appendFileSync(filePath, line, "utf-8");
217
+ } catch {}
218
+ }
219
+ function hookLogInfo(msg) {
220
+ if (!shouldEmit("info"))
221
+ return;
222
+ emitStderr("INFO", msg);
223
+ appendToFile("INFO", msg);
224
+ }
225
+ function hookLogWarn(msg) {
226
+ if (!shouldEmit("warn"))
227
+ return;
228
+ emitStderr("WARN", msg);
229
+ appendToFile("WARN", msg);
230
+ }
231
+ function hookLogError(msg) {
232
+ if (!shouldEmit("error"))
233
+ return;
234
+ emitStderr("ERROR", msg);
235
+ appendToFile("ERROR", msg);
236
+ }
237
+ var LEVEL_ORDER, MAX_FILE_SIZE, LOG_FILENAME = "hooks.log", DEFAULT_LOG_DIR, resolved = false, currentLevel = "warn", fileLoggingEnabled = false, logDir;
238
+ var init_hook_logger = __esm(() => {
239
+ LEVEL_ORDER = { info: 0, warn: 1, error: 2 };
240
+ MAX_FILE_SIZE = 512 * 1024;
241
+ DEFAULT_LOG_DIR = join(homedir(), ".failproofai", "logs");
242
+ logDir = DEFAULT_LOG_DIR;
243
+ });
244
+
245
+ // src/hooks/hooks-config.ts
246
+ import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
247
+ import { resolve, dirname } from "node:path";
248
+ import { homedir as homedir2 } from "node:os";
249
+ function configuredCustomPolicyPaths(config) {
250
+ return config.customPoliciesPaths ?? (config.customPoliciesPath ? [config.customPoliciesPath] : []);
251
+ }
252
+ function readConfigAt(path2) {
253
+ if (!existsSync2(path2))
254
+ return {};
255
+ try {
256
+ const raw = readFileSync(path2, "utf8");
257
+ return JSON.parse(raw);
258
+ } catch (err) {
259
+ hookLogWarn(`failed to parse config at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
260
+ return {};
261
+ }
262
+ }
263
+ function findProjectConfigDir(start) {
264
+ const home = homedir2();
265
+ let dir = resolve(start);
266
+ while (dir !== home) {
267
+ const marker = resolve(dir, ".failproofai");
268
+ try {
269
+ if (statSync2(marker).isDirectory())
270
+ return dir;
271
+ } catch {}
272
+ const parent = dirname(dir);
273
+ if (parent === dir)
274
+ break;
275
+ dir = parent;
276
+ }
277
+ return resolve(start);
278
+ }
279
+ function readMergedHooksConfig(cwd) {
280
+ const base = findProjectConfigDir(cwd ?? process.cwd());
281
+ const projectPath = resolve(base, ".failproofai", "policies-config.json");
282
+ const localPath = resolve(base, ".failproofai", "policies-config.local.json");
283
+ const globalPath = resolve(homedir2(), ".failproofai", "policies-config.json");
284
+ const project = readConfigAt(projectPath);
285
+ const local = readConfigAt(localPath);
286
+ const global_ = readConfigAt(globalPath);
287
+ const enabledSet = new Set([
288
+ ...project.enabledPolicies ?? [],
289
+ ...local.enabledPolicies ?? [],
290
+ ...global_.enabledPolicies ?? []
291
+ ]);
292
+ const mergedParams = {};
293
+ for (const scope of [project, local, global_]) {
294
+ if (!scope.policyParams)
295
+ continue;
296
+ for (const [policyName, params] of Object.entries(scope.policyParams)) {
297
+ if (!(policyName in mergedParams)) {
298
+ mergedParams[policyName] = params;
299
+ }
300
+ }
301
+ }
302
+ const customPoliciesPaths = [project, local, global_].map((scope) => scope.customPoliciesPaths !== undefined ? scope.customPoliciesPaths : scope.customPoliciesPath !== undefined ? [scope.customPoliciesPath] : undefined).find((paths) => paths !== undefined);
303
+ const disabledCustomPolicies = new Set([
304
+ ...project.disabledCustomPolicies ?? [],
305
+ ...local.disabledCustomPolicies ?? [],
306
+ ...global_.disabledCustomPolicies ?? []
307
+ ]);
308
+ const customPoliciesEnabled = project.customPoliciesEnabled ?? local.customPoliciesEnabled ?? global_.customPoliciesEnabled;
309
+ const llm = project.llm ?? local.llm ?? global_.llm;
310
+ return {
311
+ enabledPolicies: [...enabledSet],
312
+ ...Object.keys(mergedParams).length > 0 ? { policyParams: mergedParams } : {},
313
+ ...customPoliciesPaths !== undefined ? { customPoliciesPaths } : {},
314
+ ...disabledCustomPolicies.size ? { disabledCustomPolicies: [...disabledCustomPolicies] } : {},
315
+ ...customPoliciesEnabled !== undefined ? { customPoliciesEnabled } : {},
316
+ ...llm !== undefined ? { llm } : {}
317
+ };
318
+ }
319
+ function getConfigPath() {
320
+ return resolve(homedir2(), ".failproofai", "policies-config.json");
321
+ }
322
+ function readHooksConfig() {
323
+ const configPath = getConfigPath();
324
+ if (!existsSync2(configPath)) {
325
+ return { enabledPolicies: [] };
326
+ }
327
+ try {
328
+ const raw = readFileSync(configPath, "utf8");
329
+ return JSON.parse(raw);
330
+ } catch (err) {
331
+ hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
332
+ return { enabledPolicies: [] };
333
+ }
334
+ }
335
+ function getConfigPathForScope(scope, cwd) {
336
+ switch (scope) {
337
+ case "user":
338
+ return resolve(homedir2(), ".failproofai", "policies-config.json");
339
+ case "project":
340
+ return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.json");
341
+ case "local":
342
+ return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.local.json");
343
+ }
344
+ }
345
+ function readScopedHooksConfig(scope, cwd) {
346
+ const configPath = getConfigPathForScope(scope, cwd);
347
+ if (!existsSync2(configPath)) {
348
+ return { enabledPolicies: [] };
349
+ }
350
+ try {
351
+ const raw = readFileSync(configPath, "utf8");
352
+ return JSON.parse(raw);
353
+ } catch (err) {
354
+ hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
355
+ return { enabledPolicies: [] };
356
+ }
357
+ }
358
+ function writeScopedHooksConfig(config, scope, cwd) {
359
+ const configPath = getConfigPathForScope(scope, cwd);
360
+ const dir = dirname(configPath);
361
+ if (!existsSync2(dir)) {
362
+ mkdirSync2(dir, { recursive: true });
363
+ }
364
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + `
365
+ `, "utf8");
366
+ }
367
+ function syncConventionPolicies(entries, scope, cwd) {
368
+ const configPath = getConfigPathForScope(scope, cwd);
369
+ if (!existsSync2(configPath) && entries.length === 0)
370
+ return false;
371
+ if (existsSync2(configPath)) {
372
+ try {
373
+ JSON.parse(readFileSync(configPath, "utf8"));
374
+ } catch {
375
+ hookLogWarn(`not recording convention policies: ${configPath} is not valid JSON — ` + `fix the syntax error and re-run. Nothing was written.`);
376
+ return false;
377
+ }
378
+ }
379
+ const config = readScopedHooksConfig(scope, cwd);
380
+ const sorted = [...entries].sort((a, b) => a.file.localeCompare(b.file));
381
+ const next = { ...config };
382
+ if (sorted.length === 0)
383
+ delete next.conventionPolicies;
384
+ else
385
+ next.conventionPolicies = sorted;
386
+ if (JSON.stringify(next.conventionPolicies ?? null) === JSON.stringify(config.conventionPolicies ?? null)) {
387
+ return false;
388
+ }
389
+ writeScopedHooksConfig(next, scope, cwd);
390
+ return true;
391
+ }
392
+ var init_hooks_config = __esm(() => {
393
+ init_hook_logger();
394
+ });
395
+
396
+ // src/hooks/daemon-client.ts
397
+ var exports_daemon_client = {};
398
+ __export(exports_daemon_client, {
399
+ tryDaemonHook: () => tryDaemonHook,
400
+ isDaemonConfigured: () => isDaemonConfigured
401
+ });
402
+ import { createConnection } from "node:net";
403
+ import { resolve as resolve2 } from "node:path";
404
+ import { homedir as homedir3 } from "node:os";
405
+ function socketPath() {
406
+ if (process.env.FAILPROOFAI_DAEMON_SOCKET)
407
+ return process.env.FAILPROOFAI_DAEMON_SOCKET;
408
+ return resolve2(homedir3(), ".failproofai", "run", "failproofaid.sock");
409
+ }
410
+ function isDaemonConfigured() {
411
+ try {
412
+ return readHooksConfig().daemonConfigured === true;
413
+ } catch {
414
+ return false;
415
+ }
416
+ }
417
+ function encodeFrame(value) {
418
+ const body = Buffer.from(JSON.stringify(value), "utf8");
419
+ const header = Buffer.alloc(4);
420
+ header.writeUInt32BE(body.length, 0);
421
+ return Buffer.concat([header, body]);
422
+ }
423
+ async function tryDaemonHook(req) {
424
+ if (process.platform === "win32")
425
+ return null;
426
+ return new Promise((resolvePromise) => {
427
+ let settled = false;
428
+ let timer;
429
+ const arm = (ms) => {
430
+ clearTimeout(timer);
431
+ timer = setTimeout(() => finish(null), ms);
432
+ timer.unref?.();
433
+ };
434
+ const finish = (result) => {
435
+ if (settled)
436
+ return;
437
+ settled = true;
438
+ clearTimeout(timer);
439
+ socket.removeAllListeners();
440
+ socket.destroy();
441
+ resolvePromise(result);
442
+ };
443
+ const socket = createConnection({ path: socketPath() });
444
+ arm(DAEMON_CONNECT_TIMEOUT_MS);
445
+ let recvBuf = Buffer.alloc(0);
446
+ let declaredLen = null;
447
+ socket.on("connect", () => {
448
+ arm(DAEMON_RESPONSE_TIMEOUT_MS);
449
+ socket.write(encodeFrame({
450
+ type: "hook",
451
+ protocolVersion: PROTOCOL_VERSION,
452
+ hookEvent: req.hookEvent,
453
+ cli: req.cli,
454
+ stdin: req.stdin,
455
+ cwd: req.cwd
456
+ }));
457
+ });
458
+ socket.on("data", (chunk) => {
459
+ recvBuf = Buffer.concat([recvBuf, chunk]);
460
+ if (declaredLen === null) {
461
+ if (recvBuf.length < 4)
462
+ return;
463
+ declaredLen = recvBuf.readUInt32BE(0);
464
+ if (declaredLen > MAX_FRAME_LEN) {
465
+ finish(null);
466
+ return;
467
+ }
468
+ recvBuf = recvBuf.subarray(4);
469
+ }
470
+ if (recvBuf.length < declaredLen)
471
+ return;
472
+ const body = recvBuf.subarray(0, declaredLen);
473
+ let message;
474
+ try {
475
+ message = JSON.parse(body.toString("utf8"));
476
+ } catch {
477
+ finish(null);
478
+ return;
479
+ }
480
+ if (message.protocolVersion !== PROTOCOL_VERSION) {
481
+ finish(null);
482
+ return;
483
+ }
484
+ if (message.type === "hookResult" && typeof message.exitCode === "number" && typeof message.stdout === "string" && typeof message.stderr === "string") {
485
+ finish({ exitCode: message.exitCode, stdout: message.stdout, stderr: message.stderr });
486
+ return;
487
+ }
488
+ finish(null);
489
+ });
490
+ socket.on("error", () => finish(null));
491
+ socket.on("close", () => finish(null));
492
+ });
493
+ }
494
+ var PROTOCOL_VERSION = 1, DAEMON_CONNECT_TIMEOUT_MS = 150, DAEMON_RESPONSE_TIMEOUT_MS = 30000, MAX_FRAME_LEN;
495
+ var init_daemon_client = __esm(() => {
496
+ init_hooks_config();
497
+ MAX_FRAME_LEN = 16 * 1024 * 1024;
498
+ });
499
+
500
+ // src/hooks/read-stdin.ts
501
+ var exports_read_stdin = {};
502
+ __export(exports_read_stdin, {
503
+ readStdinPayload: () => readStdinPayload
504
+ });
505
+ async function readStdinPayload(maxBytes = 1048576) {
506
+ let payload = "";
507
+ let oversized = false;
508
+ let readError = false;
509
+ try {
510
+ payload = await new Promise((resolve3, reject) => {
511
+ const chunks = [];
512
+ let totalBytes = 0;
513
+ process.stdin.setEncoding("utf8");
514
+ process.stdin.on("data", (chunk) => {
515
+ totalBytes += Buffer.byteLength(chunk);
516
+ if (totalBytes > maxBytes) {
517
+ oversized = true;
518
+ process.stdin.destroy();
519
+ resolve3("");
520
+ return;
521
+ }
522
+ chunks.push(chunk);
523
+ });
524
+ process.stdin.on("end", () => resolve3(chunks.join("")));
525
+ process.stdin.on("error", reject);
526
+ if (process.stdin.readableEnded)
527
+ resolve3("");
528
+ });
529
+ } catch {
530
+ readError = true;
531
+ }
532
+ return { payload, oversized, readError };
533
+ }
534
+
158
535
  // src/hooks/types.ts
159
536
  var HOOK_SCOPES, INTEGRATION_TYPES, CODEX_HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_HOOK_SCOPES, HERMES_HOOK_EVENT_TYPES, HERMES_EVENT_MAP, COPILOT_HOOK_SCOPES, COPILOT_HOOK_EVENT_TYPES, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_HOOK_SCOPES, CURSOR_HOOK_EVENT_TYPES, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_HOOK_SCOPES, OPENCODE_HOOK_EVENT_TYPES, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_HOOK_SCOPES, PI_HOOK_EVENT_TYPES, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_HOOK_SCOPES, OPENCLAW_HOOK_EVENT_TYPES, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_HOOK_SCOPES, FACTORY_HOOK_EVENT_TYPES, FACTORY_TOOL_MAP, DEVIN_HOOK_SCOPES, DEVIN_HOOK_EVENT_TYPES, DEVIN_TOOL_MAP, ANTIGRAVITY_HOOK_SCOPES, ANTIGRAVITY_HOOK_EVENT_TYPES, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_HOOK_SCOPES, GOOSE_HOOK_EVENT_TYPES, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES, FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
160
537
  var init_types = __esm(() => {
@@ -578,243 +955,43 @@ var init_tool_name_canonicalize = __esm(() => {
578
955
  init_types();
579
956
  });
580
957
 
581
- // src/hooks/hook-logger.ts
582
- import {
583
- appendFileSync,
584
- renameSync,
585
- mkdirSync,
586
- existsSync,
587
- statSync
588
- } from "node:fs";
589
- import { join } from "node:path";
590
- import { homedir } from "node:os";
591
- function ensureResolved() {
592
- if (resolved)
593
- return;
594
- resolved = true;
595
- const rawLevel = (process.env.FAILPROOFAI_LOG_LEVEL ?? "").toLowerCase();
596
- if (rawLevel === "info" || rawLevel === "warn" || rawLevel === "error") {
597
- currentLevel = rawLevel;
598
- }
599
- const rawFile = (process.env.FAILPROOFAI_HOOK_LOG_FILE ?? "").trim();
600
- if (rawFile) {
601
- fileLoggingEnabled = true;
602
- if (rawFile !== "1" && rawFile !== "true") {
603
- logDir = rawFile;
958
+ // src/hooks/normalize-cli-payload.ts
959
+ function normalizeCliPayload(cli, parsed) {
960
+ if (cli === "antigravity") {
961
+ const tc = parsed.toolCall;
962
+ if (tc && typeof tc === "object") {
963
+ if (tc.name !== undefined)
964
+ parsed.tool_name = tc.name;
965
+ if (tc.args !== undefined)
966
+ parsed.tool_input = tc.args;
604
967
  }
605
- }
606
- }
607
- function shouldEmit(level) {
608
- ensureResolved();
609
- return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];
610
- }
611
- function emitStderr(label, msg) {
612
- process.stderr.write(`[failproofai:hook] ${label} ${msg}
613
- `);
614
- }
615
- function ensureLogDir() {
616
- if (!existsSync(logDir)) {
617
- mkdirSync(logDir, { recursive: true });
618
- }
619
- }
620
- function rotateIfNeeded(filePath) {
621
- try {
622
- const stats = statSync(filePath);
623
- if (stats.size >= MAX_FILE_SIZE) {
624
- const archiveName = `hooks-${Date.now()}.log`;
625
- renameSync(filePath, join(logDir, archiveName));
968
+ if (typeof parsed.conversationId === "string")
969
+ parsed.session_id = parsed.conversationId;
970
+ if (Array.isArray(parsed.workspacePaths) && typeof parsed.workspacePaths[0] === "string") {
971
+ parsed.cwd = parsed.workspacePaths[0];
626
972
  }
627
- } catch {}
628
- }
629
- function appendToFile(label, msg) {
630
- if (!fileLoggingEnabled)
631
- return;
632
- try {
633
- ensureLogDir();
634
- const filePath = join(logDir, LOG_FILENAME);
635
- rotateIfNeeded(filePath);
636
- const timestamp = new Date().toISOString();
637
- const line = `[${timestamp}] ${label} ${msg}
638
- `;
639
- appendFileSync(filePath, line, "utf-8");
640
- } catch {}
641
- }
642
- function hookLogInfo(msg) {
643
- if (!shouldEmit("info"))
644
- return;
645
- emitStderr("INFO", msg);
646
- appendToFile("INFO", msg);
647
- }
648
- function hookLogWarn(msg) {
649
- if (!shouldEmit("warn"))
650
- return;
651
- emitStderr("WARN", msg);
652
- appendToFile("WARN", msg);
653
- }
654
- function hookLogError(msg) {
655
- if (!shouldEmit("error"))
656
- return;
657
- emitStderr("ERROR", msg);
658
- appendToFile("ERROR", msg);
659
- }
660
- var LEVEL_ORDER, MAX_FILE_SIZE, LOG_FILENAME = "hooks.log", DEFAULT_LOG_DIR, resolved = false, currentLevel = "warn", fileLoggingEnabled = false, logDir;
661
- var init_hook_logger = __esm(() => {
662
- LEVEL_ORDER = { info: 0, warn: 1, error: 2 };
663
- MAX_FILE_SIZE = 512 * 1024;
664
- DEFAULT_LOG_DIR = join(homedir(), ".failproofai", "logs");
665
- logDir = DEFAULT_LOG_DIR;
666
- });
667
-
668
- // src/hooks/hooks-config.ts
669
- import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
670
- import { resolve, dirname } from "node:path";
671
- import { homedir as homedir2 } from "node:os";
672
- function configuredCustomPolicyPaths(config) {
673
- return config.customPoliciesPaths ?? (config.customPoliciesPath ? [config.customPoliciesPath] : []);
674
- }
675
- function readConfigAt(path2) {
676
- if (!existsSync2(path2))
677
- return {};
678
- try {
679
- const raw = readFileSync(path2, "utf8");
680
- return JSON.parse(raw);
681
- } catch (err) {
682
- hookLogWarn(`failed to parse config at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
683
- return {};
684
- }
685
- }
686
- function findProjectConfigDir(start) {
687
- const home = homedir2();
688
- let dir = resolve(start);
689
- while (dir !== home) {
690
- const marker = resolve(dir, ".failproofai");
691
- try {
692
- if (statSync2(marker).isDirectory())
693
- return dir;
694
- } catch {}
695
- const parent = dirname(dir);
696
- if (parent === dir)
697
- break;
698
- dir = parent;
973
+ if (typeof parsed.transcriptPath === "string")
974
+ parsed.transcript_path = parsed.transcriptPath;
699
975
  }
700
- return resolve(start);
701
- }
702
- function readMergedHooksConfig(cwd) {
703
- const base = findProjectConfigDir(cwd ?? process.cwd());
704
- const projectPath = resolve(base, ".failproofai", "policies-config.json");
705
- const localPath = resolve(base, ".failproofai", "policies-config.local.json");
706
- const globalPath = resolve(homedir2(), ".failproofai", "policies-config.json");
707
- const project = readConfigAt(projectPath);
708
- const local = readConfigAt(localPath);
709
- const global_ = readConfigAt(globalPath);
710
- const enabledSet = new Set([
711
- ...project.enabledPolicies ?? [],
712
- ...local.enabledPolicies ?? [],
713
- ...global_.enabledPolicies ?? []
714
- ]);
715
- const mergedParams = {};
716
- for (const scope of [project, local, global_]) {
717
- if (!scope.policyParams)
718
- continue;
719
- for (const [policyName, params] of Object.entries(scope.policyParams)) {
720
- if (!(policyName in mergedParams)) {
721
- mergedParams[policyName] = params;
722
- }
976
+ if (cli === "copilot") {
977
+ if (typeof parsed.toolName === "string" && parsed.tool_name === undefined) {
978
+ parsed.tool_name = parsed.toolName;
723
979
  }
724
- }
725
- const customPoliciesPaths = [project, local, global_].map((scope) => scope.customPoliciesPaths !== undefined ? scope.customPoliciesPaths : scope.customPoliciesPath !== undefined ? [scope.customPoliciesPath] : undefined).find((paths) => paths !== undefined);
726
- const disabledCustomPolicies = new Set([
727
- ...project.disabledCustomPolicies ?? [],
728
- ...local.disabledCustomPolicies ?? [],
729
- ...global_.disabledCustomPolicies ?? []
730
- ]);
731
- const customPoliciesEnabled = project.customPoliciesEnabled ?? local.customPoliciesEnabled ?? global_.customPoliciesEnabled;
732
- const llm = project.llm ?? local.llm ?? global_.llm;
733
- return {
734
- enabledPolicies: [...enabledSet],
735
- ...Object.keys(mergedParams).length > 0 ? { policyParams: mergedParams } : {},
736
- ...customPoliciesPaths !== undefined ? { customPoliciesPaths } : {},
737
- ...disabledCustomPolicies.size ? { disabledCustomPolicies: [...disabledCustomPolicies] } : {},
738
- ...customPoliciesEnabled !== undefined ? { customPoliciesEnabled } : {},
739
- ...llm !== undefined ? { llm } : {}
740
- };
741
- }
742
- function getConfigPath() {
743
- return resolve(homedir2(), ".failproofai", "policies-config.json");
744
- }
745
- function readHooksConfig() {
746
- const configPath = getConfigPath();
747
- if (!existsSync2(configPath)) {
748
- return { enabledPolicies: [] };
749
- }
750
- try {
751
- const raw = readFileSync(configPath, "utf8");
752
- return JSON.parse(raw);
753
- } catch (err) {
754
- hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
755
- return { enabledPolicies: [] };
756
- }
757
- }
758
- function getConfigPathForScope(scope, cwd) {
759
- switch (scope) {
760
- case "user":
761
- return resolve(homedir2(), ".failproofai", "policies-config.json");
762
- case "project":
763
- return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.json");
764
- case "local":
765
- return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.local.json");
766
- }
767
- }
768
- function readScopedHooksConfig(scope, cwd) {
769
- const configPath = getConfigPathForScope(scope, cwd);
770
- if (!existsSync2(configPath)) {
771
- return { enabledPolicies: [] };
772
- }
773
- try {
774
- const raw = readFileSync(configPath, "utf8");
775
- return JSON.parse(raw);
776
- } catch (err) {
777
- hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
778
- return { enabledPolicies: [] };
779
- }
780
- }
781
- function writeScopedHooksConfig(config, scope, cwd) {
782
- const configPath = getConfigPathForScope(scope, cwd);
783
- const dir = dirname(configPath);
784
- if (!existsSync2(dir)) {
785
- mkdirSync2(dir, { recursive: true });
786
- }
787
- writeFileSync(configPath, JSON.stringify(config, null, 2) + `
788
- `, "utf8");
789
- }
790
- function syncConventionPolicies(entries, scope, cwd) {
791
- const configPath = getConfigPathForScope(scope, cwd);
792
- if (!existsSync2(configPath) && entries.length === 0)
793
- return false;
794
- if (existsSync2(configPath)) {
795
- try {
796
- JSON.parse(readFileSync(configPath, "utf8"));
797
- } catch {
798
- hookLogWarn(`not recording convention policies: ${configPath} is not valid JSON — ` + `fix the syntax error and re-run. Nothing was written.`);
799
- return false;
980
+ if (parsed.toolInput !== undefined && parsed.tool_input === undefined) {
981
+ parsed.tool_input = parsed.toolInput;
982
+ }
983
+ if (typeof parsed.sessionId === "string" && parsed.session_id === undefined) {
984
+ parsed.session_id = parsed.sessionId;
985
+ }
986
+ }
987
+ if (cli === "goose") {
988
+ if (typeof parsed.working_dir === "string")
989
+ parsed.cwd = parsed.working_dir;
990
+ if (typeof parsed.event === "string" && parsed.hook_event_name === undefined) {
991
+ parsed.hook_event_name = parsed.event;
800
992
  }
801
993
  }
802
- const config = readScopedHooksConfig(scope, cwd);
803
- const sorted = [...entries].sort((a, b) => a.file.localeCompare(b.file));
804
- const next = { ...config };
805
- if (sorted.length === 0)
806
- delete next.conventionPolicies;
807
- else
808
- next.conventionPolicies = sorted;
809
- if (JSON.stringify(next.conventionPolicies ?? null) === JSON.stringify(config.conventionPolicies ?? null)) {
810
- return false;
811
- }
812
- writeScopedHooksConfig(next, scope, cwd);
813
- return true;
814
994
  }
815
- var init_hooks_config = __esm(() => {
816
- init_hook_logger();
817
- });
818
995
 
819
996
  // src/hooks/policy-helpers.ts
820
997
  function allow(reason) {
@@ -896,19 +1073,20 @@ function setAllPolicies(policies) {
896
1073
  var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
897
1074
 
898
1075
  // src/hooks/builtin-policies.ts
899
- import { resolve as resolve2, join as join2 } from "node:path";
1076
+ import { resolve as resolve3, join as join2 } from "node:path";
1077
+ import { statSync as statSync3 } from "node:fs";
900
1078
  import { readFile, writeFile } from "node:fs/promises";
901
1079
  import { execSync as execSync2, execFileSync } from "node:child_process";
902
- import { homedir as homedir3 } from "node:os";
1080
+ import { homedir as homedir4 } from "node:os";
903
1081
  function isAgentInternalPath(resolved2) {
904
1082
  const normResolved = resolved2.replaceAll("\\", "/");
905
1083
  for (const dir of [".claude", ".codex", ".copilot", ".cursor", ".opencode", ".pi", ".gemini"]) {
906
- const root = join2(homedir3(), dir).replaceAll("\\", "/");
1084
+ const root = join2(homedir4(), dir).replaceAll("\\", "/");
907
1085
  if (normResolved === root || normResolved.startsWith(root + "/"))
908
1086
  return true;
909
1087
  }
910
1088
  for (const sub of [join2(".config", "opencode"), join2(".local", "share", "opencode")]) {
911
- const root = join2(homedir3(), sub).replaceAll("\\", "/");
1089
+ const root = join2(homedir4(), sub).replaceAll("\\", "/");
912
1090
  if (normResolved === root || normResolved.startsWith(root + "/"))
913
1091
  return true;
914
1092
  }
@@ -954,17 +1132,30 @@ function getFilePath(ctx) {
954
1132
  function parseArgvTokens(cmd) {
955
1133
  return cmd.trim().split(/\s+/).map((t) => t.replace(/^['"]|['"]$/g, ""));
956
1134
  }
1135
+ function statGitHeadMtimeMs(cwd) {
1136
+ try {
1137
+ return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
1138
+ } catch {
1139
+ return null;
1140
+ }
1141
+ }
957
1142
  function getCurrentBranch(cwd) {
958
1143
  try {
959
- let branch = gitBranchCache.get(cwd);
960
- if (branch === undefined) {
961
- branch = execSync2("git rev-parse --abbrev-ref HEAD", {
962
- cwd,
963
- encoding: "utf8",
964
- stdio: ["pipe", "pipe", "pipe"],
965
- timeout: 3000
966
- }).trim();
967
- gitBranchCache.set(cwd, branch);
1144
+ const headMtimeMs = statGitHeadMtimeMs(cwd);
1145
+ const cached = gitBranchCache.get(cwd);
1146
+ if (cached && headMtimeMs !== null && cached.headMtimeMs === headMtimeMs) {
1147
+ return cached.branch || null;
1148
+ }
1149
+ const branch = execSync2("git rev-parse --abbrev-ref HEAD", {
1150
+ cwd,
1151
+ encoding: "utf8",
1152
+ stdio: ["pipe", "pipe", "pipe"],
1153
+ timeout: 3000
1154
+ }).trim();
1155
+ if (headMtimeMs !== null) {
1156
+ if (gitBranchCache.size >= GIT_BRANCH_CACHE_MAX_ENTRIES)
1157
+ gitBranchCache.clear();
1158
+ gitBranchCache.set(cwd, { branch, headMtimeMs });
968
1159
  }
969
1160
  return branch || null;
970
1161
  } catch {
@@ -1233,7 +1424,7 @@ function blockPushMaster(ctx) {
1233
1424
  }
1234
1425
  function expandHomePrefix(path2) {
1235
1426
  const m = path2.match(/^(?:~|\$HOME|\$\{HOME\})(?=$|\/)/);
1236
- return m ? homedir3() + path2.slice(m[0].length) : path2;
1427
+ return m ? homedir4() + path2.slice(m[0].length) : path2;
1237
1428
  }
1238
1429
  function stripTrailingGlob(path2) {
1239
1430
  return path2.replace(/\/\*$/, "").replace(/\/+$/, "");
@@ -1384,9 +1575,9 @@ function extractAbsolutePaths(command) {
1384
1575
  while ((m = pathRe.exec(s)) !== null) {
1385
1576
  let p = m[0];
1386
1577
  if (p === "~")
1387
- p = homedir3();
1578
+ p = homedir4();
1388
1579
  else if (p.startsWith("~/"))
1389
- p = join2(homedir3(), p.slice(2));
1580
+ p = join2(homedir4(), p.slice(2));
1390
1581
  paths.push(p);
1391
1582
  }
1392
1583
  }
@@ -1428,7 +1619,7 @@ function blockReadOutsideCwd(ctx) {
1428
1619
  const paths = extractAbsolutePaths(cmd);
1429
1620
  const cwdWithSep2 = cwd.endsWith("/") ? cwd : cwd + "/";
1430
1621
  for (const p of paths) {
1431
- const resolved3 = resolve2(cwd, p);
1622
+ const resolved3 = resolve3(cwd, p);
1432
1623
  if (isClaudeSettingsFile(resolved3)) {
1433
1624
  return deny(`Reading agent settings file blocked: ${resolved3}`);
1434
1625
  }
@@ -1449,7 +1640,7 @@ function blockReadOutsideCwd(ctx) {
1449
1640
  const target = filePath || searchPath;
1450
1641
  if (!target)
1451
1642
  return allow();
1452
- const resolved2 = resolve2(cwd, target);
1643
+ const resolved2 = resolve3(cwd, target);
1453
1644
  if (isClaudeSettingsFile(resolved2)) {
1454
1645
  return deny(`Reading agent settings file blocked: ${resolved2}`);
1455
1646
  }
@@ -1940,7 +2131,7 @@ function registerBuiltinPolicies(enabledNames) {
1940
2131
  }
1941
2132
  }
1942
2133
  }
1943
- var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
2134
+ var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
1944
2135
  var init_builtin_policies = __esm(() => {
1945
2136
  init_hook_logger();
1946
2137
  isClaudeInternalPath = isAgentInternalPath;
@@ -3442,7 +3633,7 @@ var init_custom_hooks_registry = () => {};
3442
3633
 
3443
3634
  // src/hooks/loader-utils.ts
3444
3635
  import { readFile as readFile2, writeFile as writeFile2, unlink, access } from "fs/promises";
3445
- import { resolve as resolve3, dirname as dirname2, relative } from "path";
3636
+ import { resolve as resolve4, dirname as dirname2, relative } from "path";
3446
3637
  import { pathToFileURL } from "url";
3447
3638
  async function fileExists(path3) {
3448
3639
  try {
@@ -3455,14 +3646,14 @@ async function fileExists(path3) {
3455
3646
  async function findDistIndex() {
3456
3647
  const distPath = process.env.FAILPROOFAI_DIST_PATH;
3457
3648
  if (distPath) {
3458
- const candidate = resolve3(distPath, "index.js");
3649
+ const candidate = resolve4(distPath, "index.js");
3459
3650
  if (await fileExists(candidate))
3460
3651
  return candidate;
3461
3652
  }
3462
3653
  const candidates = [
3463
- resolve3(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
3464
- resolve3(process.cwd(), "dist", "index.js"),
3465
- resolve3(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
3654
+ resolve4(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
3655
+ resolve4(process.cwd(), "dist", "index.js"),
3656
+ resolve4(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
3466
3657
  ];
3467
3658
  for (const c of candidates) {
3468
3659
  if (await fileExists(c))
@@ -3471,8 +3662,8 @@ async function findDistIndex() {
3471
3662
  return null;
3472
3663
  }
3473
3664
  async function resolveLocalImport(fromDir, specifier) {
3474
- const base = resolve3(fromDir, specifier);
3475
- const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve3(base, "index.js")];
3665
+ const base = resolve4(fromDir, specifier);
3666
+ const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve4(base, "index.js")];
3476
3667
  for (const c of candidates) {
3477
3668
  if (await fileExists(c))
3478
3669
  return c;
@@ -3565,16 +3756,16 @@ var init_loader_utils = __esm(() => {
3565
3756
  });
3566
3757
 
3567
3758
  // src/hooks/custom-hooks-loader.ts
3568
- import { resolve as resolve4, isAbsolute, basename } from "node:path";
3759
+ import { resolve as resolve5, isAbsolute, basename } from "node:path";
3569
3760
  import { existsSync as existsSync3, readdirSync } from "node:fs";
3570
3761
  import { pathToFileURL as pathToFileURL2 } from "node:url";
3571
- import { homedir as homedir4 } from "node:os";
3762
+ import { homedir as homedir5 } from "node:os";
3572
3763
  function discoverPolicyFiles(dir) {
3573
3764
  if (!existsSync3(dir))
3574
3765
  return [];
3575
3766
  try {
3576
3767
  const entries = readdirSync(dir, { withFileTypes: true });
3577
- return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve4(dir, e.name));
3768
+ return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve5(dir, e.name));
3578
3769
  } catch {
3579
3770
  return [];
3580
3771
  }
@@ -3619,7 +3810,7 @@ async function loadSingleFile(absPath, opts) {
3619
3810
  async function loadCustomHooks(customPoliciesPath, opts) {
3620
3811
  if (!customPoliciesPath)
3621
3812
  return [];
3622
- const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(opts?.sessionCwd ?? process.cwd(), customPoliciesPath);
3813
+ const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve5(opts?.sessionCwd ?? process.cwd(), customPoliciesPath);
3623
3814
  if (!existsSync3(absPath)) {
3624
3815
  if (opts?.strict)
3625
3816
  throw new Error(`Custom hooks file not found: ${absPath}`);
@@ -3650,7 +3841,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3650
3841
  const conventionEnabled = opts?.customPoliciesEnabled !== false;
3651
3842
  const loadedPaths = new Set;
3652
3843
  for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
3653
- const absPath = resolve4(projectRoot, customPoliciesPath);
3844
+ const absPath = resolve5(projectRoot, customPoliciesPath);
3654
3845
  if (existsSync3(absPath)) {
3655
3846
  if (!loadedPaths.has(absPath)) {
3656
3847
  loadedPaths.add(absPath);
@@ -3665,7 +3856,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3665
3856
  }
3666
3857
  }
3667
3858
  const hooksBeforeConvention = getCustomHooks().length;
3668
- const projectDir = resolve4(projectRoot, ".failproofai", "policies");
3859
+ const projectDir = resolve5(projectRoot, ".failproofai", "policies");
3669
3860
  if (conventionEnabled)
3670
3861
  warnSkippedPolicyFiles(projectDir, "project");
3671
3862
  const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f)) : [];
@@ -3685,7 +3876,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3685
3876
  });
3686
3877
  }
3687
3878
  }
3688
- const userDir = resolve4(homedir4(), ".failproofai", "policies");
3879
+ const userDir = resolve5(homedir5(), ".failproofai", "policies");
3689
3880
  if (conventionEnabled && userDir !== projectDir)
3690
3881
  warnSkippedPolicyFiles(userDir, "user");
3691
3882
  const userFiles = conventionEnabled && userDir !== projectDir ? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f)) : [];
@@ -3750,11 +3941,11 @@ import {
3750
3941
  readdirSync as readdirSync2,
3751
3942
  mkdirSync as mkdirSync3,
3752
3943
  existsSync as existsSync4,
3753
- statSync as statSync3,
3944
+ statSync as statSync4,
3754
3945
  unlinkSync
3755
3946
  } from "node:fs";
3756
3947
  import { join as join3 } from "node:path";
3757
- import { homedir as homedir5 } from "node:os";
3948
+ import { homedir as homedir6 } from "node:os";
3758
3949
  function ensureDir() {
3759
3950
  if (!existsSync4(storeDir)) {
3760
3951
  mkdirSync3(storeDir, { recursive: true });
@@ -3772,7 +3963,7 @@ function acquireLock() {
3772
3963
  if (e.code !== "EEXIST")
3773
3964
  return;
3774
3965
  try {
3775
- const s = statSync3(lockPath);
3966
+ const s = statSync4(lockPath);
3776
3967
  if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
3777
3968
  writeFileSync2(lockPath, String(process.pid), "utf-8");
3778
3969
  return;
@@ -3859,7 +4050,7 @@ function updateStats(entry) {
3859
4050
  }
3860
4051
  var PAGE_SIZE = 25, DEFAULT_STORE_DIR, CURRENT_FILE = "current.jsonl", COUNT_FILE = "current.count", STATS_FILE = "stats.json", LOCK_FILE = "current.lock", LOCK_STALE_MS = 2000, storeDir, rotateSeq = 0;
3861
4052
  var init_hook_activity_store = __esm(() => {
3862
- DEFAULT_STORE_DIR = join3(homedir5(), ".failproofai", "cache", "hook-activity");
4053
+ DEFAULT_STORE_DIR = join3(homedir6(), ".failproofai", "cache", "hook-activity");
3863
4054
  storeDir = DEFAULT_STORE_DIR;
3864
4055
  });
3865
4056
 
@@ -3909,10 +4100,10 @@ function runtimeCache(fn, revalidateSeconds, options) {
3909
4100
  }
3910
4101
 
3911
4102
  // lib/paths.ts
3912
- import { homedir as homedir6 } from "os";
4103
+ import { homedir as homedir7 } from "os";
3913
4104
  import { join as join4 } from "path";
3914
4105
  function getDefaultClaudeProjectsPath() {
3915
- return join4(homedir6(), ".claude", "projects");
4106
+ return join4(homedir7(), ".claude", "projects");
3916
4107
  }
3917
4108
  function decodeFolderName(name) {
3918
4109
  if (/^[A-Za-z]--/.test(name)) {
@@ -4006,7 +4197,7 @@ __export(exports_codex_projects, {
4006
4197
  getCachedCodexProjects: () => getCachedCodexProjects
4007
4198
  });
4008
4199
  import { open as open2, readdir } from "node:fs/promises";
4009
- import { homedir as homedir7 } from "node:os";
4200
+ import { homedir as homedir8 } from "node:os";
4010
4201
  import { join as join5 } from "node:path";
4011
4202
  async function safeReaddir(dir) {
4012
4203
  try {
@@ -4185,7 +4376,7 @@ var CODEX_SESSIONS_ROOT, SESSION_ID_RE, FIRST_LINE_CHUNK_BYTES, cachedScan, getC
4185
4376
  var init_codex_projects = __esm(() => {
4186
4377
  init_paths();
4187
4378
  init_logger();
4188
- CODEX_SESSIONS_ROOT = join5(homedir7(), ".codex", "sessions");
4379
+ CODEX_SESSIONS_ROOT = join5(homedir8(), ".codex", "sessions");
4189
4380
  SESSION_ID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
4190
4381
  FIRST_LINE_CHUNK_BYTES = 256 * 1024;
4191
4382
  cachedScan = runtimeCache(scanCodexSessions, 30);
@@ -4205,10 +4396,10 @@ __export(exports_copilot_projects, {
4205
4396
  getCachedCopilotProjects: () => getCachedCopilotProjects
4206
4397
  });
4207
4398
  import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
4208
- import { homedir as homedir8 } from "node:os";
4399
+ import { homedir as homedir9 } from "node:os";
4209
4400
  import { join as join6 } from "node:path";
4210
4401
  function getCopilotSessionStateRoot() {
4211
- return join6(process.env.COPILOT_HOME || join6(homedir8(), ".copilot"), "session-state");
4402
+ return join6(process.env.COPILOT_HOME || join6(homedir9(), ".copilot"), "session-state");
4212
4403
  }
4213
4404
  async function safeReaddir2(dir) {
4214
4405
  try {
@@ -4353,10 +4544,10 @@ __export(exports_cursor_projects, {
4353
4544
  getCachedCursorProjects: () => getCachedCursorProjects
4354
4545
  });
4355
4546
  import { readdir as readdir3, readFile as readFile4, stat as stat3 } from "node:fs/promises";
4356
- import { homedir as homedir9 } from "node:os";
4547
+ import { homedir as homedir10 } from "node:os";
4357
4548
  import { join as join7 } from "node:path";
4358
4549
  function getCursorHome() {
4359
- return process.env.CURSOR_HOME || join7(homedir9(), ".cursor");
4550
+ return process.env.CURSOR_HOME || join7(homedir10(), ".cursor");
4360
4551
  }
4361
4552
  async function safeReaddir3(dir) {
4362
4553
  try {
@@ -4726,10 +4917,10 @@ __export(exports_pi_projects, {
4726
4917
  getCachedPiProjects: () => getCachedPiProjects
4727
4918
  });
4728
4919
  import { readdir as readdir4, readFile as readFile5, stat as stat4 } from "node:fs/promises";
4729
- import { homedir as homedir10 } from "node:os";
4920
+ import { homedir as homedir11 } from "node:os";
4730
4921
  import { join as join8 } from "node:path";
4731
4922
  function getPiSessionsRoot() {
4732
- return process.env.PI_SESSIONS_DIR || join8(homedir10(), ".pi", "agent", "sessions");
4923
+ return process.env.PI_SESSIONS_DIR || join8(homedir11(), ".pi", "agent", "sessions");
4733
4924
  }
4734
4925
  async function safeReaddir4(dir) {
4735
4926
  try {
@@ -4949,18 +5140,18 @@ var init_sqlite_reader = () => {};
4949
5140
 
4950
5141
  // lib/hermes-profiles.ts
4951
5142
  import { readdirSync as readdirSync3 } from "node:fs";
4952
- import { homedir as homedir11 } from "node:os";
4953
- import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve5 } from "node:path";
5143
+ import { homedir as homedir12 } from "node:os";
5144
+ import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve6 } from "node:path";
4954
5145
  function hermesRoot() {
4955
5146
  const env = (process.env.HERMES_HOME || "").trim();
4956
5147
  if (env) {
4957
- const home = resolve5(env);
5148
+ const home = resolve6(env);
4958
5149
  const parent = dirname3(home);
4959
5150
  if (basename2(parent) === PROFILES_DIR)
4960
5151
  return dirname3(parent);
4961
5152
  return home;
4962
5153
  }
4963
- return join9(homedir11(), ".hermes");
5154
+ return join9(homedir12(), ".hermes");
4964
5155
  }
4965
5156
  function listHermesProfiles() {
4966
5157
  const root = hermesRoot();
@@ -5331,11 +5522,11 @@ var init_hermes_projects = __esm(() => {
5331
5522
 
5332
5523
  // lib/openclaw-sessions.ts
5333
5524
  import { readFile as readFile6 } from "node:fs/promises";
5334
- import { readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
5335
- import { homedir as homedir12 } from "node:os";
5525
+ import { readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
5526
+ import { homedir as homedir13 } from "node:os";
5336
5527
  import { join as join11 } from "node:path";
5337
5528
  function openclawHome() {
5338
- return process.env.OPENCLAW_HOME || join11(homedir12(), ".openclaw");
5529
+ return process.env.OPENCLAW_HOME || join11(homedir13(), ".openclaw");
5339
5530
  }
5340
5531
  function isPlainObject2(v) {
5341
5532
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -5480,7 +5671,7 @@ function listOpenClawTranscripts() {
5480
5671
  continue;
5481
5672
  const transcriptPath = join11(sessionsDir, file);
5482
5673
  try {
5483
- const st = statSync4(transcriptPath);
5674
+ const st = statSync5(transcriptPath);
5484
5675
  out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
5485
5676
  } catch {}
5486
5677
  }
@@ -5700,11 +5891,11 @@ var init_openclaw_projects = __esm(() => {
5700
5891
 
5701
5892
  // lib/factory-sessions.ts
5702
5893
  import { readFile as readFile7 } from "node:fs/promises";
5703
- import { readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
5704
- import { homedir as homedir13 } from "node:os";
5894
+ import { readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
5895
+ import { homedir as homedir14 } from "node:os";
5705
5896
  import { join as join13 } from "node:path";
5706
5897
  function factoryHome() {
5707
- return process.env.FACTORY_HOME || join13(homedir13(), ".factory");
5898
+ return process.env.FACTORY_HOME || join13(homedir14(), ".factory");
5708
5899
  }
5709
5900
  function factorySessionsRoot() {
5710
5901
  return join13(factoryHome(), "sessions");
@@ -5868,7 +6059,7 @@ function listFactoryTranscripts() {
5868
6059
  continue;
5869
6060
  const transcriptPath = join13(projectPath, file);
5870
6061
  try {
5871
- const st = statSync5(transcriptPath);
6062
+ const st = statSync6(transcriptPath);
5872
6063
  out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
5873
6064
  } catch {}
5874
6065
  }
@@ -5992,10 +6183,10 @@ var init_factory_projects = __esm(() => {
5992
6183
  });
5993
6184
 
5994
6185
  // lib/devin-sessions.ts
5995
- import { homedir as homedir14 } from "node:os";
6186
+ import { homedir as homedir15 } from "node:os";
5996
6187
  import { join as join14 } from "node:path";
5997
6188
  function devinHome() {
5998
- return process.env.DEVIN_HOME || join14(homedir14(), ".local", "share", "devin");
6189
+ return process.env.DEVIN_HOME || join14(homedir15(), ".local", "share", "devin");
5999
6190
  }
6000
6191
  function devinDbPath() {
6001
6192
  return process.env.DEVIN_DB_PATH || join14(devinHome(), "cli", "sessions.db");
@@ -6295,11 +6486,11 @@ var init_devin_projects = __esm(() => {
6295
6486
 
6296
6487
  // lib/antigravity-sessions.ts
6297
6488
  import { readFile as readFile8 } from "node:fs/promises";
6298
- import { readdirSync as readdirSync6, statSync as statSync6 } from "node:fs";
6299
- import { homedir as homedir15 } from "node:os";
6489
+ import { readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
6490
+ import { homedir as homedir16 } from "node:os";
6300
6491
  import { join as join15 } from "node:path";
6301
6492
  function antigravityHome() {
6302
- return process.env.ANTIGRAVITY_HOME || join15(homedir15(), ".gemini", "antigravity-cli");
6493
+ return process.env.ANTIGRAVITY_HOME || join15(homedir16(), ".gemini", "antigravity-cli");
6303
6494
  }
6304
6495
  function antigravityBrainRoot() {
6305
6496
  return join15(antigravityHome(), "brain");
@@ -6430,7 +6621,7 @@ function listAntigravityTranscripts() {
6430
6621
  continue;
6431
6622
  const transcriptPath = join15(root, conversationId, rel);
6432
6623
  try {
6433
- const st = statSync6(transcriptPath);
6624
+ const st = statSync7(transcriptPath);
6434
6625
  if (!st.isFile())
6435
6626
  continue;
6436
6627
  out.push({ conversationId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
@@ -6443,7 +6634,7 @@ function findAntigravityTranscript(conversationId) {
6443
6634
  return null;
6444
6635
  const transcriptPath = join15(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
6445
6636
  try {
6446
- if (statSync6(transcriptPath).isFile())
6637
+ if (statSync7(transcriptPath).isFile())
6447
6638
  return transcriptPath;
6448
6639
  } catch {}
6449
6640
  return null;
@@ -6626,13 +6817,13 @@ var init_antigravity_projects = __esm(() => {
6626
6817
  });
6627
6818
 
6628
6819
  // lib/goose-sessions.ts
6629
- import { homedir as homedir16 } from "node:os";
6820
+ import { homedir as homedir17 } from "node:os";
6630
6821
  import { join as join17 } from "node:path";
6631
6822
  function gooseHome() {
6632
6823
  if (process.env.GOOSE_HOME)
6633
6824
  return process.env.GOOSE_HOME;
6634
6825
  const xdg = process.env.XDG_DATA_HOME;
6635
- return join17(xdg && xdg.length > 0 ? xdg : join17(homedir16(), ".local", "share"), "goose");
6826
+ return join17(xdg && xdg.length > 0 ? xdg : join17(homedir17(), ".local", "share"), "goose");
6636
6827
  }
6637
6828
  function gooseDbPath() {
6638
6829
  return process.env.GOOSE_DB_PATH || join17(gooseHome(), "sessions", "sessions.db");
@@ -6903,7 +7094,7 @@ var init_goose_projects = __esm(() => {
6903
7094
 
6904
7095
  // lib/projects.ts
6905
7096
  import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
6906
- import { join as join18, resolve as resolve6, sep } from "node:path";
7097
+ import { join as join18, resolve as resolve7, sep } from "node:path";
6907
7098
  async function getMtime(path3, label) {
6908
7099
  try {
6909
7100
  return (await stat5(path3)).mtime;
@@ -7055,8 +7246,8 @@ function resolveProjectPath(projectName) {
7055
7246
  throw new RangeError("Empty project name");
7056
7247
  if (/^[/\\]/.test(projectName))
7057
7248
  throw new RangeError("Absolute project name");
7058
- const projectsPath = resolve6(getClaudeProjectsPath());
7059
- const candidate = resolve6(join18(projectsPath, projectName));
7249
+ const projectsPath = resolve7(getClaudeProjectsPath());
7250
+ const candidate = resolve7(join18(projectsPath, projectName));
7060
7251
  if (!candidate.startsWith(projectsPath + sep)) {
7061
7252
  throw new RangeError("Project path escapes root");
7062
7253
  }
@@ -7366,10 +7557,10 @@ var init_log_entries = __esm(() => {
7366
7557
  });
7367
7558
 
7368
7559
  // lib/codex-sessions.ts
7369
- import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync7 } from "node:fs";
7560
+ import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync8 } from "node:fs";
7370
7561
  import { readFile as readFile10 } from "node:fs/promises";
7371
7562
  import { dirname as dirname4, join as join21 } from "node:path";
7372
- import { homedir as homedir17 } from "node:os";
7563
+ import { homedir as homedir18 } from "node:os";
7373
7564
  function readCache() {
7374
7565
  try {
7375
7566
  if (!existsSync6(CACHE_PATH))
@@ -7402,7 +7593,7 @@ function findCodexTranscript(sessionId) {
7402
7593
  const cached = cache[sessionId];
7403
7594
  if (cached && existsSync6(cached))
7404
7595
  return cached;
7405
- const root = join21(homedir17(), ".codex", "sessions");
7596
+ const root = join21(homedir18(), ".codex", "sessions");
7406
7597
  const today = new Date;
7407
7598
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
7408
7599
  const datedDirs = [today, yesterday].map((d) => {
@@ -7648,7 +7839,7 @@ async function getCodexSessionLog(sessionId) {
7648
7839
  var CACHE_PATH, getCachedCodexSessionLog;
7649
7840
  var init_codex_sessions = __esm(() => {
7650
7841
  init_log_entries();
7651
- CACHE_PATH = join21(homedir17(), ".failproofai", "cache", "codex-session-paths.json");
7842
+ CACHE_PATH = join21(homedir18(), ".failproofai", "cache", "codex-session-paths.json");
7652
7843
  getCachedCodexSessionLog = runtimeCache((sessionId) => getCodexSessionLog(sessionId), 60, { maxSize: 50 });
7653
7844
  });
7654
7845
 
@@ -7693,12 +7884,12 @@ var init_resolve_permission_mode = __esm(() => {
7693
7884
  });
7694
7885
 
7695
7886
  // lib/copilot-sessions.ts
7696
- import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as statSync8 } from "node:fs";
7887
+ import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as statSync9 } from "node:fs";
7697
7888
  import { readFile as readFile11 } from "node:fs/promises";
7698
- import { join as join22, resolve as resolve7, sep as sep2 } from "node:path";
7699
- import { homedir as homedir18 } from "node:os";
7889
+ import { join as join22, resolve as resolve8, sep as sep2 } from "node:path";
7890
+ import { homedir as homedir19 } from "node:os";
7700
7891
  function getCopilotHome() {
7701
- return process.env.COPILOT_HOME || join22(homedir18(), ".copilot");
7892
+ return process.env.COPILOT_HOME || join22(homedir19(), ".copilot");
7702
7893
  }
7703
7894
  function getCopilotSessionStateRoot2() {
7704
7895
  return join22(getCopilotHome(), "session-state");
@@ -7706,8 +7897,8 @@ function getCopilotSessionStateRoot2() {
7706
7897
  function getCopilotSessionDir(sessionId) {
7707
7898
  if (!sessionId)
7708
7899
  return null;
7709
- const root = resolve7(getCopilotSessionStateRoot2());
7710
- const candidate = resolve7(root, sessionId);
7900
+ const root = resolve8(getCopilotSessionStateRoot2());
7901
+ const candidate = resolve8(root, sessionId);
7711
7902
  if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
7712
7903
  return null;
7713
7904
  return candidate;
@@ -7910,18 +8101,18 @@ var init_copilot_sessions = __esm(() => {
7910
8101
  });
7911
8102
 
7912
8103
  // lib/cursor-sessions.ts
7913
- import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as statSync9 } from "node:fs";
8104
+ import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
7914
8105
  import { readFile as readFile12 } from "node:fs/promises";
7915
- import { basename as basename3, join as join23, resolve as resolve8, sep as sep3 } from "node:path";
7916
- import { homedir as homedir19 } from "node:os";
8106
+ import { basename as basename3, join as join23, resolve as resolve9, sep as sep3 } from "node:path";
8107
+ import { homedir as homedir20 } from "node:os";
7917
8108
  function getCursorHome2() {
7918
- return process.env.CURSOR_HOME || join23(homedir19(), ".cursor");
8109
+ return process.env.CURSOR_HOME || join23(homedir20(), ".cursor");
7919
8110
  }
7920
8111
  function getCursorSessionDir(sessionId) {
7921
8112
  if (!sessionId)
7922
8113
  return null;
7923
- const home = resolve8(getCursorHome2());
7924
- const projectsRoot = resolve8(home, NEW_PROJECTS_DIR2);
8114
+ const home = resolve9(getCursorHome2());
8115
+ const projectsRoot = resolve9(home, NEW_PROJECTS_DIR2);
7925
8116
  let projectEntries = [];
7926
8117
  try {
7927
8118
  projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
@@ -7929,16 +8120,16 @@ function getCursorSessionDir(sessionId) {
7929
8120
  for (const entry of projectEntries) {
7930
8121
  if (!entry.isDirectory())
7931
8122
  continue;
7932
- const candidate = resolve8(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
7933
- const transcriptRoot = resolve8(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
8123
+ const candidate = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
8124
+ const transcriptRoot = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
7934
8125
  if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
7935
8126
  continue;
7936
8127
  if (existsSync8(candidate))
7937
8128
  return candidate;
7938
8129
  }
7939
8130
  for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
7940
- const root = resolve8(home, sub);
7941
- const candidate = resolve8(root, sessionId);
8131
+ const root = resolve9(home, sub);
8132
+ const candidate = resolve9(root, sessionId);
7942
8133
  if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
7943
8134
  continue;
7944
8135
  if (existsSync8(candidate))
@@ -8166,12 +8357,12 @@ var init_cursor_sessions = __esm(() => {
8166
8357
  });
8167
8358
 
8168
8359
  // lib/pi-sessions.ts
8169
- import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as statSync10 } from "node:fs";
8360
+ import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
8170
8361
  import { readFile as readFile13 } from "node:fs/promises";
8171
- import { join as join24, resolve as resolve9, sep as sep4 } from "node:path";
8172
- import { homedir as homedir20 } from "node:os";
8362
+ import { join as join24, resolve as resolve10, sep as sep4 } from "node:path";
8363
+ import { homedir as homedir21 } from "node:os";
8173
8364
  function getPiSessionStateRoot() {
8174
- return process.env.PI_SESSIONS_DIR || join24(homedir20(), ".pi", "agent", "sessions");
8365
+ return process.env.PI_SESSIONS_DIR || join24(homedir21(), ".pi", "agent", "sessions");
8175
8366
  }
8176
8367
  function isSafeSessionId(sessionId) {
8177
8368
  return UUID_RE.test(sessionId);
@@ -8179,7 +8370,7 @@ function isSafeSessionId(sessionId) {
8179
8370
  function findPiTranscript(sessionId) {
8180
8371
  if (!isSafeSessionId(sessionId))
8181
8372
  return null;
8182
- const root = resolve9(getPiSessionStateRoot());
8373
+ const root = resolve10(getPiSessionStateRoot());
8183
8374
  let cwdDirs;
8184
8375
  try {
8185
8376
  cwdDirs = readdirSync10(root);
@@ -8187,7 +8378,7 @@ function findPiTranscript(sessionId) {
8187
8378
  return null;
8188
8379
  }
8189
8380
  for (const cwdDir of cwdDirs) {
8190
- const cwdPath = resolve9(root, cwdDir);
8381
+ const cwdPath = resolve10(root, cwdDir);
8191
8382
  if (!cwdPath.startsWith(`${root}${sep4}`))
8192
8383
  continue;
8193
8384
  let files;
@@ -8200,7 +8391,7 @@ function findPiTranscript(sessionId) {
8200
8391
  const m = SESSION_FILE_RE2.exec(f);
8201
8392
  if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
8202
8393
  continue;
8203
- const candidate = resolve9(cwdPath, f);
8394
+ const candidate = resolve10(cwdPath, f);
8204
8395
  if (!candidate.startsWith(`${cwdPath}${sep4}`))
8205
8396
  continue;
8206
8397
  if (existsSync9(candidate))
@@ -8399,10 +8590,43 @@ var init_resolve_transcript_path = __esm(() => {
8399
8590
  init_antigravity_sessions();
8400
8591
  });
8401
8592
 
8593
+ // src/hooks/read-stdin.ts
8594
+ async function readStdinPayload2(maxBytes = 1048576) {
8595
+ let payload = "";
8596
+ let oversized = false;
8597
+ let readError = false;
8598
+ try {
8599
+ payload = await new Promise((resolve11, reject) => {
8600
+ const chunks = [];
8601
+ let totalBytes = 0;
8602
+ process.stdin.setEncoding("utf8");
8603
+ process.stdin.on("data", (chunk) => {
8604
+ totalBytes += Buffer.byteLength(chunk);
8605
+ if (totalBytes > maxBytes) {
8606
+ oversized = true;
8607
+ process.stdin.destroy();
8608
+ resolve11("");
8609
+ return;
8610
+ }
8611
+ chunks.push(chunk);
8612
+ });
8613
+ process.stdin.on("end", () => resolve11(chunks.join("")));
8614
+ process.stdin.on("error", reject);
8615
+ if (process.stdin.readableEnded)
8616
+ resolve11("");
8617
+ });
8618
+ } catch {
8619
+ readError = true;
8620
+ }
8621
+ return { payload, oversized, readError };
8622
+ }
8623
+
8402
8624
  // src/hooks/handler.ts
8403
8625
  var exports_handler = {};
8404
8626
  __export(exports_handler, {
8405
- handleHookEvent: () => handleHookEvent
8627
+ handleHookEvent: () => handleHookEvent,
8628
+ evaluateHookEvent: () => evaluateHookEvent,
8629
+ canonicalizeEventType: () => canonicalizeEventType
8406
8630
  });
8407
8631
  function canonicalizeEventType(raw, cli) {
8408
8632
  if (cli === "codex") {
@@ -8437,95 +8661,23 @@ function canonicalizeEventType(raw, cli) {
8437
8661
  }
8438
8662
  return raw;
8439
8663
  }
8440
- async function handleHookEvent(eventType, cli = "claude") {
8664
+ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts) {
8441
8665
  const startTime = performance.now();
8442
8666
  try {
8443
- const MAX_STDIN_BYTES = 1048576;
8444
- let payload = "";
8445
- let stdinOversized = false;
8446
- try {
8447
- payload = await new Promise((resolve10, reject) => {
8448
- const chunks = [];
8449
- let totalBytes = 0;
8450
- process.stdin.setEncoding("utf8");
8451
- process.stdin.on("data", (chunk) => {
8452
- totalBytes += Buffer.byteLength(chunk);
8453
- if (totalBytes > MAX_STDIN_BYTES) {
8454
- hookLogWarn(`stdin payload exceeds 1 MB for ${eventType}, discarding`);
8455
- stdinOversized = true;
8456
- process.stdin.destroy();
8457
- resolve10("");
8458
- return;
8459
- }
8460
- chunks.push(chunk);
8461
- });
8462
- process.stdin.on("end", () => resolve10(chunks.join("")));
8463
- process.stdin.on("error", reject);
8464
- if (process.stdin.readableEnded)
8465
- resolve10("");
8466
- });
8467
- } catch (err) {
8468
- hookLogWarn(`stdin read failed for ${eventType}`);
8469
- trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
8470
- event_type: eventType,
8471
- cli,
8472
- error_type: err instanceof Error ? err.name : "unknown"
8473
- });
8474
- }
8475
- if (stdinOversized) {
8476
- trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
8477
- event_type: eventType,
8478
- cli,
8479
- error_type: "oversized"
8480
- });
8481
- }
8482
8667
  let parsed = {};
8483
- if (payload) {
8668
+ if (stdinPayload) {
8484
8669
  try {
8485
- parsed = JSON.parse(payload);
8670
+ parsed = JSON.parse(stdinPayload);
8486
8671
  } catch {
8487
- hookLogWarn(`payload parse failed for ${eventType} (${payload.length} bytes)`);
8672
+ hookLogWarn(`payload parse failed for ${eventType} (${stdinPayload.length} bytes)`);
8488
8673
  trackHookEvent2(getInstanceId2(), "hook_payload_parse_error", {
8489
8674
  event_type: eventType,
8490
8675
  cli,
8491
- payload_size: payload.length
8676
+ payload_size: stdinPayload.length
8492
8677
  });
8493
8678
  }
8494
8679
  }
8495
- if (cli === "antigravity") {
8496
- const tc = parsed.toolCall;
8497
- if (tc && typeof tc === "object") {
8498
- if (tc.name !== undefined)
8499
- parsed.tool_name = tc.name;
8500
- if (tc.args !== undefined)
8501
- parsed.tool_input = tc.args;
8502
- }
8503
- if (typeof parsed.conversationId === "string")
8504
- parsed.session_id = parsed.conversationId;
8505
- if (Array.isArray(parsed.workspacePaths) && typeof parsed.workspacePaths[0] === "string") {
8506
- parsed.cwd = parsed.workspacePaths[0];
8507
- }
8508
- if (typeof parsed.transcriptPath === "string")
8509
- parsed.transcript_path = parsed.transcriptPath;
8510
- }
8511
- if (cli === "copilot") {
8512
- if (typeof parsed.toolName === "string" && parsed.tool_name === undefined) {
8513
- parsed.tool_name = parsed.toolName;
8514
- }
8515
- if (parsed.toolInput !== undefined && parsed.tool_input === undefined) {
8516
- parsed.tool_input = parsed.toolInput;
8517
- }
8518
- if (typeof parsed.sessionId === "string" && parsed.session_id === undefined) {
8519
- parsed.session_id = parsed.sessionId;
8520
- }
8521
- }
8522
- if (cli === "goose") {
8523
- if (typeof parsed.working_dir === "string")
8524
- parsed.cwd = parsed.working_dir;
8525
- if (typeof parsed.event === "string" && parsed.hook_event_name === undefined) {
8526
- parsed.hook_event_name = parsed.event;
8527
- }
8528
- }
8680
+ normalizeCliPayload(cli, parsed);
8529
8681
  const canonicalEventType = canonicalizeEventType(eventType, cli);
8530
8682
  const rawToolName = parsed.tool_name;
8531
8683
  const canonicalToolName = canonicalizeToolName(rawToolName, cli);
@@ -8541,82 +8693,86 @@ async function handleHookEvent(eventType, cli = "claude") {
8541
8693
  const session = {
8542
8694
  sessionId,
8543
8695
  transcriptPath: resolveTranscriptPath(cli, parsed, sessionId),
8544
- cwd: resolveCwd(cli, parsed),
8696
+ cwd: resolveCwd(cli, parsed) ?? opts?.fallbackCwd,
8545
8697
  permissionMode: resolvePermissionMode(cli, parsed, sessionId),
8546
8698
  hookEventName: parsed.hook_event_name,
8547
8699
  rawHookEventName: eventType,
8548
8700
  cli
8549
8701
  };
8550
- const config = readMergedHooksConfig(session.cwd);
8551
- clearPolicies();
8552
- registerBuiltinPolicies(config.enabledPolicies);
8553
- const loadResult = await loadAllCustomHooks(config.customPoliciesPaths ?? config.customPoliciesPath, {
8554
- sessionCwd: session.cwd,
8555
- customPoliciesEnabled: config.customPoliciesEnabled
8556
- });
8557
- const customHooksList = loadResult.hooks;
8558
- const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
8559
- const conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
8560
- for (const hook of customHooksList) {
8561
- const policyId = hook.__policyId;
8562
- if (policyId && disabledCustomPolicies.has(policyId))
8563
- continue;
8564
- const hookName = hook.name;
8565
- const conventionScope = hook.__conventionScope;
8566
- const isConvention = !!conventionScope;
8567
- const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
8568
- const fn = async (ctx) => {
8569
- try {
8570
- const result2 = await Promise.race([
8571
- hook.fn(ctx),
8572
- new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
8573
- ]);
8574
- return result2;
8575
- } catch (err) {
8576
- const msg = err instanceof Error ? err.message : String(err);
8577
- const isTimeout = msg === "timeout";
8578
- hookLogWarn(`${prefix} hook "${hookName}" failed: ${msg}`);
8579
- trackHookEvent2(getInstanceId2(), "custom_hook_error", {
8580
- hook_name: hookName,
8581
- error_type: isTimeout ? "timeout" : "exception",
8582
- event_type: eventType,
8583
- cli,
8584
- is_convention_policy: isConvention,
8585
- convention_scope: conventionScope ?? null
8586
- });
8587
- return { decision: "allow" };
8588
- }
8589
- };
8590
- registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
8591
- }
8592
- if (customHooksList.length > 0) {
8593
- trackHookEvent2(getInstanceId2(), "custom_hooks_loaded", {
8594
- cli,
8595
- custom_hooks_count: customHooksList.length,
8596
- custom_hook_names: customHooksList.map((h) => h.name),
8597
- event_types_covered: [...new Set(customHooksList.flatMap((h) => h.match?.events ?? []))]
8598
- });
8599
- }
8600
- if (loadResult.conventionSources.length > 0) {
8601
- trackHookEvent2(getInstanceId2(), "convention_policies_loaded", {
8602
- event_type: canonicalEventType,
8603
- cli,
8604
- project_file_count: loadResult.conventionSources.filter((s) => s.scope === "project").length,
8605
- user_file_count: loadResult.conventionSources.filter((s) => s.scope === "user").length,
8606
- convention_hook_count: conventionHookNames.size,
8607
- convention_hook_names: [...conventionHookNames]
8702
+ let config;
8703
+ let customHooksList = [];
8704
+ let conventionHookNames = new Set;
8705
+ if (opts?.forceDecision) {
8706
+ config = { enabledPolicies: [] };
8707
+ clearPolicies();
8708
+ const decision = opts.forceDecision;
8709
+ registerPolicy("failproofai/daemon-unreachable", "Fail-closed: the failproofaid daemon could not be reached for this daemon-configured scope.", async () => ({ decision: decision.decision, reason: decision.reason }), {});
8710
+ } else {
8711
+ config = readMergedHooksConfig(session.cwd);
8712
+ clearPolicies();
8713
+ registerBuiltinPolicies(config.enabledPolicies);
8714
+ const loadResult = await loadAllCustomHooks(config.customPoliciesPaths ?? config.customPoliciesPath, {
8715
+ sessionCwd: session.cwd,
8716
+ customPoliciesEnabled: config.customPoliciesEnabled
8608
8717
  });
8718
+ customHooksList = loadResult.hooks;
8719
+ const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
8720
+ conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
8721
+ for (const hook of customHooksList) {
8722
+ const policyId = hook.__policyId;
8723
+ if (policyId && disabledCustomPolicies.has(policyId))
8724
+ continue;
8725
+ const hookName = hook.name;
8726
+ const conventionScope = hook.__conventionScope;
8727
+ const isConvention = !!conventionScope;
8728
+ const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
8729
+ const fn = async (ctx) => {
8730
+ try {
8731
+ const result2 = await Promise.race([
8732
+ hook.fn(ctx),
8733
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
8734
+ ]);
8735
+ return result2;
8736
+ } catch (err) {
8737
+ const msg = err instanceof Error ? err.message : String(err);
8738
+ const isTimeout = msg === "timeout";
8739
+ hookLogWarn(`${prefix} hook "${hookName}" failed: ${msg}`);
8740
+ trackHookEvent2(getInstanceId2(), "custom_hook_error", {
8741
+ hook_name: hookName,
8742
+ error_type: isTimeout ? "timeout" : "exception",
8743
+ event_type: eventType,
8744
+ cli,
8745
+ is_convention_policy: isConvention,
8746
+ convention_scope: conventionScope ?? null
8747
+ });
8748
+ return { decision: "allow" };
8749
+ }
8750
+ };
8751
+ registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
8752
+ }
8753
+ if (customHooksList.length > 0) {
8754
+ trackHookEvent2(getInstanceId2(), "custom_hooks_loaded", {
8755
+ cli,
8756
+ custom_hooks_count: customHooksList.length,
8757
+ custom_hook_names: customHooksList.map((h) => h.name),
8758
+ event_types_covered: [...new Set(customHooksList.flatMap((h) => h.match?.events ?? []))]
8759
+ });
8760
+ }
8761
+ if (loadResult.conventionSources.length > 0) {
8762
+ trackHookEvent2(getInstanceId2(), "convention_policies_loaded", {
8763
+ event_type: canonicalEventType,
8764
+ cli,
8765
+ project_file_count: loadResult.conventionSources.filter((s) => s.scope === "project").length,
8766
+ user_file_count: loadResult.conventionSources.filter((s) => s.scope === "user").length,
8767
+ convention_hook_count: conventionHookNames.size,
8768
+ convention_hook_names: [...conventionHookNames]
8769
+ });
8770
+ }
8771
+ hookLogInfo(`event=${canonicalEventType} cli=${cli} policies=${config.enabledPolicies.length} custom=${customHooksList.length} convention=${conventionHookNames.size}`);
8609
8772
  }
8610
- hookLogInfo(`event=${canonicalEventType} cli=${cli} policies=${config.enabledPolicies.length} custom=${customHooksList.length} convention=${conventionHookNames.size}`);
8611
8773
  const result = await evaluatePolicies(canonicalEventType, parsed, session, config);
8612
8774
  const durationMs = Math.round(performance.now() - startTime);
8613
8775
  hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
8614
- if (result.stdout) {
8615
- process.stdout.write(result.stdout);
8616
- }
8617
- if (result.stderr) {
8618
- process.stderr.write(result.stderr);
8619
- }
8620
8776
  const matchedPolicies = getPoliciesForEvent(canonicalEventType, parsed.tool_name).map((p) => p.name);
8621
8777
  const activityEntry = {
8622
8778
  timestamp: Date.now(),
@@ -8648,7 +8804,7 @@ async function handleHookEvent(eventType, cli = "claude") {
8648
8804
  const hasCustomParams = !isCustomHook && !isConventionPolicy && !!(result.policyName && config.policyParams?.[result.policyName]);
8649
8805
  const paramKeysOverridden = hasCustomParams ? Object.keys(config.policyParams[result.policyName]) : [];
8650
8806
  const distinctId = getInstanceId2();
8651
- await trackHookEvent2(distinctId, "hook_policy_triggered", {
8807
+ const trackPromise = trackHookEvent2(distinctId, "hook_policy_triggered", {
8652
8808
  event_type: canonicalEventType,
8653
8809
  cli,
8654
8810
  tool_name: parsed.tool_name ?? null,
@@ -8660,12 +8816,45 @@ async function handleHookEvent(eventType, cli = "claude") {
8660
8816
  has_custom_params: hasCustomParams,
8661
8817
  param_keys_overridden: paramKeysOverridden
8662
8818
  });
8819
+ if (opts?.awaitTelemetryFlush ?? true) {
8820
+ await trackPromise;
8821
+ }
8663
8822
  } catch {}
8664
8823
  }
8665
- return result.exitCode;
8824
+ return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
8666
8825
  } finally {
8667
- await flushHookTelemetry2();
8826
+ if (opts?.awaitTelemetryFlush ?? true) {
8827
+ await flushHookTelemetry2();
8828
+ }
8829
+ }
8830
+ }
8831
+ async function handleHookEvent(eventType, cli = "claude") {
8832
+ const MAX_STDIN_BYTES = 1048576;
8833
+ const stdinRead = await readStdinPayload2(MAX_STDIN_BYTES);
8834
+ if (stdinRead.readError) {
8835
+ hookLogWarn(`stdin read failed for ${eventType}`);
8836
+ trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
8837
+ event_type: eventType,
8838
+ cli,
8839
+ error_type: "unknown"
8840
+ });
8841
+ }
8842
+ if (stdinRead.oversized) {
8843
+ hookLogWarn(`stdin payload exceeds 1 MB for ${eventType}, discarding`);
8844
+ trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
8845
+ event_type: eventType,
8846
+ cli,
8847
+ error_type: "oversized"
8848
+ });
8849
+ }
8850
+ const result = await evaluateHookEvent(eventType, cli, stdinRead.payload);
8851
+ if (result.stdout) {
8852
+ process.stdout.write(result.stdout);
8853
+ }
8854
+ if (result.stderr) {
8855
+ process.stderr.write(result.stderr);
8668
8856
  }
8857
+ return result.exitCode;
8669
8858
  }
8670
8859
  var init_handler = __esm(() => {
8671
8860
  init_types();
@@ -8686,13 +8875,13 @@ var init_handler = __esm(() => {
8686
8875
  import { createHmac, randomUUID } from "node:crypto";
8687
8876
  import { execSync as execSync4 } from "node:child_process";
8688
8877
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "node:fs";
8689
- import { homedir as homedir21, platform, arch, hostname, cpus } from "node:os";
8878
+ import { homedir as homedir22, platform, arch, hostname, cpus } from "node:os";
8690
8879
  import { join as join25 } from "node:path";
8691
8880
  function hashToId3(raw) {
8692
8881
  return createHmac("sha256", NAMESPACE3).update(raw).digest("hex");
8693
8882
  }
8694
8883
  function getInstanceId3() {
8695
- const idDir = join25(homedir21(), ".failproofai");
8884
+ const idDir = join25(homedir22(), ".failproofai");
8696
8885
  const idFile = join25(idDir, "instance-id");
8697
8886
  try {
8698
8887
  const existing = readFileSync10(idFile, "utf-8").trim();
@@ -8727,7 +8916,7 @@ function getInstanceId3() {
8727
8916
  try {
8728
8917
  const sysProps = [
8729
8918
  hostname(),
8730
- homedir21(),
8919
+ homedir22(),
8731
8920
  platform(),
8732
8921
  arch(),
8733
8922
  cpus()[0]?.model ?? ""
@@ -8774,14 +8963,14 @@ __export(exports_install_check, {
8774
8963
  checkHooks: () => checkHooks
8775
8964
  });
8776
8965
  import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "node:fs";
8777
- import { resolve as resolve10 } from "node:path";
8778
- import { platform as platform2, arch as arch2, release, homedir as homedir22, hostname as hostname2 } from "node:os";
8966
+ import { resolve as resolve11 } from "node:path";
8967
+ import { platform as platform2, arch as arch2, release, homedir as homedir23, hostname as hostname2 } from "node:os";
8779
8968
  import { createHmac as createHmac2 } from "node:crypto";
8780
8969
  function hashToId4(raw) {
8781
8970
  return createHmac2("sha256", NAMESPACE4).update(raw).digest("hex");
8782
8971
  }
8783
8972
  function lastVersionPath() {
8784
- return resolve10(homedir22(), ".failproofai", "last-version");
8973
+ return resolve11(homedir23(), ".failproofai", "last-version");
8785
8974
  }
8786
8975
  function compareSemver(a, b) {
8787
8976
  const parse = (v) => {
@@ -8828,7 +9017,7 @@ function compareSemver(a, b) {
8828
9017
  return 0;
8829
9018
  }
8830
9019
  function checkHooks() {
8831
- const hooksConfigPath = resolve10(homedir22(), ".failproofai", "policies-config.json");
9020
+ const hooksConfigPath = resolve11(homedir23(), ".failproofai", "policies-config.json");
8832
9021
  if (!existsSync10(hooksConfigPath)) {
8833
9022
  return { configured: false, registered: false, policyCount: 0 };
8834
9023
  }
@@ -8842,7 +9031,7 @@ function checkHooks() {
8842
9031
  return { configured: false, registered: false, policyCount: 0 };
8843
9032
  }
8844
9033
  const policyCount = config.enabledPolicies.length;
8845
- const settingsPath = resolve10(homedir22(), ".claude", "settings.json");
9034
+ const settingsPath = resolve11(homedir23(), ".claude", "settings.json");
8846
9035
  if (!existsSync10(settingsPath))
8847
9036
  return { configured: true, registered: false, policyCount };
8848
9037
  let settings;
@@ -8907,7 +9096,7 @@ async function maybeReportInstall(version3) {
8907
9096
  enabled_policy_count: hooks.policyCount
8908
9097
  }, { version: version3, timeoutMs: REPORT_TIMEOUT_MS }));
8909
9098
  try {
8910
- mkdirSync6(resolve10(homedir22(), ".failproofai"), { recursive: true });
9099
+ mkdirSync6(resolve11(homedir23(), ".failproofai"), { recursive: true });
8911
9100
  writeFileSync5(versionFile, version3, "utf8");
8912
9101
  } catch {}
8913
9102
  await Promise.all(events.map((p) => p.catch(() => {})));
@@ -15911,9 +16100,9 @@ var init_dist = __esm(() => {
15911
16100
  // src/hooks/integrations.ts
15912
16101
  import { execSync as execSync5 } from "node:child_process";
15913
16102
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
15914
- import { resolve as resolve11, dirname as dirname5 } from "node:path";
16103
+ import { resolve as resolve12, dirname as dirname5 } from "node:path";
15915
16104
  import { fileURLToPath } from "node:url";
15916
- import { homedir as homedir23 } from "node:os";
16105
+ import { homedir as homedir24 } from "node:os";
15917
16106
  function readJsonFile(path3) {
15918
16107
  if (!existsSync11(path3))
15919
16108
  return {};
@@ -15977,7 +16166,7 @@ function isMarkedCopilotHook(hook) {
15977
16166
  return false;
15978
16167
  }
15979
16168
  function opencodePluginFilePath(settingsPath) {
15980
- return resolve11(dirname5(settingsPath), "plugins", "failproofai.mjs");
16169
+ return resolve12(dirname5(settingsPath), "plugins", "failproofai.mjs");
15981
16170
  }
15982
16171
  function buildOpenCodePluginShim(binaryPath, scope) {
15983
16172
  const useNpx = scope === "project";
@@ -16192,8 +16381,8 @@ export default async function failproofaiPlugin({ client, directory }) {
16192
16381
  function getPiExtensionPath() {
16193
16382
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
16194
16383
  if (fromEnv)
16195
- return resolve11(fromEnv, "pi-extension");
16196
- return resolve11(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
16384
+ return resolve12(fromEnv, "pi-extension");
16385
+ return resolve12(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
16197
16386
  }
16198
16387
  function isFailproofaiPiEntry(source) {
16199
16388
  if (typeof source !== "string")
@@ -16204,8 +16393,8 @@ function isFailproofaiPiEntry(source) {
16204
16393
  }
16205
16394
  function makePiProjectRelativeEntry(extPath) {
16206
16395
  const cwd = process.cwd();
16207
- const cwdResolved = resolve11(cwd);
16208
- const extResolved = resolve11(extPath);
16396
+ const cwdResolved = resolve12(cwd);
16397
+ const extResolved = resolve12(extPath);
16209
16398
  if (extResolved.startsWith(cwdResolved + "/") || extResolved === cwdResolved) {
16210
16399
  const fromSettingsDir = "../" + extResolved.slice(cwdResolved.length + 1);
16211
16400
  return fromSettingsDir;
@@ -16229,13 +16418,13 @@ function hermesConfigHasHooks(settingsPath) {
16229
16418
  return false;
16230
16419
  }
16231
16420
  function unhookedHermesProfiles() {
16232
- return listHermesProfiles().filter((p) => existsSync11(p.home)).filter((p) => !hermesConfigHasHooks(resolve11(p.home, "config.yaml"))).map((p) => p.name);
16421
+ return listHermesProfiles().filter((p) => existsSync11(p.home)).filter((p) => !hermesConfigHasHooks(resolve12(p.home, "config.yaml"))).map((p) => p.name);
16233
16422
  }
16234
16423
  function getOpenClawPluginPath() {
16235
16424
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
16236
16425
  if (fromEnv)
16237
- return resolve11(fromEnv, "openclaw-plugin");
16238
- return resolve11(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
16426
+ return resolve12(fromEnv, "openclaw-plugin");
16427
+ return resolve12(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
16239
16428
  }
16240
16429
  function isFailproofaiOpenClawPath(p) {
16241
16430
  if (typeof p !== "string")
@@ -16272,14 +16461,14 @@ var init_integrations = __esm(() => {
16272
16461
  scopes: HOOK_SCOPES,
16273
16462
  eventTypes: CLAUDE_INSTALL_EVENT_TYPES,
16274
16463
  getSettingsPath(scope, cwd) {
16275
- const base = cwd ? resolve11(cwd) : process.cwd();
16464
+ const base = cwd ? resolve12(cwd) : process.cwd();
16276
16465
  switch (scope) {
16277
16466
  case "user":
16278
- return resolve11(homedir23(), ".claude", "settings.json");
16467
+ return resolve12(homedir24(), ".claude", "settings.json");
16279
16468
  case "project":
16280
- return resolve11(base, ".claude", "settings.json");
16469
+ return resolve12(base, ".claude", "settings.json");
16281
16470
  case "local":
16282
- return resolve11(base, ".claude", "settings.local.json");
16471
+ return resolve12(base, ".claude", "settings.local.json");
16283
16472
  }
16284
16473
  },
16285
16474
  readSettings(settingsPath) {
@@ -16404,14 +16593,14 @@ var init_integrations = __esm(() => {
16404
16593
  scopes: CODEX_HOOK_SCOPES,
16405
16594
  eventTypes: CODEX_HOOK_EVENT_TYPES,
16406
16595
  getSettingsPath(scope, cwd) {
16407
- const base = cwd ? resolve11(cwd) : process.cwd();
16596
+ const base = cwd ? resolve12(cwd) : process.cwd();
16408
16597
  switch (scope) {
16409
16598
  case "user":
16410
- return resolve11(homedir23(), ".codex", "hooks.json");
16599
+ return resolve12(homedir24(), ".codex", "hooks.json");
16411
16600
  case "project":
16412
- return resolve11(base, ".codex", "hooks.json");
16601
+ return resolve12(base, ".codex", "hooks.json");
16413
16602
  case "local":
16414
- return resolve11(base, ".codex", "hooks.json");
16603
+ return resolve12(base, ".codex", "hooks.json");
16415
16604
  }
16416
16605
  },
16417
16606
  readSettings(settingsPath) {
@@ -16520,14 +16709,14 @@ var init_integrations = __esm(() => {
16520
16709
  scopes: COPILOT_HOOK_SCOPES,
16521
16710
  eventTypes: COPILOT_HOOK_EVENT_TYPES,
16522
16711
  getSettingsPath(scope, cwd) {
16523
- const base = cwd ? resolve11(cwd) : process.cwd();
16712
+ const base = cwd ? resolve12(cwd) : process.cwd();
16524
16713
  switch (scope) {
16525
16714
  case "user":
16526
- return resolve11(homedir23(), ".copilot", "hooks", "failproofai.json");
16715
+ return resolve12(homedir24(), ".copilot", "hooks", "failproofai.json");
16527
16716
  case "project":
16528
- return resolve11(base, ".github", "hooks", "failproofai.json");
16717
+ return resolve12(base, ".github", "hooks", "failproofai.json");
16529
16718
  case "local":
16530
- return resolve11(base, ".github", "hooks", "failproofai.json");
16719
+ return resolve12(base, ".github", "hooks", "failproofai.json");
16531
16720
  }
16532
16721
  },
16533
16722
  readSettings(settingsPath) {
@@ -16634,14 +16823,14 @@ var init_integrations = __esm(() => {
16634
16823
  scopes: CURSOR_HOOK_SCOPES,
16635
16824
  eventTypes: CURSOR_HOOK_EVENT_TYPES,
16636
16825
  getSettingsPath(scope, cwd) {
16637
- const base = cwd ? resolve11(cwd) : process.cwd();
16826
+ const base = cwd ? resolve12(cwd) : process.cwd();
16638
16827
  switch (scope) {
16639
16828
  case "user":
16640
- return resolve11(homedir23(), ".cursor", "hooks.json");
16829
+ return resolve12(homedir24(), ".cursor", "hooks.json");
16641
16830
  case "project":
16642
- return resolve11(base, ".cursor", "hooks.json");
16831
+ return resolve12(base, ".cursor", "hooks.json");
16643
16832
  case "local":
16644
- return resolve11(base, ".cursor", "hooks.json");
16833
+ return resolve12(base, ".cursor", "hooks.json");
16645
16834
  }
16646
16835
  },
16647
16836
  readSettings(settingsPath) {
@@ -16733,14 +16922,14 @@ var init_integrations = __esm(() => {
16733
16922
  scopes: OPENCODE_HOOK_SCOPES,
16734
16923
  eventTypes: OPENCODE_HOOK_EVENT_TYPES,
16735
16924
  getSettingsPath(scope, cwd) {
16736
- const base = cwd ? resolve11(cwd) : process.cwd();
16925
+ const base = cwd ? resolve12(cwd) : process.cwd();
16737
16926
  switch (scope) {
16738
16927
  case "user":
16739
- return resolve11(homedir23(), ".config", "opencode", "opencode.json");
16928
+ return resolve12(homedir24(), ".config", "opencode", "opencode.json");
16740
16929
  case "project":
16741
- return resolve11(base, ".opencode", "opencode.json");
16930
+ return resolve12(base, ".opencode", "opencode.json");
16742
16931
  case "local":
16743
- return resolve11(base, ".opencode", "opencode.json");
16932
+ return resolve12(base, ".opencode", "opencode.json");
16744
16933
  }
16745
16934
  },
16746
16935
  readSettings(settingsPath) {
@@ -16751,7 +16940,7 @@ var init_integrations = __esm(() => {
16751
16940
  },
16752
16941
  buildHookEntry(_binaryPath, _eventType, scope) {
16753
16942
  if (scope === "user") {
16754
- const abs = resolve11(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
16943
+ const abs = resolve12(homedir24(), ".config", "opencode", "plugins", "failproofai.mjs");
16755
16944
  return { spec: `file://${abs}`, [FAILPROOFAI_HOOK_MARKER]: true };
16756
16945
  }
16757
16946
  return { spec: OPENCODE_PLUGIN_REL_PATH, [FAILPROOFAI_HOOK_MARKER]: true };
@@ -16766,7 +16955,7 @@ var init_integrations = __esm(() => {
16766
16955
  writeHookEntries(settings, binaryPath, scope) {
16767
16956
  const s = settings;
16768
16957
  const effectiveScope = scope ?? "project";
16769
- const settingsPath = effectiveScope === "user" ? resolve11(homedir23(), ".config", "opencode", "opencode.json") : resolve11(process.cwd(), ".opencode", "opencode.json");
16958
+ const settingsPath = effectiveScope === "user" ? resolve12(homedir24(), ".config", "opencode", "opencode.json") : resolve12(process.cwd(), ".opencode", "opencode.json");
16770
16959
  const pluginPath = opencodePluginFilePath(settingsPath);
16771
16960
  mkdirSync7(dirname5(pluginPath), { recursive: true });
16772
16961
  writeFileSync6(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
@@ -16834,14 +17023,14 @@ var init_integrations = __esm(() => {
16834
17023
  scopes: PI_HOOK_SCOPES,
16835
17024
  eventTypes: PI_HOOK_EVENT_TYPES,
16836
17025
  getSettingsPath(scope, cwd) {
16837
- const base = cwd ? resolve11(cwd) : process.cwd();
17026
+ const base = cwd ? resolve12(cwd) : process.cwd();
16838
17027
  switch (scope) {
16839
17028
  case "user":
16840
- return resolve11(homedir23(), ".pi", "agent", "settings.json");
17029
+ return resolve12(homedir24(), ".pi", "agent", "settings.json");
16841
17030
  case "project":
16842
- return resolve11(base, ".pi", "settings.json");
17031
+ return resolve12(base, ".pi", "settings.json");
16843
17032
  case "local":
16844
- return resolve11(base, ".pi", "settings.json");
17033
+ return resolve12(base, ".pi", "settings.json");
16845
17034
  }
16846
17035
  },
16847
17036
  readSettings(settingsPath) {
@@ -16919,10 +17108,10 @@ var init_integrations = __esm(() => {
16919
17108
  scopes: HERMES_HOOK_SCOPES,
16920
17109
  eventTypes: HERMES_HOOK_EVENT_TYPES,
16921
17110
  getSettingsPath() {
16922
- return resolve11(hermesRoot(), "config.yaml");
17111
+ return resolve12(hermesRoot(), "config.yaml");
16923
17112
  },
16924
17113
  getSettingsPaths() {
16925
- return listHermesProfiles().map((p) => resolve11(p.home, "config.yaml"));
17114
+ return listHermesProfiles().map((p) => resolve12(p.home, "config.yaml"));
16926
17115
  },
16927
17116
  readSettings(settingsPath) {
16928
17117
  return readYamlDoc(settingsPath);
@@ -17002,7 +17191,7 @@ var init_integrations = __esm(() => {
17002
17191
  scopes: OPENCLAW_HOOK_SCOPES,
17003
17192
  eventTypes: OPENCLAW_HOOK_EVENT_TYPES,
17004
17193
  getSettingsPath() {
17005
- return resolve11(homedir23(), ".openclaw", "openclaw.json");
17194
+ return resolve12(homedir24(), ".openclaw", "openclaw.json");
17006
17195
  },
17007
17196
  readSettings(settingsPath) {
17008
17197
  return readJsonFile(settingsPath);
@@ -17098,14 +17287,14 @@ var init_integrations = __esm(() => {
17098
17287
  scopes: FACTORY_HOOK_SCOPES,
17099
17288
  eventTypes: FACTORY_HOOK_EVENT_TYPES,
17100
17289
  getSettingsPath(scope, cwd) {
17101
- const base = cwd ? resolve11(cwd) : process.cwd();
17290
+ const base = cwd ? resolve12(cwd) : process.cwd();
17102
17291
  switch (scope) {
17103
17292
  case "user":
17104
- return resolve11(homedir23(), ".factory", "hooks.json");
17293
+ return resolve12(homedir24(), ".factory", "hooks.json");
17105
17294
  case "project":
17106
- return resolve11(base, ".factory", "hooks.json");
17295
+ return resolve12(base, ".factory", "hooks.json");
17107
17296
  case "local":
17108
- return resolve11(base, ".factory", "hooks.json");
17297
+ return resolve12(base, ".factory", "hooks.json");
17109
17298
  }
17110
17299
  },
17111
17300
  readSettings(settingsPath) {
@@ -17200,14 +17389,14 @@ var init_integrations = __esm(() => {
17200
17389
  scopes: DEVIN_HOOK_SCOPES,
17201
17390
  eventTypes: DEVIN_HOOK_EVENT_TYPES,
17202
17391
  getSettingsPath(scope, cwd) {
17203
- const base = cwd ? resolve11(cwd) : process.cwd();
17392
+ const base = cwd ? resolve12(cwd) : process.cwd();
17204
17393
  switch (scope) {
17205
17394
  case "user":
17206
- return resolve11(homedir23(), ".config", "devin", "config.json");
17395
+ return resolve12(homedir24(), ".config", "devin", "config.json");
17207
17396
  case "project":
17208
- return resolve11(base, ".devin", "config.json");
17397
+ return resolve12(base, ".devin", "config.json");
17209
17398
  case "local":
17210
- return resolve11(base, ".devin", "config.json");
17399
+ return resolve12(base, ".devin", "config.json");
17211
17400
  }
17212
17401
  },
17213
17402
  readSettings(settingsPath) {
@@ -17309,14 +17498,14 @@ var init_integrations = __esm(() => {
17309
17498
  scopes: ANTIGRAVITY_HOOK_SCOPES,
17310
17499
  eventTypes: ANTIGRAVITY_HOOK_EVENT_TYPES,
17311
17500
  getSettingsPath(scope, cwd) {
17312
- const base = cwd ? resolve11(cwd) : process.cwd();
17501
+ const base = cwd ? resolve12(cwd) : process.cwd();
17313
17502
  switch (scope) {
17314
17503
  case "user":
17315
- return resolve11(homedir23(), ".gemini", "config", "hooks.json");
17504
+ return resolve12(homedir24(), ".gemini", "config", "hooks.json");
17316
17505
  case "project":
17317
- return resolve11(base, ".agents", "hooks.json");
17506
+ return resolve12(base, ".agents", "hooks.json");
17318
17507
  case "local":
17319
- return resolve11(base, ".agents", "hooks.json");
17508
+ return resolve12(base, ".agents", "hooks.json");
17320
17509
  }
17321
17510
  },
17322
17511
  readSettings(settingsPath) {
@@ -17450,14 +17639,14 @@ var init_integrations = __esm(() => {
17450
17639
  scopes: GOOSE_HOOK_SCOPES,
17451
17640
  eventTypes: GOOSE_HOOK_EVENT_TYPES,
17452
17641
  getSettingsPath(scope, cwd) {
17453
- const base = cwd ? resolve11(cwd) : process.cwd();
17642
+ const base = cwd ? resolve12(cwd) : process.cwd();
17454
17643
  switch (scope) {
17455
17644
  case "user":
17456
- return resolve11(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17645
+ return resolve12(homedir24(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17457
17646
  case "project":
17458
- return resolve11(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17647
+ return resolve12(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17459
17648
  case "local":
17460
- return resolve11(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17649
+ return resolve12(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17461
17650
  }
17462
17651
  },
17463
17652
  readSettings(settingsPath) {
@@ -17792,7 +17981,7 @@ function runPrompt(p) {
17792
17981
  `${c.dim(BAR)} ${c.dim(p.summaryFor(result))}`
17793
17982
  ]);
17794
17983
  };
17795
- return new Promise((resolve12) => {
17984
+ return new Promise((resolve13) => {
17796
17985
  hideCursor(stdout);
17797
17986
  repaint(stdout, region, build());
17798
17987
  readline.emitKeypressEvents(stdin);
@@ -17808,7 +17997,7 @@ function runPrompt(p) {
17808
17997
  const finish = (result) => {
17809
17998
  cleanup();
17810
17999
  collapse(result);
17811
- resolve12(result);
18000
+ resolve13(result);
17812
18001
  };
17813
18002
  function onKey(_s, key) {
17814
18003
  if (!key)
@@ -18109,7 +18298,7 @@ async function promptPolicySelection(preSelected, options = {}) {
18109
18298
  `);
18110
18299
  lastLineCount = lines.length;
18111
18300
  }
18112
- return new Promise((resolve12) => {
18301
+ return new Promise((resolve13) => {
18113
18302
  render();
18114
18303
  process.stdin.setRawMode(true);
18115
18304
  process.stdin.resume();
@@ -18146,7 +18335,7 @@ async function promptPolicySelection(preSelected, options = {}) {
18146
18335
  ${dim(STEP_DONE)} Policies
18147
18336
  ${dim(BAR)} ${dim(`${selected.length} selected`)}
18148
18337
  `);
18149
- resolve12(selected);
18338
+ resolve13(selected);
18150
18339
  } else if (key.name === "escape") {
18151
18340
  search = "";
18152
18341
  cursor2 = 0;
@@ -18209,8 +18398,8 @@ __export(exports_manager, {
18209
18398
  });
18210
18399
  import { execSync as execSync6 } from "node:child_process";
18211
18400
  import { existsSync as existsSync12 } from "node:fs";
18212
- import { resolve as resolve12, basename as basename4 } from "node:path";
18213
- import { homedir as homedir24, platform as platform3, arch as arch3, release as release2, hostname as hostname3 } from "node:os";
18401
+ import { resolve as resolve13, basename as basename4 } from "node:path";
18402
+ import { homedir as homedir25, platform as platform3, arch as arch3, release as release2, hostname as hostname3 } from "node:os";
18214
18403
  function getSettingsPath(scope, cwd) {
18215
18404
  return claudeCode.getSettingsPath(scope, cwd);
18216
18405
  }
@@ -18317,8 +18506,8 @@ async function installHooksImpl(policyNames, scope = "user", cwd, includeBeta =
18317
18506
  delete configToWrite.customPoliciesPath;
18318
18507
  delete configToWrite.customPoliciesPaths;
18319
18508
  } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
18320
- const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve12(path3));
18321
- const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve12(path3)).filter((path3) => {
18509
+ const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve13(path3));
18510
+ const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve13(path3)).filter((path3) => {
18322
18511
  if (existsSync12(path3))
18323
18512
  return true;
18324
18513
  console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
@@ -18710,7 +18899,7 @@ Failproof AI Hook Policies
18710
18899
  console.log(`
18711
18900
  ── Custom Policies ───────────────────────`);
18712
18901
  for (const path3 of explicitPaths) {
18713
- const absPath = resolve12(findProjectConfigDir(cwd ?? process.cwd()), path3);
18902
+ const absPath = resolve13(findProjectConfigDir(cwd ?? process.cwd()), path3);
18714
18903
  console.log(` ${absPath}`);
18715
18904
  if (!existsSync12(absPath)) {
18716
18905
  console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
@@ -18731,8 +18920,8 @@ Failproof AI Hook Policies
18731
18920
  console.log();
18732
18921
  }
18733
18922
  const base = findProjectConfigDir(cwd ?? process.cwd());
18734
- const projectDir = resolve12(base, ".failproofai", "policies");
18735
- const userDir = resolve12(homedir24(), ".failproofai", "policies");
18923
+ const projectDir = resolve13(base, ".failproofai", "policies");
18924
+ const userDir = resolve13(homedir25(), ".failproofai", "policies");
18736
18925
  const sameDir = userDir === projectDir;
18737
18926
  const conventionDirs = [
18738
18927
  { label: sameDir ? "Project + User" : "Project", dir: projectDir },
@@ -18962,7 +19151,7 @@ async function promptCliTargetSelection(detected, action = "install") {
18962
19151
  lastLineCount = lines.length;
18963
19152
  }
18964
19153
  const itemCount = options.length;
18965
- return new Promise((resolve13) => {
19154
+ return new Promise((resolve14) => {
18966
19155
  render();
18967
19156
  readline3.emitKeypressEvents(process.stdin);
18968
19157
  const wasRaw = process.stdin.isRaw;
@@ -18995,7 +19184,7 @@ async function promptCliTargetSelection(detected, action = "install") {
18995
19184
  cleanup();
18996
19185
  process.stdout.write(`
18997
19186
  `);
18998
- resolve13(options[cursor2].value);
19187
+ resolve14(options[cursor2].value);
18999
19188
  }
19000
19189
  }
19001
19190
  process.stdin.on("keypress", onKey);
@@ -19148,7 +19337,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
19148
19337
  `);
19149
19338
  lastLineCount = lines.length;
19150
19339
  }
19151
- return new Promise((resolve13) => {
19340
+ return new Promise((resolve14) => {
19152
19341
  render();
19153
19342
  process.stdin.setRawMode(true);
19154
19343
  process.stdin.resume();
@@ -19185,7 +19374,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
19185
19374
  ${dim(STEP_DONE)} Policies
19186
19375
  ${dim(BAR)} ${dim(`${selected.length} selected`)}
19187
19376
  `);
19188
- resolve13(selected);
19377
+ resolve14(selected);
19189
19378
  } else if (key.name === "escape") {
19190
19379
  search = "";
19191
19380
  cursor2 = 0;
@@ -19369,7 +19558,7 @@ import {
19369
19558
  mkdirSync as mkdirSync8,
19370
19559
  renameSync as renameSync3,
19371
19560
  rmSync,
19372
- statSync as statSync11,
19561
+ statSync as statSync12,
19373
19562
  writeFileSync as writeFileSync7
19374
19563
  } from "node:fs";
19375
19564
  import { dirname as dirname6 } from "node:path";
@@ -19384,12 +19573,12 @@ function writeJsonAtomically(filePath, value, opts = {}) {
19384
19573
  try {
19385
19574
  writeFileSync7(tmp, JSON.stringify(value, null, 2), { mode });
19386
19575
  try {
19387
- if ((statSync11(tmp).mode & 63) !== 0)
19576
+ if ((statSync12(tmp).mode & 63) !== 0)
19388
19577
  chmodSync(tmp, mode);
19389
19578
  } catch {}
19390
19579
  renameSync3(tmp, filePath);
19391
19580
  try {
19392
- if ((statSync11(filePath).mode & 63) !== 0)
19581
+ if ((statSync12(filePath).mode & 63) !== 0)
19393
19582
  chmodSync(filePath, mode);
19394
19583
  } catch {}
19395
19584
  } catch (err) {
@@ -19403,13 +19592,13 @@ var init_atomic_write = () => {};
19403
19592
 
19404
19593
  // lib/auth/auth-store.ts
19405
19594
  import { existsSync as existsSync14, readFileSync as readFileSync13, rmSync as rmSync2 } from "node:fs";
19406
- import { homedir as homedir25 } from "node:os";
19595
+ import { homedir as homedir26 } from "node:os";
19407
19596
  import { join as join26 } from "node:path";
19408
19597
  function getAuthDir() {
19409
19598
  const override = process.env.FAILPROOFAI_AUTH_DIR;
19410
19599
  if (override)
19411
19600
  return override;
19412
- return join26(homedir25(), ".failproofai");
19601
+ return join26(homedir26(), ".failproofai");
19413
19602
  }
19414
19603
  function getAuthFilePath() {
19415
19604
  return join26(getAuthDir(), "auth.json");
@@ -19520,13 +19709,13 @@ function prompt(question, opts = {}) {
19520
19709
  orig(s);
19521
19710
  };
19522
19711
  }
19523
- return new Promise((resolve13) => {
19712
+ return new Promise((resolve14) => {
19524
19713
  rl.question(question, (answer) => {
19525
19714
  rl.close();
19526
19715
  if (opts.hidden && process.stdin.isTTY)
19527
19716
  process.stdout.write(`
19528
19717
  `);
19529
- resolve13(answer.trim());
19718
+ resolve14(answer.trim());
19530
19719
  });
19531
19720
  });
19532
19721
  }
@@ -19775,7 +19964,7 @@ EXAMPLES
19775
19964
  });
19776
19965
 
19777
19966
  // lib/claude-sessions.ts
19778
- import { readdirSync as readdirSync11, statSync as statSync12, existsSync as existsSync15 } from "node:fs";
19967
+ import { readdirSync as readdirSync11, statSync as statSync13, existsSync as existsSync15 } from "node:fs";
19779
19968
  import { join as join27, basename as basename5 } from "node:path";
19780
19969
  function getClaudeProjectsRoot() {
19781
19970
  return getClaudeProjectsPath();
@@ -19809,7 +19998,7 @@ function listClaudeTranscripts(project) {
19809
19998
  continue;
19810
19999
  const transcriptPath = join27(project.path, entry.name);
19811
20000
  try {
19812
- const s = statSync12(transcriptPath);
20001
+ const s = statSync13(transcriptPath);
19813
20002
  out.push({
19814
20003
  projectName: project.name,
19815
20004
  cwd: project.cwd,
@@ -19836,7 +20025,7 @@ function listClaudeTranscripts(project) {
19836
20025
  const agentId = sub.name.slice(0, -".jsonl".length);
19837
20026
  const transcriptPath = join27(subDir, sub.name);
19838
20027
  try {
19839
- const s = statSync12(transcriptPath);
20028
+ const s = statSync13(transcriptPath);
19840
20029
  out.push({
19841
20030
  projectName: project.name,
19842
20031
  cwd: project.cwd,
@@ -19980,7 +20169,7 @@ var init_claude = __esm(() => {
19980
20169
  });
19981
20170
 
19982
20171
  // src/audit/cli-adapters/codex.ts
19983
- import { statSync as statSync13 } from "node:fs";
20172
+ import { statSync as statSync14 } from "node:fs";
19984
20173
  async function listCodexTranscriptMetadata(opts = {}) {
19985
20174
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
19986
20175
  const sinceMs = opts.sinceMs ?? 0;
@@ -19997,7 +20186,7 @@ async function listCodexTranscriptMetadata(opts = {}) {
19997
20186
  continue;
19998
20187
  let sizeBytes = 0;
19999
20188
  try {
20000
- sizeBytes = statSync13(s.path).size;
20189
+ sizeBytes = statSync14(s.path).size;
20001
20190
  } catch {}
20002
20191
  if (!s.sessionId)
20003
20192
  continue;
@@ -20031,7 +20220,7 @@ var init_codex = __esm(() => {
20031
20220
  });
20032
20221
 
20033
20222
  // src/audit/cli-adapters/copilot.ts
20034
- import { statSync as statSync14 } from "node:fs";
20223
+ import { statSync as statSync15 } from "node:fs";
20035
20224
  async function listCopilotTranscriptMetadata(opts = {}) {
20036
20225
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
20037
20226
  const sinceMs = opts.sinceMs ?? 0;
@@ -20048,7 +20237,7 @@ async function listCopilotTranscriptMetadata(opts = {}) {
20048
20237
  continue;
20049
20238
  let sizeBytes = 0;
20050
20239
  try {
20051
- sizeBytes = statSync14(s.path).size;
20240
+ sizeBytes = statSync15(s.path).size;
20052
20241
  } catch {}
20053
20242
  if (!s.sessionId)
20054
20243
  continue;
@@ -20082,7 +20271,7 @@ var init_copilot = __esm(() => {
20082
20271
  });
20083
20272
 
20084
20273
  // src/audit/cli-adapters/cursor.ts
20085
- import { statSync as statSync15 } from "node:fs";
20274
+ import { statSync as statSync16 } from "node:fs";
20086
20275
  async function listCursorTranscriptMetadata(opts = {}) {
20087
20276
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
20088
20277
  const sinceMs = opts.sinceMs ?? 0;
@@ -20099,7 +20288,7 @@ async function listCursorTranscriptMetadata(opts = {}) {
20099
20288
  continue;
20100
20289
  let sizeBytes = 0;
20101
20290
  try {
20102
- sizeBytes = statSync15(s.path).size;
20291
+ sizeBytes = statSync16(s.path).size;
20103
20292
  } catch {}
20104
20293
  if (!s.sessionId)
20105
20294
  continue;
@@ -20345,7 +20534,7 @@ var init_opencode = __esm(() => {
20345
20534
  });
20346
20535
 
20347
20536
  // src/audit/cli-adapters/pi.ts
20348
- import { statSync as statSync16 } from "node:fs";
20537
+ import { statSync as statSync17 } from "node:fs";
20349
20538
  async function listPiTranscriptMetadata(opts = {}) {
20350
20539
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
20351
20540
  const sinceMs = opts.sinceMs ?? 0;
@@ -20362,7 +20551,7 @@ async function listPiTranscriptMetadata(opts = {}) {
20362
20551
  continue;
20363
20552
  let sizeBytes = 0;
20364
20553
  try {
20365
- sizeBytes = statSync16(s.path).size;
20554
+ sizeBytes = statSync17(s.path).size;
20366
20555
  } catch {}
20367
20556
  if (!s.sessionId)
20368
20557
  continue;
@@ -21092,7 +21281,7 @@ var init_features = __esm(() => {
21092
21281
  import { createHash } from "node:crypto";
21093
21282
  import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync8, chmodSync as chmodSync2 } from "node:fs";
21094
21283
  import { join as join28 } from "node:path";
21095
- import { homedir as homedir26 } from "node:os";
21284
+ import { homedir as homedir27 } from "node:os";
21096
21285
  function getEngineVersion() {
21097
21286
  if (cachedEngineVersion)
21098
21287
  return cachedEngineVersion;
@@ -21110,7 +21299,7 @@ function getDetectorVersion() {
21110
21299
  return cachedDetectorVersion;
21111
21300
  }
21112
21301
  function getCachePathFor(transcriptPath) {
21113
- const root = join28(homedir26(), ".failproofai", "cache", "audit");
21302
+ const root = join28(homedir27(), ".failproofai", "cache", "audit");
21114
21303
  const key = createHash("sha1").update(transcriptPath).digest("hex");
21115
21304
  return join28(root, `${key}.json`);
21116
21305
  }
@@ -21145,7 +21334,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
21145
21334
  return;
21146
21335
  const cachePath = getCachePathFor(transcriptPath);
21147
21336
  try {
21148
- mkdirSync9(join28(homedir26(), ".failproofai", "cache", "audit"), { recursive: true });
21337
+ mkdirSync9(join28(homedir27(), ".failproofai", "cache", "audit"), { recursive: true });
21149
21338
  const entry = {
21150
21339
  schemaVersion: CACHE_SCHEMA_VERSION,
21151
21340
  cachedAt: Date.now(),
@@ -21518,9 +21707,9 @@ var init_audit = __esm(() => {
21518
21707
 
21519
21708
  // src/audit/dashboard-cache.ts
21520
21709
  import { join as join29 } from "node:path";
21521
- import { homedir as homedir27 } from "node:os";
21710
+ import { homedir as homedir28 } from "node:os";
21522
21711
  function getCachePath() {
21523
- return join29(homedir27(), ".failproofai", "audit-dashboard.json");
21712
+ return join29(homedir28(), ".failproofai", "audit-dashboard.json");
21524
21713
  }
21525
21714
  function writeDashboardCache(params, result) {
21526
21715
  try {
@@ -21610,14 +21799,14 @@ var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
21610
21799
  var init_open_browser = () => {};
21611
21800
 
21612
21801
  // scripts/parse-script-args.ts
21613
- import { resolve as resolve13 } from "path";
21802
+ import { resolve as resolve14 } from "path";
21614
21803
  function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
21615
21804
  const raw = inlineValue ?? args[index + 1];
21616
21805
  if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
21617
21806
  console.error(`Error: ${flagName} requires ${errorLabel}`);
21618
21807
  process.exit(1);
21619
21808
  }
21620
- const value = options?.resolve ? resolve13(raw) : raw;
21809
+ const value = options?.resolve ? resolve14(raw) : raw;
21621
21810
  return { value, spliceCount: inlineValue !== null ? 1 : 2 };
21622
21811
  }
21623
21812
  function parseScriptArgs(argv) {
@@ -21666,17 +21855,17 @@ var init_parse_script_args = () => {};
21666
21855
 
21667
21856
  // scripts/install-diagnosis.mjs
21668
21857
  import { existsSync as existsSync17, readFileSync as readFileSync15, realpathSync } from "node:fs";
21669
- import { dirname as dirname7, resolve as resolve14 } from "node:path";
21670
- import { homedir as homedir28, platform as platform4 } from "node:os";
21858
+ import { dirname as dirname7, resolve as resolve15 } from "node:path";
21859
+ import { homedir as homedir29, platform as platform4 } from "node:os";
21671
21860
  import { spawnSync } from "node:child_process";
21672
21861
  function findPackageRoot(start) {
21673
21862
  try {
21674
21863
  let dir = realpathSync(start);
21675
- if (existsSync17(dir) && !existsSync17(resolve14(dir, "package.json"))) {
21864
+ if (existsSync17(dir) && !existsSync17(resolve15(dir, "package.json"))) {
21676
21865
  dir = dirname7(dir);
21677
21866
  }
21678
21867
  while (true) {
21679
- const pkgPath = resolve14(dir, "package.json");
21868
+ const pkgPath = resolve15(dir, "package.json");
21680
21869
  if (existsSync17(pkgPath)) {
21681
21870
  try {
21682
21871
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
@@ -21697,7 +21886,7 @@ function readPackageVersion(packageRoot) {
21697
21886
  if (!packageRoot)
21698
21887
  return null;
21699
21888
  try {
21700
- const pkg = JSON.parse(readFileSync15(resolve14(packageRoot, "package.json"), "utf8"));
21889
+ const pkg = JSON.parse(readFileSync15(resolve15(packageRoot, "package.json"), "utf8"));
21701
21890
  return typeof pkg.version === "string" ? pkg.version : null;
21702
21891
  } catch {
21703
21892
  return null;
@@ -21723,16 +21912,16 @@ function locateNpmGlobal() {
21723
21912
  const root = (res.stdout || "").trim();
21724
21913
  if (!root)
21725
21914
  return null;
21726
- const candidate = resolve14(root, PKG_NAME);
21727
- return existsSync17(resolve14(candidate, "package.json")) ? candidate : null;
21915
+ const candidate = resolve15(root, PKG_NAME);
21916
+ return existsSync17(resolve15(candidate, "package.json")) ? candidate : null;
21728
21917
  } catch {
21729
21918
  return null;
21730
21919
  }
21731
21920
  }
21732
21921
  function locateBunGlobal() {
21733
21922
  try {
21734
- const candidate = resolve14(homedir28(), ".bun", "install", "global", "node_modules", PKG_NAME);
21735
- return existsSync17(resolve14(candidate, "package.json")) ? candidate : null;
21923
+ const candidate = resolve15(homedir29(), ".bun", "install", "global", "node_modules", PKG_NAME);
21924
+ return existsSync17(resolve15(candidate, "package.json")) ? candidate : null;
21736
21925
  } catch {
21737
21926
  return null;
21738
21927
  }
@@ -21740,8 +21929,8 @@ function locateBunGlobal() {
21740
21929
  function buildRecommendation(pathFirstBin) {
21741
21930
  if (!pathFirstBin)
21742
21931
  return null;
21743
- const bunBinPrefix = resolve14(homedir28(), ".bun", "bin") + "/";
21744
- const bunGlobalPrefix = resolve14(homedir28(), ".bun", "install", "global") + "/";
21932
+ const bunBinPrefix = resolve15(homedir29(), ".bun", "bin") + "/";
21933
+ const bunGlobalPrefix = resolve15(homedir29(), ".bun", "install", "global") + "/";
21745
21934
  const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
21746
21935
  if (isBun) {
21747
21936
  return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
@@ -21822,7 +22011,7 @@ __export(exports_launch, {
21822
22011
  });
21823
22012
  import { spawn as spawn2 } from "child_process";
21824
22013
  import { realpathSync as realpathSync2, existsSync as existsSync18 } from "node:fs";
21825
- import { resolve as resolve15, dirname as dirname8 } from "node:path";
22014
+ import { resolve as resolve16, dirname as dirname8 } from "node:path";
21826
22015
  import { fileURLToPath as fileURLToPath2 } from "node:url";
21827
22016
  import { createInterface as createInterface2 } from "node:readline";
21828
22017
  function launch(mode) {
@@ -21837,8 +22026,8 @@ function launch(mode) {
21837
22026
  process.env.PORT = port;
21838
22027
  process.env.HOSTNAME = "0.0.0.0";
21839
22028
  cmd = "node";
21840
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve15(dirname8(realpathSync2(fileURLToPath2(import.meta.url))), "..");
21841
- const serverJsPath = resolve15(packageRoot, ".next/standalone/server.js");
22029
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve16(dirname8(realpathSync2(fileURLToPath2(import.meta.url))), "..");
22030
+ const serverJsPath = resolve16(packageRoot, ".next/standalone/server.js");
21842
22031
  if (!existsSync18(serverJsPath)) {
21843
22032
  let shadowMessage = null;
21844
22033
  try {
@@ -22192,8 +22381,8 @@ WHAT IT DOES
22192
22381
  // src/hooks/manager.ts
22193
22382
  import { execSync as execSync7 } from "node:child_process";
22194
22383
  import { existsSync as existsSync19 } from "node:fs";
22195
- import { resolve as resolve16, basename as basename6 } from "node:path";
22196
- import { homedir as homedir29, platform as platform5, arch as arch4, release as release3, hostname as hostname4 } from "node:os";
22384
+ import { resolve as resolve17, basename as basename6 } from "node:path";
22385
+ import { homedir as homedir30, platform as platform5, arch as arch4, release as release3, hostname as hostname4 } from "node:os";
22197
22386
  function getSettingsPath2(scope, cwd) {
22198
22387
  return claudeCode.getSettingsPath(scope, cwd);
22199
22388
  }
@@ -22300,8 +22489,8 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
22300
22489
  delete configToWrite.customPoliciesPath;
22301
22490
  delete configToWrite.customPoliciesPaths;
22302
22491
  } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
22303
- const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve16(path3));
22304
- const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve16(path3)).filter((path3) => {
22492
+ const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve17(path3));
22493
+ const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve17(path3)).filter((path3) => {
22305
22494
  if (existsSync19(path3))
22306
22495
  return true;
22307
22496
  console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
@@ -22498,6 +22687,314 @@ var init_policy_presets = __esm(() => {
22498
22687
  ];
22499
22688
  });
22500
22689
 
22690
+ // src/hooks/daemon-download.ts
22691
+ import { createHash as createHash2 } from "node:crypto";
22692
+ import { chmodSync as chmodSync3, existsSync as existsSync20, mkdirSync as mkdirSync10, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "node:fs";
22693
+ import { homedir as homedir31 } from "node:os";
22694
+ import { resolve as resolve18 } from "node:path";
22695
+ import { gunzipSync } from "node:zlib";
22696
+ function baseUrl() {
22697
+ return (process.env.FAILPROOFAI_DAEMON_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
22698
+ }
22699
+ function daemonBinaryDir() {
22700
+ return resolve18(homedir31(), ".failproofai", "bin");
22701
+ }
22702
+ function installedBinaryPath(ver = version2) {
22703
+ return resolve18(daemonBinaryDir(), `failproofaid-${ver}`);
22704
+ }
22705
+ function daemonAssetUrl(key, ver = version2) {
22706
+ return `${baseUrl()}/v${ver}/failproofaid-${key}.gz`;
22707
+ }
22708
+ function checksumsUrl(ver = version2) {
22709
+ return `${baseUrl()}/v${ver}/SHA256SUMS`;
22710
+ }
22711
+ async function fetchBytes(url) {
22712
+ const response = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
22713
+ if (!response.ok)
22714
+ throw new Error(`GET ${url} returned ${response.status}`);
22715
+ return Buffer.from(await response.arrayBuffer());
22716
+ }
22717
+ function digestFor(manifest, assetName) {
22718
+ for (const line of manifest.split(`
22719
+ `)) {
22720
+ const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
22721
+ if (match && match[2].trim() === assetName)
22722
+ return match[1].toLowerCase();
22723
+ }
22724
+ return null;
22725
+ }
22726
+ async function downloadFailproofaidBinary(key) {
22727
+ const target = installedBinaryPath();
22728
+ if (existsSync20(target))
22729
+ return { path: target };
22730
+ if (process.env.FAILPROOFAI_NO_DOWNLOAD) {
22731
+ return { error: "daemon downloads are disabled (FAILPROOFAI_NO_DOWNLOAD)" };
22732
+ }
22733
+ const assetName = `failproofaid-${key}.gz`;
22734
+ const tempPath = `${target}.${process.pid}.tmp`;
22735
+ try {
22736
+ const [asset, manifest] = await Promise.all([
22737
+ fetchBytes(daemonAssetUrl(key)),
22738
+ fetchBytes(checksumsUrl()).then((b) => b.toString("utf8"))
22739
+ ]);
22740
+ const expected = digestFor(manifest, assetName);
22741
+ if (!expected) {
22742
+ return { error: `SHA256SUMS for v${version2} has no entry for ${assetName}` };
22743
+ }
22744
+ const actual = createHash2("sha256").update(asset).digest("hex");
22745
+ if (actual !== expected) {
22746
+ return { error: `checksum mismatch for ${assetName} (expected ${expected}, got ${actual})` };
22747
+ }
22748
+ const binary = gunzipSync(asset);
22749
+ mkdirSync10(daemonBinaryDir(), { recursive: true });
22750
+ writeFileSync9(tempPath, binary);
22751
+ chmodSync3(tempPath, 493);
22752
+ renameSync4(tempPath, target);
22753
+ return { path: target };
22754
+ } catch (err) {
22755
+ rmSync3(tempPath, { force: true });
22756
+ const message = err instanceof Error ? err.message : String(err);
22757
+ return { error: `failed to download failproofaid v${version2} for ${key}: ${message}` };
22758
+ }
22759
+ }
22760
+ var DEFAULT_BASE_URL = "https://github.com/FailproofAI/failproofai/releases/download", DOWNLOAD_TIMEOUT_MS = 60000;
22761
+ var init_daemon_download = __esm(() => {
22762
+ init_package();
22763
+ });
22764
+
22765
+ // src/hooks/daemon-service.ts
22766
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync10, unlinkSync as unlinkSync3 } from "node:fs";
22767
+ import { homedir as homedir32 } from "node:os";
22768
+ import { resolve as resolve19, dirname as dirname9 } from "node:path";
22769
+ import { execFileSync as execFileSync4 } from "node:child_process";
22770
+ function setDaemonConfigured(value) {
22771
+ const path3 = getConfigPathForScope("user");
22772
+ let config = {};
22773
+ try {
22774
+ if (existsSync21(path3))
22775
+ config = JSON.parse(readFileSync16(path3, "utf8"));
22776
+ } catch {
22777
+ return;
22778
+ }
22779
+ if (value)
22780
+ config.daemonConfigured = true;
22781
+ else
22782
+ delete config.daemonConfigured;
22783
+ try {
22784
+ mkdirSync11(dirname9(path3), { recursive: true });
22785
+ writeFileSync10(path3, JSON.stringify(config, null, 2) + `
22786
+ `, "utf8");
22787
+ } catch {}
22788
+ }
22789
+ function isDaemonSupportedPlatform() {
22790
+ return process.platform === "linux" || process.platform === "darwin";
22791
+ }
22792
+ function platformKey() {
22793
+ const os4 = process.platform === "linux" ? "linux" : process.platform === "darwin" ? "darwin" : null;
22794
+ const arch5 = process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : null;
22795
+ if (!os4 || !arch5)
22796
+ return null;
22797
+ return `${os4}-${arch5}`;
22798
+ }
22799
+ function resolveFailproofaidBinaryPath() {
22800
+ if (process.env.FAILPROOFAI_DAEMON_BINARY)
22801
+ return process.env.FAILPROOFAI_DAEMON_BINARY;
22802
+ const downloaded = installedBinaryPath();
22803
+ if (existsSync21(downloaded))
22804
+ return downloaded;
22805
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
22806
+ if (packageRoot) {
22807
+ for (const profile of ["release", "debug"]) {
22808
+ const candidate = resolve19(packageRoot, "target", profile, "failproofaid");
22809
+ if (existsSync21(candidate))
22810
+ return candidate;
22811
+ }
22812
+ }
22813
+ return null;
22814
+ }
22815
+ async function ensureFailproofaidBinary() {
22816
+ const existing = resolveFailproofaidBinaryPath();
22817
+ if (existing)
22818
+ return { path: existing };
22819
+ const key = platformKey();
22820
+ if (!key) {
22821
+ return { reason: `failproofaid has no prebuilt binary for ${process.platform}/${process.arch}` };
22822
+ }
22823
+ const result = await downloadFailproofaidBinary(key);
22824
+ if (result.path)
22825
+ return { path: result.path };
22826
+ return { reason: result.error ?? "failproofaid binary could not be downloaded" };
22827
+ }
22828
+ function resolveWorkerCommand() {
22829
+ if (process.env.FAILPROOFAI_WORKER_CMD)
22830
+ return process.env.FAILPROOFAI_WORKER_CMD;
22831
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
22832
+ if (!packageRoot)
22833
+ return null;
22834
+ const workerScript = resolve19(packageRoot, "dist", "worker.mjs");
22835
+ if (!existsSync21(workerScript))
22836
+ return null;
22837
+ return `node ${workerScript}`;
22838
+ }
22839
+ function systemdUnitPath() {
22840
+ return resolve19(homedir32(), ".config", "systemd", "user", "failproofaid.service");
22841
+ }
22842
+ function launchdPlistPath() {
22843
+ return resolve19(homedir32(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
22844
+ }
22845
+ function daemonServiceFilePath() {
22846
+ if (!isDaemonSupportedPlatform())
22847
+ return null;
22848
+ return process.platform === "linux" ? systemdUnitPath() : launchdPlistPath();
22849
+ }
22850
+ function systemdUnitContents(binaryPath, workerCmd) {
22851
+ const envLine = workerCmd ? `Environment="FAILPROOFAI_WORKER_CMD=${workerCmd}"
22852
+ ` : "";
22853
+ return `[Unit]
22854
+ Description=failproofai background daemon (failproofaid)
22855
+ After=network.target
22856
+
22857
+ [Service]
22858
+ Type=simple
22859
+ ${envLine}ExecStart=${binaryPath}
22860
+ Restart=on-failure
22861
+ RestartSec=2
22862
+
22863
+ [Install]
22864
+ WantedBy=default.target
22865
+ `;
22866
+ }
22867
+ function escapeXml(s) {
22868
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22869
+ }
22870
+ function launchdPlistContents(binaryPath, logDir2, workerCmd) {
22871
+ const envBlock = workerCmd ? ` <key>EnvironmentVariables</key>
22872
+ <dict>
22873
+ <key>FAILPROOFAI_WORKER_CMD</key>
22874
+ <string>${escapeXml(workerCmd)}</string>
22875
+ </dict>
22876
+ ` : "";
22877
+ return `<?xml version="1.0" encoding="UTF-8"?>
22878
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
22879
+ <plist version="1.0">
22880
+ <dict>
22881
+ <key>Label</key>
22882
+ <string>ai.failproof.failproofaid</string>
22883
+ <key>ProgramArguments</key>
22884
+ <array>
22885
+ <string>${escapeXml(binaryPath)}</string>
22886
+ </array>
22887
+ <key>RunAtLoad</key>
22888
+ <true/>
22889
+ <key>KeepAlive</key>
22890
+ <true/>
22891
+ ${envBlock} <key>StandardOutPath</key>
22892
+ <string>${escapeXml(resolve19(logDir2, "failproofaid.log"))}</string>
22893
+ <key>StandardErrorPath</key>
22894
+ <string>${escapeXml(resolve19(logDir2, "failproofaid.err.log"))}</string>
22895
+ </dict>
22896
+ </plist>
22897
+ `;
22898
+ }
22899
+ async function installDaemonService() {
22900
+ if (!isDaemonSupportedPlatform()) {
22901
+ return { installed: false, reason: `failproofaid is not supported on ${process.platform} yet` };
22902
+ }
22903
+ const { path: binaryPath, reason: binaryReason } = await ensureFailproofaidBinary();
22904
+ if (!binaryPath) {
22905
+ return { installed: false, reason: binaryReason ?? "failproofaid binary not found for this platform" };
22906
+ }
22907
+ const workerCmd = resolveWorkerCommand();
22908
+ try {
22909
+ if (process.platform === "linux") {
22910
+ const unitPath = systemdUnitPath();
22911
+ mkdirSync11(dirname9(unitPath), { recursive: true });
22912
+ writeFileSync10(unitPath, systemdUnitContents(binaryPath, workerCmd), "utf8");
22913
+ execFileSync4("systemctl", ["--user", "daemon-reload"], {
22914
+ stdio: "ignore",
22915
+ timeout: SERVICE_CMD_TIMEOUT_MS
22916
+ });
22917
+ execFileSync4("systemctl", ["--user", "enable", "--now", "failproofaid.service"], {
22918
+ stdio: "ignore",
22919
+ timeout: SERVICE_CMD_TIMEOUT_MS
22920
+ });
22921
+ } else {
22922
+ const plistPath = launchdPlistPath();
22923
+ const logDir2 = resolve19(homedir32(), ".failproofai", "logs");
22924
+ mkdirSync11(dirname9(plistPath), { recursive: true });
22925
+ mkdirSync11(logDir2, { recursive: true });
22926
+ writeFileSync10(plistPath, launchdPlistContents(binaryPath, logDir2, workerCmd), "utf8");
22927
+ try {
22928
+ execFileSync4("launchctl", ["unload", plistPath], {
22929
+ stdio: "ignore",
22930
+ timeout: SERVICE_CMD_TIMEOUT_MS
22931
+ });
22932
+ } catch {}
22933
+ execFileSync4("launchctl", ["load", "-w", plistPath], {
22934
+ stdio: "ignore",
22935
+ timeout: SERVICE_CMD_TIMEOUT_MS
22936
+ });
22937
+ }
22938
+ } catch (err) {
22939
+ const msg = err instanceof Error ? err.message : String(err);
22940
+ hookLogWarn(`daemon service install failed: ${msg}`);
22941
+ return { installed: false, reason: msg };
22942
+ }
22943
+ if (!await waitForDaemonRunning()) {
22944
+ const reason = `failproofaid was installed but did not reach a running state within ${SERVICE_START_TIMEOUT_MS}ms (status: ${daemonServiceStatus()})`;
22945
+ hookLogWarn(`daemon service install failed: ${reason}`);
22946
+ return { installed: false, reason };
22947
+ }
22948
+ return { installed: true };
22949
+ }
22950
+ async function waitForDaemonRunning() {
22951
+ const deadline = Date.now() + SERVICE_START_TIMEOUT_MS;
22952
+ for (;; ) {
22953
+ if (daemonServiceStatus() === "running")
22954
+ break;
22955
+ if (Date.now() >= deadline)
22956
+ return false;
22957
+ await new Promise((r) => setTimeout(r, SERVICE_START_POLL_MS));
22958
+ }
22959
+ await new Promise((r) => setTimeout(r, SERVICE_SETTLE_MS));
22960
+ return daemonServiceStatus() === "running";
22961
+ }
22962
+ function daemonServiceStatus() {
22963
+ if (!isDaemonSupportedPlatform())
22964
+ return "unsupported-platform";
22965
+ if (process.platform === "linux") {
22966
+ if (!existsSync21(systemdUnitPath()))
22967
+ return "not-installed";
22968
+ try {
22969
+ const out = execFileSync4("systemctl", ["--user", "is-active", "failproofaid.service"], {
22970
+ stdio: ["ignore", "pipe", "ignore"],
22971
+ timeout: SERVICE_CMD_TIMEOUT_MS
22972
+ }).toString().trim();
22973
+ return out === "active" ? "running" : "stopped";
22974
+ } catch {
22975
+ return "stopped";
22976
+ }
22977
+ }
22978
+ const plistPath = launchdPlistPath();
22979
+ if (!existsSync21(plistPath))
22980
+ return "not-installed";
22981
+ try {
22982
+ const out = execFileSync4("launchctl", ["list"], {
22983
+ stdio: ["ignore", "pipe", "ignore"],
22984
+ timeout: SERVICE_CMD_TIMEOUT_MS
22985
+ }).toString();
22986
+ return out.includes("ai.failproof.failproofaid") ? "running" : "stopped";
22987
+ } catch {
22988
+ return "stopped";
22989
+ }
22990
+ }
22991
+ var SERVICE_CMD_TIMEOUT_MS = 1e4, SERVICE_START_TIMEOUT_MS = 5000, SERVICE_START_POLL_MS = 100, SERVICE_SETTLE_MS = 750;
22992
+ var init_daemon_service = __esm(() => {
22993
+ init_hook_logger();
22994
+ init_hooks_config();
22995
+ init_daemon_download();
22996
+ });
22997
+
22501
22998
  // src/audit/cli.ts
22502
22999
  var exports_cli3 = {};
22503
23000
  __export(exports_cli3, {
@@ -22784,20 +23281,21 @@ __export(exports_configure_wizard, {
22784
23281
  hasSeenLauncher: () => hasSeenLauncher,
22785
23282
  describeCustomPolicies: () => describeCustomPolicies,
22786
23283
  clisSupportingScope: () => clisSupportingScope,
23284
+ classifyDaemonInstallFailure: () => classifyDaemonInstallFailure,
22787
23285
  buildScopeChoices: () => buildScopeChoices,
22788
23286
  buildPresetChoices: () => buildPresetChoices,
22789
23287
  buildAgentChoices: () => buildAgentChoices
22790
23288
  });
22791
- import { existsSync as existsSync20, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync16 } from "node:fs";
22792
- import { homedir as homedir30 } from "node:os";
22793
- import { resolve as resolve17, sep as sep5 } from "node:path";
23289
+ import { existsSync as existsSync22, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11, readFileSync as readFileSync17 } from "node:fs";
23290
+ import { homedir as homedir33 } from "node:os";
23291
+ import { resolve as resolve20, sep as sep5 } from "node:path";
22794
23292
  async function emit(event, props) {
22795
23293
  try {
22796
23294
  await trackHookEvent2(getInstanceId2(), event, props);
22797
23295
  } catch {}
22798
23296
  }
22799
23297
  function homeify(p) {
22800
- const home = homedir30();
23298
+ const home = homedir33();
22801
23299
  if (p === home)
22802
23300
  return "~";
22803
23301
  if (p.startsWith(home + sep5))
@@ -22911,8 +23409,8 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
22911
23409
  const path3 = getConfigPathForScope(scope, cwd);
22912
23410
  let config = {};
22913
23411
  try {
22914
- if (existsSync20(path3))
22915
- config = JSON.parse(readFileSync16(path3, "utf8"));
23412
+ if (existsSync22(path3))
23413
+ config = JSON.parse(readFileSync17(path3, "utf8"));
22916
23414
  } catch {
22917
23415
  return;
22918
23416
  }
@@ -22921,18 +23419,37 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
22921
23419
  else
22922
23420
  config.customPoliciesEnabled = false;
22923
23421
  try {
22924
- writeFileSync9(path3, JSON.stringify(config, null, 2) + `
23422
+ writeFileSync11(path3, JSON.stringify(config, null, 2) + `
22925
23423
  `, "utf8");
22926
23424
  } catch {}
22927
23425
  }
23426
+ function classifyDaemonInstallFailure(reason) {
23427
+ if (!reason)
23428
+ return "unknown";
23429
+ if (/not supported on/.test(reason))
23430
+ return "unsupported_platform";
23431
+ if (/no prebuilt binary for/.test(reason))
23432
+ return "unsupported_platform";
23433
+ if (/binary not found/.test(reason))
23434
+ return "binary_not_found";
23435
+ if (/checksum mismatch|has no entry for/.test(reason))
23436
+ return "checksum_mismatch";
23437
+ if (/downloads are disabled/.test(reason))
23438
+ return "downloads_disabled";
23439
+ if (/failed to download/.test(reason))
23440
+ return "download_failed";
23441
+ if (/did not reach a running state/.test(reason))
23442
+ return "did_not_start";
23443
+ return "service_manager_error";
23444
+ }
22928
23445
  function describeCustomPolicies(cwd) {
22929
23446
  const active = [];
22930
23447
  const warnings = [];
22931
23448
  const scopes = [];
22932
23449
  let fileCount = 0;
22933
23450
  const dirs = [
22934
- { dir: resolve17(cwd, ".failproofai", "policies"), label: "project" },
22935
- { dir: resolve17(homedir30(), ".failproofai", "policies"), label: "global" }
23451
+ { dir: resolve20(cwd, ".failproofai", "policies"), label: "project" },
23452
+ { dir: resolve20(homedir33(), ".failproofai", "policies"), label: "global" }
22936
23453
  ];
22937
23454
  for (const { dir, label } of dirs) {
22938
23455
  const found = discoverPolicyFiles(dir);
@@ -22942,7 +23459,7 @@ function describeCustomPolicies(cwd) {
22942
23459
  fileCount += found.length;
22943
23460
  }
22944
23461
  for (const name of findSkippedPolicyFiles(dir)) {
22945
- warnings.push(`! ${homeify(resolve17(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
23462
+ warnings.push(`! ${homeify(resolve20(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
22946
23463
  }
22947
23464
  }
22948
23465
  return { active, warnings, fileCount, scopes };
@@ -22955,6 +23472,9 @@ function reviewLines(state) {
22955
23472
  lines.push(` Where : ${where}`);
22956
23473
  lines.push(` Assistants : ${assistantNames.length ? summarize(assistantNames, "assistants") : "(none)"}`);
22957
23474
  lines.push(` Policies : ${policies.length} enabled`);
23475
+ if (scope === "user" && isDaemonSupportedPlatform()) {
23476
+ lines.push(` Daemon : failproofaid will be installed/started as a background service`);
23477
+ }
22958
23478
  const custom = describeCustomPolicies(cwd);
22959
23479
  if (custom.active.length > 0) {
22960
23480
  lines.push(` Custom : ${custom.active.join(" · ")} ${customEnabled === false ? "— DISABLED, will not load" : DIM_NOTE}`);
@@ -22970,18 +23490,23 @@ function reviewLines(state) {
22970
23490
  }
22971
23491
  }
22972
23492
  lines.push(` ${homeify(getConfigPathForScope(scope, cwd))} ${policies.length} policies`);
23493
+ if (scope === "user" && isDaemonSupportedPlatform()) {
23494
+ const servicePath = daemonServiceFilePath();
23495
+ if (servicePath)
23496
+ lines.push(` ${homeify(servicePath)} failproofaid service`);
23497
+ }
22973
23498
  return lines;
22974
23499
  }
22975
23500
  function firstRunMarkerPath() {
22976
- return resolve17(homedir30(), ".failproofai", ".launcher-configured");
23501
+ return resolve20(homedir33(), ".failproofai", ".launcher-configured");
22977
23502
  }
22978
23503
  function hasSeenLauncher() {
22979
- return existsSync20(firstRunMarkerPath());
23504
+ return existsSync22(firstRunMarkerPath());
22980
23505
  }
22981
23506
  function markLauncherSeen() {
22982
23507
  try {
22983
- mkdirSync10(resolve17(homedir30(), ".failproofai"), { recursive: true });
22984
- writeFileSync9(firstRunMarkerPath(), "1", "utf8");
23508
+ mkdirSync12(resolve20(homedir33(), ".failproofai"), { recursive: true });
23509
+ writeFileSync11(firstRunMarkerPath(), "1", "utf8");
22985
23510
  } catch {}
22986
23511
  }
22987
23512
  function anyHooksInstalledGlobally() {
@@ -23104,11 +23629,27 @@ Use the flag form instead, e.g.:
23104
23629
  });
23105
23630
  await installHooks2(policies, scope, cwd, false, "configure-wizard", undefined, false, clis, { replace: true, quiet: true });
23106
23631
  setCustomPoliciesEnabled(scope, cwd, customEnabled);
23632
+ let daemonInstalled = false;
23633
+ if (scope === "user" && isDaemonSupportedPlatform()) {
23634
+ const daemonResult = await installDaemonService();
23635
+ if (daemonResult.installed) {
23636
+ setDaemonConfigured(true);
23637
+ daemonInstalled = true;
23638
+ } else {
23639
+ hookLogWarn(`failproofaid was not installed as a service: ${daemonResult.reason}`);
23640
+ }
23641
+ emit("configure_daemon_install", {
23642
+ installed: daemonResult.installed,
23643
+ reason: daemonResult.installed ? null : classifyDaemonInstallFailure(daemonResult.reason),
23644
+ platform: process.platform
23645
+ });
23646
+ }
23107
23647
  await applied;
23108
23648
  markLauncherSeen();
23109
23649
  const customNote = customEnabled === true ? " + your custom policies" : customEnabled === false ? " · custom policies DISABLED" : "";
23650
+ const daemonNote = daemonInstalled ? " · background daemon enabled" : "";
23110
23651
  const assistants = `${clis.length} assistant${clis.length === 1 ? "" : "s"}`;
23111
- outro(`Setup complete — ${policies.length} policies${customNote} · ${assistants}`, { ok: true }, stdout);
23652
+ outro(`Setup complete — ${policies.length} policies${customNote} · ${assistants}${daemonNote}`, { ok: true }, stdout);
23112
23653
  return { applied: true, scope, clis, policies };
23113
23654
  }
23114
23655
  var EVERYTHING = "__everything__", ALL_CLIS = "__all_clis__", CUSTOM = "__custom__", DIM_NOTE = "(auto-loaded)";
@@ -23122,6 +23663,8 @@ var init_configure_wizard = __esm(() => {
23122
23663
  init_custom_hooks_loader();
23123
23664
  init_hook_telemetry2();
23124
23665
  init_telemetry_id2();
23666
+ init_daemon_service();
23667
+ init_hook_logger();
23125
23668
  });
23126
23669
 
23127
23670
  // scripts/launch.ts
@@ -23130,8 +23673,8 @@ __export(exports_launch2, {
23130
23673
  launch: () => launch2
23131
23674
  });
23132
23675
  import { spawn as spawn3 } from "child_process";
23133
- import { realpathSync as realpathSync3, existsSync as existsSync21 } from "node:fs";
23134
- import { resolve as resolve18, dirname as dirname9 } from "node:path";
23676
+ import { realpathSync as realpathSync3, existsSync as existsSync23 } from "node:fs";
23677
+ import { resolve as resolve21, dirname as dirname10 } from "node:path";
23135
23678
  import { fileURLToPath as fileURLToPath3 } from "node:url";
23136
23679
  import { createInterface as createInterface3 } from "node:readline";
23137
23680
  function launch2(mode) {
@@ -23146,9 +23689,9 @@ function launch2(mode) {
23146
23689
  process.env.PORT = port;
23147
23690
  process.env.HOSTNAME = "0.0.0.0";
23148
23691
  cmd = "node";
23149
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve18(dirname9(realpathSync3(fileURLToPath3(import.meta.url))), "..");
23150
- const serverJsPath = resolve18(packageRoot, ".next/standalone/server.js");
23151
- if (!existsSync21(serverJsPath)) {
23692
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve21(dirname10(realpathSync3(fileURLToPath3(import.meta.url))), "..");
23693
+ const serverJsPath = resolve21(packageRoot, ".next/standalone/server.js");
23694
+ if (!existsSync23(serverJsPath)) {
23152
23695
  let shadowMessage = null;
23153
23696
  try {
23154
23697
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -23243,17 +23786,17 @@ var init_cli_error2 = __esm(() => {
23243
23786
 
23244
23787
  // bin/failproofai.mjs
23245
23788
  import { realpathSync as realpathSync4 } from "fs";
23246
- import { dirname as dirname10, resolve as resolve19 } from "path";
23789
+ import { dirname as dirname11, resolve as resolve22 } from "path";
23247
23790
  import { fileURLToPath as fileURLToPath4 } from "url";
23248
23791
  // package.json
23249
- var version = "0.0.15";
23792
+ var version = "1.0.0-beta.0";
23250
23793
 
23251
23794
  // bin/failproofai.mjs
23252
23795
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
23253
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve19(dirname10(realpathSync4(fileURLToPath4(import.meta.url))), "..");
23796
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve22(dirname11(realpathSync4(fileURLToPath4(import.meta.url))), "..");
23254
23797
  }
23255
23798
  if (!process.env.FAILPROOFAI_DIST_PATH) {
23256
- process.env.FAILPROOFAI_DIST_PATH = resolve19(dirname10(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
23799
+ process.env.FAILPROOFAI_DIST_PATH = resolve22(dirname11(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
23257
23800
  }
23258
23801
  var args = process.argv.slice(2);
23259
23802
  if (args[0] === "p")
@@ -23275,6 +23818,18 @@ async function track(name, props) {
23275
23818
  await _telemetry.trackHookEvent(_telemetry.getInstanceId(), name, props);
23276
23819
  } catch {}
23277
23820
  }
23821
+ async function exitAfterFlush(code) {
23822
+ const drain = (stream) => new Promise((resolveDrain) => {
23823
+ if (stream.writableLength === 0 || stream.destroyed)
23824
+ resolveDrain();
23825
+ else
23826
+ stream.write("", () => resolveDrain());
23827
+ });
23828
+ try {
23829
+ await Promise.all([drain(process.stdout), drain(process.stderr)]);
23830
+ } catch {}
23831
+ process.exit(code);
23832
+ }
23278
23833
  var hookIdx = args.indexOf("--hook");
23279
23834
  if (hookIdx >= 0) {
23280
23835
  if (!args[hookIdx + 1]) {
@@ -23287,9 +23842,32 @@ if (hookIdx >= 0) {
23287
23842
  const cliArg = cliIdx >= 0 ? args[cliIdx + 1] : undefined;
23288
23843
  const cli = cliArg && (cliArg === "claude" || cliArg === "codex" || cliArg === "copilot" || cliArg === "cursor" || cliArg === "opencode" || cliArg === "pi" || cliArg === "hermes" || cliArg === "openclaw" || cliArg === "factory" || cliArg === "devin" || cliArg === "antigravity" || cliArg === "goose") ? cliArg : "claude";
23289
23844
  try {
23845
+ const { isDaemonConfigured: isDaemonConfigured2, tryDaemonHook: tryDaemonHook2 } = await Promise.resolve().then(() => (init_daemon_client(), exports_daemon_client));
23846
+ if (isDaemonConfigured2()) {
23847
+ const { readStdinPayload: readStdinPayload3 } = await Promise.resolve().then(() => exports_read_stdin);
23848
+ const { evaluateHookEvent: evaluateHookEvent2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
23849
+ const stdinRead = await readStdinPayload3();
23850
+ const daemonResult = await tryDaemonHook2({
23851
+ hookEvent: eventType,
23852
+ cli,
23853
+ stdin: stdinRead.payload,
23854
+ cwd: process.cwd()
23855
+ });
23856
+ const result = daemonResult ?? await evaluateHookEvent2(eventType, cli, stdinRead.payload, {
23857
+ forceDecision: {
23858
+ decision: "deny",
23859
+ reason: "failproofaid could not be reached. This machine is configured to run hooks through it " + "\u2014 check the daemon (see `failproofai config`) rather than retrying blindly."
23860
+ }
23861
+ });
23862
+ if (result.stdout)
23863
+ process.stdout.write(result.stdout);
23864
+ if (result.stderr)
23865
+ process.stderr.write(result.stderr);
23866
+ await exitAfterFlush(result.exitCode);
23867
+ }
23290
23868
  const { handleHookEvent: handleHookEvent2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
23291
23869
  const exitCode = await handleHookEvent2(eventType, cli);
23292
- process.exit(exitCode);
23870
+ await exitAfterFlush(exitCode);
23293
23871
  } catch (err) {
23294
23872
  const msg = err instanceof Error ? err.message : String(err);
23295
23873
  await track("hook_dispatch_error", {