@claudeye/linux-arm64 1.0.7 → 1.0.8
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.
- package/assets/dist/hooks-api.d.ts +43 -0
- package/assets/dist/hooks-api.js +1 -0
- package/assets/dist/index.d.ts +2 -0
- package/assets/dist/index.js +1 -1
- package/assets/dist/loader.js +2 -2
- package/assets/standalone/.next/BUILD_ID +1 -1
- package/assets/standalone/.next/app-path-routes-manifest.json +2 -1
- package/assets/standalone/.next/build-manifest.json +8 -8
- package/assets/standalone/.next/prerender-manifest.json +27 -3
- package/assets/standalone/.next/routes-manifest.json +12 -6
- package/assets/standalone/.next/server/app/_global-error/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +14 -1
- package/assets/standalone/.next/server/app/_global-error/page.js +5 -5
- package/assets/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/_global-error.html +1 -1
- package/assets/standalone/.next/server/app/_global-error.rsc +7 -7
- package/assets/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/assets/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/assets/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/assets/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/assets/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/assets/standalone/.next/server/app/_not-found/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
- package/assets/standalone/.next/server/app/_not-found/page.js +6 -7
- package/assets/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/_not-found.html +2 -2
- package/assets/standalone/.next/server/app/_not-found.rsc +27 -30
- package/assets/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +27 -30
- package/assets/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/assets/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +12 -15
- package/assets/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/assets/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/assets/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/assets/standalone/.next/server/app/api/completed-history/route.js +2 -2
- package/assets/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/assets/standalone/.next/server/app/api/queue-item/route.js +2 -2
- package/assets/standalone/.next/server/app/api/queue-status/route.js +2 -2
- package/assets/standalone/.next/server/app/dashboard/[viewName]/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/dashboard/[viewName]/page/server-reference-manifest.json +5 -5
- package/assets/standalone/.next/server/app/dashboard/[viewName]/page.js +8 -9
- package/assets/standalone/.next/server/app/dashboard/[viewName]/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/dashboard/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/dashboard/page/server-reference-manifest.json +5 -5
- package/assets/standalone/.next/server/app/dashboard/page.js +9 -10
- package/assets/standalone/.next/server/app/dashboard/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/index.html +1 -0
- package/assets/standalone/.next/server/app/index.meta +15 -0
- package/assets/standalone/.next/server/app/index.rsc +22 -0
- package/assets/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +6 -0
- package/assets/standalone/.next/server/app/index.segments/_full.segment.rsc +22 -0
- package/assets/standalone/.next/server/app/index.segments/_head.segment.rsc +6 -0
- package/assets/standalone/.next/server/app/index.segments/_index.segment.rsc +12 -0
- package/assets/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -0
- package/assets/standalone/.next/server/app/login/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/login/page/server-reference-manifest.json +3 -3
- package/assets/standalone/.next/server/app/login/page.js +6 -7
- package/assets/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/login.html +2 -2
- package/assets/standalone/.next/server/app/login.rsc +30 -34
- package/assets/standalone/.next/server/app/login.segments/_full.segment.rsc +30 -34
- package/assets/standalone/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/assets/standalone/.next/server/app/login.segments/_index.segment.rsc +12 -15
- package/assets/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/assets/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/assets/standalone/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/assets/standalone/.next/server/app/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/page/server-reference-manifest.json +2 -2
- package/assets/standalone/.next/server/app/page.js +7 -8
- package/assets/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/policies/page/app-paths-manifest.json +3 -0
- package/assets/standalone/.next/server/app/{hooks → policies}/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/{hooks → policies}/page/server-reference-manifest.json +36 -24
- package/assets/standalone/.next/server/app/{hooks → policies}/page.js +9 -10
- package/assets/standalone/.next/server/app/policies/page_client-reference-manifest.js +3 -0
- package/assets/standalone/.next/server/app/project/[name]/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +2 -2
- package/assets/standalone/.next/server/app/project/[name]/page.js +7 -9
- package/assets/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/assets/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +10 -10
- package/assets/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +8 -9
- package/assets/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app/projects/page/app-paths-manifest.json +3 -0
- package/assets/standalone/.next/server/app/projects/page/build-manifest.json +18 -0
- package/assets/standalone/.next/server/app/projects/page/next-font-manifest.json +6 -0
- package/assets/standalone/.next/server/app/projects/page/react-loadable-manifest.json +1 -0
- package/assets/standalone/.next/server/app/projects/page/server-reference-manifest.json +29 -0
- package/assets/standalone/.next/server/app/projects/page.js +18 -0
- package/assets/standalone/.next/server/app/projects/page_client-reference-manifest.js +3 -0
- package/assets/standalone/.next/server/app/queue/page/build-manifest.json +5 -5
- package/assets/standalone/.next/server/app/queue/page/server-reference-manifest.json +4 -4
- package/assets/standalone/.next/server/app/queue/page.js +8 -9
- package/assets/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/app-paths-manifest.json +2 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0-_qfa9._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0.lwwrp._.js +3 -0
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__072w.g0._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__08lgurx._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0bps8pa._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0f_5ws6._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0ld-~9t._.js +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0ndqa7s._.js +8 -3
- package/assets/standalone/.next/server/chunks/{[root-of-the-server]__0wkrejt._.js → [root-of-the-server]__0rtkhr~._.js} +1 -1
- package/assets/standalone/.next/server/chunks/{[root-of-the-server]__031vvrp._.js → [root-of-the-server]__0tlbu6c._.js} +1 -1
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0vfmej5._.js +4 -0
- package/assets/standalone/.next/server/chunks/_04cgd3m._.js +11 -0
- package/assets/standalone/.next/server/chunks/_0aylu-q._.js +2 -2
- package/assets/standalone/.next/server/chunks/_0h9z2~x._.js +11 -0
- package/assets/standalone/.next/server/chunks/_0lq2_wn._.js +2 -2
- package/assets/standalone/.next/server/chunks/_0oqawqx._.js +2 -2
- package/assets/standalone/.next/server/chunks/_0vt409y._.js +2 -2
- package/assets/standalone/.next/server/chunks/lib_0a9kq7-._.js +11 -0
- package/assets/standalone/.next/server/chunks/lib_0b2.9p5._.js +11 -0
- package/assets/standalone/.next/server/chunks/lib_13x.g8a._.js +11 -0
- package/assets/standalone/.next/server/chunks/lib_eval-queue_ts_08t8vgq._.js +1 -1
- package/assets/standalone/.next/server/chunks/lib_eval-queue_ts_0h8e7f.._.js +1 -1
- package/assets/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0l15kkr.js +1 -1
- package/assets/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_oq51._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-om19y._.js +11 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wbic0._.js +45 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__02l2rag._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__04qo1w2._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__07ifmkx._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__09y6src._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__09y7ni.._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bubgf_._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l897tw._.js → [root-of-the-server]__0iv48_z._.js} +2 -2
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0j7bj94._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m0x9dt._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oy2.6r._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pw0g9c._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rm4qtt._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sn21jm._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spfsp5._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sq7snp._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uz88bg._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0x7-awk._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zva2n_._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__1370kzv._.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/_02_tcps._.js +32 -0
- package/assets/standalone/.next/server/chunks/ssr/_04.u7zx._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/_06383ya._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/_0kfjwwu._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/_0km.fsf._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/_0x26hbi._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/{_0_ia8tm._.js → _0xg44oj._.js} +2 -2
- package/assets/standalone/.next/server/chunks/ssr/_11ou~_x._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/app_04qfs8z._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_05evmdg._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_07izk21._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_0uosk1e._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_13f0ohr._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_actions_compute-dashboard_ts_13cb_9d._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_global-error_tsx_0m9qisk._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +8 -0
- package/assets/standalone/.next/server/chunks/ssr/{app_loading_tsx_05semg5._.js → app_projects_loading_tsx_13veom4._.js} +2 -2
- package/assets/standalone/.next/server/chunks/ssr/app_queue_queue-client_tsx_0c0olxv._.js +1 -1
- package/assets/standalone/.next/server/chunks/ssr/node_modules_next_dist_0~3yv~8._.js +3 -0
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0s-cxl7.js → node_modules_next_dist_esm_build_templates_app-page_06pwt.d.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0j.ij4e.js → node_modules_next_dist_esm_build_templates_app-page_091vo_-.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0a_7sdg.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0blr8v-.js → node_modules_next_dist_esm_build_templates_app-page_0ef3uwk.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0_4obh1.js → node_modules_next_dist_esm_build_templates_app-page_0ft_6~o.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js → node_modules_next_dist_esm_build_templates_app-page_0j79~gv.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0za6ylf.js → node_modules_next_dist_esm_build_templates_app-page_0pbja1x.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0r6o0i2.js +4 -0
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_13mo15i.js → node_modules_next_dist_esm_build_templates_app-page_0w9nerp.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0xv8hin.js → node_modules_next_dist_esm_build_templates_app-page_11y81~_.js} +3 -3
- package/assets/standalone/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0cmo95h.js → node_modules_next_dist_esm_build_templates_app-page_12or2kf.js} +3 -3
- package/assets/standalone/.next/server/middleware-build-manifest.js +8 -8
- package/assets/standalone/.next/server/pages/404.html +2 -2
- package/assets/standalone/.next/server/pages/500.html +1 -1
- package/assets/standalone/.next/server/server-reference-manifest.js +1 -1
- package/assets/standalone/.next/server/server-reference-manifest.json +82 -52
- package/assets/standalone/.next/static/chunks/0.~t6_2r7n04x.js +4 -0
- package/assets/standalone/.next/static/chunks/01q52wg_amm60.js +2 -0
- package/assets/standalone/.next/static/chunks/07tkt19hty~w~.js +1 -0
- package/assets/standalone/.next/static/chunks/08eqdulekk8l-.js +1 -0
- package/assets/standalone/.next/static/chunks/0_s0luks5tay-.js +1 -0
- package/assets/standalone/.next/static/chunks/0c0j9cso1kkr_.css +1 -0
- package/assets/standalone/.next/static/chunks/0dji_r0j6sq~f.js +1 -0
- package/assets/standalone/.next/static/chunks/0h2.~bz5i.4.g.js +1 -0
- package/assets/standalone/.next/static/chunks/0l8ej7oju~.oy.js +1 -0
- package/assets/standalone/.next/static/chunks/{053grpfc7gghs.js → 0nc3no0r1i66_.js} +1 -1
- package/assets/standalone/.next/static/chunks/0tp7ck~nhe8-g.js +3 -0
- package/assets/standalone/.next/static/chunks/0tyw4u3~2isbh.js +1 -0
- package/assets/standalone/.next/static/chunks/{0h4bq73pogmtb.js → 0u95dh6lcl7tm.js} +31 -1
- package/assets/standalone/.next/static/chunks/0y8d5uz5f1m~..js +6 -0
- package/assets/standalone/.next/static/chunks/10qr1cqizt~sp.js +1 -0
- package/assets/standalone/.next/static/chunks/{0lkh25hviflid.js → 11nnvwnj7e__0.js} +1 -1
- package/assets/standalone/.next/static/chunks/14trwlfhtz63l.js +1 -0
- package/assets/standalone/.next/static/chunks/{04aem330ymbdp.js → 150i0n26fnvso.js} +1 -1
- package/assets/standalone/.next/static/chunks/15fklyav5py5m.js +1 -0
- package/assets/standalone/.next/static/chunks/162jxog7oodad.js +1 -0
- package/assets/standalone/.next/static/chunks/17.b3suj8zjjj.js +1 -0
- package/assets/standalone/.next/static/chunks/{turbopack-05k-ytyhplqy7.js → turbopack-0.w3uhj.ln7pt.js} +1 -1
- package/assets/standalone/README.md +119 -5
- package/assets/standalone/design-docs/custom-hooks.md +722 -0
- package/assets/standalone/design-docs/e2e-hook-testing.md +420 -0
- package/assets/standalone/package.json +4 -2
- package/assets/standalone/vitest.config.e2e.mts +21 -0
- package/bin/claudeye +0 -0
- package/package.json +1 -1
- package/assets/standalone/.next/server/app/hooks/page/app-paths-manifest.json +0 -3
- package/assets/standalone/.next/server/app/hooks/page_client-reference-manifest.js +0 -3
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0_03u6.._.js +0 -4
- package/assets/standalone/.next/server/chunks/[root-of-the-server]__0ynza7q._.js +0 -3
- package/assets/standalone/.next/server/chunks/_0fx1kj9._.js +0 -7
- package/assets/standalone/.next/server/chunks/_12qi01p._.js +0 -7
- package/assets/standalone/.next/server/chunks/lib_0it0qxy._.js +0 -7
- package/assets/standalone/.next/server/chunks/lib_0k3u5ri._.js +0 -7
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-j~_e.._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__00fve8g._.js +0 -36
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__028-6zz._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__02x4-91._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__06tqxbp._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__08a4wva._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0a-4fjm._.js +0 -33
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cnf9rp._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dsucx_._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e9ohz2._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0fltiz1._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ksoh6e._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ni-as.._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0tsew7c._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0x5abxa._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yi-883._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zn.1q2._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__10_wjge._.js +0 -4
- package/assets/standalone/.next/server/chunks/ssr/[root-of-the-server]__11xi38p._.js +0 -7
- package/assets/standalone/.next/server/chunks/ssr/_03l0_j6._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/_050qnuh._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/_0_~1-jh._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/_0ndt7tj._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/_0uig1kl._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/_0vc9nu-._.js +0 -32
- package/assets/standalone/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/app_hooks_hooks-client_tsx_0n~.yoz._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/node_modules_0gyjybr._.js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_06j6hww._.js +0 -33
- package/assets/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js +0 -3
- package/assets/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0~flnue.js +0 -4
- package/assets/standalone/.next/static/chunks/0-06jeyuuf3mf.js +0 -1
- package/assets/standalone/.next/static/chunks/0.z9o1xk80npg.js +0 -2
- package/assets/standalone/.next/static/chunks/01xlw8hd842-c.js +0 -1
- package/assets/standalone/.next/static/chunks/09f624.buk6jr.js +0 -1
- package/assets/standalone/.next/static/chunks/0g5cvlv5.8drd.js +0 -3
- package/assets/standalone/.next/static/chunks/0g~w.4hiyi_1j.js +0 -1
- package/assets/standalone/.next/static/chunks/0il8_19l.4u-p.js +0 -1
- package/assets/standalone/.next/static/chunks/0j1-mgai_b50s.css +0 -1
- package/assets/standalone/.next/static/chunks/0ljjv.c03-d.y.js +0 -1
- package/assets/standalone/.next/static/chunks/0n6vo_gsuay4v.js +0 -4
- package/assets/standalone/.next/static/chunks/0nntcr2py4m14.js +0 -1
- package/assets/standalone/.next/static/chunks/0sp_gwqc7ka~z.js +0 -1
- package/assets/standalone/.next/static/chunks/0z0dljl8fz7.-.js +0 -1
- package/assets/standalone/.next/static/chunks/0z9fpf50h7mcq.js +0 -1
- package/assets/standalone/.next/static/chunks/115dplafwys-z.js +0 -31
- package/assets/standalone/.next/static/chunks/119bhqt_bddam.js +0 -1
- /package/assets/standalone/.next/server/app/{hooks → policies}/page/next-font-manifest.json +0 -0
- /package/assets/standalone/.next/server/app/{hooks → policies}/page/react-loadable-manifest.json +0 -0
- /package/assets/standalone/.next/static/{l1G516WtxNj1aLrIr5STH → 5FBEWHgw7h8OR_JoJzccY}/_buildManifest.js +0 -0
- /package/assets/standalone/.next/static/{l1G516WtxNj1aLrIr5STH → 5FBEWHgw7h8OR_JoJzccY}/_clientMiddlewareManifest.js +0 -0
- /package/assets/standalone/.next/static/{l1G516WtxNj1aLrIr5STH → 5FBEWHgw7h8OR_JoJzccY}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
STATUS: COMPLETED
|
|
2
|
+
|
|
3
|
+
# Custom Hooks Design
|
|
4
|
+
|
|
5
|
+
## Instructions for the Implementing Agent
|
|
6
|
+
|
|
7
|
+
1. **Branch** — create a fresh branch from the latest `main` before making any changes:
|
|
8
|
+
```bash
|
|
9
|
+
git checkout main && git pull && git checkout -b feat/custom-hooks
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. **Verify each step** — after implementing each logical unit (policy params mechanism,
|
|
13
|
+
schema on `BuiltinPolicyDefinition`, per-policy wiring, custom hooks registry,
|
|
14
|
+
loader integration, CLI flag), verify it compiles and existing tests still pass before
|
|
15
|
+
moving to the next step. Do not batch all changes and verify at the end.
|
|
16
|
+
|
|
17
|
+
3. **Keep the checklist current** — after completing each checklist item at the bottom of
|
|
18
|
+
this document, immediately mark it `[x]`. Do not defer checklist updates to the end.
|
|
19
|
+
The checklist is the source of truth for progress — if it is not marked, it is not done.
|
|
20
|
+
|
|
21
|
+
4. **Unit tests** — every new piece of logic must have unit tests:
|
|
22
|
+
- Param merging (project → local → global precedence, scalar override, array union for `enabledPolicies`)
|
|
23
|
+
- Each parameterized builtin policy (default behavior unchanged, custom param applied correctly)
|
|
24
|
+
- `customHooks` registry (`.add()`, `getCustomHooks()`, `globalThis` isolation)
|
|
25
|
+
- Hook handler loading `customHooksPath` from config and registering custom hooks
|
|
26
|
+
- `--list-hooks` output with params and custom hooks section
|
|
27
|
+
|
|
28
|
+
5. **Documentation** — update `README.md` with:
|
|
29
|
+
- `policyParams` config reference for each parameterized policy
|
|
30
|
+
- `--custom-hooks <path>` CLI flag
|
|
31
|
+
- `customHooks.add()` authoring guide with a complete example
|
|
32
|
+
|
|
33
|
+
6. **Changelog** — add an entry to `CHANGELOG.md` under a new `## [1.0.8-beta.0]` heading
|
|
34
|
+
describing both features (policy params + custom JS hooks).
|
|
35
|
+
|
|
36
|
+
7. **Version** — bump `package.json` version to `1.0.8-beta.0`. Also update the version in
|
|
37
|
+
any platform-specific `packages/*/package.json` files if they carry their own version field.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Problem / Motivation
|
|
42
|
+
|
|
43
|
+
Claudeye's builtin policies cover the common security baseline, but they're all-or-nothing
|
|
44
|
+
today:
|
|
45
|
+
|
|
46
|
+
- `block-sudo` blocks *all* sudo — you can't say "allow `sudo systemctl status`"
|
|
47
|
+
- `sanitize-api-keys` covers OpenAI/GitHub/AWS — you can't add your company's key format
|
|
48
|
+
- `block-read-outside-cwd` has no path allowlist
|
|
49
|
+
- `block-push-master` assumes `main`/`master` — you can't configure other protected branches
|
|
50
|
+
|
|
51
|
+
And there's no way to add entirely new logic: "deny writes unless a Jira ticket is in the
|
|
52
|
+
prompt", "call our internal approval API before destructive Bash", "notify Slack on every
|
|
53
|
+
session end."
|
|
54
|
+
|
|
55
|
+
Two distinct needs, both unsolved today:
|
|
56
|
+
|
|
57
|
+
1. **Configure existing policies** — tune behavior without writing code
|
|
58
|
+
2. **Fully custom hooks** — arbitrary logic tied to any hook event
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Goals & Non-Goals
|
|
63
|
+
|
|
64
|
+
### Goals
|
|
65
|
+
- Params for builtin policies with sensible defaults — users who configure nothing get
|
|
66
|
+
identical behavior to today
|
|
67
|
+
- Custom hook logic in a `hooks.js` file using the same authoring experience as `eval.js`
|
|
68
|
+
- Same `allow` / `deny` / `instruct` semantics throughout
|
|
69
|
+
- Minimal ceremony: one config file, one optional code file
|
|
70
|
+
- Project-local additions on top of global config
|
|
71
|
+
|
|
72
|
+
### Non-Goals
|
|
73
|
+
- GUI editor (CLI-first)
|
|
74
|
+
- Hook sharing / marketplace
|
|
75
|
+
- Sandboxing custom JS (user-trusted code, full user privileges)
|
|
76
|
+
- Hot reloading (hooks load fresh per event — acceptable given the ephemeral process model)
|
|
77
|
+
- Per-custom-hook configuration via `policyParams` (deferred — see open items)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Architecture
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
Claude Code event fires
|
|
85
|
+
→ claudeye --hook <EventType>
|
|
86
|
+
→ load ~/.claudeye/hooks-config.json (merged with .claudeye/hooks-config.json)
|
|
87
|
+
→ register builtin policies with their resolved params
|
|
88
|
+
→ register custom JS hooks (auto-discovered hooks.js files)
|
|
89
|
+
→ evaluate all in priority order
|
|
90
|
+
→ first deny short-circuits; instruct accumulates
|
|
91
|
+
→ write response to stdout
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Two layers, evaluated in order:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
[1] Builtin policies (with params) → fast, well-tested, security-critical
|
|
98
|
+
[2] Custom JS hook modules → full power, async, arbitrary logic
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Part 1: Policy Params
|
|
104
|
+
|
|
105
|
+
### Mechanism
|
|
106
|
+
|
|
107
|
+
`PolicyContext` gains an optional `params` field:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
interface PolicyContext {
|
|
111
|
+
eventType: HookEventType;
|
|
112
|
+
payload: Record<string, unknown>;
|
|
113
|
+
toolName?: string;
|
|
114
|
+
toolInput?: Record<string, unknown>;
|
|
115
|
+
session?: SessionMetadata;
|
|
116
|
+
params?: Record<string, unknown>;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Each parameterizable builtin declares a schema with required default values on its
|
|
121
|
+
`BuiltinPolicyDefinition`. At load time, Claudeye merges `policyParams[policy.name]` from
|
|
122
|
+
config over those defaults and injects the result into `ctx.params`. Policy functions read
|
|
123
|
+
`ctx.params` directly — no null-guarding needed since defaults are always applied first.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
function blockSudo(ctx: PolicyContext): PolicyResult {
|
|
127
|
+
const cmd = getCommand(ctx);
|
|
128
|
+
if (!/\bsudo\b/.test(cmd)) return allow();
|
|
129
|
+
|
|
130
|
+
const allowPatterns = ctx.params!.allowPatterns as string[];
|
|
131
|
+
if (allowPatterns.some((p) => matchesAllowedCommand(cmd, p))) return allow();
|
|
132
|
+
|
|
133
|
+
return deny("sudo command blocked by claudeye");
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
> **Implementation note — `allowPatterns` matching:** patterns must be matched against the
|
|
138
|
+
> parsed command (executable + arguments as tokens), not the raw command string. Raw-string
|
|
139
|
+
> glob matching allows bypass via appended shell operators (e.g. `sudo systemctl status x; rm -rf /`
|
|
140
|
+
> matches `sudo systemctl status *`). See adversarial review finding #3.
|
|
141
|
+
|
|
142
|
+
### Schema definition
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
interface BuiltinPolicyDefinition {
|
|
146
|
+
name: string;
|
|
147
|
+
description: string;
|
|
148
|
+
fn: PolicyFunction;
|
|
149
|
+
match: PolicyMatcher;
|
|
150
|
+
defaultEnabled: boolean;
|
|
151
|
+
category: string;
|
|
152
|
+
beta?: boolean;
|
|
153
|
+
params?: PolicyParamsSchema;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface PolicyParamsSchema {
|
|
157
|
+
[paramName: string]: {
|
|
158
|
+
type: "string" | "number" | "boolean" | "string[]" | "pattern[]";
|
|
159
|
+
description: string;
|
|
160
|
+
default: unknown; // every param must ship with a default
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Builtin policies with params
|
|
166
|
+
|
|
167
|
+
| Policy | Param | Type | Default |
|
|
168
|
+
|---|---|---|---|
|
|
169
|
+
| `block-sudo` | `allowPatterns` | `string[]` | `[]` |
|
|
170
|
+
| `block-rm-rf` | `allowPaths` | `string[]` | `[]` |
|
|
171
|
+
| `block-read-outside-cwd` | `allowPaths` | `string[]` | `[]` |
|
|
172
|
+
| `block-push-master` | `protectedBranches` | `string[]` | `["main", "master"]` |
|
|
173
|
+
| `block-work-on-main` | `protectedBranches` | `string[]` | `["main", "master"]` |
|
|
174
|
+
| `sanitize-api-keys` | `additionalPatterns` | `pattern[]` | `[]` |
|
|
175
|
+
| `block-secrets-write` | `additionalPatterns` | `string[]` | `[]` |
|
|
176
|
+
| `warn-large-file-write` | `thresholdKb` | `number` | `1024` |
|
|
177
|
+
|
|
178
|
+
### Config shape
|
|
179
|
+
|
|
180
|
+
`policyParams` is a new top-level key in `hooks-config.json`, keyed by builtin policy name:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"enabledPolicies": ["block-sudo", "block-push-master", "sanitize-api-keys"],
|
|
185
|
+
"policyParams": {
|
|
186
|
+
"block-sudo": {
|
|
187
|
+
"allowPatterns": ["sudo systemctl status", "sudo journalctl"]
|
|
188
|
+
},
|
|
189
|
+
"block-push-master": {
|
|
190
|
+
"protectedBranches": ["main", "release", "prod"]
|
|
191
|
+
},
|
|
192
|
+
"sanitize-api-keys": {
|
|
193
|
+
"additionalPatterns": [
|
|
194
|
+
{ "regex": "myco_[A-Za-z0-9]{32}", "label": "MyCo internal API key" }
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Config scopes and merge strategy
|
|
202
|
+
|
|
203
|
+
Three config files, evaluated in priority order:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
[1] .claudeye/hooks-config.json ← project (committed to repo)
|
|
207
|
+
[2] .claudeye/hooks-config.local.json ← local (gitignored, personal overrides)
|
|
208
|
+
[3] ~/.claudeye/hooks-config.json ← global (user-level fallback)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**`enabledPolicies`** — union across all three. A policy enabled at any level is active.
|
|
212
|
+
|
|
213
|
+
**`policyParams`** — per-policy, first level that defines it wins entirely. No merging of
|
|
214
|
+
values. If `block-sudo` has params in the project config, those are used as-is; the local
|
|
215
|
+
and global params for `block-sudo` are ignored.
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
project: block-sudo → { allowPatterns: ["sudo apt-get update"] }
|
|
219
|
+
global: block-sudo → { allowPatterns: ["sudo systemctl status"] }
|
|
220
|
+
|
|
221
|
+
resolved: { allowPatterns: ["sudo apt-get update"] } ← project wins, global ignored
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
project: (no block-sudo params)
|
|
226
|
+
local: (no block-sudo params)
|
|
227
|
+
global: block-sudo → { allowPatterns: ["sudo systemctl status"] }
|
|
228
|
+
|
|
229
|
+
resolved: { allowPatterns: ["sudo systemctl status"] } ← falls through to global
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
This means to change a policy's params for a project, you own the full value — you can't
|
|
233
|
+
partially extend the global list. That's intentional: explicit is better than surprising
|
|
234
|
+
concatenation.
|
|
235
|
+
|
|
236
|
+
### `--list-hooks` display
|
|
237
|
+
|
|
238
|
+
Configured params are shown as a summary line beneath each policy:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
✓ block-sudo Block sudo commands
|
|
242
|
+
allowPatterns: ["sudo systemctl status", "sudo journalctl"]
|
|
243
|
+
✓ block-push-master Block pushing to main/master
|
|
244
|
+
protectedBranches: ["main", "release", "prod"]
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Unknown keys in `policyParams` (e.g. typos) are flagged here — not at hook-fire time.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Part 2: Custom JS Hooks
|
|
252
|
+
|
|
253
|
+
### API design
|
|
254
|
+
|
|
255
|
+
Modeled after the `--evals` pattern. Users import `customHooks` and the decision helpers
|
|
256
|
+
from `claudeye`, call `.add()` to register hooks as side effects, then export `customHooks`.
|
|
257
|
+
The registry is backed by `globalThis` — same mechanism as the evals dashboard/alert registries.
|
|
258
|
+
|
|
259
|
+
The three decision helpers — `allow()`, `deny(message)`, `instruct(message)` — are the same
|
|
260
|
+
helpers used internally by every builtin policy. Custom hook authors use these instead of
|
|
261
|
+
returning raw objects. The `deny()` message is prefixed with `"Blocked by claudeye:"` in the
|
|
262
|
+
evaluator output, consistent with builtin policy deny messages.
|
|
263
|
+
|
|
264
|
+
```js
|
|
265
|
+
// my-hooks.js
|
|
266
|
+
import { customHooks, allow, deny, instruct } from 'claudeye';
|
|
267
|
+
import { checkApproval } from './approval-client.js'; // local module — works fine
|
|
268
|
+
|
|
269
|
+
customHooks.add({
|
|
270
|
+
name: "require-ticket-on-commit",
|
|
271
|
+
description: "Block git commits until CURRENT_TICKET env var is set",
|
|
272
|
+
match: { events: ["PreToolUse"] },
|
|
273
|
+
fn: async (ctx) => {
|
|
274
|
+
if (ctx.toolName !== "Bash") return allow();
|
|
275
|
+
const cmd = ctx.toolInput?.command ?? "";
|
|
276
|
+
if (!cmd.includes("git commit")) return allow();
|
|
277
|
+
|
|
278
|
+
if (!process.env.CURRENT_TICKET) {
|
|
279
|
+
return instruct("Set CURRENT_TICKET env var before committing.");
|
|
280
|
+
}
|
|
281
|
+
return allow();
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
customHooks.add({
|
|
286
|
+
name: "approval-gate",
|
|
287
|
+
description: "Call internal approval API before destructive Bash",
|
|
288
|
+
match: { events: ["PreToolUse"] },
|
|
289
|
+
fn: async (ctx) => {
|
|
290
|
+
if (ctx.toolName !== "Bash") return allow();
|
|
291
|
+
const cmd = ctx.toolInput?.command ?? "";
|
|
292
|
+
if (!/rm|drop|truncate/i.test(cmd)) return allow();
|
|
293
|
+
|
|
294
|
+
const approved = await checkApproval(cmd, ctx.session?.sessionId);
|
|
295
|
+
return approved
|
|
296
|
+
? allow()
|
|
297
|
+
: deny("Destructive command requires approval — visit approvals.internal");
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
export { customHooks };
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Providing the hooks file
|
|
305
|
+
|
|
306
|
+
The path to the hooks file is given explicitly via `--custom-hooks <path>` when installing
|
|
307
|
+
hooks. It is stored in `hooks-config.json` and read by the hook handler at event-fire time.
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
claudeye --install-hooks --custom-hooks ./my-hooks.js
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
`hooks-config.json` stores the resolved absolute path:
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"enabledPolicies": ["block-sudo"],
|
|
318
|
+
"customHooksPath": "/home/alice/myproject/my-hooks.js"
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
To update or remove:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
claudeye --install-hooks --custom-hooks ./new-hooks.js # replace path
|
|
326
|
+
claudeye --install-hooks --remove-custom-hooks # clear
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Loading
|
|
330
|
+
|
|
331
|
+
Same loader as `--evals eval.js` (`lib/evals/loader.ts`), applied to `customHooksPath`:
|
|
332
|
+
|
|
333
|
+
1. **ESM/CJS compat** — writes temp `.mjs` files so Node always treats the file as ESM
|
|
334
|
+
2. **`import { ... } from 'claudeye'`** — rewrites to the actual dist path via an ESM shim,
|
|
335
|
+
so `customHooks` resolves to the same `globalThis`-backed registry instance
|
|
336
|
+
3. **Transitive imports** — recursively rewrites all local relative imports reachable from
|
|
337
|
+
the entry file, so `import { checkApproval } from './approval-client.js'` works exactly
|
|
338
|
+
as the user expects
|
|
339
|
+
|
|
340
|
+
After import, the hook handler reads the registered hooks from the `globalThis` registry.
|
|
341
|
+
|
|
342
|
+
### Registry and `customHooks` object
|
|
343
|
+
|
|
344
|
+
New singleton in `src/hooks/custom-hooks-registry.ts`, mirroring the evals registry pattern:
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
// src/hooks/custom-hooks-registry.ts
|
|
348
|
+
const REGISTRY_KEY = "__claudeye_custom_hooks__";
|
|
349
|
+
|
|
350
|
+
export const customHooks = {
|
|
351
|
+
add(hook: CustomHook): void {
|
|
352
|
+
const g = globalThis as Record<string, unknown>;
|
|
353
|
+
if (!Array.isArray(g[REGISTRY_KEY])) g[REGISTRY_KEY] = [];
|
|
354
|
+
(g[REGISTRY_KEY] as CustomHook[]).push(hook);
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
export function getCustomHooks(): CustomHook[] {
|
|
359
|
+
const g = globalThis as Record<string, unknown>;
|
|
360
|
+
return Array.isArray(g[REGISTRY_KEY]) ? (g[REGISTRY_KEY] as CustomHook[]) : [];
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`customHooks` is exported from the `claudeye` package alongside `createApp`.
|
|
365
|
+
|
|
366
|
+
### `CustomHook` type and decision helpers
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
export interface CustomHook {
|
|
370
|
+
name: string;
|
|
371
|
+
description?: string;
|
|
372
|
+
match?: {
|
|
373
|
+
events?: HookEventType[];
|
|
374
|
+
};
|
|
375
|
+
fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Decision helpers — use these instead of returning raw objects
|
|
379
|
+
export function allow(): PolicyResult;
|
|
380
|
+
export function deny(message: string): PolicyResult;
|
|
381
|
+
export function instruct(message: string): PolicyResult;
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
The helpers are the same functions used internally by every builtin policy, now exported as
|
|
385
|
+
part of the public API. The `deny(message)` output is prefixed with `"Blocked by claudeye:"`
|
|
386
|
+
by the evaluator — consistent with how builtin deny messages are surfaced to Claude.
|
|
387
|
+
|
|
388
|
+
Tool filtering is the hook's responsibility — inspect `ctx.toolName` and `ctx.toolInput`
|
|
389
|
+
inside `fn`. The framework only filters by event type.
|
|
390
|
+
|
|
391
|
+
### Failure modes
|
|
392
|
+
|
|
393
|
+
| Failure | Behavior |
|
|
394
|
+
|---|---|
|
|
395
|
+
| `customHooksPath` not set | Skip — no custom hooks, builtins run normally |
|
|
396
|
+
| File not found at path | Log to `~/.claudeye/hook.log`, skip, builtins run normally |
|
|
397
|
+
| Import / syntax error | Log to `~/.claudeye/hook.log`, skip entire file, builtins run normally |
|
|
398
|
+
| `fn` throws at runtime | Log error, treat as `allow` for that hook only |
|
|
399
|
+
| `fn` exceeds 10s | Timeout, treat as `allow`, log warning |
|
|
400
|
+
|
|
401
|
+
Fail-open throughout. See exospherehost/claudeye#154 for future fail-closed support.
|
|
402
|
+
|
|
403
|
+
### `--list-hooks` display
|
|
404
|
+
|
|
405
|
+
```
|
|
406
|
+
Claudeye Hook Policies (user)
|
|
407
|
+
|
|
408
|
+
Status Name Description
|
|
409
|
+
────── ─────────────────────────────────────────────────────────────
|
|
410
|
+
✓ block-sudo Block sudo commands
|
|
411
|
+
allowPatterns: ["sudo systemctl status"]
|
|
412
|
+
✓ block-push-master Block pushing to main/master
|
|
413
|
+
|
|
414
|
+
── Custom Hooks (/home/alice/myproject/my-hooks.js) ───────────────
|
|
415
|
+
✓ require-ticket-on-commit Block git commits without ticket
|
|
416
|
+
✓ approval-gate Approval gate for destructive commands
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Unified Config Shape
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
interface HooksConfig {
|
|
425
|
+
enabledPolicies: string[];
|
|
426
|
+
llm?: LlmConfig;
|
|
427
|
+
policyParams?: {
|
|
428
|
+
[builtinPolicyName: string]: Record<string, unknown>;
|
|
429
|
+
};
|
|
430
|
+
customHooksPath?: string; // absolute path, set via --custom-hooks <path>
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Decisions
|
|
437
|
+
|
|
438
|
+
| Topic | Decision |
|
|
439
|
+
|---|---|
|
|
440
|
+
| Every param has a default | Yes — unconfigured users get identical behavior to today |
|
|
441
|
+
| Config scopes | Three levels: project → local → global (priority order) |
|
|
442
|
+
| Config merging | `enabledPolicies`: union across all levels. `policyParams`: per-policy, first level that defines it wins entirely — no value merging. |
|
|
443
|
+
| Declarative JSON rules | Dropped — params cover no-code tuning; `hooks.js` covers custom logic |
|
|
444
|
+
| JS loading mechanism | Reuse `lib/evals/loader.ts` verbatim — same as `eval.js`, handles ESM compat + transitive imports |
|
|
445
|
+
| Custom hooks API | `import { customHooks } from 'claudeye'` → `customHooks.add()` → `export { customHooks }` |
|
|
446
|
+
| Custom hooks path | Explicit via `--custom-hooks <path>`, stored as `customHooksPath` in `hooks-config.json` |
|
|
447
|
+
| Unknown `policyParams` keys | Warn in `--list-hooks`, silent at fire time |
|
|
448
|
+
| Custom hook failures | Fail-open — tracked in #154 for future fail-closed support |
|
|
449
|
+
| Per-custom-hook params (`ctx.params`) | Deferred |
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Implementation Checklist
|
|
454
|
+
|
|
455
|
+
Mark each item `[x]` as you complete it. Do not move to the next section until all items in
|
|
456
|
+
the current section are checked and the project compiles cleanly.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
### 1. Branch setup
|
|
461
|
+
- [x] `git checkout main && git pull`
|
|
462
|
+
- [x] `git checkout -b feat/custom-hooks`
|
|
463
|
+
- [x] Confirm `npm run build` (or equivalent) passes on clean main before any changes
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
### 2. Type definitions — `src/hooks/policy-types.ts`
|
|
468
|
+
- [x] Add `params?: Record<string, unknown>` to `PolicyContext`
|
|
469
|
+
- [x] Add `PolicyParamsSchema` interface with `type`, `description`, `default` per param key
|
|
470
|
+
- [x] Add `params?: PolicyParamsSchema` field to `BuiltinPolicyDefinition`
|
|
471
|
+
- [x] Add `policyParams?: Record<string, Record<string, unknown>>` to `HooksConfig`
|
|
472
|
+
- [x] Add `customHooksPath?: string` to `HooksConfig`
|
|
473
|
+
- [x] Verify project still compiles after type changes
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
### 3. Config loading — `src/hooks/hooks-config.ts`
|
|
478
|
+
- [x] Implement three-scope config discovery: project (`.claudeye/hooks-config.json`) → local (`.claudeye/hooks-config.local.json`) → global (`~/.claudeye/hooks-config.json`)
|
|
479
|
+
- [x] `enabledPolicies`: union across all three scopes (dedup)
|
|
480
|
+
- [x] `policyParams`: per-policy key, first scope that defines it wins entirely — no merging of values
|
|
481
|
+
- [x] `customHooksPath`: first scope that defines it wins
|
|
482
|
+
- [x] Expose a `readMergedHooksConfig(cwd?: string): HooksConfig` function that returns the fully merged result
|
|
483
|
+
- [x] Existing `readHooksConfig()` remains unchanged for callers that only need the global file
|
|
484
|
+
- [x] Verify project still compiles
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
### 4. Parameterize builtin policies — `src/hooks/builtin-policies.ts`
|
|
489
|
+
|
|
490
|
+
For each policy below: add `params` schema to its `BuiltinPolicyDefinition` entry and update the function body to read from `ctx.params` (never null-guard — defaults are always pre-applied by the evaluator).
|
|
491
|
+
|
|
492
|
+
- [x] `block-sudo` — param: `allowPatterns: string[]`, default `[]`. Match against parsed argv tokens, not raw string (see Open Items — allowPatterns safety)
|
|
493
|
+
- [x] `block-rm-rf` — param: `allowPaths: string[]`, default `[]`
|
|
494
|
+
- [x] `block-read-outside-cwd` — param: `allowPaths: string[]`, default `[]`
|
|
495
|
+
- [x] `block-push-master` — param: `protectedBranches: string[]`, default `["main", "master"]`. Replace hardcoded branch names with param value
|
|
496
|
+
- [x] `block-work-on-main` — param: `protectedBranches: string[]`, default `["main", "master"]`
|
|
497
|
+
- [x] `sanitize-api-keys` — param: `additionalPatterns: { regex: string; label: string }[]`, default `[]`
|
|
498
|
+
- [x] `block-secrets-write` — param: `additionalPatterns: string[]`, default `[]`
|
|
499
|
+
- [x] `warn-large-file-write` — param: `thresholdKb: number`, default `1024`
|
|
500
|
+
- [x] Verify that all policies with no user-provided params behave identically to before
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
### 5. Params injection — `src/hooks/policy-evaluator.ts`
|
|
505
|
+
- [x] Before calling each policy's `fn`, resolve `ctx.params` by merging `policyParams[policy.name]` over the policy's declared schema defaults
|
|
506
|
+
- [x] Use `readMergedHooksConfig()` (from step 3) instead of `readHooksConfig()` when loading config inside the evaluator
|
|
507
|
+
- [x] Verify project still compiles
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
### 6. Custom hooks registry — `src/hooks/custom-hooks-registry.ts` (new file)
|
|
512
|
+
- [x] Create `globalThis`-backed registry under key `__claudeye_custom_hooks__`
|
|
513
|
+
- [x] Implement `customHooks.add(hook: CustomHook): void`
|
|
514
|
+
- [x] Implement `getCustomHooks(): CustomHook[]`
|
|
515
|
+
- [x] Implement `clearCustomHooks(): void` (needed for test isolation)
|
|
516
|
+
- [x] Export `customHooks` object and `getCustomHooks`, `clearCustomHooks` functions
|
|
517
|
+
- [x] Verify project still compiles
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
### 7. Package exports
|
|
522
|
+
- [x] Export `customHooks` from the main `claudeye` package entry point
|
|
523
|
+
- [x] Export `allow`, `deny`, `instruct` helper functions from the main entry point (move them out of `builtin-policies.ts` or re-export them — they must be the same functions, not copies)
|
|
524
|
+
- [x] Export `CustomHook` type from the main entry point
|
|
525
|
+
- [x] Export `PolicyContext`, `PolicyResult`, `HookEventType` if not already exported (users need these for type annotations in their hooks file)
|
|
526
|
+
- [x] Verify `import { customHooks, allow, deny, instruct } from 'claudeye'` resolves correctly in a test import
|
|
527
|
+
- [x] Verify the ESM shim in the loader (`createEsmShim`) exports `allow`, `deny`, `instruct` alongside `customHooks` and `createApp`
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
### 8. Custom hooks loader — `src/hooks/custom-hooks-loader.ts` (new file)
|
|
532
|
+
- [x] Read `customHooksPath` from merged config; if absent, return early (no custom hooks)
|
|
533
|
+
- [x] Resolve the path to absolute
|
|
534
|
+
- [x] Check file exists; if not, log a warning to `~/.claudeye/hook.log` and return
|
|
535
|
+
- [x] Adapt `lib/evals/loader.ts` for the hook handler context: set a `__CLAUDEYE_LOADING_HOOKS__` flag on `globalThis` before import (parallel to `__CLAUDEYE_LOADING_EVALS__`), clear it after
|
|
536
|
+
- [x] Rewrite `from 'claudeye'` imports to the actual dist path so `customHooks` resolves to the same `globalThis` registry instance
|
|
537
|
+
- [x] Handle transitive relative imports (same recursive rewrite as the evals loader)
|
|
538
|
+
- [x] Clean up all temp `.mjs` files in a `finally` block
|
|
539
|
+
- [x] On import/syntax error: catch, log full error to `~/.claudeye/hook.log`, continue without crashing
|
|
540
|
+
- [x] After successful load, call `getCustomHooks()` and return the registered hooks
|
|
541
|
+
- [x] Verify project still compiles
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
### 9. Hook handler wiring — `src/hooks/handler.ts`
|
|
546
|
+
- [x] After registering builtin policies, call the custom hooks loader
|
|
547
|
+
- [x] Register each loaded custom hook into the policy registry with a `custom/` name prefix (prevents name collision with builtins)
|
|
548
|
+
- [x] Custom hooks run in layer 2 (after builtins) — verify priority ordering
|
|
549
|
+
- [x] Verify project still compiles
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
### 10. CLI — `src/hooks/manager.ts` and `src/cli-mode.ts`
|
|
554
|
+
- [x] Add `--custom-hooks <path>` flag to `installHooks()` — resolve to absolute path and write to `customHooksPath` in global `hooks-config.json`
|
|
555
|
+
- [x] Add `--remove-custom-hooks` flag — clears `customHooksPath` from config
|
|
556
|
+
- [x] Print the resolved hooks file path in the post-install summary
|
|
557
|
+
- [x] Add both flags to the CLI help text in `src/cli-mode.ts`
|
|
558
|
+
- [x] Verify project still compiles
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
### 11. `--list-hooks` display — `src/hooks/manager.ts`
|
|
563
|
+
- [x] Beneath each enabled policy that has user-configured params, print a summary line showing the active param values
|
|
564
|
+
- [x] Detect unknown keys in `policyParams` (keys that don't match any known policy schema param); print a warning line for each
|
|
565
|
+
- [x] If `customHooksPath` is set: attempt to load the file and show a "Custom Hooks" section beneath builtins
|
|
566
|
+
- [x] Each custom hook shows: status (`✓` / `✗ ERR`), declared name, description
|
|
567
|
+
- [x] On load failure: show `✗ ERR <name> failed to load: <error message>`
|
|
568
|
+
- [x] If `customHooksPath` is set but file does not exist: show a clear warning
|
|
569
|
+
- [x] Verify project still compiles
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
### 12. Unit tests — policy params
|
|
574
|
+
|
|
575
|
+
File: `__tests__/hooks/hooks-config.test.ts`
|
|
576
|
+
- [x] Test: no config files present → returns empty defaults
|
|
577
|
+
- [x] Test: only global config → returns global values
|
|
578
|
+
- [x] Test: project + global, `policyParams` defined in project → project values used, global values for that policy ignored
|
|
579
|
+
- [x] Test: project + global, policy absent in project → falls through to global
|
|
580
|
+
- [x] Test: all three scopes present, each with different `policyParams` entries → correct precedence
|
|
581
|
+
- [x] Test: `enabledPolicies` union across all three scopes, deduplication works
|
|
582
|
+
- [x] Test: `customHooksPath` — first scope that defines it wins
|
|
583
|
+
|
|
584
|
+
File: `__tests__/hooks/builtin-policies.test.ts`
|
|
585
|
+
- [x] Test: each parameterized policy with no `ctx.params` set behaves identically to current behavior (regression guard)
|
|
586
|
+
- [x] Test: `block-sudo` with `allowPatterns: ["sudo systemctl status"]` — matching command is allowed
|
|
587
|
+
- [x] Test: `block-sudo` with `allowPatterns` — non-matching sudo is still denied
|
|
588
|
+
- [x] Test: `block-rm-rf` with `allowPaths` — allowed path passes, unlisted path denied
|
|
589
|
+
- [x] Test: `block-read-outside-cwd` with `allowPaths` — listed path allowed
|
|
590
|
+
- [x] Test: `block-push-master` with `protectedBranches: ["main", "release"]` — `release` is now blocked, non-listed branch is not
|
|
591
|
+
- [x] Test: `block-work-on-main` with custom `protectedBranches`
|
|
592
|
+
- [x] Test: `sanitize-api-keys` with `additionalPatterns` — custom pattern triggers deny
|
|
593
|
+
- [x] Test: `block-secrets-write` with `additionalPatterns`
|
|
594
|
+
- [x] Test: `warn-large-file-write` with custom `thresholdKb`
|
|
595
|
+
|
|
596
|
+
File: `__tests__/hooks/policy-evaluator.test.ts`
|
|
597
|
+
- [x] Test: params are correctly resolved from schema defaults when no `policyParams` in config
|
|
598
|
+
- [x] Test: params from config correctly override schema defaults before `fn` is called
|
|
599
|
+
- [x] Test: unknown `policyParams` key does not crash the evaluator
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
### 13. Unit tests — custom hooks registry
|
|
604
|
+
|
|
605
|
+
New file: `__tests__/hooks/custom-hooks-registry.test.ts`
|
|
606
|
+
- [x] Test: `customHooks.add()` registers a hook into the globalThis registry
|
|
607
|
+
- [x] Test: `getCustomHooks()` returns all registered hooks in insertion order
|
|
608
|
+
- [x] Test: multiple `.add()` calls accumulate correctly
|
|
609
|
+
- [x] Test: `clearCustomHooks()` empties the registry (verify test isolation)
|
|
610
|
+
- [x] Test: registry survives across module re-imports (globalThis persistence)
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
### 14. Unit tests — custom hooks loader
|
|
615
|
+
|
|
616
|
+
New file: `__tests__/hooks/custom-hooks-loader.test.ts`
|
|
617
|
+
- [x] Test: `customHooksPath` absent in config → returns empty array, no error
|
|
618
|
+
- [x] Test: `customHooksPath` set but file does not exist → logs warning, returns empty array
|
|
619
|
+
- [x] Test: valid hooks file with one `customHooks.add()` call → hook registered and returned
|
|
620
|
+
- [x] Test: hooks file with import/syntax error → logs to hook.log, returns empty array, does not throw
|
|
621
|
+
- [x] Test: hooks file with transitive local import → transitive module resolved and loaded correctly
|
|
622
|
+
- [x] Test: temp `.mjs` files are cleaned up after load (success and error paths)
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
### 15. Unit tests — CLI and manager
|
|
627
|
+
|
|
628
|
+
File: `__tests__/hooks/manager.test.ts`
|
|
629
|
+
- [x] Test: `--custom-hooks <path>` saves resolved absolute path to `customHooksPath` in config
|
|
630
|
+
- [x] Test: `--remove-custom-hooks` clears `customHooksPath` from config
|
|
631
|
+
- [x] Test: `--list-hooks` output includes param summary for configured policies
|
|
632
|
+
- [x] Test: `--list-hooks` warns on unknown `policyParams` keys
|
|
633
|
+
- [x] Test: `--list-hooks` shows "Custom Hooks" section when `customHooksPath` is set
|
|
634
|
+
- [x] Test: `--list-hooks` shows error row when hooks file fails to load
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
### 16. Documentation — `README.md`
|
|
639
|
+
- [x] Add `policyParams` section explaining the three-scope config and precedence rules
|
|
640
|
+
- [x] Add a table or subsection for each parameterized policy: param name, type, default, example
|
|
641
|
+
- [x] Add `--custom-hooks <path>` to the CLI reference section
|
|
642
|
+
- [x] Add `--remove-custom-hooks` to the CLI reference section
|
|
643
|
+
- [x] Add a "Custom Hooks" authoring guide with:
|
|
644
|
+
- [x] Installation step (`claudeye --install-hooks --custom-hooks ./my-hooks.js`)
|
|
645
|
+
- [x] Full working example file using `customHooks.add()` with `allow()`, `deny()`, `instruct()`
|
|
646
|
+
- [x] Description of each decision helper: `allow()`, `deny(message)`, `instruct(message)` — what each does, when to use it, what the message becomes in Claude's context
|
|
647
|
+
- [x] Note that `deny(message)` is prefixed with `"Blocked by claudeye:"` in the output
|
|
648
|
+
- [x] Description of `ctx` fields available (`eventType`, `toolName`, `toolInput`, `session`, `payload`)
|
|
649
|
+
- [x] Note on transitive imports being supported
|
|
650
|
+
- [x] Note on fail-open behavior and the hook.log location
|
|
651
|
+
- [x] Export `CustomHook`, `PolicyContext`, `PolicyResult` types in the Types section of the README
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
655
|
+
### 17. Changelog — `CHANGELOG.md`
|
|
656
|
+
- [x] Add `## [1.0.8-beta.0]` heading at the top
|
|
657
|
+
- [x] Document Part 1: policy params — mention `policyParams` config key, list all parameterized policies and their param names
|
|
658
|
+
- [x] Document Part 2: custom JS hooks — mention `--custom-hooks <path>`, `customHooks.add()` API, loader capabilities (transitive imports, ESM compat)
|
|
659
|
+
- [x] Reference the design doc (`design-docs/custom-hooks.md`) for full design rationale
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
### 18. Version bump
|
|
664
|
+
- [x] `package.json` → `"version": "1.0.8-beta.0"`
|
|
665
|
+
- [x] `packages/darwin-arm64/package.json` → `"version": "1.0.8-beta.0"`
|
|
666
|
+
- [x] `packages/darwin-x64/package.json` → `"version": "1.0.8-beta.0"`
|
|
667
|
+
- [x] `packages/linux-arm64/package.json` → `"version": "1.0.8-beta.0"`
|
|
668
|
+
- [x] `packages/linux-x64/package.json` → `"version": "1.0.8-beta.0"`
|
|
669
|
+
- [x] `packages/win32-x64/package.json` → `"version": "1.0.8-beta.0"`
|
|
670
|
+
- [x] `packages/wrapper/package.json` → `"version": "1.0.8-beta.0"`
|
|
671
|
+
- [x] Verify all version strings are consistent (`grep -r "1.0.7" package.json packages/`)
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
### 19. Final verification
|
|
676
|
+
- [x] `npm run build` (or equivalent) passes with zero errors
|
|
677
|
+
- [x] Full test suite passes (`npm test` or equivalent) with zero failures
|
|
678
|
+
- [x] `claudeye --list-hooks` runs without error on a clean install
|
|
679
|
+
- [x] `claudeye --install-hooks --custom-hooks <path>` correctly saves the path and loads hooks on next event
|
|
680
|
+
- [x] A manually authored `my-hooks.js` using `customHooks.add()` with a transitive import loads and evaluates correctly end-to-end
|
|
681
|
+
- [x] No `console.log` debug statements left in production code paths
|
|
682
|
+
- [x] No new `any` types introduced without a comment explaining why
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
686
|
+
### 20. Pull request
|
|
687
|
+
- [x] Stage all changes: confirm `git status` shows only intentional files (no `.env`, no build artifacts, no temp files)
|
|
688
|
+
- [x] Commit with a descriptive message referencing both features, e.g. `feat: policy params + custom JS hooks (v1.0.8-beta.0)`
|
|
689
|
+
- [x] Push the branch: `git push -u origin feat/custom-hooks`
|
|
690
|
+
- [x] Open a PR against `main` using `gh pr create` with:
|
|
691
|
+
- Title: `feat: policy params and custom JS hooks (v1.0.8-beta.0)`
|
|
692
|
+
- Body covering:
|
|
693
|
+
- [x] Summary of Part 1 (policy params — what changed, which policies, config shape)
|
|
694
|
+
- [x] Summary of Part 2 (custom hooks — API, loader, `--custom-hooks` flag)
|
|
695
|
+
- [x] Link to `design-docs/custom-hooks.md` for full design rationale
|
|
696
|
+
- [x] Test plan: what was tested manually and what is covered by unit tests
|
|
697
|
+
- [x] Note on backwards compatibility (no-params users are unaffected)
|
|
698
|
+
- [x] Confirm the PR URL is accessible and the description renders correctly
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
### 21. CI monitoring and fixes
|
|
703
|
+
- [x] Watch the CI run: `gh run watch` or `gh pr checks <pr-number> --watch`
|
|
704
|
+
- [x] Wait for all checks to complete — do not assume green until every job has finished
|
|
705
|
+
- [x] If any check fails:
|
|
706
|
+
- [x] Fetch the failure logs: `gh run view <run-id> --log-failed`
|
|
707
|
+
- [x] Read the full error output before attempting a fix — do not guess
|
|
708
|
+
- [x] Fix the root cause (do not skip or suppress failing checks with `--no-verify` or similar)
|
|
709
|
+
- [x] Push the fix as a new commit on the same branch
|
|
710
|
+
- [x] Re-watch CI until all checks pass
|
|
711
|
+
- [x] Repeat the fix cycle until every CI check is green
|
|
712
|
+
- [x] Once all checks pass, mark this section complete and notify the user that the PR is ready for review
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## Open Items
|
|
717
|
+
|
|
718
|
+
- **#154** — Fail-open vs fail-closed semantics for enforcement hooks
|
|
719
|
+
- **Per-custom-hook params** — letting `policyParams` configure custom hooks via `ctx.params`
|
|
720
|
+
(deferred, design in a separate doc when ready)
|
|
721
|
+
- **`allowPatterns` matching safety** — implementation must parse commands into argv tokens,
|
|
722
|
+
not match raw strings, to prevent shell operator injection bypass
|