failproofai 0.0.10 → 0.0.11-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +7 -7
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +4 -4
  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.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 +4 -4
  18. package/.next/standalone/.next/server/app/_not-found/page/next-font-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page.js +4 -4
  21. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  27. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  30. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  32. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/index.html +1 -1
  34. package/.next/standalone/.next/server/app/index.rsc +16 -16
  35. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  36. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +16 -16
  37. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  38. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  39. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +3 -3
  40. package/.next/standalone/.next/server/app/page/build-manifest.json +4 -4
  41. package/.next/standalone/.next/server/app/page/next-font-manifest.json +1 -1
  42. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  43. package/.next/standalone/.next/server/app/page.js +4 -4
  44. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +4 -4
  47. package/.next/standalone/.next/server/app/policies/page/next-font-manifest.json +1 -1
  48. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  49. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  50. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +4 -4
  53. package/.next/standalone/.next/server/app/project/[name]/page/next-font-manifest.json +1 -1
  54. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -4
  56. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +4 -4
  59. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/next-font-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -4
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +4 -4
  66. package/.next/standalone/.next/server/app/projects/page/next-font-manifest.json +1 -1
  67. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/projects/page.js +4 -4
  69. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_ob4n._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__044xt9.._.js → [root-of-the-server]__0fwb7ao._.js} +2 -2
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g48iv.._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0j8-xkl._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0bdfoky.js +1 -1
  76. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_05pz9._._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wn51s._.js +4 -0
  79. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01as125._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__098zro9._.js +19 -0
  81. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02r.cjq._.js → [root-of-the-server]__09v.ljl._.js} +2 -2
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0agrcb8._.js +4 -0
  83. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0b7hkr~._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehh6vp._.js +4 -0
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g8l0tu._.js +3 -0
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0j4l6hl._.js +3 -0
  87. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ye1w50._.js → [root-of-the-server]__0k5n2kz._.js} +3 -3
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lp08ll._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0yaqw._.js +4 -0
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0o21f.o._.js +3 -0
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t8juvy._.js +4 -0
  92. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__10xgshr._.js → [root-of-the-server]__0tcyn68._.js} +2 -2
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ts150~._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0podumr._.js → [root-of-the-server]__0uylufv._.js} +3 -3
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ymlddl._.js +5 -5
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0~03grs._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/ssr/app_0cdqd9w._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/lib_utils_ts_068jk73._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/ssr/node_modules_0ttbz1~._.js +1 -1
  102. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_06u0kr8._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0a_7sdg.js +2 -2
  105. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0ef3uwk.js +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0j79~gv.js +2 -2
  107. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0pbja1x.js +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0r6o0i2.js +2 -2
  109. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_11y81~_.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_12or2kf.js +2 -2
  111. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0mebn66._.js +1 -1
  112. package/.next/standalone/.next/server/functions-config-manifest.json +2 -2
  113. package/.next/standalone/.next/server/middleware-build-manifest.js +7 -7
  114. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  115. package/.next/standalone/.next/server/next-font-manifest.json +6 -6
  116. package/.next/standalone/.next/server/pages/404.html +1 -1
  117. package/.next/standalone/.next/server/pages/500.html +1 -1
  118. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  120. package/.next/standalone/.next/static/chunks/07kpqoo7kuckx.js +6 -0
  121. package/.next/standalone/.next/static/chunks/0a40sy4tk8ioe.js +1 -0
  122. package/.next/standalone/.next/static/chunks/{12l2t63hkyo2q.js → 0azb~vy9ds_uy.js} +1 -1
  123. package/.next/standalone/.next/static/chunks/{0j171xiqge4rv.js → 0bke.~atnsbeb.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/{0lt8ko3lw.5yt.js → 0bv1oyxspkpkb.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/{179yytvmam0ug.js → 0dvhi-prcsh3~.js} +1 -1
  126. package/.next/standalone/.next/static/chunks/0f5p9plm.aqlp.css +2 -0
  127. package/.next/standalone/.next/static/chunks/0ffvlbgzgnlw7.js +2 -0
  128. package/.next/standalone/.next/static/chunks/{150i0n26fnvso.js → 0n1n67imq.udf.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/0spktq7xqab9h.js +1 -0
  130. package/.next/standalone/.next/static/chunks/{14lii11wmo450.js → 118q3uljozd5z.js} +1 -1
  131. package/.next/standalone/.next/static/chunks/{0pkl..xgo-qox.js → 11w14gnqzprir.js} +1 -1
  132. package/.next/standalone/.next/static/chunks/{0rnqmir4cd5p9.js → 17mubwtqwijpu.js} +1 -1
  133. package/.next/standalone/.next/static/chunks/{turbopack-05z7a19q43zfq.js → turbopack-0nh.aopesgj~5.js} +1 -1
  134. package/.next/standalone/.next/static/media/4fa387ec64143e14-s.0.qu-9752pffj.woff2 +0 -0
  135. package/.next/standalone/.next/static/media/5ce348bf30bf5439-s.0ee55_hj9qcer.woff2 +0 -0
  136. package/.next/standalone/.next/static/media/6306c77e7c8268e4-s.0mao5jbfbduzp.woff2 +0 -0
  137. package/.next/standalone/.next/static/media/797e433ab948586e-s.p.09zddjkbdep5a.woff2 +0 -0
  138. package/.next/standalone/.next/static/media/7d817b4c03b0c5f1-s.0uzt.a6d44yda.woff2 +0 -0
  139. package/.next/standalone/.next/static/media/bbc41e54d2fcbd21-s.0mvwgmnhv29no.woff2 +0 -0
  140. package/.next/standalone/.next/static/{dAuQps6jUwCz9X1Q5FFOO → tGVQM5SE3NvbVu0gbAJm7}/_clientMiddlewareManifest.js +2 -2
  141. package/.next/standalone/app/policies/hooks-client.tsx +111 -14
  142. package/.next/standalone/components/navbar.tsx +1 -1
  143. package/.next/standalone/components/reach-developers.tsx +2 -2
  144. package/.next/standalone/lib/claude-sessions.ts +181 -0
  145. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  146. package/.next/standalone/node_modules/next/dist/build/static-paths/app.js +2 -1
  147. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  148. package/.next/standalone/node_modules/next/dist/build/utils.js +2 -1
  149. package/.next/standalone/node_modules/next/dist/client/components/router-reducer/fetch-server-response.js +2 -2
  150. package/.next/standalone/node_modules/next/dist/client/components/router-reducer/set-cache-busting-search-param.js +8 -2
  151. package/.next/standalone/node_modules/next/dist/client/route-params.js +23 -6
  152. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +13 -13
  153. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +11 -11
  154. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +2 -2
  155. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +10 -10
  156. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  157. package/.next/standalone/node_modules/next/dist/server/app-render/action-handler.js +3 -6
  158. package/.next/standalone/node_modules/next/dist/server/app-render/app-render.js +62 -9
  159. package/.next/standalone/node_modules/next/dist/server/app-render/collect-segment-data.js +16 -0
  160. package/.next/standalone/node_modules/next/dist/server/app-render/create-component-tree.js +49 -19
  161. package/.next/standalone/node_modules/next/dist/server/app-render/get-script-nonce-from-header.js +8 -20
  162. package/.next/standalone/node_modules/next/dist/server/app-render/metadata-insertion/create-server-inserted-metadata.js +8 -7
  163. package/.next/standalone/node_modules/next/dist/server/app-render/use-flight-response.js +2 -2
  164. package/.next/standalone/node_modules/next/dist/server/async-storage/work-store.js +2 -1
  165. package/.next/standalone/node_modules/next/dist/server/base-server.js +13 -5
  166. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  167. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  168. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  169. package/.next/standalone/node_modules/next/dist/server/dev/static-paths-worker.js +2 -1
  170. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +22 -2
  171. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  172. package/.next/standalone/node_modules/next/dist/server/lib/is-rsc-request.js +18 -0
  173. package/.next/standalone/node_modules/next/dist/server/lib/mock-request.js +30 -5
  174. package/.next/standalone/node_modules/next/dist/server/lib/patch-set-header.js +7 -0
  175. package/.next/standalone/node_modules/next/dist/server/lib/router-server.js +6 -3
  176. package/.next/standalone/node_modules/next/dist/server/lib/router-utils/resolve-routes.js +18 -4
  177. package/.next/standalone/node_modules/next/dist/server/lib/server-ipc/utils.js +3 -1
  178. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  179. package/.next/standalone/node_modules/next/dist/server/next-server.js +1 -1
  180. package/.next/standalone/node_modules/next/dist/server/request/fallback-params.js +27 -1
  181. package/.next/standalone/node_modules/next/dist/server/route-modules/app-route/module.js +1 -0
  182. package/.next/standalone/node_modules/next/dist/server/route-modules/route-module.js +11 -1
  183. package/.next/standalone/node_modules/next/dist/server/server-utils.js +19 -2
  184. package/.next/standalone/node_modules/next/dist/server/stream-utils/node-web-streams-helper.js +5 -5
  185. package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +1 -1
  186. package/.next/standalone/node_modules/next/dist/server/web/adapter.js +4 -1
  187. package/.next/standalone/node_modules/next/dist/server/web/edge-route-module-wrapper.js +2 -1
  188. package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  189. package/.next/standalone/node_modules/next/dist/{server → shared/lib}/htmlescape.js +15 -0
  190. package/.next/standalone/node_modules/next/dist/shared/lib/router/routes/app.js +13 -1
  191. package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/cache-busting-search-param.js +56 -10
  192. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  193. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  194. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  195. package/.next/standalone/node_modules/next/package.json +15 -15
  196. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  197. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  198. package/.next/standalone/node_modules/react/package.json +1 -1
  199. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  200. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  201. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  202. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  203. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  204. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  205. package/.next/standalone/node_modules/react-dom/package.json +2 -2
  206. package/.next/standalone/package.json +5 -5
  207. package/.next/standalone/proxy.ts +1 -1
  208. package/.next/standalone/server.js +1 -1
  209. package/README.md +4 -4
  210. package/bin/failproofai.mjs +230 -73
  211. package/dist/cli.mjs +3028 -1453
  212. package/lib/claude-sessions.ts +181 -0
  213. package/package.json +5 -5
  214. package/scripts/launch.ts +1 -1
  215. package/scripts/postinstall.mjs +89 -1
  216. package/src/audit/cache.ts +113 -0
  217. package/src/audit/cli-adapters/claude.ts +97 -0
  218. package/src/audit/cli-adapters/codex.ts +56 -0
  219. package/src/audit/cli-adapters/copilot.ts +51 -0
  220. package/src/audit/cli-adapters/cursor.ts +51 -0
  221. package/src/audit/cli-adapters/gemini.ts +51 -0
  222. package/src/audit/cli-adapters/index.ts +70 -0
  223. package/src/audit/cli-adapters/opencode.ts +52 -0
  224. package/src/audit/cli-adapters/pi.ts +51 -0
  225. package/src/audit/cli-adapters/shared.ts +85 -0
  226. package/src/audit/detectors/find-from-root.ts +27 -0
  227. package/src/audit/detectors/git-commit-no-verify.ts +22 -0
  228. package/src/audit/detectors/index.ts +33 -0
  229. package/src/audit/detectors/prefer-edit-over-read-cat.ts +31 -0
  230. package/src/audit/detectors/prefer-edit-over-sed-awk.ts +27 -0
  231. package/src/audit/detectors/prefer-write-over-heredoc.ts +36 -0
  232. package/src/audit/detectors/redundant-cd-cwd.ts +28 -0
  233. package/src/audit/detectors/reread-after-edit.ts +58 -0
  234. package/src/audit/detectors/sleep-polling-loop.ts +34 -0
  235. package/src/audit/index.ts +369 -0
  236. package/src/audit/replay.ts +121 -0
  237. package/src/audit/report.ts +349 -0
  238. package/src/audit/telemetry.ts +113 -0
  239. package/src/audit/types.ts +193 -0
  240. package/src/hooks/builtin-policies.ts +79 -1
  241. package/src/hooks/custom-hooks-loader.ts +19 -3
  242. package/src/hooks/first-run-nudge.ts +146 -0
  243. package/src/hooks/handler.ts +21 -102
  244. package/src/hooks/install-prompt.ts +34 -4
  245. package/src/hooks/manager.ts +72 -5
  246. package/src/hooks/policy-evaluator.ts +19 -4
  247. package/src/hooks/policy-registry.ts +1 -1
  248. package/src/hooks/policy-types.ts +9 -0
  249. package/src/hooks/tool-name-canonicalize.ts +65 -0
  250. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0609ezh._.js +0 -3
  251. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07_-mkc._.js +0 -3
  252. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09z7o2x._.js +0 -19
  253. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_sh2n0._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e9o9ri._.js +0 -4
  255. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0l6swv1._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0logebz._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mi5ejy._.js +0 -4
  258. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0odijkc._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rkxer-._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rl2kwi._.js +0 -4
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vg0uey._.js +0 -4
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0x5limi._.js +0 -3
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__10._f0s._.js +0 -4
  264. package/.next/standalone/.next/static/chunks/01q52wg_amm60.js +0 -2
  265. package/.next/standalone/.next/static/chunks/0kqar56yl~41o.js +0 -6
  266. package/.next/standalone/.next/static/chunks/0ml1.ck_5t36i.js +0 -1
  267. package/.next/standalone/.next/static/chunks/0zig0fh30t6ou.js +0 -1
  268. package/.next/standalone/.next/static/chunks/17rm86uz2nd5a.css +0 -2
  269. package/.next/standalone/.next/static/media/4fa387ec64143e14-s.0q3udbd2bu5yp.woff2 +0 -0
  270. package/.next/standalone/.next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2 +0 -0
  271. package/.next/standalone/.next/static/media/bbc41e54d2fcbd21-s.0gw~uztddq1df.woff2 +0 -0
  272. package/src/auth/login.ts +0 -104
  273. package/src/auth/logout.ts +0 -50
  274. package/src/auth/token-store.ts +0 -64
  275. package/src/relay/daemon.ts +0 -362
  276. package/src/relay/pid.ts +0 -76
  277. package/src/relay/queue.ts +0 -225
  278. /package/.next/standalone/.next/static/{dAuQps6jUwCz9X1Q5FFOO → tGVQM5SE3NvbVu0gbAJm7}/_buildManifest.js +0 -0
  279. /package/.next/standalone/.next/static/{dAuQps6jUwCz9X1Q5FFOO → tGVQM5SE3NvbVu0gbAJm7}/_ssgManifest.js +0 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Claude Code session transcript discovery helpers.
3
+ *
4
+ * Claude stores transcripts at:
5
+ * <CLAUDE_PROJECTS_PATH>/<encoded-cwd>/<sessionId>.jsonl
6
+ *
7
+ * Subagent transcripts (when a session spawned subagents) live alongside:
8
+ * <CLAUDE_PROJECTS_PATH>/<encoded-cwd>/<sessionId>/subagents/<agentId>.jsonl
9
+ *
10
+ * The parser for these files lives in `lib/log-entries.ts` (`parseLogContent`,
11
+ * `parseSessionLog`). This module exposes discovery only — the audit pipeline
12
+ * and any future Claude-specific tool walk the directory layout via these
13
+ * helpers instead of re-implementing the path conventions.
14
+ *
15
+ * Mirrors the shape of `lib/cursor-sessions.ts` for parity across CLIs.
16
+ */
17
+ import { readdirSync, statSync, existsSync } from "node:fs";
18
+ import { join, basename } from "node:path";
19
+ import { getClaudeProjectsPath, decodeFolderName } from "./paths";
20
+
21
+ const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
22
+
23
+ export interface ClaudeProjectFolder {
24
+ /** Encoded folder name on disk (e.g. "-home-user-project"). */
25
+ name: string;
26
+ /** Decoded filesystem path (e.g. "/home/user/project"). */
27
+ cwd: string;
28
+ /** Absolute path of the encoded folder. */
29
+ path: string;
30
+ }
31
+
32
+ export interface ClaudeTranscriptFile {
33
+ projectName: string;
34
+ /** Decoded cwd of the project. */
35
+ cwd: string;
36
+ sessionId: string;
37
+ /** Absolute path of `<sessionId>.jsonl`. */
38
+ transcriptPath: string;
39
+ mtimeMs: number;
40
+ sizeBytes: number;
41
+ /** True when this is a subagent transcript spawned from a parent session. */
42
+ isSubagent: boolean;
43
+ }
44
+
45
+ /** Returns the Claude projects root, honoring the CLAUDE_PROJECTS_PATH env var. */
46
+ export function getClaudeProjectsRoot(): string {
47
+ return getClaudeProjectsPath();
48
+ }
49
+
50
+ /** Lists all Claude project folders (one per encoded cwd). Returns [] if the
51
+ * projects root doesn't exist. Filenames that don't look like Claude project
52
+ * encodings are still included — encoding is permissive. */
53
+ export function listClaudeProjects(): ClaudeProjectFolder[] {
54
+ const root = getClaudeProjectsRoot();
55
+ let entries: import("node:fs").Dirent[];
56
+ try {
57
+ entries = readdirSync(root, { withFileTypes: true });
58
+ } catch {
59
+ return [];
60
+ }
61
+ return entries
62
+ .filter((e) => e.isDirectory())
63
+ .map((e) => ({
64
+ name: e.name,
65
+ cwd: decodeFolderName(e.name),
66
+ path: join(root, e.name),
67
+ }));
68
+ }
69
+
70
+ /** Lists every JSONL transcript under one Claude project folder, including
71
+ * subagent transcripts under `<sessionId>/subagents/`. Returns [] on missing
72
+ * or unreadable paths. */
73
+ export function listClaudeTranscripts(project: ClaudeProjectFolder): ClaudeTranscriptFile[] {
74
+ const out: ClaudeTranscriptFile[] = [];
75
+ let entries: import("node:fs").Dirent[];
76
+ try {
77
+ entries = readdirSync(project.path, { withFileTypes: true });
78
+ } catch {
79
+ return out;
80
+ }
81
+
82
+ for (const entry of entries) {
83
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) {
84
+ const sessionId = entry.name.slice(0, -".jsonl".length);
85
+ if (!UUID_RE.test(sessionId)) continue;
86
+ const transcriptPath = join(project.path, entry.name);
87
+ try {
88
+ const s = statSync(transcriptPath);
89
+ out.push({
90
+ projectName: project.name,
91
+ cwd: project.cwd,
92
+ sessionId,
93
+ transcriptPath,
94
+ mtimeMs: s.mtimeMs,
95
+ sizeBytes: s.size,
96
+ isSubagent: false,
97
+ });
98
+ } catch {
99
+ // unreadable — skip
100
+ }
101
+ } else if (entry.isDirectory() && UUID_RE.test(entry.name)) {
102
+ // Subagent transcripts at <sessionId>/subagents/<agentId>.jsonl
103
+ const subDir = join(project.path, entry.name, "subagents");
104
+ if (!existsSync(subDir)) continue;
105
+ let subEntries: import("node:fs").Dirent[];
106
+ try {
107
+ subEntries = readdirSync(subDir, { withFileTypes: true });
108
+ } catch {
109
+ continue;
110
+ }
111
+ for (const sub of subEntries) {
112
+ if (!sub.isFile() || !sub.name.endsWith(".jsonl")) continue;
113
+ const agentId = sub.name.slice(0, -".jsonl".length);
114
+ const transcriptPath = join(subDir, sub.name);
115
+ try {
116
+ const s = statSync(transcriptPath);
117
+ out.push({
118
+ projectName: project.name,
119
+ cwd: project.cwd,
120
+ sessionId: agentId,
121
+ transcriptPath,
122
+ mtimeMs: s.mtimeMs,
123
+ sizeBytes: s.size,
124
+ isSubagent: true,
125
+ });
126
+ } catch {
127
+ // unreadable — skip
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ return out;
134
+ }
135
+
136
+ /** Convenience: locate one Claude transcript file by session ID across all
137
+ * project folders. Returns null if not found. */
138
+ export function findClaudeTranscript(sessionId: string): string | null {
139
+ if (!UUID_RE.test(sessionId)) return null;
140
+ for (const project of listClaudeProjects()) {
141
+ const candidate = join(project.path, `${sessionId}.jsonl`);
142
+ if (existsSync(candidate)) return candidate;
143
+ // Subagent fallback
144
+ let entries: import("node:fs").Dirent[];
145
+ try {
146
+ entries = readdirSync(project.path, { withFileTypes: true });
147
+ } catch {
148
+ continue;
149
+ }
150
+ for (const entry of entries) {
151
+ if (!entry.isDirectory() || !UUID_RE.test(entry.name)) continue;
152
+ const subCandidate = join(project.path, entry.name, "subagents", `${sessionId}.jsonl`);
153
+ if (existsSync(subCandidate)) return subCandidate;
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+
159
+ /** For tests: list all session IDs across all projects. */
160
+ export function _listAllSessionIds(): string[] {
161
+ const ids: string[] = [];
162
+ for (const project of listClaudeProjects()) {
163
+ for (const t of listClaudeTranscripts(project)) {
164
+ ids.push(t.sessionId);
165
+ }
166
+ }
167
+ return ids;
168
+ }
169
+
170
+ /** For tests: stat one transcript file. */
171
+ export function _statTranscript(path: string): { mtimeMs: number; sizeBytes: number } | null {
172
+ try {
173
+ const s = statSync(path);
174
+ return { mtimeMs: s.mtimeMs, sizeBytes: s.size };
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
179
+
180
+ /** Re-export for callers that want to construct paths from a basename. */
181
+ export { basename };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.10",
3
+ "version": "0.0.11-beta.2",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -59,19 +59,19 @@
59
59
  ],
60
60
  "author": "ExosphereHost Inc. <failproofai@exosphere.host>",
61
61
  "license": "SEE LICENSE IN LICENSE",
62
- "homepage": "https://github.com/exospherehost/failproofai",
62
+ "homepage": "https://github.com/FailproofAI/failproofai",
63
63
  "repository": {
64
64
  "type": "git",
65
- "url": "https://github.com/exospherehost/failproofai.git"
65
+ "url": "https://github.com/FailproofAI/failproofai.git"
66
66
  },
67
67
  "bugs": {
68
- "url": "https://github.com/exospherehost/failproofai/issues"
68
+ "url": "https://github.com/FailproofAI/failproofai/issues"
69
69
  },
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
- "@anthropic-ai/sdk": "^0.93.0",
74
+ "@anthropic-ai/sdk": "^0.96.0",
75
75
  "@tailwindcss/postcss": "^4.1.18",
76
76
  "@tanstack/react-virtual": "^3.13.18",
77
77
  "@testing-library/jest-dom": "^6.9.1",
package/scripts/launch.ts CHANGED
@@ -17,7 +17,7 @@ export function launch(mode: "dev" | "start"): void {
17
17
  // same column so the values form a clean right-hand column).
18
18
  console.log(`\n failproof ai\n`);
19
19
  console.log(` 📦 Version: ${version}`);
20
- console.log(` ⭐ Star us: https://github.com/exospherehost/failproofai`);
20
+ console.log(` ⭐ Star us: https://github.com/failproofai/failproofai`);
21
21
  console.log(` 📖 Docs: https://befailproof.ai`);
22
22
  console.log(` 💬 Slack: https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ\n`);
23
23
 
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * No external dependencies — Node.js built-ins only.
9
9
  */
10
- import { existsSync, readFileSync } from "node:fs";
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
11
11
  import { resolve } from "node:path";
12
12
  import { platform, arch, release, homedir, hostname } from "node:os";
13
13
  import { createHmac } from "node:crypto";
@@ -134,6 +134,94 @@ try {
134
134
  // Non-critical — don't fail the install
135
135
  }
136
136
 
137
+ if (!hooksResult.configured && !hooksResult.registered) {
138
+ console.log(
139
+ `\n[failproofai] Installed. Next steps:\n` +
140
+ ` 1. Run \`failproofai policies --install\` to enable safety policies.\n` +
141
+ ` 2. Run \`failproofai\` to open the dashboard (or just \`failproofai\` to start now — it'll offer to set up policies for you).\n` +
142
+ ` Disable first-run prompt: FAILPROOFAI_NO_FIRST_RUN=1\n`
143
+ );
144
+ }
145
+
146
+ // First-run + version_changed detection. The presence of ~/.failproofai/last-version
147
+ // is a stable signal: written on every postinstall, absent before the first one.
148
+ // Cannot piggy-back on instance-id because most users hit Tier 2 (OS machine ID)
149
+ // and never create that file.
150
+ //
151
+ // Semver comparison: a release (no prerelease tag) is greater than the same
152
+ // version with a prerelease tag (semver §11). Inside the prerelease, numeric
153
+ // identifiers are lower than non-numeric ones of the same length.
154
+ function compareSemver(a, b) {
155
+ const parse = (v) => {
156
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(v);
157
+ if (!m) return null;
158
+ return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
159
+ };
160
+ const pa = parse(a);
161
+ const pb = parse(b);
162
+ if (!pa || !pb) return a < b ? -1 : a > b ? 1 : 0;
163
+ for (let i = 0; i < 3; i++) {
164
+ if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] < pb.nums[i] ? -1 : 1;
165
+ }
166
+ if (pa.pre === null && pb.pre === null) return 0;
167
+ if (pa.pre === null) return 1;
168
+ if (pb.pre === null) return -1;
169
+ const ax = pa.pre.split(/[.-]/);
170
+ const bx = pb.pre.split(/[.-]/);
171
+ for (let i = 0; i < Math.max(ax.length, bx.length); i++) {
172
+ const ai = ax[i], bi = bx[i];
173
+ if (ai === undefined) return -1;
174
+ if (bi === undefined) return 1;
175
+ const aNum = /^\d+$/.test(ai), bNum = /^\d+$/.test(bi);
176
+ if (aNum && bNum) {
177
+ const d = Number(ai) - Number(bi);
178
+ if (d !== 0) return d < 0 ? -1 : 1;
179
+ } else if (aNum) {
180
+ return -1;
181
+ } else if (bNum) {
182
+ return 1;
183
+ } else if (ai !== bi) {
184
+ return ai < bi ? -1 : 1;
185
+ }
186
+ }
187
+ return 0;
188
+ }
189
+
190
+ const currentVersion = process.env.npm_package_version ?? "unknown";
191
+ const lastVersionFile = resolve(homedir(), ".failproofai", "last-version");
192
+ let previousVersion = null;
193
+ try {
194
+ if (existsSync(lastVersionFile)) {
195
+ previousVersion = readFileSync(lastVersionFile, "utf8").trim() || null;
196
+ }
197
+ } catch {}
198
+
199
+ if (previousVersion === null) {
200
+ trackInstallEvent("first_install", {
201
+ platform: platform(),
202
+ arch: arch(),
203
+ os_release: release(),
204
+ node_version: process.versions.node,
205
+ version: currentVersion,
206
+ }).catch(() => {});
207
+ } else {
208
+ // Same version is a reinstall — still worth tracking; users hitting `npm install -g`
209
+ // repeatedly is itself signal. Drop the `!==` guard so cmp===0 reaches the event.
210
+ const cmp = compareSemver(previousVersion, currentVersion);
211
+ trackInstallEvent("version_changed", {
212
+ from_version: previousVersion,
213
+ to_version: currentVersion,
214
+ direction: cmp < 0 ? "upgrade" : cmp > 0 ? "downgrade" : "reinstall",
215
+ platform: platform(),
216
+ arch: arch(),
217
+ }).catch(() => {});
218
+ }
219
+
220
+ try {
221
+ mkdirSync(resolve(homedir(), ".failproofai"), { recursive: true });
222
+ writeFileSync(lastVersionFile, currentVersion, "utf8");
223
+ } catch {}
224
+
137
225
  // Telemetry (best-effort, fire-and-forget)
138
226
  trackInstallEvent("package_installed", {
139
227
  platform: platform(),
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Per-transcript audit-result cache.
3
+ *
4
+ * Stored at `~/.failproofai/cache/audit/<sha1(transcriptPath)>.json` with
5
+ * mode 0600. Keyed by (mtime, size, engineVersion, detectorVersion) so the
6
+ * cache invalidates automatically when either the transcript or the policy /
7
+ * detector code changes.
8
+ *
9
+ * Skipped for transcripts whose `sizeBytes === 0` (currently: OpenCode, whose
10
+ * sessions live in a SQLite DB rather than a file with a stable mtime).
11
+ */
12
+ import { createHash } from "node:crypto";
13
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { homedir } from "node:os";
16
+ import { BUILTIN_POLICIES } from "../hooks/builtin-policies";
17
+ import { AUDIT_DETECTORS } from "./detectors";
18
+ import type { TranscriptAuditResult } from "./types";
19
+
20
+ let cachedEngineVersion: string | null = null;
21
+ let cachedDetectorVersion: string | null = null;
22
+
23
+ /** Hash of every builtin policy's name + function body. Changes when policy
24
+ * code changes, invalidating downstream caches. */
25
+ function getEngineVersion(): string {
26
+ if (cachedEngineVersion) return cachedEngineVersion;
27
+ const blob = BUILTIN_POLICIES
28
+ .map((p) => `${p.name}|${p.fn.toString()}`)
29
+ .sort()
30
+ .join("\n");
31
+ cachedEngineVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
32
+ return cachedEngineVersion;
33
+ }
34
+
35
+ /** Same for audit detectors. */
36
+ function getDetectorVersion(): string {
37
+ if (cachedDetectorVersion) return cachedDetectorVersion;
38
+ const blob = AUDIT_DETECTORS
39
+ .map((d) => `${d.name}|${d.detect.toString()}`)
40
+ .sort()
41
+ .join("\n");
42
+ cachedDetectorVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
43
+ return cachedDetectorVersion;
44
+ }
45
+
46
+ function getCachePathFor(transcriptPath: string): string {
47
+ const root = join(homedir(), ".failproofai", "cache", "audit");
48
+ const key = createHash("sha1").update(transcriptPath).digest("hex");
49
+ return join(root, `${key}.json`);
50
+ }
51
+
52
+ interface CacheEntry {
53
+ mtimeMs: number;
54
+ sizeBytes: number;
55
+ engineVersion: string;
56
+ detectorVersion: string;
57
+ result: TranscriptAuditResult;
58
+ }
59
+
60
+ export function readCachedTranscriptResult(
61
+ transcriptPath: string,
62
+ mtimeMs: number,
63
+ sizeBytes: number,
64
+ ): TranscriptAuditResult | null {
65
+ if (sizeBytes === 0) return null; // OpenCode and other DB-backed sources
66
+ const cachePath = getCachePathFor(transcriptPath);
67
+ if (!existsSync(cachePath)) return null;
68
+ try {
69
+ const raw = readFileSync(cachePath, "utf-8");
70
+ const entry = JSON.parse(raw) as CacheEntry;
71
+ if (entry.mtimeMs !== mtimeMs) return null;
72
+ if (entry.sizeBytes !== sizeBytes) return null;
73
+ if (entry.engineVersion !== getEngineVersion()) return null;
74
+ if (entry.detectorVersion !== getDetectorVersion()) return null;
75
+ return entry.result;
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ export function writeCachedTranscriptResult(
82
+ transcriptPath: string,
83
+ mtimeMs: number,
84
+ sizeBytes: number,
85
+ result: TranscriptAuditResult,
86
+ ): void {
87
+ if (sizeBytes === 0) return;
88
+ const cachePath = getCachePathFor(transcriptPath);
89
+ try {
90
+ mkdirSync(join(homedir(), ".failproofai", "cache", "audit"), { recursive: true });
91
+ const entry: CacheEntry = {
92
+ mtimeMs,
93
+ sizeBytes,
94
+ engineVersion: getEngineVersion(),
95
+ detectorVersion: getDetectorVersion(),
96
+ result,
97
+ };
98
+ // Set 0o600 at file-creation time so there's no window where the file
99
+ // exists with the umask default (typically 0o644). The chmodSync below is
100
+ // a belt-and-suspenders pass for the case where the file already existed.
101
+ writeFileSync(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 0o600 });
102
+ try { chmodSync(cachePath, 0o600); } catch { /* best-effort on POSIX */ }
103
+ } catch {
104
+ // Cache writes are best-effort — never let a cache error kill the audit.
105
+ }
106
+ }
107
+
108
+ /** Test helper: reset memoized version hashes (so they recompute after a
109
+ * detector or policy is monkey-patched in a test). */
110
+ export function _resetVersionCache(): void {
111
+ cachedEngineVersion = null;
112
+ cachedDetectorVersion = null;
113
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Claude Code transcript adapter.
3
+ *
4
+ * Discovers ~/.claude/projects/<encoded>/<sessionId>.jsonl (and any subagent
5
+ * transcripts under <sessionId>/subagents/) via lib/claude-sessions.ts, then
6
+ * parses each via lib/log-entries.ts.
7
+ */
8
+ import { readFile } from "node:fs/promises";
9
+ import {
10
+ listClaudeProjects,
11
+ listClaudeTranscripts,
12
+ type ClaudeTranscriptFile,
13
+ } from "../../../lib/claude-sessions";
14
+ import { parseLogContent, type LogSource } from "../../../lib/log-entries";
15
+ import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
16
+ import { logEntriesToEvents } from "./shared";
17
+
18
+ export interface ListOpts {
19
+ /** Restrict to sessions whose decoded cwd matches one of these paths. */
20
+ projects?: string[];
21
+ /** Filter on transcript mtime — only return if mtimeMs >= sinceMs. */
22
+ sinceMs?: number;
23
+ }
24
+
25
+ export async function listClaudeTranscriptMetadata(
26
+ opts: ListOpts = {},
27
+ ): Promise<TranscriptMetadata[]> {
28
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
29
+ const sinceMs = opts.sinceMs ?? 0;
30
+ const out: TranscriptMetadata[] = [];
31
+
32
+ for (const project of listClaudeProjects()) {
33
+ if (projectFilter && !projectFilter.has(project.cwd)) continue;
34
+ let transcripts: ClaudeTranscriptFile[];
35
+ try {
36
+ transcripts = listClaudeTranscripts(project);
37
+ } catch {
38
+ continue;
39
+ }
40
+ for (const t of transcripts) {
41
+ if (t.mtimeMs < sinceMs) continue;
42
+ out.push({
43
+ cli: "claude",
44
+ projectName: project.name,
45
+ sessionId: t.sessionId,
46
+ transcriptPath: t.transcriptPath,
47
+ mtimeMs: t.mtimeMs,
48
+ sizeBytes: t.sizeBytes,
49
+ });
50
+ }
51
+ }
52
+
53
+ return out;
54
+ }
55
+
56
+ export async function streamClaudeEvents(
57
+ meta: TranscriptMetadata,
58
+ ): Promise<NormalizedToolEvent[]> {
59
+ let content: string;
60
+ try {
61
+ content = await readFile(meta.transcriptPath, "utf-8");
62
+ } catch {
63
+ return [];
64
+ }
65
+
66
+ const source: LogSource = "session";
67
+ let entries;
68
+ try {
69
+ entries = await parseLogContent(content, source);
70
+ } catch {
71
+ return [];
72
+ }
73
+
74
+ // Best-effort cwd resolution: the JSONL lines carry `cwd` directly on each
75
+ // record (verified live — see plan exploration notes). Pull the first one
76
+ // we find rather than re-decoding the folder name (which is lossy on POSIX).
77
+ let cwd = "";
78
+ for (const line of content.split("\n", 50)) {
79
+ if (!line.trim()) continue;
80
+ try {
81
+ const parsed = JSON.parse(line) as { cwd?: unknown };
82
+ if (typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
83
+ cwd = parsed.cwd;
84
+ break;
85
+ }
86
+ } catch {
87
+ // skip malformed lines
88
+ }
89
+ }
90
+
91
+ return logEntriesToEvents(entries, {
92
+ cli: "claude",
93
+ sessionId: meta.sessionId,
94
+ transcriptPath: meta.transcriptPath,
95
+ cwd,
96
+ });
97
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Codex (OpenAI) transcript adapter.
3
+ *
4
+ * Codex stores transcripts at ~/.codex/sessions/YYYY/MM/DD/<*sessionId*>.jsonl
5
+ * with a different layout per project. We enumerate via lib/codex-projects.ts
6
+ * and parse via lib/codex-sessions.ts which produces the same LogEntry[] shape
7
+ * the Claude parser uses, so the shared converter handles the rest.
8
+ */
9
+ import { statSync } from "node:fs";
10
+ import { getCodexProjects, getCodexSessionsByEncodedName } from "../../../lib/codex-projects";
11
+ import { getCodexSessionLog } from "../../../lib/codex-sessions";
12
+ import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
13
+ import type { ListOpts } from "./claude";
14
+ import { logEntriesToEvents } from "./shared";
15
+
16
+ export async function listCodexTranscriptMetadata(
17
+ opts: ListOpts = {},
18
+ ): Promise<TranscriptMetadata[]> {
19
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
20
+ const sinceMs = opts.sinceMs ?? 0;
21
+ const out: TranscriptMetadata[] = [];
22
+
23
+ const projects = await getCodexProjects();
24
+ for (const project of projects) {
25
+ const { cwd, sessions } = await getCodexSessionsByEncodedName(project.name);
26
+ const effectiveCwd = cwd ?? "";
27
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
28
+ for (const s of sessions) {
29
+ const mtimeMs = s.lastModified.getTime();
30
+ if (mtimeMs < sinceMs) continue;
31
+ let sizeBytes = 0;
32
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
33
+ if (!s.sessionId) continue;
34
+ out.push({
35
+ cli: "codex",
36
+ projectName: project.name,
37
+ sessionId: s.sessionId,
38
+ transcriptPath: s.path,
39
+ mtimeMs,
40
+ sizeBytes,
41
+ });
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+
47
+ export async function streamCodexEvents(meta: TranscriptMetadata): Promise<NormalizedToolEvent[]> {
48
+ const log = await getCodexSessionLog(meta.sessionId);
49
+ if (!log) return [];
50
+ return logEntriesToEvents(log.entries, {
51
+ cli: "codex",
52
+ sessionId: meta.sessionId,
53
+ transcriptPath: meta.transcriptPath,
54
+ cwd: log.cwd ?? "",
55
+ });
56
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * GitHub Copilot CLI transcript adapter.
3
+ */
4
+ import { statSync } from "node:fs";
5
+ import { getCopilotProjects, getCopilotSessionsByEncodedName } from "../../../lib/copilot-projects";
6
+ import { getCopilotSessionLog } from "../../../lib/copilot-sessions";
7
+ import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
8
+ import type { ListOpts } from "./claude";
9
+ import { logEntriesToEvents } from "./shared";
10
+
11
+ export async function listCopilotTranscriptMetadata(
12
+ opts: ListOpts = {},
13
+ ): Promise<TranscriptMetadata[]> {
14
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
15
+ const sinceMs = opts.sinceMs ?? 0;
16
+ const out: TranscriptMetadata[] = [];
17
+
18
+ const projects = await getCopilotProjects();
19
+ for (const project of projects) {
20
+ const { cwd, sessions } = await getCopilotSessionsByEncodedName(project.name);
21
+ const effectiveCwd = cwd ?? "";
22
+ if (projectFilter && !projectFilter.has(effectiveCwd)) continue;
23
+ for (const s of sessions) {
24
+ const mtimeMs = s.lastModified.getTime();
25
+ if (mtimeMs < sinceMs) continue;
26
+ let sizeBytes = 0;
27
+ try { sizeBytes = statSync(s.path).size; } catch { /* unreadable */ }
28
+ if (!s.sessionId) continue;
29
+ out.push({
30
+ cli: "copilot",
31
+ projectName: project.name,
32
+ sessionId: s.sessionId,
33
+ transcriptPath: s.path,
34
+ mtimeMs,
35
+ sizeBytes,
36
+ });
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+
42
+ export async function streamCopilotEvents(meta: TranscriptMetadata): Promise<NormalizedToolEvent[]> {
43
+ const log = await getCopilotSessionLog(meta.sessionId);
44
+ if (!log) return [];
45
+ return logEntriesToEvents(log.entries, {
46
+ cli: "copilot",
47
+ sessionId: meta.sessionId,
48
+ transcriptPath: meta.transcriptPath,
49
+ cwd: log.cwd ?? "",
50
+ });
51
+ }