failproofai 0.0.2-beta.1 → 0.0.2

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 (94) 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/server/app/_global-error/page/server-reference-manifest.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  9. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  10. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  12. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  18. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  19. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  21. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  22. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  24. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/.next/standalone/.next/server/app/index.html +1 -1
  26. package/.next/standalone/.next/server/app/index.rsc +15 -15
  27. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  29. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  30. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  31. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  32. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  33. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  35. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  36. package/.next/standalone/.next/server/app/policies/page.js +1 -1
  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]__0g.lg8b._.js +2 -2
  54. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
  55. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0osi8nq._.js → [root-of-the-server]__0okos0k._.js} +3 -3
  56. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0a3kr67._.js → [root-of-the-server]__0qo8503._.js} +2 -2
  57. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +4 -3
  58. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
  59. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rbuarm._.js → [root-of-the-server]__12kr5~_._.js} +2 -2
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +1 -1
  62. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0rd0oc-._.js +1 -1
  65. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  66. package/.next/standalone/.next/server/pages/404.html +2 -2
  67. package/.next/standalone/.next/server/pages/500.html +1 -1
  68. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  70. package/.next/standalone/.next/static/chunks/{0qvj8bhl661lq.js → 06og.7e9nkpjh.js} +1 -1
  71. package/.next/standalone/.next/static/chunks/{0gcz-jqgqz~9m.js → 0_4y_t03jn2nq.js} +1 -1
  72. package/.next/standalone/.next/static/chunks/{0kob_5.phc~sk.js → 0c_ljlxa._4lc.js} +1 -1
  73. package/.next/standalone/.next/static/chunks/{0jhw8ofx.5g_e.js → 0cvffh-pbsv5u.js} +1 -1
  74. package/.next/standalone/.next/static/chunks/{0mjc3aq2wxvlt.js → 0ov60i6md~37t.js} +1 -1
  75. package/.next/standalone/.next/static/chunks/{0q7z97izctgrw.js → 0x-625~1vx1lu.js} +1 -1
  76. package/.next/standalone/.next/static/chunks/{0a08gn8709y98.js → 0y~0creqvl5wx.js} +1 -1
  77. package/.next/standalone/.next/static/chunks/{0mr-jhx402yci.js → 15wf7x-e.8ia3.js} +1 -1
  78. package/.next/standalone/README.md +9 -9
  79. package/.next/standalone/bin/failproofai.mjs +221 -128
  80. package/.next/standalone/dist/cli.mjs +202 -88
  81. package/.next/standalone/package.json +1 -1
  82. package/.next/standalone/scripts/publish-aliases.mjs +4 -2
  83. package/.next/standalone/src/cli-error.ts +18 -0
  84. package/.next/standalone/src/hooks/manager.ts +17 -3
  85. package/README.md +9 -9
  86. package/bin/failproofai.mjs +221 -128
  87. package/dist/cli.mjs +202 -88
  88. package/package.json +1 -1
  89. package/scripts/publish-aliases.mjs +4 -2
  90. package/src/cli-error.ts +18 -0
  91. package/src/hooks/manager.ts +17 -3
  92. /package/.next/standalone/.next/static/{Dnk96sbMPjYOx1pdLdOH0 → WS-OQSqL1Lp1w_obXfjvl}/_buildManifest.js +0 -0
  93. /package/.next/standalone/.next/static/{Dnk96sbMPjYOx1pdLdOH0 → WS-OQSqL1Lp1w_obXfjvl}/_clientMiddlewareManifest.js +0 -0
  94. /package/.next/standalone/.next/static/{Dnk96sbMPjYOx1pdLdOH0 → WS-OQSqL1Lp1w_obXfjvl}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -1536,7 +1536,7 @@ var init_hook_activity_store = __esm(() => {
1536
1536
  });
1537
1537
 
1538
1538
  // package.json
1539
- var version2 = "0.0.2-beta.1";
1539
+ var version2 = "0.0.2";
1540
1540
  var init_package = () => {};
1541
1541
 
1542
1542
  // src/posthog-key.ts
@@ -2077,6 +2077,19 @@ var init_install_prompt = __esm(() => {
2077
2077
  init_builtin_policies();
2078
2078
  });
