failproofai 0.0.1-beta.9 → 0.0.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  22. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/index.html +1 -1
  27. package/.next/standalone/.next/server/app/index.rsc +15 -15
  28. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  30. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  31. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  32. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  33. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  34. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  37. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  40. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  43. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  44. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02nt~6d._.js +1 -1
  50. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  51. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
  52. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
  53. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09z7o2x._.js +2 -2
  54. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0su5_t~._.js → [root-of-the-server]__0a3kr67._.js} +2 -2
  55. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
  56. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
  57. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0osi8nq._.js +2 -2
  58. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0yhzo9v._.js → [root-of-the-server]__0rbuarm._.js} +2 -2
  59. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
  62. package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0rd0oc-._.js +1 -1
  66. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  67. package/.next/standalone/.next/server/pages/404.html +2 -2
  68. package/.next/standalone/.next/server/pages/500.html +1 -1
  69. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  71. package/.next/standalone/.next/static/chunks/{124wu0bxsexm6.js → 0a08gn8709y98.js} +1 -1
  72. package/.next/standalone/.next/static/chunks/{12olt9p45z-lq.js → 0gcz-jqgqz~9m.js} +1 -1
  73. package/.next/standalone/.next/static/chunks/{0pc9pc_pilpi9.js → 0jhw8ofx.5g_e.js} +1 -1
  74. package/.next/standalone/.next/static/chunks/{0.aencsvb-yev.js → 0kob_5.phc~sk.js} +1 -1
  75. package/.next/standalone/.next/static/chunks/{0frzv~pmu0hsf.js → 0mjc3aq2wxvlt.js} +1 -1
  76. package/.next/standalone/.next/static/chunks/{0nygnut7i45jn.js → 0mr-jhx402yci.js} +1 -1
  77. package/.next/standalone/.next/static/chunks/{0qjjki0j187__.js → 0q7z97izctgrw.js} +1 -1
  78. package/.next/standalone/.next/static/chunks/{14xe0g0rgwk18.js → 0qvj8bhl661lq.js} +1 -1
  79. package/.next/standalone/AGENTS.md +80 -0
  80. package/.next/standalone/CLAUDE.md +137 -0
  81. package/.next/standalone/README.md +9 -1
  82. package/.next/standalone/bin/failproofai.mjs +12 -1
  83. package/.next/standalone/dist/cli.mjs +2891 -0
  84. package/.next/standalone/dist/index.js +80 -0
  85. package/.next/standalone/docs/architecture.md +5 -1
  86. package/.next/standalone/docs/built-in-policies.md +5 -1
  87. package/.next/standalone/docs/cli-reference.md +5 -1
  88. package/.next/standalone/docs/configuration.md +5 -1
  89. package/.next/standalone/docs/custom-hooks.md +5 -1
  90. package/.next/standalone/docs/dashboard.md +5 -1
  91. package/.next/standalone/docs/docs.json +83 -0
  92. package/.next/standalone/docs/favicon.ico +0 -0
  93. package/.next/standalone/docs/getting-started.md +8 -2
  94. package/.next/standalone/docs/introduction.md +47 -0
  95. package/.next/standalone/docs/logo/exosphere-dark.png +0 -0
  96. package/.next/standalone/docs/logo/exosphere-light.png +0 -0
  97. package/.next/standalone/docs/package-aliases.md +32 -26
  98. package/.next/standalone/docs/testing.md +5 -1
  99. package/.next/standalone/examples/policies-notification.js +77 -32
  100. package/.next/standalone/package.json +7 -5
  101. package/.next/standalone/scripts/launch.ts +1 -1
  102. package/.next/standalone/scripts/postinstall.mjs +11 -0
  103. package/.next/standalone/scripts/sync-hook-events-prompt.md +60 -0
  104. package/.next/standalone/server.js +1 -1
  105. package/.next/standalone/src/hooks/types.ts +11 -2
  106. package/README.md +9 -1
  107. package/bin/failproofai.mjs +12 -1
  108. package/dist/cli.mjs +2891 -0
  109. package/dist/index.js +80 -0
  110. package/package.json +7 -5
  111. package/scripts/launch.ts +1 -1
  112. package/scripts/postinstall.mjs +11 -0
  113. package/scripts/sync-hook-events-prompt.md +60 -0
  114. package/src/hooks/types.ts +11 -2
  115. package/.next/standalone/docs/index.md +0 -48
  116. /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → Dnk96sbMPjYOx1pdLdOH0}/_buildManifest.js +0 -0
  117. /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → Dnk96sbMPjYOx1pdLdOH0}/_clientMiddlewareManifest.js +0 -0
  118. /package/.next/standalone/.next/static/{o1smiVYETTvcMet_AU8oi → Dnk96sbMPjYOx1pdLdOH0}/_ssgManifest.js +0 -0
