failproofai 0.0.14-beta.0 → 0.0.14-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/.next/standalone/.codex/hooks.json +6 -6
  2. package/.next/standalone/.cursor/hooks.json +6 -6
  3. package/.next/standalone/.factory/hooks.json +9 -9
  4. package/.next/standalone/.next/BUILD_ID +1 -1
  5. package/.next/standalone/.next/build-manifest.json +5 -5
  6. package/.next/standalone/.next/prerender-manifest.json +3 -3
  7. package/.next/standalone/.next/required-server-files.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  9. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  15. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  16. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  20. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  27. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  30. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  40. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  41. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/index.html +1 -1
  44. package/.next/standalone/.next/server/app/index.rsc +15 -15
  45. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  47. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  48. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  49. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  50. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  51. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  55. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  56. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  59. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  64. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  69. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08w4wmd._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g6bbuw._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xuaoik._.js +3 -3
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ffkmds._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qb590j._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1shcqgr._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xuedea._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
  95. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cz9as._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__042d3cd._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__05r_17v._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09q1168._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07qfsfa._.js → [root-of-the-server]__09v9cwb._.js} +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e2l3c1._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h12me5._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hci7t3._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hdsupo._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p84xee._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0twi4fj._.js +3 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ynf7tx._.js +2 -2
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11rtg6s._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +2 -2
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dky4g0._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +2 -2
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__212nf49._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/app_087bt9w._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/app_1fvisnp._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/app_209u41o._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  129. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/lib_0xkhw_v._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0g5r64e.js → node_modules_html-to-image_es_index_06xsmte.js} +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0-uvagv.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03c7gi5.js +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_09p-8om.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0v-kfiu.js +1 -1
  137. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0xrgzyz.js +1 -1
  138. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1806lsc.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1j6dd-e.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1sa65r-.js +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
  144. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  145. package/.next/standalone/.next/server/pages/404.html +1 -1
  146. package/.next/standalone/.next/server/pages/500.html +1 -1
  147. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  149. package/.next/standalone/.next/static/chunks/{13f1kmjea-0md.js → 02jcotghcxrbu.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/{1374x9bv_s_vg.js → 0h-6ymo9v86b0.js} +2 -2
  151. package/.next/standalone/.next/static/chunks/0pndjhcxfu-0t.js +1 -0
  152. package/.next/standalone/.next/static/chunks/1-33f82_l32or.js +1 -0
  153. package/.next/standalone/.next/static/chunks/{003uz088wbmz7.js → 1bg_g3g359c_d.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{454dyizqik6t9.js → 1gg-8e9q_m__9.js} +2 -2
  155. package/.next/standalone/.next/static/chunks/{2ufuwhv03yvtb.js → 29zkom4s9de2d.js} +1 -1
  156. package/.next/standalone/.next/static/chunks/{17sdztgbcc0fi.js → 2llkdafnaejw_.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{0iinhn4417nk1.js → 3-ogr79xqbw0h.js} +1 -1
  158. package/.next/standalone/.next/static/chunks/36j05kb2x323i.css +2 -0
  159. package/.next/standalone/.next/static/chunks/{43weu8f59tlix.js → 388hgfqye67xa.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/396764jgd7-9j.js +1 -0
  161. package/.next/standalone/.next/static/chunks/3w7g0n0iwwnbs.js +1 -0
  162. package/.next/standalone/.next/static/chunks/{turbopack-0vavqo2d-tlt3.js → turbopack-10hj8t5pf33tu.js} +1 -1
  163. package/.next/standalone/.opencode/plugins/failproofai.mjs +30 -11
  164. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +11 -2
  165. package/.next/standalone/app/components/copy-button.tsx +22 -6
  166. package/.next/standalone/app/components/global-error-listeners.tsx +27 -0
  167. package/.next/standalone/components/reach-developers.tsx +1 -1
  168. package/.next/standalone/docker-hook-sync/Dockerfile +8 -4
  169. package/.next/standalone/integration-suite/Dockerfile +47 -0
  170. package/.next/standalone/integration-suite/README.md +87 -0
  171. package/.next/standalone/integration-suite/canary-policies.mjs +45 -0
  172. package/.next/standalone/integration-suite/capture-tokens.sh +48 -0
  173. package/.next/standalone/integration-suite/ci-entrypoint.sh +164 -0
  174. package/.next/standalone/integration-suite/inject-tokens.sh +44 -0
  175. package/.next/standalone/integration-suite/install-clis.sh +91 -0
  176. package/.next/standalone/integration-suite/probe-cli.sh +208 -0
  177. package/.next/standalone/integration-suite/report.js +74 -0
  178. package/.next/standalone/integration-suite/run.sh +107 -0
  179. package/.next/standalone/lib/error-origin.ts +67 -0
  180. package/.next/standalone/lib/format-duration.ts +23 -9
  181. package/.next/standalone/lib/install-check.ts +231 -0
  182. package/.next/standalone/package.json +1 -3
  183. package/.next/standalone/server.js +1 -1
  184. package/README.md +1 -17
  185. package/bin/failproofai.mjs +11 -1
  186. package/dist/cli.mjs +818 -320
  187. package/lib/error-origin.ts +67 -0
  188. package/lib/format-duration.ts +23 -9
  189. package/lib/install-check.ts +231 -0
  190. package/package.json +1 -3
  191. package/scripts/dev-hook.mjs +292 -0
  192. package/scripts/install-diagnosis.mjs +5 -8
  193. package/scripts/install-telemetry.mjs +12 -4
  194. package/scripts/translate-docs/cli.ts +53 -1
  195. package/scripts/translate-docs/mdx-translator.ts +158 -18
  196. package/scripts/translate-docs/readme-translator.ts +36 -18
  197. package/scripts/translate-docs/translator.ts +180 -9
  198. package/scripts/translate-docs/types.ts +6 -0
  199. package/scripts/translate-docs/validate-translation.ts +132 -0
  200. package/scripts/validate-mdx.ts +92 -15
  201. package/src/audit/cli.ts +53 -15
  202. package/src/auth/cli.ts +14 -5
  203. package/src/hooks/builtin-policies.ts +168 -50
  204. package/src/hooks/configure-wizard.ts +238 -22
  205. package/src/hooks/custom-hooks-loader.ts +70 -4
  206. package/src/hooks/handler.ts +4 -1
  207. package/src/hooks/policy-types.ts +8 -0
  208. package/src/hooks/tui.ts +96 -29
  209. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ge8e7w._.js +0 -3
  210. package/.next/standalone/.next/static/chunks/0405htbxay9vd.js +0 -1
  211. package/.next/standalone/.next/static/chunks/2m_tbl39fcedn.js +0 -1
  212. package/.next/standalone/.next/static/chunks/2o3ql0n5owqsw.js +0 -1
  213. package/.next/standalone/.next/static/chunks/35-90m9py64d4.js +0 -1
  214. package/.next/standalone/.next/static/chunks/3r--yyjkp9_7b.css +0 -2
  215. package/scripts/postinstall.mjs +0 -242
  216. package/scripts/preuninstall.mjs +0 -131
  217. /package/.next/standalone/.next/static/{MNJWFb2jLRrxRKGFNK6Hy → YKxI22hC-F_nHp7MEKfb1}/_buildManifest.js +0 -0
  218. /package/.next/standalone/.next/static/{MNJWFb2jLRrxRKGFNK6Hy → YKxI22hC-F_nHp7MEKfb1}/_clientMiddlewareManifest.js +0 -0
  219. /package/.next/standalone/.next/static/{MNJWFb2jLRrxRKGFNK6Hy → YKxI22hC-F_nHp7MEKfb1}/_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.14-beta.0";
22
+ var version2 = "0.0.14-beta.2";
23
23
  var init_package = () => {};
24
24
 
25
25
  // src/posthog-key.ts
@@ -727,6 +727,22 @@ function readMergedHooksConfig(cwd) {
727
727
  ...llm !== undefined ? { llm } : {}
728
728
  };
729
729
  }
730
+ function getConfigPath() {
731
+ return resolve(homedir2(), ".failproofai", "policies-config.json");
732
+ }
733
+ function readHooksConfig() {
734
+ const configPath = getConfigPath();
735
+ if (!existsSync2(configPath)) {
736
+ return { enabledPolicies: [] };
737
+ }
738
+ try {
739
+ const raw = readFileSync(configPath, "utf8");
740
+ return JSON.parse(raw);
741
+ } catch (err) {
742
+ hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
743
+ return { enabledPolicies: [] };
744
+ }
745
+ }
730
746
  function getConfigPathForScope(scope, cwd) {
731
747
  switch (scope) {
732
748
  case "user":
@@ -1178,28 +1194,73 @@ function blockPushMaster(ctx) {
1178
1194
  }
1179
1195
  return allow();
1180
1196
  }
1181
- function rmTargetIsAllowed(cmd, allowPaths) {
1182
- if (allowPaths.length === 0)
1197
+ function expandHomePrefix(path2) {
1198
+ const m = path2.match(/^(?:~|\$HOME|\$\{HOME\})(?=$|\/)/);
1199
+ return m ? homedir3() + path2.slice(m[0].length) : path2;
1200
+ }
1201
+ function stripTrailingGlob(path2) {
1202
+ return path2.replace(/\/\*$/, "").replace(/\/+$/, "");
1203
+ }
1204
+ function isCatastrophicTarget(token) {
1205
+ const raw = token.replace(/^['"]|['"]$/g, "");
1206
+ if (raw === "")
1183
1207
  return false;
1184
- const segments = cmd.split(/&&|\|\||[|;\n]/).map((s) => s.trim()).filter((s) => /\brm\b/.test(s));
1185
- if (segments.length === 0)
1208
+ const homePrefix = raw.match(HOME_PREFIX_RE);
1209
+ if (!homePrefix && /^[$`]/.test(raw))
1210
+ return true;
1211
+ const belowRoot = homePrefix ? raw.slice(homePrefix[0].length) : raw.startsWith("/") ? raw : null;
1212
+ if (belowRoot === null)
1186
1213
  return false;
1187
- for (const seg of segments) {
1188
- const tokens = parseArgvTokens(seg);
1189
- const rmIdx = tokens.findIndex((t) => t === "rm");
1190
- if (rmIdx < 0)
1191
- continue;
1192
- const flagTokens = tokens.slice(rmIdx + 1).filter((t) => /^-[^-]/.test(t));
1193
- const longFlagsInSeg = tokens.slice(rmIdx + 1).filter((t) => /^--/.test(t));
1194
- if (!/r/i.test(flagTokens.join("")) && !longFlagsInSeg.some((f) => /^--recursive$/i.test(f)))
1214
+ const segments = stripTrailingGlob(belowRoot).split("/").filter(Boolean);
1215
+ if (!homePrefix && SCRATCH_ROOTS.some((r) => `/${segments.join("/")}`.startsWith(`${r}/`)))
1216
+ return false;
1217
+ return segments.length <= CATASTROPHIC_DEPTH;
1218
+ }
1219
+ function recursiveDeletionTargets(seg) {
1220
+ const tokens = parseArgvTokens(seg);
1221
+ const findIdx = tokens.findIndex((t) => FIND_CMD_RE.test(t));
1222
+ if (findIdx >= 0) {
1223
+ const expr = tokens.slice(findIdx + 1);
1224
+ const execIdx = expr.findIndex((t) => FIND_EXEC_RE.test(t));
1225
+ const deletes = expr.includes("-delete") || execIdx >= 0 && RM_CMD_RE.test(expr[execIdx + 1] ?? "");
1226
+ if (deletes) {
1227
+ let start = 0;
1228
+ while (start < expr.length && FIND_GLOBAL_OPT_RE.test(expr[start])) {
1229
+ start += expr[start] === "-D" ? 2 : 1;
1230
+ }
1231
+ const rest = expr.slice(start);
1232
+ const end = rest.findIndex((t) => FIND_EXPR_START_RE.test(t));
1233
+ return end < 0 ? rest : rest.slice(0, end);
1234
+ }
1235
+ }
1236
+ const rmIdx = tokens.findIndex((t) => RM_CMD_RE.test(t));
1237
+ if (rmIdx >= 0) {
1238
+ const args = tokens.slice(rmIdx + 1);
1239
+ const shortFlags = args.filter((t) => /^-[^-]/.test(t)).join("");
1240
+ const longFlags = args.filter((t) => /^--/.test(t));
1241
+ const recursive = /r/i.test(shortFlags) || longFlags.some((f) => /^--recursive$/i.test(f));
1242
+ const force = /f/.test(shortFlags) || longFlags.some((f) => /^--force$/i.test(f));
1243
+ if (recursive && force)
1244
+ return args.filter((t) => !t.startsWith("-"));
1245
+ }
1246
+ return null;
1247
+ }
1248
+ function shellSegments(cmd) {
1249
+ return cmd.split(/&&|\|\||[|;\n]/).map((s) => s.trim()).filter((s) => s !== "");
1250
+ }
1251
+ function deletionTargetIsAllowed(cmd, allowPaths) {
1252
+ if (allowPaths.length === 0)
1253
+ return false;
1254
+ const normalizedAllowPaths = allowPaths.map((p) => stripTrailingGlob(expandHomePrefix(p)) || "/");
1255
+ let sawRecursiveDelete = false;
1256
+ for (const seg of shellSegments(cmd)) {
1257
+ const targets = recursiveDeletionTargets(seg);
1258
+ if (targets === null)
1195
1259
  continue;
1196
- const pathArgs = tokens.slice(rmIdx + 1).filter((t) => !t.startsWith("-"));
1197
- for (const target of pathArgs) {
1198
- const normalized = target.replace(/\/\*$/, "").replace(/\/+$/, "") || "/";
1199
- const covered = allowPaths.some((p) => {
1200
- const np = p.replace(/\/+$/, "") || "/";
1201
- return normalized === np || normalized.startsWith(np + "/");
1202
- });
1260
+ sawRecursiveDelete = true;
1261
+ for (const target of targets) {
1262
+ const normalized = stripTrailingGlob(expandHomePrefix(target)) || "/";
1263
+ const covered = normalizedAllowPaths.some((np) => normalized === np || normalized.startsWith(np + "/"));
1203
1264
  if (!covered) {
1204
1265
  const segCovered = allowPaths.some((p) => {
1205
1266
  const escaped = p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1210,35 +1271,22 @@ function rmTargetIsAllowed(cmd, allowPaths) {
1210
1271
  }
1211
1272
  }
1212
1273
  }
1213
- return true;
1274
+ return sawRecursiveDelete;
1214
1275
  }
1215
1276
  function blockRmRf(ctx) {
1216
1277
  if (ctx.toolName !== "Bash")
1217
1278
  return allow();
1218
1279
  const cmd = getCommand(ctx);
1219
- const hasDestructivePath = parseArgvTokens(cmd).some((token) => {
1220
- const normalized = token.replace(/\/\*$/, "").replace(/\/+$/, "") || (token.startsWith("/") ? "/" : "");
1221
- return normalized === "/" || normalized === "~" || /^\/[A-Za-z_][\w.-]*$/.test(normalized);
1280
+ const hasCatastrophicTarget = shellSegments(cmd).some((seg) => {
1281
+ const targets = recursiveDeletionTargets(seg);
1282
+ return targets !== null && targets.some(isCatastrophicTarget);
1222
1283
  });
1223
- if (hasDestructivePath && (/rm\s+-[^\s]*r[^\s]*f[^\s]*/.test(cmd) || /rm\s+-[^\s]*f[^\s]*r[^\s]*/.test(cmd))) {
1284
+ if (hasCatastrophicTarget) {
1224
1285
  const allowPaths = ctx.params?.allowPaths ?? [];
1225
- if (rmTargetIsAllowed(cmd, allowPaths))
1286
+ if (deletionTargetIsAllowed(cmd, allowPaths))
1226
1287
  return allow();
1227
1288
  return deny("Catastrophic deletion blocked");
1228
1289
  }
1229
- if (hasDestructivePath && /\brm\b/.test(cmd)) {
1230
- const tokens = parseArgvTokens(cmd);
1231
- const shortFlags = tokens.filter((t) => /^-[^-]/.test(t)).join("");
1232
- const longFlags = tokens.filter((t) => /^--/.test(t));
1233
- const hasRecursive = /r/i.test(shortFlags) || longFlags.some((f) => /^--recursive$/i.test(f));
1234
- const hasForce = /f/.test(shortFlags) || longFlags.some((f) => /^--force$/i.test(f));
1235
- if (hasRecursive && hasForce) {
1236
- const allowPaths = ctx.params?.allowPaths ?? [];
1237
- if (rmTargetIsAllowed(cmd, allowPaths))
1238
- return allow();
1239
- return deny("Catastrophic deletion blocked");
1240
- }
1241
- }
1242
1290
  if (/Remove-Item\s+.*-Recurse.*-Force.*(?:[A-Z]:\\(?:\s|$)|\\\*)/i.test(cmd)) {
1243
1291
  return deny("Catastrophic deletion blocked");
1244
1292
  }
@@ -1250,12 +1298,31 @@ function blockRmRf(ctx) {
1250
1298
  function blockForcePush(ctx) {
1251
1299
  if (ctx.toolName !== "Bash")
1252
1300
  return allow();
1253
- const args = extractGitPushArgs(getCommand(ctx));
1254
- if (args.some((a) => FORCE_PUSH_RE.test(a))) {
1255
- return deny("Force-pushing is blocked");
1301
+ for (const segment of extractGitPushArgs(getCommand(ctx))) {
1302
+ let sawEndOfOptions = false;
1303
+ for (const token of segment.split(/\s+/)) {
1304
+ if (token === "--") {
1305
+ sawEndOfOptions = true;
1306
+ continue;
1307
+ }
1308
+ if (sawEndOfOptions)
1309
+ continue;
1310
+ if (isForcePushFlag(token)) {
1311
+ return deny("Force-pushing is blocked");
1312
+ }
1313
+ }
1256
1314
  }
1257
1315
  return allow();
1258
1316
  }
1317
+ function isForcePushFlag(token) {
1318
+ if (token === "--force")
1319
+ return true;
1320
+ if (SAFE_FORCE_PREFIXES.some((prefix) => token.startsWith(prefix)))
1321
+ return false;
1322
+ if (token.startsWith("--force"))
1323
+ return true;
1324
+ return SHORT_FLAG_BUNDLE_RE.test(token);
1325
+ }
1259
1326
  function blockSecretsWrite(ctx) {
1260
1327
  if (ctx.toolName !== "Write")
1261
1328
  return allow();
@@ -1836,7 +1903,7 @@ function registerBuiltinPolicies(enabledNames) {
1836
1903
  }
1837
1904
  }
1838
1905
  }
1839
- 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, FORCE_PUSH_RE, 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, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
1906
+ 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;
1840
1907
  var init_builtin_policies = __esm(() => {
1841
1908
  init_hook_logger();
1842
1909
  isClaudeInternalPath = isAgentInternalPath;
@@ -1878,7 +1945,8 @@ var init_builtin_policies = __esm(() => {
1878
1945
  RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
1879
1946
  CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
1880
1947
  PS_WEB_PIPE_RE = /(?:Invoke-WebRequest|iwr|Invoke-RestMethod|irm)\s+.*\|\s*(?:Invoke-Expression|iex)/i;
1881
- FORCE_PUSH_RE = /(?:--force|-f\b)/;
1948
+ SHORT_FLAG_BUNDLE_RE = /^-[a-zA-Z]*f[a-zA-Z]*$/;
1949
+ SAFE_FORCE_PREFIXES = ["--force-with-lease", "--force-if-includes"];
1882
1950
  SECRET_FILE_RE = /\.(?:pem|key)$/;
1883
1951
  SECRET_FILE_ID_RSA_RE = /id_rsa/;
1884
1952
  SECRET_FILE_CREDENTIALS_RE = /credentials/;
@@ -1919,6 +1987,13 @@ var init_builtin_policies = __esm(() => {
1919
1987
  HELM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*helm(?:\s|$)/;
1920
1988
  GH_PIPELINE_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*gh\s+(?:workflow\s+(?:run|enable|disable)|run\s+(?:rerun|cancel)|pr\s+merge|release\s+(?:create|delete)|cache\s+delete|secret\s+(?:set|delete))\b/;
1921
1989
  gitBranchCache = new Map;
1990
+ HOME_PREFIX_RE = /^(?:~[A-Za-z0-9_.-]*|\$HOME|\$\{HOME\})(?=$|\/)/;
1991
+ RM_CMD_RE = /^(?:\/\S*\/)?rm$/;
1992
+ FIND_CMD_RE = /^(?:\/\S*\/)?find$/;
1993
+ FIND_EXEC_RE = /^-(?:exec|execdir|ok|okdir)$/;
1994
+ FIND_GLOBAL_OPT_RE = /^-(?:[HLP]|D|O\d*)$/;
1995
+ FIND_EXPR_START_RE = /^(?:-|\\?[(!])/;
1996
+ SCRATCH_ROOTS = ["/tmp", "/var/tmp"];
1922
1997
  READ_LIKE_CMDS = /(?:^|;|&&|\|\||\|)\s*(?:ls|find|cat|head|tail|less|more|wc|file|stat|tree|du)\s/;
1923
1998
  SEGMENT_SPLIT_RE = /\s*(?:&&|\|\||\||;)\s*/;
1924
1999
  BUILTIN_POLICIES = [
@@ -3435,6 +3510,15 @@ function discoverPolicyFiles(dir) {
3435
3510
  return [];
3436
3511
  }
3437
3512
  }
3513
+ function findSkippedPolicyFiles(dir) {
3514
+ if (!existsSync3(dir))
3515
+ return [];
3516
+ try {
3517
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
3518
+ } catch {
3519
+ return [];
3520
+ }
3521
+ }
3438
3522
  async function loadSingleFile(absPath, opts) {
3439
3523
  const g = globalThis;
3440
3524
  g[LOADING_KEY] = true;
@@ -3477,10 +3561,18 @@ async function loadCustomHooks(customPoliciesPath, opts) {
3477
3561
  await loadSingleFile(absPath, opts);
3478
3562
  return getCustomHooks();
3479
3563
  }
3564
+ function warnSkippedPolicyFiles(dir, scope) {
3565
+ const skipped = findSkippedPolicyFiles(dir);
3566
+ if (skipped.length === 0)
3567
+ return;
3568
+ const suggest = (n) => n.replace(/\.(js|mjs|ts)$/, "-policies.$1");
3569
+ hookLogWarn(`${scope} policies: ${skipped.length} file(s) in ${dir} are NOT loaded — ` + `the convention only loads names ending in "policies.js|mjs|ts". ` + skipped.map((n) => `${n} → rename to ${suggest(n)}`).join("; "));
3570
+ }
3480
3571
  async function loadAllCustomHooks(customPoliciesPath, opts) {
3481
3572
  clearCustomHooks();
3482
3573
  const conventionSources = [];
3483
3574
  const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
3575
+ const conventionEnabled = opts?.customPoliciesEnabled !== false;
3484
3576
  if (customPoliciesPath) {
3485
3577
  const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(projectRoot, customPoliciesPath);
3486
3578
  if (existsSync3(absPath)) {
@@ -3491,7 +3583,9 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
3491
3583
  }
3492
3584
  const hooksBeforeConvention = getCustomHooks().length;
3493
3585
  const projectDir = resolve4(projectRoot, ".failproofai", "policies");
3494
- const projectFiles = discoverPolicyFiles(projectDir);
3586
+ if (conventionEnabled)
3587
+ warnSkippedPolicyFiles(projectDir, "project");
3588
+ const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir) : [];
3495
3589
  for (const file of projectFiles) {
3496
3590
  const hooksBefore = getCustomHooks().length;
3497
3591
  await loadSingleFile(file, { conventionScope: "project" });
@@ -3505,7 +3599,9 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
3505
3599
  }
3506
3600
  }
3507
3601
  const userDir = resolve4(homedir4(), ".failproofai", "policies");
3508
- const userFiles = discoverPolicyFiles(userDir);
3602
+ if (conventionEnabled)
3603
+ warnSkippedPolicyFiles(userDir, "user");
3604
+ const userFiles = conventionEnabled ? discoverPolicyFiles(userDir) : [];
3509
3605
  for (const file of userFiles) {
3510
3606
  const hooksBefore = getCustomHooks().length;
3511
3607
  await loadSingleFile(file, { conventionScope: "user" });
@@ -3520,7 +3616,9 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
3520
3616
  }
3521
3617
  const allHooks = getCustomHooks();
3522
3618
  const conventionCount = allHooks.length - hooksBeforeConvention;
3523
- if (projectFiles.length > 0 || userFiles.length > 0) {
3619
+ if (!conventionEnabled) {
3620
+ hookLogInfo("convention policies: DISABLED via customPoliciesEnabled:false — " + "no .failproofai/policies/ files loaded");
3621
+ } else if (projectFiles.length > 0 || userFiles.length > 0) {
3524
3622
  hookLogInfo(`convention policies: ${projectFiles.length} project file(s), ${userFiles.length} user file(s), ${conventionCount} hook(s)`);
3525
3623
  }
3526
3624
  const hookNameToScope = new Map;
@@ -3540,7 +3638,7 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
3540
3638
  }
3541
3639
  return { hooks: allHooks, conventionSources };
3542
3640
  }
3543
- var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE;
3641
+ var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, LOADABLE_EXT_RE;
3544
3642
  var init_custom_hooks_loader = __esm(() => {
3545
3643
  init_hook_logger();
3546
3644
  init_custom_hooks_registry();
@@ -3549,6 +3647,7 @@ var init_custom_hooks_loader = __esm(() => {
3549
3647
  init_hook_telemetry2();
3550
3648
  init_telemetry_id2();
3551
3649
  CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
3650
+ LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
3552
3651
  });
3553
3652
 
3554
3653
  // src/hooks/hook-activity-store.ts
@@ -4761,17 +4860,20 @@ var init_sqlite_reader = () => {};
4761
4860
  function formatDuration(ms) {
4762
4861
  if (ms < 1000)
4763
4862
  return `${ms}ms`;
4764
- const seconds = ms / 1000;
4765
- if (seconds < 60)
4766
- return `${seconds.toFixed(1)}s`;
4767
- const totalMinutes = Math.floor(seconds / 60);
4768
- if (totalMinutes >= 60) {
4769
- const hours = Math.floor(totalMinutes / 60);
4770
- const remainingMinutes = totalMinutes % 60;
4771
- return `${hours}h ${remainingMinutes}m`;
4863
+ const deciseconds = Math.round(ms / 100);
4864
+ if (deciseconds < 600) {
4865
+ return `${(deciseconds / 10).toFixed(1)}s`;
4772
4866
  }
4773
- const remainingSeconds = (seconds % 60).toFixed(0);
4774
- return `${totalMinutes}m ${remainingSeconds}s`;
4867
+ const totalSeconds = Math.round(ms / 1000);
4868
+ if (totalSeconds < 3600) {
4869
+ const minutes2 = Math.floor(totalSeconds / 60);
4870
+ const seconds = totalSeconds % 60;
4871
+ return `${minutes2}m ${seconds}s`;
4872
+ }
4873
+ const totalMinutes = Math.round(ms / 60000);
4874
+ const hours = Math.floor(totalMinutes / 60);
4875
+ const minutes = totalMinutes % 60;
4876
+ return `${hours}h ${minutes}m`;
4775
4877
  }
4776
4878
 
4777
4879
  // lib/hermes-sessions.ts
@@ -8191,7 +8293,10 @@ async function handleHookEvent(eventType, cli = "claude") {
8191
8293
  const config = readMergedHooksConfig(session.cwd);
8192
8294
  clearPolicies();
8193
8295
  registerBuiltinPolicies(config.enabledPolicies);
8194
- const loadResult = await loadAllCustomHooks(config.customPoliciesPath, { sessionCwd: session.cwd });
8296
+ const loadResult = await loadAllCustomHooks(config.customPoliciesPath, {
8297
+ sessionCwd: session.cwd,
8298
+ customPoliciesEnabled: config.customPoliciesEnabled
8299
+ });
8195
8300
  const customHooksList = loadResult.hooks;
8196
8301
  const conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
8197
8302
  for (const hook of customHooksList) {
@@ -8314,6 +8419,242 @@ var init_handler = __esm(() => {
8314
8419
  init_hook_logger();
8315
8420
  });
8316
8421
 
8422
+ // scripts/install-telemetry.mjs
8423
+ import { createHmac, randomUUID } from "node:crypto";
8424
+ import { execSync as execSync4 } from "node:child_process";
8425
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "node:fs";
8426
+ import { homedir as homedir21, platform, arch, hostname, cpus } from "node:os";
8427
+ import { join as join24 } from "node:path";
8428
+ function hashToId3(raw) {
8429
+ return createHmac("sha256", NAMESPACE3).update(raw).digest("hex");
8430
+ }
8431
+ function getInstanceId3() {
8432
+ const idDir = join24(homedir21(), ".failproofai");
8433
+ const idFile = join24(idDir, "instance-id");
8434
+ try {
8435
+ const existing = readFileSync10(idFile, "utf-8").trim();
8436
+ if (existing)
8437
+ return existing;
8438
+ } catch {}
8439
+ try {
8440
+ const p = platform();
8441
+ if (p === "linux") {
8442
+ for (const machineIdPath of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
8443
+ try {
8444
+ const id2 = readFileSync10(machineIdPath, "utf-8").trim();
8445
+ if (id2)
8446
+ return hashToId3(id2);
8447
+ } catch {}
8448
+ }
8449
+ } else if (p === "darwin") {
8450
+ const out = execSync4("ioreg -rd1 -c IOPlatformExpertDevice", {
8451
+ encoding: "utf-8",
8452
+ timeout: 3000
8453
+ });
8454
+ const m = out.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
8455
+ if (m?.[1])
8456
+ return hashToId3(m[1]);
8457
+ } else if (p === "win32") {
8458
+ const out = execSync4('reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid', { encoding: "utf-8", timeout: 3000 });
8459
+ const m = out.match(/MachineGuid\s+REG_SZ\s+(\S+)/);
8460
+ if (m?.[1])
8461
+ return hashToId3(m[1]);
8462
+ }
8463
+ } catch {}
8464
+ try {
8465
+ const sysProps = [
8466
+ hostname(),
8467
+ homedir21(),
8468
+ platform(),
8469
+ arch(),
8470
+ cpus()[0]?.model ?? ""
8471
+ ].join(":");
8472
+ return hashToId3(sysProps);
8473
+ } catch {}
8474
+ const id = randomUUID();
8475
+ try {
8476
+ mkdirSync5(idDir, { recursive: true });
8477
+ writeFileSync4(idFile, id, "utf-8");
8478
+ } catch {}
8479
+ return id;
8480
+ }
8481
+ async function trackInstallEvent(event, properties = {}, opts = {}) {
8482
+ if (process.env.FAILPROOFAI_TELEMETRY_DISABLED === "1")
8483
+ return;
8484
+ const version3 = opts.version ?? process.env.npm_package_version ?? "unknown";
8485
+ const body = JSON.stringify({
8486
+ api_key: process.env.FAILPROOFAI_POSTHOG_KEY ?? API_KEY3,
8487
+ event,
8488
+ distinct_id: getInstanceId3(),
8489
+ properties: {
8490
+ ...properties,
8491
+ $lib: "failproofai-install",
8492
+ failproofai_version: version3,
8493
+ product: PRODUCT
8494
+ }
8495
+ });
8496
+ await fetch(process.env.FAILPROOFAI_POSTHOG_HOST ? `${process.env.FAILPROOFAI_POSTHOG_HOST}/capture/` : CAPTURE_URL3, {
8497
+ method: "POST",
8498
+ headers: { "Content-Type": "application/json" },
8499
+ body,
8500
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 5000)
8501
+ });
8502
+ }
8503
+ var NAMESPACE3 = "failproofai-telemetry-v1", API_KEY3 = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", CAPTURE_URL3 = "https://us.i.posthog.com/capture/", PRODUCT = "failproofai-oss";
8504
+ var init_install_telemetry = () => {};
8505
+
8506
+ // lib/install-check.ts
8507
+ var exports_install_check = {};
8508
+ __export(exports_install_check, {
8509
+ maybeReportInstall: () => maybeReportInstall,
8510
+ compareSemver: () => compareSemver,
8511
+ checkHooks: () => checkHooks
8512
+ });
8513
+ import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "node:fs";
8514
+ import { resolve as resolve9 } from "node:path";
8515
+ import { platform as platform2, arch as arch2, release, homedir as homedir22, hostname as hostname2 } from "node:os";
8516
+ import { createHmac as createHmac2 } from "node:crypto";
8517
+ function hashToId4(raw) {
8518
+ return createHmac2("sha256", NAMESPACE4).update(raw).digest("hex");
8519
+ }
8520
+ function lastVersionPath() {
8521
+ return resolve9(homedir22(), ".failproofai", "last-version");
8522
+ }
8523
+ function compareSemver(a, b) {
8524
+ const parse = (v) => {
8525
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(v);
8526
+ if (!m)
8527
+ return null;
8528
+ return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
8529
+ };
8530
+ const pa = parse(a);
8531
+ const pb = parse(b);
8532
+ if (!pa || !pb)
8533
+ return a < b ? -1 : a > b ? 1 : 0;
8534
+ for (let i = 0;i < 3; i++) {
8535
+ if (pa.nums[i] !== pb.nums[i])
8536
+ return pa.nums[i] < pb.nums[i] ? -1 : 1;
8537
+ }
8538
+ if (pa.pre === null && pb.pre === null)
8539
+ return 0;
8540
+ if (pa.pre === null)
8541
+ return 1;
8542
+ if (pb.pre === null)
8543
+ return -1;
8544
+ const ax = pa.pre.split(".");
8545
+ const bx = pb.pre.split(".");
8546
+ for (let i = 0;i < Math.max(ax.length, bx.length); i++) {
8547
+ const ai = ax[i], bi = bx[i];
8548
+ if (ai === undefined)
8549
+ return -1;
8550
+ if (bi === undefined)
8551
+ return 1;
8552
+ const aNum = /^\d+$/.test(ai), bNum = /^\d+$/.test(bi);
8553
+ if (aNum && bNum) {
8554
+ const d = Number(ai) - Number(bi);
8555
+ if (d !== 0)
8556
+ return d < 0 ? -1 : 1;
8557
+ } else if (aNum) {
8558
+ return -1;
8559
+ } else if (bNum) {
8560
+ return 1;
8561
+ } else if (ai !== bi) {
8562
+ return ai < bi ? -1 : 1;
8563
+ }
8564
+ }
8565
+ return 0;
8566
+ }
8567
+ function checkHooks() {
8568
+ const hooksConfigPath = resolve9(homedir22(), ".failproofai", "policies-config.json");
8569
+ if (!existsSync10(hooksConfigPath)) {
8570
+ return { configured: false, registered: false, policyCount: 0 };
8571
+ }
8572
+ let config;
8573
+ try {
8574
+ config = JSON.parse(readFileSync11(hooksConfigPath, "utf8"));
8575
+ } catch {
8576
+ return { configured: false, registered: false, policyCount: 0 };
8577
+ }
8578
+ if (!Array.isArray(config.enabledPolicies) || config.enabledPolicies.length === 0) {
8579
+ return { configured: false, registered: false, policyCount: 0 };
8580
+ }
8581
+ const policyCount = config.enabledPolicies.length;
8582
+ const settingsPath = resolve9(homedir22(), ".claude", "settings.json");
8583
+ if (!existsSync10(settingsPath))
8584
+ return { configured: true, registered: false, policyCount };
8585
+ let settings;
8586
+ try {
8587
+ settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
8588
+ } catch {
8589
+ return { configured: true, registered: false, policyCount };
8590
+ }
8591
+ if (!settings?.hooks)
8592
+ return { configured: true, registered: false, policyCount };
8593
+ for (const matchers of Object.values(settings.hooks)) {
8594
+ if (!Array.isArray(matchers))
8595
+ continue;
8596
+ for (const matcher of matchers) {
8597
+ if (!matcher?.hooks)
8598
+ continue;
8599
+ if (matcher.hooks.some((h) => h[FAILPROOFAI_HOOK_MARKER2] === true)) {
8600
+ return { configured: true, registered: true, policyCount };
8601
+ }
8602
+ }
8603
+ }
8604
+ return { configured: true, registered: false, policyCount };
8605
+ }
8606
+ async function maybeReportInstall(version3) {
8607
+ try {
8608
+ const versionFile = lastVersionPath();
8609
+ let previousVersion = null;
8610
+ try {
8611
+ if (existsSync10(versionFile)) {
8612
+ previousVersion = readFileSync11(versionFile, "utf8").trim() || null;
8613
+ }
8614
+ } catch {}
8615
+ if (previousVersion === version3)
8616
+ return;
8617
+ const hooks = checkHooks();
8618
+ const events = [];
8619
+ if (previousVersion === null) {
8620
+ events.push(trackInstallEvent("first_install", {
8621
+ platform: platform2(),
8622
+ arch: arch2(),
8623
+ os_release: release(),
8624
+ node_version: process.versions.node
8625
+ }, { version: version3, timeoutMs: REPORT_TIMEOUT_MS }));
8626
+ } else {
8627
+ const cmp = compareSemver(previousVersion, version3);
8628
+ events.push(trackInstallEvent("version_changed", {
8629
+ from_version: previousVersion,
8630
+ to_version: version3,
8631
+ direction: cmp < 0 ? "upgrade" : "downgrade",
8632
+ platform: platform2(),
8633
+ arch: arch2()
8634
+ }, { version: version3, timeoutMs: REPORT_TIMEOUT_MS }));
8635
+ }
8636
+ events.push(trackInstallEvent("package_installed", {
8637
+ platform: platform2(),
8638
+ arch: arch2(),
8639
+ os_release: release(),
8640
+ node_version: process.versions.node,
8641
+ hostname_hash: hashToId4(hostname2()),
8642
+ hooks_configured: hooks.configured,
8643
+ hooks_registered: hooks.registered,
8644
+ enabled_policy_count: hooks.policyCount
8645
+ }, { version: version3, timeoutMs: REPORT_TIMEOUT_MS }));
8646
+ try {
8647
+ mkdirSync6(resolve9(homedir22(), ".failproofai"), { recursive: true });
8648
+ writeFileSync5(versionFile, version3, "utf8");
8649
+ } catch {}
8650
+ await Promise.all(events.map((p) => p.catch(() => {})));
8651
+ } catch {}
8652
+ }
8653
+ var FAILPROOFAI_HOOK_MARKER2 = "__failproofai_hook__", NAMESPACE4 = "failproofai-telemetry-v1", REPORT_TIMEOUT_MS = 5000;
8654
+ var init_install_check = __esm(() => {
8655
+ init_install_telemetry();
8656
+ });
8657
+
8317
8658
  // node_modules/yaml/dist/nodes/identity.js
8318
8659
  var require_identity = __commonJS((exports) => {
8319
8660
  var ALIAS = Symbol.for("yaml.alias");
@@ -15305,32 +15646,32 @@ var init_dist = __esm(() => {
15305
15646
  });
15306
15647
 
15307
15648
  // src/hooks/integrations.ts
15308
- import { execSync as execSync4 } from "node:child_process";
15309
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, existsSync as existsSync10, mkdirSync as mkdirSync5, unlinkSync as unlinkSync2 } from "node:fs";
15310
- import { resolve as resolve9, dirname as dirname4 } from "node:path";
15649
+ import { execSync as execSync5 } from "node:child_process";
15650
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
15651
+ import { resolve as resolve10, dirname as dirname4 } from "node:path";
15311
15652
  import { fileURLToPath } from "node:url";
15312
- import { homedir as homedir21 } from "node:os";
15653
+ import { homedir as homedir23 } from "node:os";
15313
15654
  function readJsonFile(path3) {
15314
- if (!existsSync10(path3))
15655
+ if (!existsSync11(path3))
15315
15656
  return {};
15316
- const raw = readFileSync10(path3, "utf8");
15657
+ const raw = readFileSync12(path3, "utf8");
15317
15658
  return JSON.parse(raw);
15318
15659
  }
15319
15660
  function writeJsonFile(path3, data) {
15320
- mkdirSync5(dirname4(path3), { recursive: true });
15321
- writeFileSync4(path3, JSON.stringify(data, null, 2) + `
15661
+ mkdirSync7(dirname4(path3), { recursive: true });
15662
+ writeFileSync6(path3, JSON.stringify(data, null, 2) + `
15322
15663
  `, "utf8");
15323
15664
  }
15324
15665
  function readYamlDoc(path3) {
15325
15666
  try {
15326
- return existsSync10(path3) ? $parseDocument(readFileSync10(path3, "utf8")) : $parseDocument("");
15667
+ return existsSync11(path3) ? $parseDocument(readFileSync12(path3, "utf8")) : $parseDocument("");
15327
15668
  } catch {
15328
15669
  return $parseDocument("");
15329
15670
  }
15330
15671
  }
15331
15672
  function writeYamlDoc(path3, doc) {
15332
- mkdirSync5(dirname4(path3), { recursive: true });
15333
- writeFileSync4(path3, doc.toString(), "utf8");
15673
+ mkdirSync7(dirname4(path3), { recursive: true });
15674
+ writeFileSync6(path3, doc.toString(), "utf8");
15334
15675
  }
15335
15676
  function isMarkedHook(hook) {
15336
15677
  if (!hook || typeof hook !== "object")
@@ -15351,7 +15692,7 @@ function stripLegacyVersion(settings) {
15351
15692
  function binaryExists(name) {
15352
15693
  try {
15353
15694
  const cmd = process.platform === "win32" ? `where ${name}` : `which ${name}`;
15354
- execSync4(cmd, { encoding: "utf8", stdio: "pipe" });
15695
+ execSync5(cmd, { encoding: "utf8", stdio: "pipe" });
15355
15696
  return true;
15356
15697
  } catch {
15357
15698
  return false;
@@ -15369,7 +15710,7 @@ function isMarkedCopilotHook(hook) {
15369
15710
  return false;
15370
15711
  }
15371
15712
  function opencodePluginFilePath(settingsPath) {
15372
- return resolve9(dirname4(settingsPath), "plugins", "failproofai.mjs");
15713
+ return resolve10(dirname4(settingsPath), "plugins", "failproofai.mjs");
15373
15714
  }
15374
15715
  function buildOpenCodePluginShim(binaryPath, scope) {
15375
15716
  const useNpx = scope === "project";
@@ -15584,8 +15925,8 @@ export default async function failproofaiPlugin({ client, directory }) {
15584
15925
  function getPiExtensionPath() {
15585
15926
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
15586
15927
  if (fromEnv)
15587
- return resolve9(fromEnv, "pi-extension");
15588
- return resolve9(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
15928
+ return resolve10(fromEnv, "pi-extension");
15929
+ return resolve10(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
15589
15930
  }
15590
15931
  function isFailproofaiPiEntry(source) {
15591
15932
  if (typeof source !== "string")
@@ -15596,8 +15937,8 @@ function isFailproofaiPiEntry(source) {
15596
15937
  }
15597
15938
  function makePiProjectRelativeEntry(extPath) {
15598
15939
  const cwd = process.cwd();
15599
- const cwdResolved = resolve9(cwd);
15600
- const extResolved = resolve9(extPath);
15940
+ const cwdResolved = resolve10(cwd);
15941
+ const extResolved = resolve10(extPath);
15601
15942
  if (extResolved.startsWith(cwdResolved + "/") || extResolved === cwdResolved) {
15602
15943
  const fromSettingsDir = "../" + extResolved.slice(cwdResolved.length + 1);
15603
15944
  return fromSettingsDir;
@@ -15607,8 +15948,8 @@ function makePiProjectRelativeEntry(extPath) {
15607
15948
  function getOpenClawPluginPath() {
15608
15949
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
15609
15950
  if (fromEnv)
15610
- return resolve9(fromEnv, "openclaw-plugin");
15611
- return resolve9(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
15951
+ return resolve10(fromEnv, "openclaw-plugin");
15952
+ return resolve10(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
15612
15953
  }
15613
15954
  function isFailproofaiOpenClawPath(p) {
15614
15955
  if (typeof p !== "string")
@@ -15644,14 +15985,14 @@ var init_integrations = __esm(() => {
15644
15985
  scopes: HOOK_SCOPES,
15645
15986
  eventTypes: HOOK_EVENT_TYPES,
15646
15987
  getSettingsPath(scope, cwd) {
15647
- const base = cwd ? resolve9(cwd) : process.cwd();
15988
+ const base = cwd ? resolve10(cwd) : process.cwd();
15648
15989
  switch (scope) {
15649
15990
  case "user":
15650
- return resolve9(homedir21(), ".claude", "settings.json");
15991
+ return resolve10(homedir23(), ".claude", "settings.json");
15651
15992
  case "project":
15652
- return resolve9(base, ".claude", "settings.json");
15993
+ return resolve10(base, ".claude", "settings.json");
15653
15994
  case "local":
15654
- return resolve9(base, ".claude", "settings.local.json");
15995
+ return resolve10(base, ".claude", "settings.local.json");
15655
15996
  }
15656
15997
  },
15657
15998
  readSettings(settingsPath) {
@@ -15723,7 +16064,7 @@ var init_integrations = __esm(() => {
15723
16064
  },
15724
16065
  hooksInstalledInSettings(scope, cwd) {
15725
16066
  const settingsPath = this.getSettingsPath(scope, cwd);
15726
- if (!existsSync10(settingsPath))
16067
+ if (!existsSync11(settingsPath))
15727
16068
  return false;
15728
16069
  try {
15729
16070
  const settings = this.readSettings(settingsPath);
@@ -15752,14 +16093,14 @@ var init_integrations = __esm(() => {
15752
16093
  scopes: CODEX_HOOK_SCOPES,
15753
16094
  eventTypes: CODEX_HOOK_EVENT_TYPES,
15754
16095
  getSettingsPath(scope, cwd) {
15755
- const base = cwd ? resolve9(cwd) : process.cwd();
16096
+ const base = cwd ? resolve10(cwd) : process.cwd();
15756
16097
  switch (scope) {
15757
16098
  case "user":
15758
- return resolve9(homedir21(), ".codex", "hooks.json");
16099
+ return resolve10(homedir23(), ".codex", "hooks.json");
15759
16100
  case "project":
15760
- return resolve9(base, ".codex", "hooks.json");
16101
+ return resolve10(base, ".codex", "hooks.json");
15761
16102
  case "local":
15762
- return resolve9(base, ".codex", "hooks.json");
16103
+ return resolve10(base, ".codex", "hooks.json");
15763
16104
  }
15764
16105
  },
15765
16106
  readSettings(settingsPath) {
@@ -15839,7 +16180,7 @@ var init_integrations = __esm(() => {
15839
16180
  },
15840
16181
  hooksInstalledInSettings(scope, cwd) {
15841
16182
  const settingsPath = this.getSettingsPath(scope, cwd);
15842
- if (!existsSync10(settingsPath))
16183
+ if (!existsSync11(settingsPath))
15843
16184
  return false;
15844
16185
  try {
15845
16186
  const settings = this.readSettings(settingsPath);
@@ -15868,14 +16209,14 @@ var init_integrations = __esm(() => {
15868
16209
  scopes: COPILOT_HOOK_SCOPES,
15869
16210
  eventTypes: COPILOT_HOOK_EVENT_TYPES,
15870
16211
  getSettingsPath(scope, cwd) {
15871
- const base = cwd ? resolve9(cwd) : process.cwd();
16212
+ const base = cwd ? resolve10(cwd) : process.cwd();
15872
16213
  switch (scope) {
15873
16214
  case "user":
15874
- return resolve9(homedir21(), ".copilot", "hooks", "failproofai.json");
16215
+ return resolve10(homedir23(), ".copilot", "hooks", "failproofai.json");
15875
16216
  case "project":
15876
- return resolve9(base, ".github", "hooks", "failproofai.json");
16217
+ return resolve10(base, ".github", "hooks", "failproofai.json");
15877
16218
  case "local":
15878
- return resolve9(base, ".github", "hooks", "failproofai.json");
16219
+ return resolve10(base, ".github", "hooks", "failproofai.json");
15879
16220
  }
15880
16221
  },
15881
16222
  readSettings(settingsPath) {
@@ -15953,7 +16294,7 @@ var init_integrations = __esm(() => {
15953
16294
  },
15954
16295
  hooksInstalledInSettings(scope, cwd) {
15955
16296
  const settingsPath = this.getSettingsPath(scope, cwd);
15956
- if (!existsSync10(settingsPath))
16297
+ if (!existsSync11(settingsPath))
15957
16298
  return false;
15958
16299
  try {
15959
16300
  const settings = this.readSettings(settingsPath);
@@ -15982,14 +16323,14 @@ var init_integrations = __esm(() => {
15982
16323
  scopes: CURSOR_HOOK_SCOPES,
15983
16324
  eventTypes: CURSOR_HOOK_EVENT_TYPES,
15984
16325
  getSettingsPath(scope, cwd) {
15985
- const base = cwd ? resolve9(cwd) : process.cwd();
16326
+ const base = cwd ? resolve10(cwd) : process.cwd();
15986
16327
  switch (scope) {
15987
16328
  case "user":
15988
- return resolve9(homedir21(), ".cursor", "hooks.json");
16329
+ return resolve10(homedir23(), ".cursor", "hooks.json");
15989
16330
  case "project":
15990
- return resolve9(base, ".cursor", "hooks.json");
16331
+ return resolve10(base, ".cursor", "hooks.json");
15991
16332
  case "local":
15992
- return resolve9(base, ".cursor", "hooks.json");
16333
+ return resolve10(base, ".cursor", "hooks.json");
15993
16334
  }
15994
16335
  },
15995
16336
  readSettings(settingsPath) {
@@ -16056,7 +16397,7 @@ var init_integrations = __esm(() => {
16056
16397
  },
16057
16398
  hooksInstalledInSettings(scope, cwd) {
16058
16399
  const settingsPath = this.getSettingsPath(scope, cwd);
16059
- if (!existsSync10(settingsPath))
16400
+ if (!existsSync11(settingsPath))
16060
16401
  return false;
16061
16402
  try {
16062
16403
  const settings = this.readSettings(settingsPath);
@@ -16081,14 +16422,14 @@ var init_integrations = __esm(() => {
16081
16422
  scopes: OPENCODE_HOOK_SCOPES,
16082
16423
  eventTypes: OPENCODE_HOOK_EVENT_TYPES,
16083
16424
  getSettingsPath(scope, cwd) {
16084
- const base = cwd ? resolve9(cwd) : process.cwd();
16425
+ const base = cwd ? resolve10(cwd) : process.cwd();
16085
16426
  switch (scope) {
16086
16427
  case "user":
16087
- return resolve9(homedir21(), ".config", "opencode", "opencode.json");
16428
+ return resolve10(homedir23(), ".config", "opencode", "opencode.json");
16088
16429
  case "project":
16089
- return resolve9(base, ".opencode", "opencode.json");
16430
+ return resolve10(base, ".opencode", "opencode.json");
16090
16431
  case "local":
16091
- return resolve9(base, ".opencode", "opencode.json");
16432
+ return resolve10(base, ".opencode", "opencode.json");
16092
16433
  }
16093
16434
  },
16094
16435
  readSettings(settingsPath) {
@@ -16099,7 +16440,7 @@ var init_integrations = __esm(() => {
16099
16440
  },
16100
16441
  buildHookEntry(_binaryPath, _eventType, scope) {
16101
16442
  if (scope === "user") {
16102
- const abs = resolve9(homedir21(), ".config", "opencode", "plugins", "failproofai.mjs");
16443
+ const abs = resolve10(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
16103
16444
  return { spec: `file://${abs}`, [FAILPROOFAI_HOOK_MARKER]: true };
16104
16445
  }
16105
16446
  return { spec: OPENCODE_PLUGIN_REL_PATH, [FAILPROOFAI_HOOK_MARKER]: true };
@@ -16114,10 +16455,10 @@ var init_integrations = __esm(() => {
16114
16455
  writeHookEntries(settings, binaryPath, scope) {
16115
16456
  const s = settings;
16116
16457
  const effectiveScope = scope ?? "project";
16117
- const settingsPath = effectiveScope === "user" ? resolve9(homedir21(), ".config", "opencode", "opencode.json") : resolve9(process.cwd(), ".opencode", "opencode.json");
16458
+ const settingsPath = effectiveScope === "user" ? resolve10(homedir23(), ".config", "opencode", "opencode.json") : resolve10(process.cwd(), ".opencode", "opencode.json");
16118
16459
  const pluginPath = opencodePluginFilePath(settingsPath);
16119
- mkdirSync5(dirname4(pluginPath), { recursive: true });
16120
- writeFileSync4(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
16460
+ mkdirSync7(dirname4(pluginPath), { recursive: true });
16461
+ writeFileSync6(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
16121
16462
  if (!Array.isArray(s.plugin))
16122
16463
  s.plugin = [];
16123
16464
  const desired = effectiveScope === "user" ? `file://${pluginPath}` : OPENCODE_PLUGIN_REL_PATH;
@@ -16140,9 +16481,9 @@ var init_integrations = __esm(() => {
16140
16481
  }
16141
16482
  this.writeSettings(settingsPath, settings);
16142
16483
  const pluginPath = opencodePluginFilePath(settingsPath);
16143
- if (existsSync10(pluginPath)) {
16484
+ if (existsSync11(pluginPath)) {
16144
16485
  try {
16145
- const content = readFileSync10(pluginPath, "utf8");
16486
+ const content = readFileSync12(pluginPath, "utf8");
16146
16487
  if (content.includes(FAILPROOFAI_HOOK_MARKER)) {
16147
16488
  unlinkSync2(pluginPath);
16148
16489
  if (removed === 0)
@@ -16154,7 +16495,7 @@ var init_integrations = __esm(() => {
16154
16495
  },
16155
16496
  hooksInstalledInSettings(scope, cwd) {
16156
16497
  const settingsPath = this.getSettingsPath(scope, cwd);
16157
- if (!existsSync10(settingsPath))
16498
+ if (!existsSync11(settingsPath))
16158
16499
  return false;
16159
16500
  try {
16160
16501
  const settings = this.readSettings(settingsPath);
@@ -16164,9 +16505,9 @@ var init_integrations = __esm(() => {
16164
16505
  if (!hasEntry)
16165
16506
  return false;
16166
16507
  const pluginPath = opencodePluginFilePath(settingsPath);
16167
- if (!existsSync10(pluginPath))
16508
+ if (!existsSync11(pluginPath))
16168
16509
  return false;
16169
- const content = readFileSync10(pluginPath, "utf8");
16510
+ const content = readFileSync12(pluginPath, "utf8");
16170
16511
  return content.includes(FAILPROOFAI_HOOK_MARKER);
16171
16512
  } catch {
16172
16513
  return false;
@@ -16182,14 +16523,14 @@ var init_integrations = __esm(() => {
16182
16523
  scopes: PI_HOOK_SCOPES,
16183
16524
  eventTypes: PI_HOOK_EVENT_TYPES,
16184
16525
  getSettingsPath(scope, cwd) {
16185
- const base = cwd ? resolve9(cwd) : process.cwd();
16526
+ const base = cwd ? resolve10(cwd) : process.cwd();
16186
16527
  switch (scope) {
16187
16528
  case "user":
16188
- return resolve9(homedir21(), ".pi", "agent", "settings.json");
16529
+ return resolve10(homedir23(), ".pi", "agent", "settings.json");
16189
16530
  case "project":
16190
- return resolve9(base, ".pi", "settings.json");
16531
+ return resolve10(base, ".pi", "settings.json");
16191
16532
  case "local":
16192
- return resolve9(base, ".pi", "settings.json");
16533
+ return resolve10(base, ".pi", "settings.json");
16193
16534
  }
16194
16535
  },
16195
16536
  readSettings(settingsPath) {
@@ -16231,7 +16572,7 @@ var init_integrations = __esm(() => {
16231
16572
  }
16232
16573
  },
16233
16574
  removeHooksFromFile(settingsPath) {
16234
- if (!existsSync10(settingsPath))
16575
+ if (!existsSync11(settingsPath))
16235
16576
  return 0;
16236
16577
  const settings = this.readSettings(settingsPath);
16237
16578
  if (!Array.isArray(settings.packages))
@@ -16246,7 +16587,7 @@ var init_integrations = __esm(() => {
16246
16587
  },
16247
16588
  hooksInstalledInSettings(scope, cwd) {
16248
16589
  const settingsPath = this.getSettingsPath(scope, cwd);
16249
- if (!existsSync10(settingsPath))
16590
+ if (!existsSync11(settingsPath))
16250
16591
  return false;
16251
16592
  try {
16252
16593
  const settings = this.readSettings(settingsPath);
@@ -16267,7 +16608,7 @@ var init_integrations = __esm(() => {
16267
16608
  scopes: HERMES_HOOK_SCOPES,
16268
16609
  eventTypes: HERMES_HOOK_EVENT_TYPES,
16269
16610
  getSettingsPath() {
16270
- return resolve9(homedir21(), ".hermes", "config.yaml");
16611
+ return resolve10(homedir23(), ".hermes", "config.yaml");
16271
16612
  },
16272
16613
  readSettings(settingsPath) {
16273
16614
  return readYamlDoc(settingsPath);
@@ -16302,7 +16643,7 @@ var init_integrations = __esm(() => {
16302
16643
  doc.set("hooks_auto_accept", true);
16303
16644
  },
16304
16645
  removeHooksFromFile(settingsPath) {
16305
- if (!existsSync10(settingsPath))
16646
+ if (!existsSync11(settingsPath))
16306
16647
  return 0;
16307
16648
  const doc = readYamlDoc(settingsPath);
16308
16649
  const js = doc.toJS() ?? {};
@@ -16335,7 +16676,7 @@ var init_integrations = __esm(() => {
16335
16676
  },
16336
16677
  hooksInstalledInSettings(scope, cwd) {
16337
16678
  const settingsPath = this.getSettingsPath(scope, cwd);
16338
- if (!existsSync10(settingsPath))
16679
+ if (!existsSync11(settingsPath))
16339
16680
  return false;
16340
16681
  try {
16341
16682
  const doc = readYamlDoc(settingsPath);
@@ -16360,7 +16701,7 @@ var init_integrations = __esm(() => {
16360
16701
  scopes: OPENCLAW_HOOK_SCOPES,
16361
16702
  eventTypes: OPENCLAW_HOOK_EVENT_TYPES,
16362
16703
  getSettingsPath() {
16363
- return resolve9(homedir21(), ".openclaw", "openclaw.json");
16704
+ return resolve10(homedir23(), ".openclaw", "openclaw.json");
16364
16705
  },
16365
16706
  readSettings(settingsPath) {
16366
16707
  return readJsonFile(settingsPath);
@@ -16405,7 +16746,7 @@ var init_integrations = __esm(() => {
16405
16746
  hooks.allowConversationAccess = true;
16406
16747
  },
16407
16748
  removeHooksFromFile(settingsPath) {
16408
- if (!existsSync10(settingsPath))
16749
+ if (!existsSync11(settingsPath))
16409
16750
  return 0;
16410
16751
  const settings = this.readSettings(settingsPath);
16411
16752
  const plugins = settings.plugins;
@@ -16434,7 +16775,7 @@ var init_integrations = __esm(() => {
16434
16775
  },
16435
16776
  hooksInstalledInSettings(scope, cwd) {
16436
16777
  const settingsPath = this.getSettingsPath(scope, cwd);
16437
- if (!existsSync10(settingsPath))
16778
+ if (!existsSync11(settingsPath))
16438
16779
  return false;
16439
16780
  try {
16440
16781
  const settings = this.readSettings(settingsPath);
@@ -16456,14 +16797,14 @@ var init_integrations = __esm(() => {
16456
16797
  scopes: FACTORY_HOOK_SCOPES,
16457
16798
  eventTypes: FACTORY_HOOK_EVENT_TYPES,
16458
16799
  getSettingsPath(scope, cwd) {
16459
- const base = cwd ? resolve9(cwd) : process.cwd();
16800
+ const base = cwd ? resolve10(cwd) : process.cwd();
16460
16801
  switch (scope) {
16461
16802
  case "user":
16462
- return resolve9(homedir21(), ".factory", "hooks.json");
16803
+ return resolve10(homedir23(), ".factory", "hooks.json");
16463
16804
  case "project":
16464
- return resolve9(base, ".factory", "hooks.json");
16805
+ return resolve10(base, ".factory", "hooks.json");
16465
16806
  case "local":
16466
- return resolve9(base, ".factory", "hooks.json");
16807
+ return resolve10(base, ".factory", "hooks.json");
16467
16808
  }
16468
16809
  },
16469
16810
  readSettings(settingsPath) {
@@ -16531,7 +16872,7 @@ var init_integrations = __esm(() => {
16531
16872
  },
16532
16873
  hooksInstalledInSettings(scope, cwd) {
16533
16874
  const settingsPath = this.getSettingsPath(scope, cwd);
16534
- if (!existsSync10(settingsPath))
16875
+ if (!existsSync11(settingsPath))
16535
16876
  return false;
16536
16877
  try {
16537
16878
  const settings = this.readSettings(settingsPath);
@@ -16558,14 +16899,14 @@ var init_integrations = __esm(() => {
16558
16899
  scopes: DEVIN_HOOK_SCOPES,
16559
16900
  eventTypes: DEVIN_HOOK_EVENT_TYPES,
16560
16901
  getSettingsPath(scope, cwd) {
16561
- const base = cwd ? resolve9(cwd) : process.cwd();
16902
+ const base = cwd ? resolve10(cwd) : process.cwd();
16562
16903
  switch (scope) {
16563
16904
  case "user":
16564
- return resolve9(homedir21(), ".config", "devin", "config.json");
16905
+ return resolve10(homedir23(), ".config", "devin", "config.json");
16565
16906
  case "project":
16566
- return resolve9(base, ".devin", "config.json");
16907
+ return resolve10(base, ".devin", "config.json");
16567
16908
  case "local":
16568
- return resolve9(base, ".devin", "config.json");
16909
+ return resolve10(base, ".devin", "config.json");
16569
16910
  }
16570
16911
  },
16571
16912
  readSettings(settingsPath) {
@@ -16637,7 +16978,7 @@ var init_integrations = __esm(() => {
16637
16978
  },
16638
16979
  hooksInstalledInSettings(scope, cwd) {
16639
16980
  const settingsPath = this.getSettingsPath(scope, cwd);
16640
- if (!existsSync10(settingsPath))
16981
+ if (!existsSync11(settingsPath))
16641
16982
  return false;
16642
16983
  try {
16643
16984
  const settings = this.readSettings(settingsPath);
@@ -16667,14 +17008,14 @@ var init_integrations = __esm(() => {
16667
17008
  scopes: ANTIGRAVITY_HOOK_SCOPES,
16668
17009
  eventTypes: ANTIGRAVITY_HOOK_EVENT_TYPES,
16669
17010
  getSettingsPath(scope, cwd) {
16670
- const base = cwd ? resolve9(cwd) : process.cwd();
17011
+ const base = cwd ? resolve10(cwd) : process.cwd();
16671
17012
  switch (scope) {
16672
17013
  case "user":
16673
- return resolve9(homedir21(), ".gemini", "config", "hooks.json");
17014
+ return resolve10(homedir23(), ".gemini", "config", "hooks.json");
16674
17015
  case "project":
16675
- return resolve9(base, ".agents", "hooks.json");
17016
+ return resolve10(base, ".agents", "hooks.json");
16676
17017
  case "local":
16677
- return resolve9(base, ".agents", "hooks.json");
17018
+ return resolve10(base, ".agents", "hooks.json");
16678
17019
  }
16679
17020
  },
16680
17021
  readSettings(settingsPath) {
@@ -16776,7 +17117,7 @@ var init_integrations = __esm(() => {
16776
17117
  },
16777
17118
  hooksInstalledInSettings(scope, cwd) {
16778
17119
  const settingsPath = this.getSettingsPath(scope, cwd);
16779
- if (!existsSync10(settingsPath))
17120
+ if (!existsSync11(settingsPath))
16780
17121
  return false;
16781
17122
  try {
16782
17123
  const settings = this.readSettings(settingsPath);
@@ -16808,14 +17149,14 @@ var init_integrations = __esm(() => {
16808
17149
  scopes: GOOSE_HOOK_SCOPES,
16809
17150
  eventTypes: GOOSE_HOOK_EVENT_TYPES,
16810
17151
  getSettingsPath(scope, cwd) {
16811
- const base = cwd ? resolve9(cwd) : process.cwd();
17152
+ const base = cwd ? resolve10(cwd) : process.cwd();
16812
17153
  switch (scope) {
16813
17154
  case "user":
16814
- return resolve9(homedir21(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17155
+ return resolve10(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
16815
17156
  case "project":
16816
- return resolve9(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17157
+ return resolve10(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
16817
17158
  case "local":
16818
- return resolve9(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17159
+ return resolve10(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
16819
17160
  }
16820
17161
  },
16821
17162
  readSettings(settingsPath) {
@@ -16882,7 +17223,7 @@ var init_integrations = __esm(() => {
16882
17223
  },
16883
17224
  hooksInstalledInSettings(scope, cwd) {
16884
17225
  const settingsPath = this.getSettingsPath(scope, cwd);
16885
- if (!existsSync10(settingsPath))
17226
+ if (!existsSync11(settingsPath))
16886
17227
  return false;
16887
17228
  try {
16888
17229
  const settings = this.readSettings(settingsPath);
@@ -16926,6 +17267,11 @@ import * as readline from "node:readline";
16926
17267
  function colorsEnabled(out) {
16927
17268
  return !!out.isTTY && !process.env.NO_COLOR;
16928
17269
  }
17270
+ function brandAnsi(role) {
17271
+ const h = HUES[role];
17272
+ const code = truecolorEnabled() ? `38;2;${h.rgb.join(";")}` : h.basic;
17273
+ return `${ESC}[${code}m`;
17274
+ }
16929
17275
  function truecolorEnabled() {
16930
17276
  return /truecolor|24bit/i.test(process.env.COLORTERM || "");
16931
17277
  }
@@ -17050,7 +17396,7 @@ function renderLaunchBanner(version3, stdout = process.stdout) {
17050
17396
  row("version", c.pink(version3)),
17051
17397
  row("star", c.dim("https://github.com/failproofai/failproofai")),
17052
17398
  row("docs", c.dim("https://docs.befailproof.ai/introduction")),
17053
- row("discord", c.dim("https://discord.gg/2zjBZP7yQJ")),
17399
+ row("discord", c.dim("https://discord.befailproof.ai/")),
17054
17400
  ""
17055
17401
  ];
17056
17402
  }
@@ -17145,7 +17491,7 @@ function runPrompt(p) {
17145
17491
  `${c.dim(BAR)} ${c.dim(p.summaryFor(result))}`
17146
17492
  ]);
17147
17493
  };
17148
- return new Promise((resolve10) => {
17494
+ return new Promise((resolve11) => {
17149
17495
  hideCursor(stdout);
17150
17496
  repaint(stdout, region, build());
17151
17497
  readline.emitKeypressEvents(stdin);
@@ -17161,7 +17507,7 @@ function runPrompt(p) {
17161
17507
  const finish = (result) => {
17162
17508
  cleanup();
17163
17509
  collapse(result);
17164
- resolve10(result);
17510
+ resolve11(result);
17165
17511
  };
17166
17512
  function onKey(_s, key) {
17167
17513
  if (!key)
@@ -17222,7 +17568,7 @@ function multiSelect(opts) {
17222
17568
  const choices = opts.choices;
17223
17569
  const minSelected = opts.minSelected ?? 0;
17224
17570
  const noun = opts.summaryNoun ?? "selected";
17225
- const checked = choices.map((ch) => !!ch.checked);
17571
+ const checked = choices.map((ch) => ch.locked ? ch.checked ?? true : !!ch.checked);
17226
17572
  if (!stdin.isTTY || !stdout.isTTY) {
17227
17573
  return Promise.resolve(choices.filter((_, i) => checked[i]).map((ch) => ch.value));
17228
17574
  }
@@ -17238,7 +17584,7 @@ function multiSelect(opts) {
17238
17584
  renderRow: (index, active, budget) => {
17239
17585
  const choice = choices[index];
17240
17586
  const caret = active ? c.pink(CARET) : " ";
17241
- const box = checked[index] ? c.pink(CHECK_ON) : c.dim(CHECK_OFF);
17587
+ const box = choice.locked ? checked[index] ? c.guide(CHECK_ON) : c.dim(CHECK_OFF) : checked[index] ? c.pink(CHECK_ON) : c.dim(CHECK_OFF);
17242
17588
  const rawLabel = choice.label.padEnd(nameCol);
17243
17589
  const label = active ? c.pinkBold(rawLabel) : checked[index] ? rawLabel : c.dim(rawLabel);
17244
17590
  const hint = choice.hint ? ` ${c.dim(ellipsize(choice.hint, budget))}` : "";
@@ -17248,19 +17594,23 @@ function multiSelect(opts) {
17248
17594
  footer: opts.hint ?? "↑/↓ move · space select · ctrl+a all · enter confirm",
17249
17595
  onKey: (key, cursor2) => {
17250
17596
  if (key.name === "space") {
17597
+ if (choices[cursor2]?.locked)
17598
+ return "redraw";
17251
17599
  checked[cursor2] = !checked[cursor2];
17252
17600
  warn = false;
17253
17601
  return "redraw";
17254
17602
  }
17255
17603
  if (key.ctrl && key.name === "a") {
17256
- const allOn = checked.every(Boolean);
17257
- for (let i = 0;i < checked.length; i++)
17258
- checked[i] = !allOn;
17604
+ const allOn = choices.every((ch, i) => checked[i] || ch.locked);
17605
+ for (let i = 0;i < checked.length; i++) {
17606
+ checked[i] = choices[i]?.locked ? true : !allOn;
17607
+ }
17259
17608
  return "redraw";
17260
17609
  }
17261
17610
  if (key.name === "return") {
17262
17611
  const selected = choices.filter((_, i) => checked[i]).map((ch) => ch.value);
17263
- if (selected.length < minSelected) {
17612
+ const chosen = choices.filter((ch, i) => checked[i] && !ch.locked).length;
17613
+ if (chosen < minSelected) {
17264
17614
  warn = true;
17265
17615
  return "redraw";
17266
17616
  }
@@ -17268,10 +17618,10 @@ function multiSelect(opts) {
17268
17618
  }
17269
17619
  return;
17270
17620
  },
17271
- summaryFor: (values) => values === null ? "cancelled" : summarize(choices.filter((ch) => values.includes(ch.value)).map((ch) => ch.label), noun)
17621
+ summaryFor: (values) => values === null ? "cancelled" : summarize(choices.filter((ch) => values.includes(ch.value) && !ch.summaryExclude).map((ch) => ch.label), noun)
17272
17622
  });
17273
17623
  }
17274
- var ESC = "\x1B", BAR = "│", BAR_END = "└", STEP_ACTIVE = "◆", STEP_DONE = "◇", RADIO_ON = "●", RADIO_OFF = "○", CHECK_ON = "◼", CHECK_OFF = "◻", CARET = "❯", FLOWER = "❋", HUES, LOGO_MIN_COLS = 22, TAGLINE = "end-to-end failure layer for ai agents", LOGO_GRID, LOGO_TEAL, LOGO_PINK, WINDOW = 8;
17624
+ var ESC = "\x1B", BAR = "│", BAR_END = "└", STEP_ACTIVE = "◆", STEP_DONE = "◇", RADIO_ON = "●", RADIO_OFF = "○", CHECK_ON = "◼", CHECK_OFF = "◻", CARET = "❯", FLOWER = "❋", HUES, ANSI_RESET, ANSI_BOLD, ANSI_DIM, LOGO_MIN_COLS = 22, TAGLINE = "end-to-end failure layer for ai agents", LOGO_GRID, LOGO_TEAL, LOGO_PINK, WINDOW = 8;
17275
17625
  var init_tui = __esm(() => {
17276
17626
  HUES = {
17277
17627
  guide: { rgb: [102, 209, 181], basic: "36" },
@@ -17280,29 +17630,30 @@ var init_tui = __esm(() => {
17280
17630
  warn: { rgb: [227, 179, 65], basic: "33" },
17281
17631
  dim: { rgb: [107, 118, 132], basic: "2" }
17282
17632
  };
17633
+ ANSI_RESET = `${ESC}[0m`;
17634
+ ANSI_BOLD = `${ESC}[1m`;
17635
+ ANSI_DIM = `${ESC}[2m`;
17283
17636
  LOGO_GRID = [
17284
- "...tt...........",
17285
- "..tttt..........",
17286
- ".tttttt.........",
17287
- ".tttttt.....ppp.",
17288
- "..tttt......ppp.",
17289
- "...tt.......ppp.",
17290
- "............ppp.",
17291
- "............ppp.",
17292
- "..pppp......ppp.",
17293
- "..pppp......ppp.",
17294
- "..pppp......ppp.",
17295
- ".pppppp.....ppp.",
17296
- ".pppppp.....ppp.",
17297
- ".pppppp.....ppp.",
17298
- ".pppppp.....ppp.",
17299
- "..pppp......ppp.",
17300
- "..pppp......ppp.",
17301
- "..pppp......ppp.",
17302
- "..ppppppppppppp.",
17303
- "..ppppppppppppp.",
17304
- "..ppppppppppppp.",
17305
- "..ppppppppppppp."
17637
+ "...tt........",
17638
+ "..tttt.......",
17639
+ ".tttttt..pppp",
17640
+ ".tttttt..pppp",
17641
+ "..tttt...pppp",
17642
+ "...tt....pppp",
17643
+ ".........pppp",
17644
+ ".........pppp",
17645
+ "..pppp...pppp",
17646
+ "..pppp...pppp",
17647
+ "..pppp...pppp",
17648
+ ".pppppp..pppp",
17649
+ ".pppppp..pppp",
17650
+ ".pppppp..pppp",
17651
+ ".pppppp..pppp",
17652
+ "..pppp...pppp",
17653
+ "..ppppppppppp",
17654
+ "..ppppppppppp",
17655
+ "..ppppppppppp",
17656
+ "..ppppppppppp"
17306
17657
  ];
17307
17658
  LOGO_TEAL = HUES.guide.rgb;
17308
17659
  LOGO_PINK = HUES.logoPink.rgb;
@@ -17457,7 +17808,7 @@ async function promptPolicySelection(preSelected, options = {}) {
17457
17808
  `);
17458
17809
  lastLineCount = lines.length;
17459
17810
  }
17460
- return new Promise((resolve10) => {
17811
+ return new Promise((resolve11) => {
17461
17812
  render();
17462
17813
  process.stdin.setRawMode(true);
17463
17814
  process.stdin.resume();
@@ -17494,7 +17845,7 @@ async function promptPolicySelection(preSelected, options = {}) {
17494
17845
  ${dim(STEP_DONE)} Policies
17495
17846
  ${dim(BAR)} ${dim(`${selected.length} selected`)}
17496
17847
  `);
17497
- resolve10(selected);
17848
+ resolve11(selected);
17498
17849
  } else if (key.name === "escape") {
17499
17850
  search = "";
17500
17851
  cursor2 = 0;
@@ -17555,10 +17906,10 @@ __export(exports_manager, {
17555
17906
  hooksInstalledInSettings: () => hooksInstalledInSettings,
17556
17907
  getSettingsPath: () => getSettingsPath
17557
17908
  });
17558
- import { execSync as execSync5 } from "node:child_process";
17559
- import { existsSync as existsSync11 } from "node:fs";
17560
- import { resolve as resolve10, basename as basename3 } from "node:path";
17561
- import { homedir as homedir22, platform, arch, release, hostname } from "node:os";
17909
+ import { execSync as execSync6 } from "node:child_process";
17910
+ import { existsSync as existsSync12 } from "node:fs";
17911
+ import { resolve as resolve11, basename as basename3 } from "node:path";
17912
+ import { homedir as homedir24, platform as platform3, arch as arch3, release as release2, hostname as hostname3 } from "node:os";
17562
17913
  function getSettingsPath(scope, cwd) {
17563
17914
  return claudeCode.getSettingsPath(scope, cwd);
17564
17915
  }
@@ -17578,7 +17929,7 @@ function resolveFailproofaiBinary() {
17578
17929
  return override.trim();
17579
17930
  try {
17580
17931
  const cmd = process.platform === "win32" ? "where failproofai" : "which failproofai";
17581
- const result = execSync5(cmd, { encoding: "utf8" }).trim();
17932
+ const result = execSync6(cmd, { encoding: "utf8" }).trim();
17582
17933
  return result.split(`
17583
17934
  `)[0].trim();
17584
17935
  } catch {
@@ -17664,7 +18015,7 @@ async function installHooksImpl(policyNames, scope = "user", cwd, includeBeta =
17664
18015
  if (removeCustomHooks) {
17665
18016
  delete configToWrite.customPoliciesPath;
17666
18017
  } else if (customPoliciesPath) {
17667
- configToWrite.customPoliciesPath = resolve10(customPoliciesPath);
18018
+ configToWrite.customPoliciesPath = resolve11(customPoliciesPath);
17668
18019
  let validatedHooks = [];
17669
18020
  try {
17670
18021
  validatedHooks = await loadCustomHooks(configToWrite.customPoliciesPath, { strict: true });
@@ -17736,10 +18087,10 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
17736
18087
  policies_added: policiesAdded,
17737
18088
  policies_removed: policiesRemoved,
17738
18089
  ...source ? { source } : {},
17739
- platform: platform(),
17740
- arch: arch(),
17741
- os_release: release(),
17742
- hostname_hash: hashToId2(hostname()),
18090
+ platform: platform3(),
18091
+ arch: arch3(),
18092
+ os_release: release2(),
18093
+ hostname_hash: hashToId2(hostname3()),
17743
18094
  has_custom_hooks_path: !!configToWrite.customPoliciesPath,
17744
18095
  has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
17745
18096
  param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
@@ -17826,10 +18177,10 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
17826
18177
  policies_removed: actuallyRemoved,
17827
18178
  removed_count: actuallyRemoved.length,
17828
18179
  ...opts?.source ? { source: opts.source } : {},
17829
- platform: platform(),
17830
- arch: arch(),
17831
- os_release: release(),
17832
- hostname_hash: hashToId2(hostname())
18180
+ platform: platform3(),
18181
+ arch: arch3(),
18182
+ os_release: release2(),
18183
+ hostname_hash: hashToId2(hostname3())
17833
18184
  });
17834
18185
  } catch {}
17835
18186
  console.log(`Disabled ${policyNames.length - notEnabled.length} policy(ies).`);
@@ -17844,7 +18195,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
17844
18195
  const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
17845
18196
  for (const s of scopesToRemove) {
17846
18197
  const settingsPath = integration.getSettingsPath(s, cwd);
17847
- if (!existsSync11(settingsPath)) {
18198
+ if (!existsSync12(settingsPath)) {
17848
18199
  if (scope !== "all" && selectedClis.length === 1) {
17849
18200
  console.log("No settings file found. Nothing to remove.");
17850
18201
  nothingToReport = true;
@@ -17884,10 +18235,10 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
17884
18235
  policies_removed: configBeforeRemoval.enabledPolicies,
17885
18236
  removed_count: totalRemoved,
17886
18237
  ...opts?.source ? { source: opts.source } : {},
17887
- platform: platform(),
17888
- arch: arch(),
17889
- os_release: release(),
17890
- hostname_hash: hashToId2(hostname())
18238
+ platform: platform3(),
18239
+ arch: arch3(),
18240
+ os_release: release2(),
18241
+ hostname_hash: hashToId2(hostname3())
17891
18242
  });
17892
18243
  } catch {}
17893
18244
  if (scope === "all") {
@@ -18033,7 +18384,7 @@ Failproof AI Hook Policies
18033
18384
  if (config.customPoliciesPath) {
18034
18385
  console.log(`
18035
18386
  ── Custom Policies (${config.customPoliciesPath}) ───────────────────────`);
18036
- if (!existsSync11(config.customPoliciesPath)) {
18387
+ if (!existsSync12(config.customPoliciesPath)) {
18037
18388
  console.log(` \x1B[31m✗ File not found: ${config.customPoliciesPath}\x1B[0m`);
18038
18389
  } else {
18039
18390
  const hooks = await loadCustomHooks(config.customPoliciesPath);
@@ -18048,10 +18399,10 @@ Failproof AI Hook Policies
18048
18399
  }
18049
18400
  console.log();
18050
18401
  }
18051
- const base = cwd ? resolve10(cwd) : process.cwd();
18402
+ const base = cwd ? resolve11(cwd) : process.cwd();
18052
18403
  const conventionDirs = [
18053
- { label: "Project", dir: resolve10(base, ".failproofai", "policies") },
18054
- { label: "User", dir: resolve10(homedir22(), ".failproofai", "policies") }
18404
+ { label: "Project", dir: resolve11(base, ".failproofai", "policies") },
18405
+ { label: "User", dir: resolve11(homedir24(), ".failproofai", "policies") }
18055
18406
  ];
18056
18407
  for (const { label, dir } of conventionDirs) {
18057
18408
  const files = discoverPolicyFiles(dir);
@@ -18253,7 +18604,7 @@ async function promptCliTargetSelection(detected, action = "install") {
18253
18604
  lastLineCount = lines.length;
18254
18605
  }
18255
18606
  const itemCount = options.length;
18256
- return new Promise((resolve11) => {
18607
+ return new Promise((resolve12) => {
18257
18608
  render();
18258
18609
  readline3.emitKeypressEvents(process.stdin);
18259
18610
  const wasRaw = process.stdin.isRaw;
@@ -18286,7 +18637,7 @@ async function promptCliTargetSelection(detected, action = "install") {
18286
18637
  cleanup();
18287
18638
  process.stdout.write(`
18288
18639
  `);
18289
- resolve11(options[cursor2].value);
18640
+ resolve12(options[cursor2].value);
18290
18641
  }
18291
18642
  }
18292
18643
  process.stdin.on("keypress", onKey);
@@ -18439,7 +18790,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
18439
18790
  `);
18440
18791
  lastLineCount = lines.length;
18441
18792
  }
18442
- return new Promise((resolve11) => {
18793
+ return new Promise((resolve12) => {
18443
18794
  render();
18444
18795
  process.stdin.setRawMode(true);
18445
18796
  process.stdin.resume();
@@ -18476,7 +18827,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
18476
18827
  ${dim(STEP_DONE)} Policies
18477
18828
  ${dim(BAR)} ${dim(`${selected.length} selected`)}
18478
18829
  `);
18479
- resolve11(selected);
18830
+ resolve12(selected);
18480
18831
  } else if (key.name === "escape") {
18481
18832
  search = "";
18482
18833
  cursor2 = 0;
@@ -18656,12 +19007,12 @@ var init_api_server_client = __esm(() => {
18656
19007
  // lib/atomic-write.ts
18657
19008
  import {
18658
19009
  chmodSync,
18659
- existsSync as existsSync12,
18660
- mkdirSync as mkdirSync6,
19010
+ existsSync as existsSync13,
19011
+ mkdirSync as mkdirSync8,
18661
19012
  renameSync as renameSync3,
18662
19013
  rmSync,
18663
19014
  statSync as statSync11,
18664
- writeFileSync as writeFileSync5
19015
+ writeFileSync as writeFileSync7
18665
19016
  } from "node:fs";
18666
19017
  import { dirname as dirname5 } from "node:path";
18667
19018
  import { randomBytes } from "node:crypto";
@@ -18669,11 +19020,11 @@ function writeJsonAtomically(filePath, value, opts = {}) {
18669
19020
  const mode = opts.mode ?? 384;
18670
19021
  const dirMode = opts.dirMode ?? 448;
18671
19022
  const dir = dirname5(filePath);
18672
- if (!existsSync12(dir))
18673
- mkdirSync6(dir, { recursive: true, mode: dirMode });
19023
+ if (!existsSync13(dir))
19024
+ mkdirSync8(dir, { recursive: true, mode: dirMode });
18674
19025
  const tmp = `${filePath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
18675
19026
  try {
18676
- writeFileSync5(tmp, JSON.stringify(value, null, 2), { mode });
19027
+ writeFileSync7(tmp, JSON.stringify(value, null, 2), { mode });
18677
19028
  try {
18678
19029
  if ((statSync11(tmp).mode & 63) !== 0)
18679
19030
  chmodSync(tmp, mode);
@@ -18693,24 +19044,24 @@ function writeJsonAtomically(filePath, value, opts = {}) {
18693
19044
  var init_atomic_write = () => {};
18694
19045
 
18695
19046
  // lib/auth/auth-store.ts
18696
- import { existsSync as existsSync13, readFileSync as readFileSync11, rmSync as rmSync2 } from "node:fs";
18697
- import { homedir as homedir23 } from "node:os";
18698
- import { join as join24 } from "node:path";
19047
+ import { existsSync as existsSync14, readFileSync as readFileSync13, rmSync as rmSync2 } from "node:fs";
19048
+ import { homedir as homedir25 } from "node:os";
19049
+ import { join as join25 } from "node:path";
18699
19050
  function getAuthDir() {
18700
19051
  const override = process.env.FAILPROOFAI_AUTH_DIR;
18701
19052
  if (override)
18702
19053
  return override;
18703
- return join24(homedir23(), ".failproofai");
19054
+ return join25(homedir25(), ".failproofai");
18704
19055
  }
18705
19056
  function getAuthFilePath() {
18706
- return join24(getAuthDir(), "auth.json");
19057
+ return join25(getAuthDir(), "auth.json");
18707
19058
  }
18708
19059
  function readAuth() {
18709
19060
  const p = getAuthFilePath();
18710
- if (!existsSync13(p))
19061
+ if (!existsSync14(p))
18711
19062
  return null;
18712
19063
  try {
18713
- const raw = readFileSync11(p, "utf-8");
19064
+ const raw = readFileSync13(p, "utf-8");
18714
19065
  const parsed = JSON.parse(raw);
18715
19066
  if (typeof parsed.access_token !== "string" || typeof parsed.refresh_token !== "string" || typeof parsed.access_expires_at !== "number" || typeof parsed.user !== "object" || !parsed.user || typeof parsed.user.id !== "string" || typeof parsed.user.email !== "string") {
18716
19067
  return null;
@@ -18734,7 +19085,7 @@ function writeAuth(auth) {
18734
19085
  }
18735
19086
  function deleteAuth() {
18736
19087
  const p = getAuthFilePath();
18737
- if (existsSync13(p))
19088
+ if (existsSync14(p))
18738
19089
  rmSync2(p, { force: true });
18739
19090
  }
18740
19091
  function authFromTokenResponse(token, existingUser) {
@@ -18811,13 +19162,13 @@ function prompt(question, opts = {}) {
18811
19162
  orig(s);
18812
19163
  };
18813
19164
  }
18814
- return new Promise((resolve11) => {
19165
+ return new Promise((resolve12) => {
18815
19166
  rl.question(question, (answer) => {
18816
19167
  rl.close();
18817
19168
  if (opts.hidden && process.stdin.isTTY)
18818
19169
  process.stdout.write(`
18819
19170
  `);
18820
- resolve11(answer.trim());
19171
+ resolve12(answer.trim());
18821
19172
  });
18822
19173
  });
18823
19174
  }
@@ -19023,11 +19374,12 @@ async function runAuthCli(args) {
19023
19374
  return runLogout();
19024
19375
  return runWhoami();
19025
19376
  }
19026
- var HELP, LEGACY_FLAG_TO_SUB, SUBCOMMANDS, DIM = "\x1B[2m", RESET = "\x1B[0m", PINK = "\x1B[38;5;204m", GREEN = "\x1B[38;5;120m", RED = "\x1B[38;5;197m";
19377
+ var HELP, LEGACY_FLAG_TO_SUB, SUBCOMMANDS, COLOR, DIM, RESET, PINK, GREEN, RED;
19027
19378
  var init_cli = __esm(() => {
19028
19379
  init_api_server_client();
19029
19380
  init_auth_store();
19030
19381
  init_cli_error();
19382
+ init_tui();
19031
19383
  init_hook_telemetry2();
19032
19384
  init_telemetry_id2();
19033
19385
  HELP = `
@@ -19056,11 +19408,17 @@ EXAMPLES
19056
19408
  "--whoami": "whoami"
19057
19409
  };
19058
19410
  SUBCOMMANDS = new Set(["login", "logout", "whoami", "help"]);
19411
+ COLOR = colorsEnabled(process.stdout);
19412
+ DIM = COLOR ? ANSI_DIM : "";
19413
+ RESET = COLOR ? ANSI_RESET : "";
19414
+ PINK = COLOR ? brandAnsi("pink") : "";
19415
+ GREEN = COLOR ? brandAnsi("guide") : "";
19416
+ RED = COLOR ? brandAnsi("warn") : "";
19059
19417
  });
19060
19418
 
19061
19419
  // lib/claude-sessions.ts
19062
- import { readdirSync as readdirSync10, statSync as statSync12, existsSync as existsSync14 } from "node:fs";
19063
- import { join as join25, basename as basename4 } from "node:path";
19420
+ import { readdirSync as readdirSync10, statSync as statSync12, existsSync as existsSync15 } from "node:fs";
19421
+ import { join as join26, basename as basename4 } from "node:path";
19064
19422
  function getClaudeProjectsRoot() {
19065
19423
  return getClaudeProjectsPath();
19066
19424
  }
@@ -19075,7 +19433,7 @@ function listClaudeProjects() {
19075
19433
  return entries.filter((e) => e.isDirectory()).map((e) => ({
19076
19434
  name: e.name,
19077
19435
  cwd: decodeFolderName(e.name),
19078
- path: join25(root, e.name)
19436
+ path: join26(root, e.name)
19079
19437
  }));
19080
19438
  }
19081
19439
  function listClaudeTranscripts(project) {
@@ -19091,7 +19449,7 @@ function listClaudeTranscripts(project) {
19091
19449
  const sessionId = entry.name.slice(0, -".jsonl".length);
19092
19450
  if (!UUID_RE2.test(sessionId))
19093
19451
  continue;
19094
- const transcriptPath = join25(project.path, entry.name);
19452
+ const transcriptPath = join26(project.path, entry.name);
19095
19453
  try {
19096
19454
  const s = statSync12(transcriptPath);
19097
19455
  out.push({
@@ -19105,8 +19463,8 @@ function listClaudeTranscripts(project) {
19105
19463
  });
19106
19464
  } catch {}
19107
19465
  } else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
19108
- const subDir = join25(project.path, entry.name, "subagents");
19109
- if (!existsSync14(subDir))
19466
+ const subDir = join26(project.path, entry.name, "subagents");
19467
+ if (!existsSync15(subDir))
19110
19468
  continue;
19111
19469
  let subEntries;
19112
19470
  try {
@@ -19118,7 +19476,7 @@ function listClaudeTranscripts(project) {
19118
19476
  if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
19119
19477
  continue;
19120
19478
  const agentId = sub.name.slice(0, -".jsonl".length);
19121
- const transcriptPath = join25(subDir, sub.name);
19479
+ const transcriptPath = join26(subDir, sub.name);
19122
19480
  try {
19123
19481
  const s = statSync12(transcriptPath);
19124
19482
  out.push({
@@ -20372,9 +20730,9 @@ var init_features = __esm(() => {
20372
20730
 
20373
20731
  // src/audit/cache.ts
20374
20732
  import { createHash } from "node:crypto";
20375
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6, chmodSync as chmodSync2 } from "node:fs";
20376
- import { join as join26 } from "node:path";
20377
- import { homedir as homedir24 } from "node:os";
20733
+ import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync8, chmodSync as chmodSync2 } from "node:fs";
20734
+ import { join as join27 } from "node:path";
20735
+ import { homedir as homedir26 } from "node:os";
20378
20736
  function getEngineVersion() {
20379
20737
  if (cachedEngineVersion)
20380
20738
  return cachedEngineVersion;
@@ -20392,18 +20750,18 @@ function getDetectorVersion() {
20392
20750
  return cachedDetectorVersion;
20393
20751
  }
20394
20752
  function getCachePathFor(transcriptPath) {
20395
- const root = join26(homedir24(), ".failproofai", "cache", "audit");
20753
+ const root = join27(homedir26(), ".failproofai", "cache", "audit");
20396
20754
  const key = createHash("sha1").update(transcriptPath).digest("hex");
20397
- return join26(root, `${key}.json`);
20755
+ return join27(root, `${key}.json`);
20398
20756
  }
20399
20757
  function readCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes) {
20400
20758
  if (sizeBytes === 0)
20401
20759
  return null;
20402
20760
  const cachePath = getCachePathFor(transcriptPath);
20403
- if (!existsSync15(cachePath))
20761
+ if (!existsSync16(cachePath))
20404
20762
  return null;
20405
20763
  try {
20406
- const raw = readFileSync12(cachePath, "utf-8");
20764
+ const raw = readFileSync14(cachePath, "utf-8");
20407
20765
  const entry = JSON.parse(raw);
20408
20766
  if (entry.schemaVersion !== CACHE_SCHEMA_VERSION)
20409
20767
  return null;
@@ -20427,7 +20785,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
20427
20785
  return;
20428
20786
  const cachePath = getCachePathFor(transcriptPath);
20429
20787
  try {
20430
- mkdirSync7(join26(homedir24(), ".failproofai", "cache", "audit"), { recursive: true });
20788
+ mkdirSync9(join27(homedir26(), ".failproofai", "cache", "audit"), { recursive: true });
20431
20789
  const entry = {
20432
20790
  schemaVersion: CACHE_SCHEMA_VERSION,
20433
20791
  cachedAt: Date.now(),
@@ -20437,7 +20795,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
20437
20795
  detectorVersion: getDetectorVersion(),
20438
20796
  result
20439
20797
  };
20440
- writeFileSync6(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
20798
+ writeFileSync8(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
20441
20799
  try {
20442
20800
  chmodSync2(cachePath, 384);
20443
20801
  } catch {}
@@ -20799,10 +21157,10 @@ var init_audit = __esm(() => {
20799
21157
  });
20800
21158
 
20801
21159
  // src/audit/dashboard-cache.ts
20802
- import { join as join27 } from "node:path";
20803
- import { homedir as homedir25 } from "node:os";
21160
+ import { join as join28 } from "node:path";
21161
+ import { homedir as homedir27 } from "node:os";
20804
21162
  function getCachePath() {
20805
- return join27(homedir25(), ".failproofai", "audit-dashboard.json");
21163
+ return join28(homedir27(), ".failproofai", "audit-dashboard.json");
20806
21164
  }
20807
21165
  function writeDashboardCache(params, result) {
20808
21166
  try {
@@ -20892,14 +21250,14 @@ var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
20892
21250
  var init_open_browser = () => {};
20893
21251
 
20894
21252
  // scripts/parse-script-args.ts
20895
- import { resolve as resolve11 } from "path";
21253
+ import { resolve as resolve12 } from "path";
20896
21254
  function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
20897
21255
  const raw = inlineValue ?? args[index + 1];
20898
21256
  if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
20899
21257
  console.error(`Error: ${flagName} requires ${errorLabel}`);
20900
21258
  process.exit(1);
20901
21259
  }
20902
- const value = options?.resolve ? resolve11(raw) : raw;
21260
+ const value = options?.resolve ? resolve12(raw) : raw;
20903
21261
  return { value, spliceCount: inlineValue !== null ? 1 : 2 };
20904
21262
  }
20905
21263
  function parseScriptArgs(argv) {
@@ -20947,21 +21305,21 @@ function parseScriptArgs(argv) {
20947
21305
  var init_parse_script_args = () => {};
20948
21306
 
20949
21307
  // scripts/install-diagnosis.mjs
20950
- import { existsSync as existsSync16, readFileSync as readFileSync13, realpathSync } from "node:fs";
20951
- import { dirname as dirname6, resolve as resolve12 } from "node:path";
20952
- import { homedir as homedir26, platform as platform2 } from "node:os";
21308
+ import { existsSync as existsSync17, readFileSync as readFileSync15, realpathSync } from "node:fs";
21309
+ import { dirname as dirname6, resolve as resolve13 } from "node:path";
21310
+ import { homedir as homedir28, platform as platform4 } from "node:os";
20953
21311
  import { spawnSync } from "node:child_process";
20954
21312
  function findPackageRoot(start) {
20955
21313
  try {
20956
21314
  let dir = realpathSync(start);
20957
- if (existsSync16(dir) && !existsSync16(resolve12(dir, "package.json"))) {
21315
+ if (existsSync17(dir) && !existsSync17(resolve13(dir, "package.json"))) {
20958
21316
  dir = dirname6(dir);
20959
21317
  }
20960
21318
  while (true) {
20961
- const pkgPath = resolve12(dir, "package.json");
20962
- if (existsSync16(pkgPath)) {
21319
+ const pkgPath = resolve13(dir, "package.json");
21320
+ if (existsSync17(pkgPath)) {
20963
21321
  try {
20964
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
21322
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
20965
21323
  if (pkg.name === PKG_NAME)
20966
21324
  return dir;
20967
21325
  } catch {}
@@ -20979,7 +21337,7 @@ function readPackageVersion(packageRoot) {
20979
21337
  if (!packageRoot)
20980
21338
  return null;
20981
21339
  try {
20982
- const pkg = JSON.parse(readFileSync13(resolve12(packageRoot, "package.json"), "utf8"));
21340
+ const pkg = JSON.parse(readFileSync15(resolve13(packageRoot, "package.json"), "utf8"));
20983
21341
  return typeof pkg.version === "string" ? pkg.version : null;
20984
21342
  } catch {
20985
21343
  return null;
@@ -20987,7 +21345,7 @@ function readPackageVersion(packageRoot) {
20987
21345
  }
20988
21346
  function resolvePathFirstBinary() {
20989
21347
  try {
20990
- const isWin = platform2() === "win32";
21348
+ const isWin = platform4() === "win32";
20991
21349
  const res = isWin ? spawnSync("where", [PKG_NAME], { encoding: "utf8" }) : spawnSync("sh", ["-c", `command -v ${PKG_NAME}`], { encoding: "utf8" });
20992
21350
  if (res.status !== 0)
20993
21351
  return null;
@@ -21005,16 +21363,16 @@ function locateNpmGlobal() {
21005
21363
  const root = (res.stdout || "").trim();
21006
21364
  if (!root)
21007
21365
  return null;
21008
- const candidate = resolve12(root, PKG_NAME);
21009
- return existsSync16(resolve12(candidate, "package.json")) ? candidate : null;
21366
+ const candidate = resolve13(root, PKG_NAME);
21367
+ return existsSync17(resolve13(candidate, "package.json")) ? candidate : null;
21010
21368
  } catch {
21011
21369
  return null;
21012
21370
  }
21013
21371
  }
21014
21372
  function locateBunGlobal() {
21015
21373
  try {
21016
- const candidate = resolve12(homedir26(), ".bun", "install", "global", "node_modules", PKG_NAME);
21017
- return existsSync16(resolve12(candidate, "package.json")) ? candidate : null;
21374
+ const candidate = resolve13(homedir28(), ".bun", "install", "global", "node_modules", PKG_NAME);
21375
+ return existsSync17(resolve13(candidate, "package.json")) ? candidate : null;
21018
21376
  } catch {
21019
21377
  return null;
21020
21378
  }
@@ -21022,8 +21380,8 @@ function locateBunGlobal() {
21022
21380
  function buildRecommendation(pathFirstBin) {
21023
21381
  if (!pathFirstBin)
21024
21382
  return null;
21025
- const bunBinPrefix = resolve12(homedir26(), ".bun", "bin") + "/";
21026
- const bunGlobalPrefix = resolve12(homedir26(), ".bun", "install", "global") + "/";
21383
+ const bunBinPrefix = resolve13(homedir28(), ".bun", "bin") + "/";
21384
+ const bunGlobalPrefix = resolve13(homedir28(), ".bun", "install", "global") + "/";
21027
21385
  const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
21028
21386
  if (isBun) {
21029
21387
  return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
@@ -21103,8 +21461,8 @@ __export(exports_launch, {
21103
21461
  launch: () => launch
21104
21462
  });
21105
21463
  import { spawn as spawn2 } from "child_process";
21106
- import { realpathSync as realpathSync2, existsSync as existsSync17 } from "node:fs";
21107
- import { resolve as resolve13, dirname as dirname7 } from "node:path";
21464
+ import { realpathSync as realpathSync2, existsSync as existsSync18 } from "node:fs";
21465
+ import { resolve as resolve14, dirname as dirname7 } from "node:path";
21108
21466
  import { fileURLToPath as fileURLToPath2 } from "node:url";
21109
21467
  import { createInterface as createInterface2 } from "node:readline";
21110
21468
  function launch(mode) {
@@ -21119,9 +21477,9 @@ function launch(mode) {
21119
21477
  process.env.PORT = port;
21120
21478
  process.env.HOSTNAME = "0.0.0.0";
21121
21479
  cmd = "node";
21122
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve13(dirname7(realpathSync2(fileURLToPath2(import.meta.url))), "..");
21123
- const serverJsPath = resolve13(packageRoot, ".next/standalone/server.js");
21124
- if (!existsSync17(serverJsPath)) {
21480
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve14(dirname7(realpathSync2(fileURLToPath2(import.meta.url))), "..");
21481
+ const serverJsPath = resolve14(packageRoot, ".next/standalone/server.js");
21482
+ if (!existsSync18(serverJsPath)) {
21125
21483
  let shadowMessage = null;
21126
21484
  try {
21127
21485
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -21332,9 +21690,20 @@ function printSummary(result) {
21332
21690
  process.stdout.write(` ${line}
21333
21691
  `);
21334
21692
  }
21693
+ function auditCompletedProps(source, result) {
21694
+ return {
21695
+ source,
21696
+ events_scanned: result.eventsScanned,
21697
+ sessions_scanned: result.transcripts.scanned,
21698
+ total_hits: result.totals.hits,
21699
+ findings: result.results.length
21700
+ };
21701
+ }
21335
21702
  async function runPostSetupAudit() {
21336
21703
  if (process.env.FAILPROOFAI_NO_AUTO_AUDIT === "1")
21337
21704
  return;
21705
+ const instanceId = getInstanceId2();
21706
+ trackHookEvent2(instanceId, "cli_audit_started", { source: "onboarding" });
21338
21707
  process.stdout.write(`
21339
21708
  ${c(PINK2, "✦")} ${c(BOLD, "failproofai audit now running")} ${c(DIM2, "· ctrl+c to stop")}
21340
21709
 
@@ -21342,12 +21711,18 @@ async function runPostSetupAudit() {
21342
21711
  let result;
21343
21712
  try {
21344
21713
  result = await runWithProgress({});
21345
- } catch {
21714
+ } catch (err) {
21715
+ await trackHookEvent2(instanceId, "cli_audit_failed", {
21716
+ source: "onboarding",
21717
+ error_type: err instanceof Error ? err.name : "unknown",
21718
+ error_message: sanitizeErrorMessage(err)
21719
+ });
21346
21720
  process.stdout.write(` ${c(PINK2, "!")} ${c(DIM2, "audit couldn't finish — run")} ${c(CYAN, "failproofai audit")} ${c(DIM2, "later.")}
21347
21721
 
21348
21722
  `);
21349
21723
  return;
21350
21724
  }
21725
+ await trackHookEvent2(instanceId, "cli_audit_completed", auditCompletedProps("onboarding", result));
21351
21726
  if (result.eventsScanned === 0) {
21352
21727
  process.stdout.write(`
21353
21728
  ${c(DIM2, "no agent sessions to audit yet — come back after using your agent.")}
@@ -21386,13 +21761,7 @@ async function runAuditCli(args) {
21386
21761
  die(`Audit failed: ${err instanceof Error ? err.message : String(err)}`);
21387
21762
  }
21388
21763
  printSummary(result);
21389
- await trackHookEvent2(instanceId, "cli_audit_completed", {
21390
- source: "cli",
21391
- events_scanned: result.eventsScanned,
21392
- sessions_scanned: result.transcripts.scanned,
21393
- total_hits: result.totals.hits,
21394
- findings: result.results.length
21395
- });
21764
+ await trackHookEvent2(instanceId, "cli_audit_completed", auditCompletedProps("cli", result));
21396
21765
  if (result.eventsScanned === 0) {
21397
21766
  process.stdout.write(`
21398
21767
  ${c(DIM2, "no agent sessions found yet.")}
@@ -21418,7 +21787,7 @@ async function runAuditCli(args) {
21418
21787
  const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
21419
21788
  launch2("start");
21420
21789
  }
21421
- var DASHBOARD_PORT = 8020, AUDIT_STAGES, HELP2, RESET2 = "\x1B[0m", DIM2 = "\x1B[2m", BOLD = "\x1B[1m", PINK2 = "\x1B[38;5;204m", GREEN2 = "\x1B[38;5;120m", CYAN = "\x1B[38;5;81m", SPINNER;
21790
+ var DASHBOARD_PORT = 8020, AUDIT_STAGES, HELP2, RESET2, DIM2, BOLD, PINK2, GREEN2, CYAN, SPINNER;
21422
21791
  var init_cli2 = __esm(() => {
21423
21792
  init_audit();
21424
21793
  init_dashboard_cache();
@@ -21426,6 +21795,7 @@ var init_cli2 = __esm(() => {
21426
21795
  init_telemetry_id2();
21427
21796
  init_telemetry_sanitize();
21428
21797
  init_open_browser();
21798
+ init_tui();
21429
21799
  AUDIT_STAGES = [
21430
21800
  { label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
21431
21801
  { label: "parsing session logs", detail: "reading JSONL + sqlite session stores" },
@@ -21449,13 +21819,19 @@ WHAT IT DOES
21449
21819
  Runs fully offline — no account or network required. Press Ctrl+C to stop the
21450
21820
  dashboard server when you're done.
21451
21821
  `.trimStart();
21822
+ RESET2 = ANSI_RESET;
21823
+ DIM2 = ANSI_DIM;
21824
+ BOLD = ANSI_BOLD;
21825
+ PINK2 = brandAnsi("pink");
21826
+ GREEN2 = brandAnsi("guide");
21827
+ CYAN = brandAnsi("guide");
21452
21828
  SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
21453
21829
  });
21454
21830
 
21455
21831
  // src/hooks/manager.ts
21456
- import { execSync as execSync6 } from "node:child_process";
21457
- import { resolve as resolve14, basename as basename5 } from "node:path";
21458
- import { homedir as homedir27, platform as platform3, arch as arch2, release as release2, hostname as hostname2 } from "node:os";
21832
+ import { execSync as execSync7 } from "node:child_process";
21833
+ import { resolve as resolve15, basename as basename5 } from "node:path";
21834
+ import { homedir as homedir29, platform as platform5, arch as arch4, release as release3, hostname as hostname4 } from "node:os";
21459
21835
  function getSettingsPath2(scope, cwd) {
21460
21836
  return claudeCode.getSettingsPath(scope, cwd);
21461
21837
  }
@@ -21475,7 +21851,7 @@ function resolveFailproofaiBinary2() {
21475
21851
  return override.trim();
21476
21852
  try {
21477
21853
  const cmd = process.platform === "win32" ? "where failproofai" : "which failproofai";
21478
- const result = execSync6(cmd, { encoding: "utf8" }).trim();
21854
+ const result = execSync7(cmd, { encoding: "utf8" }).trim();
21479
21855
  return result.split(`
21480
21856
  `)[0].trim();
21481
21857
  } catch {
@@ -21561,7 +21937,7 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
21561
21937
  if (removeCustomHooks) {
21562
21938
  delete configToWrite.customPoliciesPath;
21563
21939
  } else if (customPoliciesPath) {
21564
- configToWrite.customPoliciesPath = resolve14(customPoliciesPath);
21940
+ configToWrite.customPoliciesPath = resolve15(customPoliciesPath);
21565
21941
  let validatedHooks = [];
21566
21942
  try {
21567
21943
  validatedHooks = await loadCustomHooks(configToWrite.customPoliciesPath, { strict: true });
@@ -21633,10 +22009,10 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
21633
22009
  policies_added: policiesAdded,
21634
22010
  policies_removed: policiesRemoved,
21635
22011
  ...source ? { source } : {},
21636
- platform: platform3(),
21637
- arch: arch2(),
21638
- os_release: release2(),
21639
- hostname_hash: hashToId2(hostname2()),
22012
+ platform: platform5(),
22013
+ arch: arch4(),
22014
+ os_release: release3(),
22015
+ hostname_hash: hashToId2(hostname4()),
21640
22016
  has_custom_hooks_path: !!configToWrite.customPoliciesPath,
21641
22017
  has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
21642
22018
  param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
@@ -21881,9 +22257,20 @@ function printSummary2(result) {
21881
22257
  process.stdout.write(` ${line}
21882
22258
  `);
21883
22259
  }
22260
+ function auditCompletedProps2(source, result) {
22261
+ return {
22262
+ source,
22263
+ events_scanned: result.eventsScanned,
22264
+ sessions_scanned: result.transcripts.scanned,
22265
+ total_hits: result.totals.hits,
22266
+ findings: result.results.length
22267
+ };
22268
+ }
21884
22269
  async function runPostSetupAudit2() {
21885
22270
  if (process.env.FAILPROOFAI_NO_AUTO_AUDIT === "1")
21886
22271
  return;
22272
+ const instanceId = getInstanceId2();
22273
+ trackHookEvent2(instanceId, "cli_audit_started", { source: "onboarding" });
21887
22274
  process.stdout.write(`
21888
22275
  ${c2(PINK3, "✦")} ${c2(BOLD2, "failproofai audit now running")} ${c2(DIM3, "· ctrl+c to stop")}
21889
22276
 
@@ -21891,12 +22278,18 @@ async function runPostSetupAudit2() {
21891
22278
  let result;
21892
22279
  try {
21893
22280
  result = await runWithProgress2({});
21894
- } catch {
22281
+ } catch (err) {
22282
+ await trackHookEvent2(instanceId, "cli_audit_failed", {
22283
+ source: "onboarding",
22284
+ error_type: err instanceof Error ? err.name : "unknown",
22285
+ error_message: sanitizeErrorMessage(err)
22286
+ });
21895
22287
  process.stdout.write(` ${c2(PINK3, "!")} ${c2(DIM3, "audit couldn't finish — run")} ${c2(CYAN2, "failproofai audit")} ${c2(DIM3, "later.")}
21896
22288
 
21897
22289
  `);
21898
22290
  return;
21899
22291
  }
22292
+ await trackHookEvent2(instanceId, "cli_audit_completed", auditCompletedProps2("onboarding", result));
21900
22293
  if (result.eventsScanned === 0) {
21901
22294
  process.stdout.write(`
21902
22295
  ${c2(DIM3, "no agent sessions to audit yet — come back after using your agent.")}
@@ -21935,13 +22328,7 @@ async function runAuditCli2(args) {
21935
22328
  die2(`Audit failed: ${err instanceof Error ? err.message : String(err)}`);
21936
22329
  }
21937
22330
  printSummary2(result);
21938
- await trackHookEvent2(instanceId, "cli_audit_completed", {
21939
- source: "cli",
21940
- events_scanned: result.eventsScanned,
21941
- sessions_scanned: result.transcripts.scanned,
21942
- total_hits: result.totals.hits,
21943
- findings: result.results.length
21944
- });
22331
+ await trackHookEvent2(instanceId, "cli_audit_completed", auditCompletedProps2("cli", result));
21945
22332
  if (result.eventsScanned === 0) {
21946
22333
  process.stdout.write(`
21947
22334
  ${c2(DIM3, "no agent sessions found yet.")}
@@ -21967,7 +22354,7 @@ async function runAuditCli2(args) {
21967
22354
  const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
21968
22355
  launch2("start");
21969
22356
  }
21970
- var DASHBOARD_PORT2 = 8020, AUDIT_STAGES2, HELP3, RESET3 = "\x1B[0m", DIM3 = "\x1B[2m", BOLD2 = "\x1B[1m", PINK3 = "\x1B[38;5;204m", GREEN3 = "\x1B[38;5;120m", CYAN2 = "\x1B[38;5;81m", SPINNER2;
22357
+ var DASHBOARD_PORT2 = 8020, AUDIT_STAGES2, HELP3, RESET3, DIM3, BOLD2, PINK3, GREEN3, CYAN2, SPINNER2;
21971
22358
  var init_cli3 = __esm(() => {
21972
22359
  init_audit();
21973
22360
  init_dashboard_cache();
@@ -21975,6 +22362,7 @@ var init_cli3 = __esm(() => {
21975
22362
  init_telemetry_id2();
21976
22363
  init_telemetry_sanitize();
21977
22364
  init_open_browser();
22365
+ init_tui();
21978
22366
  AUDIT_STAGES2 = [
21979
22367
  { label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
21980
22368
  { label: "parsing session logs", detail: "reading JSONL + sqlite session stores" },
@@ -21998,32 +22386,41 @@ WHAT IT DOES
21998
22386
  Runs fully offline — no account or network required. Press Ctrl+C to stop the
21999
22387
  dashboard server when you're done.
22000
22388
  `.trimStart();
22389
+ RESET3 = ANSI_RESET;
22390
+ DIM3 = ANSI_DIM;
22391
+ BOLD2 = ANSI_BOLD;
22392
+ PINK3 = brandAnsi("pink");
22393
+ GREEN3 = brandAnsi("guide");
22394
+ CYAN2 = brandAnsi("guide");
22001
22395
  SPINNER2 = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
22002
22396
  });
22003
22397
 
22004
22398
  // src/hooks/configure-wizard.ts
22005
22399
  var exports_configure_wizard = {};
22006
22400
  __export(exports_configure_wizard, {
22401
+ setCustomPoliciesEnabled: () => setCustomPoliciesEnabled,
22007
22402
  runConfigureWizard: () => runConfigureWizard,
22008
22403
  reviewLines: () => reviewLines,
22009
22404
  resolvePresetSelection: () => resolvePresetSelection,
22010
22405
  maybeFirstRunConfigure: () => maybeFirstRunConfigure,
22011
22406
  markLauncherSeen: () => markLauncherSeen,
22012
22407
  hasSeenLauncher: () => hasSeenLauncher,
22408
+ describeCustomPolicies: () => describeCustomPolicies,
22409
+ clisSupportingScope: () => clisSupportingScope,
22013
22410
  buildScopeChoices: () => buildScopeChoices,
22014
22411
  buildPresetChoices: () => buildPresetChoices,
22015
22412
  buildAgentChoices: () => buildAgentChoices
22016
22413
  });
22017
- import { existsSync as existsSync18, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "node:fs";
22018
- import { homedir as homedir28 } from "node:os";
22019
- import { resolve as resolve15, sep as sep5 } from "node:path";
22414
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync16 } from "node:fs";
22415
+ import { homedir as homedir30 } from "node:os";
22416
+ import { resolve as resolve16, sep as sep5 } from "node:path";
22020
22417
  async function emit(event, props) {
22021
22418
  try {
22022
22419
  await trackHookEvent2(getInstanceId2(), event, props);
22023
22420
  } catch {}
22024
22421
  }
22025
22422
  function homeify(p) {
22026
- const home = homedir28();
22423
+ const home = homedir30();
22027
22424
  if (p === home)
22028
22425
  return "~";
22029
22426
  if (p.startsWith(home + sep5))
@@ -22044,6 +22441,9 @@ function buildScopeChoices(cwd) {
22044
22441
  }
22045
22442
  ];
22046
22443
  }
22444
+ function clisSupportingScope(scope) {
22445
+ return INTEGRATION_TYPES.filter((id) => getIntegration(id).scopes.includes(scope));
22446
+ }
22047
22447
  function buildAgentChoices(scope, cwd) {
22048
22448
  const detected = new Set(detectInstalledClis());
22049
22449
  const ordered = [
@@ -22053,6 +22453,17 @@ function buildAgentChoices(scope, cwd) {
22053
22453
  return ordered.map((id) => {
22054
22454
  const integration = getIntegration(id);
22055
22455
  const isDetected = detected.has(id);
22456
+ const supported = integration.scopes.includes(scope);
22457
+ if (!supported) {
22458
+ return {
22459
+ label: integration.displayName,
22460
+ value: id,
22461
+ checked: false,
22462
+ locked: true,
22463
+ section: "Global only · not configurable per-project",
22464
+ hint: `supports ${integration.scopes.join(", ")} scope — rerun with "Everywhere I code"`
22465
+ };
22466
+ }
22056
22467
  let installedHere = false;
22057
22468
  try {
22058
22469
  installedHere = integration.hooksInstalledInSettings(scope, cwd);
@@ -22068,28 +22479,102 @@ function buildAgentChoices(scope, cwd) {
22068
22479
  };
22069
22480
  });
22070
22481
  }
22071
- function buildPresetChoices() {
22072
- const choices = POLICY_PRESETS.map((p) => ({ label: p.label, value: p.id, hint: p.description }));
22482
+ function buildPresetChoices(cwd = process.cwd(), enabled = true) {
22483
+ const choices = POLICY_PRESETS.map((p) => ({
22484
+ label: p.label,
22485
+ value: p.id,
22486
+ hint: p.description
22487
+ }));
22073
22488
  choices.push({
22074
22489
  label: "Everything",
22075
22490
  value: EVERYTHING,
22076
22491
  hint: `all ${resolveEverything().length} policies`
22077
22492
  });
22493
+ const custom = describeCustomPolicies(cwd);
22494
+ const skipped = custom.warnings.length;
22495
+ const plural = (n) => `${n} file${n === 1 ? "" : "s"}`;
22496
+ const skippedNote = skipped > 0 ? ` · ${skipped} skipped, see next screen` : "";
22497
+ if (custom.fileCount > 0) {
22498
+ choices.push({
22499
+ label: "Custom",
22500
+ value: CUSTOM,
22501
+ checked: enabled,
22502
+ hint: `${plural(custom.fileCount)} in ${custom.scopes.join(" + ")}${skippedNote}`
22503
+ });
22504
+ } else {
22505
+ choices.push({
22506
+ label: "Custom",
22507
+ value: CUSTOM,
22508
+ locked: true,
22509
+ checked: false,
22510
+ summaryExclude: true,
22511
+ hint: skipped > 0 ? `${plural(skipped)} found but NOT loaded — see next screen` : "none yet · drop *-policies.mjs in .failproofai/policies/"
22512
+ });
22513
+ }
22078
22514
  return choices;
22079
22515
  }
22080
22516
  function resolvePresetSelection(values) {
22081
- if (values.includes(EVERYTHING))
22517
+ const selected = values.filter((v) => v !== CUSTOM);
22518
+ if (selected.includes(EVERYTHING))
22082
22519
  return resolveEverything();
22083
- return [...new Set(values.flatMap((id) => resolvePreset(id)))];
22520
+ return [...new Set(selected.flatMap((id) => resolvePreset(id)))];
22521
+ }
22522
+ function setCustomPoliciesEnabled(scope, cwd, enabled) {
22523
+ if (enabled === undefined)
22524
+ return;
22525
+ const path3 = getConfigPathForScope(scope, cwd);
22526
+ let config = {};
22527
+ try {
22528
+ if (existsSync19(path3))
22529
+ config = JSON.parse(readFileSync16(path3, "utf8"));
22530
+ } catch {
22531
+ return;
22532
+ }
22533
+ if (enabled)
22534
+ delete config.customPoliciesEnabled;
22535
+ else
22536
+ config.customPoliciesEnabled = false;
22537
+ try {
22538
+ writeFileSync9(path3, JSON.stringify(config, null, 2) + `
22539
+ `, "utf8");
22540
+ } catch {}
22541
+ }
22542
+ function describeCustomPolicies(cwd) {
22543
+ const active = [];
22544
+ const warnings = [];
22545
+ const scopes = [];
22546
+ let fileCount = 0;
22547
+ const dirs = [
22548
+ { dir: resolve16(cwd, ".failproofai", "policies"), label: "project" },
22549
+ { dir: resolve16(homedir30(), ".failproofai", "policies"), label: "global" }
22550
+ ];
22551
+ for (const { dir, label } of dirs) {
22552
+ const found = discoverPolicyFiles(dir);
22553
+ if (found.length > 0) {
22554
+ active.push(`${found.length} file${found.length === 1 ? "" : "s"} (${label})`);
22555
+ scopes.push(label);
22556
+ fileCount += found.length;
22557
+ }
22558
+ for (const name of findSkippedPolicyFiles(dir)) {
22559
+ warnings.push(`! ${homeify(resolve16(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
22560
+ }
22561
+ }
22562
+ return { active, warnings, fileCount, scopes };
22084
22563
  }
22085
22564
  function reviewLines(state) {
22086
- const { scope, clis, policies, cwd } = state;
22565
+ const { scope, clis, policies, cwd, customEnabled } = state;
22087
22566
  const where = scope === "project" ? `This project (${homeify(cwd)})` : "Everywhere (global)";
22088
22567
  const lines = [];
22089
22568
  const assistantNames = clis.map((c3) => getIntegration(c3).displayName);
22090
22569
  lines.push(` Where : ${where}`);
22091
22570
  lines.push(` Assistants : ${assistantNames.length ? summarize(assistantNames, "assistants") : "(none)"}`);
22092
22571
  lines.push(` Policies : ${policies.length} enabled`);
22572
+ const custom = describeCustomPolicies(cwd);
22573
+ if (custom.active.length > 0) {
22574
+ lines.push(` Custom : ${custom.active.join(" · ")} ${customEnabled === false ? "— DISABLED, will not load" : DIM_NOTE}`);
22575
+ }
22576
+ for (const warning of custom.warnings)
22577
+ lines.push(` ${warning}`);
22093
22578
  lines.push("");
22094
22579
  lines.push(" This will update:");
22095
22580
  for (const cli of clis) {
@@ -22100,15 +22585,15 @@ function reviewLines(state) {
22100
22585
  return lines;
22101
22586
  }
22102
22587
  function firstRunMarkerPath() {
22103
- return resolve15(homedir28(), ".failproofai", ".launcher-configured");
22588
+ return resolve16(homedir30(), ".failproofai", ".launcher-configured");
22104
22589
  }
22105
22590
  function hasSeenLauncher() {
22106
- return existsSync18(firstRunMarkerPath());
22591
+ return existsSync19(firstRunMarkerPath());
22107
22592
  }
22108
22593
  function markLauncherSeen() {
22109
22594
  try {
22110
- mkdirSync8(resolve15(homedir28(), ".failproofai"), { recursive: true });
22111
- writeFileSync7(firstRunMarkerPath(), "1", "utf8");
22595
+ mkdirSync10(resolve16(homedir30(), ".failproofai"), { recursive: true });
22596
+ writeFileSync9(firstRunMarkerPath(), "1", "utf8");
22112
22597
  } catch {}
22113
22598
  }
22114
22599
  function anyHooksInstalledGlobally() {
@@ -22179,7 +22664,8 @@ Use the flag form instead, e.g.:
22179
22664
  {
22180
22665
  label: "Everything available",
22181
22666
  value: ALL_CLIS,
22182
- hint: `protect all ${INTEGRATION_TYPES.length} supported CLIs`
22667
+ hint: `protect all ${clisSupportingScope(scope).length} CLIs configurable here`,
22668
+ summaryExclude: true
22183
22669
  },
22184
22670
  ...buildAgentChoices(scope, cwd)
22185
22671
  ],
@@ -22191,10 +22677,14 @@ Use the flag form instead, e.g.:
22191
22677
  });
22192
22678
  if (clisSel === null)
22193
22679
  return cancel();
22194
- const clis = clisSel.includes(ALL_CLIS) ? [...INTEGRATION_TYPES] : clisSel.filter((v) => v !== ALL_CLIS);
22680
+ const supported = new Set(clisSupportingScope(scope));
22681
+ const clis = (clisSel.includes(ALL_CLIS) ? [...INTEGRATION_TYPES] : clisSel.filter((v) => v !== ALL_CLIS)).filter((id) => supported.has(id));
22682
+ const customEnabledBefore = readHooksConfig().customPoliciesEnabled !== false;
22683
+ const presetChoices = buildPresetChoices(cwd, customEnabledBefore);
22684
+ const hasCustomFiles = describeCustomPolicies(cwd).fileCount > 0;
22195
22685
  const presets = await multiSelect({
22196
22686
  message: "What should we guard against?",
22197
- choices: buildPresetChoices(),
22687
+ choices: presetChoices,
22198
22688
  minSelected: 1,
22199
22689
  summaryNoun: "bundles",
22200
22690
  hint: "space toggles · combine presets · ↵ confirm",
@@ -22204,9 +22694,10 @@ Use the flag form instead, e.g.:
22204
22694
  if (presets === null)
22205
22695
  return cancel();
22206
22696
  const policies = resolvePresetSelection(presets);
22697
+ const customEnabled = hasCustomFiles ? presets.includes(CUSTOM) : undefined;
22207
22698
  const decision = await selectOne({
22208
22699
  message: "Ready to apply?",
22209
- body: reviewLines({ scope, clis, policies, cwd }),
22700
+ body: reviewLines({ scope, clis, policies, cwd, customEnabled }),
22210
22701
  choices: [
22211
22702
  { label: "Yes, apply now", value: "apply", hint: "write the config" },
22212
22703
  { label: "Cancel", value: "cancel", hint: "quit, no changes" }
@@ -22224,13 +22715,15 @@ Use the flag form instead, e.g.:
22224
22715
  source: presets.join("+")
22225
22716
  });
22226
22717
  await installHooks2(policies, scope, cwd, false, "configure-wizard", undefined, false, clis, { replace: true, quiet: true });
22718
+ setCustomPoliciesEnabled(scope, cwd, customEnabled);
22227
22719
  await applied;
22228
22720
  markLauncherSeen();
22229
- const agentNames = clis.map((c3) => getIntegration(c3).displayName).join(", ");
22230
- outro(`Setup complete ${policies.length} policies guarding ${agentNames}.`, { ok: true }, stdout);
22721
+ const customNote = customEnabled === true ? " + your custom policies" : customEnabled === false ? " · custom policies DISABLED" : "";
22722
+ const assistants = `${clis.length} assistant${clis.length === 1 ? "" : "s"}`;
22723
+ outro(`Setup complete — ${policies.length} policies${customNote} · ${assistants}`, { ok: true }, stdout);
22231
22724
  return { applied: true, scope, clis, policies };
22232
22725
  }
22233
- var EVERYTHING = "__everything__", ALL_CLIS = "__all_clis__";
22726
+ var EVERYTHING = "__everything__", ALL_CLIS = "__all_clis__", CUSTOM = "__custom__", DIM_NOTE = "(auto-loaded)";
22234
22727
  var init_configure_wizard = __esm(() => {
22235
22728
  init_tui();
22236
22729
  init_integrations();
@@ -22238,6 +22731,7 @@ var init_configure_wizard = __esm(() => {
22238
22731
  init_manager2();
22239
22732
  init_hooks_config();
22240
22733
  init_policy_presets();
22734
+ init_custom_hooks_loader();
22241
22735
  init_hook_telemetry2();
22242
22736
  init_telemetry_id2();
22243
22737
  });
@@ -22248,8 +22742,8 @@ __export(exports_launch2, {
22248
22742
  launch: () => launch2
22249
22743
  });
22250
22744
  import { spawn as spawn3 } from "child_process";
22251
- import { realpathSync as realpathSync3, existsSync as existsSync19 } from "node:fs";
22252
- import { resolve as resolve16, dirname as dirname8 } from "node:path";
22745
+ import { realpathSync as realpathSync3, existsSync as existsSync20 } from "node:fs";
22746
+ import { resolve as resolve17, dirname as dirname8 } from "node:path";
22253
22747
  import { fileURLToPath as fileURLToPath3 } from "node:url";
22254
22748
  import { createInterface as createInterface3 } from "node:readline";
22255
22749
  function launch2(mode) {
@@ -22264,9 +22758,9 @@ function launch2(mode) {
22264
22758
  process.env.PORT = port;
22265
22759
  process.env.HOSTNAME = "0.0.0.0";
22266
22760
  cmd = "node";
22267
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve16(dirname8(realpathSync3(fileURLToPath3(import.meta.url))), "..");
22268
- const serverJsPath = resolve16(packageRoot, ".next/standalone/server.js");
22269
- if (!existsSync19(serverJsPath)) {
22761
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve17(dirname8(realpathSync3(fileURLToPath3(import.meta.url))), "..");
22762
+ const serverJsPath = resolve17(packageRoot, ".next/standalone/server.js");
22763
+ if (!existsSync20(serverJsPath)) {
22270
22764
  let shadowMessage = null;
22271
22765
  try {
22272
22766
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -22360,17 +22854,17 @@ var init_cli_error2 = __esm(() => {
22360
22854
 
22361
22855
  // bin/failproofai.mjs
22362
22856
  import { realpathSync as realpathSync4 } from "fs";
22363
- import { dirname as dirname9, resolve as resolve17 } from "path";
22857
+ import { dirname as dirname9, resolve as resolve18 } from "path";
22364
22858
  import { fileURLToPath as fileURLToPath4 } from "url";
22365
22859
  // package.json
22366
- var version = "0.0.14-beta.0";
22860
+ var version = "0.0.14-beta.2";
22367
22861
 
22368
22862
  // bin/failproofai.mjs
22369
22863
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
22370
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve17(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..");
22864
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve18(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..");
22371
22865
  }
22372
22866
  if (!process.env.FAILPROOFAI_DIST_PATH) {
22373
- process.env.FAILPROOFAI_DIST_PATH = resolve17(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
22867
+ process.env.FAILPROOFAI_DIST_PATH = resolve18(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
22374
22868
  }
22375
22869
  var args = process.argv.slice(2);
22376
22870
  if (args[0] === "p")
@@ -22423,6 +22917,10 @@ if (hookIdx >= 0) {
22423
22917
  }
22424
22918
  }
22425
22919
  async function runCli() {
22920
+ try {
22921
+ const { maybeReportInstall: maybeReportInstall2 } = await Promise.resolve().then(() => (init_install_check(), exports_install_check));
22922
+ await maybeReportInstall2(version);
22923
+ } catch {}
22426
22924
  const SUBCOMMANDS2 = ["policies", "policy", "auth", "audit", "config"];
22427
22925
  if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS2.includes(args[0])) {
22428
22926
  const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
@@ -22508,7 +23006,7 @@ EXAMPLES
22508
23006
  LINKS
22509
23007
  \u2B50 Star us: https://github.com/failproofai/failproofai
22510
23008
  \uD83D\uDCD6 Docs: https://docs.befailproof.ai/introduction
22511
- \uD83D\uDCAC Discord: https://discord.gg/2zjBZP7yQJ
23009
+ \uD83D\uDCAC Discord: https://discord.befailproof.ai/
22512
23010
  `.trimStart());
22513
23011
  process.exit(0);
22514
23012
  }