failproofai 0.0.8 → 0.0.9-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/.codex/hooks.json +77 -0
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- 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/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/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/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js +1 -1
- 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/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +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/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 +1 -1
- 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/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 +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]__0m72uj7._.js → [root-of-the-server]__03rd.z8._.js} +2 -2
- 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]__0h3orxc._.js → [root-of-the-server]__0ca1zru._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e74wa-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ea22pr._.js +3 -0
- 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]__0okos0k._.js → [root-of-the-server]__0vu.o-3._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +7 -7
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0of~riu._.js → [root-of-the-server]__0zqcovi._.js} +2 -2
- 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/_07a1g.3._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0uy6m~m._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0zaq1hm._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_11rg2a_._.js +3 -0
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js +1 -1
- 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 +3 -3
- 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/0.rk1iwdt1d7c.css +1 -0
- package/.next/standalone/.next/static/chunks/00b5h4r1el.6f.js +1 -0
- package/.next/standalone/.next/static/chunks/{151bdxm9n-pry.js → 03lsndql_yml5.js} +1 -1
- package/.next/standalone/.next/static/chunks/0amfi~vb_gfgo.js +1 -0
- package/.next/standalone/.next/static/chunks/0fw2h.g66c0h3.js +1 -0
- package/.next/standalone/.next/static/chunks/{0mbc8hyeqe2c4.js → 0jce49ygr4fdv.js} +1 -1
- package/.next/standalone/.next/static/chunks/0mungg3~jpwe7.js +1 -0
- package/.next/standalone/.next/static/chunks/{175-vim0.ztb2.js → 0uq_5p-p7myfe.js} +2 -2
- package/.next/standalone/.next/static/chunks/0v.xuf4ynzp~~.js +6 -0
- package/.next/standalone/.next/static/chunks/{0eowehbf5egcz.js → 0vb8xxj_v2tz8.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0vlk_pv4somht.js → 0vwqucikost_q.js} +1 -1
- package/.next/standalone/.next/static/chunks/0~mroziiwl1m5.js +1 -0
- package/.next/standalone/app/actions/install-hooks-web.ts +21 -5
- package/.next/standalone/app/policies/hooks-client.tsx +23 -0
- package/.next/standalone/assets/logos/claude.svg +1 -0
- package/.next/standalone/assets/logos/openai-dark.svg +1 -0
- package/.next/standalone/assets/logos/openai-light.svg +1 -0
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/server.js +1 -1
- package/README.md +22 -3
- package/bin/failproofai.mjs +89 -9
- package/dist/cli.mjs +1039 -281
- package/package.json +2 -2
- package/src/hooks/builtin-policies.ts +29 -6
- package/src/hooks/handler.ts +39 -10
- package/src/hooks/hook-activity-store.ts +2 -0
- package/src/hooks/install-prompt.ts +69 -0
- package/src/hooks/integrations.ts +373 -0
- package/src/hooks/manager.ts +96 -171
- package/src/hooks/policy-evaluator.ts +28 -1
- package/src/hooks/policy-types.ts +3 -1
- package/src/hooks/resolve-permission-mode.ts +147 -0
- package/src/hooks/types.ts +30 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_rr1ty._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dj-tbi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0h21oar._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0i~.gk_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0q3h.2s._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0x..fj-._.js +0 -3
- package/.next/standalone/.next/static/chunks/096~b1zwv69ph.js +0 -1
- package/.next/standalone/.next/static/chunks/0bkir2pd22ski.js +0 -1
- package/.next/standalone/.next/static/chunks/0ksdlt_1hucdm.js +0 -1
- package/.next/standalone/.next/static/chunks/0lua3p__elu_..js +0 -6
- package/.next/standalone/.next/static/chunks/0mir9jdxn35~s.css +0 -1
- package/.next/standalone/.next/static/chunks/0s_18.dox44e9.js +0 -1
- package/.next/standalone/.next/static/chunks/0t3euwspxi_zg.js +0 -1
- /package/.next/standalone/.next/static/{RYld7TSCDXm2_WhJq20rD → oUO8u4z9JvtTzS_2RJoGo}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{RYld7TSCDXm2_WhJq20rD → oUO8u4z9JvtTzS_2RJoGo}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{RYld7TSCDXm2_WhJq20rD → oUO8u4z9JvtTzS_2RJoGo}/_ssgManifest.js +0 -0
package/src/hooks/manager.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Install/remove/list failproofai hooks
|
|
2
|
+
* Install/remove/list failproofai hooks for one or more agent CLIs.
|
|
3
|
+
*
|
|
4
|
+
* Per-CLI path resolution and settings I/O live in `./integrations` (one
|
|
5
|
+
* `Integration` impl per CLI). This module orchestrates: validation, policy
|
|
6
|
+
* selection, telemetry, multi-scope warnings, and console output.
|
|
3
7
|
*/
|
|
4
8
|
import { execSync } from "node:child_process";
|
|
5
|
-
import {
|
|
6
|
-
import { resolve,
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { resolve, basename } from "node:path";
|
|
7
11
|
import { homedir, platform, arch, release, hostname } from "node:os";
|
|
8
12
|
import {
|
|
9
|
-
HOOK_EVENT_TYPES,
|
|
10
13
|
HOOK_SCOPES,
|
|
11
|
-
FAILPROOFAI_HOOK_MARKER,
|
|
12
14
|
type HookScope,
|
|
13
|
-
type
|
|
14
|
-
type ClaudeHookMatcher,
|
|
15
|
-
type ClaudeSettings,
|
|
15
|
+
type IntegrationType,
|
|
16
16
|
} from "./types";
|
|
17
|
+
import { claudeCode, getIntegration } from "./integrations";
|
|
17
18
|
import { promptPolicySelection } from "./install-prompt";
|
|
18
19
|
import { readMergedHooksConfig, readScopedHooksConfig, writeScopedHooksConfig } from "./hooks-config";
|
|
19
20
|
import type { HooksConfig } from "./policy-types";
|
|
@@ -25,16 +26,9 @@ import { CliError } from "../cli-error";
|
|
|
25
26
|
|
|
26
27
|
const VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
|
|
27
28
|
|
|
29
|
+
/** Settings path for the Claude Code integration. Kept as a public export for `app/actions/get-hooks-config.ts`. */
|
|
28
30
|
export function getSettingsPath(scope: HookScope, cwd?: string): string {
|
|
29
|
-
|
|
30
|
-
switch (scope) {
|
|
31
|
-
case "user":
|
|
32
|
-
return resolve(homedir(), ".claude", "settings.json");
|
|
33
|
-
case "project":
|
|
34
|
-
return resolve(base, ".claude", "settings.json");
|
|
35
|
-
case "local":
|
|
36
|
-
return resolve(base, ".claude", "settings.local.json");
|
|
37
|
-
}
|
|
31
|
+
return claudeCode.getSettingsPath(scope, cwd);
|
|
38
32
|
}
|
|
39
33
|
|
|
40
34
|
function scopeLabel(scope: HookScope): string {
|
|
@@ -48,20 +42,11 @@ function scopeLabel(scope: HookScope): string {
|
|
|
48
42
|
}
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
function readSettings(settingsPath: string): ClaudeSettings {
|
|
52
|
-
if (!existsSync(settingsPath)) {
|
|
53
|
-
return {};
|
|
54
|
-
}
|
|
55
|
-
const raw = readFileSync(settingsPath, "utf8");
|
|
56
|
-
return JSON.parse(raw) as ClaudeSettings;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function writeSettings(settingsPath: string, settings: ClaudeSettings): void {
|
|
60
|
-
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
61
|
-
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
45
|
function resolveFailproofaiBinary(): string {
|
|
46
|
+
// Test/CI override: lets E2E tests point at the in-tree bin/failproofai.mjs
|
|
47
|
+
// without requiring `npm install -g` or `bun link`.
|
|
48
|
+
const override = process.env.FAILPROOFAI_BINARY_OVERRIDE;
|
|
49
|
+
if (override && override.trim()) return override.trim();
|
|
65
50
|
try {
|
|
66
51
|
const cmd = process.platform === "win32" ? "where failproofai" : "which failproofai";
|
|
67
52
|
const result = execSync(cmd, { encoding: "utf8" }).trim();
|
|
@@ -75,13 +60,6 @@ function resolveFailproofaiBinary(): string {
|
|
|
75
60
|
}
|
|
76
61
|
}
|
|
77
62
|
|
|
78
|
-
function isFailproofaiHook(hook: Record<string, unknown>): boolean {
|
|
79
|
-
if (hook[FAILPROOFAI_HOOK_MARKER] === true) return true;
|
|
80
|
-
// Fallback for legacy installs that predate the marker
|
|
81
|
-
const cmd = typeof hook.command === "string" ? hook.command : "";
|
|
82
|
-
return cmd.includes("failproofai") && cmd.includes("--hook");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
63
|
function validatePolicyNames(names: string[]): void {
|
|
86
64
|
const invalid = names.filter((n) => !VALID_POLICY_NAMES.has(n));
|
|
87
65
|
if (invalid.length > 0) {
|
|
@@ -105,67 +83,7 @@ function deduplicateScopes(scopes: readonly HookScope[], cwd?: string): HookScop
|
|
|
105
83
|
}
|
|
106
84
|
|
|
107
85
|
export function hooksInstalledInSettings(scope: HookScope, cwd?: string): boolean {
|
|
108
|
-
|
|
109
|
-
if (!existsSync(settingsPath)) return false;
|
|
110
|
-
try {
|
|
111
|
-
const settings = readSettings(settingsPath);
|
|
112
|
-
if (!settings.hooks) return false;
|
|
113
|
-
for (const matchers of Object.values(settings.hooks)) {
|
|
114
|
-
if (!Array.isArray(matchers)) continue;
|
|
115
|
-
for (const matcher of matchers) {
|
|
116
|
-
if (!matcher.hooks) continue;
|
|
117
|
-
if (matcher.hooks.some((h) => isFailproofaiHook(h as Record<string, unknown>))) {
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
} catch {
|
|
123
|
-
// Corrupted settings — treat as not installed
|
|
124
|
-
}
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
function removeHooksFromSettingsFile(settingsPath: string): number {
|
|
130
|
-
const settings = readSettings(settingsPath);
|
|
131
|
-
|
|
132
|
-
if (!settings.hooks) return 0;
|
|
133
|
-
|
|
134
|
-
let removed = 0;
|
|
135
|
-
|
|
136
|
-
for (const eventType of Object.keys(settings.hooks)) {
|
|
137
|
-
const matchers = settings.hooks[eventType];
|
|
138
|
-
if (!Array.isArray(matchers)) continue;
|
|
139
|
-
|
|
140
|
-
for (let i = matchers.length - 1; i >= 0; i--) {
|
|
141
|
-
const matcher = matchers[i];
|
|
142
|
-
if (!matcher.hooks) continue;
|
|
143
|
-
|
|
144
|
-
const before = matcher.hooks.length;
|
|
145
|
-
matcher.hooks = matcher.hooks.filter(
|
|
146
|
-
(h) => !isFailproofaiHook(h as Record<string, unknown>)
|
|
147
|
-
);
|
|
148
|
-
removed += before - matcher.hooks.length;
|
|
149
|
-
|
|
150
|
-
// Remove empty matchers
|
|
151
|
-
if (matcher.hooks.length === 0) {
|
|
152
|
-
matchers.splice(i, 1);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Remove empty event type arrays
|
|
157
|
-
if (matchers.length === 0) {
|
|
158
|
-
delete settings.hooks[eventType];
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Remove empty hooks object
|
|
163
|
-
if (Object.keys(settings.hooks).length === 0) {
|
|
164
|
-
delete settings.hooks;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
writeSettings(settingsPath, settings);
|
|
168
|
-
return removed;
|
|
86
|
+
return claudeCode.hooksInstalledInSettings(scope, cwd);
|
|
169
87
|
}
|
|
170
88
|
|
|
171
89
|
/**
|
|
@@ -185,6 +103,7 @@ export async function installHooks(
|
|
|
185
103
|
source?: string,
|
|
186
104
|
customPoliciesPath?: string,
|
|
187
105
|
removeCustomHooks = false,
|
|
106
|
+
cli?: IntegrationType[],
|
|
188
107
|
): Promise<void> {
|
|
189
108
|
// Validate user input first before any system checks
|
|
190
109
|
if (policyNames !== undefined && policyNames.length > 0) {
|
|
@@ -200,6 +119,21 @@ export async function installHooks(
|
|
|
200
119
|
}
|
|
201
120
|
}
|
|
202
121
|
|
|
122
|
+
// Back-compat default: ["claude"]. Callers (bin/failproofai.mjs) prompt
|
|
123
|
+
// the user for multi-CLI selection before reaching here when --cli is omitted.
|
|
124
|
+
const selectedClis: IntegrationType[] = cli && cli.length > 0 ? [...new Set(cli)] : ["claude"];
|
|
125
|
+
|
|
126
|
+
// Per-CLI scope validation: Codex doesn't have a "local" scope.
|
|
127
|
+
for (const cliId of selectedClis) {
|
|
128
|
+
const integration = getIntegration(cliId);
|
|
129
|
+
if (!integration.scopes.includes(scope)) {
|
|
130
|
+
throw new CliError(
|
|
131
|
+
`Scope "${scope}" is not supported by ${integration.displayName}. ` +
|
|
132
|
+
`Valid scopes: ${integration.scopes.join(", ")}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
203
137
|
const binaryPath = resolveFailproofaiBinary();
|
|
204
138
|
|
|
205
139
|
// Capture existing config before overwriting (used for telemetry diff)
|
|
@@ -259,52 +193,17 @@ export async function installHooks(
|
|
|
259
193
|
console.log(`Custom hooks path: ${configToWrite.customPoliciesPath}`);
|
|
260
194
|
}
|
|
261
195
|
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
196
|
+
// Write hooks for each selected CLI
|
|
197
|
+
const writtenSettingsPaths: { cli: IntegrationType; path: string }[] = [];
|
|
198
|
+
for (const cliId of selectedClis) {
|
|
199
|
+
const integration = getIntegration(cliId);
|
|
200
|
+
const settingsPath = integration.getSettingsPath(scope, cwd);
|
|
201
|
+
const settings = integration.readSettings(settingsPath);
|
|
202
|
+
integration.writeHookEntries(settings, binaryPath, scope);
|
|
203
|
+
integration.writeSettings(settingsPath, settings);
|
|
204
|
+
writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
|
|
267
205
|
}
|
|
268
206
|
|
|
269
|
-
for (const eventType of HOOK_EVENT_TYPES) {
|
|
270
|
-
const command = scope === "project"
|
|
271
|
-
? `npx -y failproofai --hook ${eventType}`
|
|
272
|
-
: `"${binaryPath}" --hook ${eventType}`;
|
|
273
|
-
const hookEntry: ClaudeHookEntry = {
|
|
274
|
-
type: "command",
|
|
275
|
-
command,
|
|
276
|
-
timeout: 60_000,
|
|
277
|
-
[FAILPROOFAI_HOOK_MARKER]: true,
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
if (!settings.hooks[eventType]) {
|
|
281
|
-
settings.hooks[eventType] = [];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const matchers: ClaudeHookMatcher[] = settings.hooks[eventType];
|
|
285
|
-
|
|
286
|
-
// Find existing failproofai matcher
|
|
287
|
-
let found = false;
|
|
288
|
-
for (const matcher of matchers) {
|
|
289
|
-
if (!matcher.hooks) continue;
|
|
290
|
-
const failproofaiIdx = matcher.hooks.findIndex((h: ClaudeHookEntry | Record<string, unknown>) =>
|
|
291
|
-
isFailproofaiHook(h as Record<string, unknown>)
|
|
292
|
-
);
|
|
293
|
-
if (failproofaiIdx >= 0) {
|
|
294
|
-
matcher.hooks[failproofaiIdx] = hookEntry;
|
|
295
|
-
found = true;
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (!found) {
|
|
301
|
-
// Append a new matcher with the failproofai hook
|
|
302
|
-
matchers.push({ hooks: [hookEntry] });
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
writeSettings(settingsPath, settings);
|
|
307
|
-
|
|
308
207
|
// Telemetry: track successful hook installation (with diff vs previous config)
|
|
309
208
|
try {
|
|
310
209
|
const newSet = new Set(selectedPolicies);
|
|
@@ -313,6 +212,8 @@ export async function installHooks(
|
|
|
313
212
|
const distinctId = getInstanceId();
|
|
314
213
|
await trackHookEvent(distinctId, "hooks_installed", {
|
|
315
214
|
scope,
|
|
215
|
+
cli: selectedClis,
|
|
216
|
+
cli_count: selectedClis.length,
|
|
316
217
|
policies: selectedPolicies,
|
|
317
218
|
policy_count: selectedPolicies.length,
|
|
318
219
|
policies_added: policiesAdded,
|
|
@@ -331,8 +232,14 @@ export async function installHooks(
|
|
|
331
232
|
// Telemetry is best-effort — never block the operation
|
|
332
233
|
}
|
|
333
234
|
|
|
334
|
-
|
|
335
|
-
|
|
235
|
+
for (const { cli: cliId, path } of writtenSettingsPaths) {
|
|
236
|
+
const integration = getIntegration(cliId);
|
|
237
|
+
console.log(
|
|
238
|
+
`Failproof AI hooks installed for ${integration.displayName} ` +
|
|
239
|
+
`(${integration.eventTypes.length} event types, scope: ${scope}).`
|
|
240
|
+
);
|
|
241
|
+
console.log(`Settings: ${path}`);
|
|
242
|
+
}
|
|
336
243
|
if (scope === "project") {
|
|
337
244
|
console.log(`Command: npx -y failproofai`);
|
|
338
245
|
console.log(`\nThis file can be committed to git — no machine-specific paths.`);
|
|
@@ -340,7 +247,7 @@ export async function installHooks(
|
|
|
340
247
|
console.log(`Binary: ${binaryPath}`);
|
|
341
248
|
}
|
|
342
249
|
|
|
343
|
-
// Warn about duplicate-scope installations
|
|
250
|
+
// Warn about duplicate-scope installations (Claude Code only — uses HOOK_SCOPES)
|
|
344
251
|
const otherScopes = deduplicateScopes(HOOK_SCOPES, cwd).filter((s) => s !== scope);
|
|
345
252
|
const duplicates = otherScopes.filter((s) => hooksInstalledInSettings(s, cwd));
|
|
346
253
|
if (duplicates.length > 0) {
|
|
@@ -362,9 +269,13 @@ export async function installHooks(
|
|
|
362
269
|
* @param scope — settings scope to remove from (default: "user"), or "all" to remove from all scopes
|
|
363
270
|
* @param opts.betaOnly — set to true when removing only beta policies (adds beta_only flag to telemetry)
|
|
364
271
|
*/
|
|
365
|
-
export async function removeHooks(policyNames?: string[], scope: HookScope | "all" = "user", cwd?: string, opts?: { betaOnly?: boolean; source?: string; removeCustomHooks?: boolean }): Promise<void> {
|
|
272
|
+
export async function removeHooks(policyNames?: string[], scope: HookScope | "all" = "user", cwd?: string, opts?: { betaOnly?: boolean; source?: string; removeCustomHooks?: boolean; cli?: IntegrationType[] }): Promise<void> {
|
|
366
273
|
// Resolve the effective config scope ("all" falls back to "user" for config reads/writes)
|
|
367
274
|
const configScope: HookScope = scope === "all" ? "user" : scope;
|
|
275
|
+
// Back-compat default: ["claude"]. The bin layer prompts for CLI selection
|
|
276
|
+
// when --cli is omitted and an interactive TTY is attached.
|
|
277
|
+
const selectedClis: IntegrationType[] =
|
|
278
|
+
opts?.cli && opts.cli.length > 0 ? [...new Set(opts.cli)] : ["claude"];
|
|
368
279
|
|
|
369
280
|
// Clear custom hooks path if requested
|
|
370
281
|
if (opts?.removeCustomHooks) {
|
|
@@ -401,6 +312,7 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
401
312
|
const actuallyRemoved = policyNames.filter((p) => config.enabledPolicies.includes(p));
|
|
402
313
|
await trackHookEvent(distinctId, "hooks_removed", {
|
|
403
314
|
scope,
|
|
315
|
+
cli: selectedClis,
|
|
404
316
|
removal_mode: opts?.betaOnly ? "beta_policies" : "policies",
|
|
405
317
|
beta_only: opts?.betaOnly ?? false,
|
|
406
318
|
policies_removed: actuallyRemoved,
|
|
@@ -423,44 +335,56 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
423
335
|
// Capture enabled policies before clearing (used for accurate telemetry below)
|
|
424
336
|
const configBeforeRemoval = readScopedHooksConfig(configScope, cwd);
|
|
425
337
|
|
|
426
|
-
// Remove
|
|
427
|
-
const scopesToRemove: HookScope[] = scope === "all" ? [...HOOK_SCOPES] : [scope];
|
|
338
|
+
// Remove failproofai hooks from each selected CLI's settings file(s)
|
|
428
339
|
let totalRemoved = 0;
|
|
340
|
+
let nothingToReport = false;
|
|
341
|
+
|
|
342
|
+
for (const cliId of selectedClis) {
|
|
343
|
+
const integration = getIntegration(cliId);
|
|
344
|
+
// For "all" scope, iterate over the integration's scopes; otherwise, only
|
|
345
|
+
// touch the single scope (skipping CLIs that don't support it).
|
|
346
|
+
const scopesToRemove: HookScope[] =
|
|
347
|
+
scope === "all"
|
|
348
|
+
? [...integration.scopes]
|
|
349
|
+
: integration.scopes.includes(scope)
|
|
350
|
+
? [scope]
|
|
351
|
+
: [];
|
|
429
352
|
|
|
430
|
-
|
|
431
|
-
|
|
353
|
+
for (const s of scopesToRemove) {
|
|
354
|
+
const settingsPath = integration.getSettingsPath(s, cwd);
|
|
432
355
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
356
|
+
if (!existsSync(settingsPath)) {
|
|
357
|
+
if (scope !== "all" && selectedClis.length === 1) {
|
|
358
|
+
console.log("No settings file found. Nothing to remove.");
|
|
359
|
+
nothingToReport = true;
|
|
360
|
+
}
|
|
361
|
+
continue;
|
|
437
362
|
}
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
440
363
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (!settings.hooks) {
|
|
444
|
-
if (scope !== "all") {
|
|
364
|
+
const removed = integration.removeHooksFromFile(settingsPath);
|
|
365
|
+
if (removed === 0 && scope !== "all" && selectedClis.length === 1) {
|
|
445
366
|
console.log("No hooks found in settings. Nothing to remove.");
|
|
446
|
-
|
|
367
|
+
nothingToReport = true;
|
|
368
|
+
continue;
|
|
447
369
|
}
|
|
448
|
-
|
|
449
|
-
}
|
|
370
|
+
totalRemoved += removed;
|
|
450
371
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
console.log(`Removed ${removed} failproofai hook(s) from settings.`);
|
|
456
|
-
console.log(`Settings: ${settingsPath}`);
|
|
372
|
+
if (scope !== "all") {
|
|
373
|
+
console.log(`Removed ${removed} failproofai hook(s) from ${integration.displayName} settings.`);
|
|
374
|
+
console.log(`Settings: ${settingsPath}`);
|
|
375
|
+
}
|
|
457
376
|
}
|
|
458
377
|
}
|
|
459
378
|
|
|
379
|
+
if (nothingToReport && totalRemoved === 0) return;
|
|
380
|
+
|
|
460
381
|
if (scope === "all") {
|
|
461
382
|
console.log(`Removed ${totalRemoved} failproofai hook(s) from all scopes.`);
|
|
462
|
-
for (const
|
|
463
|
-
|
|
383
|
+
for (const cliId of selectedClis) {
|
|
384
|
+
const integration = getIntegration(cliId);
|
|
385
|
+
for (const s of integration.scopes) {
|
|
386
|
+
console.log(` ${integration.displayName} / ${s}: ${integration.getSettingsPath(s, cwd)}`);
|
|
387
|
+
}
|
|
464
388
|
}
|
|
465
389
|
}
|
|
466
390
|
|
|
@@ -469,6 +393,7 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
469
393
|
const distinctId = getInstanceId();
|
|
470
394
|
await trackHookEvent(distinctId, "hooks_removed", {
|
|
471
395
|
scope,
|
|
396
|
+
cli: selectedClis,
|
|
472
397
|
removal_mode: "hooks",
|
|
473
398
|
policies_removed: configBeforeRemoval.enabledPolicies,
|
|
474
399
|
removed_count: totalRemoved,
|
|
@@ -76,6 +76,7 @@ export async function evaluatePolicies(
|
|
|
76
76
|
toolName,
|
|
77
77
|
toolInput,
|
|
78
78
|
session,
|
|
79
|
+
cli: session?.cli,
|
|
79
80
|
};
|
|
80
81
|
|
|
81
82
|
// Track all instruct results (accumulated, does not short-circuit)
|
|
@@ -137,6 +138,28 @@ export async function evaluatePolicies(
|
|
|
137
138
|
};
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
if (eventType === "PermissionRequest") {
|
|
142
|
+
// Codex-only: hookSpecificOutput.decision.behavior = "allow" | "deny"
|
|
143
|
+
// (per https://developers.openai.com/codex/hooks#permissionrequest).
|
|
144
|
+
const response = {
|
|
145
|
+
hookSpecificOutput: {
|
|
146
|
+
hookEventName: eventType,
|
|
147
|
+
decision: {
|
|
148
|
+
behavior: "deny",
|
|
149
|
+
message: `Blocked ${displayTool} by failproofai because: ${reason}, as per the policy configured by the user`,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
exitCode: 0,
|
|
155
|
+
stdout: JSON.stringify(response),
|
|
156
|
+
stderr: "",
|
|
157
|
+
policyName: policy.name,
|
|
158
|
+
reason,
|
|
159
|
+
decision: "deny",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
140
163
|
if (eventType === "PostToolUse") {
|
|
141
164
|
const response = {
|
|
142
165
|
hookSpecificOutput: {
|
|
@@ -235,7 +258,11 @@ export async function evaluatePolicies(
|
|
|
235
258
|
if (allowEntries.length > 0) {
|
|
236
259
|
const combined = allowEntries.map((e) => e.reason).join("\n");
|
|
237
260
|
const policyNames = allowEntries.map((e) => e.policyName);
|
|
238
|
-
const supportsHookSpecificOutput =
|
|
261
|
+
const supportsHookSpecificOutput =
|
|
262
|
+
eventType === "PreToolUse" ||
|
|
263
|
+
eventType === "PostToolUse" ||
|
|
264
|
+
eventType === "UserPromptSubmit" ||
|
|
265
|
+
eventType === "PermissionRequest";
|
|
239
266
|
const response = supportsHookSpecificOutput
|
|
240
267
|
? { hookSpecificOutput: { hookEventName: eventType, additionalContext: `Note from failproofai: ${combined}` } }
|
|
241
268
|
: { reason: combined };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for the hook policy system.
|
|
3
3
|
*/
|
|
4
|
-
import type { HookEventType, SessionMetadata } from "./types";
|
|
4
|
+
import type { HookEventType, IntegrationType, SessionMetadata } from "./types";
|
|
5
5
|
|
|
6
6
|
export type PolicyDecision = "allow" | "deny" | "instruct";
|
|
7
7
|
|
|
@@ -12,6 +12,8 @@ export interface PolicyContext {
|
|
|
12
12
|
toolInput?: Record<string, unknown>;
|
|
13
13
|
session?: SessionMetadata;
|
|
14
14
|
params?: Record<string, unknown>;
|
|
15
|
+
/** Which agent CLI fired this hook. Mirrors session.cli; exposed at the top level for ergonomics. */
|
|
16
|
+
cli?: IntegrationType;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export interface PolicyResult {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-CLI permission mode resolver.
|
|
3
|
+
*
|
|
4
|
+
* • Claude Code: reads `permission_mode` directly from the hook stdin payload.
|
|
5
|
+
* Possible values per `claude --help`: acceptEdits, auto, bypassPermissions,
|
|
6
|
+
* default, dontAsk, plan.
|
|
7
|
+
*
|
|
8
|
+
* • Codex: stdin doesn't carry the permission mode. We walk
|
|
9
|
+
* ~/.codex/sessions/<YYYY>/<MM>/<DD>/<file containing sessionId>.jsonl
|
|
10
|
+
* looking for a `turn_context` record whose payload has `approval_policy`,
|
|
11
|
+
* and map: never → full-auto, on-request → default. Other values pass
|
|
12
|
+
* through. If the transcript can't be read, falls back to "default".
|
|
13
|
+
*
|
|
14
|
+
* Hot-path note: handleHookEvent calls this for every Codex tool use. To
|
|
15
|
+
* avoid an O(history-size) tree scan, we (1) try today + yesterday's date
|
|
16
|
+
* directories first (transcripts for an active session live there in the
|
|
17
|
+
* common case), (2) cache the resolved transcript path to disk keyed by
|
|
18
|
+
* sessionId so subsequent hooks in the same session skip the walk entirely.
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync } from "node:fs";
|
|
21
|
+
import { dirname, join } from "node:path";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
import type { IntegrationType } from "./types";
|
|
24
|
+
|
|
25
|
+
export function resolvePermissionMode(
|
|
26
|
+
integration: IntegrationType,
|
|
27
|
+
parsed: Record<string, unknown>,
|
|
28
|
+
sessionId: string | undefined,
|
|
29
|
+
): string {
|
|
30
|
+
if (integration === "claude") {
|
|
31
|
+
return (parsed.permission_mode as string | undefined) ?? "default";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (integration === "codex" && sessionId) {
|
|
35
|
+
return resolveCodexMode(sessionId) ?? "default";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return "default";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const CACHE_PATH = join(homedir(), ".failproofai", "cache", "codex-session-paths.json");
|
|
42
|
+
|
|
43
|
+
function readCache(): Record<string, string> {
|
|
44
|
+
try {
|
|
45
|
+
if (!existsSync(CACHE_PATH)) return {};
|
|
46
|
+
return JSON.parse(readFileSync(CACHE_PATH, "utf-8")) as Record<string, string>;
|
|
47
|
+
} catch {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function writeCacheEntry(sessionId: string, path: string): void {
|
|
53
|
+
try {
|
|
54
|
+
mkdirSync(dirname(CACHE_PATH), { recursive: true });
|
|
55
|
+
const cache = readCache();
|
|
56
|
+
cache[sessionId] = path;
|
|
57
|
+
writeFileSync(CACHE_PATH, JSON.stringify(cache), "utf-8");
|
|
58
|
+
} catch {
|
|
59
|
+
// Cache is best-effort — never block the hook on a write failure.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function dirSearch(dir: string, sessionId: string): string | null {
|
|
64
|
+
try {
|
|
65
|
+
for (const f of readdirSync(dir, { withFileTypes: true })) {
|
|
66
|
+
if (f.isFile() && f.name.includes(sessionId) && f.name.endsWith(".jsonl")) {
|
|
67
|
+
return join(dir, f.name);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// dir doesn't exist or unreadable
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function findCodexTranscriptSync(sessionId: string): string | null {
|
|
77
|
+
// 1) Cache hit — fastest path, O(1).
|
|
78
|
+
const cache = readCache();
|
|
79
|
+
const cached = cache[sessionId];
|
|
80
|
+
if (cached && existsSync(cached)) return cached;
|
|
81
|
+
|
|
82
|
+
const root = join(homedir(), ".codex", "sessions");
|
|
83
|
+
|
|
84
|
+
// 2) Today + yesterday (covers the active-session common case).
|
|
85
|
+
const today = new Date();
|
|
86
|
+
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
87
|
+
const datedDirs = [today, yesterday].map((d) => {
|
|
88
|
+
const y = String(d.getUTCFullYear());
|
|
89
|
+
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
90
|
+
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
91
|
+
return join(root, y, m, day);
|
|
92
|
+
});
|
|
93
|
+
for (const dir of datedDirs) {
|
|
94
|
+
const hit = dirSearch(dir, sessionId);
|
|
95
|
+
if (hit) {
|
|
96
|
+
writeCacheEntry(sessionId, hit);
|
|
97
|
+
return hit;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 3) Fallback — full tree scan (rare; older or out-of-clock-skew sessions).
|
|
102
|
+
try {
|
|
103
|
+
for (const y of readdirSync(root, { withFileTypes: true })) {
|
|
104
|
+
if (!y.isDirectory()) continue;
|
|
105
|
+
for (const m of readdirSync(join(root, y.name), { withFileTypes: true })) {
|
|
106
|
+
if (!m.isDirectory()) continue;
|
|
107
|
+
for (const d of readdirSync(join(root, y.name, m.name), { withFileTypes: true })) {
|
|
108
|
+
if (!d.isDirectory()) continue;
|
|
109
|
+
const hit = dirSearch(join(root, y.name, m.name, d.name), sessionId);
|
|
110
|
+
if (hit) {
|
|
111
|
+
writeCacheEntry(sessionId, hit);
|
|
112
|
+
return hit;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// Session may not have flushed yet; or path doesn't exist.
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveCodexMode(sessionId: string): string | undefined {
|
|
124
|
+
try {
|
|
125
|
+
const path = findCodexTranscriptSync(sessionId);
|
|
126
|
+
if (!path) return undefined;
|
|
127
|
+
for (const line of readFileSync(path, "utf-8").split("\n")) {
|
|
128
|
+
if (!line.includes("turn_context")) continue;
|
|
129
|
+
try {
|
|
130
|
+
const obj = JSON.parse(line) as Record<string, unknown>;
|
|
131
|
+
if (obj.type === "turn_context") {
|
|
132
|
+
const policy = (obj.payload as Record<string, unknown> | undefined)?.approval_policy as
|
|
133
|
+
| string
|
|
134
|
+
| undefined;
|
|
135
|
+
if (policy === "never") return "full-auto";
|
|
136
|
+
if (policy === "on-request") return "default";
|
|
137
|
+
if (policy) return policy;
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
// skip malformed line
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
// file vanished or permission denied — fall through to undefined
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
package/src/hooks/types.ts
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Constants and interfaces for Claude Code
|
|
2
|
+
* Constants and interfaces for agent CLI hooks integrations (Claude Code, OpenAI Codex, …).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export const HOOK_SCOPES = ["user", "project", "local"] as const;
|
|
6
6
|
export type HookScope = (typeof HOOK_SCOPES)[number];
|
|
7
7
|
|
|
8
|
+
export const INTEGRATION_TYPES = ["claude", "codex"] as const;
|
|
9
|
+
export type IntegrationType = (typeof INTEGRATION_TYPES)[number];
|
|
10
|
+
|
|
11
|
+
export const CODEX_HOOK_SCOPES = ["user", "project"] as const;
|
|
12
|
+
export type CodexHookScope = (typeof CODEX_HOOK_SCOPES)[number];
|
|
13
|
+
|
|
14
|
+
export const CODEX_HOOK_EVENT_TYPES = [
|
|
15
|
+
"session_start",
|
|
16
|
+
"pre_tool_use",
|
|
17
|
+
"permission_request",
|
|
18
|
+
"post_tool_use",
|
|
19
|
+
"user_prompt_submit",
|
|
20
|
+
"stop",
|
|
21
|
+
] as const;
|
|
22
|
+
export type CodexHookEventType = (typeof CODEX_HOOK_EVENT_TYPES)[number];
|
|
23
|
+
|
|
24
|
+
export const CODEX_EVENT_MAP: Record<CodexHookEventType, HookEventType> = {
|
|
25
|
+
session_start: "SessionStart",
|
|
26
|
+
pre_tool_use: "PreToolUse",
|
|
27
|
+
permission_request: "PermissionRequest",
|
|
28
|
+
post_tool_use: "PostToolUse",
|
|
29
|
+
user_prompt_submit: "UserPromptSubmit",
|
|
30
|
+
stop: "Stop",
|
|
31
|
+
};
|
|
32
|
+
|
|
8
33
|
export const HOOK_EVENT_TYPES = [
|
|
9
34
|
"SessionStart",
|
|
10
35
|
"SessionEnd",
|
|
@@ -32,6 +57,8 @@ export const HOOK_EVENT_TYPES = [
|
|
|
32
57
|
"PostCompact",
|
|
33
58
|
"Elicitation",
|
|
34
59
|
"ElicitationResult",
|
|
60
|
+
"UserPromptExpansion",
|
|
61
|
+
"PostToolBatch",
|
|
35
62
|
] as const;
|
|
36
63
|
|
|
37
64
|
export type HookEventType = (typeof HOOK_EVENT_TYPES)[number];
|
|
@@ -55,6 +82,8 @@ export interface SessionMetadata {
|
|
|
55
82
|
cwd?: string;
|
|
56
83
|
permissionMode?: string;
|
|
57
84
|
hookEventName?: string;
|
|
85
|
+
/** Which agent CLI fired this hook (claude | codex). Set by handler.ts from --cli. */
|
|
86
|
+
cli?: IntegrationType;
|
|
58
87
|
}
|
|
59
88
|
|
|
60
89
|
export interface ClaudeSettings {
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},56704,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},20635,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/action-async-storage.external.js",()=>require("next/dist/server/app-render/action-async-storage.external.js"))},24725,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},43285,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/dynamic-access-async-storage.external.js",()=>require("next/dist/server/app-render/dynamic-access-async-storage.external.js"))},42602,(a,b,c)=>{"use strict";b.exports=a.r(18622)},87924,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactJsxRuntime},72131,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].React},38783,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactServerDOMTurbopackClient},9270,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.AppRouterContext},36313,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.HooksClientContext},18341,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.ServerInsertedHtml},35112,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactDOM},66036,a=>{"use strict";a.s(["captureClientEvent",0,function(a,b){},"setClientTelemetryConfig",0,function(a){}])},33354,(a,b,c)=>{"use strict";c._=function(a){return a&&a.__esModule?a:{default:a}}},6236,a=>{"use strict";var b=a.i(87924),c=a.i(72131);let d=(0,c.createContext)(void 0);a.s(["AutoRefreshProvider",0,function({children:a}){let[e,f]=(0,c.useState)(0);return(0,b.jsx)(d.Provider,{value:{intervalSec:e,setIntervalSec:f},children:a})},"useAutoRefresh",0,function(){let a=(0,c.useContext)(d);if(!a)throw Error("useAutoRefresh must be used within AutoRefreshProvider");return a}])},3314,a=>{"use strict";let b=(0,a.i(70106).default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);a.s(["Shield",0,b],3314)},40695,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(97895);let e=c.forwardRef(({className:a,variant:c="default",size:e="default",...f},g)=>(0,b.jsx)("button",{className:(0,d.cn)("inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none",{"bg-primary text-primary-foreground hover:bg-primary/90":"default"===c,"hover:bg-muted hover:text-muted-foreground":"ghost"===c,"border border-border bg-background hover:bg-muted":"outline"===c,"h-10 py-2 px-4":"default"===e,"h-10 w-10":"icon"===e,"h-9 px-3":"sm"===e,"h-11 px-8":"lg"===e},a),ref:g,...f}));e.displayName="Button",a.s(["Button",0,e])},5784,a=>{"use strict";let b=(0,a.i(70106).default)("chevron-down",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);a.s(["ChevronDown",0,b],5784)},46058,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},88347,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d,e,f={ACTION_HMR_REFRESH:function(){return l},ACTION_NAVIGATE:function(){return i},ACTION_REFRESH:function(){return h},ACTION_RESTORE:function(){return j},ACTION_SERVER_ACTION:function(){return m},ACTION_SERVER_PATCH:function(){return k},PrefetchKind:function(){return n},ScrollBehavior:function(){return o}};for(var g in f)Object.defineProperty(c,g,{enumerable:!0,get:f[g]});let h="refresh",i="navigate",j="restore",k="server-patch",l="hmr-refresh",m="server-action";var n=((d={}).AUTO="auto",d.FULL="full",d),o=((e={})[e.Default=0]="Default",e[e.NoScroll=1]="NoScroll",e);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},67009,(a,b,c)=>{"use strict";function d(a){return null!==a&&"object"==typeof a&&"then"in a&&"function"==typeof a.then}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"isThenable",{enumerable:!0,get:function(){return d}})},90841,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={dispatchAppRouterAction:function(){return i},dispatchGestureState:function(){return j},refreshOnInstantNavigationUnlock:function(){return h},useActionQueue:function(){return k}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(46058)._(a.r(72131)),g=a.r(67009);a.r(88347);function h(){}function i(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function j(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function k(a){let[b,c]=f.default.useState(a.state),[d,e]=(0,f.useOptimistic)(b),h=(0,f.useMemo)(()=>d,[d]);return(0,g.isThenable)(h)?(0,f.use)(h):h}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},20611,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"callServer",{enumerable:!0,get:function(){return g}});let d=a.r(72131),e=a.r(88347),f=a.r(90841);async function g(a,b){return new Promise((c,g)=>{(0,d.startTransition)(()=>{(0,f.dispatchAppRouterAction)({type:e.ACTION_SERVER_ACTION,actionId:a,actionArgs:b,resolve:c,reject:g})})})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},1722,(a,b,c)=>{"use strict";let d;Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"findSourceMapURL",{enumerable:!0,get:function(){return d}});("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},5050,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={callServer:function(){return f.callServer},createServerReference:function(){return h.createServerReference},findSourceMapURL:function(){return g.findSourceMapURL}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20611),g=a.r(1722),h=a.r(38783)},57594,a=>{"use strict";var b=a.i(5050);let c=(0,b.createServerReference)("001510e3800b2119dc0f0781ca25904622b8c2293d",b.callServer,void 0,b.findSourceMapURL,"getTelemetryConfig");a.s(["getTelemetryConfig",0,c])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0_rr1ty._.js.map
|