@@ -0,0 +1,80 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+
39
+ // src/index.ts
40
+ var exports_src = {};
41
+ __export(exports_src, {
42
+ instruct: () => instruct,
43
+ getCustomHooks: () => getCustomHooks,
44
+ deny: () => deny,
45
+ customPolicies: () => customPolicies,
46
+ clearCustomHooks: () => clearCustomHooks,
47
+ allow: () => allow
48
+ });
49
+ module.exports = __toCommonJS(exports_src);
50
+
51
+ // src/hooks/custom-hooks-registry.ts
52
+ var REGISTRY_KEY = "__failproofai_custom_hooks__";
53
+ function getRegistry() {
54
+ const g = globalThis;
55
+ if (!Array.isArray(g[REGISTRY_KEY]))
56
+ g[REGISTRY_KEY] = [];
57
+ return g[REGISTRY_KEY];
58
+ }
59
+ var customPolicies = {
60
+ add(hook) {
61
+ getRegistry().push(hook);
62
+ }
63
+ };
64
+ function getCustomHooks() {
65
+ return getRegistry();
66
+ }
67
+ function clearCustomHooks() {
68
+ const g = globalThis;
69
+ g[REGISTRY_KEY] = [];
70
+ }
71
+ // src/hooks/policy-helpers.ts
72
+ function allow() {
73
+ return { decision: "allow" };
74
+ }
75
+ function deny(reason) {
76
+ return { decision: "deny", reason };
77
+ }
78
+ function instruct(reason) {
79
+ return { decision: "instruct", reason };
80
+ }
@@ -1,4 +1,8 @@
1
- # Architecture
1
+ ---
2
+ title: Architecture
3
+ description: "How the hook handler, config loading, and policy evaluation work internally"
4
+ icon: sitemap
5
+ ---
2
6
 
3
7
  This document explains how failproofai works internally: how the hook system processes events, how configuration is loaded and merged, how policies are evaluated, and how the dashboard fits in.
4
8
 
@@ -1,4 +1,8 @@
1
- # Built-in Policies
1
+ ---
2
+ title: Built-in Policies
3
+ description: "All 35+ security policies with descriptions and parameters"
4
+ icon: shield
5
+ ---
2
6
 
3
7
  failproofai ships with 35+ built-in security policies. Each policy fires on a specific hook event type and tool name. Eight policies accept parameters that let you tune their behavior without writing code.
4
8
 
@@ -1,4 +1,8 @@
1
- # CLI Reference
1
+ ---
2
+ title: CLI Reference
3
+ description: "All commands, flags, and environment variables for the failproofai CLI"
4
+ icon: terminal
5
+ ---
2
6
 
3
7
  All commands are invoked via the `failproofai` binary.
4
8
 
@@ -1,4 +1,8 @@
1
- # Configuration
1
+ ---
2
+ title: Configuration
3
+ description: "Config file format, three-scope system, and merge rules"
4
+ icon: gear
5
+ ---
2
6
 
3
7
  failproofai uses JSON configuration files to control which policies are active, how they behave, and where custom hooks are loaded from.
4
8
 
@@ -1,4 +1,8 @@
1
- # Custom Hooks
1
+ ---
2
+ title: Custom Hooks
3
+ description: "Write your own policies in JavaScript with allow, deny, and instruct decisions"
4
+ icon: code
5
+ ---
2
6
 
3
7
  Custom hooks let you write your own policies in JavaScript. They integrate with the same hook event system as built-in policies and support the same `allow`, `deny`, and `instruct` decisions.
4
8
 
@@ -1,4 +1,8 @@
1
- # Dashboard
1
+ ---
2
+ title: Dashboard
3
+ description: "Session viewer, policy management, and activity log"
4
+ icon: chart-line
5
+ ---
2
6
 
3
7
  The failproofai dashboard is a local web application for browsing Claude Code sessions and managing security policies.
