failproofai 0.0.1-beta.9 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  22. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/index.html +1 -1
  27. package/.next/standalone/.next/server/app/index.rsc +15 -15
  28. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  30. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  31. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  32. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  33. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  34. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  37. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  40. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  43. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  44. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02nt~6d._.js +1 -1
  50. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  51. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
  52. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
  53. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
  54. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
  55. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0su5_t~._.js → [root-of-the-server]__0jvf9jj._.js} +2 -2
  56. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0osi8nq._.js +2 -2
  57. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
  58. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0yhzo9v._.js → [root-of-the-server]__0~7bzp~._.js} +2 -2
  59. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +1 -1
  62. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
  64. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  65. package/.next/standalone/.next/server/pages/404.html +2 -2
  66. package/.next/standalone/.next/server/pages/500.html +1 -1
  67. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  69. package/.next/standalone/.next/static/chunks/{0qjjki0j187__.js → 056c865hodfe7.js} +1 -1
  70. package/.next/standalone/.next/static/chunks/{124wu0bxsexm6.js → 066e8ajzl234v.js} +1 -1
  71. package/.next/standalone/.next/static/chunks/{0frzv~pmu0hsf.js → 0eh2hq9~6bf53.js} +1 -1
  72. package/.next/standalone/.next/static/chunks/{0nygnut7i45jn.js → 0nwr.y4dwla00.js} +1 -1
  73. package/.next/standalone/.next/static/chunks/{14xe0g0rgwk18.js → 0o04-obbhh9.s.js} +1 -1
  74. package/.next/standalone/.next/static/chunks/{0.aencsvb-yev.js → 0wmsi.tszy~9y.js} +1 -1
  75. package/.next/standalone/.next/static/chunks/{12olt9p45z-lq.js → 15lp0u9f5fwae.js} +1 -1
  76. package/.next/standalone/.next/static/chunks/{0pc9pc_pilpi9.js → 17q_c2.bbcoh1.js} +1 -1
  77. package/.next/standalone/AGENTS.md +80 -0
  78. package/.next/standalone/CLAUDE.md +137 -0
  79. package/.next/standalone/bin/failproofai.mjs +12 -1
  80. package/.next/standalone/dist/index.js +80 -0
  81. package/.next/standalone/docs/package-aliases.md +25 -25
  82. package/.next/standalone/examples/policies-notification.js +77 -32
  83. package/.next/standalone/package.json +3 -2
  84. package/.next/standalone/scripts/sync-hook-events-prompt.md +60 -0
  85. package/.next/standalone/server.js +1 -1
  86. package/.next/standalone/src/hooks/types.ts +11 -2
  87. package/bin/failproofai.mjs +12 -1
  88. package/dist/index.js +80 -0
  89. package/package.json +3 -2
  90. package/scripts/sync-hook-events-prompt.md +60 -0
  91. package/src/hooks/types.ts +11 -2
  92. /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → odPaRK23IkTvoPxqrn_8P}/_buildManifest.js +0 -0
  93. /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → odPaRK23IkTvoPxqrn_8P}/_clientMiddlewareManifest.js +0 -0
  94. /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → odPaRK23IkTvoPxqrn_8P}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,63059,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["ChevronRight",0,t],63059)},73375,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);e.s(["ChevronLeft",0,t],73375)},73520,e=>{"use strict";var t=e.i(43476),a=e.i(73375),r=e.i(63059);e.s(["default",0,function({currentPage:e,totalPages:i,onPageChange:s}){if(i<=1)return null;let l=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>s(e-1),disabled:1===e,className:l(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(i<=7)return Array.from({length:i},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(i-1,e+1);e<=3&&(r=Math.min(5,i-1)),e>=i-2&&(a=Math.max(2,i-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<i-1&&t.push("ellipsis-end"),t.push(i),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>s(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>s(e+1),disabled:e===i,className:l(e===i),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return`${t.toFixed(1)}s`;let a=Math.floor(t/60);if(a>=60){let e=Math.floor(a/60);return`${e}h ${a%60}m`}let r=(t%60).toFixed(0);return`${a}m ${r}s`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},39616,82893,e=>{"use strict";let t=(0,e.i(75254).default)("settings",[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Settings",0,t],39616);var a=e.i(95187);let r=(0,a.createServerReference)("60f557090ccfcfc0e7f1b75249e555466e20f5cbe5",a.callServer,void 0,a.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,r],82893)},81418,88092,51737,e=>{"use strict";var t=e.i(75254);let a=(0,t.default)("shield-check",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);e.s(["ShieldCheck",0,a],81418);let r=(0,t.default)("shield-x",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]]);e.s(["ShieldX",0,r],88092);let i=(0,t.default)("shield-alert",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]);e.s(["ShieldAlert",0,i],51737)}]);
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,63059,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["ChevronRight",0,t],63059)},73375,e=>{"use strict";let t=(0,e.i(75254).default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);e.s(["ChevronLeft",0,t],73375)},73520,e=>{"use strict";var t=e.i(43476),a=e.i(73375),r=e.i(63059);e.s(["default",0,function({currentPage:e,totalPages:i,onPageChange:s}){if(i<=1)return null;let l=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>s(e-1),disabled:1===e,className:l(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(i<=7)return Array.from({length:i},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(i-1,e+1);e<=3&&(r=Math.min(5,i-1)),e>=i-2&&(a=Math.max(2,i-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<i-1&&t.push("ellipsis-end"),t.push(i),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>s(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>s(e+1),disabled:e===i,className:l(e===i),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return`${t.toFixed(1)}s`;let a=Math.floor(t/60);if(a>=60){let e=Math.floor(a/60);return`${e}h ${a%60}m`}let r=(t%60).toFixed(0);return`${a}m ${r}s`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},39616,14627,e=>{"use strict";let t=(0,e.i(75254).default)("settings",[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Settings",0,t],39616);var a=e.i(95187);let r=(0,a.createServerReference)("6041b7d2c91a163d94f0cd988378f19d39b4a5cd65",a.callServer,void 0,a.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,r],14627)},81418,88092,51737,e=>{"use strict";var t=e.i(75254);let a=(0,t.default)("shield-check",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);e.s(["ShieldCheck",0,a],81418);let r=(0,t.default)("shield-x",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]]);e.s(["ShieldX",0,r],88092);let i=(0,t.default)("shield-alert",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]);e.s(["ShieldAlert",0,i],51737)}]);
@@ -1 +1 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,33525,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"warnOnce",{enumerable:!0,get:function(){return n}});let n=e=>{}},18967,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={DecodeError:function(){return b},MiddlewareNotFoundError:function(){return P},MissingStaticPage:function(){return S},NormalizeError:function(){return h},PageNotFoundError:function(){return v},SP:function(){return m},ST:function(){return y},WEB_VITALS:function(){return i},execOnce:function(){return a},getDisplayName:function(){return f},getLocationOrigin:function(){return u},getURL:function(){return l},isAbsoluteUrl:function(){return c},isResSent:function(){return d},loadGetInitialProps:function(){return g},normalizeRepeatedSlashes:function(){return p},stringifyError:function(){return E}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=["CLS","FCP","FID","INP","LCP","TTFB"];function a(e){let r,t=!1;return(...n)=>(t||(t=!0,r=e(...n)),r)}let s=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,c=e=>s.test(e);function u(){let{protocol:e,hostname:r,port:t}=window.location;return`${e}//${r}${t?":"+t:""}`}function l(){let{href:e}=window.location,r=u();return e.substring(r.length)}function f(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function d(e){return e.finished||e.headersSent}function p(e){let r=e.split("?");return r[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(r[1]?`?${r.slice(1).join("?")}`:"")}async function g(e,r){let t=r.res||r.ctx&&r.ctx.res;if(!e.getInitialProps)return r.ctx&&r.Component?{pageProps:await g(r.Component,r.ctx)}:{};let n=await e.getInitialProps(r);if(t&&d(t))return n;if(!n)throw Object.defineProperty(Error(`"${f(e)}.getInitialProps()" should resolve to an object. But found "${n}" instead.`),"__NEXT_ERROR_CODE",{value:"E1025",enumerable:!1,configurable:!0});return n}let m="u">typeof performance,y=m&&["mark","measure","getEntriesByName"].every(e=>"function"==typeof performance[e]);class b extends Error{}class h extends Error{}class v extends Error{constructor(e){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message=`Cannot find module for page: ${e}`}}class S extends Error{constructor(e,r){super(),this.message=`Failed to load static file for page: ${e} ${r}`}}class P extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function E(e){return JSON.stringify({message:e.message,stack:e.stack})}},98183,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={assign:function(){return c},searchParamsToUrlQuery:function(){return i},urlQueryToSearchParams:function(){return s}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});function i(e){let r={};for(let[t,n]of e.entries()){let e=r[t];void 0===e?r[t]=n:Array.isArray(e)?e.push(n):r[t]=[e,n]}return r}function a(e){return"string"==typeof e?e:("number"!=typeof e||isNaN(e))&&"boolean"!=typeof e?"":String(e)}function s(e){let r=new URLSearchParams;for(let[t,n]of Object.entries(e))if(Array.isArray(n))for(let e of n)r.append(t,a(e));else r.set(t,a(n));return r}function c(e,...r){for(let t of r){for(let r of t.keys())e.delete(r);for(let[r,n]of t.entries())e.append(r,n)}return e}},9969,e=>{"use strict";let r=null;e.s(["captureClientEvent",0,function(e,t){if(!r||!r.enabled)return;let n=JSON.stringify({api_key:r.apiKey,event:e,distinct_id:r.distinctId,properties:{...t,$lib:"failproofai-web",failproofai_version:r.version,$current_url:window.location.href,$pathname:window.location.pathname}});fetch(r.host.replace(/\/+$/,"")+"/capture/",{method:"POST",headers:{"Content-Type":"application/json"},body:n,signal:AbortSignal.timeout(5e3)}).catch(()=>{})},"setClientTelemetryConfig",0,function(e){r=e}])},95187,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={callServer:function(){return i.callServer},createServerReference:function(){return s.createServerReference},findSourceMapURL:function(){return a.findSourceMapURL}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=e.r(32120),a=e.r(92245),s=e.r(35326)},15754,e=>{"use strict";var r=e.i(95187);let t=(0,r.createServerReference)("00fc72016b4567b19ebb6c5fead82ead1d8c03e990",r.callServer,void 0,r.findSourceMapURL,"getTelemetryConfig");e.s(["getTelemetryConfig",0,t])},53348,e=>{"use strict";var r=e.i(43476),t=e.i(71645),n=e.i(15754),o=e.i(9969);e.s(["default",0,function({error:e,reset:i}){return(0,t.useEffect)(()=>{(0,n.getTelemetryConfig)().then(r=>{(0,o.setClientTelemetryConfig)(r),(0,o.captureClientEvent)("client_error",{error_message:e.message,error_name:e.name,error_digest:e.digest,boundary:"global"})}).catch(()=>{})},[e]),(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,r.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,r.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,r.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:e.message||"An unexpected error occurred."}),(0,r.jsx)("button",{onClick:i,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])}]);
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,33525,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"warnOnce",{enumerable:!0,get:function(){return n}});let n=e=>{}},18967,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={DecodeError:function(){return b},MiddlewareNotFoundError:function(){return P},MissingStaticPage:function(){return S},NormalizeError:function(){return h},PageNotFoundError:function(){return v},SP:function(){return m},ST:function(){return y},WEB_VITALS:function(){return i},execOnce:function(){return a},getDisplayName:function(){return f},getLocationOrigin:function(){return u},getURL:function(){return l},isAbsoluteUrl:function(){return c},isResSent:function(){return d},loadGetInitialProps:function(){return g},normalizeRepeatedSlashes:function(){return p},stringifyError:function(){return E}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=["CLS","FCP","FID","INP","LCP","TTFB"];function a(e){let r,t=!1;return(...n)=>(t||(t=!0,r=e(...n)),r)}let s=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,c=e=>s.test(e);function u(){let{protocol:e,hostname:r,port:t}=window.location;return`${e}//${r}${t?":"+t:""}`}function l(){let{href:e}=window.location,r=u();return e.substring(r.length)}function f(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function d(e){return e.finished||e.headersSent}function p(e){let r=e.split("?");return r[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(r[1]?`?${r.slice(1).join("?")}`:"")}async function g(e,r){let t=r.res||r.ctx&&r.ctx.res;if(!e.getInitialProps)return r.ctx&&r.Component?{pageProps:await g(r.Component,r.ctx)}:{};let n=await e.getInitialProps(r);if(t&&d(t))return n;if(!n)throw Object.defineProperty(Error(`"${f(e)}.getInitialProps()" should resolve to an object. But found "${n}" instead.`),"__NEXT_ERROR_CODE",{value:"E1025",enumerable:!1,configurable:!0});return n}let m="u">typeof performance,y=m&&["mark","measure","getEntriesByName"].every(e=>"function"==typeof performance[e]);class b extends Error{}class h extends Error{}class v extends Error{constructor(e){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message=`Cannot find module for page: ${e}`}}class S extends Error{constructor(e,r){super(),this.message=`Failed to load static file for page: ${e} ${r}`}}class P extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function E(e){return JSON.stringify({message:e.message,stack:e.stack})}},98183,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={assign:function(){return c},searchParamsToUrlQuery:function(){return i},urlQueryToSearchParams:function(){return s}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});function i(e){let r={};for(let[t,n]of e.entries()){let e=r[t];void 0===e?r[t]=n:Array.isArray(e)?e.push(n):r[t]=[e,n]}return r}function a(e){return"string"==typeof e?e:("number"!=typeof e||isNaN(e))&&"boolean"!=typeof e?"":String(e)}function s(e){let r=new URLSearchParams;for(let[t,n]of Object.entries(e))if(Array.isArray(n))for(let e of n)r.append(t,a(e));else r.set(t,a(n));return r}function c(e,...r){for(let t of r){for(let r of t.keys())e.delete(r);for(let[r,n]of t.entries())e.append(r,n)}return e}},9969,e=>{"use strict";let r=null;e.s(["captureClientEvent",0,function(e,t){if(!r||!r.enabled)return;let n=JSON.stringify({api_key:r.apiKey,event:e,distinct_id:r.distinctId,properties:{...t,$lib:"failproofai-web",failproofai_version:r.version,$current_url:window.location.href,$pathname:window.location.pathname}});fetch(r.host.replace(/\/+$/,"")+"/capture/",{method:"POST",headers:{"Content-Type":"application/json"},body:n,signal:AbortSignal.timeout(5e3)}).catch(()=>{})},"setClientTelemetryConfig",0,function(e){r=e}])},95187,(e,r,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={callServer:function(){return i.callServer},createServerReference:function(){return s.createServerReference},findSourceMapURL:function(){return a.findSourceMapURL}};for(var o in n)Object.defineProperty(t,o,{enumerable:!0,get:n[o]});let i=e.r(32120),a=e.r(92245),s=e.r(35326)},32167,e=>{"use strict";var r=e.i(95187);let t=(0,r.createServerReference)("00da780697b4e8e1582c5a997fdeb82db1d8130bfc",r.callServer,void 0,r.findSourceMapURL,"getTelemetryConfig");e.s(["getTelemetryConfig",0,t])},53348,e=>{"use strict";var r=e.i(43476),t=e.i(71645),n=e.i(32167),o=e.i(9969);e.s(["default",0,function({error:e,reset:i}){return(0,t.useEffect)(()=>{(0,n.getTelemetryConfig)().then(r=>{(0,o.setClientTelemetryConfig)(r),(0,o.captureClientEvent)("client_error",{error_message:e.message,error_name:e.name,error_digest:e.digest,boundary:"global"})}).catch(()=>{})},[e]),(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,r.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,r.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,r.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:e.message||"An unexpected error occurred."}),(0,r.jsx)("button",{onClick:i,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])}]);
@@ -0,0 +1,80 @@
1
+ # AGENTS.md — Codex / agent guidance for this repo
2
+
3
+ This file is read by AI coding agents (Codex, Claude Code, etc.). It duplicates the most
4
+ critical rules from CLAUDE.md in a format optimised for agent consumption.
5
+
6
+ ## Non-negotiable rules
7
+
8
+ 1. **Branch must contain all commits from main.** Before every push, run:
9
+ ```bash
10
+ git fetch origin && git log --oneline origin/main ^HEAD
11
+ ```
12
+ If that prints anything, rebase first: `git rebase origin/main`. Never push a branch
13
+ that is missing commits from `main`.
14
+
15
+ 3. **One PR per branch.** Check `gh pr list --head <current-branch>` before creating a PR.
16
+ Push to the existing PR if one already exists.
17
+
18
+ 4. **CI must be green before you stop.** After every push, poll `gh run list --limit 3`
19
+ until all checks complete. Fix failures before proceeding or declaring done.
20
+
21
+ 5. **Never edit a test to make it pass.** Fix the code. Tests may only be changed when the
22
+ test itself is wrong or when the feature under test was intentionally changed.
23
+
24
+ 6. **Always add unit tests for new behaviour.** Place tests in `__tests__/`. Unit tests live
25
+ in `__tests__/hooks/`, e2e tests in `__tests__/e2e/hooks/`.
26
+
27
+ 7. **Docker is available.** Use `oven/bun:latest` with `--network=host` to do clean-install
28
+ end-to-end testing after every non-trivial implementation. See CLAUDE.md for the exact
29
+ Docker test recipe.
30
+
31
+ ## Test commands
32
+
33
+ ```bash
34
+ bun run test:run # unit tests (fast, run first)
35
+ bun run test:e2e # end-to-end hook tests (slower, run before push)
36
+ bun run lint # eslint
37
+ bunx tsc --noEmit # type check
38
+ ```
39
+
40
+ ## Docker smoke test (run after every change to src/hooks/ or package.json)
41
+
42
+ ```bash
43
+ npm pack --ignore-scripts
44
+ docker run --rm --network=host \
45
+ -v $(pwd)/failproofai-*.tgz:/pkg.tgz \
46
+ oven/bun:latest bash -c "
47
+ apt-get update -qq && apt-get install -y -qq nodejs npm 2>&1 | tail -2
48
+ npm install -g /pkg.tgz --ignore-scripts 2>&1 | tail -3
49
+ cat > /tmp/tp.mjs << 'EOF'
50
+ import { customPolicies, allow } from 'failproofai';
51
+ customPolicies.add({ name: 't', description: 't', match: { events: ['PreToolUse'] }, fn: async () => allow() });
52
+ EOF
53
+ failproofai p -i -c /tmp/tp.mjs
54
+ "
55
+ rm failproofai-*.tgz
56
+ ```
57
+
58
+ Expected: `Validated 1 custom hook(s): t`, exit 0.
59
+
60
+ ## Regression checklist
61
+
62
+ Run through this mentally after any change to `src/hooks/` or `dist/` build:
63
+
64
+ - [ ] ESM import (`from 'failproofai'`) in custom policy resolves
65
+ - [ ] CJS require (`require('failproofai')`) in custom policy resolves
66
+ - [ ] Transitive local imports inside custom policy file work
67
+ - [ ] Builtin policies run when no custom file is configured
68
+ - [ ] `failproofai p -i -c <nonexistent>` fails gracefully (no crash)
69
+ - [ ] `bun run test:run` — all unit tests pass
70
+ - [ ] `bun run test:e2e` — all e2e tests pass
71
+ - [ ] CI is green
72
+
73
+ ## Key files
74
+
75
+ | File | Purpose |
76
+ |------|---------|
77
+ | `src/hooks/loader-utils.ts` | `findDistIndex()`, ESM shim, import rewriting |
78
+ | `src/hooks/custom-hooks-loader.ts` | Top-level custom hook loading orchestrator |
79
+ | `src/index.ts` | Public API → `dist/index.js` bundle entry |
80
+ | `package.json` | `files` must include `dist/`; `build` must build `dist/index.js` |
@@ -0,0 +1,137 @@
1
+ # CLAUDE.md — Agent guidance for this repo
2
+
3
+ ## Environment
4
+
5
+ - **Runtime:** bun (≥1.3.0) and Node.js (≥20.9.0) are both present.
6
+ - **Docker CLI** is available. Use it to spin up clean containers that mimic real user
7
+ installs and validate every non-trivial change end-to-end before pushing.
8
+ - **Package manager:** bun (`bun install`, `bun run <script>`). Do not use npm/yarn to
9
+ install deps locally.
10
+
11
+ ## Workflow rules
12
+
13
+ ### One PR per branch
14
+ Each local branch maps to exactly one PR. Before opening a PR, check with
15
+ `gh pr list --head <branch>`. If one exists, push new commits to the same branch — never
16
+ open a second PR for the same branch.
17
+
18
+ ### Branch must contain all commits from main
19
+ Before pushing, verify your branch is up to date with `main`:
20
+
21
+ ```bash
22
+ git fetch origin
23
+ git log --oneline origin/main ^HEAD # should print nothing
24
+ ```
25
+
26
+ If it prints commits, rebase before pushing:
27
+
28
+ ```bash
29
+ git rebase origin/main
30
+ ```
31
+
32
+ Resolve any conflicts, then continue. Never push a branch that is missing commits from
33
+ `main` — the PR diff will be polluted and CI may test against a stale base.
34
+
35
+ ### CI must be green after every commit you push
36
+ After every `git push`, run `gh run watch` or poll `gh run list --limit 3` until all checks
37
+ finish. If any job fails, **stop and fix it before continuing**. Never leave a red CI.
38
+
39
+ The CI runs four jobs — all must pass:
40
+ | Job | Command |
41
+ |-----|---------|
42
+ | quality | lint + tsc + version-consistency check |
43
+ | test | `bun run test:run` (unit, 4 env configs) |
44
+ | build | `bun run build` (Next.js + dist/index.js) |
45
+ | test-e2e | `bun run test:e2e` |
46
+
47
+ ### Always add unit tests for new behaviour
48
+ When you add or change logic, add a corresponding test in `__tests__/`. Never modify
49
+ existing tests just to make them pass — if a test breaks, fix the code, not the test.
50
+ Exception: updating a test that explicitly tests the value you're changing (e.g. a version
51
+ string or an error message you intentionally changed).
52
+
53
+ ## Testing protocol
54
+
55
+ ### After every implementation change
56
+
57
+ 1. **Unit tests first** — fast, in-process:
58
+ ```bash
59
+ bun run test:run
60
+ ```
61
+
62
+ 2. **Local smoke test** — use the dev dist directly:
63
+ ```bash
64
+ bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts
65
+ FAILPROOFAI_DIST_PATH=$(pwd)/dist failproofai p -i -c <policy-file>
66
+ ```
67
+
68
+ 3. **Docker clean-install test** — mimics a real `npm install -g` from scratch.
69
+ Use the `oven/bun:latest` image (bun pre-installed) with `--network=host`:
70
+
71
+ ```bash
72
+ # Pack without running the full build
73
+ npm pack --ignore-scripts
74
+
75
+ docker run --rm --network=host \
76
+ -v $(pwd)/failproofai-*.tgz:/pkg.tgz \
77
+ oven/bun:latest bash -c "
78
+ apt-get update -qq && apt-get install -y -qq nodejs npm 2>&1 | tail -2
79
+ npm install -g /pkg.tgz --ignore-scripts 2>&1 | tail -3
80
+ cat > /tmp/test-policy.mjs << 'EOF'
81
+ import { customPolicies, allow } from 'failproofai';
82
+ customPolicies.add({
83
+ name: 'smoke-test',
84
+ description: 'Smoke test',
85
+ match: { events: ['PreToolUse'] },
86
+ fn: async (ctx) => allow(),
87
+ });
88
+ EOF
89
+ failproofai --version
90
+ failproofai p -i -c /tmp/test-policy.mjs
91
+ "
92
+
93
+ rm failproofai-*.tgz
94
+ ```
95
+
96
+ Expected output includes `Validated 1 custom hook(s): smoke-test` and exit 0.
97
+
98
+ 4. **E2E tests** (before pushing):
99
+ ```bash
100
+ bun run test:e2e
101
+ ```
102
+
103
+ ### Regression areas to always check
104
+
105
+ After any change to `src/hooks/`, verify these scenarios don't regress:
106
+
107
+ | Scenario | How to check |
108
+ |----------|-------------|
109
+ | Custom policy with `from 'failproofai'` ESM import | Docker clean-install test above |
110
+ | Custom policy with `require('failproofai')` CJS | Write a `.js` test file with `require` and run `p -i -c` |
111
+ | Transitive local imports in custom policy | Use `examples/policies-advanced/index.js` |
112
+ | Builtin policies still fire (no custom file) | `failproofai p -i` without `-c` |
113
+ | `findDistIndex()` fallback when `FAILPROOFAI_DIST_PATH` unset | Unset the var and test |
114
+ | `loadCustomHooks` fail-open (bad file path) | Pass a nonexistent file without `--strict` |
115
+
116
+ ## Project structure cheatsheet
117
+
118
+ ```
119
+ bin/failproofai.mjs Entry point (bun shebang); sets FAILPROOFAI_DIST_PATH
120
+ src/hooks/
121
+ custom-hooks-loader.ts Orchestrates temp-file creation + dynamic import
122
+ loader-utils.ts findDistIndex(), createEsmShim(), rewriteFileTree()
123
+ custom-hooks-registry.ts globalThis registry shared between loader and handler
124
+ policy-helpers.ts allow() / deny() / instruct()
125
+ handler.ts Called by Claude Code --hook events
126
+ manager.ts policies --install / --uninstall / list
127
+ src/index.ts Public API entry point → compiled to dist/index.js
128
+ dist/index.js CJS bundle (built by `bun run build`; shipped in npm pkg)
129
+ __tests__/ Unit + e2e tests (vitest)
130
+ examples/ Sample custom policy files
131
+ ```
132
+
133
+ ## Version bumps
134
+
135
+ When bumping the version, update **only** `package.json` (root). The CI version-consistency
136
+ check compares `packages/*/package.json` against root — that directory does not currently
137
+ exist, so no other files need updating.
@@ -24,8 +24,19 @@ if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
24
24
  );
25
25
  }
26
26
 
27
+ if (!process.env.FAILPROOFAI_DIST_PATH) {
28
+ process.env.FAILPROOFAI_DIST_PATH = resolve(
29
+ dirname(realpathSync(fileURLToPath(import.meta.url))),
30
+ "..",
31
+ "dist"
32
+ );
33
+ }
34
+
27
35
  const args = process.argv.slice(2);
28
36
 
37
+ // Normalize 'p' → 'policies' (shorthand alias)
38
+ if (args[0] === "p") args[0] = "policies";
39
+
29
40
  // --help / -h (only when not inside a subcommand that handles its own --help)
30
41
  const SUBCOMMANDS = ["policies"];
31
42
  if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
@@ -38,7 +49,7 @@ USAGE
38
49
  COMMANDS
39
50
  (no args) Launch the policy dashboard
40
51
 
41
- policies List all available policies and their status
52
+ policies, p List all available policies and their status
42
53
  policies --install, -i Enable policies in Claude Code settings
43
54
  [names...] Specific policy names to enable
44
55
  --scope user|project|local Config scope to write to (default: user)
@@ -0,0 +1,80 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+
39
+ // src/index.ts
40
+ var exports_src = {};
41
+ __export(exports_src, {
42
+ instruct: () => instruct,
43
+ getCustomHooks: () => getCustomHooks,
44
+ deny: () => deny,
45
+ customPolicies: () => customPolicies,
46
+ clearCustomHooks: () => clearCustomHooks,
47
+ allow: () => allow
48
+ });
49
+ module.exports = __toCommonJS(exports_src);
50
+
51
+ // src/hooks/custom-hooks-registry.ts
52
+ var REGISTRY_KEY = "__failproofai_custom_hooks__";
53
+ function getRegistry() {
54
+ const g = globalThis;
55
+ if (!Array.isArray(g[REGISTRY_KEY]))
56
+ g[REGISTRY_KEY] = [];
57
+ return g[REGISTRY_KEY];
58
+ }
59
+ var customPolicies = {
60
+ add(hook) {
61
+ getRegistry().push(hook);
62
+ }
63
+ };
64
+ function getCustomHooks() {
65
+ return getRegistry();
66
+ }
67
+ function clearCustomHooks() {
68
+ const g = globalThis;
69
+ g[REGISTRY_KEY] = [];
70
+ }
71
+ // src/hooks/policy-helpers.ts
72
+ function allow() {
73
+ return { decision: "allow" };
74
+ }
75
+ function deny(reason) {
76
+ return { decision: "deny", reason };
77
+ }
78
+ function instruct(reason) {
79
+ return { decision: "instruct", reason };
80
+ }
@@ -22,39 +22,39 @@ To eliminate this surface, **we pre-emptively own all common misspellings and fo
22
22
 
23
23
  **Formatting variants** — different ways to write "failproof ai":
24
24
 
25
- | Package | Install command | Status |
26
- |---------|----------------|--------|
27
- | `failproof` | `npm install -g failproof` | published |
28
- | `failproof-ai` | `npm install -g failproof-ai` | pending npm support approval |
29
- | `fail-proof-ai` | `npm install -g fail-proof-ai` | pending npm support approval |
30
- | `failproof_ai` | `npm install -g failproof_ai` | pending npm support approval |
31
- | `fail_proof_ai` | `npm install -g fail_proof_ai` | pending npm support approval |
32
- | `fail-proofai` | `npm install -g fail-proofai` | pending npm support approval |
33
-
34
- > **Why pending?** npm's spam-prevention policy blocks registration of names that normalize to the same string as an existing package after stripping punctuation (e.g. `failproof-ai` → `failproofai`). We have contacted npm support to reserve these names for anti-squatting purposes. They will be activated once approved.
25
+ | Package | Status |
26
+ |---------|--------|
27
+ | `failproof` | ✅ Published |
28
+ | `failproof-ai` | ⏳ Pending npm support |
29
+ | `fail-proof-ai` | ⏳ Pending npm support |
30
+ | `failproof_ai` | ⏳ Pending npm support |
31
+ | `fail_proof_ai` | ⏳ Pending npm support |
32
+ | `fail-proofai` | ⏳ Pending npm support |
35
33
 
36
34
  **`failprof*` typos** — missing one `o` from "proof":
37
35
 
38
- | Package | Install command |
39
- |---------|----------------|
40
- | `failprof` | `npm install -g failprof` |
41
- | `failprof-ai` | `npm install -g failprof-ai` |
42
- | `failprofai` | `npm install -g failprofai` |
43
- | `fail-prof-ai` | `npm install -g fail-prof-ai` |
44
- | `failprof_ai` | `npm install -g failprof_ai` |
36
+ | Package | Status |
37
+ |---------|--------|
38
+ | `failprof` | Published |
39
+ | `failprof-ai` | Published |
40
+ | `failprofai` | Pending npm support |
41
+ | `fail-prof-ai` | Pending npm support |
42
+ | `failprof_ai` | Pending npm support |
45
43
 
46
44
  **`faliproof*` typos** — transposed `a` and `i`:
47
45
 
48
- | Package | Install command |
49
- |---------|----------------|
50
- | `faliproof` | `npm install -g faliproof` |
51
- | `faliproof-ai` | `npm install -g faliproof-ai` |
52
- | `faliproofai` | `npm install -g faliproofai` |
46
+ | Package | Status |
47
+ |---------|--------|
48
+ | `faliproof` | Published |
49
+ | `faliproof-ai` | Published |
50
+ | `faliproofai` | Pending npm support |
51
+
52
+ > **Why pending?** npm's spam-prevention policy blocks names that normalize to the same string as an existing package after stripping punctuation and running similarity checks. We have contacted npm support to reserve these names for anti-squatting purposes. They will be activated once approved.
53
53
 
54
- All 14 aliases are published by **ExosphereHost Inc.** (the same npm account as `failproofai`). You can verify any of them:
54
+ You can verify any published alias is owned by us:
55
55
 
56
56
  ```bash
57
- npm info failproof-ai
57
+ npm info failproof
58
58
  # Look for: "ExosphereHost Inc." in the maintainers field
59
59
  ```
60
60
 
@@ -65,7 +65,7 @@ npm info failproof-ai
65
65
  Each alias package:
66
66
 
67
67
  1. Lists `failproofai` as a dependency — so the real package (including its `postinstall` hook setup) runs on install
68
- 2. Exposes a binary matching its own name (e.g. `failproof-ai`) that proxies all arguments to the `failproofai` binary
68
+ 2. Exposes a binary matching its own name (e.g. `failprof-ai`) that proxies all arguments to the `failproofai` binary
69
69
 
70
70
  The proxy is a two-line Node script; there is no logic, no network calls, and no data collection beyond what `failproofai` itself does.
71
71
 
@@ -1,7 +1,7 @@
1
1
  /**
2
- * policies-notification.js — Notification event example
2
+ * policies-notification.js — Notification and SessionEnd event examples
3
3
  *
4
- * Forwards Claude's idle notifications to Slack.
4
+ * Forwards Claude's idle notifications and session-end events to Slack.
5
5
  *
6
6
  * Prerequisites:
7
7
  * Set the SLACK_WEBHOOK_URL environment variable to your Slack incoming webhook URL.
@@ -10,8 +10,9 @@
10
10
  * failproofai --install-hooks custom ./examples/policies-notification.js
11
11
  *
12
12
  * Test by letting Claude finish a task and go idle — you should receive a Slack message.
13
+ * Test session end by exiting Claude — you should receive a session summary message.
13
14
  */
14
- import { customPolicies, allow } from "failproofai";
15
+ import { customPolicies, allow, instruct } from "failproofai";
15
16
 
16
17
  // Forward Claude idle notifications to Slack
17
18
  customPolicies.add({
@@ -22,38 +23,82 @@ customPolicies.add({
22
23
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;
23
24
  if (!webhookUrl) return allow(); // skip if not configured
24
25
 
25
- const type = String(ctx.payload?.notification_type ?? "");
26
- if (type !== "idle") return allow(); // only forward idle notifications
27
-
28
26
  const message = String(ctx.payload?.message ?? "Claude is waiting for input");
29
27
  const cwd = ctx.session?.cwd ?? "unknown";
30
28
  const sessionId = ctx.session?.sessionId ?? "unknown";
31
29
 
32
- // Fire-and-forget never block Claude if Slack is unreachable
33
- fetch(webhookUrl, {
34
- method: "POST",
35
- headers: { "Content-Type": "application/json" },
36
- body: JSON.stringify({
37
- blocks: [
38
- {
39
- type: "header",
40
- text: { type: "plain_text", text: "💬 Claude is waiting for you", emoji: true },
41
- },
42
- {
43
- type: "section",
44
- text: { type: "mrkdwn", text: message },
45
- },
46
- {
47
- type: "section",
48
- fields: [
49
- { type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
50
- { type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
51
- ],
52
- },
53
- ],
54
- }),
55
- }).catch(() => {});
56
-
57
- return allow(); // Notification hooks must always return allow
30
+ // Await so the request completes before process.exit() is called by the CLI
31
+ try {
32
+ await fetch(webhookUrl, {
33
+ method: "POST",
34
+ headers: { "Content-Type": "application/json" },
35
+ body: JSON.stringify({
36
+ blocks: [
37
+ {
38
+ type: "header",
39
+ text: { type: "plain_text", text: "💬 Claude is waiting for you", emoji: true },
40
+ },
41
+ {
42
+ type: "section",
43
+ text: { type: "mrkdwn", text: message },
44
+ },
45
+ {
46
+ type: "section",
47
+ fields: [
48
+ { type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
49
+ { type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
50
+ ],
51
+ },
52
+ ],
53
+ }),
54
+ signal: AbortSignal.timeout(5000),
55
+ });
56
+ } catch {
57
+ // Never block Claude if Slack is unreachable
58
+ }
59
+
60
+ return instruct(`We have sent the notification to the user on Slack about: ${message}`);
61
+ },
62
+ });
63
+
64
+ // Notify Slack when a Claude session ends
65
+ customPolicies.add({
66
+ name: "slack-on-session-end",
67
+ description: "Notify Slack when a Claude session ends (set SLACK_WEBHOOK_URL env var)",
68
+ match: { events: ["SessionEnd"] },
69
+ fn: async (ctx) => {
70
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL;
71
+ if (!webhookUrl) return allow(); // skip if not configured
72
+
73
+ const cwd = ctx.session?.cwd ?? "unknown";
74
+ const sessionId = ctx.session?.sessionId ?? "unknown";
75
+
76
+ // Await so the request completes before process.exit() is called by the CLI
77
+ try {
78
+ await fetch(webhookUrl, {
79
+ method: "POST",
80
+ headers: { "Content-Type": "application/json" },
81
+ body: JSON.stringify({
82
+ blocks: [
83
+ {
84
+ type: "header",
85
+ text: { type: "plain_text", text: "✅ Claude session ended", emoji: true },
86
+ },
87
+ {
88
+ type: "section",
89
+ fields: [
90
+ { type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
91
+ { type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
92
+ ],
93
+ },
94
+ ],
95
+ }),
96
+ signal: AbortSignal.timeout(5000),
97
+ });
98
+ } catch {
99
+ // Never block Claude if Slack is unreachable
100
+ }
101
+
102
+ return instruct(`We have sent the notification to the user on Slack about: Claude session ended (project: ${cwd}, session: ${sessionId})`);
58
103
  },
59
104
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.1-beta.9",
3
+ "version": "0.0.1",
4
4
  "description": "Open-source hooks, policies, and project visualization for Claude Code & Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./bin/failproofai.mjs"
@@ -11,6 +11,7 @@
11
11
  "scripts/",
12
12
  "lib/",
13
13
  ".next/standalone/",
14
+ "dist/",
14
15
  "README.md"
15
16
  ],
16
17
  "engines": {
@@ -20,7 +21,7 @@
20
21
  "scripts": {
21
22
  "predev": "bun link",
22
23
  "dev": "FAILPROOFAI_TELEMETRY_DISABLED=1 bun scripts/dev.ts --port 8020",
23
- "build": "bun --bun next build && node -e \"const {cpSync}=require('fs');cpSync('.next/static','.next/standalone/.next/static',{recursive:true});\"",
24
+ "build": "bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts && bun --bun next build && node -e \"const {cpSync}=require('fs');cpSync('.next/static','.next/standalone/.next/static',{recursive:true});\"",
24
25
  "prestart": "bun link",
25
26
  "start": "FAILPROOFAI_TELEMETRY_DISABLED=1 bun scripts/start.ts",
26
27
  "test": "vitest",