failproofai 1.0.10 → 1.0.11-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) 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 +4 -4
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  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 +4 -4
  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.meta +8 -0
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  17. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page.js +4 -4
  19. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.meta +8 -0
  23. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  26. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/invite/route.js +4 -4
  28. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
  30. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
  34. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -4
  36. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -3
  38. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -3
  40. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  42. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  44. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  45. package/.next/standalone/.next/server/app/audit/page.js +4 -4
  46. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/index.html +1 -1
  49. package/.next/standalone/.next/server/app/index.meta +8 -0
  50. package/.next/standalone/.next/server/app/index.rsc +14 -14
  51. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  52. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  53. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  54. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  55. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  56. package/.next/standalone/.next/server/app/page.js +4 -4
  57. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  60. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  61. package/.next/standalone/.next/server/app/policies/page.js +8 -8
  62. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  65. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/page.js +5 -5
  67. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +5 -5
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  76. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  77. package/.next/standalone/.next/server/app/projects/page.js +5 -5
  78. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +3 -3
  81. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  82. package/.next/standalone/.next/server/app/settings/page.js +9 -9
  83. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  85. package/.next/standalone/.next/server/chunks/{[externals]__0qxhecv._.js → [externals]__0d7i9ea._.js} +2 -2
  86. package/.next/standalone/.next/server/chunks/{[externals]__0sg9-3p._.js → [externals]__0hjaw70._.js} +1 -1
  87. package/.next/standalone/.next/server/chunks/[externals]__1_9ze9b._.js +3 -0
  88. package/.next/standalone/.next/server/chunks/[externals]__1oenfmn._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ga9hs7._.js +3 -0
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l3yhx4._.js +2 -2
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0moxohc._.js +22 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0z2ll-a._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +11 -11
  96. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1autwno._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1v25n-6._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +2 -2
  102. package/.next/standalone/.next/server/chunks/_1fz8m31._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/_1jrv3hg._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/_1nx8fim._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/_1ogwigf._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/{_1q5i8mb._.js → _1q27azx._.js} +2 -2
  107. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0el6rg8._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0ol5pa3._.js} +2 -2
  109. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1rqmk27._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1w7tiq4._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1-bm5zf._.js → [root-of-the-server]__0502lat._.js} +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_gn5iz._.js +4 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0f765xr._.js +4 -0
  116. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01bmjsj._.js → [root-of-the-server]__0h38gzz._.js} +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hoaz1g._.js +4 -0
  118. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ouobn4._.js → [root-of-the-server]__0mxmwtx._.js} +1 -1
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__19evfi8._.js → [root-of-the-server]__0uescqv._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1484zv2._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1mf3zp6._.js → [root-of-the-server]__19dutr7._.js} +4 -4
  122. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0oqrl71._.js → [root-of-the-server]__1_qv8oh._.js} +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1u6sn69._.js → [root-of-the-server]__1ab4ifb._.js} +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1fbrk56._.js +4 -0
  125. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +2 -2
  126. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1um4yxn._.js +4 -0
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1wb4yt2._.js +4 -0
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1y3n3cn._.js +4 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/{_16pa9ht._.js → _0lnpwk8._.js} +2 -2
  132. package/.next/standalone/.next/server/chunks/ssr/{_0yq5_3a._.js → _15y8myg._.js} +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/_1gb0ifp._.js +2 -2
  134. package/.next/standalone/.next/server/chunks/ssr/{_042cgl1._.js → _1o95rf0._.js} +2 -2
  135. package/.next/standalone/.next/server/chunks/ssr/{_1zopuov._.js → _1thr5rf._.js} +2 -2
  136. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +12 -14
  137. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +1 -1
  138. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/ssr/node_modules_1hl81y4._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0nkkbfv._.js +6 -0
  144. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  145. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  146. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  147. package/.next/standalone/.next/server/middleware.js +1 -1
  148. package/.next/standalone/.next/server/pages/404.html +1 -1
  149. package/.next/standalone/.next/server/pages/500.html +1 -1
  150. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  151. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  152. package/.next/standalone/.next/static/chunks/{03h3b-uomi7_d.js → 0pra9i676pym1.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/{2kj826h1w9r47.js → 1-lpi0m-hc1kb.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{3ob2161ap37ul.js → 17yvyyblr_eb5.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{3nkc2jlgyygda.js → 1mylcr99gccyz.js} +1 -1
  156. package/.next/standalone/.next/static/chunks/{2j92xul6y24_v.js → 1wozchtn2etvh.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{3mjfvng11xnoo.js → 23thtelz1a-ur.js} +2 -2
  158. package/.next/standalone/.next/static/chunks/{1fqukzzowjflt.js → 2bc17tohgs4bs.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/{3u1s8dsqiean5.js → 2xq-ejo8kti18.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/3-_pth_c9n938.js +1 -0
  161. package/.next/standalone/.next/static/chunks/{3i8rg2fv9nd6e.js → 3n46e45atic-m.js} +1 -1
  162. package/.next/standalone/.next/static/chunks/{078dofaxvjgpd.js → 3q7sndqftqhw9.js} +1 -1
  163. package/.next/standalone/.next/static/chunks/{0542cuk_va8g1.js → 3sg07wnn969xq.js} +1 -1
  164. package/.next/standalone/.next/static/chunks/{turbopack-11dn2pegmg9_9.js → turbopack-1opxka4dtjbc5.js} +1 -1
  165. package/.next/standalone/app/actions/install-hooks-web.ts +10 -2
  166. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  167. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  168. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  169. package/.next/standalone/node_modules/next/dist/compiled/hash.js/sha256/256.js +1 -0
  170. package/.next/standalone/node_modules/next/dist/compiled/hash.js/sha256/package.json +1 -0
  171. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +17 -17
  172. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +18 -18
  173. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +3 -3
  174. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +13 -13
  175. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  176. package/.next/standalone/node_modules/next/dist/server/base-server.js +1 -2
  177. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  178. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  179. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  180. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +265 -53
  181. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  182. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js +177 -19
  183. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/index.js +36 -17
  184. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/shared-cache-controls.external.js +14 -6
  185. package/.next/standalone/node_modules/next/dist/server/lib/match-next-data-pathname.js +3 -1
  186. package/.next/standalone/node_modules/next/dist/server/lib/node-fs-methods.js +14 -0
  187. package/.next/standalone/node_modules/next/dist/server/lib/route-cache-key.js +77 -0
  188. package/.next/standalone/node_modules/next/dist/server/lib/router-utils/block-cross-site-dev.js +9 -11
  189. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  190. package/.next/standalone/node_modules/next/dist/server/mcp/get-mcp-middleware.js +4 -2
  191. package/.next/standalone/node_modules/next/dist/server/next-server.js +7 -3
  192. package/.next/standalone/node_modules/next/dist/server/response-cache/index.js +32 -16
  193. package/.next/standalone/node_modules/next/dist/server/route-matcher-providers/app-page-route-matcher-provider.js +2 -2
  194. package/.next/standalone/node_modules/next/dist/server/route-modules/pages/pages-handler.js +6 -1
  195. package/.next/standalone/node_modules/next/dist/server/route-modules/route-module.js +9 -2
  196. package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +30 -29
  197. package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/app-paths.js +18 -1
  198. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  199. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  200. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  201. package/.next/standalone/node_modules/next/package.json +10 -10
  202. package/.next/standalone/package.json +14 -11
  203. package/.next/standalone/sdk/python/README.md +41 -0
  204. package/.next/standalone/sdk/python/docs/README.md +1 -0
  205. package/.next/standalone/sdk/python/docs/otel/README.md +36 -0
  206. package/.next/standalone/sdk/python/docs/otel/examples/agent_trace.py +30 -0
  207. package/.next/standalone/sdk/python/docs/otel/examples/quickstart.py +22 -0
  208. package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +8 -0
  209. package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +3 -0
  210. package/.next/standalone/sdk/python/failproofai_sdk/integrations/otel.py +189 -0
  211. package/.next/standalone/sdk/python/pyproject.toml +2 -1
  212. package/.next/standalone/sdk/python/tests/test_docs.py +16 -3
  213. package/.next/standalone/sdk/python/tests/test_integrations.py +1 -1
  214. package/.next/standalone/sdk/python/tests/test_otel.py +147 -0
  215. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +4 -1
  216. package/.next/standalone/sdk/python/uv.lock +258 -147
  217. package/.next/standalone/sdk/typescript/README.md +43 -0
  218. package/.next/standalone/sdk/typescript/integration/ai.test.ts +9 -2
  219. package/.next/standalone/sdk/typescript/package-lock.json +57 -5
  220. package/.next/standalone/sdk/typescript/package.json +2 -0
  221. package/.next/standalone/sdk/typescript/src/edge/index.ts +9 -0
  222. package/.next/standalone/sdk/typescript/src/index.ts +2 -0
  223. package/.next/standalone/sdk/typescript/src/integrations/index.ts +4 -1
  224. package/.next/standalone/sdk/typescript/src/integrations/otel.ts +204 -0
  225. package/.next/standalone/sdk/typescript/test/integrations.test.ts +1 -1
  226. package/.next/standalone/sdk/typescript/test/otel.test.ts +125 -0
  227. package/.next/standalone/server.js +1 -1
  228. package/bin/failproofai.mjs +653 -1077
  229. package/dist/cli.mjs +8781 -4050
  230. package/dist/worker.mjs +170 -62
  231. package/hermes-plugin/plugin.yaml +1 -1
  232. package/package.json +14 -11
  233. package/scripts/launch-facts.ts +157 -0
  234. package/scripts/launch-ready.ts +228 -0
  235. package/scripts/launch-screen.ts +202 -0
  236. package/scripts/launch.ts +106 -49
  237. package/scripts/skew-log-filter.ts +38 -0
  238. package/src/audit/cli-login.ts +39 -59
  239. package/src/audit/cli.ts +308 -228
  240. package/src/audit/index.ts +45 -2
  241. package/src/audit/schedule-cli.ts +83 -120
  242. package/src/audit/types.ts +31 -0
  243. package/src/hooks/agent-selection.ts +155 -0
  244. package/src/hooks/backfill-cli.ts +151 -137
  245. package/src/hooks/backfill-request.ts +149 -0
  246. package/src/hooks/cloud-connection.ts +1 -1
  247. package/src/hooks/cloud-enrollment-cli.ts +78 -28
  248. package/src/hooks/cloud-introspect.ts +3 -0
  249. package/src/hooks/collector-config.ts +10 -1
  250. package/src/hooks/config-status.ts +717 -0
  251. package/src/hooks/configure-wizard.ts +967 -988
  252. package/src/hooks/daemon-service.ts +16 -6
  253. package/src/hooks/did-you-mean.ts +185 -0
  254. package/src/hooks/flush-cli.ts +33 -7
  255. package/src/hooks/fp-config.ts +96 -0
  256. package/src/hooks/fp-home.ts +11 -0
  257. package/src/hooks/fp-reset.ts +102 -95
  258. package/src/hooks/handler.ts +4 -0
  259. package/src/hooks/harness-cli.ts +87 -138
  260. package/src/hooks/install-prompt.ts +38 -477
  261. package/src/hooks/integrations.ts +1 -1
  262. package/src/hooks/jev-cli.ts +183 -196
  263. package/src/hooks/manager.ts +554 -358
  264. package/src/hooks/onboarding-attempt.ts +11 -9
  265. package/src/hooks/otel-cli.ts +350 -0
  266. package/src/hooks/otel-output.ts +24 -0
  267. package/src/hooks/otel-writers.ts +215 -0
  268. package/src/hooks/pack-cli.ts +219 -204
  269. package/src/hooks/report-body.ts +50 -0
  270. package/src/hooks/safe-config-write.ts +4 -2
  271. package/src/hooks/session-pause-cli.ts +93 -73
  272. package/src/hooks/tui.ts +849 -389
  273. package/src/hooks/uninstall-cli.ts +136 -149
  274. package/src/hooks/untraced-note.ts +52 -0
  275. package/.next/standalone/.next/server/chunks/[externals]__19_pzeq._.js +0 -3
  276. package/.next/standalone/.next/server/chunks/[externals]__20kzpkf._.js +0 -3
  277. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +0 -3
  278. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05ja_05._.js +0 -3
  279. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0igxt32._.js +0 -3
  280. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +0 -3
  281. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ttrwnd._.js +0 -22
  282. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +0 -3
  283. package/.next/standalone/.next/server/chunks/_185gyk9._.js +0 -3
  284. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +0 -4
  285. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +0 -4
  286. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +0 -4
  287. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +0 -3
  288. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +0 -4
  289. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +0 -4
  290. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +0 -4
  291. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +0 -4
  292. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +0 -6
  293. package/.next/standalone/.next/static/chunks/1lyg_puttohbc.js +0 -1
  294. /package/.next/standalone/.next/static/{ECs8__hcNabYV58WfDh-3 → xtcpqBNBdjQVerPMkI2HI}/_buildManifest.js +0 -0
  295. /package/.next/standalone/.next/static/{ECs8__hcNabYV58WfDh-3 → xtcpqBNBdjQVerPMkI2HI}/_clientMiddlewareManifest.js +0 -0
  296. /package/.next/standalone/.next/static/{ECs8__hcNabYV58WfDh-3 → xtcpqBNBdjQVerPMkI2HI}/_ssgManifest.js +0 -0
@@ -1,48 +1,19 @@
1
1
  /**
2
- * Interactive searchable multi-select prompt for choosing hook policies.
3
- * Uses raw mode stdin with node:readline for keypress handling.
4
- * No external dependencies.
2
+ * Which agent CLIs a `policies --install / --uninstall` acts on, asked with the
3
+ * shared `selectOne` when more than one is detected.
5
4
  *
6
- * Rendering strategy: track line count, use cursor-up + clear-to-end-of-screen
7
- * (\x1B[NA\x1B[J) to avoid flickering. Lines are truncated to terminal width to
8
- * ensure lastLineCount stays accurate when the terminal is narrow.
9
- *
10
- * Keybindings: ↑↓ navigate · Space toggle · Ctrl+A all · Ctrl+S save · Esc clear search
5
+ * This file used to carry two hand-rolled raw-mode menus of its own: this one,
6
+ * with a two-write (non-atomic) repaint and an escape-gate that ignored pipes,
7
+ * and a searchable policy picker that nothing had called since `--install`
8
+ * stopped choosing policies. The picker is gone and the menu is `selectOne`,
9
+ * so it draws, windows and repaints like every other prompt in the CLI.
11
10
  */
12
- import * as readline from "node:readline";
13
- // Brand palette, ANSI-aware truncation, and glyphs shared with the configure
14
- // wizard (tui.ts) so every failproofai menu reads as one system.
15
- import {
16
- paint,
17
- truncate as truncateLine,
18
- BAR,
19
- STEP_ACTIVE as STEP,
20
- STEP_DONE as DONE,
21
- CHECK_ON as BOX_ON,
22
- CHECK_OFF as BOX_OFF,
23
- } from "./tui";
24
- import { BUILTIN_POLICIES } from "./builtin-policies";
11
+ import { colorsEnabled, optsFor, screenKit, selectOne, type SelectChoice } from "./tui";
25
12
  import { detectInstalledClis, getIntegration, listInstallableIds } from "./integrations";
26
13
  import { type IntegrationType } from "./types";
27
14
  import { trackHookEvent } from "./hook-telemetry";
28
15
  import { getInstanceId } from "../../lib/telemetry-id";
29
16
 
30
- interface SelectItem {
31
- name: string;
32
- description: string;
33
- category: string;
34
- selected: boolean;
35
- beta: boolean;
36
- }
37
-
38
- type DisplayRow =
39
- | { kind: "header"; category: string; enabledCount: number; totalCount: number }
40
- | { kind: "item"; item: SelectItem; filteredIndex: number };
41
-
42
- export interface PromptOptions {
43
- includeBeta?: boolean;
44
- }
45
-
46
17
  /** Whether the prompt is being shown for an install or an uninstall flow.
47
18
  * Drives heading + hint text so `policies --uninstall` no longer says
48
19
  * "Install Hooks". */
@@ -93,15 +64,17 @@ export async function resolveTargetClis(
93
64
  // Uninstall flow: no agent CLIs detected — nothing to remove from. Default to
94
65
  // claude so removeHooks operates over Claude's scopes (no-op if no settings file).
95
66
  console.log(
96
- "\x1B[33mWarning: no agent CLI binary found in PATH (claude, codex, copilot, cursor-agent, opencode, pi)." +
97
- "Defaulting to Claude Code; nothing will be removed if no settings file exists.\x1B[0m",
67
+ screenKit({ color: colorsEnabled(process.stdout) }).caution(
68
+ "No agent CLI was found on PATH, so this acts on Claude Code. Nothing is removed if it has no settings file.",
69
+ ),
98
70
  );
99
71
  fireDetectionEvent(["claude"], "defaulted_to_claude");
100
72
  return ["claude"];
101
73
  }
102
74
  console.log(
103
- "\x1B[33mWarning: no agent CLI binary found in PATH (claude, codex, copilot, cursor-agent, opencode, pi)." +
104
- "Defaulting to Claude Code; hooks will activate when an agent is installed.\x1B[0m",
75
+ screenKit({ color: colorsEnabled(process.stdout) }).caution(
76
+ "No agent CLI was found on PATH, so hooks go into Claude Code. They take effect once it is installed.",
77
+ ),
105
78
  );
106
79
  fireDetectionEvent(["claude"], "defaulted_to_claude");
107
80
  return ["claude"];
@@ -159,9 +132,8 @@ export function buildCliMenuOptions(
159
132
 
160
133
  const options: CliMenuOption[] = [];
161
134
  if (detected.length > 1) {
162
- const verb = action === "uninstall" ? "Remove from" : "Install for";
163
135
  options.push({
164
- label: `${verb} all ${detected.length} detected`,
136
+ label: `All ${detected.length} agents`,
165
137
  value: detected,
166
138
  detected: true,
167
139
  isAll: true,
@@ -187,446 +159,35 @@ export function buildCliMenuOptions(
187
159
  }
188
160
 
189
161
  /**
190
- * Interactive arrow-key single-select for "install/remove for which CLI?" when
191
- * multiple agent CLIs are detected.
162
+ * "Install / remove hooks for which agent?" when more than one is detected.
192
163
  *
193
- * Layout:
194
- * • DETECTED section: an "Install for all detected" option (only when >1
195
- * detected) followed by each detected CLI individually.
196
- * • NOT INSTALLED section (install action only): each undetected CLI as a
197
- * forward-install option, so users can prep hooks before adding the CLI.
198
- *
199
- * Cursor skips section headers — it only lands on selectable item rows.
164
+ * Detected agents first — with an "All N agents" row when there are several —
165
+ * then, for an install, every undetected agent as a forward install, so hooks
166
+ * can be in place before the agent is. Esc exits 130, as it always has: the
167
+ * caller has no "cancelled" path, and an install that quietly picked something
168
+ * would be worse.
200
169
  */
201
170
  async function promptCliTargetSelection(
202
171
  detected: IntegrationType[],
203
172
  action: CliPromptAction = "install",
204
173
  ): Promise<IntegrationType[]> {
205
- const { options, undetected } = buildCliMenuOptions(detected, action);
206
-
207
- type DisplayRow =
208
- | { kind: "header"; title: string; hint?: string }
209
- | { kind: "blank" }
210
- | { kind: "item"; option: CliMenuOption; itemIndex: number };
211
-
212
- function buildDisplayRows(): DisplayRow[] {
213
- const rows: DisplayRow[] = [];
214
- let itemIndex = 0;
215
-
216
- rows.push({
217
- kind: "header",
218
- title: `Detected (${detected.length})`,
219
- });
220
- for (const opt of options) {
221
- if (opt.detected) {
222
- rows.push({ kind: "item", option: opt, itemIndex: itemIndex++ });
223
- }
224
- }
225
-
226
- if (undetected.length > 0) {
227
- rows.push({ kind: "blank" });
228
- rows.push({
229
- kind: "header",
230
- title: `Not installed (${undetected.length})`,
231
- hint: "install hooks ahead of time",
232
- });
233
- for (const opt of options) {
234
- if (!opt.detected) {
235
- rows.push({ kind: "item", option: opt, itemIndex: itemIndex++ });
236
- }
237
- }
238
- }
239
-
240
- return rows;
241
- }
242
-
243
- let cursor = 0;
244
- let lastLineCount = 0;
245
- let cursorHidden = false;
246
-
247
- function hideCursor(): void {
248
- if (!cursorHidden) {
249
- process.stdout.write("\x1B[?25l");
250
- cursorHidden = true;
251
- }
252
- }
253
- function showCursor(): void {
254
- if (cursorHidden) {
255
- process.stdout.write("\x1B[?25h");
256
- cursorHidden = false;
257
- }
258
- }
259
-
260
- // Shared brand painter from tui.ts (NO_COLOR-gated like the rest of this file).
261
- const { dim, bold, guide: teal, pink, pinkBold } = paint(!process.env.NO_COLOR);
262
-
263
- const heading =
264
- action === "uninstall" ? "Remove failproofai hooks" : "Install failproofai hooks";
265
-
266
- function render(): void {
267
- const cols = process.stdout.columns || 100;
268
- hideCursor();
269
-
270
- const g = dim(BAR);
271
- const lines: string[] = [g, `${teal(STEP)} ${bold(heading)}`];
272
-
273
- for (const row of buildDisplayRows()) {
274
- if (row.kind === "blank") {
275
- lines.push(g);
276
- continue;
277
- }
278
- if (row.kind === "header") {
279
- const hint = row.hint ? ` ${dim("· " + row.hint)}` : "";
280
- lines.push(`${g} ${dim(row.title)}${hint}`);
281
- continue;
282
- }
283
-
284
- const opt = row.option;
285
- const isActive = row.itemIndex === cursor;
286
- const caret = isActive ? pink("❯") : " ";
287
- const marker = opt.isAll ? teal("★") : opt.detected ? pink("●") : dim("○");
288
- const label = isActive
289
- ? pinkBold(opt.label)
290
- : opt.detected
291
- ? opt.label
292
- : dim(opt.label);
293
- lines.push(`${g} ${caret} ${marker} ${label}`);
294
- }
295
-
296
- lines.push(g);
297
- lines.push(`${g} ${dim("↑/↓ move · ↵ select · esc cancel")}`);
298
-
299
- if (lastLineCount > 0) {
300
- process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
301
- }
302
- process.stdout.write(lines.map((l) => truncateLine(l, cols)).join("\n") + "\n");
303
- lastLineCount = lines.length;
304
- }
305
-
306
- const itemCount = options.length;
307
-
308
- return new Promise<IntegrationType[]>((resolve) => {
309
- render();
310
- readline.emitKeypressEvents(process.stdin);
311
- const wasRaw = process.stdin.isRaw;
312
- if (process.stdin.setRawMode) process.stdin.setRawMode(true);
313
- process.stdin.resume();
314
-
315
- function cleanup(): void {
316
- showCursor();
317
- process.stdin.removeListener("keypress", onKey);
318
- if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw ?? false);
319
- process.stdin.pause();
320
- }
321
-
322
- function onKey(_str: string | undefined, key: readline.Key): void {
323
- if (!key) return;
324
- if ((key.ctrl && (key.name === "c" || key.name === "d")) || key.name === "escape") {
325
- cleanup();
326
- process.stdout.write("\n");
327
- process.exit(130); // SIGINT-equivalent
328
- }
329
- if (key.name === "up") {
330
- cursor = cursor > 0 ? cursor - 1 : itemCount - 1;
331
- render();
332
- } else if (key.name === "down") {
333
- cursor = cursor < itemCount - 1 ? cursor + 1 : 0;
334
- render();
335
- } else if (key.name === "return" || key.name === "space") {
336
- cleanup();
337
- process.stdout.write("\n");
338
- resolve(options[cursor].value);
339
- }
340
- }
341
-
342
- process.stdin.on("keypress", onKey);
174
+ const { options } = buildCliMenuOptions(detected, action);
175
+ const kit = screenKit(optsFor(process.stdout));
176
+ process.stdout.write(`\n${kit.header(action === "uninstall" ? "Remove hooks" : "Install hooks")}\n\n`);
177
+ const choices: SelectChoice<IntegrationType[]>[] = options.map((option) => ({
178
+ label: option.label,
179
+ value: option.value,
180
+ section: option.detected ? "found on this machine" : "not installed, set up ahead of time",
181
+ }));
182
+ const picked = await selectOne<IntegrationType[]>({
183
+ message: "Choose agents",
184
+ meta: `${detected.length} found on this machine`,
185
+ choices,
186
+ spaceSelects: true,
343
187
  });
344
- }
345
-
346
- /**
347
- * Show interactive searchable policy selector.
348
- * @param preSelected — policy names to pre-check (e.g. from existing config).
349
- * When omitted, uses each policy's defaultEnabled flag.
350
- * @param options — prompt options (e.g. includeBeta)
351
- */
352
- export async function promptPolicySelection(
353
- preSelected?: string[],
354
- options: PromptOptions = {},
355
- ): Promise<string[]> {
356
- const { includeBeta = false } = options;
357
-
358
- // If stdin is not a TTY (piped/CI), return defaults
359
- if (!process.stdin.isTTY) {
360
- const available = BUILTIN_POLICIES.filter((p) => includeBeta || !p.beta);
361
- // Returned UNCHANGED, deliberately. This used to intersect with the builtin
362
- // catalog, and manager.ts writes the result straight back — so a piped
363
- // `policies --install` silently deleted every enabled name the catalog did
364
- // not contain: a `failproofai/`-qualified name (a form the enforcement path
365
- // explicitly accepts, `registerBuiltinPolicies` canonicalizes both), a beta
366
- // policy enabled with `--beta`, and now any pack policy. The line that
367
- // reports what was enabled prints only the survivors, so the loss was
368
- // invisible. Carrying an unknown name costs nothing — registration looks it
369
- // up in a Set and finds nothing — while dropping one destroys configuration.
370
- if (preSelected) return [...preSelected];
371
- return available.filter((p) => p.defaultEnabled).map((p) => p.name);
372
- }
373
-
374
- const preSelectedSet = preSelected ? new Set(preSelected) : null;
375
-
376
- const items: SelectItem[] = BUILTIN_POLICIES
377
- .filter((p) => includeBeta || !p.beta)
378
- .map((p) => ({
379
- name: p.name,
380
- description: p.description,
381
- category: p.category,
382
- selected: preSelectedSet ? preSelectedSet.has(p.name) : p.defaultEnabled,
383
- beta: !!p.beta,
384
- }));
385
-
386
- // Everything the user has enabled that this picker cannot show a row for, for
387
- // the same reason as the non-TTY branch above: the picker's universe is the
388
- // builtin catalog, and resolving from it alone deletes anything else. Kept in
389
- // their original order and appended to whatever the user picks.
390
- const carried = preSelected
391
- ? preSelected.filter((name) => !items.some((i) => i.name === name))
392
- : [];
393
-
394
- const total = items.length;
395
- const WINDOW_SIZE = 10;
396
-
397
- // Shared brand painter from tui.ts, bound to role names so a reader never has
398
- // to decode which hue a role happens to use today (glyphs imported above).
399
- const useColor = !process.env.NO_COLOR;
400
- const c = paint(useColor);
401
- const { dim, bold } = c;
402
- const stepMark = c.guide; // ◆ step marker + live selected-count
403
- const activeName = c.pinkBold; // row under the cursor
404
- const selectedMark = c.pink; // checked boxes / selected names
405
- const betaTag = c.softPink; // "beta" pill
406
- // Fixed name column so descriptions align into a clean, scannable second column.
407
- const NAME_COL = Math.min(34, Math.max(10, ...items.map((i) => i.name.length)));
408
-
409
- let cursor = 0;
410
- let search = "";
411
- let lastLineCount = 0;
412
- let cursorHidden = false;
413
-
414
- function hideCursor(): void {
415
- if (!cursorHidden) {
416
- process.stdout.write("\x1B[?25l");
417
- cursorHidden = true;
418
- }
419
- }
420
-
421
- function showCursor(): void {
422
- if (cursorHidden) {
423
- process.stdout.write("\x1B[?25h");
424
- cursorHidden = false;
425
- }
426
- }
427
-
428
- function getFiltered(): SelectItem[] {
429
- if (!search) return items;
430
- const q = search.toLowerCase();
431
- return items.filter(
432
- (i) => i.name.toLowerCase().includes(q) || i.description.toLowerCase().includes(q),
433
- );
434
- }
435
-
436
- // Build display rows: category header rows interspersed with item rows.
437
- // Categories appear in the order they first appear in BUILTIN_POLICIES.
438
- function buildDisplayRows(filtered: SelectItem[]): DisplayRow[] {
439
- // Single pass: compute category order, enabled counts, and total counts together.
440
- const categoryOrder: string[] = [];
441
- const categoryEnabledCount = new Map<string, number>();
442
- const categoryTotalCount = new Map<string, number>();
443
- for (const p of items) {
444
- if (!categoryEnabledCount.has(p.category)) {
445
- categoryOrder.push(p.category);
446
- categoryEnabledCount.set(p.category, 0);
447
- categoryTotalCount.set(p.category, 0);
448
- }
449
- categoryTotalCount.set(p.category, categoryTotalCount.get(p.category)! + 1);
450
- if (p.selected) categoryEnabledCount.set(p.category, categoryEnabledCount.get(p.category)! + 1);
451
- }
452
-
453
- const filteredByCategory = new Map<string, SelectItem[]>();
454
- for (const item of filtered) {
455
- const bucket = filteredByCategory.get(item.category) ?? [];
456
- bucket.push(item);
457
- filteredByCategory.set(item.category, bucket);
458
- }
459
-
460
- const rows: DisplayRow[] = [];
461
- let idx = 0;
462
- for (const cat of categoryOrder) {
463
- const catFiltered = filteredByCategory.get(cat);
464
- if (!catFiltered || catFiltered.length === 0) continue;
465
- rows.push({ kind: "header", category: cat, enabledCount: categoryEnabledCount.get(cat)!, totalCount: categoryTotalCount.get(cat)! });
466
- for (const item of catFiltered) {
467
- rows.push({ kind: "item", item, filteredIndex: idx++ });
468
- }
469
- }
470
- return rows;
188
+ if (picked === null) {
189
+ process.stdout.write("\n");
190
+ process.exit(130); // SIGINT-equivalent
471
191
  }
472
-
473
- function render(): void {
474
- const cols = process.stdout.columns || 100;
475
- hideCursor();
476
-
477
- const filtered = getFiltered();
478
- const shown = filtered.length;
479
- if (shown > 0 && cursor >= shown) cursor = shown - 1;
480
- const selectedCount = items.filter((i) => i.selected).length;
481
-
482
- const g = dim(BAR);
483
- const lines: string[] = [];
484
-
485
- // header
486
- lines.push(g);
487
- lines.push(
488
- `${stepMark(STEP)} ${bold("Choose the policies to enable")} ${dim("·")} ` +
489
- `${stepMark(String(selectedCount))} ${dim("selected")}`,
490
- );
491
-
492
- // search
493
- const blockCursor = useColor ? "\x1B[7m \x1B[0m" : "_";
494
- const countLabel = search ? dim(`${shown}/${total} shown`) : dim(`${total} policies`);
495
- lines.push(`${g} ${dim("filter ›")} ${search}${blockCursor} ${countLabel}`);
496
- lines.push(g);
497
-
498
- if (shown === 0) {
499
- lines.push(`${g} ${dim(`no policies match “${search}”`)}`);
500
- for (let i = 0; i < WINDOW_SIZE + 1; i++) lines.push(g);
501
- } else {
502
- const displayRows = buildDisplayRows(filtered);
503
-
504
- let cursorDisplayRow = 0;
505
- for (let i = 0; i < displayRows.length; i++) {
506
- const row = displayRows[i];
507
- if (row.kind === "item" && row.filteredIndex === cursor) {
508
- cursorDisplayRow = i;
509
- break;
510
- }
511
- }
512
- let windowStart = cursorDisplayRow - Math.floor(WINDOW_SIZE / 2);
513
- windowStart = Math.max(0, windowStart);
514
- windowStart = Math.min(windowStart, Math.max(0, displayRows.length - WINDOW_SIZE));
515
- const windowEnd = Math.min(displayRows.length, windowStart + WINDOW_SIZE);
516
-
517
- const aboveItems = displayRows.slice(0, windowStart).filter((r) => r.kind === "item").length;
518
- lines.push(aboveItems > 0 ? `${g} ${dim(`↑ ${aboveItems} more`)}` : g);
519
-
520
- for (let i = windowStart; i < windowEnd; i++) {
521
- const row = displayRows[i];
522
- if (row.kind === "header") {
523
- lines.push(`${g} ${dim(row.category.toUpperCase())} ${dim(`${row.enabledCount}/${row.totalCount}`)}`);
524
- } else {
525
- const item = row.item;
526
- const active = row.filteredIndex === cursor;
527
- const box = item.selected ? selectedMark(BOX_ON) : dim(BOX_OFF);
528
- const rawName = item.name.padEnd(NAME_COL);
529
- const name = active ? activeName(rawName) : item.selected ? rawName : dim(rawName);
530
- const beta = item.beta ? ` ${betaTag("beta")}` : "";
531
- const descWidth = Math.max(8, cols - NAME_COL - 8 - (item.beta ? 5 : 0));
532
- const desc = dim(truncateLine(item.description, descWidth));
533
- lines.push(`${g} ${box} ${name}${beta} ${desc}`);
534
- }
535
- }
536
- for (let i = windowEnd - windowStart; i < WINDOW_SIZE; i++) lines.push(g);
537
-
538
- const belowItems = displayRows.slice(windowEnd).filter((r) => r.kind === "item").length;
539
- lines.push(belowItems > 0 ? `${g} ${dim(`↓ ${belowItems} more`)}` : g);
540
- }
541
-
542
- // footer
543
- lines.push(g);
544
- lines.push(`${g} ${dim("↑/↓ move · space select · ctrl+a all · ↵ save · type to filter · esc clear")}`);
545
-
546
- if (lastLineCount > 0) process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
547
- process.stdout.write(lines.map((l) => truncateLine(l, cols)).join("\n") + "\n");
548
- lastLineCount = lines.length;
549
- }
550
-
551
- return new Promise<string[]>((resolve) => {
552
- render();
553
-
554
- process.stdin.setRawMode(true);
555
- process.stdin.resume();
556
- // Use a single data→keypress pipeline with no readline.Interface.
557
- // readline.createInterface would register its own competing data listener
558
- // and its close() call would unexpectedly pause stdin, breaking arrow keys.
559
- readline.emitKeypressEvents(process.stdin);
560
-
561
- function keypressHandler(_str: string | undefined, key: readline.Key): void {
562
- if (!key) return;
563
-
564
- if (key.ctrl && key.name === "c") {
565
- cleanup();
566
- process.exit(0);
567
- }
568
-
569
- const filtered = getFiltered();
570
-
571
- if (key.name === "up") {
572
- if (filtered.length > 0) {
573
- cursor = cursor > 0 ? cursor - 1 : filtered.length - 1;
574
- }
575
- render();
576
- } else if (key.name === "down") {
577
- if (filtered.length > 0) {
578
- cursor = cursor < filtered.length - 1 ? cursor + 1 : 0;
579
- }
580
- render();
581
- } else if (key.name === "space") {
582
- const item = filtered[cursor];
583
- if (item) item.selected = !item.selected;
584
- render();
585
- } else if (key.name === "return" || (key.ctrl && key.name === "s")) {
586
- // Save — collapse the picker to a one-line summary, then resolve.
587
- cleanup();
588
- const selected = items.filter((i) => i.selected).map((i) => i.name);
589
- if (lastLineCount > 0) process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
590
- // Carried entries are NAMED, not counted. "3 kept" reads as fine right
591
- // up until someone needs to know which three.
592
- const summary = carried.length > 0
593
- ? `${selected.length} selected, kept ${carried.join(", ")}`
594
- : `${selected.length} selected`;
595
- process.stdout.write(
596
- `${dim(BAR)}\n${dim(DONE)} Policies\n${dim(BAR)} ${dim(summary)}\n`,
597
- );
598
- resolve([...selected, ...carried]);
599
- } else if (key.name === "escape") {
600
- // Clear search filter
601
- search = "";
602
- cursor = 0;
603
- render();
604
- } else if (key.ctrl && key.name === "a") {
605
- // Toggle all visible items
606
- const allSelected = filtered.length > 0 && filtered.every((i) => i.selected);
607
- for (const item of filtered) item.selected = !allSelected;
608
- render();
609
- } else if (key.name === "backspace" || key.name === "delete") {
610
- if (search.length > 0) {
611
- search = search.slice(0, -1);
612
- cursor = 0;
613
- render();
614
- }
615
- } else if (_str && _str.length === 1 && !key.ctrl && !key.meta) {
616
- // All printable characters (including 'a', 's') go to search
617
- search += _str;
618
- cursor = 0;
619
- render();
620
- }
621
- }
622
-
623
- function cleanup(): void {
624
- showCursor();
625
- process.stdin.removeListener("keypress", keypressHandler);
626
- process.stdin.setRawMode(false);
627
- process.stdin.pause();
628
- }
629
-
630
- process.stdin.on("keypress", keypressHandler);
631
- });
192
+ return picked;
632
193
  }
@@ -85,7 +85,7 @@ export class UnreadableAgentConfigError extends Error {
85
85
  }
86
86
  }
87
87
 
88
- function readJsonFile(path: string): Record<string, unknown> {
88
+ export function readJsonFile(path: string): Record<string, unknown> {
89
89
  if (!existsSync(path)) return {};
90
90
  let raw: string;
91
91
  try {