4
8
 
@@ -0,0 +1,83 @@
1
+ {
2
+ "$schema": "https://mintlify.com/docs.json",
3
+ "theme": "luma",
4
+ "name": "FailproofAI",
5
+ "colors": {
6
+ "primary": "#002CA7",
7
+ "light": "#e4587d",
8
+ "dark": "#002CA7"
9
+ },
10
+ "favicon": "/favicon.ico",
11
+ "navigation": {
12
+ "tabs": [
13
+ {
14
+ "tab": "Docs",
15
+ "groups": [
16
+ {
17
+ "group": "Getting Started",
18
+ "pages": [
19
+ "introduction",
20
+ "getting-started"
21
+ ]
22
+ },
23
+ {
24
+ "group": "Core Concepts",
25
+ "pages": [
26
+ "configuration",
27
+ "built-in-policies",
28
+ "custom-hooks"
29
+ ]
30
+ },
31
+ {
32
+ "group": "Tools",
33
+ "pages": [
34
+ "cli-reference",
35
+ "dashboard"
36
+ ]
37
+ },
38
+ {
39
+ "group": "Advanced",
40
+ "pages": [
41
+ "architecture",
42
+ "testing",
43
+ "package-aliases"
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ ],
49
+ "global": {
50
+ "anchors": [
51
+ {
52
+ "anchor": "GitHub",
53
+ "href": "https://github.com/exospherehost/failproofai",
54
+ "icon": "github"
55
+ },
56
+ {
57
+ "anchor": "npm",
58
+ "href": "https://www.npmjs.com/package/failproofai",
59
+ "icon": "npm"
60
+ },
61
+ {
62
+ "anchor": "Discord",
63
+ "href": "https://discord.com/invite/zT92CAgvkj",
64
+ "icon": "discord"
65
+ }
66
+ ]
67
+ }
68
+ },
69
+ "navbar": {
70
+ "links": [],
71
+ "primary": {
72
+ "type": "button",
73
+ "label": "Get started",
74
+ "href": "/getting-started"
75
+ }
76
+ },
77
+ "footer": {
78
+ "socials": {
79
+ "github": "https://github.com/exospherehost/failproofai",
80
+ "x": "https://x.com/exospherehost"
81
+ }
82
+ }
83
+ }
@@ -1,9 +1,13 @@
1
- # Getting Started
1
+ ---
2
+ title: Getting Started
3
+ description: "Install failproofai, enable policies, and take it for a spin"
4
+ icon: rocket
5
+ ---
2
6
 
3
7
  ## Requirements
4
8
 
5
9
  - **Node.js** >= 20.9.0
6
- - **Bun** >= 1.3.0 (used as the runtime and bundler)
10
+ - **Bun** >= 1.3.0 (optional only needed for development / building from source)
7
11
 
8
12
  ---
9
13
 
@@ -11,6 +15,8 @@
11
15
 
12
16
  ```bash
13
17
  npm install -g failproofai
18
+ # or
19
+ bun add -g failproofai
14
20
  ```
15
21
 
16
22
  ---
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: Introduction
3
+ description: "Open-source hooks, policies, and session visualization for Claude Code and the Agents SDK"
4
+ ---
5
+
6
+ # Failproof AI
7
+
8
+ Open-source hooks, policies, and session visualization for **Claude Code** and the **Agents SDK**. Runs entirely locally — no data leaves your machine.
9
+
10
+ ## What is Failproof AI?
11
+
12
+ Failproof AI is a security and observability toolkit that intercepts Claude Code tool calls in real time. It evaluates configurable policies — blocking dangerous commands, redacting secrets, and adding safety instructions — before Claude can act.
13
+
14
+ It also includes a local web dashboard for browsing Claude Code sessions, inspecting tool calls, and managing policies visually.
15
+
16
+ ## Key features
17
+
18
+ | Feature | Description |
19
+ |---------|-------------|
20
+ | [35+ Built-in Policies](./built-in-policies.md) | Block sudo, rm -rf, force-push, secret leaks, and more — out of the box. |
21
+ | [Custom Hooks](./custom-hooks.md) | Write your own policies in JavaScript with a simple allow/deny/instruct API. |
22
+ | [Session Dashboard](./dashboard.md) | Browse projects, inspect sessions, and review every tool call and policy decision. |
23
+ | [Three-Scope Config](./configuration.md) | Global, project, and local configuration with automatic merging. |
24
+
25
+ ## Quick start
26
+
27
+ Install globally:
28
+
29
+ ```bash
30
+ npm install -g failproofai
31
+ # or
32
+ bun add -g failproofai
33
+ ```
34
+
35
+ Enable policies:
36
+
37
+ ```bash
38
+ failproofai --install-policies
39
+ ```
40
+
41
+ Launch the dashboard:
42
+
43
+ ```bash
44
+ failproofai
45
+ ```
46
+
47
+ See the [Getting Started](./getting-started.md) guide for a full walkthrough.
@@ -1,4 +1,8 @@
1
- # Package Aliases & Typosquatting Protection
1
+ ---
2
+ title: Package Aliases
3
+ description: "Registered typosquat-prevention aliases and how they work"
4
+ icon: copy
5
+ ---
2
6
 
3
7
  ## Official package
4
8
 
@@ -6,6 +10,8 @@ The canonical npm package is **`failproofai`**:
6
10
 
7
11
  ```bash
8
12
  npm install -g failproofai
13
+ # or
14
+ bun add -g failproofai
9
15
  ```
10
16
 
11
17
  ---
@@ -22,39 +28,39 @@ To eliminate this surface, **we pre-emptively own all common misspellings and fo
22
28
 
23
29
  **Formatting variants** — different ways to write "failproof ai":
24
30
 
25
- | Package | Install command | Status |
26
- |---------|----------------|--------|
27
- | `failproof` | `npm install -g failproof` | published |
28
- | `failproof-ai` | `npm install -g failproof-ai` | pending npm support approval |
29
- | `fail-proof-ai` | `npm install -g fail-proof-ai` | pending npm support approval |
30
- | `failproof_ai` | `npm install -g failproof_ai` | pending npm support approval |
31
- | `fail_proof_ai` | `npm install -g fail_proof_ai` | pending npm support approval |
32
- | `fail-proofai` | `npm install -g fail-proofai` | pending npm support approval |
33
-
34
- > **Why pending?** npm's spam-prevention policy blocks registration of names that normalize to the same string as an existing package after stripping punctuation (e.g. `failproof-ai` → `failproofai`). We have contacted npm support to reserve these names for anti-squatting purposes. They will be activated once approved.
31
+ | Package | Status |
32
+ |---------|--------|
33
+ | `failproof` | ✅ Published |
34
+ | `failproof-ai` | ⏳ Pending npm support |
35
+ | `fail-proof-ai` | ⏳ Pending npm support |
36
+ | `failproof_ai` | ⏳ Pending npm support |
37
+ | `fail_proof_ai` | ⏳ Pending npm support |
38
+ | `fail-proofai` | ⏳ Pending npm support |
35
39
 
36
40
  **`failprof*` typos** — missing one `o` from "proof":
37
41
 
38
- | Package | Install command |
39
- |---------|----------------|
40
- | `failprof` | `npm install -g failprof` |
41
- | `failprof-ai` | `npm install -g failprof-ai` |
42
- | `failprofai` | `npm install -g failprofai` |
43
- | `fail-prof-ai` | `npm install -g fail-prof-ai` |
44
- | `failprof_ai` | `npm install -g failprof_ai` |
42
+ | Package | Status |
43
+ |---------|--------|
44
+ | `failprof` | Published |
45
+ | `failprof-ai` | Published |
46
+ | `failprofai` | Pending npm support |
47
+ | `fail-prof-ai` | Pending npm support |
48
+ | `failprof_ai` | Pending npm support |
45
49
 
46
50
  **`faliproof*` typos** — transposed `a` and `i`:
47
51
 
48
- | Package | Install command |
49
- |---------|----------------|
50
- | `faliproof` | `npm install -g faliproof` |
51
- | `faliproof-ai` | `npm install -g faliproof-ai` |
52
- | `faliproofai` | `npm install -g faliproofai` |
52
+ | Package | Status |
53
+ |---------|--------|
54
+ | `faliproof` | Published |
55
+ | `faliproof-ai` | Published |
56
+ | `faliproofai` | Pending npm support |
57
+
58
+ > **Why pending?** npm's spam-prevention policy blocks names that normalize to the same string as an existing package after stripping punctuation and running similarity checks. We have contacted npm support to reserve these names for anti-squatting purposes. They will be activated once approved.
53
59
 
54
- All 14 aliases are published by **ExosphereHost Inc.** (the same npm account as `failproofai`). You can verify any of them:
60
+ You can verify any published alias is owned by us:
55
61
 
56
62
  ```bash
57
- npm info failproof-ai
63
+ npm info failproof
58
64
  # Look for: "ExosphereHost Inc." in the maintainers field
59
65
  ```
60
66
 
@@ -65,7 +71,7 @@ npm info failproof-ai
65
71
  Each alias package:
66
72
 
67
73
  1. Lists `failproofai` as a dependency — so the real package (including its `postinstall` hook setup) runs on install
68
- 2. Exposes a binary matching its own name (e.g. `failproof-ai`) that proxies all arguments to the `failproofai` binary
74
+ 2. Exposes a binary matching its own name (e.g. `failprof-ai`) that proxies all arguments to the `failproofai` binary
69
75
 
70
76
  The proxy is a two-line Node script; there is no logic, no network calls, and no data collection beyond what `failproofai` itself does.
71
77
 
@@ -1,4 +1,8 @@
1
- # Testing
1
+ ---
2
+ title: Testing
3
+ description: "Unit tests, E2E tests, and test helpers"
4
+ icon: flask-vial
5
+ ---
2
6
 
3
7
  failproofai has two test suites: **unit tests** (fast, mocked) and **end-to-end tests** (real subprocess invocations).
4
8
 
@@ -1,7 +1,7 @@
1
1
  /**
2
- * policies-notification.js — Notification event example
2
+ * policies-notification.js — Notification and SessionEnd event examples
3
3
  *
4
- * Forwards Claude's idle notifications to Slack.
4
+ * Forwards Claude's idle notifications and session-end events to Slack.
5
5
  *
6
6
  * Prerequisites:
7
7
  * Set the SLACK_WEBHOOK_URL environment variable to your Slack incoming webhook URL.
@@ -10,8 +10,9 @@
10
10
  * failproofai --install-hooks custom ./examples/policies-notification.js
11
11
  *
12
12
  * Test by letting Claude finish a task and go idle — you should receive a Slack message.
13
+ * Test session end by exiting Claude — you should receive a session summary message.
13
14
  */
14
- import { customPolicies, allow } from "failproofai";
15
+ import { customPolicies, allow, instruct } from "failproofai";
15
16
 
16
17
  // Forward Claude idle notifications to Slack
17
18
  customPolicies.add({
@@ -22,38 +23,82 @@ customPolicies.add({
22
23
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;
23
24
  if (!webhookUrl) return allow(); // skip if not configured
24
25
 
25
- const type = String(ctx.payload?.notification_type ?? "");
26
- if (type !== "idle") return allow(); // only forward idle notifications
27
-
28
26
  const message = String(ctx.payload?.message ?? "Claude is waiting for input");
29
27
  const cwd = ctx.session?.cwd ?? "unknown";
30
28
  const sessionId = ctx.session?.sessionId ?? "unknown";
31
29
 
32
- // Fire-and-forget never block Claude if Slack is unreachable
33
- fetch(webhookUrl, {
34
- method: "POST",
35
- headers: { "Content-Type": "application/json" },
36
- body: JSON.stringify({
37
- blocks: [
38
- {
39
- type: "header",
40
- text: { type: "plain_text", text: "💬 Claude is waiting for you", emoji: true },
41
- },
42
- {
43
- type: "section",
44
- text: { type: "mrkdwn", text: message },
45
- },
46
- {
47
- type: "section",
48
- fields: [
49
- { type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
50
- { type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
51
- ],
52
- },
53
- ],
54
- }),
55
- }).catch(() => {});
56
-
57
- return allow(); // Notification hooks must always return allow
30
+ // Await so the request completes before process.exit() is called by the CLI
31
+ try {
32
+ await fetch(webhookUrl, {
33
+ method: "POST",
34
+ headers: { "Content-Type": "application/json" },
35
+ body: JSON.stringify({
36
+ blocks: [
37
+ {
38
+ type: "header",
39
+ text: { type: "plain_text", text: "💬 Claude is waiting for you", emoji: true },
40
+ },
41
+ {
42
+ type: "section",
43
+ text: { type: "mrkdwn", text: message },
44
+ },
45
+ {
46
+ type: "section",
47
+ fields: [
48
+ { type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
49
+ { type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
50
+ ],
51
+ },
52
+ ],
53
+ }),
54
+ signal: AbortSignal.timeout(5000),
55
+ });
56
+ } catch {
57
+ // Never block Claude if Slack is unreachable
58
+ }
59
+
60
+ return instruct(`We have sent the notification to the user on Slack about: ${message}`);
61
+ },
62
+ });
63
+
64
+ // Notify Slack when a Claude session ends
65
+ customPolicies.add({
66
+ name: "slack-on-session-end",
67
+ description: "Notify Slack when a Claude session ends (set SLACK_WEBHOOK_URL env var)",
68
+ match: { events: ["SessionEnd"] },
69
+ fn: async (ctx) => {
70
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL;
71
+ if (!webhookUrl) return allow(); // skip if not configured
72
+
73
+ const cwd = ctx.session?.cwd ?? "unknown";
74
+ const sessionId = ctx.session?.sessionId ?? "unknown";
75
+
76
+ // Await so the request completes before process.exit() is called by the CLI
77
+ try {
78
+ await fetch(webhookUrl, {
79
+ method: "POST",
80
+ headers: { "Content-Type": "application/json" },
81
+ body: JSON.stringify({
82
+ blocks: [
83
+ {
84
+ type: "header",
85
+ text: { type: "plain_text", text: "✅ Claude session ended", emoji: true },
86
+ },
87
+ {
88
+ type: "section",
89
+ fields: [
90
+ { type: "mrkdwn", text: `*Project*\n\`${cwd}\`` },
91
+ { type: "mrkdwn", text: `*Session*\n\`${sessionId}\`` },
92
+ ],
93
+ },
94
+ ],
95
+ }),
96
+ signal: AbortSignal.timeout(5000),
97
+ });
98
+ } catch {
99
+ // Never block Claude if Slack is unreachable
100
+ }
101
+
102
+ return instruct(`We have sent the notification to the user on Slack about: Claude session ended (project: ${cwd}, session: ${sessionId})`);
58
103
  },
59
104
  });
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.1-beta.9",
3
+ "version": "0.0.2-beta.1",
4
4
  "description": "Open-source hooks, policies, and project visualization for Claude Code & Agents SDK",
5
5
  "bin": {
6
- "failproofai": "./bin/failproofai.mjs"
6
+ "failproofai": "./dist/cli.mjs"
7
7
  },
8
8
  "files": [
9
9
  "bin/",
@@ -11,6 +11,7 @@
11
11
  "scripts/",
12
12
  "lib/",
13
13
  ".next/standalone/",
14
+ "dist/",
14
15
  "README.md"
15
16
  ],
16
17
  "engines": {
@@ -18,10 +19,11 @@
18
19
  "bun": ">=1.3.0"
19
20
  },
20
21
  "scripts": {
21
- "predev": "bun link",
22
+ "predev": "bun run build:cli && bun link",
22
23
  "dev": "FAILPROOFAI_TELEMETRY_DISABLED=1 bun scripts/dev.ts --port 8020",
23
- "build": "bun --bun next build && node -e \"const {cpSync}=require('fs');cpSync('.next/static','.next/standalone/.next/static',{recursive:true});\"",
24
- "prestart": "bun link",
24
+ "build:cli": "bun build --target=node --format=esm --outfile=dist/cli.mjs bin/failproofai.mjs --external posthog-node && node -e \"const fs=require('fs');const c=fs.readFileSync('dist/cli.mjs','utf8');fs.writeFileSync('dist/cli.mjs',c.replace('#!/usr/bin/env bun','#!/usr/bin/env node').replace('// @bun\\n',''))\"",
25
+ "build": "bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts && bun run build:cli && bun --bun next build && node -e \"const {cpSync}=require('fs');cpSync('.next/static','.next/standalone/.next/static',{recursive:true});\"",
26
+ "prestart": "bun run build:cli && bun link",
25
27
  "start": "FAILPROOFAI_TELEMETRY_DISABLED=1 bun scripts/start.ts",
26
28
  "test": "vitest",
27
29
  "test:run": "vitest run",
@@ -49,7 +49,7 @@ export function launch(mode: "dev" | "start"): void {
49
49
  const serverJsPath = resolve(packageRoot, ".next/standalone/server.js");
50
50
  if (!existsSync(serverJsPath)) {
51
51
  console.error(
52
- `\nError: Cannot find server binary at:\n ${serverJsPath}\n\n` +
52
+ `\nError: Cannot find server.js at:\n ${serverJsPath}\n\n` +
53
53
  `The package may be missing its build output.\n` +
54
54
  `Try reinstalling:\n npm install -g failproofai@latest\n`
55
55
  );
@@ -18,6 +18,17 @@ import { trackInstallEvent } from "./install-telemetry.mjs";
18
18
  // from process.cwd() only when we are being installed as a dependency by someone else.
19
19
  if (!process.env.INIT_CWD || process.env.INIT_CWD === process.cwd()) process.exit(0);
20
20
 
21
+ // Verify server.js exists — fail the install early if the dashboard build is missing.
22
+ const serverJsPath = resolve(process.cwd(), ".next", "standalone", "server.js");
23
+ if (!existsSync(serverJsPath)) {
24
+ console.error(
25
+ `\n[failproofai] Error: server.js not found at:\n ${serverJsPath}\n\n` +
26
+ ` The package may not have been built correctly.\n` +
27
+ ` Try reinstalling: npm install -g failproofai@latest\n`
28
+ );
29
+ process.exit(1);
30
+ }
31
+
21
32
  const FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
22
33
  const NAMESPACE = "failproofai-telemetry-v1";
23
34
 
@@ -0,0 +1,60 @@
1
+ You are an automated agent running in GitHub Actions to keep failproofai's hook
2
+ event types in sync with the official Claude Code documentation.
3
+
4
+ ## Your task
5
+
6
+ 1. Fetch the Claude Code hooks reference pages using WebFetch:
7
+ - https://code.claude.com/docs/en/hooks (full reference — has the complete event table)
8
+ - https://code.claude.com/docs/en/hooks-guide (guide — also has a summary event table)
9
+ Extract the complete list of all hook event type names (e.g. SessionStart,
10
+ PreToolUse, PostToolUse, etc.) from the event lifecycle/trigger table.
11
+ Use both pages; union the results if they differ. Prefer the reference page.
12
+
13
+ 2. Read `src/hooks/types.ts` and extract the current `HOOK_EVENT_TYPES` array
14
+ (the TypeScript `as const` array of strings).
15
+
16
+ 3. Diff the two lists:
17
+ - **added**: event types in the docs but NOT in our array
18
+ - **removed**: event types in our array but NOT in the docs
19
+
20
+ 4. If there are NO differences:
21
+ Write the following JSON to `.sync-hook-events-output.json` in the repo root:
22
+ ```json
23
+ { "changed": false }
24
+ ```
25
+ Then stop.
26
+
27
+ 5. If there are differences:
28
+
29
+ a. Update `HOOK_EVENT_TYPES` in `src/hooks/types.ts`:
30
+ - Add new event types (append after the last existing entry, before `] as const`)
31
+ - Remove stale event types if any
32
+
33
+ b. Update `__tests__/hooks/manager.test.ts` — find the hardcoded event-type
34
+ counts and update them to the new total:
35
+ - The test description string matching `all N event types`
36
+ - The `toHaveLength(N)` assertion(s) that check `Object.keys(written.hooks)`
37
+ Search by the current count number to locate them.
38
+
39
+ c. Write the following JSON to `.sync-hook-events-output.json` in the repo root:
40
+ ```json
41
+ {
42
+ "changed": true,
43
+ "added": ["EventA", "EventB"],
44
+ "removed": ["EventC"],
45
+ "prTitle": "[auto] sync hook event types with Claude Code docs",
46
+ "prBody": "..."
47
+ }
48
+ ```
49
+ The `prBody` must be a Markdown string containing:
50
+ - List of **added** event types (or "none")
51
+ - List of **removed** event types (or "none")
52
+ - Source URLs used
53
+ - A note: "CI must pass and this PR must be reviewed before merging."
54
+
55
+ ## Constraints
56
+
57
+ - **Only edit `src/hooks/types.ts`, `__tests__/hooks/manager.test.ts`, and
58
+ `.sync-hook-events-output.json`**. No other files.
59
+ - Do NOT run any shell commands (no git, no gh, no bun).
60
+ - Do NOT modify `policy-evaluator.ts`, `manager.ts`, or any other source file.