2079
2079
 
2080
+ // src/cli-error.ts
2081
+ var CliError;
2082
+ var init_cli_error = __esm(() => {
2083
+ CliError = class CliError extends Error {
2084
+ exitCode;
2085
+ constructor(message, exitCode = 1) {
2086
+ super(message);
2087
+ this.name = "CliError";
2088
+ this.exitCode = exitCode;
2089
+ }
2090
+ };
2091
+ });
2092
+
2080
2093
  // src/hooks/manager.ts
2081
2094
  var exports_manager = {};
2082
2095
  __export(exports_manager, {
@@ -2130,7 +2143,7 @@ function resolveFailproofaiBinary() {
2130
2143
  return result.split(`
2131
2144
  `)[0].trim();
2132
2145
  } catch {
2133
- throw new Error(`failproofai binary not found in PATH.
2146
+ throw new CliError(`failproofai binary not found in PATH.
2134
2147
  ` + "Install it globally first: npm install -g failproofai");
2135
2148
  }
2136
2149
  }
@@ -2144,7 +2157,7 @@ function validatePolicyNames(names) {
2144
2157
  const invalid = names.filter((n) => !VALID_POLICY_NAMES.has(n));
2145
2158
  if (invalid.length > 0) {
2146
2159
  const validList = [...VALID_POLICY_NAMES].join(", ");
2147
- throw new Error(`Unknown policy name(s): ${invalid.join(", ")}
2160
+ throw new CliError(`Unknown policy name(s): ${invalid.join(", ")}
2148
2161
  ` + `Valid policies: ${validList}`);
2149
2162
  }
2150
2163
  }
@@ -2211,6 +2224,15 @@ function removeHooksFromSettingsFile(settingsPath) {
2211
2224
  return removed;
2212
2225
  }
2213
2226
  async function installHooks(policyNames, scope = "user", cwd, includeBeta = false, source, customPoliciesPath, removeCustomHooks = false) {
2227
+ if (policyNames !== undefined && policyNames.length > 0) {
2228
+ const nonAllNames = policyNames.filter((n) => n !== "all");
2229
+ if (nonAllNames.length > 0)
2230
+ validatePolicyNames(nonAllNames);
2231
+ if (policyNames.includes("all") && nonAllNames.length > 0) {
2232
+ throw new CliError(`"all" cannot be combined with specific policy names.
2233
+ ` + `Use either: --install all or --install block-sudo sanitize-jwt ...`);
2234
+ }
2235
+ }
2214
2236
  const binaryPath = resolveFailproofaiBinary();
2215
2237
  const previousConfig = readHooksConfig();
2216
2238
  const previousEnabled = new Set(previousConfig.enabledPolicies);
@@ -2220,8 +2242,6 @@ async function installHooks(policyNames, scope = "user", cwd, includeBeta = fals
2220
2242
  if (policyNames.length === 1 && policyNames[0] === "all") {
2221
2243
  incoming = BUILTIN_POLICIES.filter((p) => includeBeta || !p.beta).map((p) => p.name);
2222
2244
  } else {
2223
- if (policyNames.length > 0)
2224
- validatePolicyNames(policyNames);
2225
2245
  incoming = policyNames;
2226
2246
  }
2227
2247
  selectedPolicies = [...new Set([...previousConfig.enabledPolicies, ...incoming])];
@@ -2565,6 +2585,7 @@ var init_manager = __esm(() => {
2565
2585
  init_custom_hooks_loader();
2566
2586
  init_hook_telemetry();
2567
2587
  init_telemetry_id();
2588
+ init_cli_error();
2568
2589
  VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
2569
2590
  });
2570
2591
 
@@ -2719,12 +2740,29 @@ var init_launch = __esm(() => {
2719
2740
  init_package();
2720
2741
  });
2721
2742
 
2743
+ // src/cli-error.ts
2744
+ var exports_cli_error = {};
2745
+ __export(exports_cli_error, {
2746
+ CliError: () => CliError2
2747
+ });
2748
+ var CliError2;
2749
+ var init_cli_error2 = __esm(() => {
2750
+ CliError2 = class CliError2 extends Error {
2751
+ exitCode;
2752
+ constructor(message, exitCode = 1) {
2753
+ super(message);
2754
+ this.name = "CliError";
2755
+ this.exitCode = exitCode;
2756
+ }
2757
+ };
2758
+ });
2759
+
2722
2760
  // bin/failproofai.mjs
2723
2761
  import { realpathSync as realpathSync2 } from "fs";
2724
2762
  import { dirname as dirname5, resolve as resolve8 } from "path";
2725
2763
  import { fileURLToPath as fileURLToPath2 } from "url";
2726
2764
  // package.json
2727
- var version = "0.0.2-beta.1";
2765
+ var version = "0.0.2";
2728
2766
 
2729
2767
  // bin/failproofai.mjs
2730
2768
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
@@ -2736,9 +2774,32 @@ if (!process.env.FAILPROOFAI_DIST_PATH) {
2736
2774
  var args = process.argv.slice(2);
2737
2775
  if (args[0] === "p")
2738
2776
  args[0] = "policies";
2739
- var SUBCOMMANDS = ["policies"];
2740
- if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
2741
- console.log(`
2777
+ var hookIdx = args.indexOf("--hook");
2778
+ if (hookIdx >= 0) {
2779
+ if (!args[hookIdx + 1]) {
2780
+ console.error("Error: Missing event type after --hook");
2781
+ console.error("Usage: failproofai --hook <event> (e.g. PreToolUse, PostToolUse)");
2782
+ process.exit(1);
2783
+ }
2784
+ try {
2785
+ const { handleHookEvent: handleHookEvent2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
2786
+ const exitCode = await handleHookEvent2(args[hookIdx + 1]);
2787
+ process.exit(exitCode);
2788
+ } catch (err) {
2789
+ const msg = err instanceof Error ? err.message : String(err);
2790
+ console.error(`Unexpected error: ${msg}`);
2791
+ process.exit(2);
2792
+ }
2793
+ }
2794
+ async function runCli() {
2795
+ const SUBCOMMANDS = ["policies"];
2796
+ if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
2797
+ const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
2798
+ if (extraArgs.length > 0) {
2799
+ throw new CliError3(`Unexpected argument: ${extraArgs[0]}
2800
+ Run \`failproofai --help\` for usage.`);
2801
+ }
2802
+ console.log(`
2742
2803
  failproofai v${version}
2743
2804
 
2744
2805
  USAGE
@@ -2778,25 +2839,24 @@ LINKS
2778
2839
  \u2B50 Star us: https://github.com/exospherehost/failproofai
2779
2840
  \uD83D\uDCD6 Docs: https://befailproof.ai
2780
2841
  `.trimStart());
2781
- process.exit(0);
2782
- }
2783
- if (args.includes("--version") || args.includes("-v")) {
2784
- console.log(version);
2785
- process.exit(0);
2786
- }
2787
- var hookIdx = args.indexOf("--hook");
2788
- if (hookIdx >= 0 && args[hookIdx + 1]) {
2789
- const { handleHookEvent: handleHookEvent2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
2790
- const exitCode = await handleHookEvent2(args[hookIdx + 1]);
2791
- process.exit(exitCode);
2792
- }
2793
- if (args[0] === "policies") {
2794
- const subArgs = args.slice(1);
2795
- const isInstall = subArgs.includes("--install") || subArgs.includes("-i");
2796
- const isUninstall = subArgs.includes("--uninstall") || subArgs.includes("-u");
2797
- const isHelp = subArgs.includes("--help") || subArgs.includes("-h");
2798
- if (isHelp) {
2799
- console.log(`
2842
+ process.exit(0);
2843
+ }
2844
+ if ((args.includes("--version") || args.includes("-v")) && !SUBCOMMANDS.includes(args[0])) {
2845
+ const extraArgs = args.filter((a) => a !== "--version" && a !== "-v");
2846
+ if (extraArgs.length > 0) {
2847
+ throw new CliError3(`Unexpected argument: ${extraArgs[0]}
2848
+ Run \`failproofai --help\` for usage.`);
2849
+ }
2850
+ console.log(version);
2851
+ process.exit(0);
2852
+ }
2853
+ if (args[0] === "policies") {
2854
+ const subArgs = args.slice(1);
2855
+ const isInstall = subArgs.includes("--install") || subArgs.includes("-i");
2856
+ const isUninstall = subArgs.includes("--uninstall") || subArgs.includes("-u");
2857
+ const isHelp = subArgs.includes("--help") || subArgs.includes("-h");
2858
+ if (isHelp) {
2859
+ console.log(`
2800
2860
  failproofai policies \u2014 manage Failproof AI policies
2801
2861
 
2802
2862
  USAGE
@@ -2827,65 +2887,119 @@ EXAMPLES
2827
2887
  failproofai policies -u
2828
2888
  failproofai policies --uninstall --custom
2829
2889
  `.trimStart());
2890
+ process.exit(0);
2891
+ }
2892
+ if (isInstall) {
2893
+ const { installHooks: installHooks2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
2894
+ const scopeIdx = subArgs.indexOf("--scope");
2895
+ const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
2896
+ if (scopeIdx >= 0 && (!scope || scope.startsWith("-"))) {
2897
+ throw new CliError3("Missing value for --scope. Valid values: user, project, local");
2898
+ }
2899
+ if (scopeIdx >= 0 && !["user", "project", "local"].includes(scope)) {
2900
+ throw new CliError3(`Invalid scope: ${scope}. Valid values: user, project, local`);
2901
+ }
2902
+ const customIdx = subArgs.includes("--custom") ? subArgs.indexOf("--custom") : subArgs.includes("-c") ? subArgs.indexOf("-c") : -1;
2903
+ const customPoliciesPath = customIdx >= 0 ? subArgs[customIdx + 1] : undefined;
2904
+ if (customIdx >= 0 && (!customPoliciesPath || customPoliciesPath.startsWith("-"))) {
2905
+ throw new CliError3(`Missing path after --custom/-c
2906
+ Usage: --custom <path> (e.g. --custom ./my-policies.js)`);
2907
+ }
2908
+ const includeBeta = subArgs.includes("--beta");
2909
+ const consumedIdxs = new Set;
2910
+ if (scopeIdx >= 0)
2911
+ consumedIdxs.add(scopeIdx + 1);
2912
+ if (customIdx >= 0)
2913
+ consumedIdxs.add(customIdx + 1);
2914
+ const flags = new Set(["--install", "-i", "--scope", "--beta", "--custom", "-c"]);
2915
+ const unknownInstallFlag = subArgs.find((a) => a.startsWith("-") && !flags.has(a));
2916
+ if (unknownInstallFlag) {
2917
+ throw new CliError3(`Unknown flag: ${unknownInstallFlag}
2918
+ Run \`failproofai policies --help\` for usage.`);
2919
+ }
2920
+ const explicitPolicyNames = subArgs.filter((a, idx) => !a.startsWith("-") && !consumedIdxs.has(idx));
2921
+ const policyNames = explicitPolicyNames.length > 0 ? explicitPolicyNames : customPoliciesPath !== undefined ? [] : undefined;
2922
+ await installHooks2(policyNames, scope, undefined, includeBeta, undefined, customPoliciesPath);
2923
+ process.exit(0);
2924
+ }
2925
+ if (isUninstall) {
2926
+ const { removeHooks: removeHooks2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
2927
+ const scopeIdx = subArgs.indexOf("--scope");
2928
+ const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
2929
+ if (scopeIdx >= 0 && (!scope || scope.startsWith("-"))) {
2930
+ throw new CliError3("Missing value for --scope. Valid values: user, project, local, all");
2931
+ }
2932
+ if (scopeIdx >= 0 && !["user", "project", "local", "all"].includes(scope)) {
2933
+ throw new CliError3(`Invalid scope: ${scope}. Valid values: user, project, local, all`);
2934
+ }
2935
+ const betaOnly = subArgs.includes("--beta");
2936
+ const removeCustomHooks = subArgs.includes("--custom") || subArgs.includes("-c");
2937
+ const consumedIdxs = new Set;
2938
+ if (scopeIdx >= 0)
2939
+ consumedIdxs.add(scopeIdx + 1);
2940
+ const flags = new Set(["--uninstall", "-u", "--scope", "--beta", "--custom", "-c"]);
2941
+ const unknownUninstallFlag = subArgs.find((a) => a.startsWith("-") && !flags.has(a));
2942
+ if (unknownUninstallFlag) {
2943
+ throw new CliError3(`Unknown flag: ${unknownUninstallFlag}
2944
+ Run \`failproofai policies --help\` for usage.`);
2945
+ }
2946
+ const policyNames = subArgs.filter((a, idx) => !a.startsWith("-") && !consumedIdxs.has(idx));
2947
+ await removeHooks2(policyNames.length > 0 ? policyNames : undefined, scope, undefined, { betaOnly, removeCustomHooks });
2948
+ process.exit(0);
2949
+ }
2950
+ const knownListFlags = new Set(["--install", "-i", "--uninstall", "-u", "--help", "-h", "--list"]);
2951
+ const unknownListArg = subArgs.find((a) => a.startsWith("-") && !knownListFlags.has(a));
2952
+ if (unknownListArg) {
2953
+ throw new CliError3(`Unknown flag: ${unknownListArg}
2954
+ Run \`failproofai policies --help\` for usage.`);
2955
+ }
2956
+ const positionalArgs = subArgs.filter((a) => !a.startsWith("-"));
2957
+ if (positionalArgs.length > 0) {
2958
+ throw new CliError3(`Unexpected argument: ${positionalArgs[0]}
2959
+ Run \`failproofai policies --help\` for usage.`);
2960
+ }
2961
+ const { listHooks: listHooks2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
2962
+ await listHooks2();
2830
2963
  process.exit(0);
2831
2964
  }
2832
- if (isInstall) {
2833
- const { installHooks: installHooks2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
2834
- const scopeIdx = subArgs.indexOf("--scope");
2835
- const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
2836
- const customIdx = subArgs.includes("--custom") ? subArgs.indexOf("--custom") : subArgs.includes("-c") ? subArgs.indexOf("-c") : -1;
2837
- const customPoliciesPath = customIdx >= 0 ? subArgs[customIdx + 1] : undefined;
2838
- const includeBeta = subArgs.includes("--beta");
2839
- const consumed = new Set([scope, customPoliciesPath].filter(Boolean));
2840
- const flags = new Set(["--install", "-i", "--scope", "--beta", "--custom", "-c"]);
2841
- const explicitPolicyNames = subArgs.filter((a) => !a.startsWith("-") && !flags.has(a) && !consumed.has(a));
2842
- const policyNames = explicitPolicyNames.length > 0 ? explicitPolicyNames : customPoliciesPath !== undefined ? [] : undefined;
2843
- await installHooks2(policyNames, scope, undefined, includeBeta, undefined, customPoliciesPath);
2844
- process.exit(0);
2845
- }
2846
- if (isUninstall) {
2847
- const { removeHooks: removeHooks2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
2848
- const scopeIdx = subArgs.indexOf("--scope");
2849
- const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
2850
- const betaOnly = subArgs.includes("--beta");
2851
- const removeCustomHooks = subArgs.includes("--custom") || subArgs.includes("-c");
2852
- const consumed = new Set([scope].filter(Boolean));
2853
- const flags = new Set(["--uninstall", "-u", "--scope", "--beta", "--custom", "-c"]);
2854
- const policyNames = subArgs.filter((a) => !a.startsWith("-") && !flags.has(a) && !consumed.has(a));
2855
- await removeHooks2(policyNames.length > 0 ? policyNames : undefined, scope, undefined, { betaOnly, removeCustomHooks });
2856
- process.exit(0);
2857
- }
2858
- const { listHooks: listHooks2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
2859
- await listHooks2();
2860
- process.exit(0);
2861
- }
2862
- var knownFlags = ["--version", "-v", "--help", "-h", "--hook"];
2863
- var unknownFlag = args.find((a) => a.startsWith("-") && !knownFlags.includes(a));
2864
- if (unknownFlag) {
2865
- let levenshtein = function(a, b) {
2866
- const m = a.length, n = b.length;
2867
- const dp = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0));
2868
- for (let i = 1;i <= m; i++)
2869
- for (let j = 1;j <= n; j++)
2870
- dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
2871
- return dp[m][n];
2872
- };
2873
- const primary = ["--version", "--help", "--hook", "policies"];
2874
- const closest = primary.reduce((best, flag) => {
2875
- const dist = levenshtein(unknownFlag, flag);
2876
- return dist < best.dist ? { flag, dist } : best;
2877
- }, { flag: primary[0], dist: Infinity });
2878
- console.error(`Unknown flag: ${unknownFlag}`);
2879
- console.error(`Did you mean: ${closest.flag}?`);
2880
- console.error(`Run \`failproofai --help\` for usage details.`);
2881
- process.exit(1);
2882
- }
2883
- var unknownSubcommand = args.find((a) => !a.startsWith("-") && a !== "policies");
2884
- if (unknownSubcommand) {
2885
- console.error(`Unknown command: ${unknownSubcommand}`);
2886
- console.error(`Did you mean: failproofai policies?`);
2887
- console.error(`Run \`failproofai --help\` for usage details.`);
2888
- process.exit(1);
2889
- }
2890
- var { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
2891
- launch2("start");
2965
+ const knownFlags = ["--version", "-v", "--help", "-h", "--hook"];
2966
+ const unknownFlag = args.find((a) => a.startsWith("-") && !knownFlags.includes(a));
2967
+ if (unknownFlag) {
2968
+ let levenshtein = function(a, b) {
2969
+ const m = a.length, n = b.length;
2970
+ const dp = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0));
2971
+ for (let i = 1;i <= m; i++)
2972
+ for (let j = 1;j <= n; j++)
2973
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
2974
+ return dp[m][n];
2975
+ };
2976
+ const primary = ["--version", "--help", "--hook", "policies"];
2977
+ const closest = primary.reduce((best, flag) => {
2978
+ const dist = levenshtein(unknownFlag, flag);
2979
+ return dist < best.dist ? { flag, dist } : best;
2980
+ }, { flag: primary[0], dist: Infinity });
2981
+ throw new CliError3(`Unknown flag: ${unknownFlag}
2982
+ Did you mean: ${closest.flag}?
2983
+ Run \`failproofai --help\` for usage details.`);
2984
+ }
2985
+ const unknownSubcommand = args.find((a) => !a.startsWith("-") && a !== "policies");
2986
+ if (unknownSubcommand) {
2987
+ throw new CliError3(`Unknown command: ${unknownSubcommand}
2988
+ Did you mean: failproofai policies?
2989
+ Run \`failproofai --help\` for usage details.`);
2990
+ }
2991
+ const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
2992
+ launch2("start");
2993
+ }
2994
+ var { CliError: CliError3 } = await Promise.resolve().then(() => (init_cli_error2(), exports_cli_error));
2995
+ try {
2996
+ await runCli();
2997
+ } catch (err) {
2998
+ if (err instanceof CliError3) {
2999
+ console.error(`Error: ${err.message}`);
3000
+ process.exit(err.exitCode);
3001
+ }
3002
+ const msg = err instanceof Error ? err.message : String(err);
3003
+ console.error(`Unexpected error: ${msg}`);
3004
+ process.exit(2);
3005
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.2-beta.1",
3
+ "version": "0.0.2",
4
4
  "description": "Open-source hooks, policies, and project visualization for Claude Code & Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -8,6 +8,8 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
8
8
  const rootPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
9
9
  const VERSION = rootPkg.version;
10
10
  const DRY_RUN = process.argv.includes('--dry-run');
11
+ const distTagIdx = process.argv.indexOf('--dist-tag');
12
+ const DIST_TAG = distTagIdx !== -1 ? process.argv[distTagIdx + 1] : (VERSION.includes('-') ? 'beta' : 'latest');
11
13
 
12
14
  const ALIASES = [
13
15
  // Formatting variants — no "ai", or hyphen/underscore separators
@@ -54,7 +56,7 @@ for (const name of ALIASES) {
54
56
  cpSync(join(__dirname, 'alias-proxy.js'), join(binDir, 'proxy.js'));
55
57
 
56
58
  if (DRY_RUN) {
57
- console.log(`[dry-run] Would publish ${name}@${VERSION}`);
59
+ console.log(`[dry-run] Would publish ${name}@${VERSION} (tag: ${DIST_TAG})`);
58
60
  console.log(JSON.stringify(pkg, null, 2));
59
61
  console.log('---');
60
62
  rmSync(tmpDir, { recursive: true, force: true });
@@ -63,7 +65,7 @@ for (const name of ALIASES) {
63
65
 
64
66
  console.log(`Publishing ${name}@${VERSION}...`);
65
67
  try {
66
- execSync('npm publish', { cwd: tmpDir, stdio: 'pipe' });
68
+ execSync(`npm publish --tag ${DIST_TAG}`, { cwd: tmpDir, stdio: 'pipe' });
67
69
  console.log(`Done: ${name}`);
68
70
  } catch (err) {
69
71
  const output = (err.stdout?.toString() ?? '') + (err.stderr?.toString() ?? '');
@@ -0,0 +1,18 @@
1
+ /**
2
+ * CliError — structured error for the failproofai CLI.
3
+ *
4
+ * Throw this for any error that should be reported to the user as a clean
5
+ * message (no stack trace). The exit code communicates the failure class:
6
+ *
7
+ * 1 — user error (bad args, unknown policy name, invalid flag)
8
+ * 2 — internal (file I/O failure, unexpected state)
9
+ */
10
+ export class CliError extends Error {
11
+ readonly exitCode: 1 | 2;
12
+
13
+ constructor(message: string, exitCode: 1 | 2 = 1) {
14
+ super(message);
15
+ this.name = "CliError";
16
+ this.exitCode = exitCode;
17
+ }
18
+ }
@@ -21,6 +21,7 @@ import { BUILTIN_POLICIES } from "./builtin-policies";
21
21
  import { loadCustomHooks } from "./custom-hooks-loader";
22
22
  import { trackHookEvent } from "./hook-telemetry";
23
23
  import { getInstanceId, hashToId } from "../../lib/telemetry-id";
24
+ import { CliError } from "../cli-error";
24
25
 
25
26
  const VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
26
27
 
@@ -67,7 +68,7 @@ function resolveFailproofaiBinary(): string {
67
68
  // `where` on Windows may return multiple lines; take the first
68
69
  return result.split("\n")[0].trim();
69
70
  } catch {
70
- throw new Error(
71
+ throw new CliError(
71
72
  "failproofai binary not found in PATH.\n" +
72
73
  "Install it globally first: npm install -g failproofai"
73
74
  );
@@ -85,7 +86,7 @@ function validatePolicyNames(names: string[]): void {
85
86
  const invalid = names.filter((n) => !VALID_POLICY_NAMES.has(n));
86
87
  if (invalid.length > 0) {
87
88
  const validList = [...VALID_POLICY_NAMES].join(", ");
88
- throw new Error(
89
+ throw new CliError(
89
90
  `Unknown policy name(s): ${invalid.join(", ")}\n` +
90
91
  `Valid policies: ${validList}`
91
92
  );
@@ -185,6 +186,20 @@ export async function installHooks(
185
186
  customPoliciesPath?: string,
186
187
  removeCustomHooks = false,
187
188
  ): Promise<void> {
189
+ // Validate user input first before any system checks
190
+ if (policyNames !== undefined && policyNames.length > 0) {
191
+ const nonAllNames = policyNames.filter((n) => n !== "all");
192
+ // Check unknown names first (most actionable error for the user)
193
+ if (nonAllNames.length > 0) validatePolicyNames(nonAllNames);
194
+ // Then check if "all" is mixed with valid specific names
195
+ if (policyNames.includes("all") && nonAllNames.length > 0) {
196
+ throw new CliError(
197
+ `"all" cannot be combined with specific policy names.\n` +
198
+ `Use either: --install all or --install block-sudo sanitize-jwt ...`
199
+ );
200
+ }
201
+ }
202
+
188
203
  const binaryPath = resolveFailproofaiBinary();
189
204
 
190
205
  // Capture existing config before overwriting (used for telemetry diff)
@@ -201,7 +216,6 @@ export async function installHooks(
201
216
  .filter((p) => includeBeta || !p.beta)
202
217
  .map((p) => p.name);
203
218
  } else {
204
- if (policyNames.length > 0) validatePolicyNames(policyNames);
205
219
  incoming = policyNames;
206
220
  }
207
221
  // Additive: union with whatever was already enabled, deduplicated.