failproofai 0.0.2 → 0.0.4-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.claude/settings.json +316 -0
- package/.next/standalone/.failproofai/policies/workflow-policies.mjs +62 -0
- package/.next/standalone/.failproofai/policies-config.json +39 -0
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +5 -5
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +3 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +17 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +17 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kjo7d_._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_05pz9._._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0qo8503._.js → [root-of-the-server]__0jqus-j._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +9 -9
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__12kr5~_._.js → [root-of-the-server]__131id~1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0a_7sdg.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0ef3uwk.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0j79~gv.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0pbja1x.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0r6o0i2.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_11y81~_.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_12or2kf.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0mebn66._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/static/chunks/{0y~0creqvl5wx.js → 045lpk_isd5np.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0cvffh-pbsv5u.js → 065qrrpfkts8s.js} +1 -1
- package/.next/standalone/.next/static/chunks/{031pa5~qfzt~_.js → 09e7drilkf1sn.js} +1 -1
- package/.next/standalone/.next/static/chunks/0gu_a.a80ritd.css +1 -0
- package/.next/standalone/.next/static/chunks/{15wf7x-e.8ia3.js → 0je_~y72wv~~2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0x-625~1vx1lu.js → 0rqcttnl9u32c.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ov60i6md~37t.js → 0v2-.v07.zb9u.js} +2 -2
- package/.next/standalone/.next/static/chunks/{06og.7e9nkpjh.js → 0yye9-w._6rz~.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0_4y_t03jn2nq.js → 15proylk5ye2k.js} +1 -1
- package/.next/standalone/.next/static/chunks/174boqk9e~20i.js +6 -0
- package/.next/standalone/.next/static/chunks/{turbopack-0uc5y~g6h.n7-.js → turbopack-0r26pc8h0y_-e.js} +1 -1
- package/.next/standalone/CHANGELOG.md +108 -0
- package/.next/standalone/CLAUDE.md +28 -0
- package/.next/standalone/Dockerfile.docs +12 -0
- package/.next/standalone/README.md +95 -49
- package/.next/standalone/app/components/session-hooks-panel.tsx +14 -1
- package/.next/standalone/app/policies/hooks-client.tsx +14 -1
- package/.next/standalone/bin/failproofai.mjs +5 -0
- package/.next/standalone/bun.lock +76 -63
- package/.next/standalone/components/navbar.tsx +5 -0
- package/.next/standalone/dist/cli.mjs +535 -90
- package/.next/standalone/dist/index.js +2 -2
- package/.next/standalone/docs/ar/architecture.mdx +333 -0
- package/.next/standalone/docs/ar/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/ar/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/ar/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/ar/cli/hook.mdx +31 -0
- package/.next/standalone/docs/ar/cli/install-policies.mdx +48 -0
- package/.next/standalone/docs/ar/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/ar/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/ar/cli/version.mdx +13 -0
- package/.next/standalone/docs/ar/configuration.mdx +223 -0
- package/.next/standalone/docs/ar/custom-policies.mdx +359 -0
- package/.next/standalone/docs/ar/dashboard.mdx +142 -0
- package/.next/standalone/docs/ar/examples.mdx +254 -0
- package/.next/standalone/docs/ar/for-agents.mdx +39 -0
- package/.next/standalone/docs/ar/getting-started.mdx +134 -0
- package/.next/standalone/docs/ar/introduction.mdx +58 -0
- package/.next/standalone/docs/ar/package-aliases.mdx +82 -0
- package/.next/standalone/docs/ar/testing.mdx +261 -0
- package/.next/standalone/docs/{architecture.md → architecture.mdx} +40 -23
- package/.next/standalone/docs/{built-in-policies.md → built-in-policies.mdx} +126 -15
- package/.next/standalone/docs/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/cli/hook.mdx +30 -0
- package/.next/standalone/docs/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/cli/version.mdx +12 -0
- package/.next/standalone/docs/{configuration.md → configuration.mdx} +62 -16
- package/.next/standalone/docs/custom-policies.mdx +357 -0
- package/.next/standalone/docs/{dashboard.md → dashboard.mdx} +26 -29
- package/.next/standalone/docs/de/architecture.mdx +332 -0
- package/.next/standalone/docs/de/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/de/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/de/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/de/cli/hook.mdx +30 -0
- package/.next/standalone/docs/de/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/de/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/de/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/de/cli/version.mdx +12 -0
- package/.next/standalone/docs/de/configuration.mdx +222 -0
- package/.next/standalone/docs/de/custom-policies.mdx +357 -0
- package/.next/standalone/docs/de/dashboard.mdx +142 -0
- package/.next/standalone/docs/de/examples.mdx +253 -0
- package/.next/standalone/docs/de/for-agents.mdx +38 -0
- package/.next/standalone/docs/de/getting-started.mdx +134 -0
- package/.next/standalone/docs/de/introduction.mdx +57 -0
- package/.next/standalone/docs/de/package-aliases.mdx +82 -0
- package/.next/standalone/docs/de/testing.mdx +260 -0
- package/.next/standalone/docs/docs.json +943 -24
- package/.next/standalone/docs/es/architecture.mdx +332 -0
- package/.next/standalone/docs/es/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/es/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/es/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/es/cli/hook.mdx +30 -0
- package/.next/standalone/docs/es/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/es/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/es/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/es/cli/version.mdx +12 -0
- package/.next/standalone/docs/es/configuration.mdx +222 -0
- package/.next/standalone/docs/es/custom-policies.mdx +357 -0
- package/.next/standalone/docs/es/dashboard.mdx +142 -0
- package/.next/standalone/docs/es/examples.mdx +253 -0
- package/.next/standalone/docs/es/for-agents.mdx +38 -0
- package/.next/standalone/docs/es/getting-started.mdx +134 -0
- package/.next/standalone/docs/es/introduction.mdx +57 -0
- package/.next/standalone/docs/es/package-aliases.mdx +82 -0
- package/.next/standalone/docs/es/testing.mdx +260 -0
- package/.next/standalone/docs/examples.mdx +253 -0
- package/.next/standalone/docs/for-agents.mdx +38 -0
- package/.next/standalone/docs/fr/architecture.mdx +332 -0
- package/.next/standalone/docs/fr/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/fr/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/fr/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/fr/cli/hook.mdx +30 -0
- package/.next/standalone/docs/fr/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/fr/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/fr/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/fr/cli/version.mdx +12 -0
- package/.next/standalone/docs/fr/configuration.mdx +222 -0
- package/.next/standalone/docs/fr/custom-policies.mdx +357 -0
- package/.next/standalone/docs/fr/dashboard.mdx +142 -0
- package/.next/standalone/docs/fr/examples.mdx +253 -0
- package/.next/standalone/docs/fr/for-agents.mdx +38 -0
- package/.next/standalone/docs/fr/getting-started.mdx +134 -0
- package/.next/standalone/docs/fr/introduction.mdx +57 -0
- package/.next/standalone/docs/fr/package-aliases.mdx +82 -0
- package/.next/standalone/docs/fr/testing.mdx +260 -0
- package/.next/standalone/docs/getting-started.mdx +134 -0
- package/.next/standalone/docs/he/architecture.mdx +333 -0
- package/.next/standalone/docs/he/built-in-policies.mdx +535 -0
- package/.next/standalone/docs/he/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/he/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/he/cli/hook.mdx +30 -0
- package/.next/standalone/docs/he/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/he/cli/list-policies.mdx +32 -0
- package/.next/standalone/docs/he/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/he/cli/version.mdx +12 -0
- package/.next/standalone/docs/he/configuration.mdx +222 -0
- package/.next/standalone/docs/he/custom-policies.mdx +357 -0
- package/.next/standalone/docs/he/dashboard.mdx +142 -0
- package/.next/standalone/docs/he/examples.mdx +253 -0
- package/.next/standalone/docs/he/for-agents.mdx +38 -0
- package/.next/standalone/docs/he/getting-started.mdx +135 -0
- package/.next/standalone/docs/he/introduction.mdx +57 -0
- package/.next/standalone/docs/he/package-aliases.mdx +82 -0
- package/.next/standalone/docs/he/testing.mdx +260 -0
- package/.next/standalone/docs/hi/architecture.mdx +334 -0
- package/.next/standalone/docs/hi/built-in-policies.mdx +535 -0
- package/.next/standalone/docs/hi/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/hi/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/hi/cli/hook.mdx +30 -0
- package/.next/standalone/docs/hi/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/hi/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/hi/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/hi/cli/version.mdx +12 -0
- package/.next/standalone/docs/hi/configuration.mdx +222 -0
- package/.next/standalone/docs/hi/custom-policies.mdx +357 -0
- package/.next/standalone/docs/hi/dashboard.mdx +142 -0
- package/.next/standalone/docs/hi/examples.mdx +255 -0
- package/.next/standalone/docs/hi/for-agents.mdx +38 -0
- package/.next/standalone/docs/hi/getting-started.mdx +134 -0
- package/.next/standalone/docs/hi/introduction.mdx +57 -0
- package/.next/standalone/docs/hi/package-aliases.mdx +82 -0
- package/.next/standalone/docs/hi/testing.mdx +260 -0
- package/.next/standalone/docs/i18n/README.ar.md +312 -0
- package/.next/standalone/docs/i18n/README.de.md +307 -0
- package/.next/standalone/docs/i18n/README.es.md +307 -0
- package/.next/standalone/docs/i18n/README.fr.md +307 -0
- package/.next/standalone/docs/i18n/README.he.md +312 -0
- package/.next/standalone/docs/i18n/README.hi.md +307 -0
- package/.next/standalone/docs/i18n/README.it.md +307 -0
- package/.next/standalone/docs/i18n/README.ja.md +307 -0
- package/.next/standalone/docs/i18n/README.ko.md +307 -0
- package/.next/standalone/docs/i18n/README.pt-br.md +307 -0
- package/.next/standalone/docs/i18n/README.ru.md +308 -0
- package/.next/standalone/docs/i18n/README.tr.md +308 -0
- package/.next/standalone/docs/i18n/README.vi.md +308 -0
- package/.next/standalone/docs/i18n/README.zh.md +307 -0
- package/.next/standalone/docs/introduction.mdx +57 -0
- package/.next/standalone/docs/it/architecture.mdx +333 -0
- package/.next/standalone/docs/it/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/it/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/it/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/it/cli/hook.mdx +30 -0
- package/.next/standalone/docs/it/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/it/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/it/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/it/cli/version.mdx +12 -0
- package/.next/standalone/docs/it/configuration.mdx +223 -0
- package/.next/standalone/docs/it/custom-policies.mdx +358 -0
- package/.next/standalone/docs/it/dashboard.mdx +142 -0
- package/.next/standalone/docs/it/examples.mdx +253 -0
- package/.next/standalone/docs/it/for-agents.mdx +38 -0
- package/.next/standalone/docs/it/getting-started.mdx +134 -0
- package/.next/standalone/docs/it/introduction.mdx +57 -0
- package/.next/standalone/docs/it/package-aliases.mdx +82 -0
- package/.next/standalone/docs/it/testing.mdx +260 -0
- package/.next/standalone/docs/ja/architecture.mdx +332 -0
- package/.next/standalone/docs/ja/built-in-policies.mdx +535 -0
- package/.next/standalone/docs/ja/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/ja/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/ja/cli/hook.mdx +30 -0
- package/.next/standalone/docs/ja/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/ja/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/ja/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/ja/cli/version.mdx +12 -0
- package/.next/standalone/docs/ja/configuration.mdx +222 -0
- package/.next/standalone/docs/ja/custom-policies.mdx +357 -0
- package/.next/standalone/docs/ja/dashboard.mdx +142 -0
- package/.next/standalone/docs/ja/examples.mdx +253 -0
- package/.next/standalone/docs/ja/for-agents.mdx +38 -0
- package/.next/standalone/docs/ja/getting-started.mdx +134 -0
- package/.next/standalone/docs/ja/introduction.mdx +57 -0
- package/.next/standalone/docs/ja/package-aliases.mdx +82 -0
- package/.next/standalone/docs/ja/testing.mdx +260 -0
- package/.next/standalone/docs/ko/architecture.mdx +332 -0
- package/.next/standalone/docs/ko/built-in-policies.mdx +535 -0
- package/.next/standalone/docs/ko/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/ko/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/ko/cli/hook.mdx +30 -0
- package/.next/standalone/docs/ko/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/ko/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/ko/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/ko/cli/version.mdx +12 -0
- package/.next/standalone/docs/ko/configuration.mdx +222 -0
- package/.next/standalone/docs/ko/custom-policies.mdx +357 -0
- package/.next/standalone/docs/ko/dashboard.mdx +142 -0
- package/.next/standalone/docs/ko/examples.mdx +253 -0
- package/.next/standalone/docs/ko/for-agents.mdx +38 -0
- package/.next/standalone/docs/ko/getting-started.mdx +134 -0
- package/.next/standalone/docs/ko/introduction.mdx +57 -0
- package/.next/standalone/docs/ko/package-aliases.mdx +82 -0
- package/.next/standalone/docs/ko/testing.mdx +260 -0
- package/.next/standalone/docs/logo/dark.svg +21 -0
- package/.next/standalone/docs/logo/light.svg +21 -0
- package/.next/standalone/docs/{package-aliases.md → package-aliases.mdx} +5 -5
- package/.next/standalone/docs/pt-br/architecture.mdx +332 -0
- package/.next/standalone/docs/pt-br/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/pt-br/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/pt-br/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/pt-br/cli/hook.mdx +30 -0
- package/.next/standalone/docs/pt-br/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/pt-br/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/pt-br/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/pt-br/cli/version.mdx +12 -0
- package/.next/standalone/docs/pt-br/configuration.mdx +222 -0
- package/.next/standalone/docs/pt-br/custom-policies.mdx +357 -0
- package/.next/standalone/docs/pt-br/dashboard.mdx +142 -0
- package/.next/standalone/docs/pt-br/examples.mdx +253 -0
- package/.next/standalone/docs/pt-br/for-agents.mdx +38 -0
- package/.next/standalone/docs/pt-br/getting-started.mdx +134 -0
- package/.next/standalone/docs/pt-br/introduction.mdx +57 -0
- package/.next/standalone/docs/pt-br/package-aliases.mdx +82 -0
- package/.next/standalone/docs/pt-br/testing.mdx +260 -0
- package/.next/standalone/docs/ru/architecture.mdx +334 -0
- package/.next/standalone/docs/ru/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/ru/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/ru/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/ru/cli/hook.mdx +30 -0
- package/.next/standalone/docs/ru/cli/install-policies.mdx +48 -0
- package/.next/standalone/docs/ru/cli/list-policies.mdx +32 -0
- package/.next/standalone/docs/ru/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/ru/cli/version.mdx +12 -0
- package/.next/standalone/docs/ru/configuration.mdx +223 -0
- package/.next/standalone/docs/ru/custom-policies.mdx +357 -0
- package/.next/standalone/docs/ru/dashboard.mdx +142 -0
- package/.next/standalone/docs/ru/examples.mdx +254 -0
- package/.next/standalone/docs/ru/for-agents.mdx +38 -0
- package/.next/standalone/docs/ru/getting-started.mdx +134 -0
- package/.next/standalone/docs/ru/introduction.mdx +57 -0
- package/.next/standalone/docs/ru/package-aliases.mdx +82 -0
- package/.next/standalone/docs/ru/testing.mdx +260 -0
- package/.next/standalone/docs/{testing.md → testing.mdx} +11 -11
- package/.next/standalone/docs/tr/architecture.mdx +333 -0
- package/.next/standalone/docs/tr/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/tr/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/tr/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/tr/cli/hook.mdx +30 -0
- package/.next/standalone/docs/tr/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/tr/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/tr/cli/remove-policies.mdx +44 -0
- package/.next/standalone/docs/tr/cli/version.mdx +12 -0
- package/.next/standalone/docs/tr/configuration.mdx +223 -0
- package/.next/standalone/docs/tr/custom-policies.mdx +357 -0
- package/.next/standalone/docs/tr/dashboard.mdx +142 -0
- package/.next/standalone/docs/tr/examples.mdx +253 -0
- package/.next/standalone/docs/tr/for-agents.mdx +38 -0
- package/.next/standalone/docs/tr/getting-started.mdx +134 -0
- package/.next/standalone/docs/tr/introduction.mdx +57 -0
- package/.next/standalone/docs/tr/package-aliases.mdx +82 -0
- package/.next/standalone/docs/tr/testing.mdx +260 -0
- package/.next/standalone/docs/vi/architecture.mdx +333 -0
- package/.next/standalone/docs/vi/built-in-policies.mdx +537 -0
- package/.next/standalone/docs/vi/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/vi/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/vi/cli/hook.mdx +30 -0
- package/.next/standalone/docs/vi/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/vi/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/vi/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/vi/cli/version.mdx +13 -0
- package/.next/standalone/docs/vi/configuration.mdx +222 -0
- package/.next/standalone/docs/vi/custom-policies.mdx +357 -0
- package/.next/standalone/docs/vi/dashboard.mdx +142 -0
- package/.next/standalone/docs/vi/examples.mdx +253 -0
- package/.next/standalone/docs/vi/for-agents.mdx +38 -0
- package/.next/standalone/docs/vi/getting-started.mdx +134 -0
- package/.next/standalone/docs/vi/introduction.mdx +57 -0
- package/.next/standalone/docs/vi/package-aliases.mdx +82 -0
- package/.next/standalone/docs/vi/testing.mdx +260 -0
- package/.next/standalone/docs/zh/architecture.mdx +332 -0
- package/.next/standalone/docs/zh/built-in-policies.mdx +535 -0
- package/.next/standalone/docs/zh/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/zh/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/zh/cli/hook.mdx +30 -0
- package/.next/standalone/docs/zh/cli/install-policies.mdx +47 -0
- package/.next/standalone/docs/zh/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/zh/cli/remove-policies.mdx +43 -0
- package/.next/standalone/docs/zh/cli/version.mdx +12 -0
- package/.next/standalone/docs/zh/configuration.mdx +222 -0
- package/.next/standalone/docs/zh/custom-policies.mdx +357 -0
- package/.next/standalone/docs/zh/dashboard.mdx +142 -0
- package/.next/standalone/docs/zh/examples.mdx +253 -0
- package/.next/standalone/docs/zh/for-agents.mdx +38 -0
- package/.next/standalone/docs/zh/getting-started.mdx +134 -0
- package/.next/standalone/docs/zh/introduction.mdx +57 -0
- package/.next/standalone/docs/zh/package-aliases.mdx +82 -0
- package/.next/standalone/docs/zh/testing.mdx +260 -0
- package/.next/standalone/examples/convention-policies/security-policies.mjs +40 -0
- package/.next/standalone/examples/convention-policies/workflow-policies.mjs +41 -0
- package/.next/standalone/next.config.ts +5 -3
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/jsonwebtoken/index.js +2 -2
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +7 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/render.js +20 -19
- package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +15 -15
- package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
- package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
- package/.next/standalone/node_modules/react/package.json +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/package.json +2 -2
- package/.next/standalone/package.json +13 -10
- package/.next/standalone/scripts/translate-docs/cache.ts +62 -0
- package/.next/standalone/scripts/translate-docs/cli.ts +357 -0
- package/.next/standalone/scripts/translate-docs/config.ts +248 -0
- package/.next/standalone/scripts/translate-docs/mdx-translator.ts +153 -0
- package/.next/standalone/scripts/translate-docs/mintlify-nav.ts +107 -0
- package/.next/standalone/scripts/translate-docs/readme-translator.ts +154 -0
- package/.next/standalone/scripts/translate-docs/translator.ts +68 -0
- package/.next/standalone/scripts/translate-docs/types.ts +43 -0
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/skills-lock.json +10 -0
- package/.next/standalone/src/hooks/builtin-policies.ts +401 -25
- package/.next/standalone/src/hooks/custom-hooks-loader.ts +165 -21
- package/.next/standalone/src/hooks/handler.ts +33 -6
- package/.next/standalone/src/hooks/hook-activity-store.ts +6 -1
- package/.next/standalone/src/hooks/hooks-config.ts +47 -2
- package/.next/standalone/src/hooks/llm-client.ts +2 -2
- package/.next/standalone/src/hooks/loader-utils.ts +4 -4
- package/.next/standalone/src/hooks/manager.ts +67 -16
- package/.next/standalone/src/hooks/policy-evaluator.ts +58 -19
- package/.next/standalone/src/hooks/policy-helpers.ts +2 -2
- package/.next/standalone/vitest.config.e2e.mts +3 -0
- package/.next/standalone/vitest.config.mts +3 -0
- package/README.md +95 -49
- package/bin/failproofai.mjs +5 -0
- package/dist/cli.mjs +535 -90
- package/dist/index.js +2 -2
- package/package.json +13 -10
- package/scripts/translate-docs/cache.ts +62 -0
- package/scripts/translate-docs/cli.ts +357 -0
- package/scripts/translate-docs/config.ts +248 -0
- package/scripts/translate-docs/mdx-translator.ts +153 -0
- package/scripts/translate-docs/mintlify-nav.ts +107 -0
- package/scripts/translate-docs/readme-translator.ts +154 -0
- package/scripts/translate-docs/translator.ts +68 -0
- package/scripts/translate-docs/types.ts +43 -0
- package/src/hooks/builtin-policies.ts +401 -25
- package/src/hooks/custom-hooks-loader.ts +165 -21
- package/src/hooks/handler.ts +33 -6
- package/src/hooks/hook-activity-store.ts +6 -1
- package/src/hooks/hooks-config.ts +47 -2
- package/src/hooks/llm-client.ts +2 -2
- package/src/hooks/loader-utils.ts +4 -4
- package/src/hooks/manager.ts +67 -16
- package/src/hooks/policy-evaluator.ts +58 -19
- package/src/hooks/policy-helpers.ts +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__02nt~6d._.js +0 -3
- package/.next/standalone/.next/static/chunks/0c_ljlxa._4lc.js +0 -6
- package/.next/standalone/.next/static/chunks/15jpradyu_531.css +0 -1
- package/.next/standalone/docs/cli-reference.md +0 -175
- package/.next/standalone/docs/custom-hooks.md +0 -261
- package/.next/standalone/docs/getting-started.md +0 -128
- package/.next/standalone/docs/introduction.md +0 -47
- /package/.next/standalone/.next/static/{WS-OQSqL1Lp1w_obXfjvl → WRbDp8A_ORPof197CezOZ}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{WS-OQSqL1Lp1w_obXfjvl → WRbDp8A_ORPof197CezOZ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{WS-OQSqL1Lp1w_obXfjvl → WRbDp8A_ORPof197CezOZ}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -149,11 +149,19 @@ function readMergedHooksConfig(cwd) {
|
|
|
149
149
|
...llm !== undefined ? { llm } : {}
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
152
|
+
function getConfigPathForScope(scope, cwd) {
|
|
153
|
+
const base = cwd ? resolve(cwd) : process.cwd();
|
|
154
|
+
switch (scope) {
|
|
155
|
+
case "user":
|
|
156
|
+
return resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
157
|
+
case "project":
|
|
158
|
+
return resolve(base, ".failproofai", "policies-config.json");
|
|
159
|
+
case "local":
|
|
160
|
+
return resolve(base, ".failproofai", "policies-config.local.json");
|
|
161
|
+
}
|
|
154
162
|
}
|
|
155
|
-
function
|
|
156
|
-
const configPath =
|
|
163
|
+
function readScopedHooksConfig(scope, cwd) {
|
|
164
|
+
const configPath = getConfigPathForScope(scope, cwd);
|
|
157
165
|
if (!existsSync2(configPath)) {
|
|
158
166
|
return { enabledPolicies: [] };
|
|
159
167
|
}
|
|
@@ -165,8 +173,8 @@ function readHooksConfig() {
|
|
|
165
173
|
return { enabledPolicies: [] };
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
|
-
function
|
|
169
|
-
const configPath =
|
|
176
|
+
function writeScopedHooksConfig(config, scope, cwd) {
|
|
177
|
+
const configPath = getConfigPathForScope(scope, cwd);
|
|
170
178
|
const dir = dirname(configPath);
|
|
171
179
|
if (!existsSync2(dir)) {
|
|
172
180
|
mkdirSync2(dir, { recursive: true });
|
|
@@ -179,8 +187,8 @@ var init_hooks_config = __esm(() => {
|
|
|
179
187
|
});
|
|
180
188
|
|
|
181
189
|
// src/hooks/policy-helpers.ts
|
|
182
|
-
function allow() {
|
|
183
|
-
return { decision: "allow" };
|
|
190
|
+
function allow(reason) {
|
|
191
|
+
return reason ? { decision: "allow", reason } : { decision: "allow" };
|
|
184
192
|
}
|
|
185
193
|
function deny(reason) {
|
|
186
194
|
return { decision: "deny", reason };
|
|
@@ -248,7 +256,7 @@ var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILP
|
|
|
248
256
|
// src/hooks/builtin-policies.ts
|
|
249
257
|
import { resolve as resolve2, join as join2 } from "node:path";
|
|
250
258
|
import { readFile, writeFile } from "node:fs/promises";
|
|
251
|
-
import { execSync } from "node:child_process";
|
|
259
|
+
import { execSync, execFileSync } from "node:child_process";
|
|
252
260
|
import { homedir as homedir3 } from "node:os";
|
|
253
261
|
function isClaudeInternalPath(resolved2) {
|
|
254
262
|
const claudeDir = join2(homedir3(), ".claude");
|
|
@@ -266,6 +274,53 @@ function getFilePath(ctx) {
|
|
|
266
274
|
function parseArgvTokens(cmd) {
|
|
267
275
|
return cmd.trim().split(/\s+/).map((t) => t.replace(/^['"]|['"]$/g, ""));
|
|
268
276
|
}
|
|
277
|
+
function getCurrentBranch(cwd) {
|
|
278
|
+
try {
|
|
279
|
+
let branch = gitBranchCache.get(cwd);
|
|
280
|
+
if (branch === undefined) {
|
|
281
|
+
branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
282
|
+
cwd,
|
|
283
|
+
encoding: "utf8",
|
|
284
|
+
timeout: 3000
|
|
285
|
+
}).trim();
|
|
286
|
+
gitBranchCache.set(cwd, branch);
|
|
287
|
+
}
|
|
288
|
+
return branch || null;
|
|
289
|
+
} catch {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function getHeadSha(cwd) {
|
|
294
|
+
try {
|
|
295
|
+
const sha = execSync("git rev-parse HEAD", {
|
|
296
|
+
cwd,
|
|
297
|
+
encoding: "utf8",
|
|
298
|
+
timeout: 3000
|
|
299
|
+
}).trim();
|
|
300
|
+
return sha || null;
|
|
301
|
+
} catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function getThirdPartyCheckRuns(cwd, sha) {
|
|
306
|
+
try {
|
|
307
|
+
const json = execFileSync("gh", [
|
|
308
|
+
"api",
|
|
309
|
+
`repos/{owner}/{repo}/commits/${sha}/check-runs`,
|
|
310
|
+
"--jq",
|
|
311
|
+
'.check_runs | map(select(.app.slug != "github-actions")) | map({name: .name, status: .status, conclusion: (.conclusion // "")})'
|
|
312
|
+
], {
|
|
313
|
+
cwd,
|
|
314
|
+
encoding: "utf8",
|
|
315
|
+
timeout: 15000
|
|
316
|
+
}).trim();
|
|
317
|
+
if (!json || json === "[]")
|
|
318
|
+
return [];
|
|
319
|
+
return JSON.parse(json);
|
|
320
|
+
} catch {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
269
324
|
function matchesAllowedPattern(cmd, pattern) {
|
|
270
325
|
const cmdTokens = parseArgvTokens(cmd);
|
|
271
326
|
const patTokens = parseArgvTokens(pattern);
|
|
@@ -480,7 +535,8 @@ function rmTargetIsAllowed(cmd, allowPaths) {
|
|
|
480
535
|
if (rmIdx < 0)
|
|
481
536
|
continue;
|
|
482
537
|
const flagTokens = tokens.slice(rmIdx + 1).filter((t) => /^-[^-]/.test(t));
|
|
483
|
-
|
|
538
|
+
const longFlagsInSeg = tokens.slice(rmIdx + 1).filter((t) => /^--/.test(t));
|
|
539
|
+
if (!/r/i.test(flagTokens.join("")) && !longFlagsInSeg.some((f) => /^--recursive$/i.test(f)))
|
|
484
540
|
continue;
|
|
485
541
|
const pathArgs = tokens.slice(rmIdx + 1).filter((t) => !t.startsWith("-"));
|
|
486
542
|
for (const target of pathArgs) {
|
|
@@ -505,7 +561,10 @@ function blockRmRf(ctx) {
|
|
|
505
561
|
if (ctx.toolName !== "Bash")
|
|
506
562
|
return allow();
|
|
507
563
|
const cmd = getCommand(ctx);
|
|
508
|
-
const hasDestructivePath =
|
|
564
|
+
const hasDestructivePath = parseArgvTokens(cmd).some((token) => {
|
|
565
|
+
const normalized = token.replace(/\/\*$/, "").replace(/\/+$/, "") || (token.startsWith("/") ? "/" : "");
|
|
566
|
+
return normalized === "/" || normalized === "~" || /^\/[A-Za-z_][\w.-]*$/.test(normalized);
|
|
567
|
+
});
|
|
509
568
|
if (hasDestructivePath && (/rm\s+-[^\s]*r[^\s]*f[^\s]*/.test(cmd) || /rm\s+-[^\s]*f[^\s]*r[^\s]*/.test(cmd))) {
|
|
510
569
|
const allowPaths = ctx.params?.allowPaths ?? [];
|
|
511
570
|
if (rmTargetIsAllowed(cmd, allowPaths))
|
|
@@ -515,7 +574,10 @@ function blockRmRf(ctx) {
|
|
|
515
574
|
if (hasDestructivePath && /\brm\b/.test(cmd)) {
|
|
516
575
|
const tokens = parseArgvTokens(cmd);
|
|
517
576
|
const shortFlags = tokens.filter((t) => /^-[^-]/.test(t)).join("");
|
|
518
|
-
|
|
577
|
+
const longFlags = tokens.filter((t) => /^--/.test(t));
|
|
578
|
+
const hasRecursive = /r/i.test(shortFlags) || longFlags.some((f) => /^--recursive$/i.test(f));
|
|
579
|
+
const hasForce = /f/.test(shortFlags) || longFlags.some((f) => /^--force$/i.test(f));
|
|
580
|
+
if (hasRecursive && hasForce) {
|
|
519
581
|
const allowPaths = ctx.params?.allowPaths ?? [];
|
|
520
582
|
if (rmTargetIsAllowed(cmd, allowPaths))
|
|
521
583
|
return allow();
|
|
@@ -653,22 +715,12 @@ function blockWorkOnMain(ctx) {
|
|
|
653
715
|
const cwd = ctx.session?.cwd;
|
|
654
716
|
if (!cwd)
|
|
655
717
|
return allow();
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
if (branch === undefined) {
|
|
659
|
-
branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
660
|
-
cwd,
|
|
661
|
-
encoding: "utf8",
|
|
662
|
-
timeout: 3000
|
|
663
|
-
}).trim();
|
|
664
|
-
gitBranchCache.set(cwd, branch);
|
|
665
|
-
}
|
|
666
|
-
const protectedBranches = ctx.params?.protectedBranches ?? ["main", "master"];
|
|
667
|
-
if (protectedBranches.includes(branch)) {
|
|
668
|
-
return deny(`Git ${cmd.match(/git\s+(\S+)/)?.[1] ?? "operation"} on ${branch} is blocked. Create a feature branch first.`);
|
|
669
|
-
}
|
|
670
|
-
} catch {
|
|
718
|
+
const branch = getCurrentBranch(cwd);
|
|
719
|
+
if (!branch)
|
|
671
720
|
return allow();
|
|
721
|
+
const protectedBranches = ctx.params?.protectedBranches ?? ["main", "master"];
|
|
722
|
+
if (protectedBranches.includes(branch)) {
|
|
723
|
+
return deny(`Git ${cmd.match(/git\s+(\S+)/)?.[1] ?? "operation"} on ${branch} is blocked. Create a feature branch first.`);
|
|
672
724
|
}
|
|
673
725
|
return allow();
|
|
674
726
|
}
|
|
@@ -767,6 +819,170 @@ function warnBackgroundProcess(ctx) {
|
|
|
767
819
|
}
|
|
768
820
|
return allow();
|
|
769
821
|
}
|
|
822
|
+
function requireCommitBeforeStop(ctx) {
|
|
823
|
+
const cwd = ctx.session?.cwd;
|
|
824
|
+
if (!cwd)
|
|
825
|
+
return allow("No working directory available, skipping commit check.");
|
|
826
|
+
try {
|
|
827
|
+
const status = execSync("git status --porcelain", {
|
|
828
|
+
cwd,
|
|
829
|
+
encoding: "utf8",
|
|
830
|
+
timeout: 5000
|
|
831
|
+
}).trim();
|
|
832
|
+
if (status.length > 0) {
|
|
833
|
+
return deny("You have uncommitted changes in the working directory. Commit all changes before stopping.");
|
|
834
|
+
}
|
|
835
|
+
return allow("All changes are committed.");
|
|
836
|
+
} catch {
|
|
837
|
+
return allow("Not a git repository, skipping commit check.");
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
function requirePushBeforeStop(ctx) {
|
|
841
|
+
const cwd = ctx.session?.cwd;
|
|
842
|
+
if (!cwd)
|
|
843
|
+
return allow("No working directory available, skipping push check.");
|
|
844
|
+
try {
|
|
845
|
+
const remotes = execSync("git remote", {
|
|
846
|
+
cwd,
|
|
847
|
+
encoding: "utf8",
|
|
848
|
+
timeout: 3000
|
|
849
|
+
}).trim();
|
|
850
|
+
if (!remotes)
|
|
851
|
+
return allow("No git remote configured, skipping push check.");
|
|
852
|
+
const remote = ctx.params?.remote ?? "origin";
|
|
853
|
+
const branch = getCurrentBranch(cwd);
|
|
854
|
+
if (!branch || branch === "HEAD")
|
|
855
|
+
return allow("Detached HEAD, skipping push check.");
|
|
856
|
+
const baseBranch = ctx.params?.baseBranch ?? "main";
|
|
857
|
+
if (branch === baseBranch) {
|
|
858
|
+
return allow(`On base branch "${baseBranch}", skipping push check.`);
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
const ahead = execFileSync("git", ["log", `${remote}/${baseBranch}..HEAD`, "--oneline"], { cwd, encoding: "utf8", timeout: 5000 }).trim();
|
|
862
|
+
if (!ahead) {
|
|
863
|
+
return allow(`No commits ahead of ${remote}/${baseBranch}, skipping push check.`);
|
|
864
|
+
}
|
|
865
|
+
const diff = execFileSync("git", ["diff", "--stat", `${remote}/${baseBranch}`, "HEAD"], { cwd, encoding: "utf8", timeout: 5000 }).trim();
|
|
866
|
+
if (!diff) {
|
|
867
|
+
return allow(`No file changes compared to ${remote}/${baseBranch}, skipping push check.`);
|
|
868
|
+
}
|
|
869
|
+
} catch {}
|
|
870
|
+
let hasTracking = false;
|
|
871
|
+
try {
|
|
872
|
+
execFileSync("git", ["rev-parse", "--verify", `${remote}/${branch}`], {
|
|
873
|
+
cwd,
|
|
874
|
+
encoding: "utf8",
|
|
875
|
+
timeout: 3000
|
|
876
|
+
});
|
|
877
|
+
hasTracking = true;
|
|
878
|
+
} catch {}
|
|
879
|
+
if (!hasTracking) {
|
|
880
|
+
return deny(`Branch "${branch}" has not been pushed to remote "${remote}". ` + `Push your branch with: git push -u ${remote} ${branch}`);
|
|
881
|
+
}
|
|
882
|
+
const unpushed = execFileSync("git", ["log", `${remote}/${branch}..HEAD`, "--oneline"], {
|
|
883
|
+
cwd,
|
|
884
|
+
encoding: "utf8",
|
|
885
|
+
timeout: 5000
|
|
886
|
+
}).trim();
|
|
887
|
+
if (unpushed.length > 0) {
|
|
888
|
+
const commitCount = unpushed.split(`
|
|
889
|
+
`).length;
|
|
890
|
+
return deny(`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` + `Push your changes with: git push`);
|
|
891
|
+
}
|
|
892
|
+
return allow(`All commits pushed to "${remote}".`);
|
|
893
|
+
} catch {
|
|
894
|
+
return allow("Could not check push status, skipping.");
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function requirePrBeforeStop(ctx) {
|
|
898
|
+
const cwd = ctx.session?.cwd;
|
|
899
|
+
if (!cwd)
|
|
900
|
+
return allow("No working directory available, skipping PR check.");
|
|
901
|
+
try {
|
|
902
|
+
try {
|
|
903
|
+
execSync("gh --version", { cwd, encoding: "utf8", timeout: 3000 });
|
|
904
|
+
} catch {
|
|
905
|
+
return allow("GitHub CLI (gh) not installed, skipping PR check.");
|
|
906
|
+
}
|
|
907
|
+
const branch = getCurrentBranch(cwd);
|
|
908
|
+
if (!branch || branch === "HEAD")
|
|
909
|
+
return allow("Detached HEAD, skipping PR check.");
|
|
910
|
+
const baseBranch = ctx.params?.baseBranch ?? "main";
|
|
911
|
+
if (branch === baseBranch) {
|
|
912
|
+
return allow(`On base branch "${baseBranch}", skipping PR check.`);
|
|
913
|
+
}
|
|
914
|
+
try {
|
|
915
|
+
const ahead = execFileSync("git", ["log", `origin/${baseBranch}..HEAD`, "--oneline"], { cwd, encoding: "utf8", timeout: 5000 }).trim();
|
|
916
|
+
if (!ahead) {
|
|
917
|
+
return allow(`No commits ahead of origin/${baseBranch}, skipping PR check.`);
|
|
918
|
+
}
|
|
919
|
+
const diff = execFileSync("git", ["diff", "--stat", `origin/${baseBranch}`, "HEAD"], { cwd, encoding: "utf8", timeout: 5000 }).trim();
|
|
920
|
+
if (!diff) {
|
|
921
|
+
return allow(`No file changes compared to origin/${baseBranch}, skipping PR check.`);
|
|
922
|
+
}
|
|
923
|
+
} catch {}
|
|
924
|
+
let prJson;
|
|
925
|
+
try {
|
|
926
|
+
prJson = execSync("gh pr view --json number,url,state", {
|
|
927
|
+
cwd,
|
|
928
|
+
encoding: "utf8",
|
|
929
|
+
timeout: 15000
|
|
930
|
+
}).trim();
|
|
931
|
+
} catch {
|
|
932
|
+
return deny(`No pull request found for branch "${branch}". ` + `Create one with: gh pr create`);
|
|
933
|
+
}
|
|
934
|
+
const pr = JSON.parse(prJson);
|
|
935
|
+
if (pr.state === "OPEN") {
|
|
936
|
+
return allow(`PR #${pr.number} exists: ${pr.url}`);
|
|
937
|
+
}
|
|
938
|
+
return deny(`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Create a new PR with: gh pr create`);
|
|
939
|
+
} catch {
|
|
940
|
+
return allow("Could not check PR status, skipping.");
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
function requireCiGreenBeforeStop(ctx) {
|
|
944
|
+
const cwd = ctx.session?.cwd;
|
|
945
|
+
if (!cwd)
|
|
946
|
+
return allow("No working directory available, skipping CI check.");
|
|
947
|
+
try {
|
|
948
|
+
try {
|
|
949
|
+
execSync("gh --version", { cwd, encoding: "utf8", timeout: 3000 });
|
|
950
|
+
} catch {
|
|
951
|
+
return allow("GitHub CLI (gh) not installed, skipping CI check.");
|
|
952
|
+
}
|
|
953
|
+
const branch = getCurrentBranch(cwd);
|
|
954
|
+
if (!branch || branch === "HEAD")
|
|
955
|
+
return allow("Detached HEAD, skipping CI check.");
|
|
956
|
+
let workflowRuns = [];
|
|
957
|
+
try {
|
|
958
|
+
const runsJson = execFileSync("gh", ["run", "list", "--branch", branch, "--limit", "5", "--json", "status,conclusion,name"], { cwd, encoding: "utf8", timeout: 15000 }).trim();
|
|
959
|
+
if (runsJson && runsJson !== "[]") {
|
|
960
|
+
workflowRuns = JSON.parse(runsJson);
|
|
961
|
+
}
|
|
962
|
+
} catch {}
|
|
963
|
+
let thirdPartyChecks = [];
|
|
964
|
+
const sha = getHeadSha(cwd);
|
|
965
|
+
if (sha) {
|
|
966
|
+
thirdPartyChecks = getThirdPartyCheckRuns(cwd, sha);
|
|
967
|
+
}
|
|
968
|
+
const allChecks = [...workflowRuns, ...thirdPartyChecks];
|
|
969
|
+
if (allChecks.length === 0)
|
|
970
|
+
return allow(`No CI runs found for branch "${branch}".`);
|
|
971
|
+
const failing = allChecks.filter((r) => r.status === "completed" && r.conclusion !== "success" && r.conclusion !== "skipped");
|
|
972
|
+
if (failing.length > 0) {
|
|
973
|
+
const names = failing.map((r) => `"${r.name}"`).join(", ");
|
|
974
|
+
return deny(`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks before stopping.`);
|
|
975
|
+
}
|
|
976
|
+
const pending = allChecks.filter((r) => r.status === "in_progress" || r.status === "queued" || r.status === "waiting");
|
|
977
|
+
if (pending.length > 0) {
|
|
978
|
+
const names = pending.map((r) => `"${r.name}"`).join(", ");
|
|
979
|
+
return deny(`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete and verify they pass.`);
|
|
980
|
+
}
|
|
981
|
+
return allow(`All CI checks passed on branch "${branch}".`);
|
|
982
|
+
} catch {
|
|
983
|
+
return allow("Could not check CI status, skipping.");
|
|
984
|
+
}
|
|
985
|
+
}
|
|
770
986
|
function registerBuiltinPolicies(enabledNames) {
|
|
771
987
|
const enabledSet = new Set(enabledNames);
|
|
772
988
|
for (const policy of BUILTIN_POLICIES) {
|
|
@@ -802,7 +1018,7 @@ var init_builtin_policies = __esm(() => {
|
|
|
802
1018
|
SCHEMA_ALTER_RE = /\bALTER\s+TABLE\b[\s\S]*\b(?:DROP\s+COLUMN|ADD\s+COLUMN|RENAME\s+(?:COLUMN|TO)|MODIFY\s+COLUMN)\b/i;
|
|
803
1019
|
PUBLISH_CMD_RE = /(?:npm\s+publish|bun\s+publish|pnpm\s+publish|yarn\s+npm\s+publish|twine\s+upload|poetry\s+publish|cargo\s+publish|gem\s+push)\b/;
|
|
804
1020
|
ENV_PRINTENV_RE = /(?:^|\s|;|&&|\|\|)(?:env|printenv)(?:\s|$|;|&&|\|)/;
|
|
805
|
-
ECHO_ENV_RE = /echo\s
|
|
1021
|
+
ECHO_ENV_RE = /echo\s+.*\$\{?[A-Za-z_]/;
|
|
806
1022
|
EXPORT_RE = /(?:^|\s|;|&&|\|\|)export\s+\w+/;
|
|
807
1023
|
PS_ENV_VAR_RE = /\$env:[A-Za-z_]/i;
|
|
808
1024
|
PS_CHILDITEM_ENV_RE = /(?:Get-ChildItem|dir|gci|ls)\s+Env:/i;
|
|
@@ -813,7 +1029,7 @@ var init_builtin_policies = __esm(() => {
|
|
|
813
1029
|
SUDO_RE = /(?:^|;|&&|\|\|)\s*sudo\s/;
|
|
814
1030
|
PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
|
|
815
1031
|
RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
|
|
816
|
-
CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh)/;
|
|
1032
|
+
CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
|
|
817
1033
|
PS_WEB_PIPE_RE = /(?:Invoke-WebRequest|iwr|Invoke-RestMethod|irm)\s+.*\|\s*(?:Invoke-Expression|iex)/i;
|
|
818
1034
|
FORCE_PUSH_RE = /(?:--force|-f\b)/;
|
|
819
1035
|
SECRET_FILE_RE = /\.(?:pem|key)$/;
|
|
@@ -1102,11 +1318,71 @@ var init_builtin_policies = __esm(() => {
|
|
|
1102
1318
|
match: { events: ["PreToolUse"] },
|
|
1103
1319
|
defaultEnabled: false,
|
|
1104
1320
|
category: "AI Behavior"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
name: "require-commit-before-stop",
|
|
1324
|
+
description: "Require all changes to be committed before Claude stops",
|
|
1325
|
+
fn: requireCommitBeforeStop,
|
|
1326
|
+
match: { events: ["Stop"] },
|
|
1327
|
+
defaultEnabled: false,
|
|
1328
|
+
category: "Workflow"
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
name: "require-push-before-stop",
|
|
1332
|
+
description: "Require all commits to be pushed to remote before Claude stops",
|
|
1333
|
+
fn: requirePushBeforeStop,
|
|
1334
|
+
match: { events: ["Stop"] },
|
|
1335
|
+
defaultEnabled: false,
|
|
1336
|
+
category: "Workflow",
|
|
1337
|
+
params: {
|
|
1338
|
+
remote: {
|
|
1339
|
+
type: "string",
|
|
1340
|
+
description: "Remote name to push to (default: origin)",
|
|
1341
|
+
default: "origin"
|
|
1342
|
+
},
|
|
1343
|
+
baseBranch: {
|
|
1344
|
+
type: "string",
|
|
1345
|
+
description: "Base branch to compare against (default: main)",
|
|
1346
|
+
default: "main"
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
{
|
|
1351
|
+
name: "require-pr-before-stop",
|
|
1352
|
+
description: "Require a pull request to exist for the current branch before Claude stops",
|
|
1353
|
+
fn: requirePrBeforeStop,
|
|
1354
|
+
match: { events: ["Stop"] },
|
|
1355
|
+
defaultEnabled: false,
|
|
1356
|
+
category: "Workflow",
|
|
1357
|
+
params: {
|
|
1358
|
+
baseBranch: {
|
|
1359
|
+
type: "string",
|
|
1360
|
+
description: "Base branch to compare against (default: main)",
|
|
1361
|
+
default: "main"
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
name: "require-ci-green-before-stop",
|
|
1367
|
+
description: "Require CI checks to pass on the current branch before Claude stops",
|
|
1368
|
+
fn: requireCiGreenBeforeStop,
|
|
1369
|
+
match: { events: ["Stop"] },
|
|
1370
|
+
defaultEnabled: false,
|
|
1371
|
+
category: "Workflow"
|
|
1105
1372
|
}
|
|
1106
1373
|
];
|
|
1107
1374
|
});
|
|
1108
1375
|
|
|
1109
1376
|
// src/hooks/policy-evaluator.ts
|
|
1377
|
+
function appendHint(baseReason, hint) {
|
|
1378
|
+
const base = baseReason.trim();
|
|
1379
|
+
const normalizedHint = typeof hint === "string" ? hint.trim() : "";
|
|
1380
|
+
if (!normalizedHint)
|
|
1381
|
+
return base;
|
|
1382
|
+
if (!base)
|
|
1383
|
+
return normalizedHint;
|
|
1384
|
+
return `${base}. ${normalizedHint}`;
|
|
1385
|
+
}
|
|
1110
1386
|
async function evaluatePolicies(eventType, payload, session, config) {
|
|
1111
1387
|
const toolName = payload.tool_name;
|
|
1112
1388
|
const toolInput = payload.tool_input;
|
|
@@ -1122,8 +1398,8 @@ async function evaluatePolicies(eventType, payload, session, config) {
|
|
|
1122
1398
|
toolInput,
|
|
1123
1399
|
session
|
|
1124
1400
|
};
|
|
1125
|
-
|
|
1126
|
-
|
|
1401
|
+
const instructEntries = [];
|
|
1402
|
+
const allowEntries = [];
|
|
1127
1403
|
for (const policy of policies) {
|
|
1128
1404
|
const schema = POLICY_PARAMS_MAP.get(policy.name);
|
|
1129
1405
|
let ctx;
|
|
@@ -1145,7 +1421,7 @@ async function evaluatePolicies(eventType, payload, session, config) {
|
|
|
1145
1421
|
continue;
|
|
1146
1422
|
}
|
|
1147
1423
|
if (result.decision === "deny") {
|
|
1148
|
-
const reason = result.reason ?? `Blocked by policy: ${policy.name}
|
|
1424
|
+
const reason = appendHint(result.reason ?? `Blocked by policy: ${policy.name}`, config?.policyParams?.[policy.name]?.hint);
|
|
1149
1425
|
hookLogInfo(`deny by "${policy.name}": ${reason}`);
|
|
1150
1426
|
const displayTool = ctx.toolName ?? "unknown tool";
|
|
1151
1427
|
if (eventType === "PreToolUse") {
|
|
@@ -1184,44 +1460,63 @@ async function evaluatePolicies(eventType, payload, session, config) {
|
|
|
1184
1460
|
return {
|
|
1185
1461
|
exitCode: 2,
|
|
1186
1462
|
stdout: "",
|
|
1187
|
-
stderr:
|
|
1463
|
+
stderr: reason,
|
|
1188
1464
|
policyName: policy.name,
|
|
1189
1465
|
reason,
|
|
1190
1466
|
decision: "deny"
|
|
1191
1467
|
};
|
|
1192
1468
|
}
|
|
1193
|
-
if (result.decision === "instruct"
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
hookLogInfo(`instruct by "${policy.name}": ${
|
|
1469
|
+
if (result.decision === "instruct") {
|
|
1470
|
+
const reason = appendHint(result.reason ?? `Instruction from policy: ${policy.name}`, config?.policyParams?.[policy.name]?.hint);
|
|
1471
|
+
instructEntries.push({ policyName: policy.name, reason });
|
|
1472
|
+
hookLogInfo(`instruct by "${policy.name}": ${reason}`);
|
|
1473
|
+
}
|
|
1474
|
+
if (result.decision === "allow" && result.reason) {
|
|
1475
|
+
allowEntries.push({ policyName: policy.name, reason: result.reason });
|
|
1197
1476
|
}
|
|
1198
1477
|
}
|
|
1199
|
-
if (
|
|
1478
|
+
if (instructEntries.length > 0) {
|
|
1479
|
+
const combined = instructEntries.map((e) => e.reason).join(`
|
|
1480
|
+
`);
|
|
1481
|
+
const policyNames = instructEntries.map((e) => e.policyName);
|
|
1200
1482
|
if (eventType === "Stop") {
|
|
1201
1483
|
return {
|
|
1202
1484
|
exitCode: 2,
|
|
1203
1485
|
stdout: "",
|
|
1204
|
-
stderr:
|
|
1205
|
-
policyName:
|
|
1206
|
-
|
|
1486
|
+
stderr: combined,
|
|
1487
|
+
policyName: policyNames[0],
|
|
1488
|
+
policyNames,
|
|
1489
|
+
reason: combined,
|
|
1207
1490
|
decision: "instruct"
|
|
1208
1491
|
};
|
|
1209
1492
|
}
|
|
1210
1493
|
const response = {
|
|
1211
1494
|
hookSpecificOutput: {
|
|
1212
1495
|
hookEventName: eventType,
|
|
1213
|
-
additionalContext: `Instruction from failproofai: ${
|
|
1496
|
+
additionalContext: `Instruction from failproofai: ${combined}`
|
|
1214
1497
|
}
|
|
1215
1498
|
};
|
|
1216
1499
|
return {
|
|
1217
1500
|
exitCode: 0,
|
|
1218
1501
|
stdout: JSON.stringify(response),
|
|
1219
1502
|
stderr: "",
|
|
1220
|
-
policyName:
|
|
1221
|
-
|
|
1503
|
+
policyName: policyNames[0],
|
|
1504
|
+
policyNames,
|
|
1505
|
+
reason: combined,
|
|
1222
1506
|
decision: "instruct"
|
|
1223
1507
|
};
|
|
1224
1508
|
}
|
|
1509
|
+
if (allowEntries.length > 0) {
|
|
1510
|
+
const combined = allowEntries.map((e) => e.reason).join(`
|
|
1511
|
+
`);
|
|
1512
|
+
const policyNames = allowEntries.map((e) => e.policyName);
|
|
1513
|
+
const supportsHookSpecificOutput = eventType === "PreToolUse" || eventType === "PostToolUse" || eventType === "UserPromptSubmit";
|
|
1514
|
+
const response = supportsHookSpecificOutput ? { hookSpecificOutput: { hookEventName: eventType, additionalContext: `Note from failproofai: ${combined}` } } : { reason: combined };
|
|
1515
|
+
const stderrMsg = allowEntries.map((e) => `[failproofai] ${e.policyName}: ${e.reason}`).join(`
|
|
1516
|
+
`);
|
|
1517
|
+
return { exitCode: 0, stdout: JSON.stringify(response), stderr: stderrMsg + `
|
|
1518
|
+
`, policyName: policyNames[0], policyNames, reason: combined, decision: "allow" };
|
|
1519
|
+
}
|
|
1225
1520
|
return { exitCode: 0, stdout: "", stderr: "", policyName: null, reason: null, decision: "allow" };
|
|
1226
1521
|
}
|
|
1227
1522
|
var POLICY_PARAMS_MAP;
|
|
@@ -1291,10 +1586,9 @@ async function createEsmShim(distIndex, distUrl) {
|
|
|
1291
1586
|
const shimPath = distIndex + ".__failproofai_esm_shim__.mjs";
|
|
1292
1587
|
const shimCode = [
|
|
1293
1588
|
`import _cjs from '${distUrl}';`,
|
|
1294
|
-
`export const createApp = _cjs.createApp;`,
|
|
1295
|
-
`export const getQueueCondition = _cjs.getQueueCondition;`,
|
|
1296
|
-
`export const clearQueueCondition = _cjs.clearQueueCondition;`,
|
|
1297
1589
|
`export const customPolicies = _cjs.customPolicies;`,
|
|
1590
|
+
`export const getCustomHooks = _cjs.getCustomHooks;`,
|
|
1591
|
+
`export const clearCustomHooks = _cjs.clearCustomHooks;`,
|
|
1298
1592
|
`export const allow = _cjs.allow;`,
|
|
1299
1593
|
`export const deny = _cjs.deny;`,
|
|
1300
1594
|
`export const instruct = _cjs.instruct;`,
|
|
@@ -1374,20 +1668,21 @@ var init_loader_utils = __esm(() => {
|
|
|
1374
1668
|
});
|
|
1375
1669
|
|
|
1376
1670
|
// src/hooks/custom-hooks-loader.ts
|
|
1377
|
-
import { resolve as resolve4, isAbsolute } from "node:path";
|
|
1378
|
-
import { existsSync as existsSync3 } from "node:fs";
|
|
1671
|
+
import { resolve as resolve4, isAbsolute, basename } from "node:path";
|
|
1672
|
+
import { existsSync as existsSync3, readdirSync } from "node:fs";
|
|
1379
1673
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
1380
|
-
|
|
1381
|
-
|
|
1674
|
+
import { homedir as homedir4 } from "node:os";
|
|
1675
|
+
function discoverPolicyFiles(dir) {
|
|
1676
|
+
if (!existsSync3(dir))
|
|
1382
1677
|
return [];
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
hookLogWarn(`customPoliciesPath not found: ${absPath}`);
|
|
1678
|
+
try {
|
|
1679
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
1680
|
+
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve4(dir, e.name));
|
|
1681
|
+
} catch {
|
|
1388
1682
|
return [];
|
|
1389
1683
|
}
|
|
1390
|
-
|
|
1684
|
+
}
|
|
1685
|
+
async function loadSingleFile(absPath, opts) {
|
|
1391
1686
|
const g = globalThis;
|
|
1392
1687
|
g[LOADING_KEY] = true;
|
|
1393
1688
|
let tmpFiles = [];
|
|
@@ -1403,18 +1698,93 @@ async function loadCustomHooks(customPoliciesPath, opts) {
|
|
|
1403
1698
|
if (opts?.strict)
|
|
1404
1699
|
throw new Error(`Failed to load custom hooks from ${absPath}: ${msg}`);
|
|
1405
1700
|
hookLogError(`failed to load custom hooks from ${absPath}: ${msg}`);
|
|
1406
|
-
return [];
|
|
1407
1701
|
} finally {
|
|
1408
1702
|
g[LOADING_KEY] = false;
|
|
1409
1703
|
await cleanupTmpFiles(tmpFiles);
|
|
1410
1704
|
}
|
|
1705
|
+
}
|
|
1706
|
+
async function loadCustomHooks(customPoliciesPath, opts) {
|
|
1707
|
+
if (!customPoliciesPath)
|
|
1708
|
+
return [];
|
|
1709
|
+
const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(opts?.sessionCwd ?? process.cwd(), customPoliciesPath);
|
|
1710
|
+
if (!existsSync3(absPath)) {
|
|
1711
|
+
if (opts?.strict)
|
|
1712
|
+
throw new Error(`Custom hooks file not found: ${absPath}`);
|
|
1713
|
+
hookLogWarn(`customPoliciesPath not found: ${absPath}`);
|
|
1714
|
+
return [];
|
|
1715
|
+
}
|
|
1716
|
+
clearCustomHooks();
|
|
1717
|
+
await loadSingleFile(absPath, opts);
|
|
1411
1718
|
return getCustomHooks();
|
|
1412
1719
|
}
|
|
1413
|
-
|
|
1720
|
+
async function loadAllCustomHooks(customPoliciesPath, opts) {
|
|
1721
|
+
clearCustomHooks();
|
|
1722
|
+
const conventionSources = [];
|
|
1723
|
+
if (customPoliciesPath) {
|
|
1724
|
+
const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(opts?.sessionCwd ?? process.cwd(), customPoliciesPath);
|
|
1725
|
+
if (existsSync3(absPath)) {
|
|
1726
|
+
await loadSingleFile(absPath);
|
|
1727
|
+
} else {
|
|
1728
|
+
hookLogWarn(`customPoliciesPath not found: ${absPath}`);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
const hooksBeforeConvention = getCustomHooks().length;
|
|
1732
|
+
const projectDir = resolve4(opts?.sessionCwd ?? process.cwd(), ".failproofai", "policies");
|
|
1733
|
+
const projectFiles = discoverPolicyFiles(projectDir);
|
|
1734
|
+
for (const file of projectFiles) {
|
|
1735
|
+
const hooksBefore = getCustomHooks().length;
|
|
1736
|
+
await loadSingleFile(file);
|
|
1737
|
+
const newHooks = getCustomHooks().slice(hooksBefore);
|
|
1738
|
+
if (newHooks.length > 0) {
|
|
1739
|
+
conventionSources.push({
|
|
1740
|
+
scope: "project",
|
|
1741
|
+
file: basename(file),
|
|
1742
|
+
hookNames: newHooks.map((h) => h.name)
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
const userDir = resolve4(homedir4(), ".failproofai", "policies");
|
|
1747
|
+
const userFiles = discoverPolicyFiles(userDir);
|
|
1748
|
+
for (const file of userFiles) {
|
|
1749
|
+
const hooksBefore = getCustomHooks().length;
|
|
1750
|
+
await loadSingleFile(file);
|
|
1751
|
+
const newHooks = getCustomHooks().slice(hooksBefore);
|
|
1752
|
+
if (newHooks.length > 0) {
|
|
1753
|
+
conventionSources.push({
|
|
1754
|
+
scope: "user",
|
|
1755
|
+
file: basename(file),
|
|
1756
|
+
hookNames: newHooks.map((h) => h.name)
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const allHooks = getCustomHooks();
|
|
1761
|
+
const conventionCount = allHooks.length - hooksBeforeConvention;
|
|
1762
|
+
if (projectFiles.length > 0 || userFiles.length > 0) {
|
|
1763
|
+
hookLogInfo(`convention policies: ${projectFiles.length} project file(s), ${userFiles.length} user file(s), ${conventionCount} hook(s)`);
|
|
1764
|
+
}
|
|
1765
|
+
const hookNameToScope = new Map;
|
|
1766
|
+
for (const source of conventionSources) {
|
|
1767
|
+
for (const name of source.hookNames) {
|
|
1768
|
+
hookNameToScope.set(name, source.scope);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
const conventionHookRefs = new Set;
|
|
1772
|
+
for (const hook of allHooks.slice(hooksBeforeConvention)) {
|
|
1773
|
+
conventionHookRefs.add(hook);
|
|
1774
|
+
}
|
|
1775
|
+
for (const hook of allHooks) {
|
|
1776
|
+
if (conventionHookRefs.has(hook)) {
|
|
1777
|
+
hook.__conventionScope = hookNameToScope.get(hook.name) ?? "project";
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
return { hooks: allHooks, conventionSources };
|
|
1781
|
+
}
|
|
1782
|
+
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE;
|
|
1414
1783
|
var init_custom_hooks_loader = __esm(() => {
|
|
1415
1784
|
init_hook_logger();
|
|
1416
1785
|
init_custom_hooks_registry();
|
|
1417
1786
|
init_loader_utils();
|
|
1787
|
+
CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
|
|
1418
1788
|
});
|
|
1419
1789
|
|
|
1420
1790
|
// src/hooks/hook-activity-store.ts
|
|
@@ -1423,14 +1793,14 @@ import {
|
|
|
1423
1793
|
writeFileSync as writeFileSync2,
|
|
1424
1794
|
appendFileSync as appendFileSync2,
|
|
1425
1795
|
renameSync as renameSync2,
|
|
1426
|
-
readdirSync,
|
|
1796
|
+
readdirSync as readdirSync2,
|
|
1427
1797
|
mkdirSync as mkdirSync3,
|
|
1428
1798
|
existsSync as existsSync4,
|
|
1429
1799
|
statSync as statSync2,
|
|
1430
1800
|
unlinkSync
|
|
1431
1801
|
} from "node:fs";
|
|
1432
1802
|
import { join as join3 } from "node:path";
|
|
1433
|
-
import { homedir as
|
|
1803
|
+
import { homedir as homedir5 } from "node:os";
|
|
1434
1804
|
function ensureDir() {
|
|
1435
1805
|
if (!existsSync4(storeDir)) {
|
|
1436
1806
|
mkdirSync3(storeDir, { recursive: true });
|
|
@@ -1516,7 +1886,11 @@ function updateStats(entry) {
|
|
|
1516
1886
|
s.totalEvents += 1;
|
|
1517
1887
|
if (entry.decision === "deny")
|
|
1518
1888
|
s.denyCount += 1;
|
|
1519
|
-
if (entry.
|
|
1889
|
+
if (entry.policyNames && entry.policyNames.length > 0) {
|
|
1890
|
+
for (const name of entry.policyNames) {
|
|
1891
|
+
s.policyMap[name] = (s.policyMap[name] ?? 0) + 1;
|
|
1892
|
+
}
|
|
1893
|
+
} else if (entry.policyName) {
|
|
1520
1894
|
s.policyMap[entry.policyName] = (s.policyMap[entry.policyName] ?? 0) + 1;
|
|
1521
1895
|
}
|
|
1522
1896
|
const tmpPath = join3(storeDir, `stats.json.${process.pid}.tmp`);
|
|
@@ -1531,12 +1905,12 @@ function updateStats(entry) {
|
|
|
1531
1905
|
}
|
|
1532
1906
|
var PAGE_SIZE = 25, DEFAULT_STORE_DIR, CURRENT_FILE = "current.jsonl", COUNT_FILE = "current.count", STATS_FILE = "stats.json", LOCK_FILE = "current.lock", LOCK_STALE_MS = 2000, storeDir, rotateSeq = 0;
|
|
1533
1907
|
var init_hook_activity_store = __esm(() => {
|
|
1534
|
-
DEFAULT_STORE_DIR = join3(
|
|
1908
|
+
DEFAULT_STORE_DIR = join3(homedir5(), ".failproofai", "cache", "hook-activity");
|
|
1535
1909
|
storeDir = DEFAULT_STORE_DIR;
|
|
1536
1910
|
});
|
|
1537
1911
|
|
|
1538
1912
|
// package.json
|
|
1539
|
-
var version2 = "0.0.
|
|
1913
|
+
var version2 = "0.0.4-beta.0";
|
|
1540
1914
|
var init_package = () => {};
|
|
1541
1915
|
|
|
1542
1916
|
// src/posthog-key.ts
|
|
@@ -1698,9 +2072,14 @@ async function handleHookEvent(eventType) {
|
|
|
1698
2072
|
const config = readMergedHooksConfig(session.cwd);
|
|
1699
2073
|
clearPolicies();
|
|
1700
2074
|
registerBuiltinPolicies(config.enabledPolicies);
|
|
1701
|
-
const
|
|
2075
|
+
const loadResult = await loadAllCustomHooks(config.customPoliciesPath, { sessionCwd: session.cwd });
|
|
2076
|
+
const customHooksList = loadResult.hooks;
|
|
2077
|
+
const conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
|
|
1702
2078
|
for (const hook of customHooksList) {
|
|
1703
2079
|
const hookName = hook.name;
|
|
2080
|
+
const conventionScope = hook.__conventionScope;
|
|
2081
|
+
const isConvention = !!conventionScope;
|
|
2082
|
+
const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
|
|
1704
2083
|
const fn = async (ctx) => {
|
|
1705
2084
|
try {
|
|
1706
2085
|
const result2 = await Promise.race([
|
|
@@ -1711,16 +2090,18 @@ async function handleHookEvent(eventType) {
|
|
|
1711
2090
|
} catch (err) {
|
|
1712
2091
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1713
2092
|
const isTimeout = msg === "timeout";
|
|
1714
|
-
hookLogWarn(
|
|
2093
|
+
hookLogWarn(`${prefix} hook "${hookName}" failed: ${msg}`);
|
|
1715
2094
|
trackHookEvent(getInstanceId(), "custom_hook_error", {
|
|
1716
2095
|
hook_name: hookName,
|
|
1717
2096
|
error_type: isTimeout ? "timeout" : "exception",
|
|
1718
|
-
event_type: eventType
|
|
2097
|
+
event_type: eventType,
|
|
2098
|
+
is_convention_policy: isConvention,
|
|
2099
|
+
convention_scope: conventionScope ?? null
|
|
1719
2100
|
});
|
|
1720
2101
|
return { decision: "allow" };
|
|
1721
2102
|
}
|
|
1722
2103
|
};
|
|
1723
|
-
registerPolicy(
|
|
2104
|
+
registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
|
|
1724
2105
|
}
|
|
1725
2106
|
if (customHooksList.length > 0) {
|
|
1726
2107
|
trackHookEvent(getInstanceId(), "custom_hooks_loaded", {
|
|
@@ -1729,7 +2110,16 @@ async function handleHookEvent(eventType) {
|
|
|
1729
2110
|
event_types_covered: [...new Set(customHooksList.flatMap((h) => h.match?.events ?? []))]
|
|
1730
2111
|
});
|
|
1731
2112
|
}
|
|
1732
|
-
|
|
2113
|
+
if (loadResult.conventionSources.length > 0) {
|
|
2114
|
+
trackHookEvent(getInstanceId(), "convention_policies_loaded", {
|
|
2115
|
+
event_type: eventType,
|
|
2116
|
+
project_file_count: loadResult.conventionSources.filter((s) => s.scope === "project").length,
|
|
2117
|
+
user_file_count: loadResult.conventionSources.filter((s) => s.scope === "user").length,
|
|
2118
|
+
convention_hook_count: conventionHookNames.size,
|
|
2119
|
+
convention_hook_names: [...conventionHookNames]
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
hookLogInfo(`event=${eventType} policies=${config.enabledPolicies.length} custom=${customHooksList.length} convention=${conventionHookNames.size}`);
|
|
1733
2123
|
const result = await evaluatePolicies(eventType, parsed, session, config);
|
|
1734
2124
|
const durationMs = Math.round(performance.now() - startTime);
|
|
1735
2125
|
hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
|
|
@@ -1745,6 +2135,7 @@ async function handleHookEvent(eventType) {
|
|
|
1745
2135
|
eventType,
|
|
1746
2136
|
toolName: parsed.tool_name ?? null,
|
|
1747
2137
|
policyName: result.policyName,
|
|
2138
|
+
policyNames: result.policyNames,
|
|
1748
2139
|
decision: result.decision,
|
|
1749
2140
|
reason: result.reason,
|
|
1750
2141
|
durationMs,
|
|
@@ -1760,7 +2151,9 @@ async function handleHookEvent(eventType) {
|
|
|
1760
2151
|
if (result.decision === "deny" || result.decision === "instruct") {
|
|
1761
2152
|
try {
|
|
1762
2153
|
const isCustomHook = result.policyName?.startsWith("custom/") ?? false;
|
|
1763
|
-
const
|
|
2154
|
+
const isConventionPolicy = result.policyName?.startsWith(".failproofai-") ?? false;
|
|
2155
|
+
const conventionScope = isConventionPolicy ? result.policyName.match(/^\.failproofai-(project|user)\//)?.[1] ?? null : null;
|
|
2156
|
+
const hasCustomParams = !isCustomHook && !isConventionPolicy && !!(result.policyName && config.policyParams?.[result.policyName]);
|
|
1764
2157
|
const paramKeysOverridden = hasCustomParams ? Object.keys(config.policyParams[result.policyName]) : [];
|
|
1765
2158
|
const distinctId = getInstanceId();
|
|
1766
2159
|
await trackHookEvent(distinctId, "hook_policy_triggered", {
|
|
@@ -1769,6 +2162,8 @@ async function handleHookEvent(eventType) {
|
|
|
1769
2162
|
policy_name: result.policyName,
|
|
1770
2163
|
decision: result.decision,
|
|
1771
2164
|
is_custom_hook: isCustomHook,
|
|
2165
|
+
is_convention_policy: isConventionPolicy,
|
|
2166
|
+
convention_scope: conventionScope,
|
|
1772
2167
|
has_custom_params: hasCustomParams,
|
|
1773
2168
|
param_keys_overridden: paramKeysOverridden
|
|
1774
2169
|
});
|
|
@@ -2101,13 +2496,13 @@ __export(exports_manager, {
|
|
|
2101
2496
|
});
|
|
2102
2497
|
import { execSync as execSync3 } from "node:child_process";
|
|
2103
2498
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "node:fs";
|
|
2104
|
-
import { resolve as resolve5, dirname as dirname3 } from "node:path";
|
|
2105
|
-
import { homedir as
|
|
2499
|
+
import { resolve as resolve5, dirname as dirname3, basename as basename2 } from "node:path";
|
|
2500
|
+
import { homedir as homedir6, platform, arch, release, hostname } from "node:os";
|
|
2106
2501
|
function getSettingsPath(scope, cwd) {
|
|
2107
2502
|
const base = cwd ? resolve5(cwd) : process.cwd();
|
|
2108
2503
|
switch (scope) {
|
|
2109
2504
|
case "user":
|
|
2110
|
-
return resolve5(
|
|
2505
|
+
return resolve5(homedir6(), ".claude", "settings.json");
|
|
2111
2506
|
case "project":
|
|
2112
2507
|
return resolve5(base, ".claude", "settings.json");
|
|
2113
2508
|
case "local":
|
|
@@ -2234,7 +2629,7 @@ async function installHooks(policyNames, scope = "user", cwd, includeBeta = fals
|
|
|
2234
2629
|
}
|
|
2235
2630
|
}
|
|
2236
2631
|
const binaryPath = resolveFailproofaiBinary();
|
|
2237
|
-
const previousConfig =
|
|
2632
|
+
const previousConfig = readScopedHooksConfig(scope, cwd);
|
|
2238
2633
|
const previousEnabled = new Set(previousConfig.enabledPolicies);
|
|
2239
2634
|
let selectedPolicies;
|
|
2240
2635
|
if (policyNames !== undefined) {
|
|
@@ -2268,7 +2663,7 @@ async function installHooks(policyNames, scope = "user", cwd, includeBeta = fals
|
|
|
2268
2663
|
console.log(`
|
|
2269
2664
|
Validated ${validatedHooks.length} custom hook(s): ${validatedHooks.map((h) => h.name).join(", ")}`);
|
|
2270
2665
|
}
|
|
2271
|
-
|
|
2666
|
+
writeScopedHooksConfig(configToWrite, scope, cwd);
|
|
2272
2667
|
console.log(`
|
|
2273
2668
|
Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}`);
|
|
2274
2669
|
if (removeCustomHooks) {
|
|
@@ -2282,7 +2677,7 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}`)
|
|
|
2282
2677
|
settings.hooks = {};
|
|
2283
2678
|
}
|
|
2284
2679
|
for (const eventType of HOOK_EVENT_TYPES) {
|
|
2285
|
-
const command = `"${binaryPath}" --hook ${eventType}`;
|
|
2680
|
+
const command = scope === "project" ? `npx -y failproofai --hook ${eventType}` : `"${binaryPath}" --hook ${eventType}`;
|
|
2286
2681
|
const hookEntry = {
|
|
2287
2682
|
type: "command",
|
|
2288
2683
|
command,
|
|
@@ -2327,12 +2722,19 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}`)
|
|
|
2327
2722
|
hostname_hash: hashToId(hostname()),
|
|
2328
2723
|
has_custom_hooks_path: !!configToWrite.customPoliciesPath,
|
|
2329
2724
|
has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
|
|
2330
|
-
param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : []
|
|
2725
|
+
param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
|
|
2726
|
+
command_format: scope === "project" ? "npx" : "absolute"
|
|
2331
2727
|
});
|
|
2332
2728
|
} catch {}
|
|
2333
2729
|
console.log(`Failproof AI hooks installed for all ${HOOK_EVENT_TYPES.length} event types (scope: ${scope}).`);
|
|
2334
2730
|
console.log(`Settings: ${settingsPath}`);
|
|
2335
|
-
|
|
2731
|
+
if (scope === "project") {
|
|
2732
|
+
console.log(`Command: npx -y failproofai`);
|
|
2733
|
+
console.log(`
|
|
2734
|
+
This file can be committed to git — no machine-specific paths.`);
|
|
2735
|
+
} else {
|
|
2736
|
+
console.log(`Binary: ${binaryPath}`);
|
|
2737
|
+
}
|
|
2336
2738
|
const otherScopes = deduplicateScopes(HOOK_SCOPES, cwd).filter((s) => s !== scope);
|
|
2337
2739
|
const duplicates = otherScopes.filter((s) => hooksInstalledInSettings(s, cwd));
|
|
2338
2740
|
if (duplicates.length > 0) {
|
|
@@ -2345,15 +2747,16 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}`)
|
|
|
2345
2747
|
}
|
|
2346
2748
|
}
|
|
2347
2749
|
async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
2750
|
+
const configScope = scope === "all" ? "user" : scope;
|
|
2348
2751
|
if (opts?.removeCustomHooks) {
|
|
2349
|
-
const config =
|
|
2752
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
2350
2753
|
delete config.customPoliciesPath;
|
|
2351
|
-
|
|
2754
|
+
writeScopedHooksConfig(config, configScope, cwd);
|
|
2352
2755
|
console.log("Custom hooks path cleared.");
|
|
2353
2756
|
}
|
|
2354
2757
|
if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
|
|
2355
2758
|
validatePolicyNames(policyNames);
|
|
2356
|
-
const config =
|
|
2759
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
2357
2760
|
const removeSet = new Set(policyNames);
|
|
2358
2761
|
const remaining = config.enabledPolicies.filter((p) => !removeSet.has(p));
|
|
2359
2762
|
const notEnabled = policyNames.filter((p) => !config.enabledPolicies.includes(p));
|
|
@@ -2367,7 +2770,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
|
2367
2770
|
enabledPolicies: remaining,
|
|
2368
2771
|
...filteredParams && Object.keys(filteredParams).length > 0 ? { policyParams: filteredParams } : {}
|
|
2369
2772
|
};
|
|
2370
|
-
|
|
2773
|
+
writeScopedHooksConfig(updatedConfig, configScope, cwd);
|
|
2371
2774
|
try {
|
|
2372
2775
|
const distinctId = getInstanceId();
|
|
2373
2776
|
const actuallyRemoved = policyNames.filter((p) => config.enabledPolicies.includes(p));
|
|
@@ -2388,7 +2791,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
|
2388
2791
|
console.log(`Remaining: ${remaining.length > 0 ? remaining.join(", ") : "(none)"}`);
|
|
2389
2792
|
return;
|
|
2390
2793
|
}
|
|
2391
|
-
const configBeforeRemoval =
|
|
2794
|
+
const configBeforeRemoval = readScopedHooksConfig(configScope, cwd);
|
|
2392
2795
|
const scopesToRemove = scope === "all" ? [...HOOK_SCOPES] : [scope];
|
|
2393
2796
|
let totalRemoved = 0;
|
|
2394
2797
|
for (const s of scopesToRemove) {
|
|
@@ -2435,10 +2838,18 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
|
2435
2838
|
hostname_hash: hashToId(hostname())
|
|
2436
2839
|
});
|
|
2437
2840
|
} catch {}
|
|
2438
|
-
if (scope === "all"
|
|
2439
|
-
const
|
|
2440
|
-
|
|
2441
|
-
|
|
2841
|
+
if (scope === "all") {
|
|
2842
|
+
for (const s of HOOK_SCOPES) {
|
|
2843
|
+
const existing = readScopedHooksConfig(s, cwd);
|
|
2844
|
+
if (existing.enabledPolicies.length > 0 || existing.customPoliciesPath || existing.policyParams) {
|
|
2845
|
+
const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
|
|
2846
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, s, cwd);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
} else if (!HOOK_SCOPES.some((s) => hooksInstalledInSettings(s, cwd))) {
|
|
2850
|
+
const existing = readScopedHooksConfig(configScope, cwd);
|
|
2851
|
+
const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
|
|
2852
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, configScope, cwd);
|
|
2442
2853
|
}
|
|
2443
2854
|
}
|
|
2444
2855
|
async function listHooks(cwd) {
|
|
@@ -2575,6 +2986,35 @@ Failproof AI Hook Policies
|
|
|
2575
2986
|
}
|
|
2576
2987
|
console.log();
|
|
2577
2988
|
}
|
|
2989
|
+
const base = cwd ? resolve5(cwd) : process.cwd();
|
|
2990
|
+
const conventionDirs = [
|
|
2991
|
+
{ label: "Project", dir: resolve5(base, ".failproofai", "policies") },
|
|
2992
|
+
{ label: "User", dir: resolve5(homedir6(), ".failproofai", "policies") }
|
|
2993
|
+
];
|
|
2994
|
+
for (const { label, dir } of conventionDirs) {
|
|
2995
|
+
const files = discoverPolicyFiles(dir);
|
|
2996
|
+
if (files.length === 0)
|
|
2997
|
+
continue;
|
|
2998
|
+
console.log(`
|
|
2999
|
+
── Convention Policies — ${label} (${dir}) ──────────`);
|
|
3000
|
+
for (const file of files) {
|
|
3001
|
+
try {
|
|
3002
|
+
const hooks = await loadCustomHooks(file);
|
|
3003
|
+
if (hooks.length === 0) {
|
|
3004
|
+
const filename = basename2(file);
|
|
3005
|
+
console.log(` \x1B[31m✗\x1B[0m ${filename.padEnd(nameColWidth)}\x1B[31mfailed to load\x1B[0m`);
|
|
3006
|
+
} else {
|
|
3007
|
+
const filename = basename2(file);
|
|
3008
|
+
const hookSummary = hooks.map((h) => h.name).join(", ");
|
|
3009
|
+
console.log(` \x1B[32m✓\x1B[0m ${filename.padEnd(nameColWidth)}${hooks.length} hook(s): ${hookSummary}`);
|
|
3010
|
+
}
|
|
3011
|
+
} catch {
|
|
3012
|
+
const filename = basename2(file);
|
|
3013
|
+
console.log(` \x1B[31m✗\x1B[0m ${filename.padEnd(nameColWidth)}\x1B[31merror\x1B[0m`);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
console.log();
|
|
3017
|
+
}
|
|
2578
3018
|
}
|
|
2579
3019
|
var VALID_POLICY_NAMES;
|
|
2580
3020
|
var init_manager = __esm(() => {
|
|
@@ -2590,10 +3030,10 @@ var init_manager = __esm(() => {
|
|
|
2590
3030
|
});
|
|
2591
3031
|
|
|
2592
3032
|
// lib/paths.ts
|
|
2593
|
-
import { homedir as
|
|
3033
|
+
import { homedir as homedir7 } from "os";
|
|
2594
3034
|
import { join as join4 } from "path";
|
|
2595
3035
|
function getDefaultClaudeProjectsPath() {
|
|
2596
|
-
return join4(
|
|
3036
|
+
return join4(homedir7(), ".claude", "projects");
|
|
2597
3037
|
}
|
|
2598
3038
|
var init_paths = () => {};
|
|
2599
3039
|
|
|
@@ -2762,7 +3202,7 @@ import { realpathSync as realpathSync2 } from "fs";
|
|
|
2762
3202
|
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
2763
3203
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2764
3204
|
// package.json
|
|
2765
|
-
var version = "0.0.
|
|
3205
|
+
var version = "0.0.4-beta.0";
|
|
2766
3206
|
|
|
2767
3207
|
// bin/failproofai.mjs
|
|
2768
3208
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
@@ -2826,6 +3266,11 @@ COMMANDS
|
|
|
2826
3266
|
--version, -v Print version and exit
|
|
2827
3267
|
--help, -h Show this help message
|
|
2828
3268
|
|
|
3269
|
+
CONVENTION POLICIES
|
|
3270
|
+
Drop *policies.{js,mjs,ts} files into .failproofai/policies/ for auto-loading.
|
|
3271
|
+
Works at project level (.failproofai/policies/) and user level (~/.failproofai/policies/).
|
|
3272
|
+
No --custom flag or config changes needed \u2014 just drop files and they're picked up.
|
|
3273
|
+
|
|
2829
3274
|
EXAMPLES
|
|
2830
3275
|
failproofai policies
|
|
2831
3276
|
failproofai policies --install
|