failproofai 0.0.14 → 0.0.15-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +6 -6
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +2 -2
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  8. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  13. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  15. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  18. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  20. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  26. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  27. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  29. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/invite/route.js +1 -1
  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 +1 -1
  33. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/logout/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/status/route.js +1 -1
  43. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  45. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  47. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  49. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/index.html +1 -1
  52. package/.next/standalone/.next/server/app/index.rsc +15 -15
  53. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  54. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  55. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  56. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  57. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  59. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/page.js +2 -2
  61. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  64. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +20 -8
  65. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  66. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  69. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -4
  71. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  75. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  76. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -4
  77. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  80. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  81. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  82. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0hv08mx._.js → [root-of-the-server]__0-dh5jy._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0obaznk._.js → [root-of-the-server]__0h3lfmp._.js} +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0xvsnvt._.js → [root-of-the-server]__0l4vuqm._.js} +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sb_5m8._.js +4 -4
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xuaoik._.js +6 -4
  94. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1hkdkmz._.js → [root-of-the-server]__10oa-v9._.js} +1 -1
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1i9rknc._.js → [root-of-the-server]__18d-9oi._.js} +1 -1
  97. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1is3glr._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +6 -6
  102. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0hdsupo._.js → [root-of-the-server]__0114ewg._.js} +3 -3
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01od0m6._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +4 -0
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +4 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +4 -0
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lpvwqm._.js +21 -0
  111. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ynf7tx._.js → [root-of-the-server]__0qucxyj._.js} +3 -3
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +4 -0
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +3 -0
  114. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1s2b6h0._.js → [root-of-the-server]__0x3anhr._.js} +2 -2
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0y4wf1u._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +10 -10
  117. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00cz9as._.js → [root-of-the-server]__1apb7k_._.js} +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +4 -0
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1py795m._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +4 -0
  123. package/.next/standalone/.next/server/chunks/ssr/_077y5np._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/_1-3unkg._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr._.js +3 -0
  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 +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/lib_0xkhw_v._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/lib_hermes-projects_ts_05b8-it._.js +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0gkzjaa.js → node_modules_html-to-image_es_index_1wnt2ya.js} +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  136. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  137. package/.next/standalone/.next/server/pages/404.html +1 -1
  138. package/.next/standalone/.next/server/pages/500.html +1 -1
  139. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  140. package/.next/standalone/.next/server/server-reference-manifest.json +22 -10
  141. package/.next/standalone/.next/static/chunks/{29o4j32z643_a.js → 05h7pau8d3rpj.js} +1 -1
  142. package/.next/standalone/.next/static/chunks/0oos4ntyvkmjl.js +1 -0
  143. package/.next/standalone/.next/static/chunks/0tuwf_7uhfn2y.js +6 -0
  144. package/.next/standalone/.next/static/chunks/1hdhteui932bq.js +1 -0
  145. package/.next/standalone/.next/static/chunks/1l6q2-8m0gk19.js +1 -0
  146. package/.next/standalone/.next/static/chunks/2geqsiz0n8e3w.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{3-ogr79xqbw0h.js → 2mazwb1qmur1p.js} +1 -1
  148. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +2 -0
  149. package/.next/standalone/.next/static/chunks/{1ua5armwfph8o.js → 31fmsp1soxi07.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/{3s8aaig91937v.js → 37-so-zy208k7.js} +1 -1
  151. package/.next/standalone/.next/static/chunks/3a_o5v_sl858j.js +1 -0
  152. package/.next/standalone/.next/static/chunks/{02jcotghcxrbu.js → 3kmwfieo15mr2.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/{2uh_4k4m296z5.js → 3p66lo2y-ezfr.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{06z_ex1ts3n4s.js → 412ajx6jkb5_i.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{turbopack-10hj8t5pf33tu.js → turbopack-1lsv92x7klwrn.js} +1 -1
  156. package/.next/standalone/app/actions/get-hooks-config.ts +125 -8
  157. package/.next/standalone/app/actions/update-hooks-config.ts +38 -1
  158. package/.next/standalone/app/components/project-list.tsx +229 -42
  159. package/.next/standalone/app/policies/hooks-client.tsx +149 -27
  160. package/.next/standalone/app/project/[name]/page.tsx +12 -2
  161. package/.next/standalone/integration-suite/README.md +43 -1
  162. package/.next/standalone/integration-suite/ci-entrypoint.sh +21 -6
  163. package/.next/standalone/integration-suite/install-clis.sh +58 -13
  164. package/.next/standalone/integration-suite/probe-cli.sh +51 -11
  165. package/.next/standalone/integration-suite/report.js +52 -6
  166. package/.next/standalone/integration-suite/run.sh +55 -3
  167. package/.next/standalone/lib/hermes-profiles.ts +156 -0
  168. package/.next/standalone/lib/hermes-projects.ts +91 -25
  169. package/.next/standalone/lib/hermes-sessions.ts +65 -10
  170. package/.next/standalone/lib/openclaw-projects.ts +111 -18
  171. package/.next/standalone/lib/openclaw-sessions.ts +20 -0
  172. package/.next/standalone/lib/paths.ts +36 -0
  173. package/.next/standalone/lib/project-tree.ts +193 -0
  174. package/.next/standalone/lib/projects.ts +22 -0
  175. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  176. package/.next/standalone/node_modules/next/dist/build/next-config-ts/transpile-config.js +3 -114
  177. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  178. package/.next/standalone/node_modules/next/dist/cli/next-test.js +2 -1
  179. package/.next/standalone/node_modules/next/dist/compiled/http-proxy/index.js +5 -5
  180. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +11 -10
  181. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +11 -10
  182. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +2 -2
  183. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +9 -9
  184. package/.next/standalone/node_modules/next/dist/lib/install-dependencies.js +1 -1
  185. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  186. package/.next/standalone/node_modules/next/dist/lib/typescript/loadTsConfig.js +181 -0
  187. package/.next/standalone/node_modules/next/dist/lib/typescript/missingDependencyError.js +1 -1
  188. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeCheckCli.js +53 -0
  189. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +256 -0
  190. package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +97 -56
  191. package/.next/standalone/node_modules/next/dist/server/app-render/action-handler.js +129 -34
  192. package/.next/standalone/node_modules/next/dist/server/app-render/manifests-singleton.js +52 -4
  193. package/.next/standalone/node_modules/next/dist/server/config-schema.js +1 -1
  194. package/.next/standalone/node_modules/next/dist/server/config-shared.js +1 -2
  195. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  196. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  197. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  198. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +8 -27
  199. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  200. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/index.js +90 -53
  201. package/.next/standalone/node_modules/next/dist/server/lib/patch-fetch.js +15 -2
  202. package/.next/standalone/node_modules/next/dist/server/lib/router-utils/proxy-request.js +4 -0
  203. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  204. package/.next/standalone/node_modules/next/dist/server/web/spec-extension/unstable-cache.js +1 -1
  205. package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  206. package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/prepare-destination.js +2 -1
  207. package/.next/standalone/node_modules/next/dist/shared/lib/server-reference-info.js +12 -0
  208. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  209. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  210. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  211. package/.next/standalone/node_modules/next/package.json +10 -10
  212. package/.next/standalone/package.json +7 -6
  213. package/.next/standalone/pi-extension/index.ts +38 -5
  214. package/.next/standalone/server.js +1 -1
  215. package/bin/failproofai.mjs +14 -13
  216. package/dist/cli.mjs +789 -399
  217. package/lib/hermes-profiles.ts +156 -0
  218. package/lib/hermes-projects.ts +91 -25
  219. package/lib/hermes-sessions.ts +65 -10
  220. package/lib/openclaw-projects.ts +111 -18
  221. package/lib/openclaw-sessions.ts +20 -0
  222. package/lib/paths.ts +36 -0
  223. package/lib/project-tree.ts +193 -0
  224. package/lib/projects.ts +22 -0
  225. package/package.json +7 -6
  226. package/pi-extension/index.ts +38 -5
  227. package/scripts/launch.ts +7 -0
  228. package/src/audit/cli-adapters/hermes.ts +6 -3
  229. package/src/audit/cli-adapters/openclaw.ts +17 -13
  230. package/src/hooks/configure-wizard.ts +24 -2
  231. package/src/hooks/custom-hooks-loader.ts +90 -13
  232. package/src/hooks/enforcement-capability.ts +266 -0
  233. package/src/hooks/handler.ts +20 -3
  234. package/src/hooks/hook-activity-store.ts +14 -0
  235. package/src/hooks/hooks-config.ts +90 -6
  236. package/src/hooks/integrations.ts +123 -19
  237. package/src/hooks/manager.ts +206 -75
  238. package/src/hooks/policy-evaluator.ts +59 -7
  239. package/src/hooks/policy-types.ts +31 -0
  240. package/src/hooks/types.ts +50 -2
  241. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d9qen3._.js +0 -3
  243. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +0 -3
  244. package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +0 -4
  246. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +0 -3
  247. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +0 -4
  248. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__042d3cd._.js +0 -3
  249. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09q1168._.js +0 -3
  250. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +0 -4
  251. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +0 -4
  252. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zf8xlq._.js +0 -3
  253. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +0 -4
  254. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1d8omgc._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1fax1sl._.js +0 -19
  256. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +0 -4
  257. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/ssr/_0_9_ytf._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/_1p0-leb._.js +0 -3
  261. package/.next/standalone/.next/static/chunks/0o0ut0ugd0_-s.js +0 -1
  262. package/.next/standalone/.next/static/chunks/0xl97x-874k-u.js +0 -1
  263. package/.next/standalone/.next/static/chunks/19d7hqx8udopz.js +0 -1
  264. package/.next/standalone/.next/static/chunks/36j05kb2x323i.css +0 -2
  265. package/.next/standalone/.next/static/chunks/370sj2lgkw5ey.js +0 -1
  266. package/.next/standalone/.next/static/chunks/3w7g0n0iwwnbs.js +0 -1
  267. package/.next/standalone/.next/static/chunks/42oeva1-w0z2d.js +0 -6
  268. /package/.next/standalone/.next/static/{WQRKgSfFekLcC_pvyzp4S → nhn6Tt0--xZKdgVGIqR6E}/_buildManifest.js +0 -0
  269. /package/.next/standalone/.next/static/{WQRKgSfFekLcC_pvyzp4S → nhn6Tt0--xZKdgVGIqR6E}/_clientMiddlewareManifest.js +0 -0
  270. /package/.next/standalone/.next/static/{WQRKgSfFekLcC_pvyzp4S → nhn6Tt0--xZKdgVGIqR6E}/_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";
22
+ var version2 = "0.0.15-beta.1";
23
23
  var init_package = () => {};
24
24
 
25
25
  // src/posthog-key.ts
@@ -156,7 +156,7 @@ var init_telemetry_id = __esm(() => {
156
156
  });
157
157
 
158
158
  // src/hooks/types.ts
159
- var HOOK_SCOPES, INTEGRATION_TYPES, CODEX_HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_HOOK_SCOPES, HERMES_HOOK_EVENT_TYPES, HERMES_EVENT_MAP, COPILOT_HOOK_SCOPES, COPILOT_HOOK_EVENT_TYPES, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_HOOK_SCOPES, CURSOR_HOOK_EVENT_TYPES, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_HOOK_SCOPES, OPENCODE_HOOK_EVENT_TYPES, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_HOOK_SCOPES, PI_HOOK_EVENT_TYPES, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_HOOK_SCOPES, OPENCLAW_HOOK_EVENT_TYPES, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_HOOK_SCOPES, FACTORY_HOOK_EVENT_TYPES, FACTORY_TOOL_MAP, DEVIN_HOOK_SCOPES, DEVIN_HOOK_EVENT_TYPES, DEVIN_TOOL_MAP, ANTIGRAVITY_HOOK_SCOPES, ANTIGRAVITY_HOOK_EVENT_TYPES, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_HOOK_SCOPES, GOOSE_HOOK_EVENT_TYPES, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
159
+ var HOOK_SCOPES, INTEGRATION_TYPES, CODEX_HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_HOOK_SCOPES, HERMES_HOOK_EVENT_TYPES, HERMES_EVENT_MAP, COPILOT_HOOK_SCOPES, COPILOT_HOOK_EVENT_TYPES, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_HOOK_SCOPES, CURSOR_HOOK_EVENT_TYPES, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_HOOK_SCOPES, OPENCODE_HOOK_EVENT_TYPES, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_HOOK_SCOPES, PI_HOOK_EVENT_TYPES, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_HOOK_SCOPES, OPENCLAW_HOOK_EVENT_TYPES, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_HOOK_SCOPES, FACTORY_HOOK_EVENT_TYPES, FACTORY_TOOL_MAP, DEVIN_HOOK_SCOPES, DEVIN_HOOK_EVENT_TYPES, DEVIN_TOOL_MAP, ANTIGRAVITY_HOOK_SCOPES, ANTIGRAVITY_HOOK_EVENT_TYPES, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_HOOK_SCOPES, GOOSE_HOOK_EVENT_TYPES, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES, FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
160
160
  var init_types = __esm(() => {
161
161
  HOOK_SCOPES = ["user", "project", "local"];
162
162
  INTEGRATION_TYPES = ["claude", "codex", "copilot", "cursor", "opencode", "pi", "hermes", "openclaw", "factory", "devin", "antigravity", "goose"];
@@ -516,6 +516,7 @@ var init_types = __esm(() => {
516
516
  "PostToolBatch",
517
517
  "Setup"
518
518
  ];
519
+ CLAUDE_INSTALL_EVENT_TYPES = HOOK_EVENT_TYPES.filter((e) => e !== "WorktreeCreate");
519
520
  });
520
521
 
521
522
  // src/hooks/tool-name-canonicalize.ts
@@ -668,6 +669,9 @@ var init_hook_logger = __esm(() => {
668
669
  import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
669
670
  import { resolve, dirname } from "node:path";
670
671
  import { homedir as homedir2 } from "node:os";
672
+ function configuredCustomPolicyPaths(config) {
673
+ return config.customPoliciesPaths ?? (config.customPoliciesPath ? [config.customPoliciesPath] : []);
674
+ }
671
675
  function readConfigAt(path2) {
672
676
  if (!existsSync2(path2))
673
677
  return {};
@@ -718,12 +722,20 @@ function readMergedHooksConfig(cwd) {
718
722
  }
719
723
  }
720
724
  }
721
- const customPoliciesPath = project.customPoliciesPath ?? local.customPoliciesPath ?? global_.customPoliciesPath;
725
+ const customPoliciesPaths = [project, local, global_].map((scope) => scope.customPoliciesPaths !== undefined ? scope.customPoliciesPaths : scope.customPoliciesPath !== undefined ? [scope.customPoliciesPath] : undefined).find((paths) => paths !== undefined);
726
+ const disabledCustomPolicies = new Set([
727
+ ...project.disabledCustomPolicies ?? [],
728
+ ...local.disabledCustomPolicies ?? [],
729
+ ...global_.disabledCustomPolicies ?? []
730
+ ]);
731
+ const customPoliciesEnabled = project.customPoliciesEnabled ?? local.customPoliciesEnabled ?? global_.customPoliciesEnabled;
722
732
  const llm = project.llm ?? local.llm ?? global_.llm;
723
733
  return {
724
734
  enabledPolicies: [...enabledSet],
725
735
  ...Object.keys(mergedParams).length > 0 ? { policyParams: mergedParams } : {},
726
- ...customPoliciesPath !== undefined ? { customPoliciesPath } : {},
736
+ ...customPoliciesPaths !== undefined ? { customPoliciesPaths } : {},
737
+ ...disabledCustomPolicies.size ? { disabledCustomPolicies: [...disabledCustomPolicies] } : {},
738
+ ...customPoliciesEnabled !== undefined ? { customPoliciesEnabled } : {},
727
739
  ...llm !== undefined ? { llm } : {}
728
740
  };
729
741
  }
@@ -775,6 +787,31 @@ function writeScopedHooksConfig(config, scope, cwd) {
775
787
  writeFileSync(configPath, JSON.stringify(config, null, 2) + `
776
788
  `, "utf8");
777
789
  }
790
+ function syncConventionPolicies(entries, scope, cwd) {
791
+ const configPath = getConfigPathForScope(scope, cwd);
792
+ if (!existsSync2(configPath) && entries.length === 0)
793
+ return false;
794
+ if (existsSync2(configPath)) {
795
+ try {
796
+ JSON.parse(readFileSync(configPath, "utf8"));
797
+ } catch {
798
+ hookLogWarn(`not recording convention policies: ${configPath} is not valid JSON — ` + `fix the syntax error and re-run. Nothing was written.`);
799
+ return false;
800
+ }
801
+ }
802
+ const config = readScopedHooksConfig(scope, cwd);
803
+ const sorted = [...entries].sort((a, b) => a.file.localeCompare(b.file));
804
+ const next = { ...config };
805
+ if (sorted.length === 0)
806
+ delete next.conventionPolicies;
807
+ else
808
+ next.conventionPolicies = sorted;
809
+ if (JSON.stringify(next.conventionPolicies ?? null) === JSON.stringify(config.conventionPolicies ?? null)) {
810
+ return false;
811
+ }
812
+ writeScopedHooksConfig(next, scope, cwd);
813
+ return true;
814
+ }
778
815
  var init_hooks_config = __esm(() => {
779
816
  init_hook_logger();
780
817
  });
@@ -2752,6 +2789,16 @@ You MUST complete the above action NOW. Do NOT ask the user for confirmation —
2752
2789
  decision: "deny"
2753
2790
  };
2754
2791
  }
2792
+ if (eventType === "UserPromptSubmit") {
2793
+ return {
2794
+ exitCode: 0,
2795
+ stdout: JSON.stringify({ continue: false, user_message: blockedMessage }),
2796
+ stderr: "",
2797
+ policyName: policy.name,
2798
+ reason,
2799
+ decision: "deny"
2800
+ };
2801
+ }
2755
2802
  const response = {
2756
2803
  permission: "deny",
2757
2804
  user_message: blockedMessage,
@@ -2928,6 +2975,28 @@ You MUST complete the above action NOW. Do NOT ask the user for confirmation —
2928
2975
  decision: "deny"
2929
2976
  };
2930
2977
  }
2978
+ if (session?.cli === "copilot") {
2979
+ if (eventType === "UserPromptSubmit") {
2980
+ return {
2981
+ exitCode: 0,
2982
+ stdout: JSON.stringify({ decision: "block", reason: blockedMessage }),
2983
+ stderr: "",
2984
+ policyName: policy.name,
2985
+ reason,
2986
+ decision: "deny"
2987
+ };
2988
+ }
2989
+ if (eventType === "PermissionRequest") {
2990
+ return {
2991
+ exitCode: 0,
2992
+ stdout: JSON.stringify({ behavior: "deny", message: blockedMessage }),
2993
+ stderr: "",
2994
+ policyName: policy.name,
2995
+ reason,
2996
+ decision: "deny"
2997
+ };
2998
+ }
2999
+ }
2931
3000
  if (eventType === "PermissionRequest") {
2932
3001
  const response = {
2933
3002
  hookSpecificOutput: {
@@ -3528,7 +3597,7 @@ async function loadSingleFile(absPath, opts) {
3528
3597
  const distUrl = distIndex ? pathToFileURL2(distIndex).href : null;
3529
3598
  tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex);
3530
3599
  const entryTmp = absPath + TMP_SUFFIX;
3531
- const fileUrl = pathToFileURL2(entryTmp).href;
3600
+ const fileUrl = `${pathToFileURL2(entryTmp).href}?v=${++loadSequence}`;
3532
3601
  await import(fileUrl);
3533
3602
  } catch (err) {
3534
3603
  const msg = err instanceof Error ? err.message : String(err);
@@ -3561,6 +3630,12 @@ async function loadCustomHooks(customPoliciesPath, opts) {
3561
3630
  await loadSingleFile(absPath, opts);
3562
3631
  return getCustomHooks();
3563
3632
  }
3633
+ function customPolicyId(file, name) {
3634
+ return `custom:${file}:${name}`;
3635
+ }
3636
+ function conventionPolicyId(scope, file, name) {
3637
+ return `convention:${scope}:${file}:${name}`;
3638
+ }
3564
3639
  function warnSkippedPolicyFiles(dir, scope) {
3565
3640
  const skipped = findSkippedPolicyFiles(dir);
3566
3641
  if (skipped.length === 0)
@@ -3568,28 +3643,40 @@ function warnSkippedPolicyFiles(dir, scope) {
3568
3643
  const suggest = (n) => n.replace(/\.(js|mjs|ts)$/, "-policies.$1");
3569
3644
  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
3645
  }
3571
- async function loadAllCustomHooks(customPoliciesPath, opts) {
3646
+ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3572
3647
  clearCustomHooks();
3573
3648
  const conventionSources = [];
3574
3649
  const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
3575
3650
  const conventionEnabled = opts?.customPoliciesEnabled !== false;
3576
- if (customPoliciesPath) {
3577
- const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(projectRoot, customPoliciesPath);
3651
+ const loadedPaths = new Set;
3652
+ for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
3653
+ const absPath = resolve4(projectRoot, customPoliciesPath);
3578
3654
  if (existsSync3(absPath)) {
3579
- await loadSingleFile(absPath);
3655
+ if (!loadedPaths.has(absPath)) {
3656
+ loadedPaths.add(absPath);
3657
+ const hooksBefore = getCustomHooks().length;
3658
+ await loadSingleFile(absPath);
3659
+ for (const hook of getCustomHooks().slice(hooksBefore)) {
3660
+ hook.__policyId = customPolicyId(absPath, hook.name);
3661
+ }
3662
+ }
3580
3663
  } else {
3581
- hookLogWarn(`customPoliciesPath not found: ${absPath}`);
3664
+ hookLogWarn(`custom policy path not found: ${absPath}`);
3582
3665
  }
3583
3666
  }
3584
3667
  const hooksBeforeConvention = getCustomHooks().length;
3585
3668
  const projectDir = resolve4(projectRoot, ".failproofai", "policies");
3586
3669
  if (conventionEnabled)
3587
3670
  warnSkippedPolicyFiles(projectDir, "project");
3588
- const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir) : [];
3671
+ const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f)) : [];
3589
3672
  for (const file of projectFiles) {
3673
+ loadedPaths.add(file);
3590
3674
  const hooksBefore = getCustomHooks().length;
3591
3675
  await loadSingleFile(file, { conventionScope: "project" });
3592
3676
  const newHooks = getCustomHooks().slice(hooksBefore);
3677
+ for (const hook of newHooks) {
3678
+ hook.__policyId = conventionPolicyId("project", basename(file), hook.name);
3679
+ }
3593
3680
  if (newHooks.length > 0) {
3594
3681
  conventionSources.push({
3595
3682
  scope: "project",
@@ -3599,13 +3686,17 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
3599
3686
  }
3600
3687
  }
3601
3688
  const userDir = resolve4(homedir4(), ".failproofai", "policies");
3602
- if (conventionEnabled)
3689
+ if (conventionEnabled && userDir !== projectDir)
3603
3690
  warnSkippedPolicyFiles(userDir, "user");
3604
- const userFiles = conventionEnabled ? discoverPolicyFiles(userDir) : [];
3691
+ const userFiles = conventionEnabled && userDir !== projectDir ? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f)) : [];
3605
3692
  for (const file of userFiles) {
3693
+ loadedPaths.add(file);
3606
3694
  const hooksBefore = getCustomHooks().length;
3607
3695
  await loadSingleFile(file, { conventionScope: "user" });
3608
3696
  const newHooks = getCustomHooks().slice(hooksBefore);
3697
+ for (const hook of newHooks) {
3698
+ hook.__policyId = conventionPolicyId("user", basename(file), hook.name);
3699
+ }
3609
3700
  if (newHooks.length > 0) {
3610
3701
  conventionSources.push({
3611
3702
  scope: "user",
@@ -3638,7 +3729,7 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
3638
3729
  }
3639
3730
  return { hooks: allHooks, conventionSources };
3640
3731
  }
3641
- var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, LOADABLE_EXT_RE;
3732
+ var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, LOADABLE_EXT_RE;
3642
3733
  var init_custom_hooks_loader = __esm(() => {
3643
3734
  init_hook_logger();
3644
3735
  init_custom_hooks_registry();
@@ -4856,6 +4947,65 @@ async function openSqliteReadonly(dbPath) {
4856
4947
  var sqlPromise = null;
4857
4948
  var init_sqlite_reader = () => {};
4858
4949
 
4950
+ // lib/hermes-profiles.ts
4951
+ import { readdirSync as readdirSync3 } from "node:fs";
4952
+ import { homedir as homedir11 } from "node:os";
4953
+ import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve5 } from "node:path";
4954
+ function hermesRoot() {
4955
+ const env = (process.env.HERMES_HOME || "").trim();
4956
+ if (env) {
4957
+ const home = resolve5(env);
4958
+ const parent = dirname3(home);
4959
+ if (basename2(parent) === PROFILES_DIR)
4960
+ return dirname3(parent);
4961
+ return home;
4962
+ }
4963
+ return join9(homedir11(), ".hermes");
4964
+ }
4965
+ function listHermesProfiles() {
4966
+ const root = hermesRoot();
4967
+ const out = [{ name: HERMES_DEFAULT_PROFILE, home: root }];
4968
+ const seen = new Set([HERMES_DEFAULT_PROFILE]);
4969
+ let names;
4970
+ try {
4971
+ names = readdirSync3(join9(root, PROFILES_DIR), { withFileTypes: true }).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".")).map((e) => e.name).sort();
4972
+ } catch {
4973
+ return out;
4974
+ }
4975
+ for (const name of names) {
4976
+ if (seen.has(name))
4977
+ continue;
4978
+ seen.add(name);
4979
+ out.push({ name, home: join9(root, PROFILES_DIR, name) });
4980
+ }
4981
+ return out;
4982
+ }
4983
+ function hermesProjectName(profile, source) {
4984
+ return `hermes-${profile}-${source}`;
4985
+ }
4986
+ function hermesProjectPath(profile, source) {
4987
+ return `hermes:${profile}:${source}`;
4988
+ }
4989
+ function hermesProjectNameCandidates(name) {
4990
+ if (!name.startsWith("hermes-"))
4991
+ return [];
4992
+ const rest = name.slice("hermes-".length);
4993
+ if (!rest)
4994
+ return [];
4995
+ const out = [];
4996
+ const profiles = listHermesProfiles().map((p) => p.name).sort((a, b) => b.length - a.length);
4997
+ for (const profile of profiles) {
4998
+ const prefix = `${profile}-`;
4999
+ if (rest.startsWith(prefix) && rest.length > prefix.length) {
5000
+ out.push({ profile, source: rest.slice(prefix.length) });
5001
+ }
5002
+ }
5003
+ out.push({ profile: HERMES_DEFAULT_PROFILE, source: rest });
5004
+ return out;
5005
+ }
5006
+ var PROFILES_DIR = "profiles", HERMES_DEFAULT_PROFILE = "default";
5007
+ var init_hermes_profiles = () => {};
5008
+
4859
5009
  // lib/format-duration.ts
4860
5010
  function formatDuration(ms) {
4861
5011
  if (ms < 1000)
@@ -4877,10 +5027,15 @@ function formatDuration(ms) {
4877
5027
  }
4878
5028
 
4879
5029
  // lib/hermes-sessions.ts
4880
- import { homedir as homedir11 } from "node:os";
4881
- import { join as join9 } from "node:path";
4882
- function hermesDbPath() {
4883
- return process.env.HERMES_DB_PATH || join9(homedir11(), ".hermes", "state.db");
5030
+ import { join as join10 } from "node:path";
5031
+ function hermesDbPaths() {
5032
+ const override = process.env.HERMES_DB_PATH;
5033
+ if (override)
5034
+ return [{ profile: HERMES_DEFAULT_PROFILE, dbPath: override }];
5035
+ return listHermesProfiles().map((p) => ({
5036
+ profile: p.name,
5037
+ dbPath: join10(p.home, "state.db")
5038
+ }));
4884
5039
  }
4885
5040
  function epochToMs(value) {
4886
5041
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -5011,7 +5166,15 @@ function hermesRowsToLogEntries(rows, source = "session") {
5011
5166
  async function getHermesSessionLog(sessionId) {
5012
5167
  if (!sessionId || !/^[A-Za-z0-9_-]+$/.test(sessionId))
5013
5168
  return null;
5014
- const db = await openSqliteReadonly(hermesDbPath());
5169
+ for (const { profile, dbPath } of hermesDbPaths()) {
5170
+ const found = await loadFromDb(dbPath, profile, sessionId);
5171
+ if (found)
5172
+ return found;
5173
+ }
5174
+ return null;
5175
+ }
5176
+ async function loadFromDb(dbPath, profile, sessionId) {
5177
+ const db = await openSqliteReadonly(dbPath);
5015
5178
  if (!db)
5016
5179
  return null;
5017
5180
  try {
@@ -5021,12 +5184,13 @@ async function getHermesSessionLog(sessionId) {
5021
5184
  const { source, cwd: realCwd } = sessionRows[0];
5022
5185
  const msgRows = db.query("SELECT id, role, content, tool_call_id, tool_calls, tool_name, timestamp " + "FROM messages WHERE session_id = ? ORDER BY timestamp ASC", [sessionId]);
5023
5186
  const entries = hermesRowsToLogEntries(msgRows, "session");
5024
- const cwd = realCwd && realCwd.length > 0 ? realCwd : source ? `hermes:${source}` : undefined;
5187
+ const cwd = realCwd && realCwd.length > 0 ? realCwd : source ? hermesProjectPath(profile, source) : undefined;
5025
5188
  return {
5026
5189
  entries,
5027
5190
  rawLines: msgRows,
5028
5191
  cwd,
5029
- filePath: `hermes://${sessionId}`
5192
+ filePath: `hermes://${sessionId}`,
5193
+ profile
5030
5194
  };
5031
5195
  } catch {
5032
5196
  return null;
@@ -5037,6 +5201,7 @@ async function getHermesSessionLog(sessionId) {
5037
5201
  var getCachedHermesSessionLog;
5038
5202
  var init_hermes_sessions = __esm(() => {
5039
5203
  init_sqlite_reader();
5204
+ init_hermes_profiles();
5040
5205
  init_log_entries();
5041
5206
  getCachedHermesSessionLog = runtimeCache((sessionId) => getHermesSessionLog(sessionId), 2, { maxSize: 50 });
5042
5207
  });
@@ -5052,13 +5217,22 @@ __export(exports_hermes_projects, {
5052
5217
  getCachedHermesProjects: () => getCachedHermesProjects
5053
5218
  });
5054
5219
  async function getHermesSessions() {
5055
- const db = await openSqliteReadonly(hermesDbPath());
5220
+ const out = [];
5221
+ for (const { profile, dbPath } of hermesDbPaths()) {
5222
+ out.push(...await readSessionsFromDb(dbPath, profile));
5223
+ }
5224
+ out.sort((a, b) => b.mtimeMs - a.mtimeMs);
5225
+ return out;
5226
+ }
5227
+ async function readSessionsFromDb(dbPath, profile) {
5228
+ const db = await openSqliteReadonly(dbPath);
5056
5229
  if (!db)
5057
5230
  return [];
5058
5231
  try {
5059
5232
  const rows = db.query("SELECT s.id, s.source, s.cwd, s.title, s.user_id, s.chat_id, s.chat_type, " + "s.started_at, s.ended_at, s.message_count, lm.last_activity " + "FROM sessions s " + "LEFT JOIN (SELECT session_id, MAX(timestamp) AS last_activity FROM messages GROUP BY session_id) lm " + "ON lm.session_id = s.id " + "ORDER BY lm.last_activity DESC");
5060
5233
  return rows.map((r) => ({
5061
5234
  sessionId: r.id,
5235
+ profile,
5062
5236
  source: r.source ?? undefined,
5063
5237
  cwd: r.cwd ?? undefined,
5064
5238
  title: r.title ?? undefined,
@@ -5077,36 +5251,58 @@ async function getHermesSessions() {
5077
5251
  }
5078
5252
  async function getHermesProjects() {
5079
5253
  const sessions = await getHermesSessions();
5080
- const latestBySource = new Map;
5254
+ const groups = new Map;
5081
5255
  for (const s of sessions) {
5082
5256
  if (s.messageCount <= 0 && !s.hasMessages)
5083
5257
  continue;
5084
- const src = s.source ?? "unknown";
5085
- latestBySource.set(src, Math.max(latestBySource.get(src) ?? 0, s.mtimeMs));
5258
+ const profile = s.profile || HERMES_DEFAULT_PROFILE;
5259
+ const source = s.source ?? "unknown";
5260
+ const key = JSON.stringify([profile, source]);
5261
+ const prev = groups.get(key);
5262
+ if (prev) {
5263
+ prev.latest = Math.max(prev.latest, s.mtimeMs);
5264
+ prev.count += 1;
5265
+ } else {
5266
+ groups.set(key, { profile, source, latest: s.mtimeMs, count: 1 });
5267
+ }
5086
5268
  }
5087
5269
  const out = [];
5088
- for (const [src, latest] of latestBySource) {
5270
+ for (const { profile, source, latest, count } of groups.values()) {
5089
5271
  const lastModified = new Date(latest);
5090
5272
  out.push({
5091
- name: `hermes-${src}`,
5092
- path: `hermes:${src}`,
5273
+ name: hermesProjectName(profile, source),
5274
+ path: hermesProjectPath(profile, source),
5093
5275
  isDirectory: true,
5094
5276
  lastModified,
5095
5277
  lastModifiedFormatted: formatDate(lastModified),
5096
- cli: ["hermes"]
5278
+ cli: ["hermes"],
5279
+ sessionCount: count
5097
5280
  });
5098
5281
  }
5099
5282
  out.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
5100
5283
  return out;
5101
5284
  }
5102
5285
  async function getHermesSessionsByEncodedName(name) {
5103
- if (!name.startsWith("hermes-"))
5286
+ const candidates = hermesProjectNameCandidates(name);
5287
+ if (candidates.length === 0)
5104
5288
  return { cwd: null, sessions: [] };
5105
- const source = name.slice("hermes-".length);
5106
5289
  const sessions = await getHermesSessions();
5107
- const matched = sessions.filter((s) => (s.messageCount > 0 || s.hasMessages) && (s.source ?? "unknown") === source);
5290
+ const matching = (split) => sessions.filter((s) => (s.messageCount > 0 || s.hasMessages) && (s.profile || HERMES_DEFAULT_PROFILE) === split.profile && (s.source ?? "unknown") === split.source);
5291
+ let chosen = candidates[0];
5292
+ let matched = matching(chosen);
5293
+ if (matched.length === 0) {
5294
+ for (const candidate of candidates.slice(1)) {
5295
+ const hits = matching(candidate);
5296
+ if (hits.length > 0) {
5297
+ chosen = candidate;
5298
+ matched = hits;
5299
+ break;
5300
+ }
5301
+ }
5302
+ }
5303
+ const { profile, source } = chosen;
5108
5304
  return {
5109
- cwd: `hermes:${source}`,
5305
+ cwd: hermesProjectPath(profile, source),
5110
5306
  sessions: matched.map((s) => {
5111
5307
  const lastModified = new Date(s.mtimeMs);
5112
5308
  return {
@@ -5127,6 +5323,7 @@ var getCachedHermesSessions, getCachedHermesProjects, getCachedHermesSessionsByE
5127
5323
  var init_hermes_projects = __esm(() => {
5128
5324
  init_sqlite_reader();
5129
5325
  init_hermes_sessions();
5326
+ init_hermes_profiles();
5130
5327
  getCachedHermesSessions = runtimeCache(getHermesSessions, 2);
5131
5328
  getCachedHermesProjects = runtimeCache(getHermesProjects, 2);
5132
5329
  getCachedHermesSessionsByEncodedName = runtimeCache((name) => getHermesSessionsByEncodedName(name), 2, { maxSize: 50 });
@@ -5134,11 +5331,11 @@ var init_hermes_projects = __esm(() => {
5134
5331
 
5135
5332
  // lib/openclaw-sessions.ts
5136
5333
  import { readFile as readFile6 } from "node:fs/promises";
5137
- import { readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
5334
+ import { readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
5138
5335
  import { homedir as homedir12 } from "node:os";
5139
- import { join as join10 } from "node:path";
5336
+ import { join as join11 } from "node:path";
5140
5337
  function openclawHome() {
5141
- return process.env.OPENCLAW_HOME || join10(homedir12(), ".openclaw");
5338
+ return process.env.OPENCLAW_HOME || join11(homedir12(), ".openclaw");
5142
5339
  }
5143
5340
  function isPlainObject2(v) {
5144
5341
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -5251,20 +5448,27 @@ function openclawLinesToLogEntries(lines, source = "session") {
5251
5448
  entries.sort((a, b) => a.timestampMs - b.timestampMs);
5252
5449
  return entries;
5253
5450
  }
5451
+ function listOpenClawAgents() {
5452
+ try {
5453
+ return readdirSync4(join11(openclawHome(), "agents"), { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort();
5454
+ } catch {
5455
+ return [];
5456
+ }
5457
+ }
5254
5458
  function listOpenClawTranscripts() {
5255
- const agentsDir = join10(openclawHome(), "agents");
5459
+ const agentsDir = join11(openclawHome(), "agents");
5256
5460
  const out = [];
5257
5461
  let agentIds;
5258
5462
  try {
5259
- agentIds = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
5463
+ agentIds = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
5260
5464
  } catch {
5261
5465
  return out;
5262
5466
  }
5263
5467
  for (const agentId of agentIds) {
5264
- const sessionsDir = join10(agentsDir, agentId, "sessions");
5468
+ const sessionsDir = join11(agentsDir, agentId, "sessions");
5265
5469
  let files;
5266
5470
  try {
5267
- files = readdirSync3(sessionsDir);
5471
+ files = readdirSync4(sessionsDir);
5268
5472
  } catch {
5269
5473
  continue;
5270
5474
  }
@@ -5274,7 +5478,7 @@ function listOpenClawTranscripts() {
5274
5478
  const sessionId = file.slice(0, -".jsonl".length);
5275
5479
  if (!OPENCLAW_SESSION_ID_RE.test(sessionId))
5276
5480
  continue;
5277
- const transcriptPath = join10(sessionsDir, file);
5481
+ const transcriptPath = join11(sessionsDir, file);
5278
5482
  try {
5279
5483
  const st = statSync4(transcriptPath);
5280
5484
  out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
@@ -5323,6 +5527,10 @@ var init_openclaw_sessions = __esm(() => {
5323
5527
  // lib/openclaw-projects.ts
5324
5528
  var exports_openclaw_projects = {};
5325
5529
  __export(exports_openclaw_projects, {
5530
+ parseOpenClawProjectName: () => parseOpenClawProjectName,
5531
+ openClawProjectPath: () => openClawProjectPath,
5532
+ openClawProjectNameCandidates: () => openClawProjectNameCandidates,
5533
+ openClawProjectName: () => openClawProjectName,
5326
5534
  getOpenClawSessionsByEncodedName: () => getOpenClawSessionsByEncodedName,
5327
5535
  getOpenClawSessions: () => getOpenClawSessions,
5328
5536
  getOpenClawProjects: () => getOpenClawProjects,
@@ -5331,13 +5539,13 @@ __export(exports_openclaw_projects, {
5331
5539
  getCachedOpenClawProjects: () => getCachedOpenClawProjects
5332
5540
  });
5333
5541
  import { readFileSync as readFileSync4 } from "node:fs";
5334
- import { join as join11 } from "node:path";
5542
+ import { join as join12 } from "node:path";
5335
5543
  function str(v) {
5336
5544
  return typeof v === "string" && v.length > 0 ? v : undefined;
5337
5545
  }
5338
5546
  function readSessionsIndex(agentId) {
5339
5547
  const out = new Map;
5340
- const indexPath = join11(openclawHome(), "agents", agentId, "sessions", "sessions.json");
5548
+ const indexPath = join12(openclawHome(), "agents", agentId, "sessions", "sessions.json");
5341
5549
  let raw;
5342
5550
  try {
5343
5551
  raw = JSON.parse(readFileSync4(indexPath, "utf-8"));
@@ -5383,6 +5591,7 @@ async function getOpenClawSessions() {
5383
5591
  label: meta?.label,
5384
5592
  chatType: meta?.chatType,
5385
5593
  chatId: meta?.chatId,
5594
+ transcriptPath: t.transcriptPath,
5386
5595
  mtimeMs: meta?.lastMs ?? t.mtimeMs,
5387
5596
  sizeBytes: t.sizeBytes
5388
5597
  });
@@ -5390,35 +5599,82 @@ async function getOpenClawSessions() {
5390
5599
  refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
5391
5600
  return refs;
5392
5601
  }
5602
+ function openClawProjectName(agentId, channel) {
5603
+ return `openclaw-${agentId}-${channel}`;
5604
+ }
5605
+ function openClawProjectPath(agentId, channel) {
5606
+ return `openclaw:${agentId}:${channel}`;
5607
+ }
5608
+ function openClawProjectNameCandidates(name) {
5609
+ if (!name.startsWith("openclaw-"))
5610
+ return [];
5611
+ const rest = name.slice("openclaw-".length);
5612
+ if (!rest)
5613
+ return [];
5614
+ const out = [];
5615
+ const agents = listOpenClawAgents().sort((a, b) => b.length - a.length);
5616
+ for (const agentId of agents) {
5617
+ const prefix = `${agentId}-`;
5618
+ if (rest.startsWith(prefix) && rest.length > prefix.length) {
5619
+ out.push({ agentId, channel: rest.slice(prefix.length) });
5620
+ }
5621
+ }
5622
+ out.push({ agentId: null, channel: rest });
5623
+ return out;
5624
+ }
5625
+ function parseOpenClawProjectName(name) {
5626
+ return openClawProjectNameCandidates(name)[0] ?? null;
5627
+ }
5393
5628
  async function getOpenClawProjects() {
5394
5629
  const sessions = await getOpenClawSessions();
5395
- const latestByChannel = new Map;
5630
+ const groups = new Map;
5396
5631
  for (const s of sessions) {
5397
- latestByChannel.set(s.channel, Math.max(latestByChannel.get(s.channel) ?? 0, s.mtimeMs));
5632
+ const key = JSON.stringify([s.agentId, s.channel]);
5633
+ const prev = groups.get(key);
5634
+ if (prev) {
5635
+ prev.latest = Math.max(prev.latest, s.mtimeMs);
5636
+ prev.count += 1;
5637
+ } else {
5638
+ groups.set(key, { agentId: s.agentId, channel: s.channel, latest: s.mtimeMs, count: 1 });
5639
+ }
5398
5640
  }
5399
5641
  const out = [];
5400
- for (const [channel, latest] of latestByChannel) {
5642
+ for (const { agentId, channel, latest, count } of groups.values()) {
5401
5643
  const lastModified = new Date(latest);
5402
5644
  out.push({
5403
- name: `openclaw-${channel}`,
5404
- path: `openclaw:${channel}`,
5645
+ name: openClawProjectName(agentId, channel),
5646
+ path: openClawProjectPath(agentId, channel),
5405
5647
  isDirectory: true,
5406
5648
  lastModified,
5407
5649
  lastModifiedFormatted: formatDate(lastModified),
5408
- cli: ["openclaw"]
5650
+ cli: ["openclaw"],
5651
+ sessionCount: count
5409
5652
  });
5410
5653
  }
5411
5654
  out.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
5412
5655
  return out;
5413
5656
  }
5414
5657
  async function getOpenClawSessionsByEncodedName(name) {
5415
- if (!name.startsWith("openclaw-"))
5658
+ const candidates = openClawProjectNameCandidates(name);
5659
+ if (candidates.length === 0)
5416
5660
  return { cwd: null, sessions: [] };
5417
- const channel = name.slice("openclaw-".length);
5418
5661
  const sessions = await getOpenClawSessions();
5419
- const matched = sessions.filter((s) => s.channel === channel);
5662
+ const matching = (split) => sessions.filter((s) => s.channel === split.channel && (split.agentId === null || s.agentId === split.agentId));
5663
+ let chosen = candidates[0];
5664
+ let matched = matching(chosen);
5665
+ if (matched.length === 0) {
5666
+ for (const candidate of candidates.slice(1)) {
5667
+ const hits = matching(candidate);
5668
+ if (hits.length > 0) {
5669
+ chosen = candidate;
5670
+ matched = hits;
5671
+ break;
5672
+ }
5673
+ }
5674
+ }
5675
+ const { agentId, channel } = chosen;
5420
5676
  return {
5421
- cwd: `openclaw:${channel}`,
5677
+ cwd: agentId === null ? `openclaw:${channel}` : openClawProjectPath(agentId, channel),
5422
5678
  sessions: matched.map((s) => {
5423
5679
  const lastModified = new Date(s.mtimeMs);
5424
5680
  return {
@@ -5444,14 +5700,14 @@ var init_openclaw_projects = __esm(() => {
5444
5700
 
5445
5701
  // lib/factory-sessions.ts
5446
5702
  import { readFile as readFile7 } from "node:fs/promises";
5447
- import { readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
5703
+ import { readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
5448
5704
  import { homedir as homedir13 } from "node:os";
5449
- import { join as join12 } from "node:path";
5705
+ import { join as join13 } from "node:path";
5450
5706
  function factoryHome() {
5451
- return process.env.FACTORY_HOME || join12(homedir13(), ".factory");
5707
+ return process.env.FACTORY_HOME || join13(homedir13(), ".factory");
5452
5708
  }
5453
5709
  function factorySessionsRoot() {
5454
- return join12(factoryHome(), "sessions");
5710
+ return join13(factoryHome(), "sessions");
5455
5711
  }
5456
5712
  function isPlainObject3(v) {
5457
5713
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -5590,17 +5846,17 @@ function listFactoryTranscripts() {
5590
5846
  const out = [];
5591
5847
  let projectDirs;
5592
5848
  try {
5593
- projectDirs = readdirSync4(root, { withFileTypes: true }).filter((d) => d.isDirectory());
5849
+ projectDirs = readdirSync5(root, { withFileTypes: true }).filter((d) => d.isDirectory());
5594
5850
  } catch {
5595
5851
  return out;
5596
5852
  }
5597
5853
  for (const dir of projectDirs) {
5598
5854
  const projectName = dir.name;
5599
5855
  const cwd = decodeFolderName(projectName);
5600
- const projectPath = join12(root, projectName);
5856
+ const projectPath = join13(root, projectName);
5601
5857
  let files;
5602
5858
  try {
5603
- files = readdirSync4(projectPath);
5859
+ files = readdirSync5(projectPath);
5604
5860
  } catch {
5605
5861
  continue;
5606
5862
  }
@@ -5610,7 +5866,7 @@ function listFactoryTranscripts() {
5610
5866
  const sessionId = file.slice(0, -".jsonl".length);
5611
5867
  if (!FACTORY_SESSION_ID_RE.test(sessionId))
5612
5868
  continue;
5613
- const transcriptPath = join12(projectPath, file);
5869
+ const transcriptPath = join13(projectPath, file);
5614
5870
  try {
5615
5871
  const st = statSync5(transcriptPath);
5616
5872
  out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
@@ -5737,12 +5993,12 @@ var init_factory_projects = __esm(() => {
5737
5993
 
5738
5994
  // lib/devin-sessions.ts
5739
5995
  import { homedir as homedir14 } from "node:os";
5740
- import { join as join13 } from "node:path";
5996
+ import { join as join14 } from "node:path";
5741
5997
  function devinHome() {
5742
- return process.env.DEVIN_HOME || join13(homedir14(), ".local", "share", "devin");
5998
+ return process.env.DEVIN_HOME || join14(homedir14(), ".local", "share", "devin");
5743
5999
  }
5744
6000
  function devinDbPath() {
5745
- return process.env.DEVIN_DB_PATH || join13(devinHome(), "cli", "sessions.db");
6001
+ return process.env.DEVIN_DB_PATH || join14(devinHome(), "cli", "sessions.db");
5746
6002
  }
5747
6003
  function devinEpochToMs(value) {
5748
6004
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -6039,17 +6295,17 @@ var init_devin_projects = __esm(() => {
6039
6295
 
6040
6296
  // lib/antigravity-sessions.ts
6041
6297
  import { readFile as readFile8 } from "node:fs/promises";
6042
- import { readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
6298
+ import { readdirSync as readdirSync6, statSync as statSync6 } from "node:fs";
6043
6299
  import { homedir as homedir15 } from "node:os";
6044
- import { join as join14 } from "node:path";
6300
+ import { join as join15 } from "node:path";
6045
6301
  function antigravityHome() {
6046
- return process.env.ANTIGRAVITY_HOME || join14(homedir15(), ".gemini", "antigravity-cli");
6302
+ return process.env.ANTIGRAVITY_HOME || join15(homedir15(), ".gemini", "antigravity-cli");
6047
6303
  }
6048
6304
  function antigravityBrainRoot() {
6049
- return join14(antigravityHome(), "brain");
6305
+ return join15(antigravityHome(), "brain");
6050
6306
  }
6051
6307
  function antigravityTranscriptRelPath() {
6052
- return join14(".system_generated", "logs", "transcript_full.jsonl");
6308
+ return join15(".system_generated", "logs", "transcript_full.jsonl");
6053
6309
  }
6054
6310
  function isPlainObject5(v) {
6055
6311
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -6164,7 +6420,7 @@ function listAntigravityTranscripts() {
6164
6420
  const out = [];
6165
6421
  let convoDirs;
6166
6422
  try {
6167
- convoDirs = readdirSync5(root, { withFileTypes: true }).filter((d) => d.isDirectory());
6423
+ convoDirs = readdirSync6(root, { withFileTypes: true }).filter((d) => d.isDirectory());
6168
6424
  } catch {
6169
6425
  return out;
6170
6426
  }
@@ -6172,7 +6428,7 @@ function listAntigravityTranscripts() {
6172
6428
  const conversationId = dir.name;
6173
6429
  if (!ANTIGRAVITY_CONVERSATION_ID_RE.test(conversationId))
6174
6430
  continue;
6175
- const transcriptPath = join14(root, conversationId, rel);
6431
+ const transcriptPath = join15(root, conversationId, rel);
6176
6432
  try {
6177
6433
  const st = statSync6(transcriptPath);
6178
6434
  if (!st.isFile())
@@ -6185,7 +6441,7 @@ function listAntigravityTranscripts() {
6185
6441
  function findAntigravityTranscript(conversationId) {
6186
6442
  if (!ANTIGRAVITY_CONVERSATION_ID_RE.test(conversationId))
6187
6443
  return null;
6188
- const transcriptPath = join14(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
6444
+ const transcriptPath = join15(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
6189
6445
  try {
6190
6446
  if (statSync6(transcriptPath).isFile())
6191
6447
  return transcriptPath;
@@ -6225,9 +6481,9 @@ __export(exports_antigravity_projects, {
6225
6481
  getAntigravityIndex: () => getAntigravityIndex,
6226
6482
  antigravitySummariesDbPath: () => antigravitySummariesDbPath
6227
6483
  });
6228
- import { join as join15 } from "node:path";
6484
+ import { join as join16 } from "node:path";
6229
6485
  function antigravitySummariesDbPath() {
6230
- return join15(antigravityHome(), "conversation_summaries.db");
6486
+ return join16(antigravityHome(), "conversation_summaries.db");
6231
6487
  }
6232
6488
  function parseWorkspaceCwd(raw) {
6233
6489
  if (!raw)
@@ -6371,15 +6627,15 @@ var init_antigravity_projects = __esm(() => {
6371
6627
 
6372
6628
  // lib/goose-sessions.ts
6373
6629
  import { homedir as homedir16 } from "node:os";
6374
- import { join as join16 } from "node:path";
6630
+ import { join as join17 } from "node:path";
6375
6631
  function gooseHome() {
6376
6632
  if (process.env.GOOSE_HOME)
6377
6633
  return process.env.GOOSE_HOME;
6378
6634
  const xdg = process.env.XDG_DATA_HOME;
6379
- return join16(xdg && xdg.length > 0 ? xdg : join16(homedir16(), ".local", "share"), "goose");
6635
+ return join17(xdg && xdg.length > 0 ? xdg : join17(homedir16(), ".local", "share"), "goose");
6380
6636
  }
6381
6637
  function gooseDbPath() {
6382
- return process.env.GOOSE_DB_PATH || join16(gooseHome(), "sessions", "sessions.db");
6638
+ return process.env.GOOSE_DB_PATH || join17(gooseHome(), "sessions", "sessions.db");
6383
6639
  }
6384
6640
  function gooseTimestampToMs(value) {
6385
6641
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -6647,7 +6903,7 @@ var init_goose_projects = __esm(() => {
6647
6903
 
6648
6904
  // lib/projects.ts
6649
6905
  import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
6650
- import { join as join17, resolve as resolve5, sep } from "node:path";
6906
+ import { join as join18, resolve as resolve6, sep } from "node:path";
6651
6907
  async function getMtime(path3, label) {
6652
6908
  try {
6653
6909
  return (await stat5(path3)).mtime;
@@ -6673,7 +6929,7 @@ async function getClaudeProjectFolders() {
6673
6929
  if (!entries)
6674
6930
  return [];
6675
6931
  const settled = await batchAll(entries.filter((entry) => entry.isDirectory()).map((entry) => async () => {
6676
- const folderPath = join17(projectsPath, entry.name);
6932
+ const folderPath = join18(projectsPath, entry.name);
6677
6933
  const mtime = await getMtime(folderPath, entry.name);
6678
6934
  return {
6679
6935
  name: entry.name,
@@ -6710,7 +6966,8 @@ function mergeProjectFolders(...sources) {
6710
6966
  ...existing,
6711
6967
  cli: mergedCli,
6712
6968
  lastModified: newer.lastModified,
6713
- lastModifiedFormatted: newer.lastModifiedFormatted
6969
+ lastModifiedFormatted: newer.lastModifiedFormatted,
6970
+ sessionCount: existing.sessionCount === undefined && f.sessionCount === undefined ? undefined : (existing.sessionCount ?? 0) + (f.sessionCount ?? 0)
6714
6971
  });
6715
6972
  }
6716
6973
  }
@@ -6798,8 +7055,8 @@ function resolveProjectPath(projectName) {
6798
7055
  throw new RangeError("Empty project name");
6799
7056
  if (/^[/\\]/.test(projectName))
6800
7057
  throw new RangeError("Absolute project name");
6801
- const projectsPath = resolve5(getClaudeProjectsPath());
6802
- const candidate = resolve5(join17(projectsPath, projectName));
7058
+ const projectsPath = resolve6(getClaudeProjectsPath());
7059
+ const candidate = resolve6(join18(projectsPath, projectName));
6803
7060
  if (!candidate.startsWith(projectsPath + sep)) {
6804
7061
  throw new RangeError("Project path escapes root");
6805
7062
  }
@@ -6817,7 +7074,7 @@ async function getSessionFiles(projectPath) {
6817
7074
  return [];
6818
7075
  const jsonlEntries = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl") && extractSessionId2(entry.name));
6819
7076
  const settled = await batchAll(jsonlEntries.map((entry) => async () => {
6820
- const filePath = join17(projectPath, entry.name);
7077
+ const filePath = join18(projectPath, entry.name);
6821
7078
  const mtime = await getMtime(filePath, entry.name);
6822
7079
  return {
6823
7080
  name: entry.name,
@@ -6846,13 +7103,13 @@ var init_projects = __esm(() => {
6846
7103
 
6847
7104
  // lib/resolve-subagent-path.ts
6848
7105
  import { access as access2 } from "fs/promises";
6849
- import { join as join18, relative as relative2 } from "path";
7106
+ import { join as join19, relative as relative2 } from "path";
6850
7107
  async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId) {
6851
7108
  const fileName = `agent-${agentId}.jsonl`;
6852
7109
  const candidatePaths = [
6853
- join18(projectsPath, projectName, fileName),
6854
- join18(projectsPath, projectName, sessionId, fileName),
6855
- join18(projectsPath, projectName, sessionId, "subagents", fileName)
7110
+ join19(projectsPath, projectName, fileName),
7111
+ join19(projectsPath, projectName, sessionId, fileName),
7112
+ join19(projectsPath, projectName, sessionId, "subagents", fileName)
6856
7113
  ];
6857
7114
  for (const candidatePath of candidatePaths) {
6858
7115
  if (relative2(projectsPath, candidatePath).startsWith("..")) {
@@ -6874,7 +7131,7 @@ var init_resolve_subagent_path = () => {};
6874
7131
 
6875
7132
  // lib/log-entries.ts
6876
7133
  import { readFile as readFile9 } from "fs/promises";
6877
- import { join as join19 } from "path";
7134
+ import { join as join20 } from "path";
6878
7135
  function formatTimestamp(date) {
6879
7136
  const base = formatDate(date);
6880
7137
  const ms = date.getMilliseconds().toString().padStart(3, "0");
@@ -7072,7 +7329,7 @@ async function parseLogContent(fileContent, source = "session") {
7072
7329
  async function parseSessionLog(projectName, sessionId) {
7073
7330
  const projectDir = resolveProjectPath(projectName);
7074
7331
  const projectsPath = getClaudeProjectsPath();
7075
- const filePath = join19(projectDir, `${sessionId}.jsonl`);
7332
+ const filePath = join20(projectDir, `${sessionId}.jsonl`);
7076
7333
  const fileContent = await readFile9(filePath, "utf-8");
7077
7334
  const { entries: sessionEntries, rawLines: sessionRawLines, subagentIds } = await parseFileContent(fileContent, "session");
7078
7335
  if (subagentIds.length === 0) {
@@ -7109,9 +7366,9 @@ var init_log_entries = __esm(() => {
7109
7366
  });
7110
7367
 
7111
7368
  // lib/codex-sessions.ts
7112
- import { readFileSync as readFileSync5, readdirSync as readdirSync6, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync7 } from "node:fs";
7369
+ import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync7 } from "node:fs";
7113
7370
  import { readFile as readFile10 } from "node:fs/promises";
7114
- import { dirname as dirname3, join as join20 } from "node:path";
7371
+ import { dirname as dirname4, join as join21 } from "node:path";
7115
7372
  import { homedir as homedir17 } from "node:os";
7116
7373
  function readCache() {
7117
7374
  try {
@@ -7124,7 +7381,7 @@ function readCache() {
7124
7381
  }
7125
7382
  function writeCacheEntry(sessionId, path3) {
7126
7383
  try {
7127
- mkdirSync4(dirname3(CACHE_PATH), { recursive: true });
7384
+ mkdirSync4(dirname4(CACHE_PATH), { recursive: true });
7128
7385
  const cache = readCache();
7129
7386
  cache[sessionId] = path3;
7130
7387
  writeFileSync3(CACHE_PATH, JSON.stringify(cache), "utf-8");
@@ -7132,9 +7389,9 @@ function writeCacheEntry(sessionId, path3) {
7132
7389
  }
7133
7390
  function dirSearch(dir, sessionId) {
7134
7391
  try {
7135
- for (const f of readdirSync6(dir, { withFileTypes: true })) {
7392
+ for (const f of readdirSync7(dir, { withFileTypes: true })) {
7136
7393
  if (f.isFile() && f.name.includes(sessionId) && f.name.endsWith(".jsonl")) {
7137
- return join20(dir, f.name);
7394
+ return join21(dir, f.name);
7138
7395
  }
7139
7396
  }
7140
7397
  } catch {}
@@ -7145,14 +7402,14 @@ function findCodexTranscript(sessionId) {
7145
7402
  const cached = cache[sessionId];
7146
7403
  if (cached && existsSync6(cached))
7147
7404
  return cached;
7148
- const root = join20(homedir17(), ".codex", "sessions");
7405
+ const root = join21(homedir17(), ".codex", "sessions");
7149
7406
  const today = new Date;
7150
7407
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
7151
7408
  const datedDirs = [today, yesterday].map((d) => {
7152
7409
  const y = String(d.getUTCFullYear());
7153
7410
  const m = String(d.getUTCMonth() + 1).padStart(2, "0");
7154
7411
  const day = String(d.getUTCDate()).padStart(2, "0");
7155
- return join20(root, y, m, day);
7412
+ return join21(root, y, m, day);
7156
7413
  });
7157
7414
  for (const dir of datedDirs) {
7158
7415
  const hit = dirSearch(dir, sessionId);
@@ -7162,16 +7419,16 @@ function findCodexTranscript(sessionId) {
7162
7419
  }
7163
7420
  }
7164
7421
  try {
7165
- for (const y of readdirSync6(root, { withFileTypes: true })) {
7422
+ for (const y of readdirSync7(root, { withFileTypes: true })) {
7166
7423
  if (!y.isDirectory())
7167
7424
  continue;
7168
- for (const m of readdirSync6(join20(root, y.name), { withFileTypes: true })) {
7425
+ for (const m of readdirSync7(join21(root, y.name), { withFileTypes: true })) {
7169
7426
  if (!m.isDirectory())
7170
7427
  continue;
7171
- for (const d of readdirSync6(join20(root, y.name, m.name), { withFileTypes: true })) {
7428
+ for (const d of readdirSync7(join21(root, y.name, m.name), { withFileTypes: true })) {
7172
7429
  if (!d.isDirectory())
7173
7430
  continue;
7174
- const hit = dirSearch(join20(root, y.name, m.name, d.name), sessionId);
7431
+ const hit = dirSearch(join21(root, y.name, m.name, d.name), sessionId);
7175
7432
  if (hit) {
7176
7433
  writeCacheEntry(sessionId, hit);
7177
7434
  return hit;
@@ -7391,7 +7648,7 @@ async function getCodexSessionLog(sessionId) {
7391
7648
  var CACHE_PATH, getCachedCodexSessionLog;
7392
7649
  var init_codex_sessions = __esm(() => {
7393
7650
  init_log_entries();
7394
- CACHE_PATH = join20(homedir17(), ".failproofai", "cache", "codex-session-paths.json");
7651
+ CACHE_PATH = join21(homedir17(), ".failproofai", "cache", "codex-session-paths.json");
7395
7652
  getCachedCodexSessionLog = runtimeCache((sessionId) => getCodexSessionLog(sessionId), 60, { maxSize: 50 });
7396
7653
  });
7397
7654
 
@@ -7436,21 +7693,21 @@ var init_resolve_permission_mode = __esm(() => {
7436
7693
  });
7437
7694
 
7438
7695
  // lib/copilot-sessions.ts
7439
- import { readFileSync as readFileSync7, readdirSync as readdirSync7, existsSync as existsSync7, statSync as statSync8 } from "node:fs";
7696
+ import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as statSync8 } from "node:fs";
7440
7697
  import { readFile as readFile11 } from "node:fs/promises";
7441
- import { join as join21, resolve as resolve6, sep as sep2 } from "node:path";
7698
+ import { join as join22, resolve as resolve7, sep as sep2 } from "node:path";
7442
7699
  import { homedir as homedir18 } from "node:os";
7443
7700
  function getCopilotHome() {
7444
- return process.env.COPILOT_HOME || join21(homedir18(), ".copilot");
7701
+ return process.env.COPILOT_HOME || join22(homedir18(), ".copilot");
7445
7702
  }
7446
7703
  function getCopilotSessionStateRoot2() {
7447
- return join21(getCopilotHome(), "session-state");
7704
+ return join22(getCopilotHome(), "session-state");
7448
7705
  }
7449
7706
  function getCopilotSessionDir(sessionId) {
7450
7707
  if (!sessionId)
7451
7708
  return null;
7452
- const root = resolve6(getCopilotSessionStateRoot2());
7453
- const candidate = resolve6(root, sessionId);
7709
+ const root = resolve7(getCopilotSessionStateRoot2());
7710
+ const candidate = resolve7(root, sessionId);
7454
7711
  if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
7455
7712
  return null;
7456
7713
  return candidate;
@@ -7459,7 +7716,7 @@ function resolveSessionFile(sessionId, filename) {
7459
7716
  const dir = getCopilotSessionDir(sessionId);
7460
7717
  if (!dir)
7461
7718
  return null;
7462
- return join21(dir, filename);
7719
+ return join22(dir, filename);
7463
7720
  }
7464
7721
  function findCopilotTranscript(sessionId) {
7465
7722
  const candidate = resolveSessionFile(sessionId, "events.jsonl");
@@ -7653,35 +7910,35 @@ var init_copilot_sessions = __esm(() => {
7653
7910
  });
7654
7911
 
7655
7912
  // lib/cursor-sessions.ts
7656
- import { readFileSync as readFileSync8, readdirSync as readdirSync8, existsSync as existsSync8, statSync as statSync9 } from "node:fs";
7913
+ import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as statSync9 } from "node:fs";
7657
7914
  import { readFile as readFile12 } from "node:fs/promises";
7658
- import { basename as basename2, join as join22, resolve as resolve7, sep as sep3 } from "node:path";
7915
+ import { basename as basename3, join as join23, resolve as resolve8, sep as sep3 } from "node:path";
7659
7916
  import { homedir as homedir19 } from "node:os";
7660
7917
  function getCursorHome2() {
7661
- return process.env.CURSOR_HOME || join22(homedir19(), ".cursor");
7918
+ return process.env.CURSOR_HOME || join23(homedir19(), ".cursor");
7662
7919
  }
7663
7920
  function getCursorSessionDir(sessionId) {
7664
7921
  if (!sessionId)
7665
7922
  return null;
7666
- const home = resolve7(getCursorHome2());
7667
- const projectsRoot = resolve7(home, NEW_PROJECTS_DIR2);
7923
+ const home = resolve8(getCursorHome2());
7924
+ const projectsRoot = resolve8(home, NEW_PROJECTS_DIR2);
7668
7925
  let projectEntries = [];
7669
7926
  try {
7670
- projectEntries = readdirSync8(projectsRoot, { withFileTypes: true });
7927
+ projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
7671
7928
  } catch {}
7672
7929
  for (const entry of projectEntries) {
7673
7930
  if (!entry.isDirectory())
7674
7931
  continue;
7675
- const candidate = resolve7(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
7676
- const transcriptRoot = resolve7(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
7932
+ const candidate = resolve8(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
7933
+ const transcriptRoot = resolve8(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
7677
7934
  if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
7678
7935
  continue;
7679
7936
  if (existsSync8(candidate))
7680
7937
  return candidate;
7681
7938
  }
7682
7939
  for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
7683
- const root = resolve7(home, sub);
7684
- const candidate = resolve7(root, sessionId);
7940
+ const root = resolve8(home, sub);
7941
+ const candidate = resolve8(root, sessionId);
7685
7942
  if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
7686
7943
  continue;
7687
7944
  if (existsSync8(candidate))
@@ -7693,11 +7950,11 @@ function findCursorTranscript(sessionId) {
7693
7950
  const dir = getCursorSessionDir(sessionId);
7694
7951
  if (!dir)
7695
7952
  return null;
7696
- const newCandidate = join22(dir, `${basename2(dir)}.jsonl`);
7953
+ const newCandidate = join23(dir, `${basename3(dir)}.jsonl`);
7697
7954
  if (existsSync8(newCandidate))
7698
7955
  return newCandidate;
7699
7956
  for (const name of LEGACY_TRANSCRIPT_FILE_CANDIDATES2) {
7700
- const candidate = join22(dir, name);
7957
+ const candidate = join23(dir, name);
7701
7958
  if (existsSync8(candidate))
7702
7959
  return candidate;
7703
7960
  }
@@ -7909,12 +8166,12 @@ var init_cursor_sessions = __esm(() => {
7909
8166
  });
7910
8167
 
7911
8168
  // lib/pi-sessions.ts
7912
- import { readFileSync as readFileSync9, readdirSync as readdirSync9, existsSync as existsSync9, statSync as statSync10 } from "node:fs";
8169
+ import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as statSync10 } from "node:fs";
7913
8170
  import { readFile as readFile13 } from "node:fs/promises";
7914
- import { join as join23, resolve as resolve8, sep as sep4 } from "node:path";
8171
+ import { join as join24, resolve as resolve9, sep as sep4 } from "node:path";
7915
8172
  import { homedir as homedir20 } from "node:os";
7916
8173
  function getPiSessionStateRoot() {
7917
- return process.env.PI_SESSIONS_DIR || join23(homedir20(), ".pi", "agent", "sessions");
8174
+ return process.env.PI_SESSIONS_DIR || join24(homedir20(), ".pi", "agent", "sessions");
7918
8175
  }
7919
8176
  function isSafeSessionId(sessionId) {
7920
8177
  return UUID_RE.test(sessionId);
@@ -7922,20 +8179,20 @@ function isSafeSessionId(sessionId) {
7922
8179
  function findPiTranscript(sessionId) {
7923
8180
  if (!isSafeSessionId(sessionId))
7924
8181
  return null;
7925
- const root = resolve8(getPiSessionStateRoot());
8182
+ const root = resolve9(getPiSessionStateRoot());
7926
8183
  let cwdDirs;
7927
8184
  try {
7928
- cwdDirs = readdirSync9(root);
8185
+ cwdDirs = readdirSync10(root);
7929
8186
  } catch {
7930
8187
  return null;
7931
8188
  }
7932
8189
  for (const cwdDir of cwdDirs) {
7933
- const cwdPath = resolve8(root, cwdDir);
8190
+ const cwdPath = resolve9(root, cwdDir);
7934
8191
  if (!cwdPath.startsWith(`${root}${sep4}`))
7935
8192
  continue;
7936
8193
  let files;
7937
8194
  try {
7938
- files = readdirSync9(cwdPath);
8195
+ files = readdirSync10(cwdPath);
7939
8196
  } catch {
7940
8197
  continue;
7941
8198
  }
@@ -7943,7 +8200,7 @@ function findPiTranscript(sessionId) {
7943
8200
  const m = SESSION_FILE_RE2.exec(f);
7944
8201
  if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
7945
8202
  continue;
7946
- const candidate = resolve8(cwdPath, f);
8203
+ const candidate = resolve9(cwdPath, f);
7947
8204
  if (!candidate.startsWith(`${cwdPath}${sep4}`))
7948
8205
  continue;
7949
8206
  if (existsSync9(candidate))
@@ -8187,7 +8444,7 @@ async function handleHookEvent(eventType, cli = "claude") {
8187
8444
  let payload = "";
8188
8445
  let stdinOversized = false;
8189
8446
  try {
8190
- payload = await new Promise((resolve9, reject) => {
8447
+ payload = await new Promise((resolve10, reject) => {
8191
8448
  const chunks = [];
8192
8449
  let totalBytes = 0;
8193
8450
  process.stdin.setEncoding("utf8");
@@ -8197,15 +8454,15 @@ async function handleHookEvent(eventType, cli = "claude") {
8197
8454
  hookLogWarn(`stdin payload exceeds 1 MB for ${eventType}, discarding`);
8198
8455
  stdinOversized = true;
8199
8456
  process.stdin.destroy();
8200
- resolve9("");
8457
+ resolve10("");
8201
8458
  return;
8202
8459
  }
8203
8460
  chunks.push(chunk);
8204
8461
  });
8205
- process.stdin.on("end", () => resolve9(chunks.join("")));
8462
+ process.stdin.on("end", () => resolve10(chunks.join("")));
8206
8463
  process.stdin.on("error", reject);
8207
8464
  if (process.stdin.readableEnded)
8208
- resolve9("");
8465
+ resolve10("");
8209
8466
  });
8210
8467
  } catch (err) {
8211
8468
  hookLogWarn(`stdin read failed for ${eventType}`);
@@ -8293,13 +8550,17 @@ async function handleHookEvent(eventType, cli = "claude") {
8293
8550
  const config = readMergedHooksConfig(session.cwd);
8294
8551
  clearPolicies();
8295
8552
  registerBuiltinPolicies(config.enabledPolicies);
8296
- const loadResult = await loadAllCustomHooks(config.customPoliciesPath, {
8553
+ const loadResult = await loadAllCustomHooks(config.customPoliciesPaths ?? config.customPoliciesPath, {
8297
8554
  sessionCwd: session.cwd,
8298
8555
  customPoliciesEnabled: config.customPoliciesEnabled
8299
8556
  });
8300
8557
  const customHooksList = loadResult.hooks;
8558
+ const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
8301
8559
  const conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
8302
8560
  for (const hook of customHooksList) {
8561
+ const policyId = hook.__policyId;
8562
+ if (policyId && disabledCustomPolicies.has(policyId))
8563
+ continue;
8303
8564
  const hookName = hook.name;
8304
8565
  const conventionScope = hook.__conventionScope;
8305
8566
  const isConvention = !!conventionScope;
@@ -8356,6 +8617,7 @@ async function handleHookEvent(eventType, cli = "claude") {
8356
8617
  if (result.stderr) {
8357
8618
  process.stderr.write(result.stderr);
8358
8619
  }
8620
+ const matchedPolicies = getPoliciesForEvent(canonicalEventType, parsed.tool_name).map((p) => p.name);
8359
8621
  const activityEntry = {
8360
8622
  timestamp: Date.now(),
8361
8623
  eventType: canonicalEventType,
@@ -8363,6 +8625,7 @@ async function handleHookEvent(eventType, cli = "claude") {
8363
8625
  toolName: parsed.tool_name ?? null,
8364
8626
  policyName: result.policyName,
8365
8627
  policyNames: result.policyNames,
8628
+ matchedPolicies,
8366
8629
  decision: result.decision,
8367
8630
  reason: result.reason,
8368
8631
  durationMs,
@@ -8424,13 +8687,13 @@ import { createHmac, randomUUID } from "node:crypto";
8424
8687
  import { execSync as execSync4 } from "node:child_process";
8425
8688
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "node:fs";
8426
8689
  import { homedir as homedir21, platform, arch, hostname, cpus } from "node:os";
8427
- import { join as join24 } from "node:path";
8690
+ import { join as join25 } from "node:path";
8428
8691
  function hashToId3(raw) {
8429
8692
  return createHmac("sha256", NAMESPACE3).update(raw).digest("hex");
8430
8693
  }
8431
8694
  function getInstanceId3() {
8432
- const idDir = join24(homedir21(), ".failproofai");
8433
- const idFile = join24(idDir, "instance-id");
8695
+ const idDir = join25(homedir21(), ".failproofai");
8696
+ const idFile = join25(idDir, "instance-id");
8434
8697
  try {
8435
8698
  const existing = readFileSync10(idFile, "utf-8").trim();
8436
8699
  if (existing)
@@ -8511,14 +8774,14 @@ __export(exports_install_check, {
8511
8774
  checkHooks: () => checkHooks
8512
8775
  });
8513
8776
  import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "node:fs";
8514
- import { resolve as resolve9 } from "node:path";
8777
+ import { resolve as resolve10 } from "node:path";
8515
8778
  import { platform as platform2, arch as arch2, release, homedir as homedir22, hostname as hostname2 } from "node:os";
8516
8779
  import { createHmac as createHmac2 } from "node:crypto";
8517
8780
  function hashToId4(raw) {
8518
8781
  return createHmac2("sha256", NAMESPACE4).update(raw).digest("hex");
8519
8782
  }
8520
8783
  function lastVersionPath() {
8521
- return resolve9(homedir22(), ".failproofai", "last-version");
8784
+ return resolve10(homedir22(), ".failproofai", "last-version");
8522
8785
  }
8523
8786
  function compareSemver(a, b) {
8524
8787
  const parse = (v) => {
@@ -8565,7 +8828,7 @@ function compareSemver(a, b) {
8565
8828
  return 0;
8566
8829
  }
8567
8830
  function checkHooks() {
8568
- const hooksConfigPath = resolve9(homedir22(), ".failproofai", "policies-config.json");
8831
+ const hooksConfigPath = resolve10(homedir22(), ".failproofai", "policies-config.json");
8569
8832
  if (!existsSync10(hooksConfigPath)) {
8570
8833
  return { configured: false, registered: false, policyCount: 0 };
8571
8834
  }
@@ -8579,7 +8842,7 @@ function checkHooks() {
8579
8842
  return { configured: false, registered: false, policyCount: 0 };
8580
8843
  }
8581
8844
  const policyCount = config.enabledPolicies.length;
8582
- const settingsPath = resolve9(homedir22(), ".claude", "settings.json");
8845
+ const settingsPath = resolve10(homedir22(), ".claude", "settings.json");
8583
8846
  if (!existsSync10(settingsPath))
8584
8847
  return { configured: true, registered: false, policyCount };
8585
8848
  let settings;
@@ -8644,7 +8907,7 @@ async function maybeReportInstall(version3) {
8644
8907
  enabled_policy_count: hooks.policyCount
8645
8908
  }, { version: version3, timeoutMs: REPORT_TIMEOUT_MS }));
8646
8909
  try {
8647
- mkdirSync6(resolve9(homedir22(), ".failproofai"), { recursive: true });
8910
+ mkdirSync6(resolve10(homedir22(), ".failproofai"), { recursive: true });
8648
8911
  writeFileSync5(versionFile, version3, "utf8");
8649
8912
  } catch {}
8650
8913
  await Promise.all(events.map((p) => p.catch(() => {})));
@@ -15648,7 +15911,7 @@ var init_dist = __esm(() => {
15648
15911
  // src/hooks/integrations.ts
15649
15912
  import { execSync as execSync5 } from "node:child_process";
15650
15913
  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";
15914
+ import { resolve as resolve11, dirname as dirname5 } from "node:path";
15652
15915
  import { fileURLToPath } from "node:url";
15653
15916
  import { homedir as homedir23 } from "node:os";
15654
15917
  function readJsonFile(path3) {
@@ -15658,7 +15921,7 @@ function readJsonFile(path3) {
15658
15921
  return JSON.parse(raw);
15659
15922
  }
15660
15923
  function writeJsonFile(path3, data) {
15661
- mkdirSync7(dirname4(path3), { recursive: true });
15924
+ mkdirSync7(dirname5(path3), { recursive: true });
15662
15925
  writeFileSync6(path3, JSON.stringify(data, null, 2) + `
15663
15926
  `, "utf8");
15664
15927
  }
@@ -15670,7 +15933,7 @@ function readYamlDoc(path3) {
15670
15933
  }
15671
15934
  }
15672
15935
  function writeYamlDoc(path3, doc) {
15673
- mkdirSync7(dirname4(path3), { recursive: true });
15936
+ mkdirSync7(dirname5(path3), { recursive: true });
15674
15937
  writeFileSync6(path3, doc.toString(), "utf8");
15675
15938
  }
15676
15939
  function isMarkedHook(hook) {
@@ -15698,6 +15961,10 @@ function binaryExists(name) {
15698
15961
  return false;
15699
15962
  }
15700
15963
  }
15964
+ function settingsPathsFor(integration, scope, cwd) {
15965
+ const paths = integration.getSettingsPaths?.(scope, cwd);
15966
+ return paths && paths.length > 0 ? paths : [integration.getSettingsPath(scope, cwd)];
15967
+ }
15701
15968
  function isMarkedCopilotHook(hook) {
15702
15969
  if (hook[FAILPROOFAI_HOOK_MARKER] === true)
15703
15970
  return true;
@@ -15710,7 +15977,7 @@ function isMarkedCopilotHook(hook) {
15710
15977
  return false;
15711
15978
  }
15712
15979
  function opencodePluginFilePath(settingsPath) {
15713
- return resolve10(dirname4(settingsPath), "plugins", "failproofai.mjs");
15980
+ return resolve11(dirname5(settingsPath), "plugins", "failproofai.mjs");
15714
15981
  }
15715
15982
  function buildOpenCodePluginShim(binaryPath, scope) {
15716
15983
  const useNpx = scope === "project";
@@ -15925,8 +16192,8 @@ export default async function failproofaiPlugin({ client, directory }) {
15925
16192
  function getPiExtensionPath() {
15926
16193
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
15927
16194
  if (fromEnv)
15928
- return resolve10(fromEnv, "pi-extension");
15929
- return resolve10(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
16195
+ return resolve11(fromEnv, "pi-extension");
16196
+ return resolve11(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
15930
16197
  }
15931
16198
  function isFailproofaiPiEntry(source) {
15932
16199
  if (typeof source !== "string")
@@ -15937,19 +16204,38 @@ function isFailproofaiPiEntry(source) {
15937
16204
  }
15938
16205
  function makePiProjectRelativeEntry(extPath) {
15939
16206
  const cwd = process.cwd();
15940
- const cwdResolved = resolve10(cwd);
15941
- const extResolved = resolve10(extPath);
16207
+ const cwdResolved = resolve11(cwd);
16208
+ const extResolved = resolve11(extPath);
15942
16209
  if (extResolved.startsWith(cwdResolved + "/") || extResolved === cwdResolved) {
15943
16210
  const fromSettingsDir = "../" + extResolved.slice(cwdResolved.length + 1);
15944
16211
  return fromSettingsDir;
15945
16212
  }
15946
16213
  return extResolved;
15947
16214
  }
16215
+ function hermesConfigHasHooks(settingsPath) {
16216
+ if (!existsSync11(settingsPath))
16217
+ return false;
16218
+ try {
16219
+ const doc = readYamlDoc(settingsPath);
16220
+ const js = doc.toJS() ?? {};
16221
+ const hooks = js.hooks;
16222
+ if (!hooks || typeof hooks !== "object")
16223
+ return false;
16224
+ for (const entries of Object.values(hooks)) {
16225
+ if (Array.isArray(entries) && entries.some((h) => isMarkedHook(h)))
16226
+ return true;
16227
+ }
16228
+ } catch {}
16229
+ return false;
16230
+ }
16231
+ function unhookedHermesProfiles() {
16232
+ return listHermesProfiles().filter((p) => existsSync11(p.home)).filter((p) => !hermesConfigHasHooks(resolve11(p.home, "config.yaml"))).map((p) => p.name);
16233
+ }
15948
16234
  function getOpenClawPluginPath() {
15949
16235
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
15950
16236
  if (fromEnv)
15951
- return resolve10(fromEnv, "openclaw-plugin");
15952
- return resolve10(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
16237
+ return resolve11(fromEnv, "openclaw-plugin");
16238
+ return resolve11(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
15953
16239
  }
15954
16240
  function isFailproofaiOpenClawPath(p) {
15955
16241
  if (typeof p !== "string")
@@ -15978,21 +16264,22 @@ function detectInstalledClis() {
15978
16264
  var claudeCode, codex, copilot, cursor, OPENCODE_PLUGIN_REL_PATH = "./plugins/failproofai.mjs", opencode, pi, hermes, OPENCLAW_PLUGIN_ID = "failproofai", openclaw, factory, devin, ANTIGRAVITY_HOOK_NAME = "failproofai", ANTIGRAVITY_TOOL_EVENTS, antigravity, goose, INTEGRATIONS;
15979
16265
  var init_integrations = __esm(() => {
15980
16266
  init_dist();
16267
+ init_hermes_profiles();
15981
16268
  init_types();
15982
16269
  claudeCode = {
15983
16270
  id: "claude",
15984
16271
  displayName: "Claude Code",
15985
16272
  scopes: HOOK_SCOPES,
15986
- eventTypes: HOOK_EVENT_TYPES,
16273
+ eventTypes: CLAUDE_INSTALL_EVENT_TYPES,
15987
16274
  getSettingsPath(scope, cwd) {
15988
- const base = cwd ? resolve10(cwd) : process.cwd();
16275
+ const base = cwd ? resolve11(cwd) : process.cwd();
15989
16276
  switch (scope) {
15990
16277
  case "user":
15991
- return resolve10(homedir23(), ".claude", "settings.json");
16278
+ return resolve11(homedir23(), ".claude", "settings.json");
15992
16279
  case "project":
15993
- return resolve10(base, ".claude", "settings.json");
16280
+ return resolve11(base, ".claude", "settings.json");
15994
16281
  case "local":
15995
- return resolve10(base, ".claude", "settings.local.json");
16282
+ return resolve11(base, ".claude", "settings.local.json");
15996
16283
  }
15997
16284
  },
15998
16285
  readSettings(settingsPath) {
@@ -16015,7 +16302,31 @@ var init_integrations = __esm(() => {
16015
16302
  const s = settings;
16016
16303
  if (!s.hooks)
16017
16304
  s.hooks = {};
16018
- for (const eventType of HOOK_EVENT_TYPES) {
16305
+ const installed = new Set(CLAUDE_INSTALL_EVENT_TYPES);
16306
+ for (const eventType of Object.keys(s.hooks)) {
16307
+ if (installed.has(eventType))
16308
+ continue;
16309
+ const matchers = s.hooks[eventType];
16310
+ if (!Array.isArray(matchers))
16311
+ continue;
16312
+ const kept = [];
16313
+ for (const matcher of matchers) {
16314
+ if (!matcher || !Array.isArray(matcher.hooks)) {
16315
+ if (matcher)
16316
+ kept.push(matcher);
16317
+ continue;
16318
+ }
16319
+ const before = matcher.hooks.length;
16320
+ matcher.hooks = matcher.hooks.filter((h) => !isMarkedHook(h));
16321
+ const weEmptiedIt = matcher.hooks.length === 0 && before > 0;
16322
+ if (!weEmptiedIt)
16323
+ kept.push(matcher);
16324
+ }
16325
+ s.hooks[eventType] = kept;
16326
+ if (kept.length === 0)
16327
+ delete s.hooks[eventType];
16328
+ }
16329
+ for (const eventType of CLAUDE_INSTALL_EVENT_TYPES) {
16019
16330
  const hookEntry = this.buildHookEntry(binaryPath, eventType, scope);
16020
16331
  if (!s.hooks[eventType])
16021
16332
  s.hooks[eventType] = [];
@@ -16093,14 +16404,14 @@ var init_integrations = __esm(() => {
16093
16404
  scopes: CODEX_HOOK_SCOPES,
16094
16405
  eventTypes: CODEX_HOOK_EVENT_TYPES,
16095
16406
  getSettingsPath(scope, cwd) {
16096
- const base = cwd ? resolve10(cwd) : process.cwd();
16407
+ const base = cwd ? resolve11(cwd) : process.cwd();
16097
16408
  switch (scope) {
16098
16409
  case "user":
16099
- return resolve10(homedir23(), ".codex", "hooks.json");
16410
+ return resolve11(homedir23(), ".codex", "hooks.json");
16100
16411
  case "project":
16101
- return resolve10(base, ".codex", "hooks.json");
16412
+ return resolve11(base, ".codex", "hooks.json");
16102
16413
  case "local":
16103
- return resolve10(base, ".codex", "hooks.json");
16414
+ return resolve11(base, ".codex", "hooks.json");
16104
16415
  }
16105
16416
  },
16106
16417
  readSettings(settingsPath) {
@@ -16209,14 +16520,14 @@ var init_integrations = __esm(() => {
16209
16520
  scopes: COPILOT_HOOK_SCOPES,
16210
16521
  eventTypes: COPILOT_HOOK_EVENT_TYPES,
16211
16522
  getSettingsPath(scope, cwd) {
16212
- const base = cwd ? resolve10(cwd) : process.cwd();
16523
+ const base = cwd ? resolve11(cwd) : process.cwd();
16213
16524
  switch (scope) {
16214
16525
  case "user":
16215
- return resolve10(homedir23(), ".copilot", "hooks", "failproofai.json");
16526
+ return resolve11(homedir23(), ".copilot", "hooks", "failproofai.json");
16216
16527
  case "project":
16217
- return resolve10(base, ".github", "hooks", "failproofai.json");
16528
+ return resolve11(base, ".github", "hooks", "failproofai.json");
16218
16529
  case "local":
16219
- return resolve10(base, ".github", "hooks", "failproofai.json");
16530
+ return resolve11(base, ".github", "hooks", "failproofai.json");
16220
16531
  }
16221
16532
  },
16222
16533
  readSettings(settingsPath) {
@@ -16323,14 +16634,14 @@ var init_integrations = __esm(() => {
16323
16634
  scopes: CURSOR_HOOK_SCOPES,
16324
16635
  eventTypes: CURSOR_HOOK_EVENT_TYPES,
16325
16636
  getSettingsPath(scope, cwd) {
16326
- const base = cwd ? resolve10(cwd) : process.cwd();
16637
+ const base = cwd ? resolve11(cwd) : process.cwd();
16327
16638
  switch (scope) {
16328
16639
  case "user":
16329
- return resolve10(homedir23(), ".cursor", "hooks.json");
16640
+ return resolve11(homedir23(), ".cursor", "hooks.json");
16330
16641
  case "project":
16331
- return resolve10(base, ".cursor", "hooks.json");
16642
+ return resolve11(base, ".cursor", "hooks.json");
16332
16643
  case "local":
16333
- return resolve10(base, ".cursor", "hooks.json");
16644
+ return resolve11(base, ".cursor", "hooks.json");
16334
16645
  }
16335
16646
  },
16336
16647
  readSettings(settingsPath) {
@@ -16422,14 +16733,14 @@ var init_integrations = __esm(() => {
16422
16733
  scopes: OPENCODE_HOOK_SCOPES,
16423
16734
  eventTypes: OPENCODE_HOOK_EVENT_TYPES,
16424
16735
  getSettingsPath(scope, cwd) {
16425
- const base = cwd ? resolve10(cwd) : process.cwd();
16736
+ const base = cwd ? resolve11(cwd) : process.cwd();
16426
16737
  switch (scope) {
16427
16738
  case "user":
16428
- return resolve10(homedir23(), ".config", "opencode", "opencode.json");
16739
+ return resolve11(homedir23(), ".config", "opencode", "opencode.json");
16429
16740
  case "project":
16430
- return resolve10(base, ".opencode", "opencode.json");
16741
+ return resolve11(base, ".opencode", "opencode.json");
16431
16742
  case "local":
16432
- return resolve10(base, ".opencode", "opencode.json");
16743
+ return resolve11(base, ".opencode", "opencode.json");
16433
16744
  }
16434
16745
  },
16435
16746
  readSettings(settingsPath) {
@@ -16440,7 +16751,7 @@ var init_integrations = __esm(() => {
16440
16751
  },
16441
16752
  buildHookEntry(_binaryPath, _eventType, scope) {
16442
16753
  if (scope === "user") {
16443
- const abs = resolve10(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
16754
+ const abs = resolve11(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
16444
16755
  return { spec: `file://${abs}`, [FAILPROOFAI_HOOK_MARKER]: true };
16445
16756
  }
16446
16757
  return { spec: OPENCODE_PLUGIN_REL_PATH, [FAILPROOFAI_HOOK_MARKER]: true };
@@ -16455,9 +16766,9 @@ var init_integrations = __esm(() => {
16455
16766
  writeHookEntries(settings, binaryPath, scope) {
16456
16767
  const s = settings;
16457
16768
  const effectiveScope = scope ?? "project";
16458
- const settingsPath = effectiveScope === "user" ? resolve10(homedir23(), ".config", "opencode", "opencode.json") : resolve10(process.cwd(), ".opencode", "opencode.json");
16769
+ const settingsPath = effectiveScope === "user" ? resolve11(homedir23(), ".config", "opencode", "opencode.json") : resolve11(process.cwd(), ".opencode", "opencode.json");
16459
16770
  const pluginPath = opencodePluginFilePath(settingsPath);
16460
- mkdirSync7(dirname4(pluginPath), { recursive: true });
16771
+ mkdirSync7(dirname5(pluginPath), { recursive: true });
16461
16772
  writeFileSync6(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
16462
16773
  if (!Array.isArray(s.plugin))
16463
16774
  s.plugin = [];
@@ -16523,14 +16834,14 @@ var init_integrations = __esm(() => {
16523
16834
  scopes: PI_HOOK_SCOPES,
16524
16835
  eventTypes: PI_HOOK_EVENT_TYPES,
16525
16836
  getSettingsPath(scope, cwd) {
16526
- const base = cwd ? resolve10(cwd) : process.cwd();
16837
+ const base = cwd ? resolve11(cwd) : process.cwd();
16527
16838
  switch (scope) {
16528
16839
  case "user":
16529
- return resolve10(homedir23(), ".pi", "agent", "settings.json");
16840
+ return resolve11(homedir23(), ".pi", "agent", "settings.json");
16530
16841
  case "project":
16531
- return resolve10(base, ".pi", "settings.json");
16842
+ return resolve11(base, ".pi", "settings.json");
16532
16843
  case "local":
16533
- return resolve10(base, ".pi", "settings.json");
16844
+ return resolve11(base, ".pi", "settings.json");
16534
16845
  }
16535
16846
  },
16536
16847
  readSettings(settingsPath) {
@@ -16608,7 +16919,10 @@ var init_integrations = __esm(() => {
16608
16919
  scopes: HERMES_HOOK_SCOPES,
16609
16920
  eventTypes: HERMES_HOOK_EVENT_TYPES,
16610
16921
  getSettingsPath() {
16611
- return resolve10(homedir23(), ".hermes", "config.yaml");
16922
+ return resolve11(hermesRoot(), "config.yaml");
16923
+ },
16924
+ getSettingsPaths() {
16925
+ return listHermesProfiles().map((p) => resolve11(p.home, "config.yaml"));
16612
16926
  },
16613
16927
  readSettings(settingsPath) {
16614
16928
  return readYamlDoc(settingsPath);
@@ -16675,21 +16989,8 @@ var init_integrations = __esm(() => {
16675
16989
  return removed;
16676
16990
  },
16677
16991
  hooksInstalledInSettings(scope, cwd) {
16678
- const settingsPath = this.getSettingsPath(scope, cwd);
16679
- if (!existsSync11(settingsPath))
16680
- return false;
16681
- try {
16682
- const doc = readYamlDoc(settingsPath);
16683
- const js = doc.toJS() ?? {};
16684
- const hooks = js.hooks;
16685
- if (!hooks || typeof hooks !== "object")
16686
- return false;
16687
- for (const entries of Object.values(hooks)) {
16688
- if (Array.isArray(entries) && entries.some((h) => isMarkedHook(h)))
16689
- return true;
16690
- }
16691
- } catch {}
16692
- return false;
16992
+ const paths = settingsPathsFor(this, scope, cwd);
16993
+ return paths.length > 0 && paths.every((p) => hermesConfigHasHooks(p));
16693
16994
  },
16694
16995
  detectInstalled() {
16695
16996
  return binaryExists("hermes");
@@ -16701,7 +17002,7 @@ var init_integrations = __esm(() => {
16701
17002
  scopes: OPENCLAW_HOOK_SCOPES,
16702
17003
  eventTypes: OPENCLAW_HOOK_EVENT_TYPES,
16703
17004
  getSettingsPath() {
16704
- return resolve10(homedir23(), ".openclaw", "openclaw.json");
17005
+ return resolve11(homedir23(), ".openclaw", "openclaw.json");
16705
17006
  },
16706
17007
  readSettings(settingsPath) {
16707
17008
  return readJsonFile(settingsPath);
@@ -16797,14 +17098,14 @@ var init_integrations = __esm(() => {
16797
17098
  scopes: FACTORY_HOOK_SCOPES,
16798
17099
  eventTypes: FACTORY_HOOK_EVENT_TYPES,
16799
17100
  getSettingsPath(scope, cwd) {
16800
- const base = cwd ? resolve10(cwd) : process.cwd();
17101
+ const base = cwd ? resolve11(cwd) : process.cwd();
16801
17102
  switch (scope) {
16802
17103
  case "user":
16803
- return resolve10(homedir23(), ".factory", "hooks.json");
17104
+ return resolve11(homedir23(), ".factory", "hooks.json");
16804
17105
  case "project":
16805
- return resolve10(base, ".factory", "hooks.json");
17106
+ return resolve11(base, ".factory", "hooks.json");
16806
17107
  case "local":
16807
- return resolve10(base, ".factory", "hooks.json");
17108
+ return resolve11(base, ".factory", "hooks.json");
16808
17109
  }
16809
17110
  },
16810
17111
  readSettings(settingsPath) {
@@ -16899,14 +17200,14 @@ var init_integrations = __esm(() => {
16899
17200
  scopes: DEVIN_HOOK_SCOPES,
16900
17201
  eventTypes: DEVIN_HOOK_EVENT_TYPES,
16901
17202
  getSettingsPath(scope, cwd) {
16902
- const base = cwd ? resolve10(cwd) : process.cwd();
17203
+ const base = cwd ? resolve11(cwd) : process.cwd();
16903
17204
  switch (scope) {
16904
17205
  case "user":
16905
- return resolve10(homedir23(), ".config", "devin", "config.json");
17206
+ return resolve11(homedir23(), ".config", "devin", "config.json");
16906
17207
  case "project":
16907
- return resolve10(base, ".devin", "config.json");
17208
+ return resolve11(base, ".devin", "config.json");
16908
17209
  case "local":
16909
- return resolve10(base, ".devin", "config.json");
17210
+ return resolve11(base, ".devin", "config.json");
16910
17211
  }
16911
17212
  },
16912
17213
  readSettings(settingsPath) {
@@ -17008,14 +17309,14 @@ var init_integrations = __esm(() => {
17008
17309
  scopes: ANTIGRAVITY_HOOK_SCOPES,
17009
17310
  eventTypes: ANTIGRAVITY_HOOK_EVENT_TYPES,
17010
17311
  getSettingsPath(scope, cwd) {
17011
- const base = cwd ? resolve10(cwd) : process.cwd();
17312
+ const base = cwd ? resolve11(cwd) : process.cwd();
17012
17313
  switch (scope) {
17013
17314
  case "user":
17014
- return resolve10(homedir23(), ".gemini", "config", "hooks.json");
17315
+ return resolve11(homedir23(), ".gemini", "config", "hooks.json");
17015
17316
  case "project":
17016
- return resolve10(base, ".agents", "hooks.json");
17317
+ return resolve11(base, ".agents", "hooks.json");
17017
17318
  case "local":
17018
- return resolve10(base, ".agents", "hooks.json");
17319
+ return resolve11(base, ".agents", "hooks.json");
17019
17320
  }
17020
17321
  },
17021
17322
  readSettings(settingsPath) {
@@ -17149,14 +17450,14 @@ var init_integrations = __esm(() => {
17149
17450
  scopes: GOOSE_HOOK_SCOPES,
17150
17451
  eventTypes: GOOSE_HOOK_EVENT_TYPES,
17151
17452
  getSettingsPath(scope, cwd) {
17152
- const base = cwd ? resolve10(cwd) : process.cwd();
17453
+ const base = cwd ? resolve11(cwd) : process.cwd();
17153
17454
  switch (scope) {
17154
17455
  case "user":
17155
- return resolve10(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17456
+ return resolve11(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17156
17457
  case "project":
17157
- return resolve10(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17458
+ return resolve11(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17158
17459
  case "local":
17159
- return resolve10(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17460
+ return resolve11(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
17160
17461
  }
17161
17462
  },
17162
17463
  readSettings(settingsPath) {
@@ -17491,7 +17792,7 @@ function runPrompt(p) {
17491
17792
  `${c.dim(BAR)} ${c.dim(p.summaryFor(result))}`
17492
17793
  ]);
17493
17794
  };
17494
- return new Promise((resolve11) => {
17795
+ return new Promise((resolve12) => {
17495
17796
  hideCursor(stdout);
17496
17797
  repaint(stdout, region, build());
17497
17798
  readline.emitKeypressEvents(stdin);
@@ -17507,7 +17808,7 @@ function runPrompt(p) {
17507
17808
  const finish = (result) => {
17508
17809
  cleanup();
17509
17810
  collapse(result);
17510
- resolve11(result);
17811
+ resolve12(result);
17511
17812
  };
17512
17813
  function onKey(_s, key) {
17513
17814
  if (!key)
@@ -17808,7 +18109,7 @@ async function promptPolicySelection(preSelected, options = {}) {
17808
18109
  `);
17809
18110
  lastLineCount = lines.length;
17810
18111
  }
17811
- return new Promise((resolve11) => {
18112
+ return new Promise((resolve12) => {
17812
18113
  render();
17813
18114
  process.stdin.setRawMode(true);
17814
18115
  process.stdin.resume();
@@ -17845,7 +18146,7 @@ async function promptPolicySelection(preSelected, options = {}) {
17845
18146
  ${dim(STEP_DONE)} Policies
17846
18147
  ${dim(BAR)} ${dim(`${selected.length} selected`)}
17847
18148
  `);
17848
- resolve11(selected);
18149
+ resolve12(selected);
17849
18150
  } else if (key.name === "escape") {
17850
18151
  search = "";
17851
18152
  cursor2 = 0;
@@ -17908,7 +18209,7 @@ __export(exports_manager, {
17908
18209
  });
17909
18210
  import { execSync as execSync6 } from "node:child_process";
17910
18211
  import { existsSync as existsSync12 } from "node:fs";
17911
- import { resolve as resolve11, basename as basename3 } from "node:path";
18212
+ import { resolve as resolve12, basename as basename4 } from "node:path";
17912
18213
  import { homedir as homedir24, platform as platform3, arch as arch3, release as release2, hostname as hostname3 } from "node:os";
17913
18214
  function getSettingsPath(scope, cwd) {
17914
18215
  return claudeCode.getSettingsPath(scope, cwd);
@@ -18014,34 +18315,45 @@ async function installHooksImpl(policyNames, scope = "user", cwd, includeBeta =
18014
18315
  const configToWrite = { ...previousConfig, enabledPolicies: selectedPolicies };
18015
18316
  if (removeCustomHooks) {
18016
18317
  delete configToWrite.customPoliciesPath;
18017
- } else if (customPoliciesPath) {
18018
- configToWrite.customPoliciesPath = resolve11(customPoliciesPath);
18019
- let validatedHooks = [];
18020
- try {
18021
- validatedHooks = await loadCustomHooks(configToWrite.customPoliciesPath, { strict: true });
18022
- } catch (err) {
18023
- const msg = err instanceof Error ? err.message : String(err);
18024
- try {
18025
- await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
18026
- scope,
18027
- error_type: /not found/i.test(msg) ? "file_not_found" : "load_error"
18028
- });
18029
- } catch {}
18030
- console.error(`Error: ${msg}`);
18031
- process.exit(1);
18032
- }
18033
- if (validatedHooks.length === 0) {
18318
+ delete configToWrite.customPoliciesPaths;
18319
+ } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
18320
+ const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve12(path3));
18321
+ const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve12(path3)).filter((path3) => {
18322
+ if (existsSync12(path3))
18323
+ return true;
18324
+ console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
18325
+ return false;
18326
+ });
18327
+ configToWrite.customPoliciesPaths = replace ? [...new Set(incoming)] : [...new Set([...carried, ...incoming])];
18328
+ delete configToWrite.customPoliciesPath;
18329
+ for (const path3 of incoming) {
18330
+ let validatedHooks = [];
18034
18331
  try {
18035
- await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
18036
- scope,
18037
- error_type: "no_hooks_registered"
18038
- });
18039
- } catch {}
18040
- console.error(`Error: no hooks registered in ${customPoliciesPath}. ` + `Make sure your file calls customPolicies.add(...) at least once.`);
18041
- process.exit(1);
18332
+ validatedHooks = await loadCustomHooks(path3, { strict: true });
18333
+ } catch (err) {
18334
+ const msg = err instanceof Error ? err.message : String(err);
18335
+ try {
18336
+ await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
18337
+ scope,
18338
+ error_type: /not found/i.test(msg) ? "file_not_found" : "load_error"
18339
+ });
18340
+ } catch {}
18341
+ console.error(`Error: ${msg}`);
18342
+ process.exit(1);
18343
+ }
18344
+ if (validatedHooks.length === 0) {
18345
+ try {
18346
+ await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
18347
+ scope,
18348
+ error_type: "no_hooks_registered"
18349
+ });
18350
+ } catch {}
18351
+ console.error(`Error: no hooks registered in ${path3}. ` + `Make sure your file calls customPolicies.add(...) at least once.`);
18352
+ process.exit(1);
18353
+ }
18354
+ console.log(`
18355
+ Validated ${validatedHooks.length} custom hook(s) from ${path3}: ${validatedHooks.map((h) => h.name).join(", ")}`);
18042
18356
  }
18043
- console.log(`
18044
- Validated ${validatedHooks.length} custom hook(s): ${validatedHooks.map((h) => h.name).join(", ")}`);
18045
18357
  }
18046
18358
  writeScopedHooksConfig(configToWrite, scope, cwd);
18047
18359
  console.log(`
@@ -18049,18 +18361,21 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
18049
18361
  `);
18050
18362
  if (removeCustomHooks) {
18051
18363
  console.log("Custom hooks path cleared.");
18052
- } else if (configToWrite.customPoliciesPath) {
18053
- console.log(`Custom hooks path: ${configToWrite.customPoliciesPath}`);
18364
+ } else if (configToWrite.customPoliciesPaths?.length || configToWrite.customPoliciesPath) {
18365
+ const paths = configToWrite.customPoliciesPaths ?? [configToWrite.customPoliciesPath];
18366
+ console.log(`Custom hooks paths: ${paths.join(", ")}`);
18054
18367
  }
18055
18368
  const writtenSettingsPaths = [];
18056
18369
  for (const cliId of selectedClis) {
18057
18370
  const integration = getIntegration(cliId);
18058
- const settingsPath = integration.getSettingsPath(scope, cwd);
18371
+ const settingsPaths = settingsPathsFor(integration, scope, cwd);
18059
18372
  try {
18060
- const settings = integration.readSettings(settingsPath);
18061
- integration.writeHookEntries(settings, binaryPath, scope);
18062
- integration.writeSettings(settingsPath, settings);
18063
- writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
18373
+ for (const settingsPath of settingsPaths) {
18374
+ const settings = integration.readSettings(settingsPath);
18375
+ integration.writeHookEntries(settings, binaryPath, scope);
18376
+ integration.writeSettings(settingsPath, settings);
18377
+ writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
18378
+ }
18064
18379
  } catch (err) {
18065
18380
  const errorType = err instanceof Error && /EACCES|EPERM/.test(err.message) ? "permission_denied" : err instanceof Error && /ENOENT|ENOTDIR/.test(err.message) ? "path_not_found" : "write_error";
18066
18381
  try {
@@ -18091,7 +18406,7 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
18091
18406
  arch: arch3(),
18092
18407
  os_release: release2(),
18093
18408
  hostname_hash: hashToId2(hostname3()),
18094
- has_custom_hooks_path: !!configToWrite.customPoliciesPath,
18409
+ has_custom_hooks_path: !!(configToWrite.customPoliciesPaths?.length || configToWrite.customPoliciesPath),
18095
18410
  has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
18096
18411
  param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
18097
18412
  command_format: scope === "project" ? "npx" : "absolute"
@@ -18146,6 +18461,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
18146
18461
  if (opts?.removeCustomHooks) {
18147
18462
  const config = readScopedHooksConfig(configScope, cwd);
18148
18463
  delete config.customPoliciesPath;
18464
+ delete config.customPoliciesPaths;
18149
18465
  writeScopedHooksConfig(config, configScope, cwd);
18150
18466
  console.log("Custom hooks path cleared.");
18151
18467
  }
@@ -18194,25 +18510,30 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
18194
18510
  const integration = getIntegration(cliId);
18195
18511
  const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
18196
18512
  for (const s of scopesToRemove) {
18197
- const settingsPath = integration.getSettingsPath(s, cwd);
18198
- if (!existsSync12(settingsPath)) {
18513
+ const settingsPaths = settingsPathsFor(integration, s, cwd);
18514
+ const existing = settingsPaths.filter((p) => existsSync12(p));
18515
+ if (existing.length === 0) {
18199
18516
  if (scope !== "all" && selectedClis.length === 1) {
18200
18517
  console.log("No settings file found. Nothing to remove.");
18201
18518
  nothingToReport = true;
18202
18519
  }
18203
18520
  continue;
18204
18521
  }
18205
- const removed = integration.removeHooksFromFile(settingsPath);
18206
- if (removed === 0 && scope !== "all" && selectedClis.length === 1) {
18522
+ let removedHere = 0;
18523
+ for (const settingsPath of existing) {
18524
+ const removed = integration.removeHooksFromFile(settingsPath);
18525
+ removedHere += removed;
18526
+ if (removed > 0 && scope !== "all") {
18527
+ console.log(`Removed ${removed} failproofai hook(s) from ${integration.displayName} settings.`);
18528
+ console.log(`Settings: ${settingsPath}`);
18529
+ }
18530
+ }
18531
+ if (removedHere === 0 && scope !== "all" && selectedClis.length === 1) {
18207
18532
  console.log("No hooks found in settings. Nothing to remove.");
18208
18533
  nothingToReport = true;
18209
18534
  continue;
18210
18535
  }
18211
- totalRemoved += removed;
18212
- if (scope !== "all") {
18213
- console.log(`Removed ${removed} failproofai hook(s) from ${integration.displayName} settings.`);
18214
- console.log(`Settings: ${settingsPath}`);
18215
- }
18536
+ totalRemoved += removedHere;
18216
18537
  }
18217
18538
  }
18218
18539
  if (nothingToReport && totalRemoved === 0)
@@ -18222,7 +18543,9 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
18222
18543
  for (const cliId of selectedClis) {
18223
18544
  const integration = getIntegration(cliId);
18224
18545
  for (const s of integration.scopes) {
18225
- console.log(` ${integration.displayName} / ${s}: ${integration.getSettingsPath(s, cwd)}`);
18546
+ for (const p of settingsPathsFor(integration, s, cwd)) {
18547
+ console.log(` ${integration.displayName} / ${s}: ${p}`);
18548
+ }
18226
18549
  }
18227
18550
  }
18228
18551
  }
@@ -18244,20 +18567,21 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
18244
18567
  if (scope === "all") {
18245
18568
  for (const s of HOOK_SCOPES) {
18246
18569
  const existing = readScopedHooksConfig(s, cwd);
18247
- if (existing.enabledPolicies.length > 0 || existing.customPoliciesPath || existing.policyParams) {
18248
- const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
18570
+ if (existing.enabledPolicies.length > 0 || existing.customPoliciesPaths?.length || existing.customPoliciesPath || existing.policyParams) {
18571
+ const { customPoliciesPath: _drop, customPoliciesPaths: _dropMany, policyParams: _dropParams, ...rest } = existing;
18249
18572
  writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, s, cwd);
18250
18573
  }
18251
18574
  }
18252
18575
  } else if (!HOOK_SCOPES.some((s) => hooksInstalledInSettings(s, cwd))) {
18253
18576
  const existing = readScopedHooksConfig(configScope, cwd);
18254
- const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
18577
+ const { customPoliciesPath: _drop, customPoliciesPaths: _dropMany, policyParams: _dropParams, ...rest } = existing;
18255
18578
  writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, configScope, cwd);
18256
18579
  }
18257
18580
  }
18258
18581
  async function listHooks(cwd) {
18259
18582
  const config = readMergedHooksConfig(cwd);
18260
18583
  const enabledSet = new Set(config.enabledPolicies);
18584
+ const disabledCustomSet = new Set(config.disabledCustomPolicies ?? []);
18261
18585
  const uniqueScopes = deduplicateScopes(HOOK_SCOPES, cwd);
18262
18586
  const installedScopes = uniqueScopes.filter((s) => hooksInstalledInSettings(s, cwd));
18263
18587
  const regularPolicies = BUILTIN_POLICIES.filter((p) => !p.beta);
@@ -18381,53 +18705,86 @@ Failproof AI Hook Policies
18381
18705
  } catch {}
18382
18706
  }
18383
18707
  }
18384
- if (config.customPoliciesPath) {
18708
+ const explicitPaths = configuredCustomPolicyPaths(config);
18709
+ if (explicitPaths.length > 0) {
18385
18710
  console.log(`
18386
- ── Custom Policies (${config.customPoliciesPath}) ───────────────────────`);
18387
- if (!existsSync12(config.customPoliciesPath)) {
18388
- console.log(` \x1B[31m✗ File not found: ${config.customPoliciesPath}\x1B[0m`);
18389
- } else {
18390
- const hooks = await loadCustomHooks(config.customPoliciesPath);
18711
+ ── Custom Policies ───────────────────────`);
18712
+ for (const path3 of explicitPaths) {
18713
+ const absPath = resolve12(findProjectConfigDir(cwd ?? process.cwd()), path3);
18714
+ console.log(` ${absPath}`);
18715
+ if (!existsSync12(absPath)) {
18716
+ console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
18717
+ continue;
18718
+ }
18719
+ const hooks = await loadCustomHooks(absPath);
18391
18720
  if (hooks.length === 0) {
18392
18721
  console.log(` \x1B[31m✗ ERR failed to load (check ~/.failproofai/logs/hooks.log)\x1B[0m`);
18393
18722
  } else {
18394
18723
  const descColWidth = nameColWidth;
18395
18724
  for (const hook of hooks) {
18396
- console.log(` \x1B[32m✓\x1B[0m ${hook.name.padEnd(descColWidth)}${hook.description ?? ""}`);
18725
+ const disabled = disabledCustomSet.has(`custom:${absPath}:${hook.name}`);
18726
+ const status = disabled ? "\x1B[2m OFF\x1B[0m" : "\x1B[32m✓ ON\x1B[0m";
18727
+ console.log(` ${status} ${hook.name.padEnd(descColWidth)}${hook.description ?? ""}`);
18397
18728
  }
18398
18729
  }
18399
18730
  }
18400
18731
  console.log();
18401
18732
  }
18402
- const base = cwd ? resolve11(cwd) : process.cwd();
18733
+ const base = findProjectConfigDir(cwd ?? process.cwd());
18734
+ const projectDir = resolve12(base, ".failproofai", "policies");
18735
+ const userDir = resolve12(homedir24(), ".failproofai", "policies");
18736
+ const sameDir = userDir === projectDir;
18403
18737
  const conventionDirs = [
18404
- { label: "Project", dir: resolve11(base, ".failproofai", "policies") },
18405
- { label: "User", dir: resolve11(homedir24(), ".failproofai", "policies") }
18738
+ { label: sameDir ? "Project + User" : "Project", dir: projectDir },
18739
+ ...sameDir ? [] : [{ label: "User", dir: userDir }]
18406
18740
  ];
18741
+ const discovered = {
18742
+ project: [],
18743
+ user: []
18744
+ };
18407
18745
  for (const { label, dir } of conventionDirs) {
18408
18746
  const files = discoverPolicyFiles(dir);
18409
18747
  if (files.length === 0)
18410
18748
  continue;
18749
+ const targets = dir === projectDir && dir === userDir ? ["project", "user"] : dir === projectDir ? ["project"] : ["user"];
18750
+ const policyScope = dir === projectDir ? "project" : "user";
18751
+ const record = (file, hooks) => {
18752
+ for (const t of targets)
18753
+ discovered[t].push({ file, hooks });
18754
+ };
18411
18755
  console.log(`
18412
18756
  ── Convention Policies — ${label} (${dir}) ──────────`);
18757
+ const colWidth = Math.max(nameColWidth, ...files.map((f) => basename4(f).length + 2));
18413
18758
  for (const file of files) {
18414
18759
  try {
18415
18760
  const hooks = await loadCustomHooks(file);
18761
+ const filename = basename4(file);
18762
+ record(filename, hooks.map((h) => h.name));
18416
18763
  if (hooks.length === 0) {
18417
- const filename = basename3(file);
18418
- console.log(` \x1B[31m✗\x1B[0m ${filename.padEnd(nameColWidth)}\x1B[31mfailed to load\x1B[0m`);
18764
+ console.log(` \x1B[31m✗\x1B[0m ${filename.padEnd(colWidth)}\x1B[31mfailed to load\x1B[0m`);
18419
18765
  } else {
18420
- const filename = basename3(file);
18421
- const hookSummary = hooks.map((h) => h.name).join(", ");
18422
- console.log(` \x1B[32m✓\x1B[0m ${filename.padEnd(nameColWidth)}${hooks.length} hook(s): ${hookSummary}`);
18766
+ const hookStates = hooks.map((hook) => ({
18767
+ hook,
18768
+ disabled: disabledCustomSet.has(`convention:${policyScope}:${filename}:${hook.name}`)
18769
+ }));
18770
+ const disabledCount = hookStates.filter((entry) => entry.disabled).length;
18771
+ const status = disabledCount === 0 ? "\x1B[32m✓ ON\x1B[0m" : disabledCount === hooks.length ? "\x1B[2m OFF\x1B[0m" : "\x1B[33m◐ MIXED\x1B[0m";
18772
+ const hookSummary = hookStates.map(({ hook, disabled }) => `${hook.name}${disabled ? " (OFF)" : ""}`).join(", ");
18773
+ console.log(` ${status} ${filename.padEnd(colWidth)}${hooks.length} hook(s): ${hookSummary}`);
18423
18774
  }
18424
18775
  } catch {
18425
- const filename = basename3(file);
18426
- console.log(` \x1B[31m✗\x1B[0m ${filename.padEnd(nameColWidth)}\x1B[31merror\x1B[0m`);
18776
+ const filename = basename4(file);
18777
+ record(filename, []);
18778
+ console.log(` \x1B[31m✗\x1B[0m ${filename.padEnd(colWidth)}\x1B[31merror\x1B[0m`);
18427
18779
  }
18428
18780
  }
18429
18781
  console.log();
18430
18782
  }
18783
+ try {
18784
+ syncConventionPolicies(discovered.user, "user");
18785
+ } catch (err) {
18786
+ hookLogWarn(`could not record convention policies in config: ${err instanceof Error ? err.message : String(err)}`);
18787
+ }
18431
18788
  }
18432
18789
  var VALID_POLICY_NAMES;
18433
18790
  var init_manager = __esm(() => {
@@ -18440,6 +18797,7 @@ var init_manager = __esm(() => {
18440
18797
  init_hook_telemetry2();
18441
18798
  init_telemetry_id2();
18442
18799
  init_cli_error();
18800
+ init_hook_logger();
18443
18801
  VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
18444
18802
  });
18445
18803
 
@@ -18604,7 +18962,7 @@ async function promptCliTargetSelection(detected, action = "install") {
18604
18962
  lastLineCount = lines.length;
18605
18963
  }
18606
18964
  const itemCount = options.length;
18607
- return new Promise((resolve12) => {
18965
+ return new Promise((resolve13) => {
18608
18966
  render();
18609
18967
  readline3.emitKeypressEvents(process.stdin);
18610
18968
  const wasRaw = process.stdin.isRaw;
@@ -18637,7 +18995,7 @@ async function promptCliTargetSelection(detected, action = "install") {
18637
18995
  cleanup();
18638
18996
  process.stdout.write(`
18639
18997
  `);
18640
- resolve12(options[cursor2].value);
18998
+ resolve13(options[cursor2].value);
18641
18999
  }
18642
19000
  }
18643
19001
  process.stdin.on("keypress", onKey);
@@ -18790,7 +19148,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
18790
19148
  `);
18791
19149
  lastLineCount = lines.length;
18792
19150
  }
18793
- return new Promise((resolve12) => {
19151
+ return new Promise((resolve13) => {
18794
19152
  render();
18795
19153
  process.stdin.setRawMode(true);
18796
19154
  process.stdin.resume();
@@ -18827,7 +19185,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
18827
19185
  ${dim(STEP_DONE)} Policies
18828
19186
  ${dim(BAR)} ${dim(`${selected.length} selected`)}
18829
19187
  `);
18830
- resolve12(selected);
19188
+ resolve13(selected);
18831
19189
  } else if (key.name === "escape") {
18832
19190
  search = "";
18833
19191
  cursor2 = 0;
@@ -19014,12 +19372,12 @@ import {
19014
19372
  statSync as statSync11,
19015
19373
  writeFileSync as writeFileSync7
19016
19374
  } from "node:fs";
19017
- import { dirname as dirname5 } from "node:path";
19375
+ import { dirname as dirname6 } from "node:path";
19018
19376
  import { randomBytes } from "node:crypto";
19019
19377
  function writeJsonAtomically(filePath, value, opts = {}) {
19020
19378
  const mode = opts.mode ?? 384;
19021
19379
  const dirMode = opts.dirMode ?? 448;
19022
- const dir = dirname5(filePath);
19380
+ const dir = dirname6(filePath);
19023
19381
  if (!existsSync13(dir))
19024
19382
  mkdirSync8(dir, { recursive: true, mode: dirMode });
19025
19383
  const tmp = `${filePath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
@@ -19046,15 +19404,15 @@ var init_atomic_write = () => {};
19046
19404
  // lib/auth/auth-store.ts
19047
19405
  import { existsSync as existsSync14, readFileSync as readFileSync13, rmSync as rmSync2 } from "node:fs";
19048
19406
  import { homedir as homedir25 } from "node:os";
19049
- import { join as join25 } from "node:path";
19407
+ import { join as join26 } from "node:path";
19050
19408
  function getAuthDir() {
19051
19409
  const override = process.env.FAILPROOFAI_AUTH_DIR;
19052
19410
  if (override)
19053
19411
  return override;
19054
- return join25(homedir25(), ".failproofai");
19412
+ return join26(homedir25(), ".failproofai");
19055
19413
  }
19056
19414
  function getAuthFilePath() {
19057
- return join25(getAuthDir(), "auth.json");
19415
+ return join26(getAuthDir(), "auth.json");
19058
19416
  }
19059
19417
  function readAuth() {
19060
19418
  const p = getAuthFilePath();
@@ -19162,13 +19520,13 @@ function prompt(question, opts = {}) {
19162
19520
  orig(s);
19163
19521
  };
19164
19522
  }
19165
- return new Promise((resolve12) => {
19523
+ return new Promise((resolve13) => {
19166
19524
  rl.question(question, (answer) => {
19167
19525
  rl.close();
19168
19526
  if (opts.hidden && process.stdin.isTTY)
19169
19527
  process.stdout.write(`
19170
19528
  `);
19171
- resolve12(answer.trim());
19529
+ resolve13(answer.trim());
19172
19530
  });
19173
19531
  });
19174
19532
  }
@@ -19417,8 +19775,8 @@ EXAMPLES
19417
19775
  });
19418
19776
 
19419
19777
  // lib/claude-sessions.ts
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";
19778
+ import { readdirSync as readdirSync11, statSync as statSync12, existsSync as existsSync15 } from "node:fs";
19779
+ import { join as join27, basename as basename5 } from "node:path";
19422
19780
  function getClaudeProjectsRoot() {
19423
19781
  return getClaudeProjectsPath();
19424
19782
  }
@@ -19426,21 +19784,21 @@ function listClaudeProjects() {
19426
19784
  const root = getClaudeProjectsRoot();
19427
19785
  let entries;
19428
19786
  try {
19429
- entries = readdirSync10(root, { withFileTypes: true });
19787
+ entries = readdirSync11(root, { withFileTypes: true });
19430
19788
  } catch {
19431
19789
  return [];
19432
19790
  }
19433
19791
  return entries.filter((e) => e.isDirectory()).map((e) => ({
19434
19792
  name: e.name,
19435
19793
  cwd: decodeFolderName(e.name),
19436
- path: join26(root, e.name)
19794
+ path: join27(root, e.name)
19437
19795
  }));
19438
19796
  }
19439
19797
  function listClaudeTranscripts(project) {
19440
19798
  const out = [];
19441
19799
  let entries;
19442
19800
  try {
19443
- entries = readdirSync10(project.path, { withFileTypes: true });
19801
+ entries = readdirSync11(project.path, { withFileTypes: true });
19444
19802
  } catch {
19445
19803
  return out;
19446
19804
  }
@@ -19449,7 +19807,7 @@ function listClaudeTranscripts(project) {
19449
19807
  const sessionId = entry.name.slice(0, -".jsonl".length);
19450
19808
  if (!UUID_RE2.test(sessionId))
19451
19809
  continue;
19452
- const transcriptPath = join26(project.path, entry.name);
19810
+ const transcriptPath = join27(project.path, entry.name);
19453
19811
  try {
19454
19812
  const s = statSync12(transcriptPath);
19455
19813
  out.push({
@@ -19463,12 +19821,12 @@ function listClaudeTranscripts(project) {
19463
19821
  });
19464
19822
  } catch {}
19465
19823
  } else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
19466
- const subDir = join26(project.path, entry.name, "subagents");
19824
+ const subDir = join27(project.path, entry.name, "subagents");
19467
19825
  if (!existsSync15(subDir))
19468
19826
  continue;
19469
19827
  let subEntries;
19470
19828
  try {
19471
- subEntries = readdirSync10(subDir, { withFileTypes: true });
19829
+ subEntries = readdirSync11(subDir, { withFileTypes: true });
19472
19830
  } catch {
19473
19831
  continue;
19474
19832
  }
@@ -19476,7 +19834,7 @@ function listClaudeTranscripts(project) {
19476
19834
  if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
19477
19835
  continue;
19478
19836
  const agentId = sub.name.slice(0, -".jsonl".length);
19479
- const transcriptPath = join26(subDir, sub.name);
19837
+ const transcriptPath = join27(subDir, sub.name);
19480
19838
  try {
19481
19839
  const s = statSync12(transcriptPath);
19482
19840
  out.push({
@@ -20051,7 +20409,7 @@ async function listHermesTranscriptMetadata(opts = {}) {
20051
20409
  continue;
20052
20410
  out.push({
20053
20411
  cli: "hermes",
20054
- projectName: s.source ? `hermes:${s.source}` : "hermes",
20412
+ projectName: s.source ? hermesProjectPath(s.profile, s.source) : `hermes:${s.profile}`,
20055
20413
  sessionId: s.sessionId,
20056
20414
  transcriptPath: `hermes://${s.sessionId}`,
20057
20415
  mtimeMs: s.mtimeMs,
@@ -20073,6 +20431,7 @@ async function streamHermesEvents(meta) {
20073
20431
  }
20074
20432
  var init_hermes = __esm(() => {
20075
20433
  init_hermes_projects();
20434
+ init_hermes_profiles();
20076
20435
  init_hermes_sessions();
20077
20436
  init_shared();
20078
20437
  });
@@ -20083,16 +20442,16 @@ async function listOpenClawTranscriptMetadata(opts = {}) {
20083
20442
  return [];
20084
20443
  const sinceMs = opts.sinceMs ?? 0;
20085
20444
  const out = [];
20086
- for (const t of listOpenClawTranscripts()) {
20087
- if (t.mtimeMs < sinceMs)
20445
+ for (const s of await getOpenClawSessions()) {
20446
+ if (s.mtimeMs < sinceMs)
20088
20447
  continue;
20089
20448
  out.push({
20090
20449
  cli: "openclaw",
20091
- projectName: `openclaw:${t.agentId}`,
20092
- sessionId: t.sessionId,
20093
- transcriptPath: t.transcriptPath,
20094
- mtimeMs: t.mtimeMs,
20095
- sizeBytes: t.sizeBytes
20450
+ projectName: openClawProjectPath(s.agentId, s.channel),
20451
+ sessionId: s.sessionId,
20452
+ transcriptPath: s.transcriptPath,
20453
+ mtimeMs: s.mtimeMs,
20454
+ sizeBytes: s.sizeBytes
20096
20455
  });
20097
20456
  }
20098
20457
  return out;
@@ -20109,6 +20468,7 @@ async function streamOpenClawEvents(meta) {
20109
20468
  });
20110
20469
  }
20111
20470
  var init_openclaw = __esm(() => {
20471
+ init_openclaw_projects();
20112
20472
  init_openclaw_sessions();
20113
20473
  init_shared();
20114
20474
  });
@@ -20731,7 +21091,7 @@ var init_features = __esm(() => {
20731
21091
  // src/audit/cache.ts
20732
21092
  import { createHash } from "node:crypto";
20733
21093
  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";
21094
+ import { join as join28 } from "node:path";
20735
21095
  import { homedir as homedir26 } from "node:os";
20736
21096
  function getEngineVersion() {
20737
21097
  if (cachedEngineVersion)
@@ -20750,9 +21110,9 @@ function getDetectorVersion() {
20750
21110
  return cachedDetectorVersion;
20751
21111
  }
20752
21112
  function getCachePathFor(transcriptPath) {
20753
- const root = join27(homedir26(), ".failproofai", "cache", "audit");
21113
+ const root = join28(homedir26(), ".failproofai", "cache", "audit");
20754
21114
  const key = createHash("sha1").update(transcriptPath).digest("hex");
20755
- return join27(root, `${key}.json`);
21115
+ return join28(root, `${key}.json`);
20756
21116
  }
20757
21117
  function readCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes) {
20758
21118
  if (sizeBytes === 0)
@@ -20785,7 +21145,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
20785
21145
  return;
20786
21146
  const cachePath = getCachePathFor(transcriptPath);
20787
21147
  try {
20788
- mkdirSync9(join27(homedir26(), ".failproofai", "cache", "audit"), { recursive: true });
21148
+ mkdirSync9(join28(homedir26(), ".failproofai", "cache", "audit"), { recursive: true });
20789
21149
  const entry = {
20790
21150
  schemaVersion: CACHE_SCHEMA_VERSION,
20791
21151
  cachedAt: Date.now(),
@@ -21157,10 +21517,10 @@ var init_audit = __esm(() => {
21157
21517
  });
21158
21518
 
21159
21519
  // src/audit/dashboard-cache.ts
21160
- import { join as join28 } from "node:path";
21520
+ import { join as join29 } from "node:path";
21161
21521
  import { homedir as homedir27 } from "node:os";
21162
21522
  function getCachePath() {
21163
- return join28(homedir27(), ".failproofai", "audit-dashboard.json");
21523
+ return join29(homedir27(), ".failproofai", "audit-dashboard.json");
21164
21524
  }
21165
21525
  function writeDashboardCache(params, result) {
21166
21526
  try {
@@ -21250,14 +21610,14 @@ var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
21250
21610
  var init_open_browser = () => {};
21251
21611
 
21252
21612
  // scripts/parse-script-args.ts
21253
- import { resolve as resolve12 } from "path";
21613
+ import { resolve as resolve13 } from "path";
21254
21614
  function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
21255
21615
  const raw = inlineValue ?? args[index + 1];
21256
21616
  if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
21257
21617
  console.error(`Error: ${flagName} requires ${errorLabel}`);
21258
21618
  process.exit(1);
21259
21619
  }
21260
- const value = options?.resolve ? resolve12(raw) : raw;
21620
+ const value = options?.resolve ? resolve13(raw) : raw;
21261
21621
  return { value, spliceCount: inlineValue !== null ? 1 : 2 };
21262
21622
  }
21263
21623
  function parseScriptArgs(argv) {
@@ -21306,17 +21666,17 @@ var init_parse_script_args = () => {};
21306
21666
 
21307
21667
  // scripts/install-diagnosis.mjs
21308
21668
  import { existsSync as existsSync17, readFileSync as readFileSync15, realpathSync } from "node:fs";
21309
- import { dirname as dirname6, resolve as resolve13 } from "node:path";
21669
+ import { dirname as dirname7, resolve as resolve14 } from "node:path";
21310
21670
  import { homedir as homedir28, platform as platform4 } from "node:os";
21311
21671
  import { spawnSync } from "node:child_process";
21312
21672
  function findPackageRoot(start) {
21313
21673
  try {
21314
21674
  let dir = realpathSync(start);
21315
- if (existsSync17(dir) && !existsSync17(resolve13(dir, "package.json"))) {
21316
- dir = dirname6(dir);
21675
+ if (existsSync17(dir) && !existsSync17(resolve14(dir, "package.json"))) {
21676
+ dir = dirname7(dir);
21317
21677
  }
21318
21678
  while (true) {
21319
- const pkgPath = resolve13(dir, "package.json");
21679
+ const pkgPath = resolve14(dir, "package.json");
21320
21680
  if (existsSync17(pkgPath)) {
21321
21681
  try {
21322
21682
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
@@ -21324,7 +21684,7 @@ function findPackageRoot(start) {
21324
21684
  return dir;
21325
21685
  } catch {}
21326
21686
  }
21327
- const parent = dirname6(dir);
21687
+ const parent = dirname7(dir);
21328
21688
  if (parent === dir)
21329
21689
  return null;
21330
21690
  dir = parent;
@@ -21337,7 +21697,7 @@ function readPackageVersion(packageRoot) {
21337
21697
  if (!packageRoot)
21338
21698
  return null;
21339
21699
  try {
21340
- const pkg = JSON.parse(readFileSync15(resolve13(packageRoot, "package.json"), "utf8"));
21700
+ const pkg = JSON.parse(readFileSync15(resolve14(packageRoot, "package.json"), "utf8"));
21341
21701
  return typeof pkg.version === "string" ? pkg.version : null;
21342
21702
  } catch {
21343
21703
  return null;
@@ -21363,16 +21723,16 @@ function locateNpmGlobal() {
21363
21723
  const root = (res.stdout || "").trim();
21364
21724
  if (!root)
21365
21725
  return null;
21366
- const candidate = resolve13(root, PKG_NAME);
21367
- return existsSync17(resolve13(candidate, "package.json")) ? candidate : null;
21726
+ const candidate = resolve14(root, PKG_NAME);
21727
+ return existsSync17(resolve14(candidate, "package.json")) ? candidate : null;
21368
21728
  } catch {
21369
21729
  return null;
21370
21730
  }
21371
21731
  }
21372
21732
  function locateBunGlobal() {
21373
21733
  try {
21374
- const candidate = resolve13(homedir28(), ".bun", "install", "global", "node_modules", PKG_NAME);
21375
- return existsSync17(resolve13(candidate, "package.json")) ? candidate : null;
21734
+ const candidate = resolve14(homedir28(), ".bun", "install", "global", "node_modules", PKG_NAME);
21735
+ return existsSync17(resolve14(candidate, "package.json")) ? candidate : null;
21376
21736
  } catch {
21377
21737
  return null;
21378
21738
  }
@@ -21380,8 +21740,8 @@ function locateBunGlobal() {
21380
21740
  function buildRecommendation(pathFirstBin) {
21381
21741
  if (!pathFirstBin)
21382
21742
  return null;
21383
- const bunBinPrefix = resolve13(homedir28(), ".bun", "bin") + "/";
21384
- const bunGlobalPrefix = resolve13(homedir28(), ".bun", "install", "global") + "/";
21743
+ const bunBinPrefix = resolve14(homedir28(), ".bun", "bin") + "/";
21744
+ const bunGlobalPrefix = resolve14(homedir28(), ".bun", "install", "global") + "/";
21385
21745
  const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
21386
21746
  if (isBun) {
21387
21747
  return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
@@ -21462,7 +21822,7 @@ __export(exports_launch, {
21462
21822
  });
21463
21823
  import { spawn as spawn2 } from "child_process";
21464
21824
  import { realpathSync as realpathSync2, existsSync as existsSync18 } from "node:fs";
21465
- import { resolve as resolve14, dirname as dirname7 } from "node:path";
21825
+ import { resolve as resolve15, dirname as dirname8 } from "node:path";
21466
21826
  import { fileURLToPath as fileURLToPath2 } from "node:url";
21467
21827
  import { createInterface as createInterface2 } from "node:readline";
21468
21828
  function launch(mode) {
@@ -21477,8 +21837,8 @@ function launch(mode) {
21477
21837
  process.env.PORT = port;
21478
21838
  process.env.HOSTNAME = "0.0.0.0";
21479
21839
  cmd = "node";
21480
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve14(dirname7(realpathSync2(fileURLToPath2(import.meta.url))), "..");
21481
- const serverJsPath = resolve14(packageRoot, ".next/standalone/server.js");
21840
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve15(dirname8(realpathSync2(fileURLToPath2(import.meta.url))), "..");
21841
+ const serverJsPath = resolve15(packageRoot, ".next/standalone/server.js");
21482
21842
  if (!existsSync18(serverJsPath)) {
21483
21843
  let shadowMessage = null;
21484
21844
  try {
@@ -21517,6 +21877,7 @@ Error: Cannot find server.js at:
21517
21877
  stdio: filterLogs ? ["inherit", "pipe", "pipe"] : "inherit",
21518
21878
  env: {
21519
21879
  ...process.env,
21880
+ FAILPROOFAI_LAUNCH_CWD: process.env.FAILPROOFAI_LAUNCH_CWD ?? process.cwd(),
21520
21881
  ...filterLogs ? { FORCE_COLOR: process.env.FORCE_COLOR ?? "1" } : {},
21521
21882
  ...loggingLevel ? { FAILPROOFAI_LOG_LEVEL: loggingLevel } : {},
21522
21883
  ...disableTelemetry ? { FAILPROOFAI_TELEMETRY_DISABLED: "1" } : {},
@@ -21830,7 +22191,8 @@ WHAT IT DOES
21830
22191
 
21831
22192
  // src/hooks/manager.ts
21832
22193
  import { execSync as execSync7 } from "node:child_process";
21833
- import { resolve as resolve15, basename as basename5 } from "node:path";
22194
+ import { existsSync as existsSync19 } from "node:fs";
22195
+ import { resolve as resolve16, basename as basename6 } from "node:path";
21834
22196
  import { homedir as homedir29, platform as platform5, arch as arch4, release as release3, hostname as hostname4 } from "node:os";
21835
22197
  function getSettingsPath2(scope, cwd) {
21836
22198
  return claudeCode.getSettingsPath(scope, cwd);
@@ -21936,34 +22298,45 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
21936
22298
  const configToWrite = { ...previousConfig, enabledPolicies: selectedPolicies };
21937
22299
  if (removeCustomHooks) {
21938
22300
  delete configToWrite.customPoliciesPath;
21939
- } else if (customPoliciesPath) {
21940
- configToWrite.customPoliciesPath = resolve15(customPoliciesPath);
21941
- let validatedHooks = [];
21942
- try {
21943
- validatedHooks = await loadCustomHooks(configToWrite.customPoliciesPath, { strict: true });
21944
- } catch (err) {
21945
- const msg = err instanceof Error ? err.message : String(err);
21946
- try {
21947
- await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
21948
- scope,
21949
- error_type: /not found/i.test(msg) ? "file_not_found" : "load_error"
21950
- });
21951
- } catch {}
21952
- console.error(`Error: ${msg}`);
21953
- process.exit(1);
21954
- }
21955
- if (validatedHooks.length === 0) {
22301
+ delete configToWrite.customPoliciesPaths;
22302
+ } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
22303
+ const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve16(path3));
22304
+ const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve16(path3)).filter((path3) => {
22305
+ if (existsSync19(path3))
22306
+ return true;
22307
+ console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
22308
+ return false;
22309
+ });
22310
+ configToWrite.customPoliciesPaths = replace ? [...new Set(incoming)] : [...new Set([...carried, ...incoming])];
22311
+ delete configToWrite.customPoliciesPath;
22312
+ for (const path3 of incoming) {
22313
+ let validatedHooks = [];
21956
22314
  try {
21957
- await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
21958
- scope,
21959
- error_type: "no_hooks_registered"
21960
- });
21961
- } catch {}
21962
- console.error(`Error: no hooks registered in ${customPoliciesPath}. ` + `Make sure your file calls customPolicies.add(...) at least once.`);
21963
- process.exit(1);
22315
+ validatedHooks = await loadCustomHooks(path3, { strict: true });
22316
+ } catch (err) {
22317
+ const msg = err instanceof Error ? err.message : String(err);
22318
+ try {
22319
+ await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
22320
+ scope,
22321
+ error_type: /not found/i.test(msg) ? "file_not_found" : "load_error"
22322
+ });
22323
+ } catch {}
22324
+ console.error(`Error: ${msg}`);
22325
+ process.exit(1);
22326
+ }
22327
+ if (validatedHooks.length === 0) {
22328
+ try {
22329
+ await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
22330
+ scope,
22331
+ error_type: "no_hooks_registered"
22332
+ });
22333
+ } catch {}
22334
+ console.error(`Error: no hooks registered in ${path3}. ` + `Make sure your file calls customPolicies.add(...) at least once.`);
22335
+ process.exit(1);
22336
+ }
22337
+ console.log(`
22338
+ Validated ${validatedHooks.length} custom hook(s) from ${path3}: ${validatedHooks.map((h) => h.name).join(", ")}`);
21964
22339
  }
21965
- console.log(`
21966
- Validated ${validatedHooks.length} custom hook(s): ${validatedHooks.map((h) => h.name).join(", ")}`);
21967
22340
  }
21968
22341
  writeScopedHooksConfig(configToWrite, scope, cwd);
21969
22342
  console.log(`
@@ -21971,18 +22344,21 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
21971
22344
  `);
21972
22345
  if (removeCustomHooks) {
21973
22346
  console.log("Custom hooks path cleared.");
21974
- } else if (configToWrite.customPoliciesPath) {
21975
- console.log(`Custom hooks path: ${configToWrite.customPoliciesPath}`);
22347
+ } else if (configToWrite.customPoliciesPaths?.length || configToWrite.customPoliciesPath) {
22348
+ const paths = configToWrite.customPoliciesPaths ?? [configToWrite.customPoliciesPath];
22349
+ console.log(`Custom hooks paths: ${paths.join(", ")}`);
21976
22350
  }
21977
22351
  const writtenSettingsPaths = [];
21978
22352
  for (const cliId of selectedClis) {
21979
22353
  const integration = getIntegration(cliId);
21980
- const settingsPath = integration.getSettingsPath(scope, cwd);
22354
+ const settingsPaths = settingsPathsFor(integration, scope, cwd);
21981
22355
  try {
21982
- const settings = integration.readSettings(settingsPath);
21983
- integration.writeHookEntries(settings, binaryPath, scope);
21984
- integration.writeSettings(settingsPath, settings);
21985
- writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
22356
+ for (const settingsPath of settingsPaths) {
22357
+ const settings = integration.readSettings(settingsPath);
22358
+ integration.writeHookEntries(settings, binaryPath, scope);
22359
+ integration.writeSettings(settingsPath, settings);
22360
+ writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
22361
+ }
21986
22362
  } catch (err) {
21987
22363
  const errorType = err instanceof Error && /EACCES|EPERM/.test(err.message) ? "permission_denied" : err instanceof Error && /ENOENT|ENOTDIR/.test(err.message) ? "path_not_found" : "write_error";
21988
22364
  try {
@@ -22013,7 +22389,7 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
22013
22389
  arch: arch4(),
22014
22390
  os_release: release3(),
22015
22391
  hostname_hash: hashToId2(hostname4()),
22016
- has_custom_hooks_path: !!configToWrite.customPoliciesPath,
22392
+ has_custom_hooks_path: !!(configToWrite.customPoliciesPaths?.length || configToWrite.customPoliciesPath),
22017
22393
  has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
22018
22394
  param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
22019
22395
  command_format: scope === "project" ? "npx" : "absolute"
@@ -22073,6 +22449,7 @@ var init_manager2 = __esm(() => {
22073
22449
  init_hook_telemetry2();
22074
22450
  init_telemetry_id2();
22075
22451
  init_cli_error();
22452
+ init_hook_logger();
22076
22453
  VALID_POLICY_NAMES2 = new Set(BUILTIN_POLICIES.map((p) => p.name));
22077
22454
  });
22078
22455
 
@@ -22411,9 +22788,9 @@ __export(exports_configure_wizard, {
22411
22788
  buildPresetChoices: () => buildPresetChoices,
22412
22789
  buildAgentChoices: () => buildAgentChoices
22413
22790
  });
22414
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync16 } from "node:fs";
22791
+ import { existsSync as existsSync20, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync16 } from "node:fs";
22415
22792
  import { homedir as homedir30 } from "node:os";
22416
- import { resolve as resolve16, sep as sep5 } from "node:path";
22793
+ import { resolve as resolve17, sep as sep5 } from "node:path";
22417
22794
  async function emit(event, props) {
22418
22795
  try {
22419
22796
  await trackHookEvent2(getInstanceId2(), event, props);
@@ -22470,12 +22847,21 @@ function buildAgentChoices(scope, cwd) {
22470
22847
  } catch {
22471
22848
  installedHere = false;
22472
22849
  }
22850
+ let partialHint;
22851
+ if (id === "hermes" && !installedHere) {
22852
+ try {
22853
+ const unhooked = unhookedHermesProfiles();
22854
+ if (unhooked.length > 0) {
22855
+ partialHint = `${unhooked.length} unhooked profile(s): ${unhooked.join(", ")}`;
22856
+ }
22857
+ } catch {}
22858
+ }
22473
22859
  return {
22474
22860
  label: integration.displayName,
22475
22861
  value: id,
22476
22862
  checked: isDetected || installedHere,
22477
22863
  section: isDetected ? "Detected" : "Not installed · set up ahead of time",
22478
- hint: installedHere ? "already configured" : isDetected ? undefined : "not on PATH"
22864
+ hint: installedHere ? "already configured" : partialHint ?? (isDetected ? undefined : "not on PATH")
22479
22865
  };
22480
22866
  });
22481
22867
  }
@@ -22525,7 +22911,7 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
22525
22911
  const path3 = getConfigPathForScope(scope, cwd);
22526
22912
  let config = {};
22527
22913
  try {
22528
- if (existsSync19(path3))
22914
+ if (existsSync20(path3))
22529
22915
  config = JSON.parse(readFileSync16(path3, "utf8"));
22530
22916
  } catch {
22531
22917
  return;
@@ -22545,8 +22931,8 @@ function describeCustomPolicies(cwd) {
22545
22931
  const scopes = [];
22546
22932
  let fileCount = 0;
22547
22933
  const dirs = [
22548
- { dir: resolve16(cwd, ".failproofai", "policies"), label: "project" },
22549
- { dir: resolve16(homedir30(), ".failproofai", "policies"), label: "global" }
22934
+ { dir: resolve17(cwd, ".failproofai", "policies"), label: "project" },
22935
+ { dir: resolve17(homedir30(), ".failproofai", "policies"), label: "global" }
22550
22936
  ];
22551
22937
  for (const { dir, label } of dirs) {
22552
22938
  const found = discoverPolicyFiles(dir);
@@ -22556,7 +22942,7 @@ function describeCustomPolicies(cwd) {
22556
22942
  fileCount += found.length;
22557
22943
  }
22558
22944
  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")}`);
22945
+ warnings.push(`! ${homeify(resolve17(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
22560
22946
  }
22561
22947
  }
22562
22948
  return { active, warnings, fileCount, scopes };
@@ -22579,20 +22965,22 @@ function reviewLines(state) {
22579
22965
  lines.push(" This will update:");
22580
22966
  for (const cli of clis) {
22581
22967
  const integration = getIntegration(cli);
22582
- lines.push(` ${homeify(integration.getSettingsPath(scope, cwd))} ${integration.displayName} hooks`);
22968
+ for (const p of settingsPathsFor(integration, scope, cwd)) {
22969
+ lines.push(` ${homeify(p)} ${integration.displayName} hooks`);
22970
+ }
22583
22971
  }
22584
22972
  lines.push(` ${homeify(getConfigPathForScope(scope, cwd))} ${policies.length} policies`);
22585
22973
  return lines;
22586
22974
  }
22587
22975
  function firstRunMarkerPath() {
22588
- return resolve16(homedir30(), ".failproofai", ".launcher-configured");
22976
+ return resolve17(homedir30(), ".failproofai", ".launcher-configured");
22589
22977
  }
22590
22978
  function hasSeenLauncher() {
22591
- return existsSync19(firstRunMarkerPath());
22979
+ return existsSync20(firstRunMarkerPath());
22592
22980
  }
22593
22981
  function markLauncherSeen() {
22594
22982
  try {
22595
- mkdirSync10(resolve16(homedir30(), ".failproofai"), { recursive: true });
22983
+ mkdirSync10(resolve17(homedir30(), ".failproofai"), { recursive: true });
22596
22984
  writeFileSync9(firstRunMarkerPath(), "1", "utf8");
22597
22985
  } catch {}
22598
22986
  }
@@ -22742,8 +23130,8 @@ __export(exports_launch2, {
22742
23130
  launch: () => launch2
22743
23131
  });
22744
23132
  import { spawn as spawn3 } from "child_process";
22745
- import { realpathSync as realpathSync3, existsSync as existsSync20 } from "node:fs";
22746
- import { resolve as resolve17, dirname as dirname8 } from "node:path";
23133
+ import { realpathSync as realpathSync3, existsSync as existsSync21 } from "node:fs";
23134
+ import { resolve as resolve18, dirname as dirname9 } from "node:path";
22747
23135
  import { fileURLToPath as fileURLToPath3 } from "node:url";
22748
23136
  import { createInterface as createInterface3 } from "node:readline";
22749
23137
  function launch2(mode) {
@@ -22758,9 +23146,9 @@ function launch2(mode) {
22758
23146
  process.env.PORT = port;
22759
23147
  process.env.HOSTNAME = "0.0.0.0";
22760
23148
  cmd = "node";
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)) {
23149
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve18(dirname9(realpathSync3(fileURLToPath3(import.meta.url))), "..");
23150
+ const serverJsPath = resolve18(packageRoot, ".next/standalone/server.js");
23151
+ if (!existsSync21(serverJsPath)) {
22764
23152
  let shadowMessage = null;
22765
23153
  try {
22766
23154
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -22798,6 +23186,7 @@ Error: Cannot find server.js at:
22798
23186
  stdio: filterLogs ? ["inherit", "pipe", "pipe"] : "inherit",
22799
23187
  env: {
22800
23188
  ...process.env,
23189
+ FAILPROOFAI_LAUNCH_CWD: process.env.FAILPROOFAI_LAUNCH_CWD ?? process.cwd(),
22801
23190
  ...filterLogs ? { FORCE_COLOR: process.env.FORCE_COLOR ?? "1" } : {},
22802
23191
  ...loggingLevel ? { FAILPROOFAI_LOG_LEVEL: loggingLevel } : {},
22803
23192
  ...disableTelemetry ? { FAILPROOFAI_TELEMETRY_DISABLED: "1" } : {},
@@ -22854,17 +23243,17 @@ var init_cli_error2 = __esm(() => {
22854
23243
 
22855
23244
  // bin/failproofai.mjs
22856
23245
  import { realpathSync as realpathSync4 } from "fs";
22857
- import { dirname as dirname9, resolve as resolve18 } from "path";
23246
+ import { dirname as dirname10, resolve as resolve19 } from "path";
22858
23247
  import { fileURLToPath as fileURLToPath4 } from "url";
22859
23248
  // package.json
22860
- var version = "0.0.14";
23249
+ var version = "0.0.15-beta.1";
22861
23250
 
22862
23251
  // bin/failproofai.mjs
22863
23252
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
22864
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve18(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..");
23253
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve19(dirname10(realpathSync4(fileURLToPath4(import.meta.url))), "..");
22865
23254
  }
22866
23255
  if (!process.env.FAILPROOFAI_DIST_PATH) {
22867
- process.env.FAILPROOFAI_DIST_PATH = resolve18(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
23256
+ process.env.FAILPROOFAI_DIST_PATH = resolve19(dirname10(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
22868
23257
  }
22869
23258
  var args = process.argv.slice(2);
22870
23259
  if (args[0] === "p")
@@ -22951,7 +23340,7 @@ COMMANDS
22951
23340
  --scope user|project|local Config scope to write to (default: user)
22952
23341
  (Codex / Copilot / Cursor / OpenCode / Pi support user|project only)
22953
23342
  --beta Include beta policies
22954
- --custom, -c <path> Path to a JS file of custom policies
23343
+ --custom, -c <path> Custom policy file (repeat for multiple files)
22955
23344
 
22956
23345
  policies --uninstall, -u Disable policies or remove hooks
22957
23346
  [names...] Specific policy names to disable
@@ -22959,7 +23348,7 @@ COMMANDS
22959
23348
  Agent CLI(s) to uninstall from
22960
23349
  --scope user|project|local|all Config scope to remove from (default: user)
22961
23350
  --beta Remove only beta policies
22962
- --custom, -c Clear the customPoliciesPath from config
23351
+ --custom, -c Clear all explicit custom policy paths
22963
23352
 
22964
23353
  policies --help, -h Show this help for the policies command
22965
23354
 
@@ -23043,7 +23432,7 @@ OPTIONS (install)
23043
23432
  --scope user|project|local Config scope to write to (default: user)
23044
23433
  (Codex / Copilot / Cursor / OpenCode / Pi support user|project only)
23045
23434
  --beta Include beta policies
23046
- --custom, -c <path> Path to a JS file of custom policies
23435
+ --custom, -c <path> Custom policy file (repeat for multiple files)
23047
23436
  (skips interactive prompt; validates file first)
23048
23437
 
23049
23438
  OPTIONS (uninstall)
@@ -23052,7 +23441,7 @@ OPTIONS (uninstall)
23052
23441
  Agent CLI(s) to uninstall from
23053
23442
  --scope user|project|local|all Config scope to remove from (default: user)
23054
23443
  --beta Remove only beta policies
23055
- --custom, -c Clear the customPoliciesPath from config
23444
+ --custom, -c Clear all explicit custom policy paths
23056
23445
 
23057
23446
  EXAMPLES
23058
23447
  failproofai policies
@@ -23067,6 +23456,7 @@ EXAMPLES
23067
23456
  failproofai policies --install --cli devin --scope project
23068
23457
  failproofai policies --install --cli claude codex copilot cursor opencode pi hermes openclaw factory devin antigravity goose
23069
23458
  failproofai policies --install --custom ./my-policies.js
23459
+ failproofai policies --install --custom ./security.js --custom ./workflow.js
23070
23460
  failproofai policies -i -c ./my-policies.js
23071
23461
  failproofai policies --uninstall block-sudo
23072
23462
  failproofai policies --uninstall --cli codex
@@ -23091,9 +23481,9 @@ EXAMPLES
23091
23481
  if (scopeIdx >= 0 && !["user", "project", "local"].includes(scope)) {
23092
23482
  throw new CliError3(`Invalid scope: ${scope}. Valid values: user, project, local`);
23093
23483
  }
23094
- const customIdx = subArgs.includes("--custom") ? subArgs.indexOf("--custom") : subArgs.includes("-c") ? subArgs.indexOf("-c") : -1;
23095
- const customPoliciesPath = customIdx >= 0 ? subArgs[customIdx + 1] : undefined;
23096
- if (customIdx >= 0 && (!customPoliciesPath || customPoliciesPath.startsWith("-"))) {
23484
+ const customIdxs = subArgs.map((arg, index) => arg === "--custom" || arg === "-c" ? index : -1).filter((index) => index >= 0);
23485
+ const customPoliciesPaths = customIdxs.map((index) => subArgs[index + 1]);
23486
+ if (customPoliciesPaths.some((path3) => !path3 || path3.startsWith("-"))) {
23097
23487
  throw new CliError3(`Missing path after --custom/-c
23098
23488
  Usage: --custom <path> (e.g. --custom ./my-policies.js)`);
23099
23489
  }
@@ -23121,7 +23511,7 @@ Usage: --custom <path> (e.g. --custom ./my-policies.js)`);
23121
23511
  const consumedIdxs = new Set;
23122
23512
  if (scopeIdx >= 0)
23123
23513
  consumedIdxs.add(scopeIdx + 1);
23124
- if (customIdx >= 0)
23514
+ for (const customIdx of customIdxs)
23125
23515
  consumedIdxs.add(customIdx + 1);
23126
23516
  for (const i of cliConsumedIdxs)
23127
23517
  consumedIdxs.add(i);
@@ -23132,16 +23522,16 @@ Usage: --custom <path> (e.g. --custom ./my-policies.js)`);
23132
23522
  Run \`failproofai policies --help\` for usage.`);
23133
23523
  }
23134
23524
  const explicitPolicyNames = subArgs.filter((a, idx) => !a.startsWith("-") && !consumedIdxs.has(idx));
23135
- const policyNames = explicitPolicyNames.length > 0 ? explicitPolicyNames : customPoliciesPath !== undefined ? [] : undefined;
23525
+ const policyNames = explicitPolicyNames.length > 0 ? explicitPolicyNames : customPoliciesPaths.length > 0 ? [] : undefined;
23136
23526
  const cli = await resolveTargetClis2(cliFlagValues.length > 0 ? cliFlagValues : undefined, "install");
23137
- await installHooks3(policyNames, scope, undefined, includeBeta, undefined, customPoliciesPath, false, cli);
23527
+ await installHooks3(policyNames, scope, undefined, includeBeta, undefined, customPoliciesPaths.length > 0 ? customPoliciesPaths : undefined, false, cli);
23138
23528
  await track("cli_install_success", {
23139
23529
  scope,
23140
23530
  cli,
23141
23531
  cli_count: cli.length,
23142
23532
  explicit_policies: explicitPolicyNames.length > 0,
23143
23533
  include_beta: includeBeta,
23144
- has_custom_path: !!customPoliciesPath
23534
+ has_custom_path: customPoliciesPaths.length > 0
23145
23535
  });
23146
23536
  process.exit(0);
23147
23537
  }