failproofai 0.0.2-beta.5 → 0.0.2-beta.7
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/.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 +1 -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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- 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 +10 -10
- 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 +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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]__02nt~6d._.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]__00_.atk._.js → [root-of-the-server]__05zi2mt._.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]__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]__0gw4qdj._.js → [root-of-the-server]__0kkt_9z._.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 +8 -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/_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/{0issdwvmb81z_.js → 02u4v.k5amfah.js} +1 -1
- package/.next/standalone/.next/static/chunks/{031pa5~qfzt~_.js → 09e7drilkf1sn.js} +1 -1
- package/.next/standalone/.next/static/chunks/{14ee68i9dy9b3.js → 0bkizbynk9via.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0gleuaabeolm~.js → 0e76l4~hq_sei.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0odv81fzkn6u~.js → 0ltx5i0xv85_s.js} +1 -1
- package/.next/standalone/.next/static/chunks/{040il49xqyq~j.js → 0q7atesxo-36k.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ezymnwrt2x6i.js → 0suauczjqzn07.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0d-hv1uc827s6.js → 0w.rtg9.m8dk-.js} +2 -2
- package/.next/standalone/.next/static/chunks/{10uhv8kh~ad6m.js → 13jdpvk~s2da8.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-0uc5y~g6h.n7-.js → turbopack-0r26pc8h0y_-e.js} +1 -1
- package/.next/standalone/CHANGELOG.md +74 -0
- package/.next/standalone/CLAUDE.md +14 -0
- package/.next/standalone/README.md +20 -3
- package/.next/standalone/bin/failproofai.mjs +5 -0
- package/.next/standalone/bun.lock +31 -63
- package/.next/standalone/dist/cli.mjs +261 -61
- package/.next/standalone/docs/built-in-policies.mdx +2 -2
- package/.next/standalone/docs/configuration.mdx +46 -0
- package/.next/standalone/docs/custom-policies.mdx +63 -5
- package/.next/standalone/docs/docs.json +3 -3
- 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/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 +1 -1
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/hooks/builtin-policies.ts +110 -18
- package/.next/standalone/src/hooks/custom-hooks-loader.ts +158 -21
- package/.next/standalone/src/hooks/handler.ts +26 -6
- 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 +57 -14
- package/.next/standalone/src/hooks/policy-evaluator.ts +16 -2
- package/README.md +20 -3
- package/bin/failproofai.mjs +5 -0
- package/dist/cli.mjs +261 -61
- package/package.json +1 -1
- package/src/hooks/builtin-policies.ts +110 -18
- package/src/hooks/custom-hooks-loader.ts +158 -21
- package/src/hooks/handler.ts +26 -6
- 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 +57 -14
- package/src/hooks/policy-evaluator.ts +16 -2
- /package/.next/standalone/.next/static/{p7b7Yk0VOBDjbtr1aHDyV → Opbai6exOQP2W488FWmr6}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{p7b7Yk0VOBDjbtr1aHDyV → Opbai6exOQP2W488FWmr6}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{p7b7Yk0VOBDjbtr1aHDyV → Opbai6exOQP2W488FWmr6}/_ssgManifest.js +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
6
|
-
import { resolve, dirname } from "node:path";
|
|
6
|
+
import { resolve, dirname, basename } from "node:path";
|
|
7
7
|
import { homedir, platform, arch, release, hostname } from "node:os";
|
|
8
8
|
import {
|
|
9
9
|
HOOK_EVENT_TYPES,
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
type ClaudeSettings,
|
|
16
16
|
} from "./types";
|
|
17
17
|
import { promptPolicySelection } from "./install-prompt";
|
|
18
|
-
import {
|
|
18
|
+
import { readMergedHooksConfig, readScopedHooksConfig, writeScopedHooksConfig } from "./hooks-config";
|
|
19
19
|
import type { HooksConfig } from "./policy-types";
|
|
20
20
|
import { BUILTIN_POLICIES } from "./builtin-policies";
|
|
21
|
-
import { loadCustomHooks } from "./custom-hooks-loader";
|
|
21
|
+
import { loadCustomHooks, discoverPolicyFiles } from "./custom-hooks-loader";
|
|
22
22
|
import { trackHookEvent } from "./hook-telemetry";
|
|
23
23
|
import { getInstanceId, hashToId } from "../../lib/telemetry-id";
|
|
24
24
|
import { CliError } from "../cli-error";
|
|
@@ -203,7 +203,7 @@ export async function installHooks(
|
|
|
203
203
|
const binaryPath = resolveFailproofaiBinary();
|
|
204
204
|
|
|
205
205
|
// Capture existing config before overwriting (used for telemetry diff)
|
|
206
|
-
const previousConfig =
|
|
206
|
+
const previousConfig = readScopedHooksConfig(scope, cwd);
|
|
207
207
|
const previousEnabled = new Set(previousConfig.enabledPolicies);
|
|
208
208
|
|
|
209
209
|
let selectedPolicies: string[];
|
|
@@ -251,7 +251,7 @@ export async function installHooks(
|
|
|
251
251
|
`\nValidated ${validatedHooks.length} custom hook(s): ${validatedHooks.map((h) => h.name).join(", ")}`,
|
|
252
252
|
);
|
|
253
253
|
}
|
|
254
|
-
|
|
254
|
+
writeScopedHooksConfig(configToWrite, scope, cwd);
|
|
255
255
|
console.log(`\nEnabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}`);
|
|
256
256
|
if (removeCustomHooks) {
|
|
257
257
|
console.log("Custom hooks path cleared.");
|
|
@@ -355,18 +355,21 @@ export async function installHooks(
|
|
|
355
355
|
* @param opts.betaOnly — set to true when removing only beta policies (adds beta_only flag to telemetry)
|
|
356
356
|
*/
|
|
357
357
|
export async function removeHooks(policyNames?: string[], scope: HookScope | "all" = "user", cwd?: string, opts?: { betaOnly?: boolean; source?: string; removeCustomHooks?: boolean }): Promise<void> {
|
|
358
|
+
// Resolve the effective config scope ("all" falls back to "user" for config reads/writes)
|
|
359
|
+
const configScope: HookScope = scope === "all" ? "user" : scope;
|
|
360
|
+
|
|
358
361
|
// Clear custom hooks path if requested
|
|
359
362
|
if (opts?.removeCustomHooks) {
|
|
360
|
-
const config =
|
|
363
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
361
364
|
delete config.customPoliciesPath;
|
|
362
|
-
|
|
365
|
+
writeScopedHooksConfig(config, configScope, cwd);
|
|
363
366
|
console.log("Custom hooks path cleared.");
|
|
364
367
|
}
|
|
365
368
|
|
|
366
369
|
// Remove specific policies from config (keep hooks installed)
|
|
367
370
|
if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
|
|
368
371
|
validatePolicyNames(policyNames);
|
|
369
|
-
const config =
|
|
372
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
370
373
|
const removeSet = new Set(policyNames);
|
|
371
374
|
const remaining = config.enabledPolicies.filter((p) => !removeSet.has(p));
|
|
372
375
|
const notEnabled = policyNames.filter((p) => !config.enabledPolicies.includes(p));
|
|
@@ -382,7 +385,7 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
382
385
|
enabledPolicies: remaining,
|
|
383
386
|
...(filteredParams && Object.keys(filteredParams).length > 0 ? { policyParams: filteredParams } : {}),
|
|
384
387
|
};
|
|
385
|
-
|
|
388
|
+
writeScopedHooksConfig(updatedConfig, configScope, cwd);
|
|
386
389
|
|
|
387
390
|
// Telemetry: track policy-only removal from config
|
|
388
391
|
try {
|
|
@@ -410,7 +413,7 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
410
413
|
}
|
|
411
414
|
|
|
412
415
|
// Capture enabled policies before clearing (used for accurate telemetry below)
|
|
413
|
-
const configBeforeRemoval =
|
|
416
|
+
const configBeforeRemoval = readScopedHooksConfig(configScope, cwd);
|
|
414
417
|
|
|
415
418
|
// Remove all failproofai hooks from Claude Code settings
|
|
416
419
|
const scopesToRemove: HookScope[] = scope === "all" ? [...HOOK_SCOPES] : [scope];
|
|
@@ -472,10 +475,19 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
472
475
|
}
|
|
473
476
|
|
|
474
477
|
// Clear policy config when removing from all scopes, or when no hooks remain in any scope
|
|
475
|
-
if (scope === "all"
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
|
|
478
|
+
if (scope === "all") {
|
|
479
|
+
// Clear config across all three scopes
|
|
480
|
+
for (const s of HOOK_SCOPES) {
|
|
481
|
+
const existing = readScopedHooksConfig(s, cwd);
|
|
482
|
+
if (existing.enabledPolicies.length > 0 || existing.customPoliciesPath || existing.policyParams) {
|
|
483
|
+
const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
|
|
484
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, s, cwd);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} else if (!HOOK_SCOPES.some((s) => hooksInstalledInSettings(s, cwd))) {
|
|
488
|
+
const existing = readScopedHooksConfig(configScope, cwd);
|
|
489
|
+
const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
|
|
490
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, configScope, cwd);
|
|
479
491
|
}
|
|
480
492
|
}
|
|
481
493
|
|
|
@@ -638,4 +650,35 @@ export async function listHooks(cwd?: string): Promise<void> {
|
|
|
638
650
|
}
|
|
639
651
|
console.log();
|
|
640
652
|
}
|
|
653
|
+
|
|
654
|
+
// Convention Policies section (.failproofai/policies/*policies.{js,mjs,ts})
|
|
655
|
+
const base = cwd ? resolve(cwd) : process.cwd();
|
|
656
|
+
const conventionDirs: { label: string; dir: string }[] = [
|
|
657
|
+
{ label: "Project", dir: resolve(base, ".failproofai", "policies") },
|
|
658
|
+
{ label: "User", dir: resolve(homedir(), ".failproofai", "policies") },
|
|
659
|
+
];
|
|
660
|
+
|
|
661
|
+
for (const { label, dir } of conventionDirs) {
|
|
662
|
+
const files = discoverPolicyFiles(dir);
|
|
663
|
+
if (files.length === 0) continue;
|
|
664
|
+
|
|
665
|
+
console.log(`\n \u2500\u2500 Convention Policies \u2014 ${label} (${dir}) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
666
|
+
for (const file of files) {
|
|
667
|
+
try {
|
|
668
|
+
const hooks = await loadCustomHooks(file);
|
|
669
|
+
if (hooks.length === 0) {
|
|
670
|
+
const filename = basename(file);
|
|
671
|
+
console.log(` \x1B[31m\u2717\x1B[0m ${filename.padEnd(nameColWidth)}\x1B[31mfailed to load\x1B[0m`);
|
|
672
|
+
} else {
|
|
673
|
+
const filename = basename(file);
|
|
674
|
+
const hookSummary = hooks.map((h) => h.name).join(", ");
|
|
675
|
+
console.log(` \x1B[32m\u2713\x1B[0m ${filename.padEnd(nameColWidth)}${hooks.length} hook(s): ${hookSummary}`);
|
|
676
|
+
}
|
|
677
|
+
} catch {
|
|
678
|
+
const filename = basename(file);
|
|
679
|
+
console.log(` \x1B[31m\u2717\x1B[0m ${filename.padEnd(nameColWidth)}\x1B[31merror\x1B[0m`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
console.log();
|
|
683
|
+
}
|
|
641
684
|
}
|
|
@@ -8,6 +8,14 @@ import { BUILTIN_POLICIES } from "./builtin-policies";
|
|
|
8
8
|
import { getPoliciesForEvent } from "./policy-registry";
|
|
9
9
|
import { hookLogInfo, hookLogWarn } from "./hook-logger";
|
|
10
10
|
|
|
11
|
+
function appendHint(baseReason: string, hint: unknown): string {
|
|
12
|
+
const base = baseReason.trim();
|
|
13
|
+
const normalizedHint = typeof hint === "string" ? hint.trim() : "";
|
|
14
|
+
if (!normalizedHint) return base;
|
|
15
|
+
if (!base) return normalizedHint;
|
|
16
|
+
return `${base}. ${normalizedHint}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export interface EvaluationResult {
|
|
12
20
|
exitCode: number;
|
|
13
21
|
stdout: string;
|
|
@@ -80,7 +88,10 @@ export async function evaluatePolicies(
|
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
if (result.decision === "deny") {
|
|
83
|
-
const reason =
|
|
91
|
+
const reason = appendHint(
|
|
92
|
+
result.reason ?? `Blocked by policy: ${policy.name}`,
|
|
93
|
+
config?.policyParams?.[policy.name]?.hint,
|
|
94
|
+
);
|
|
84
95
|
hookLogInfo(`deny by "${policy.name}": ${reason}`);
|
|
85
96
|
|
|
86
97
|
const displayTool = ctx.toolName ?? "unknown tool";
|
|
@@ -134,7 +145,10 @@ export async function evaluatePolicies(
|
|
|
134
145
|
// Accumulate first instruct (does not short-circuit — later policies can still deny)
|
|
135
146
|
if (result.decision === "instruct" && !instructPolicyName) {
|
|
136
147
|
instructPolicyName = policy.name;
|
|
137
|
-
instructReason =
|
|
148
|
+
instructReason = appendHint(
|
|
149
|
+
result.reason ?? `Instruction from policy: ${policy.name}`,
|
|
150
|
+
config?.policyParams?.[policy.name]?.hint,
|
|
151
|
+
);
|
|
138
152
|
hookLogInfo(`instruct by "${policy.name}": ${instructReason}`);
|
|
139
153
|
}
|
|
140
154
|
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](https://www.npmjs.com/package/failproofai)
|
|
14
14
|
[](LICENSE)
|
|
15
15
|
[](https://github.com/exospherehost/failproofai/actions)
|
|
16
|
-
[](https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ)
|
|
17
17
|
|
|
18
18
|
The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously - for **Claude Code** & the **Agents SDK**.
|
|
19
19
|
|
|
@@ -111,10 +111,12 @@ Policy configuration lives in `~/.failproofai/policies-config.json` (global) or
|
|
|
111
111
|
],
|
|
112
112
|
"policyParams": {
|
|
113
113
|
"block-sudo": {
|
|
114
|
-
"allowPatterns": ["sudo systemctl status", "sudo journalctl"]
|
|
114
|
+
"allowPatterns": ["sudo systemctl status", "sudo journalctl"],
|
|
115
|
+
"hint": "Use apt-get directly without sudo."
|
|
115
116
|
},
|
|
116
117
|
"block-push-master": {
|
|
117
|
-
"protectedBranches": ["main", "release", "prod"]
|
|
118
|
+
"protectedBranches": ["main", "release", "prod"],
|
|
119
|
+
"hint": "Try creating a fresh branch instead."
|
|
118
120
|
},
|
|
119
121
|
"sanitize-api-keys": {
|
|
120
122
|
"additionalPatterns": [
|
|
@@ -216,6 +218,21 @@ failproofai policies --install --custom ./my-policies.js
|
|
|
216
218
|
|
|
217
219
|
Custom hooks support transitive local imports, async/await, and access to `process.env`. Errors are fail-open (logged to `~/.failproofai/hook.log`, built-in policies continue). See [docs/custom-hooks.mdx](docs/custom-hooks.mdx) for the full guide.
|
|
218
220
|
|
|
221
|
+
### Convention-based policies (v0.0.2-beta.7+)
|
|
222
|
+
|
|
223
|
+
Drop `*policies.{js,mjs,ts}` files into `.failproofai/policies/` and they're automatically loaded — no `--custom` flag or config changes needed. Works like git hooks: drop a file, it just works.
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
# Project level — committed to git, shared with the team
|
|
227
|
+
.failproofai/policies/security-policies.mjs
|
|
228
|
+
.failproofai/policies/workflow-policies.mjs
|
|
229
|
+
|
|
230
|
+
# User level — personal, applies to all projects
|
|
231
|
+
~/.failproofai/policies/my-policies.mjs
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Both levels load (union). Files are loaded alphabetically within each directory. Prefix with `01-`, `02-`, etc. to control order. See [examples/convention-policies/](examples/convention-policies/) for ready-to-use examples.
|
|
235
|
+
|
|
219
236
|
---
|
|
220
237
|
|
|
221
238
|
## Telemetry
|
package/bin/failproofai.mjs
CHANGED
|
@@ -97,6 +97,11 @@ COMMANDS
|
|
|
97
97
|
--version, -v Print version and exit
|
|
98
98
|
--help, -h Show this help message
|
|
99
99
|
|
|
100
|
+
CONVENTION POLICIES
|
|
101
|
+
Drop *policies.{js,mjs,ts} files into .failproofai/policies/ for auto-loading.
|
|
102
|
+
Works at project level (.failproofai/policies/) and user level (~/.failproofai/policies/).
|
|
103
|
+
No --custom flag or config changes needed — just drop files and they're picked up.
|
|
104
|
+
|
|
100
105
|
EXAMPLES
|
|
101
106
|
failproofai policies
|
|
102
107
|
failproofai policies --install
|