failproofai 1.0.2-beta.5 → 1.0.2-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/audit/run/route.js +3 -3
  23. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  31. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/index.html +1 -1
  34. package/.next/standalone/.next/server/app/index.rsc +14 -14
  35. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  36. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  37. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  38. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  39. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  42. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  55. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  58. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1zkek3z._.js → [root-of-the-server]__1tg4pu6._.js} +1 -1
  59. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -1
  60. package/.next/standalone/.next/server/chunks/{_12moi5-._.js → _19uwam-._.js} +1 -1
  61. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +7 -7
  62. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  66. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  68. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07gm3zl._.js +5 -5
  69. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  70. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  71. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  73. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  74. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0stn0gx._.js → [root-of-the-server]__128se8m._.js} +2 -2
  75. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  76. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/ssr/{_1km9-gn._.js → _0no96tu._.js} +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/{_1kesqs-._.js → _11fc-hb._.js} +2 -2
  79. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  85. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  86. package/.next/standalone/.next/server/pages/404.html +1 -1
  87. package/.next/standalone/.next/server/pages/500.html +1 -1
  88. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  89. package/.next/standalone/.next/server/server-reference-manifest.json +19 -19
  90. package/.next/standalone/.next/static/chunks/{0tyt-3a5rtzy8.js → 0931avh-06lj5.js} +1 -1
  91. package/.next/standalone/.next/static/chunks/{36-8i_vbkh85w.js → 0h-3zffoy-_ji.js} +1 -1
  92. package/.next/standalone/.next/static/chunks/{21r7iw3rmixf5.js → 0p84npzxdgpc5.js} +1 -1
  93. package/.next/standalone/.next/static/chunks/{2hwc8e923g5df.js → 0sdgnflvw8kta.js} +1 -1
  94. package/.next/standalone/.next/static/chunks/{2_27jraat038x.js → 0z7au3njlak0j.js} +1 -1
  95. package/.next/standalone/.next/static/chunks/{391tpdqcas7rq.js → 1id7o44jjegf0.js} +1 -1
  96. package/.next/standalone/.next/static/chunks/{3_b73tc78jwsq.js → 276n-edifojbd.js} +2 -2
  97. package/.next/standalone/.next/static/chunks/{26lfjhb2kzzgb.js → 2jfskwd2u8o5r.js} +1 -1
  98. package/.next/standalone/.next/static/chunks/{3jqm8ubpv3i-4.js → 2pxr7nr1nyotn.js} +1 -1
  99. package/.next/standalone/.next/static/chunks/{33np9nrh1mvj9.js → 3vu_7_eor1m-k.js} +1 -1
  100. package/.next/standalone/package.json +9 -9
  101. package/.next/standalone/server.js +1 -1
  102. package/bin/failproofai.mjs +93 -26
  103. package/dist/cli.mjs +791 -588
  104. package/dist/worker.mjs +2 -2
  105. package/package.json +9 -9
  106. package/src/audit/cli.ts +12 -0
  107. package/src/audit/index.ts +33 -2
  108. package/src/hooks/configure-wizard.ts +112 -18
  109. package/src/hooks/fp-reset.ts +1 -1
  110. package/src/hooks/handler.ts +13 -1
  111. package/src/hooks/manager.ts +19 -1
  112. package/src/hooks/onboarding-attempt.ts +4 -4
  113. package/src/hooks/pack-cli.ts +162 -21
  114. package/src/hooks/pack-store.ts +21 -8
  115. package/src/hooks/read-stdin.ts +78 -8
  116. /package/.next/standalone/.next/static/{59jsH11LoyXu9UBoHvgGM → mAYN9BOxWVFREnVp3yCHp}/_buildManifest.js +0 -0
  117. /package/.next/standalone/.next/static/{59jsH11LoyXu9UBoHvgGM → mAYN9BOxWVFREnVp3yCHp}/_clientMiddlewareManifest.js +0 -0
  118. /package/.next/standalone/.next/static/{59jsH11LoyXu9UBoHvgGM → mAYN9BOxWVFREnVp3yCHp}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -19,7 +19,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  // package.json
22
- var version2 = "1.0.2-beta.5", package_default;
22
+ var version2 = "1.0.2-beta.6", package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "failproofai",
@@ -143,16 +143,16 @@ var init_package = __esm(() => {
143
143
  sharp: "0.35.0"
144
144
  },
145
145
  optionalDependencies: {
146
- "@failproofai/failproofaid-linux-x64": "1.0.2-beta.5",
147
- "@failproofai/failproofaid-linux-arm64": "1.0.2-beta.5",
148
- "@failproofai/failproofaid-darwin-x64": "1.0.2-beta.5",
149
- "@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.5"
146
+ "@failproofai/failproofaid-linux-x64": "1.0.2-beta.6",
147
+ "@failproofai/failproofaid-linux-arm64": "1.0.2-beta.6",
148
+ "@failproofai/failproofaid-darwin-x64": "1.0.2-beta.6",
149
+ "@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.6"
150
150
  },
151
151
  failproofaidBinaries: {
152
- "linux-x64": "e19a1cf9a254df830f205fac024b8a229c162c93ae73851b985d2794e09da035",
153
- "linux-arm64": "fafcff01e4cce00809b5cd9ff2822705d7373acbab79444e0384cb058b6da921",
154
- "darwin-x64": "d7bd609387e13f5e75287674d948c69d739bac8d6963aa731b93111d1bdd2ea2",
155
- "darwin-arm64": "9a57c8eb0f23ed25328e0f7b06638a6ab7f51e1deebf479bac2c779bf9912941"
152
+ "linux-x64": "1e883d50ea40dad46394a137fab9b8ecddb7b37064312af9ddf89cb3de5eb7b8",
153
+ "linux-arm64": "47a51a75a01c021417a4d67fb7eb5caa8fa9c46abca5ec85d23e8ddead1ebfdd",
154
+ "darwin-x64": "bbc4e714c1e961d53d7dd6c65b84dcc74b4e24439349ebdef3f7d8681ece1776",
155
+ "darwin-arm64": "ca6f6e688070a2d2a63fd333f7c3ac7c7d2bd991ca499cfced23b249e7ca684b"
156
156
  }
157
157
  };
158
158
  });
@@ -937,35 +937,50 @@ var exports_read_stdin = {};
937
937
  __export(exports_read_stdin, {
938
938
  readStdinPayload: () => readStdinPayload
939
939
  });
940
- async function readStdinPayload(maxBytes = 1048576) {
940
+ async function readStdinPayload(maxBytes = 1048576, timeoutMs = STDIN_TIMEOUT_MS, stream = process.stdin) {
941
941
  let payload = "";
942
942
  let oversized = false;
943
943
  let readError = false;
944
+ let timedOut = false;
945
+ if (stream.isTTY) {
946
+ return { payload: "", oversized: false, readError: false, timedOut: false };
947
+ }
948
+ let timer;
944
949
  try {
945
950
  payload = await new Promise((resolve2, reject) => {
946
951
  const chunks = [];
947
952
  let totalBytes = 0;
948
- process.stdin.setEncoding("utf8");
949
- process.stdin.on("data", (chunk) => {
953
+ stream.setEncoding("utf8");
954
+ stream.on("data", (chunk) => {
950
955
  totalBytes += Buffer.byteLength(chunk);
951
956
  if (totalBytes > maxBytes) {
952
957
  oversized = true;
953
- process.stdin.destroy();
958
+ stream.destroy?.();
954
959
  resolve2("");
955
960
  return;
956
961
  }
957
962
  chunks.push(chunk);
958
963
  });
959
- process.stdin.on("end", () => resolve2(chunks.join("")));
960
- process.stdin.on("error", reject);
961
- if (process.stdin.readableEnded)
964
+ stream.on("end", () => resolve2(chunks.join("")));
965
+ stream.on("error", reject);
966
+ if (stream.readableEnded)
962
967
  resolve2("");
968
+ timer = setTimeout(() => {
969
+ timedOut = true;
970
+ stream.destroy?.();
971
+ resolve2("");
972
+ }, timeoutMs);
973
+ timer.unref?.();
963
974
  });
964
975
  } catch {
965
976
  readError = true;
977
+ } finally {
978
+ if (timer)
979
+ clearTimeout(timer);
966
980
  }
967
- return { payload, oversized, readError };
981
+ return { payload, oversized, readError, timedOut };
968
982
  }
983
+ var STDIN_TIMEOUT_MS = 1e4;
969
984
 
970
985
  // src/hooks/types.ts
971
986
  var HOOK_SCOPES, INTEGRATION_TYPES, CODEX_HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_HOOK_SCOPES, HERMES_HOOK_EVENT_TYPES, HERMES_EVENT_MAP, COPILOT_HOOK_SCOPES, COPILOT_HOOK_EVENT_TYPES, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_HOOK_SCOPES, CURSOR_HOOK_EVENT_TYPES, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_HOOK_SCOPES, OPENCODE_HOOK_EVENT_TYPES, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_HOOK_SCOPES, PI_HOOK_EVENT_TYPES, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_HOOK_SCOPES, OPENCLAW_HOOK_EVENT_TYPES, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_HOOK_SCOPES, FACTORY_HOOK_EVENT_TYPES, FACTORY_TOOL_MAP, DEVIN_HOOK_SCOPES, DEVIN_HOOK_EVENT_TYPES, DEVIN_TOOL_MAP, ANTIGRAVITY_HOOK_SCOPES, ANTIGRAVITY_HOOK_EVENT_TYPES, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_HOOK_SCOPES, GOOSE_HOOK_EVENT_TYPES, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES, FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
@@ -9934,35 +9949,50 @@ var init_resolve_transcript_path = __esm(() => {
9934
9949
  });
9935
9950
 
9936
9951
  // src/hooks/read-stdin.ts
9937
- async function readStdinPayload2(maxBytes = 1048576) {
9952
+ async function readStdinPayload2(maxBytes = 1048576, timeoutMs = STDIN_TIMEOUT_MS2, stream = process.stdin) {
9938
9953
  let payload = "";
9939
9954
  let oversized = false;
9940
9955
  let readError = false;
9956
+ let timedOut = false;
9957
+ if (stream.isTTY) {
9958
+ return { payload: "", oversized: false, readError: false, timedOut: false };
9959
+ }
9960
+ let timer;
9941
9961
  try {
9942
9962
  payload = await new Promise((resolve11, reject) => {
9943
9963
  const chunks = [];
9944
9964
  let totalBytes = 0;
9945
- process.stdin.setEncoding("utf8");
9946
- process.stdin.on("data", (chunk) => {
9965
+ stream.setEncoding("utf8");
9966
+ stream.on("data", (chunk) => {
9947
9967
  totalBytes += Buffer.byteLength(chunk);
9948
9968
  if (totalBytes > maxBytes) {
9949
9969
  oversized = true;
9950
- process.stdin.destroy();
9970
+ stream.destroy?.();
9951
9971
  resolve11("");
9952
9972
  return;
9953
9973
  }
9954
9974
  chunks.push(chunk);
9955
9975
  });
9956
- process.stdin.on("end", () => resolve11(chunks.join("")));
9957
- process.stdin.on("error", reject);
9958
- if (process.stdin.readableEnded)
9976
+ stream.on("end", () => resolve11(chunks.join("")));
9977
+ stream.on("error", reject);
9978
+ if (stream.readableEnded)
9979
+ resolve11("");
9980
+ timer = setTimeout(() => {
9981
+ timedOut = true;
9982
+ stream.destroy?.();
9959
9983
  resolve11("");
9984
+ }, timeoutMs);
9985
+ timer.unref?.();
9960
9986
  });
9961
9987
  } catch {
9962
9988
  readError = true;
9989
+ } finally {
9990
+ if (timer)
9991
+ clearTimeout(timer);
9963
9992
  }
9964
- return { payload, oversized, readError };
9993
+ return { payload, oversized, readError, timedOut };
9965
9994
  }
9995
+ var STDIN_TIMEOUT_MS2 = 1e4;
9966
9996
 
9967
9997
  // src/hooks/cloud-managed-policies.ts
9968
9998
  import { createHash as createHash3 } from "node:crypto";
@@ -12009,7 +12039,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
12009
12039
  const legacyNames = activePause || packsInstalledHere ? [] : config.enabledPolicies;
12010
12040
  registerBuiltinPolicies(activePause ? [] : legacyNames);
12011
12041
  if (legacyNames.length > 0) {
12012
- hookLogWarn(`enforcing ${legacyNames.length} policies from this build because no pack is installed — ` + `run \`failproofai policies add core\` to move them into a real pack`);
12042
+ hookLogWarn(`enforcing ${legacyNames.length} policies from this build because no pack is installed — ` + `run \`failproofai policies add FailproofAI/policies\` to move them into a real pack`);
12013
12043
  }
12014
12044
  const enabledBuiltinNames = new Set(legacyNames);
12015
12045
  let cloudManagedPolicies = [];
@@ -12251,6 +12281,14 @@ async function handleHookEvent(eventType, cli = "claude") {
12251
12281
  error_type: "oversized"
12252
12282
  });
12253
12283
  }
12284
+ if (stdinRead.timedOut) {
12285
+ hookLogWarn(`stdin never closed for ${eventType}; evaluated nothing`);
12286
+ trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
12287
+ event_type: eventType,
12288
+ cli,
12289
+ error_type: "timeout"
12290
+ });
12291
+ }
12254
12292
  const result = await evaluateHookEvent(eventType, cli, stdinRead.payload);
12255
12293
  try {
12256
12294
  if (canonicalizeEventType(eventType, cli) === "SessionStart") {
@@ -14555,6 +14593,426 @@ var init_first_run_gate = __esm(() => {
14555
14593
  ];
14556
14594
  });
14557
14595
 
14596
+ // src/hooks/pack-store.ts
14597
+ import { createHash as createHash7 } from "node:crypto";
14598
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as readFileSync21, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync14 } from "node:fs";
14599
+ import { dirname as dirname13, resolve as resolve20 } from "node:path";
14600
+ function baseUrl2() {
14601
+ return (process.env.FAILPROOFAI_PACK_BASE_URL || DEFAULT_BASE_URL2).replace(/\/+$/, "");
14602
+ }
14603
+ function parsePackSpec(source) {
14604
+ const raw = source.trim();
14605
+ if (raw.length === 0)
14606
+ throw new Error("pack source is empty");
14607
+ const url = raw.match(/^(?:https?:\/\/)?(?:www\.)?github\.com\/(.+)$/i);
14608
+ if (url) {
14609
+ const parts = url[1].replace(/\/+$/, "").split("/");
14610
+ const [owner, repo, ...rest] = parts;
14611
+ let tag2 = null;
14612
+ if (rest[0] === "releases" && (rest[1] === "tag" || rest[1] === "download")) {
14613
+ const tail = rest.slice(2);
14614
+ tag2 = (rest[1] === "download" ? tail.slice(0, -1) : tail).join("/") || null;
14615
+ } else if (rest[0] === "releases" && rest[1] === "latest") {
14616
+ tag2 = null;
14617
+ }
14618
+ return validated(owner, repo, tag2);
14619
+ }
14620
+ const withoutScheme = raw.replace(/^github:/i, "");
14621
+ const at2 = withoutScheme.lastIndexOf("@");
14622
+ const repoPart = at2 > 0 ? withoutScheme.slice(0, at2) : withoutScheme;
14623
+ const tag = at2 > 0 ? withoutScheme.slice(at2 + 1) : null;
14624
+ const slash = repoPart.indexOf("/");
14625
+ if (slash <= 0 || slash === repoPart.length - 1) {
14626
+ if (RETIRED_CORE_ALIASES.has(repoPart.trim().toLowerCase())) {
14627
+ throw new Error(`${JSON.stringify(source)} is no longer a pack name — ours is a pack like anyone else's now. Use ${CORE_SOURCE} instead.`);
14628
+ }
14629
+ throw new Error(`pack source must be owner/repo, owner/repo@tag, or a github.com URL — got ${JSON.stringify(source)}`);
14630
+ }
14631
+ return validated(repoPart.slice(0, slash), repoPart.slice(slash + 1), tag);
14632
+ }
14633
+ function validated(owner, repo, tag) {
14634
+ if (!owner || !OWNER_RE.test(owner))
14635
+ throw new Error(`unsafe owner ${JSON.stringify(owner)}`);
14636
+ if (!repo || !OWNER_RE.test(repo))
14637
+ throw new Error(`unsafe repo ${JSON.stringify(repo)}`);
14638
+ if (tag !== null && !TAG_RE.test(tag))
14639
+ throw new Error(`unsafe tag ${JSON.stringify(tag)}`);
14640
+ return { owner, repo, tag };
14641
+ }
14642
+ async function resolveLatestTag(spec) {
14643
+ const url = `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/latest`;
14644
+ const response = await fetch(url, {
14645
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
14646
+ redirect: "manual"
14647
+ });
14648
+ const location = response.headers.get("location");
14649
+ if (!location) {
14650
+ throw new Error(`could not resolve the newest release of ${spec.owner}/${spec.repo} ` + `(GET ${url} returned ${response.status} with no redirect). That redirect is issued only ` + `for a published, non-prerelease release, so either the repository has no releases at all ` + `or every release it has is a prerelease or a draft. Name a tag explicitly.`);
14651
+ }
14652
+ const match = location.match(/\/releases\/tag\/(.+)$/);
14653
+ if (!match)
14654
+ throw new Error(`unexpected redirect from ${url}: ${location}`);
14655
+ const tag = decodeURIComponent(match[1]).replace(/\/+$/, "");
14656
+ if (!TAG_RE.test(tag))
14657
+ throw new Error(`unsafe tag ${JSON.stringify(tag)} from ${url}`);
14658
+ return tag;
14659
+ }
14660
+ function formatPackSpec(spec) {
14661
+ return `github:${spec.owner}/${spec.repo}@${spec.tag}`;
14662
+ }
14663
+ function packAssetUrl(spec, asset) {
14664
+ return `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/download/${spec.tag}/${asset}`;
14665
+ }
14666
+ function packTagMatchesVersion(tag, version3) {
14667
+ const tail = tag.slice(tag.lastIndexOf("/") + 1);
14668
+ return tail === version3 || tail === `v${version3}`;
14669
+ }
14670
+ async function fetchBytes2(url) {
14671
+ const response = await fetch(url, {
14672
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
14673
+ redirect: "follow"
14674
+ });
14675
+ if (!response.ok)
14676
+ throw new Error(`GET ${url} returned ${response.status}`);
14677
+ const contentLength = response.headers.get("content-length");
14678
+ if (contentLength !== null) {
14679
+ const declared = Number(contentLength);
14680
+ if (Number.isFinite(declared) && declared > MAX_ARTIFACT_BYTES) {
14681
+ await response.body?.cancel();
14682
+ throw new Error(`${url} declares ${declared} bytes, over the ${MAX_ARTIFACT_BYTES} limit`);
14683
+ }
14684
+ }
14685
+ if (!response.body)
14686
+ return Buffer.alloc(0);
14687
+ const chunks = [];
14688
+ const reader = response.body.getReader();
14689
+ let total = 0;
14690
+ try {
14691
+ while (true) {
14692
+ const { done, value } = await reader.read();
14693
+ if (done)
14694
+ break;
14695
+ total += value.byteLength;
14696
+ if (total > MAX_ARTIFACT_BYTES) {
14697
+ await reader.cancel();
14698
+ throw new Error(`${url} is over the ${MAX_ARTIFACT_BYTES} byte limit`);
14699
+ }
14700
+ chunks.push(Buffer.from(value));
14701
+ }
14702
+ } finally {
14703
+ reader.releaseLock();
14704
+ }
14705
+ return Buffer.concat(chunks, total);
14706
+ }
14707
+ function digestFor2(manifest, assetName) {
14708
+ for (const line of manifest.split(`
14709
+ `)) {
14710
+ const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
14711
+ if (match && match[2].trim() === assetName)
14712
+ return match[1].toLowerCase();
14713
+ }
14714
+ return null;
14715
+ }
14716
+ function sha256(bytes) {
14717
+ return createHash7("sha256").update(bytes).digest("hex");
14718
+ }
14719
+ function writeAtomic(target, bytes) {
14720
+ const tempPath = `${target}.${process.pid}.tmp`;
14721
+ try {
14722
+ mkdirSync16(dirname13(target), { recursive: true });
14723
+ writeFileSync14(tempPath, bytes);
14724
+ renameSync8(tempPath, target);
14725
+ } catch (err) {
14726
+ rmSync9(tempPath, { force: true });
14727
+ throw err;
14728
+ }
14729
+ }
14730
+ async function fetchPackPreview(source) {
14731
+ if (process.env.FAILPROOFAI_NO_DOWNLOAD === "1") {
14732
+ throw new Error("FAILPROOFAI_NO_DOWNLOAD is set, so nothing can be fetched to preview");
14733
+ }
14734
+ const parsed = parsePackSpec(source);
14735
+ const resolvedFromLatest = parsed.tag === null;
14736
+ const spec = parsed.tag !== null ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
14737
+ const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
14738
+ const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
14739
+ const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
14740
+ if (!manifestDigest) {
14741
+ throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
14742
+ }
14743
+ if (sha256(manifestBytes) !== manifestDigest) {
14744
+ throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
14745
+ }
14746
+ let raw;
14747
+ try {
14748
+ raw = JSON.parse(manifestBytes.toString("utf8"));
14749
+ } catch (err) {
14750
+ throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
14751
+ }
14752
+ if (!raw || typeof raw !== "object")
14753
+ throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
14754
+ const value = raw;
14755
+ const identity = parsePackIdentity(value);
14756
+ if (!Array.isArray(value.policies) || value.policies.length === 0) {
14757
+ throw new Error("pack manifest declares no policies");
14758
+ }
14759
+ return {
14760
+ id: identity.id,
14761
+ version: identity.version,
14762
+ effect: identity.effect,
14763
+ policies: value.policies.map((policy, i) => parsePackPolicy(identity.id, policy, i)),
14764
+ source: formatPackSpec(spec),
14765
+ resolvedFromLatest
14766
+ };
14767
+ }
14768
+ function priorRecordFor(id, sha2562) {
14769
+ const installed = readInstalledPacks().packs;
14770
+ return installed.find((p) => p.id === id) ?? installed.find((p) => p.sha256 === sha2562);
14771
+ }
14772
+ async function fetchPack(spec) {
14773
+ const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
14774
+ const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
14775
+ const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
14776
+ if (!manifestDigest)
14777
+ throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
14778
+ if (sha256(manifestBytes) !== manifestDigest) {
14779
+ throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
14780
+ }
14781
+ const artifact = await fetchBytes2(packAssetUrl(spec, PACK_ENTRY_ASSET));
14782
+ const entryDigest = digestFor2(checksums, PACK_ENTRY_ASSET);
14783
+ if (!entryDigest)
14784
+ throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_ENTRY_ASSET}`);
14785
+ const artifactDigest = sha256(artifact);
14786
+ if (artifactDigest !== entryDigest) {
14787
+ throw new Error(`${PACK_ENTRY_ASSET} failed integrity verification`);
14788
+ }
14789
+ let parsed;
14790
+ try {
14791
+ parsed = JSON.parse(manifestBytes.toString("utf8"));
14792
+ } catch (err) {
14793
+ throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
14794
+ }
14795
+ if (!parsed || typeof parsed !== "object")
14796
+ throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
14797
+ const raw = parsed;
14798
+ const identity = parsePackIdentity(raw);
14799
+ if (!Array.isArray(raw.policies) || raw.policies.length === 0) {
14800
+ throw new Error("pack manifest declares no policies");
14801
+ }
14802
+ const policies = raw.policies.map((p, i) => parsePackPolicy(identity.id, p, i));
14803
+ return {
14804
+ id: identity.id,
14805
+ version: identity.version,
14806
+ policies,
14807
+ ...raw.effect !== undefined ? { effect: identity.effect } : {},
14808
+ artifact,
14809
+ artifactDigest
14810
+ };
14811
+ }
14812
+ function slugifyCategory(category) {
14813
+ return category.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
14814
+ }
14815
+ function resolveSelection(policies, opts, previous, previouslyInstalled) {
14816
+ const available = policies.map((p) => p.name);
14817
+ if (opts?.all)
14818
+ return { enabled: null, reason: "all" };
14819
+ const picked = new Set;
14820
+ if (opts?.categories?.length) {
14821
+ const wanted = new Set(opts.categories.map(slugifyCategory));
14822
+ const known = new Map(policies.map((p) => [slugifyCategory(p.category), p.category]));
14823
+ const unknown = [...wanted].filter((c) => !known.has(c));
14824
+ if (unknown.length > 0) {
14825
+ throw new Error(`no such categor${unknown.length === 1 ? "y" : "ies"}: ${unknown.join(", ")} ` + `(has: ${[...new Set(policies.map((p) => slugifyCategory(p.category)))].join(", ")})`);
14826
+ }
14827
+ for (const p of policies)
14828
+ if (wanted.has(slugifyCategory(p.category)))
14829
+ picked.add(p.name);
14830
+ }
14831
+ if (opts?.only?.length) {
14832
+ const unknown = opts.only.filter((n) => !available.includes(n));
14833
+ if (unknown.length > 0) {
14834
+ throw new Error(`pack does not contain ${unknown.join(", ")}`);
14835
+ }
14836
+ for (const n of opts.only)
14837
+ picked.add(n);
14838
+ }
14839
+ if (opts?.only !== undefined || opts?.categories?.length) {
14840
+ return { enabled: available.filter((n) => picked.has(n)), reason: "selected" };
14841
+ }
14842
+ if (previouslyInstalled) {
14843
+ return { enabled: previous === null ? null : previous ?? null, reason: "carried" };
14844
+ }
14845
+ return { enabled: policies.filter((p) => p.defaultEnabled).map((p) => p.name), reason: "defaults" };
14846
+ }
14847
+ async function verifyArtifactRegisters(artifactPath, declared) {
14848
+ const { loadCustomHooks: loadCustomHooks2 } = await Promise.resolve().then(() => (init_custom_hooks_loader(), exports_custom_hooks_loader));
14849
+ let registered;
14850
+ try {
14851
+ registered = (await loadCustomHooks2(artifactPath, { strict: true })).map((h) => h.name);
14852
+ } catch (err) {
14853
+ throw new Error(`its artifact could not be loaded: ${err instanceof Error ? err.message : String(err)}`);
14854
+ }
14855
+ const missing = declared.filter((name) => !registered.includes(name));
14856
+ const extra = registered.filter((name) => !declared.includes(name));
14857
+ if (missing.length > 0) {
14858
+ throw new Error(`its manifest declares ${missing.join(", ")} but the artifact does not register ${missing.length === 1 ? "it" : "them"} — that would install as protection that never runs`);
14859
+ }
14860
+ if (extra.length > 0) {
14861
+ throw new Error(`its artifact registers undeclared ${extra.join(", ")} — a policy that enforces but ` + `appears in no listing cannot be reviewed or switched off`);
14862
+ }
14863
+ }
14864
+ async function checkPackArtifact(artifactPath, declared) {
14865
+ try {
14866
+ await verifyArtifactRegisters(artifactPath, declared);
14867
+ return null;
14868
+ } catch (err) {
14869
+ return err instanceof Error ? err.message : String(err);
14870
+ }
14871
+ }
14872
+ async function addPack(source, opts) {
14873
+ if (process.env.FAILPROOFAI_NO_DOWNLOAD) {
14874
+ throw new Error("pack downloads are disabled (FAILPROOFAI_NO_DOWNLOAD). Already-installed packs keep enforcing.");
14875
+ }
14876
+ const parsed = parsePackSpec(source);
14877
+ const spec = parsed.tag ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
14878
+ const resolvedFromLatest = parsed.tag === null;
14879
+ const fetched = await fetchPack(spec);
14880
+ if (!packTagMatchesVersion(spec.tag, fetched.version)) {
14881
+ const fromTag = spec.tag.slice(spec.tag.lastIndexOf("/") + 1).replace(/^v/, "");
14882
+ const fix = `re-tag the release ${fetched.version} (a leading v is fine), or rebuild the pack with --version ${fromTag}`;
14883
+ throw new Error(`${fetched.id} was not installed: release tag ${spec.tag} does not match the version its manifest declares (${fetched.version}). ` + (resolvedFromLatest ? `That is the newest release of ${spec.owner}/${spec.repo}, so the release and the pack ` + `disagree at the source — ask the publisher to ${fix}.` : `Name the tag whose manifest says ${fetched.version}, or fix the pack: ${fix}.`));
14884
+ }
14885
+ const available = fetched.policies.map((p) => p.name);
14886
+ const prior = priorRecordFor(fetched.id, fetched.artifactDigest);
14887
+ const { enabled, reason } = resolveSelection(fetched.policies, opts, prior?.enabled ? prior.enabled.filter((n) => available.includes(n)) : prior?.enabled, Boolean(prior));
14888
+ const root = packsRoot();
14889
+ const artifactRel = `artifacts/${fetched.artifactDigest}.mjs`;
14890
+ const artifactAbs = resolve20(root, artifactRel);
14891
+ if (!existsSync25(artifactAbs))
14892
+ writeAtomic(artifactAbs, fetched.artifact);
14893
+ try {
14894
+ await verifyArtifactRegisters(artifactAbs, available);
14895
+ } catch (err) {
14896
+ throw new Error(`${fetched.id}@${fetched.version} was not installed: ${err instanceof Error ? err.message : String(err)}`);
14897
+ }
14898
+ const repoOf = (source2) => {
14899
+ const at2 = source2.lastIndexOf("@");
14900
+ return at2 > source2.indexOf(":") ? source2.slice(0, at2) : source2;
14901
+ };
14902
+ if (prior && !prior.source.startsWith("bundled:") && repoOf(prior.source) !== repoOf(formatPackSpec(spec))) {
14903
+ throw new Error(`pack id ${fetched.id} is already installed from ${prior.source}. Refusing to replace it with ${formatPackSpec(spec)} — remove it first if that is what you mean.`);
14904
+ }
14905
+ const record = {
14906
+ id: fetched.id,
14907
+ version: fetched.version,
14908
+ source: formatPackSpec(spec),
14909
+ entry: artifactRel,
14910
+ sha256: fetched.artifactDigest,
14911
+ policies: fetched.policies,
14912
+ ...fetched.effect ? { effect: fetched.effect } : {},
14913
+ ...enabled ? { enabled } : {},
14914
+ ...opts?.clis ? { clis: opts.clis } : {}
14915
+ };
14916
+ const absorbed = upsertInstalled(record);
14917
+ return {
14918
+ replaced: absorbed,
14919
+ id: fetched.id,
14920
+ version: fetched.version,
14921
+ source: record.source,
14922
+ tag: spec.tag,
14923
+ resolvedFromLatest,
14924
+ enabled: enabled ?? available,
14925
+ available,
14926
+ selection: reason,
14927
+ categories: [...new Set(fetched.policies.map((p) => slugifyCategory(p.category)))],
14928
+ artifact: artifactAbs
14929
+ };
14930
+ }
14931
+ function setPackPolicyEnabled(packId, name, on) {
14932
+ const manifestPath = resolve20(packsRoot(), "installed.json");
14933
+ if (!existsSync25(manifestPath))
14934
+ return { ok: false, reason: "no packs are installed" };
14935
+ let raw;
14936
+ try {
14937
+ raw = JSON.parse(readFileSync21(manifestPath, "utf8"));
14938
+ } catch {
14939
+ return { ok: false, reason: `pack manifest at ${manifestPath} is unreadable` };
14940
+ }
14941
+ const packs = Array.isArray(raw.packs) ? raw.packs : [];
14942
+ const pack = packs.find((p) => p?.id === packId);
14943
+ if (!pack)
14944
+ return { ok: false, reason: `no installed pack with id ${packId}` };
14945
+ const declared = Array.isArray(pack.policies) ? pack.policies : [];
14946
+ const available = declared.map((p) => typeof p?.name === "string" ? p.name : null).filter((n) => n !== null);
14947
+ if (!available.includes(name)) {
14948
+ return { ok: false, reason: `pack ${packId} declares no policy named ${name}` };
14949
+ }
14950
+ const current = pack.enabled ?? available;
14951
+ const next = on ? [...new Set([...current, name])] : current.filter((n) => n !== name);
14952
+ pack.enabled = available.filter((n) => next.includes(n));
14953
+ writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs }, null, 2) + `
14954
+ `);
14955
+ return { ok: true };
14956
+ }
14957
+ function removePack(id) {
14958
+ const manifestPath = resolve20(packsRoot(), "installed.json");
14959
+ if (!existsSync25(manifestPath))
14960
+ return false;
14961
+ let raw;
14962
+ try {
14963
+ raw = JSON.parse(readFileSync21(manifestPath, "utf8"));
14964
+ } catch {
14965
+ return false;
14966
+ }
14967
+ const packs = Array.isArray(raw.packs) ? raw.packs : [];
14968
+ const remaining = packs.filter((p) => p?.id !== id);
14969
+ if (remaining.length === packs.length)
14970
+ return false;
14971
+ writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
14972
+ `);
14973
+ return true;
14974
+ }
14975
+ function upsertInstalled(record) {
14976
+ const manifestPath = resolve20(packsRoot(), "installed.json");
14977
+ let packs = [];
14978
+ if (existsSync25(manifestPath)) {
14979
+ try {
14980
+ const raw = JSON.parse(readFileSync21(manifestPath, "utf8"));
14981
+ if (Array.isArray(raw.packs))
14982
+ packs = raw.packs;
14983
+ } catch {
14984
+ throw new Error(`existing pack manifest at ${manifestPath} is unreadable; move it aside and re-add`);
14985
+ }
14986
+ }
14987
+ const absorbed = packs.filter((p) => p?.sha256 === record.sha256 && p?.id !== record.id).map((p) => p.id);
14988
+ if (!record.enabled) {
14989
+ const prior = packs.find((p) => absorbed.includes(p?.id));
14990
+ const carried = prior?.enabled?.filter((n) => record.policies?.some((rp) => rp?.name === n));
14991
+ if (carried && carried.length > 0)
14992
+ record.enabled = carried;
14993
+ }
14994
+ const remaining = packs.filter((p) => !absorbed.includes(p?.id));
14995
+ const idx = remaining.findIndex((p) => p?.id === record.id);
14996
+ if (idx >= 0)
14997
+ remaining[idx] = record;
14998
+ else
14999
+ remaining.push(record);
15000
+ writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
15001
+ `);
15002
+ return absorbed;
15003
+ }
15004
+ function errText2(err) {
15005
+ return err instanceof Error ? err.message : String(err);
15006
+ }
15007
+ var DEFAULT_BASE_URL2 = "https://github.com", FETCH_TIMEOUT_MS = 30000, MAX_ARTIFACT_BYTES, PACK_MANIFEST_ASSET = "failproofai-pack.json", PACK_ENTRY_ASSET = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET = "SHA256SUMS", OWNER_RE, TAG_RE, CORE_SOURCE = "FailproofAI/policies", RETIRED_CORE_ALIASES;
15008
+ var init_pack_store = __esm(() => {
15009
+ init_pack_manifest();
15010
+ MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
15011
+ OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
15012
+ TAG_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
15013
+ RETIRED_CORE_ALIASES = new Set(["core", "failproofai", "official"]);
15014
+ });
15015
+
14558
15016
  // src/hooks/tui.ts
14559
15017
  import * as readline2 from "node:readline";
14560
15018
  function colorsEnabled2(out) {
@@ -14824,7 +15282,7 @@ function runPrompt2(p) {
14824
15282
  `${c.dim(BAR2)} ${c.dim(summary)}`
14825
15283
  ]);
14826
15284
  };
14827
- return new Promise((resolve20) => {
15285
+ return new Promise((resolve21) => {
14828
15286
  hideCursor2(stdout);
14829
15287
  repaint2(stdout, region, build());
14830
15288
  readline2.emitKeypressEvents(stdin);
@@ -14840,7 +15298,7 @@ function runPrompt2(p) {
14840
15298
  const finish = (result) => {
14841
15299
  cleanup();
14842
15300
  collapse(result);
14843
- resolve20(result);
15301
+ resolve21(result);
14844
15302
  };
14845
15303
  function onKey(_s, key) {
14846
15304
  if (!key)
@@ -14970,7 +15428,7 @@ function promptText2(opts) {
14970
15428
  return error ? { ok: false, error } : { ok: true, value };
14971
15429
  };
14972
15430
  if (!stdin.isTTY) {
14973
- return new Promise((resolve20) => {
15431
+ return new Promise((resolve21) => {
14974
15432
  let buf = "";
14975
15433
  const onData = (chunk) => {
14976
15434
  buf += String(chunk);
@@ -14983,13 +15441,13 @@ function promptText2(opts) {
14983
15441
  stdin.removeListener("end", done);
14984
15442
  const r = accept(buf.split(`
14985
15443
  `)[0] ?? "");
14986
- resolve20(r.ok ? r.value : null);
15444
+ resolve21(r.ok ? r.value : null);
14987
15445
  };
14988
15446
  stdin.on("data", onData);
14989
15447
  stdin.on("end", done);
14990
15448
  });
14991
15449
  }
14992
- return new Promise((resolve20) => {
15450
+ return new Promise((resolve21) => {
14993
15451
  let value = "";
14994
15452
  const draw = (error) => {
14995
15453
  const cols = stdout.columns || 80;
@@ -15019,14 +15477,14 @@ ${opts.prefix ?? " "}${truncate2(c.warn(error), cols - 3)}` : "";
15019
15477
  return;
15020
15478
  if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
15021
15479
  cleanup();
15022
- resolve20(null);
15480
+ resolve21(null);
15023
15481
  return;
15024
15482
  }
15025
15483
  if (key.name === "return" || key.name === "enter") {
15026
15484
  const r = accept(value);
15027
15485
  if (r.ok) {
15028
15486
  cleanup();
15029
- resolve20(r.value);
15487
+ resolve21(r.value);
15030
15488
  } else {
15031
15489
  draw(r.error);
15032
15490
  }
@@ -22376,31 +22834,31 @@ var init_dist = __esm(() => {
22376
22834
 
22377
22835
  // src/hooks/integrations.ts
22378
22836
  import { execSync as execSync5 } from "node:child_process";
22379
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync14, existsSync as existsSync25, mkdirSync as mkdirSync16, unlinkSync as unlinkSync3 } from "node:fs";
22380
- import { resolve as resolve20, dirname as dirname13 } from "node:path";
22837
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync15, existsSync as existsSync26, mkdirSync as mkdirSync17, unlinkSync as unlinkSync3 } from "node:fs";
22838
+ import { resolve as resolve21, dirname as dirname14 } from "node:path";
22381
22839
  import { fileURLToPath } from "node:url";
22382
22840
  import { homedir as homedir23 } from "node:os";
22383
22841
  function readJsonFile(path3) {
22384
- if (!existsSync25(path3))
22842
+ if (!existsSync26(path3))
22385
22843
  return {};
22386
- const raw = readFileSync21(path3, "utf8");
22844
+ const raw = readFileSync22(path3, "utf8");
22387
22845
  return JSON.parse(raw);
22388
22846
  }
22389
22847
  function writeJsonFile(path3, data) {
22390
- mkdirSync16(dirname13(path3), { recursive: true });
22391
- writeFileSync14(path3, JSON.stringify(data, null, 2) + `
22848
+ mkdirSync17(dirname14(path3), { recursive: true });
22849
+ writeFileSync15(path3, JSON.stringify(data, null, 2) + `
22392
22850
  `, "utf8");
22393
22851
  }
22394
22852
  function readYamlDoc(path3) {
22395
22853
  try {
22396
- return existsSync25(path3) ? $parseDocument(readFileSync21(path3, "utf8")) : $parseDocument("");
22854
+ return existsSync26(path3) ? $parseDocument(readFileSync22(path3, "utf8")) : $parseDocument("");
22397
22855
  } catch {
22398
22856
  return $parseDocument("");
22399
22857
  }
22400
22858
  }
22401
22859
  function writeYamlDoc(path3, doc) {
22402
- mkdirSync16(dirname13(path3), { recursive: true });
22403
- writeFileSync14(path3, doc.toString(), "utf8");
22860
+ mkdirSync17(dirname14(path3), { recursive: true });
22861
+ writeFileSync15(path3, doc.toString(), "utf8");
22404
22862
  }
22405
22863
  function isMarkedHook(hook) {
22406
22864
  if (!hook || typeof hook !== "object")
@@ -22443,7 +22901,7 @@ function isMarkedCopilotHook(hook) {
22443
22901
  return false;
22444
22902
  }
22445
22903
  function opencodePluginFilePath(settingsPath) {
22446
- return resolve20(dirname13(settingsPath), "plugins", "failproofai.mjs");
22904
+ return resolve21(dirname14(settingsPath), "plugins", "failproofai.mjs");
22447
22905
  }
22448
22906
  function buildOpenCodePluginShim(binaryPath, scope) {
22449
22907
  const useNpx = scope === "project";
@@ -22658,8 +23116,8 @@ export default async function failproofaiPlugin({ client, directory }) {
22658
23116
  function getPiExtensionPath() {
22659
23117
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
22660
23118
  if (fromEnv)
22661
- return resolve20(fromEnv, "pi-extension");
22662
- return resolve20(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
23119
+ return resolve21(fromEnv, "pi-extension");
23120
+ return resolve21(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
22663
23121
  }
22664
23122
  function isFailproofaiPiEntry(source) {
22665
23123
  if (typeof source !== "string")
@@ -22670,8 +23128,8 @@ function isFailproofaiPiEntry(source) {
22670
23128
  }
22671
23129
  function makePiProjectRelativeEntry(extPath) {
22672
23130
  const cwd = process.cwd();
22673
- const cwdResolved = resolve20(cwd);
22674
- const extResolved = resolve20(extPath);
23131
+ const cwdResolved = resolve21(cwd);
23132
+ const extResolved = resolve21(extPath);
22675
23133
  if (extResolved.startsWith(cwdResolved + "/") || extResolved === cwdResolved) {
22676
23134
  const fromSettingsDir = "../" + extResolved.slice(cwdResolved.length + 1);
22677
23135
  return fromSettingsDir;
@@ -22679,7 +23137,7 @@ function makePiProjectRelativeEntry(extPath) {
22679
23137
  return extResolved;
22680
23138
  }
22681
23139
  function hermesConfigHasHooks(settingsPath) {
22682
- if (!existsSync25(settingsPath))
23140
+ if (!existsSync26(settingsPath))
22683
23141
  return false;
22684
23142
  try {
22685
23143
  const doc = readYamlDoc(settingsPath);
@@ -22695,13 +23153,13 @@ function hermesConfigHasHooks(settingsPath) {
22695
23153
  return false;
22696
23154
  }
22697
23155
  function unhookedHermesProfiles() {
22698
- return listHermesProfiles().filter((p) => existsSync25(p.home)).filter((p) => !hermesConfigHasHooks(resolve20(p.home, "config.yaml"))).map((p) => p.name);
23156
+ return listHermesProfiles().filter((p) => existsSync26(p.home)).filter((p) => !hermesConfigHasHooks(resolve21(p.home, "config.yaml"))).map((p) => p.name);
22699
23157
  }
22700
23158
  function getOpenClawPluginPath() {
22701
23159
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
22702
23160
  if (fromEnv)
22703
- return resolve20(fromEnv, "openclaw-plugin");
22704
- return resolve20(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
23161
+ return resolve21(fromEnv, "openclaw-plugin");
23162
+ return resolve21(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
22705
23163
  }
22706
23164
  function isFailproofaiOpenClawPath(p) {
22707
23165
  if (typeof p !== "string")
@@ -22738,14 +23196,14 @@ var init_integrations = __esm(() => {
22738
23196
  scopes: HOOK_SCOPES,
22739
23197
  eventTypes: CLAUDE_INSTALL_EVENT_TYPES,
22740
23198
  getSettingsPath(scope, cwd) {
22741
- const base = cwd ? resolve20(cwd) : process.cwd();
23199
+ const base = cwd ? resolve21(cwd) : process.cwd();
22742
23200
  switch (scope) {
22743
23201
  case "user":
22744
- return resolve20(homedir23(), ".claude", "settings.json");
23202
+ return resolve21(homedir23(), ".claude", "settings.json");
22745
23203
  case "project":
22746
- return resolve20(base, ".claude", "settings.json");
23204
+ return resolve21(base, ".claude", "settings.json");
22747
23205
  case "local":
22748
- return resolve20(base, ".claude", "settings.local.json");
23206
+ return resolve21(base, ".claude", "settings.local.json");
22749
23207
  }
22750
23208
  },
22751
23209
  readSettings(settingsPath) {
@@ -22841,7 +23299,7 @@ var init_integrations = __esm(() => {
22841
23299
  },
22842
23300
  hooksInstalledInSettings(scope, cwd) {
22843
23301
  const settingsPath = this.getSettingsPath(scope, cwd);
22844
- if (!existsSync25(settingsPath))
23302
+ if (!existsSync26(settingsPath))
22845
23303
  return false;
22846
23304
  try {
22847
23305
  const settings = this.readSettings(settingsPath);
@@ -22870,14 +23328,14 @@ var init_integrations = __esm(() => {
22870
23328
  scopes: CODEX_HOOK_SCOPES,
22871
23329
  eventTypes: CODEX_HOOK_EVENT_TYPES,
22872
23330
  getSettingsPath(scope, cwd) {
22873
- const base = cwd ? resolve20(cwd) : process.cwd();
23331
+ const base = cwd ? resolve21(cwd) : process.cwd();
22874
23332
  switch (scope) {
22875
23333
  case "user":
22876
- return resolve20(homedir23(), ".codex", "hooks.json");
23334
+ return resolve21(homedir23(), ".codex", "hooks.json");
22877
23335
  case "project":
22878
- return resolve20(base, ".codex", "hooks.json");
23336
+ return resolve21(base, ".codex", "hooks.json");
22879
23337
  case "local":
22880
- return resolve20(base, ".codex", "hooks.json");
23338
+ return resolve21(base, ".codex", "hooks.json");
22881
23339
  }
22882
23340
  },
22883
23341
  readSettings(settingsPath) {
@@ -22957,7 +23415,7 @@ var init_integrations = __esm(() => {
22957
23415
  },
22958
23416
  hooksInstalledInSettings(scope, cwd) {
22959
23417
  const settingsPath = this.getSettingsPath(scope, cwd);
22960
- if (!existsSync25(settingsPath))
23418
+ if (!existsSync26(settingsPath))
22961
23419
  return false;
22962
23420
  try {
22963
23421
  const settings = this.readSettings(settingsPath);
@@ -22986,14 +23444,14 @@ var init_integrations = __esm(() => {
22986
23444
  scopes: COPILOT_HOOK_SCOPES,
22987
23445
  eventTypes: COPILOT_HOOK_EVENT_TYPES,
22988
23446
  getSettingsPath(scope, cwd) {
22989
- const base = cwd ? resolve20(cwd) : process.cwd();
23447
+ const base = cwd ? resolve21(cwd) : process.cwd();
22990
23448
  switch (scope) {
22991
23449
  case "user":
22992
- return resolve20(homedir23(), ".copilot", "hooks", "failproofai.json");
23450
+ return resolve21(homedir23(), ".copilot", "hooks", "failproofai.json");
22993
23451
  case "project":
22994
- return resolve20(base, ".github", "hooks", "failproofai.json");
23452
+ return resolve21(base, ".github", "hooks", "failproofai.json");
22995
23453
  case "local":
22996
- return resolve20(base, ".github", "hooks", "failproofai.json");
23454
+ return resolve21(base, ".github", "hooks", "failproofai.json");
22997
23455
  }
22998
23456
  },
22999
23457
  readSettings(settingsPath) {
@@ -23071,7 +23529,7 @@ var init_integrations = __esm(() => {
23071
23529
  },
23072
23530
  hooksInstalledInSettings(scope, cwd) {
23073
23531
  const settingsPath = this.getSettingsPath(scope, cwd);
23074
- if (!existsSync25(settingsPath))
23532
+ if (!existsSync26(settingsPath))
23075
23533
  return false;
23076
23534
  try {
23077
23535
  const settings = this.readSettings(settingsPath);
@@ -23100,14 +23558,14 @@ var init_integrations = __esm(() => {
23100
23558
  scopes: CURSOR_HOOK_SCOPES,
23101
23559
  eventTypes: CURSOR_HOOK_EVENT_TYPES,
23102
23560
  getSettingsPath(scope, cwd) {
23103
- const base = cwd ? resolve20(cwd) : process.cwd();
23561
+ const base = cwd ? resolve21(cwd) : process.cwd();
23104
23562
  switch (scope) {
23105
23563
  case "user":
23106
- return resolve20(homedir23(), ".cursor", "hooks.json");
23564
+ return resolve21(homedir23(), ".cursor", "hooks.json");
23107
23565
  case "project":
23108
- return resolve20(base, ".cursor", "hooks.json");
23566
+ return resolve21(base, ".cursor", "hooks.json");
23109
23567
  case "local":
23110
- return resolve20(base, ".cursor", "hooks.json");
23568
+ return resolve21(base, ".cursor", "hooks.json");
23111
23569
  }
23112
23570
  },
23113
23571
  readSettings(settingsPath) {
@@ -23174,7 +23632,7 @@ var init_integrations = __esm(() => {
23174
23632
  },
23175
23633
  hooksInstalledInSettings(scope, cwd) {
23176
23634
  const settingsPath = this.getSettingsPath(scope, cwd);
23177
- if (!existsSync25(settingsPath))
23635
+ if (!existsSync26(settingsPath))
23178
23636
  return false;
23179
23637
  try {
23180
23638
  const settings = this.readSettings(settingsPath);
@@ -23199,14 +23657,14 @@ var init_integrations = __esm(() => {
23199
23657
  scopes: OPENCODE_HOOK_SCOPES,
23200
23658
  eventTypes: OPENCODE_HOOK_EVENT_TYPES,
23201
23659
  getSettingsPath(scope, cwd) {
23202
- const base = cwd ? resolve20(cwd) : process.cwd();
23660
+ const base = cwd ? resolve21(cwd) : process.cwd();
23203
23661
  switch (scope) {
23204
23662
  case "user":
23205
- return resolve20(homedir23(), ".config", "opencode", "opencode.json");
23663
+ return resolve21(homedir23(), ".config", "opencode", "opencode.json");
23206
23664
  case "project":
23207
- return resolve20(base, ".opencode", "opencode.json");
23665
+ return resolve21(base, ".opencode", "opencode.json");
23208
23666
  case "local":
23209
- return resolve20(base, ".opencode", "opencode.json");
23667
+ return resolve21(base, ".opencode", "opencode.json");
23210
23668
  }
23211
23669
  },
23212
23670
  readSettings(settingsPath) {
@@ -23217,7 +23675,7 @@ var init_integrations = __esm(() => {
23217
23675
  },
23218
23676
  buildHookEntry(_binaryPath, _eventType, scope) {
23219
23677
  if (scope === "user") {
23220
- const abs = resolve20(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
23678
+ const abs = resolve21(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
23221
23679
  return { spec: `file://${abs}`, [FAILPROOFAI_HOOK_MARKER]: true };
23222
23680
  }
23223
23681
  return { spec: OPENCODE_PLUGIN_REL_PATH, [FAILPROOFAI_HOOK_MARKER]: true };
@@ -23232,10 +23690,10 @@ var init_integrations = __esm(() => {
23232
23690
  writeHookEntries(settings, binaryPath, scope) {
23233
23691
  const s = settings;
23234
23692
  const effectiveScope = scope ?? "project";
23235
- const settingsPath = effectiveScope === "user" ? resolve20(homedir23(), ".config", "opencode", "opencode.json") : resolve20(process.cwd(), ".opencode", "opencode.json");
23693
+ const settingsPath = effectiveScope === "user" ? resolve21(homedir23(), ".config", "opencode", "opencode.json") : resolve21(process.cwd(), ".opencode", "opencode.json");
23236
23694
  const pluginPath = opencodePluginFilePath(settingsPath);
23237
- mkdirSync16(dirname13(pluginPath), { recursive: true });
23238
- writeFileSync14(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
23695
+ mkdirSync17(dirname14(pluginPath), { recursive: true });
23696
+ writeFileSync15(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
23239
23697
  if (!Array.isArray(s.plugin))
23240
23698
  s.plugin = [];
23241
23699
  const desired = effectiveScope === "user" ? `file://${pluginPath}` : OPENCODE_PLUGIN_REL_PATH;
@@ -23258,9 +23716,9 @@ var init_integrations = __esm(() => {
23258
23716
  }
23259
23717
  this.writeSettings(settingsPath, settings);
23260
23718
  const pluginPath = opencodePluginFilePath(settingsPath);
23261
- if (existsSync25(pluginPath)) {
23719
+ if (existsSync26(pluginPath)) {
23262
23720
  try {
23263
- const content = readFileSync21(pluginPath, "utf8");
23721
+ const content = readFileSync22(pluginPath, "utf8");
23264
23722
  if (content.includes(FAILPROOFAI_HOOK_MARKER)) {
23265
23723
  unlinkSync3(pluginPath);
23266
23724
  if (removed === 0)
@@ -23272,7 +23730,7 @@ var init_integrations = __esm(() => {
23272
23730
  },
23273
23731
  hooksInstalledInSettings(scope, cwd) {
23274
23732
  const settingsPath = this.getSettingsPath(scope, cwd);
23275
- if (!existsSync25(settingsPath))
23733
+ if (!existsSync26(settingsPath))
23276
23734
  return false;
23277
23735
  try {
23278
23736
  const settings = this.readSettings(settingsPath);
@@ -23282,9 +23740,9 @@ var init_integrations = __esm(() => {
23282
23740
  if (!hasEntry)
23283
23741
  return false;
23284
23742
  const pluginPath = opencodePluginFilePath(settingsPath);
23285
- if (!existsSync25(pluginPath))
23743
+ if (!existsSync26(pluginPath))
23286
23744
  return false;
23287
- const content = readFileSync21(pluginPath, "utf8");
23745
+ const content = readFileSync22(pluginPath, "utf8");
23288
23746
  return content.includes(FAILPROOFAI_HOOK_MARKER);
23289
23747
  } catch {
23290
23748
  return false;
@@ -23300,14 +23758,14 @@ var init_integrations = __esm(() => {
23300
23758
  scopes: PI_HOOK_SCOPES,
23301
23759
  eventTypes: PI_HOOK_EVENT_TYPES,
23302
23760
  getSettingsPath(scope, cwd) {
23303
- const base = cwd ? resolve20(cwd) : process.cwd();
23761
+ const base = cwd ? resolve21(cwd) : process.cwd();
23304
23762
  switch (scope) {
23305
23763
  case "user":
23306
- return resolve20(homedir23(), ".pi", "agent", "settings.json");
23764
+ return resolve21(homedir23(), ".pi", "agent", "settings.json");
23307
23765
  case "project":
23308
- return resolve20(base, ".pi", "settings.json");
23766
+ return resolve21(base, ".pi", "settings.json");
23309
23767
  case "local":
23310
- return resolve20(base, ".pi", "settings.json");
23768
+ return resolve21(base, ".pi", "settings.json");
23311
23769
  }
23312
23770
  },
23313
23771
  readSettings(settingsPath) {
@@ -23349,7 +23807,7 @@ var init_integrations = __esm(() => {
23349
23807
  }
23350
23808
  },
23351
23809
  removeHooksFromFile(settingsPath) {
23352
- if (!existsSync25(settingsPath))
23810
+ if (!existsSync26(settingsPath))
23353
23811
  return 0;
23354
23812
  const settings = this.readSettings(settingsPath);
23355
23813
  if (!Array.isArray(settings.packages))
@@ -23364,7 +23822,7 @@ var init_integrations = __esm(() => {
23364
23822
  },
23365
23823
  hooksInstalledInSettings(scope, cwd) {
23366
23824
  const settingsPath = this.getSettingsPath(scope, cwd);
23367
- if (!existsSync25(settingsPath))
23825
+ if (!existsSync26(settingsPath))
23368
23826
  return false;
23369
23827
  try {
23370
23828
  const settings = this.readSettings(settingsPath);
@@ -23385,10 +23843,10 @@ var init_integrations = __esm(() => {
23385
23843
  scopes: HERMES_HOOK_SCOPES,
23386
23844
  eventTypes: HERMES_HOOK_EVENT_TYPES,
23387
23845
  getSettingsPath() {
23388
- return resolve20(hermesRoot(), "config.yaml");
23846
+ return resolve21(hermesRoot(), "config.yaml");
23389
23847
  },
23390
23848
  getSettingsPaths() {
23391
- return listHermesProfiles().map((p) => resolve20(p.home, "config.yaml"));
23849
+ return listHermesProfiles().map((p) => resolve21(p.home, "config.yaml"));
23392
23850
  },
23393
23851
  readSettings(settingsPath) {
23394
23852
  return readYamlDoc(settingsPath);
@@ -23423,7 +23881,7 @@ var init_integrations = __esm(() => {
23423
23881
  doc.set("hooks_auto_accept", true);
23424
23882
  },
23425
23883
  removeHooksFromFile(settingsPath) {
23426
- if (!existsSync25(settingsPath))
23884
+ if (!existsSync26(settingsPath))
23427
23885
  return 0;
23428
23886
  const doc = readYamlDoc(settingsPath);
23429
23887
  const js = doc.toJS() ?? {};
@@ -23468,7 +23926,7 @@ var init_integrations = __esm(() => {
23468
23926
  scopes: OPENCLAW_HOOK_SCOPES,
23469
23927
  eventTypes: OPENCLAW_HOOK_EVENT_TYPES,
23470
23928
  getSettingsPath() {
23471
- return resolve20(homedir23(), ".openclaw", "openclaw.json");
23929
+ return resolve21(homedir23(), ".openclaw", "openclaw.json");
23472
23930
  },
23473
23931
  readSettings(settingsPath) {
23474
23932
  return readJsonFile(settingsPath);
@@ -23513,7 +23971,7 @@ var init_integrations = __esm(() => {
23513
23971
  hooks.allowConversationAccess = true;
23514
23972
  },
23515
23973
  removeHooksFromFile(settingsPath) {
23516
- if (!existsSync25(settingsPath))
23974
+ if (!existsSync26(settingsPath))
23517
23975
  return 0;
23518
23976
  const settings = this.readSettings(settingsPath);
23519
23977
  const plugins = settings.plugins;
@@ -23542,7 +24000,7 @@ var init_integrations = __esm(() => {
23542
24000
  },
23543
24001
  hooksInstalledInSettings(scope, cwd) {
23544
24002
  const settingsPath = this.getSettingsPath(scope, cwd);
23545
- if (!existsSync25(settingsPath))
24003
+ if (!existsSync26(settingsPath))
23546
24004
  return false;
23547
24005
  try {
23548
24006
  const settings = this.readSettings(settingsPath);
@@ -23564,14 +24022,14 @@ var init_integrations = __esm(() => {
23564
24022
  scopes: FACTORY_HOOK_SCOPES,
23565
24023
  eventTypes: FACTORY_HOOK_EVENT_TYPES,
23566
24024
  getSettingsPath(scope, cwd) {
23567
- const base = cwd ? resolve20(cwd) : process.cwd();
24025
+ const base = cwd ? resolve21(cwd) : process.cwd();
23568
24026
  switch (scope) {
23569
24027
  case "user":
23570
- return resolve20(homedir23(), ".factory", "hooks.json");
24028
+ return resolve21(homedir23(), ".factory", "hooks.json");
23571
24029
  case "project":
23572
- return resolve20(base, ".factory", "hooks.json");
24030
+ return resolve21(base, ".factory", "hooks.json");
23573
24031
  case "local":
23574
- return resolve20(base, ".factory", "hooks.json");
24032
+ return resolve21(base, ".factory", "hooks.json");
23575
24033
  }
23576
24034
  },
23577
24035
  readSettings(settingsPath) {
@@ -23639,7 +24097,7 @@ var init_integrations = __esm(() => {
23639
24097
  },
23640
24098
  hooksInstalledInSettings(scope, cwd) {
23641
24099
  const settingsPath = this.getSettingsPath(scope, cwd);
23642
- if (!existsSync25(settingsPath))
24100
+ if (!existsSync26(settingsPath))
23643
24101
  return false;
23644
24102
  try {
23645
24103
  const settings = this.readSettings(settingsPath);
@@ -23666,14 +24124,14 @@ var init_integrations = __esm(() => {
23666
24124
  scopes: DEVIN_HOOK_SCOPES,
23667
24125
  eventTypes: DEVIN_HOOK_EVENT_TYPES,
23668
24126
  getSettingsPath(scope, cwd) {
23669
- const base = cwd ? resolve20(cwd) : process.cwd();
24127
+ const base = cwd ? resolve21(cwd) : process.cwd();
23670
24128
  switch (scope) {
23671
24129
  case "user":
23672
- return resolve20(homedir23(), ".config", "devin", "config.json");
24130
+ return resolve21(homedir23(), ".config", "devin", "config.json");
23673
24131
  case "project":
23674
- return resolve20(base, ".devin", "config.json");
24132
+ return resolve21(base, ".devin", "config.json");
23675
24133
  case "local":
23676
- return resolve20(base, ".devin", "config.json");
24134
+ return resolve21(base, ".devin", "config.json");
23677
24135
  }
23678
24136
  },
23679
24137
  readSettings(settingsPath) {
@@ -23745,7 +24203,7 @@ var init_integrations = __esm(() => {
23745
24203
  },
23746
24204
  hooksInstalledInSettings(scope, cwd) {
23747
24205
  const settingsPath = this.getSettingsPath(scope, cwd);
23748
- if (!existsSync25(settingsPath))
24206
+ if (!existsSync26(settingsPath))
23749
24207
  return false;
23750
24208
  try {
23751
24209
  const settings = this.readSettings(settingsPath);
@@ -23775,14 +24233,14 @@ var init_integrations = __esm(() => {
23775
24233
  scopes: ANTIGRAVITY_HOOK_SCOPES,
23776
24234
  eventTypes: ANTIGRAVITY_HOOK_EVENT_TYPES,
23777
24235
  getSettingsPath(scope, cwd) {
23778
- const base = cwd ? resolve20(cwd) : process.cwd();
24236
+ const base = cwd ? resolve21(cwd) : process.cwd();
23779
24237
  switch (scope) {
23780
24238
  case "user":
23781
- return resolve20(homedir23(), ".gemini", "config", "hooks.json");
24239
+ return resolve21(homedir23(), ".gemini", "config", "hooks.json");
23782
24240
  case "project":
23783
- return resolve20(base, ".agents", "hooks.json");
24241
+ return resolve21(base, ".agents", "hooks.json");
23784
24242
  case "local":
23785
- return resolve20(base, ".agents", "hooks.json");
24243
+ return resolve21(base, ".agents", "hooks.json");
23786
24244
  }
23787
24245
  },
23788
24246
  readSettings(settingsPath) {
@@ -23884,7 +24342,7 @@ var init_integrations = __esm(() => {
23884
24342
  },
23885
24343
  hooksInstalledInSettings(scope, cwd) {
23886
24344
  const settingsPath = this.getSettingsPath(scope, cwd);
23887
- if (!existsSync25(settingsPath))
24345
+ if (!existsSync26(settingsPath))
23888
24346
  return false;
23889
24347
  try {
23890
24348
  const settings = this.readSettings(settingsPath);
@@ -23916,14 +24374,14 @@ var init_integrations = __esm(() => {
23916
24374
  scopes: GOOSE_HOOK_SCOPES,
23917
24375
  eventTypes: GOOSE_HOOK_EVENT_TYPES,
23918
24376
  getSettingsPath(scope, cwd) {
23919
- const base = cwd ? resolve20(cwd) : process.cwd();
24377
+ const base = cwd ? resolve21(cwd) : process.cwd();
23920
24378
  switch (scope) {
23921
24379
  case "user":
23922
- return resolve20(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
24380
+ return resolve21(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
23923
24381
  case "project":
23924
- return resolve20(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
24382
+ return resolve21(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
23925
24383
  case "local":
23926
- return resolve20(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
24384
+ return resolve21(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
23927
24385
  }
23928
24386
  },
23929
24387
  readSettings(settingsPath) {
@@ -23990,7 +24448,7 @@ var init_integrations = __esm(() => {
23990
24448
  },
23991
24449
  hooksInstalledInSettings(scope, cwd) {
23992
24450
  const settingsPath = this.getSettingsPath(scope, cwd);
23993
- if (!existsSync25(settingsPath))
24451
+ if (!existsSync26(settingsPath))
23994
24452
  return false;
23995
24453
  try {
23996
24454
  const settings = this.readSettings(settingsPath);
@@ -24179,7 +24637,7 @@ async function promptPolicySelection(preSelected, options = {}) {
24179
24637
  `);
24180
24638
  lastLineCount = lines.length;
24181
24639
  }
24182
- return new Promise((resolve21) => {
24640
+ return new Promise((resolve22) => {
24183
24641
  render();
24184
24642
  process.stdin.setRawMode(true);
24185
24643
  process.stdin.resume();
@@ -24217,7 +24675,7 @@ async function promptPolicySelection(preSelected, options = {}) {
24217
24675
  ${dim(STEP_DONE2)} Policies
24218
24676
  ${dim(BAR2)} ${dim(summary)}
24219
24677
  `);
24220
- resolve21([...selected, ...carried]);
24678
+ resolve22([...selected, ...carried]);
24221
24679
  } else if (key.name === "escape") {
24222
24680
  search = "";
24223
24681
  cursor2 = 0;
@@ -24269,423 +24727,6 @@ var init_cli_error = __esm(() => {
24269
24727
  };
24270
24728
  });
24271
24729
 
24272
- // src/hooks/pack-store.ts
24273
- import { createHash as createHash7 } from "node:crypto";
24274
- import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync22, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync15 } from "node:fs";
24275
- import { dirname as dirname14, resolve as resolve21 } from "node:path";
24276
- function baseUrl2() {
24277
- return (process.env.FAILPROOFAI_PACK_BASE_URL || DEFAULT_BASE_URL2).replace(/\/+$/, "");
24278
- }
24279
- function parsePackSpec(source) {
24280
- const raw = source.trim();
24281
- if (raw.length === 0)
24282
- throw new Error("pack source is empty");
24283
- const url = raw.match(/^(?:https?:\/\/)?(?:www\.)?github\.com\/(.+)$/i);
24284
- if (url) {
24285
- const parts = url[1].replace(/\/+$/, "").split("/");
24286
- const [owner, repo, ...rest] = parts;
24287
- let tag2 = null;
24288
- if (rest[0] === "releases" && (rest[1] === "tag" || rest[1] === "download")) {
24289
- const tail = rest.slice(2);
24290
- tag2 = (rest[1] === "download" ? tail.slice(0, -1) : tail).join("/") || null;
24291
- } else if (rest[0] === "releases" && rest[1] === "latest") {
24292
- tag2 = null;
24293
- }
24294
- return validated(owner, repo, tag2);
24295
- }
24296
- const withoutScheme = raw.replace(/^github:/i, "");
24297
- const at2 = withoutScheme.lastIndexOf("@");
24298
- const repoPart = at2 > 0 ? withoutScheme.slice(0, at2) : withoutScheme;
24299
- const tag = at2 > 0 ? withoutScheme.slice(at2 + 1) : null;
24300
- const slash = repoPart.indexOf("/");
24301
- if (slash <= 0 || slash === repoPart.length - 1) {
24302
- throw new Error(`pack source must be owner/repo, owner/repo@tag, or a github.com URL — got ${JSON.stringify(source)}`);
24303
- }
24304
- return validated(repoPart.slice(0, slash), repoPart.slice(slash + 1), tag);
24305
- }
24306
- function validated(owner, repo, tag) {
24307
- if (!owner || !OWNER_RE.test(owner))
24308
- throw new Error(`unsafe owner ${JSON.stringify(owner)}`);
24309
- if (!repo || !OWNER_RE.test(repo))
24310
- throw new Error(`unsafe repo ${JSON.stringify(repo)}`);
24311
- if (tag !== null && !TAG_RE.test(tag))
24312
- throw new Error(`unsafe tag ${JSON.stringify(tag)}`);
24313
- return { owner, repo, tag };
24314
- }
24315
- async function resolveLatestTag(spec) {
24316
- const url = `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/latest`;
24317
- const response = await fetch(url, {
24318
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
24319
- redirect: "manual"
24320
- });
24321
- const location = response.headers.get("location");
24322
- if (!location) {
24323
- throw new Error(`could not resolve the newest release of ${spec.owner}/${spec.repo} ` + `(GET ${url} returned ${response.status} with no redirect). That redirect is issued only ` + `for a published, non-prerelease release, so either the repository has no releases at all ` + `or every release it has is a prerelease or a draft. Name a tag explicitly.`);
24324
- }
24325
- const match = location.match(/\/releases\/tag\/(.+)$/);
24326
- if (!match)
24327
- throw new Error(`unexpected redirect from ${url}: ${location}`);
24328
- const tag = decodeURIComponent(match[1]).replace(/\/+$/, "");
24329
- if (!TAG_RE.test(tag))
24330
- throw new Error(`unsafe tag ${JSON.stringify(tag)} from ${url}`);
24331
- return tag;
24332
- }
24333
- function formatPackSpec(spec) {
24334
- return `github:${spec.owner}/${spec.repo}@${spec.tag}`;
24335
- }
24336
- function packAssetUrl(spec, asset) {
24337
- return `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/download/${spec.tag}/${asset}`;
24338
- }
24339
- function packTagMatchesVersion(tag, version3) {
24340
- const tail = tag.slice(tag.lastIndexOf("/") + 1);
24341
- return tail === version3 || tail === `v${version3}`;
24342
- }
24343
- async function fetchBytes2(url) {
24344
- const response = await fetch(url, {
24345
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
24346
- redirect: "follow"
24347
- });
24348
- if (!response.ok)
24349
- throw new Error(`GET ${url} returned ${response.status}`);
24350
- const contentLength = response.headers.get("content-length");
24351
- if (contentLength !== null) {
24352
- const declared = Number(contentLength);
24353
- if (Number.isFinite(declared) && declared > MAX_ARTIFACT_BYTES) {
24354
- await response.body?.cancel();
24355
- throw new Error(`${url} declares ${declared} bytes, over the ${MAX_ARTIFACT_BYTES} limit`);
24356
- }
24357
- }
24358
- if (!response.body)
24359
- return Buffer.alloc(0);
24360
- const chunks = [];
24361
- const reader = response.body.getReader();
24362
- let total = 0;
24363
- try {
24364
- while (true) {
24365
- const { done, value } = await reader.read();
24366
- if (done)
24367
- break;
24368
- total += value.byteLength;
24369
- if (total > MAX_ARTIFACT_BYTES) {
24370
- await reader.cancel();
24371
- throw new Error(`${url} is over the ${MAX_ARTIFACT_BYTES} byte limit`);
24372
- }
24373
- chunks.push(Buffer.from(value));
24374
- }
24375
- } finally {
24376
- reader.releaseLock();
24377
- }
24378
- return Buffer.concat(chunks, total);
24379
- }
24380
- function digestFor2(manifest, assetName) {
24381
- for (const line of manifest.split(`
24382
- `)) {
24383
- const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
24384
- if (match && match[2].trim() === assetName)
24385
- return match[1].toLowerCase();
24386
- }
24387
- return null;
24388
- }
24389
- function sha256(bytes) {
24390
- return createHash7("sha256").update(bytes).digest("hex");
24391
- }
24392
- function writeAtomic(target, bytes) {
24393
- const tempPath = `${target}.${process.pid}.tmp`;
24394
- try {
24395
- mkdirSync17(dirname14(target), { recursive: true });
24396
- writeFileSync15(tempPath, bytes);
24397
- renameSync8(tempPath, target);
24398
- } catch (err) {
24399
- rmSync9(tempPath, { force: true });
24400
- throw err;
24401
- }
24402
- }
24403
- async function fetchPackPreview(source) {
24404
- if (process.env.FAILPROOFAI_NO_DOWNLOAD === "1") {
24405
- throw new Error("FAILPROOFAI_NO_DOWNLOAD is set, so nothing can be fetched to preview");
24406
- }
24407
- const parsed = parsePackSpec(source);
24408
- const resolvedFromLatest = parsed.tag === null;
24409
- const spec = parsed.tag !== null ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
24410
- const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
24411
- const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
24412
- const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
24413
- if (!manifestDigest) {
24414
- throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
24415
- }
24416
- if (sha256(manifestBytes) !== manifestDigest) {
24417
- throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
24418
- }
24419
- let raw;
24420
- try {
24421
- raw = JSON.parse(manifestBytes.toString("utf8"));
24422
- } catch (err) {
24423
- throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
24424
- }
24425
- if (!raw || typeof raw !== "object")
24426
- throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
24427
- const value = raw;
24428
- const identity2 = parsePackIdentity(value);
24429
- if (!Array.isArray(value.policies) || value.policies.length === 0) {
24430
- throw new Error("pack manifest declares no policies");
24431
- }
24432
- return {
24433
- id: identity2.id,
24434
- version: identity2.version,
24435
- effect: identity2.effect,
24436
- policies: value.policies.map((policy, i) => parsePackPolicy(identity2.id, policy, i)),
24437
- source: formatPackSpec(spec),
24438
- resolvedFromLatest
24439
- };
24440
- }
24441
- function priorRecordFor(id, sha2562) {
24442
- const installed = readInstalledPacks().packs;
24443
- return installed.find((p) => p.id === id) ?? installed.find((p) => p.sha256 === sha2562);
24444
- }
24445
- async function fetchPack(spec) {
24446
- const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
24447
- const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
24448
- const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
24449
- if (!manifestDigest)
24450
- throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
24451
- if (sha256(manifestBytes) !== manifestDigest) {
24452
- throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
24453
- }
24454
- const artifact = await fetchBytes2(packAssetUrl(spec, PACK_ENTRY_ASSET));
24455
- const entryDigest = digestFor2(checksums, PACK_ENTRY_ASSET);
24456
- if (!entryDigest)
24457
- throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_ENTRY_ASSET}`);
24458
- const artifactDigest = sha256(artifact);
24459
- if (artifactDigest !== entryDigest) {
24460
- throw new Error(`${PACK_ENTRY_ASSET} failed integrity verification`);
24461
- }
24462
- let parsed;
24463
- try {
24464
- parsed = JSON.parse(manifestBytes.toString("utf8"));
24465
- } catch (err) {
24466
- throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
24467
- }
24468
- if (!parsed || typeof parsed !== "object")
24469
- throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
24470
- const raw = parsed;
24471
- const identity2 = parsePackIdentity(raw);
24472
- if (!Array.isArray(raw.policies) || raw.policies.length === 0) {
24473
- throw new Error("pack manifest declares no policies");
24474
- }
24475
- const policies = raw.policies.map((p, i) => parsePackPolicy(identity2.id, p, i));
24476
- return {
24477
- id: identity2.id,
24478
- version: identity2.version,
24479
- policies,
24480
- ...raw.effect !== undefined ? { effect: identity2.effect } : {},
24481
- artifact,
24482
- artifactDigest
24483
- };
24484
- }
24485
- function slugifyCategory(category) {
24486
- return category.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
24487
- }
24488
- function resolveSelection(policies, opts, previous, previouslyInstalled) {
24489
- const available = policies.map((p) => p.name);
24490
- if (opts?.all)
24491
- return { enabled: null, reason: "all" };
24492
- const picked = new Set;
24493
- if (opts?.categories?.length) {
24494
- const wanted = new Set(opts.categories.map(slugifyCategory));
24495
- const known = new Map(policies.map((p) => [slugifyCategory(p.category), p.category]));
24496
- const unknown = [...wanted].filter((c) => !known.has(c));
24497
- if (unknown.length > 0) {
24498
- throw new Error(`no such categor${unknown.length === 1 ? "y" : "ies"}: ${unknown.join(", ")} ` + `(has: ${[...new Set(policies.map((p) => slugifyCategory(p.category)))].join(", ")})`);
24499
- }
24500
- for (const p of policies)
24501
- if (wanted.has(slugifyCategory(p.category)))
24502
- picked.add(p.name);
24503
- }
24504
- if (opts?.only?.length) {
24505
- const unknown = opts.only.filter((n) => !available.includes(n));
24506
- if (unknown.length > 0) {
24507
- throw new Error(`pack does not contain ${unknown.join(", ")}`);
24508
- }
24509
- for (const n of opts.only)
24510
- picked.add(n);
24511
- }
24512
- if (opts?.only !== undefined || opts?.categories?.length) {
24513
- return { enabled: available.filter((n) => picked.has(n)), reason: "selected" };
24514
- }
24515
- if (previouslyInstalled) {
24516
- return { enabled: previous === null ? null : previous ?? null, reason: "carried" };
24517
- }
24518
- return { enabled: policies.filter((p) => p.defaultEnabled).map((p) => p.name), reason: "defaults" };
24519
- }
24520
- async function verifyArtifactRegisters(artifactPath, declared) {
24521
- const { loadCustomHooks: loadCustomHooks2 } = await Promise.resolve().then(() => (init_custom_hooks_loader(), exports_custom_hooks_loader));
24522
- let registered;
24523
- try {
24524
- registered = (await loadCustomHooks2(artifactPath, { strict: true })).map((h) => h.name);
24525
- } catch (err) {
24526
- throw new Error(`its artifact could not be loaded: ${err instanceof Error ? err.message : String(err)}`);
24527
- }
24528
- const missing = declared.filter((name) => !registered.includes(name));
24529
- const extra = registered.filter((name) => !declared.includes(name));
24530
- if (missing.length > 0) {
24531
- throw new Error(`its manifest declares ${missing.join(", ")} but the artifact does not register ${missing.length === 1 ? "it" : "them"} — that would install as protection that never runs`);
24532
- }
24533
- if (extra.length > 0) {
24534
- throw new Error(`its artifact registers undeclared ${extra.join(", ")} — a policy that enforces but ` + `appears in no listing cannot be reviewed or switched off`);
24535
- }
24536
- }
24537
- async function checkPackArtifact(artifactPath, declared) {
24538
- try {
24539
- await verifyArtifactRegisters(artifactPath, declared);
24540
- return null;
24541
- } catch (err) {
24542
- return err instanceof Error ? err.message : String(err);
24543
- }
24544
- }
24545
- async function addPack(source, opts) {
24546
- if (process.env.FAILPROOFAI_NO_DOWNLOAD) {
24547
- throw new Error("pack downloads are disabled (FAILPROOFAI_NO_DOWNLOAD). Already-installed packs keep enforcing.");
24548
- }
24549
- const parsed = parsePackSpec(source);
24550
- const spec = parsed.tag ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
24551
- const resolvedFromLatest = parsed.tag === null;
24552
- const fetched = await fetchPack(spec);
24553
- if (!packTagMatchesVersion(spec.tag, fetched.version)) {
24554
- const fromTag = spec.tag.slice(spec.tag.lastIndexOf("/") + 1).replace(/^v/, "");
24555
- const fix = `re-tag the release ${fetched.version} (a leading v is fine), or rebuild the pack with --version ${fromTag}`;
24556
- throw new Error(`${fetched.id} was not installed: release tag ${spec.tag} does not match the version its manifest declares (${fetched.version}). ` + (resolvedFromLatest ? `That is the newest release of ${spec.owner}/${spec.repo}, so the release and the pack ` + `disagree at the source — ask the publisher to ${fix}.` : `Name the tag whose manifest says ${fetched.version}, or fix the pack: ${fix}.`));
24557
- }
24558
- const available = fetched.policies.map((p) => p.name);
24559
- const prior = priorRecordFor(fetched.id, fetched.artifactDigest);
24560
- const { enabled, reason } = resolveSelection(fetched.policies, opts, prior?.enabled ? prior.enabled.filter((n) => available.includes(n)) : prior?.enabled, Boolean(prior));
24561
- const root = packsRoot();
24562
- const artifactRel = `artifacts/${fetched.artifactDigest}.mjs`;
24563
- const artifactAbs = resolve21(root, artifactRel);
24564
- if (!existsSync26(artifactAbs))
24565
- writeAtomic(artifactAbs, fetched.artifact);
24566
- try {
24567
- await verifyArtifactRegisters(artifactAbs, available);
24568
- } catch (err) {
24569
- throw new Error(`${fetched.id}@${fetched.version} was not installed: ${err instanceof Error ? err.message : String(err)}`);
24570
- }
24571
- const repoOf = (source2) => {
24572
- const at2 = source2.lastIndexOf("@");
24573
- return at2 > source2.indexOf(":") ? source2.slice(0, at2) : source2;
24574
- };
24575
- if (prior && !prior.source.startsWith("bundled:") && repoOf(prior.source) !== repoOf(formatPackSpec(spec))) {
24576
- throw new Error(`pack id ${fetched.id} is already installed from ${prior.source}. Refusing to replace it with ${formatPackSpec(spec)} — remove it first if that is what you mean.`);
24577
- }
24578
- const record = {
24579
- id: fetched.id,
24580
- version: fetched.version,
24581
- source: formatPackSpec(spec),
24582
- entry: artifactRel,
24583
- sha256: fetched.artifactDigest,
24584
- policies: fetched.policies,
24585
- ...fetched.effect ? { effect: fetched.effect } : {},
24586
- ...enabled ? { enabled } : {},
24587
- ...opts?.clis ? { clis: opts.clis } : {}
24588
- };
24589
- const absorbed = upsertInstalled(record);
24590
- return {
24591
- replaced: absorbed,
24592
- id: fetched.id,
24593
- version: fetched.version,
24594
- source: record.source,
24595
- tag: spec.tag,
24596
- resolvedFromLatest,
24597
- enabled: enabled ?? available,
24598
- available,
24599
- selection: reason,
24600
- categories: [...new Set(fetched.policies.map((p) => slugifyCategory(p.category)))],
24601
- artifact: artifactAbs
24602
- };
24603
- }
24604
- function setPackPolicyEnabled(packId, name, on) {
24605
- const manifestPath = resolve21(packsRoot(), "installed.json");
24606
- if (!existsSync26(manifestPath))
24607
- return { ok: false, reason: "no packs are installed" };
24608
- let raw;
24609
- try {
24610
- raw = JSON.parse(readFileSync22(manifestPath, "utf8"));
24611
- } catch {
24612
- return { ok: false, reason: `pack manifest at ${manifestPath} is unreadable` };
24613
- }
24614
- const packs = Array.isArray(raw.packs) ? raw.packs : [];
24615
- const pack = packs.find((p) => p?.id === packId);
24616
- if (!pack)
24617
- return { ok: false, reason: `no installed pack with id ${packId}` };
24618
- const declared = Array.isArray(pack.policies) ? pack.policies : [];
24619
- const available = declared.map((p) => typeof p?.name === "string" ? p.name : null).filter((n) => n !== null);
24620
- if (!available.includes(name)) {
24621
- return { ok: false, reason: `pack ${packId} declares no policy named ${name}` };
24622
- }
24623
- const current = pack.enabled ?? available;
24624
- const next = on ? [...new Set([...current, name])] : current.filter((n) => n !== name);
24625
- pack.enabled = available.filter((n) => next.includes(n));
24626
- writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs }, null, 2) + `
24627
- `);
24628
- return { ok: true };
24629
- }
24630
- function removePack(id) {
24631
- const manifestPath = resolve21(packsRoot(), "installed.json");
24632
- if (!existsSync26(manifestPath))
24633
- return false;
24634
- let raw;
24635
- try {
24636
- raw = JSON.parse(readFileSync22(manifestPath, "utf8"));
24637
- } catch {
24638
- return false;
24639
- }
24640
- const packs = Array.isArray(raw.packs) ? raw.packs : [];
24641
- const remaining = packs.filter((p) => p?.id !== id);
24642
- if (remaining.length === packs.length)
24643
- return false;
24644
- writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
24645
- `);
24646
- return true;
24647
- }
24648
- function upsertInstalled(record) {
24649
- const manifestPath = resolve21(packsRoot(), "installed.json");
24650
- let packs = [];
24651
- if (existsSync26(manifestPath)) {
24652
- try {
24653
- const raw = JSON.parse(readFileSync22(manifestPath, "utf8"));
24654
- if (Array.isArray(raw.packs))
24655
- packs = raw.packs;
24656
- } catch {
24657
- throw new Error(`existing pack manifest at ${manifestPath} is unreadable; move it aside and re-add`);
24658
- }
24659
- }
24660
- const absorbed = packs.filter((p) => p?.sha256 === record.sha256 && p?.id !== record.id).map((p) => p.id);
24661
- if (!record.enabled) {
24662
- const prior = packs.find((p) => absorbed.includes(p?.id));
24663
- const carried = prior?.enabled?.filter((n) => record.policies?.some((rp) => rp?.name === n));
24664
- if (carried && carried.length > 0)
24665
- record.enabled = carried;
24666
- }
24667
- const remaining = packs.filter((p) => !absorbed.includes(p?.id));
24668
- const idx = remaining.findIndex((p) => p?.id === record.id);
24669
- if (idx >= 0)
24670
- remaining[idx] = record;
24671
- else
24672
- remaining.push(record);
24673
- writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
24674
- `);
24675
- return absorbed;
24676
- }
24677
- function errText2(err) {
24678
- return err instanceof Error ? err.message : String(err);
24679
- }
24680
- var DEFAULT_BASE_URL2 = "https://github.com", FETCH_TIMEOUT_MS = 30000, MAX_ARTIFACT_BYTES, PACK_MANIFEST_ASSET = "failproofai-pack.json", PACK_ENTRY_ASSET = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET = "SHA256SUMS", OWNER_RE, TAG_RE, CORE_SOURCE = "FailproofAI/policies", CORE_ALIASES;
24681
- var init_pack_store = __esm(() => {
24682
- init_pack_manifest();
24683
- MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
24684
- OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
24685
- TAG_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
24686
- CORE_ALIASES = new Set(["core", "failproofai", "official"]);
24687
- });
24688
-
24689
24730
  // src/hooks/manager.ts
24690
24731
  import { execSync as execSync6 } from "node:child_process";
24691
24732
  import { existsSync as existsSync27 } from "node:fs";
@@ -24951,7 +24992,7 @@ Validated ${validatedHooks.length} custom hook(s) from ${path3}: ${validatedHook
24951
24992
  } catch (err) {
24952
24993
  console.log(`
24953
24994
  Warning: could not fetch the policy pack (${err instanceof Error ? err.message : String(err)}).
24954
- ` + `Run \`failproofai policies add core\` once you are online.`);
24995
+ ` + `Run \`failproofai policies add FailproofAI/policies\` once you are online.`);
24955
24996
  }
24956
24997
  } else {
24957
24998
  for (const name of fromPack) {
@@ -25690,7 +25731,6 @@ function blockerCleared(attempt, probe) {
25690
25731
  return probe.cliVersion !== attempt.cliVersion;
25691
25732
  case "unsupported_platform":
25692
25733
  return probe.cliVersion !== attempt.cliVersion;
25693
- case "not_a_tty":
25694
25734
  case "running_as_sudo":
25695
25735
  return true;
25696
25736
  default:
@@ -25703,7 +25743,6 @@ function attemptHintLines(attempt) {
25703
25743
  daemon_failed: "the failproofaid service could not be started",
25704
25744
  cancelled: "it was cancelled",
25705
25745
  unsupported_platform: "failproofaid does not run on this platform",
25706
- not_a_tty: "there was no terminal to ask in",
25707
25746
  running_as_sudo: "it was run under sudo"
25708
25747
  };
25709
25748
  const detail = why[attempt.reason] ?? "it did not finish";
@@ -27602,6 +27641,13 @@ async function runAuditInner(opts, startedAt) {
27602
27641
  const sinceMs = parseSinceOpt(opts.since);
27603
27642
  const userConfig = readMergedHooksConfig();
27604
27643
  const enabledBuiltins = new Set((userConfig.enabledPolicies ?? []).map((n) => normalizePolicyName(n)));
27644
+ try {
27645
+ for (const pack of readInstalledPacks().packs) {
27646
+ const taken = pack.enabled ?? pack.policies.map((entry) => entry.name);
27647
+ for (const name of taken)
27648
+ enabledBuiltins.add(normalizePolicyName(name));
27649
+ }
27650
+ } catch {}
27605
27651
  const allTranscripts = [];
27606
27652
  for (const cli of clis) {
27607
27653
  const adapter = ADAPTERS[cli];
@@ -27702,6 +27748,7 @@ var TRANSCRIPT_CONCURRENCY = 8;
27702
27748
  var init_audit = __esm(() => {
27703
27749
  init_builtin_policies();
27704
27750
  init_hooks_config();
27751
+ init_pack_manifest();
27705
27752
  init_types();
27706
27753
  init_cli_adapters();
27707
27754
  init_detectors();
@@ -29509,6 +29556,12 @@ async function runPostSetupAudit() {
29509
29556
  printSummary(result);
29510
29557
  process.stdout.write(`
29511
29558
  `);
29559
+ if (!hasInstalledPacks()) {
29560
+ process.stdout.write(` ${c(DIM, "none of this is being enforced yet. take ours, or anyone's:")}
29561
+ ` + ` ${c(CYAN, `failproofai policies add ${CORE_SOURCE}`)}
29562
+
29563
+ `);
29564
+ }
29512
29565
  } finally {
29513
29566
  attempt.lock.release();
29514
29567
  }
@@ -29638,6 +29691,8 @@ Two audits write the same cache files, so this one won't start. Try again shortl
29638
29691
  var DASHBOARD_PORT = 8020, EXIT_AUDIT_ALREADY_RUNNING = 75, AUDIT_STAGES, HELP_DESC_COL = 40, RESET, DIM, BOLD, PINK, GREEN, CYAN, SPINNER;
29639
29692
  var init_cli = __esm(() => {
29640
29693
  init_audit();
29694
+ init_pack_manifest();
29695
+ init_pack_store();
29641
29696
  init_audit_lock();
29642
29697
  init_dashboard_cache();
29643
29698
  init_hook_telemetry2();
@@ -29967,17 +30022,11 @@ async function maybeFirstRunConfigure(io = {}, opts = {}) {
29967
30022
  lock.release();
29968
30023
  }
29969
30024
  }
29970
- async function runConfigureWizard(io = {}) {
30025
+ async function runConfigureWizard(io = {}, answers = {}) {
29971
30026
  const stdin = io.stdin ?? process.stdin;
29972
30027
  const stdout = io.stdout ?? process.stdout;
29973
30028
  const cwd = process.cwd();
29974
- if (!stdin.isTTY || !stdout.isTTY) {
29975
- stdout.write(`failproofai config needs an interactive terminal.
29976
- Use the flag form instead, e.g.:
29977
- failproofai policies --install --scope user --cli claude
29978
- `);
29979
- return { applied: false };
29980
- }
30029
+ const unattended = !stdin.isTTY || !stdout.isTTY;
29981
30030
  if (typeof process.getuid === "function" && process.getuid() === 0 && process.env.SUDO_USER) {
29982
30031
  stdout.write(`Run failproofai config as ${process.env.SUDO_USER}, not under sudo.
29983
30032
  ` + `Everything it configures is per-user — hooks, policies and the daemon's own
@@ -29985,7 +30034,7 @@ Use the flag form instead, e.g.:
29985
30034
  ` + `The one step that needs root (installing the service) asks for your password
29986
30035
  on its own.
29987
30036
  `);
29988
- return { applied: false };
30037
+ return { applied: false, abort: "running_as_sudo" };
29989
30038
  }
29990
30039
  emit("configure_started", {});
29991
30040
  if (!isDaemonSupportedPlatform()) {
@@ -30021,7 +30070,7 @@ ${process.platform} service target.
30021
30070
  ` : `Installing failproofaid — needs sudo once.
30022
30071
 
30023
30072
  `);
30024
- if (!primeElevation()) {
30073
+ if (!(unattended ? canElevate() : primeElevation())) {
30025
30074
  stdout.write(`
30026
30075
  Could not get root, so setup stopped before changing anything.
30027
30076
 
@@ -30038,7 +30087,7 @@ Could not get root, so setup stopped before changing anything.
30038
30087
  ` + `version — it cannot start a scheduled audit. Refreshing it needs root once.
30039
30088
 
30040
30089
  `);
30041
- if (!primeElevation()) {
30090
+ if (!(unattended ? canElevate() : primeElevation())) {
30042
30091
  stdout.write(`
30043
30092
  Could not get root, so the service definition was left as it is.
30044
30093
  Everything else is set up as normal; scheduled audits stay off until it is
@@ -30066,7 +30115,7 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
30066
30115
  const clis = [...clisSupportingScope(primaryScope)];
30067
30116
  let connect = null;
30068
30117
  {
30069
- const choice = await selectOne2({
30118
+ const choice = unattended ? answers.token ? "key" : "local" : await selectOne2({
30070
30119
  message: "Connect this machine to FailproofAI Cloud?",
30071
30120
  body: [
30072
30121
  " Sessions include prompts, file contents and command output.",
@@ -30095,7 +30144,12 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
30095
30144
  let token = null;
30096
30145
  let machineId = resolveMachineId();
30097
30146
  let machineLabel2 = existing?.machineLabel ?? resolveMachineLabel();
30098
- if (existing) {
30147
+ if (existing && unattended && !answers.url) {
30148
+ url = existing.url;
30149
+ token = answers.token ?? existing.token;
30150
+ machineId = answers.machineId ?? existing.machineId;
30151
+ machineLabel2 = answers.machineLabel ?? existing.machineLabel ?? machineLabel2;
30152
+ } else if (existing && !unattended) {
30099
30153
  const reuse = await selectOne2({
30100
30154
  message: `Use this machine's existing connection to ${existing.url}?`,
30101
30155
  choices: [
@@ -30136,6 +30190,9 @@ Using ${url} (from FAILPROOFAI_CLOUD_URL).
30136
30190
  url = cloudBaseFor(DEFAULT_INGEST_URL);
30137
30191
  }
30138
30192
  }
30193
+ if (token === null && unattended) {
30194
+ token = answers.token ?? null;
30195
+ }
30139
30196
  if (token === null) {
30140
30197
  token = await promptText2({
30141
30198
  message: `API key for ${new URL(url).host}`,
@@ -30151,6 +30208,12 @@ Using ${url} (from FAILPROOFAI_CLOUD_URL).
30151
30208
  stdout.write(`
30152
30209
  Checking the key… `);
30153
30210
  const probe = await validateIngestKey({ url: ingestUrlFor(url), key: token });
30211
+ if (!probe.ok && unattended) {
30212
+ stdout.write(`
30213
+ That key did not work: ${probe.reason}
30214
+ `);
30215
+ return { applied: false, abort: "cloud_unverified" };
30216
+ }
30154
30217
  if (!probe.ok) {
30155
30218
  stdout.write(`
30156
30219
  That did not work: ${probe.reason}
@@ -30179,7 +30242,7 @@ That did not work: ${probe.reason}
30179
30242
  }
30180
30243
  }
30181
30244
  }
30182
- const decision = await selectOne2({
30245
+ const decision = unattended ? "apply" : await selectOne2({
30183
30246
  message: "Ready to apply?",
30184
30247
  body: reviewLines({
30185
30248
  target,
@@ -30334,6 +30397,14 @@ Hooks keep enforcing; scheduled audits stay off until it is.
30334
30397
  markLauncherSeen();
30335
30398
  clearOnboardingAttempt();
30336
30399
  outro2(buildCompletionSummary(policies.length, clis.length, customEnabled, daemonInstalled, connected), { ok: true }, stdout);
30400
+ if (policies.length === 0) {
30401
+ stdout.write(`
30402
+ Nothing is enforcing yet. Take ours, or anyone's:
30403
+ failproofai policies add ${CORE_SOURCE}
30404
+ failproofai policies show <owner>/<repo> (look first)
30405
+
30406
+ `);
30407
+ }
30337
30408
  return {
30338
30409
  applied: true,
30339
30410
  target,
@@ -30346,6 +30417,7 @@ Hooks keep enforcing; scheduled audits stay off until it is.
30346
30417
  }
30347
30418
  var DIM_NOTE = "(auto-loaded)", MAX_SUMMARY_COLUMNS = 77, POLICY_LIST_INDENT;
30348
30419
  var init_configure_wizard = __esm(() => {
30420
+ init_pack_store();
30349
30421
  init_tui2();
30350
30422
  init_collector_config();
30351
30423
  init_integrations();
@@ -30641,7 +30713,8 @@ __export(exports_pack_cli, {
30641
30713
  packAddSource: () => packAddSource,
30642
30714
  runPackCommand: () => runPackCommand,
30643
30715
  runPolicyPicker: () => runPolicyPicker,
30644
- runPublishCommand: () => runPublishCommand
30716
+ runPublishCommand: () => runPublishCommand,
30717
+ selectionFromForTest: () => selectionFromForTest
30645
30718
  });
30646
30719
  import { execFileSync as execFileSync3 } from "node:child_process";
30647
30720
  import { homedir as homedir29, tmpdir as tmpdir3 } from "node:os";
@@ -30657,22 +30730,72 @@ function parseList(rest, flag) {
30657
30730
  return [];
30658
30731
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
30659
30732
  }
30733
+ function looksLikeCliName(token) {
30734
+ return !token.startsWith("-") && !token.includes("/");
30735
+ }
30736
+ function cliValueIndices(rest) {
30737
+ const taken = new Set;
30738
+ for (let i = 0;i < rest.length; i += 1) {
30739
+ if (rest[i] !== "--cli" && !rest[i].startsWith("--cli="))
30740
+ continue;
30741
+ if (rest[i].startsWith("--cli="))
30742
+ continue;
30743
+ for (let j = i + 1;j < rest.length && looksLikeCliName(rest[j]); j += 1) {
30744
+ taken.add(j);
30745
+ }
30746
+ }
30747
+ return taken;
30748
+ }
30660
30749
  function packAddSource(rest) {
30661
- const consumed = new Set;
30750
+ const consumed = cliValueIndices(rest);
30662
30751
  for (let i = 0;i < rest.length; i += 1) {
30663
30752
  if (VALUE_FLAGS.has(rest[i]))
30664
30753
  consumed.add(i + 1);
30665
30754
  }
30666
- return rest.find((arg, index) => !arg.startsWith("--") && !consumed.has(index));
30755
+ return rest.find((arg, index) => !arg.startsWith("-") && !consumed.has(index));
30756
+ }
30757
+ function parseCliList(rest) {
30758
+ const idx = rest.findIndex((a) => a === "--cli" || a.startsWith("--cli="));
30759
+ if (idx === -1)
30760
+ return {};
30761
+ const values = [];
30762
+ if (rest[idx].startsWith("--cli=")) {
30763
+ values.push(...rest[idx].slice("--cli=".length).split(","));
30764
+ } else {
30765
+ for (let j = idx + 1;j < rest.length && looksLikeCliName(rest[j]); j += 1) {
30766
+ values.push(...rest[j].split(","));
30767
+ }
30768
+ }
30769
+ const names = values.map((v) => v.trim()).filter(Boolean);
30770
+ if (names.length === 0)
30771
+ return { clis: [] };
30772
+ const known = new Set(INTEGRATION_TYPES);
30773
+ const unknown = names.filter((n) => !known.has(n));
30774
+ if (unknown.length > 0) {
30775
+ const near = (bad) => INTEGRATION_TYPES.find((k) => k.startsWith(bad.slice(0, 3)) || bad.startsWith(k.slice(0, 3)));
30776
+ const hint = unknown.map((u) => near(u) ? `${u} (did you mean ${near(u)}?)` : u).join(", ");
30777
+ return {
30778
+ error: [
30779
+ `Not an agent: ${hint}`,
30780
+ `--cli takes any of: ${INTEGRATION_TYPES.join(", ")}`
30781
+ ]
30782
+ };
30783
+ }
30784
+ return { clis: names };
30785
+ }
30786
+ function selectionFromForTest(rest) {
30787
+ return selectionFrom(rest);
30667
30788
  }
30668
30789
  function selectionFrom(rest) {
30669
30790
  const only = parseList(rest, "--policy") ?? parseList(rest, "--only");
30670
30791
  const categories = parseList(rest, "--category");
30671
- const clis = parseList(rest, "--cli");
30792
+ const parsedClis = parseCliList(rest);
30793
+ if ("error" in parsedClis)
30794
+ return { error: parsedClis.error };
30672
30795
  return {
30673
30796
  ...only ? { only } : {},
30674
30797
  ...categories ? { categories } : {},
30675
- ...clis ? { clis } : {},
30798
+ ...parsedClis.clis ? { clis: parsedClis.clis } : {},
30676
30799
  ...rest.includes("--all") ? { all: true } : {}
30677
30800
  };
30678
30801
  }
@@ -30728,6 +30851,18 @@ async function build(rest) {
30728
30851
  "A pack entry calls customPolicies.add({ name, description, match, fn }) for each one."
30729
30852
  ]);
30730
30853
  }
30854
+ const byName = new Map;
30855
+ for (const hook of hooks)
30856
+ byName.set(hook.name, (byName.get(hook.name) ?? 0) + 1);
30857
+ const duplicates = [...byName.entries()].filter(([, n]) => n > 1).map(([name]) => name);
30858
+ if (duplicates.length > 0) {
30859
+ return fail([
30860
+ duplicates.length === 1 ? `Two policies are called ${JSON.stringify(duplicates[0])}.` : `${duplicates.length} names are used by more than one policy: ${duplicates.join(", ")}.`,
30861
+ "A name is what --policy selects and what the picker toggles, so a pack",
30862
+ "cannot carry the same one twice — one of them would be unreachable.",
30863
+ "Rename or delete one of each pair."
30864
+ ]);
30865
+ }
30731
30866
  const policies = [];
30732
30867
  for (const [index, hook] of hooks.entries()) {
30733
30868
  const extra = hook;
@@ -30783,7 +30918,7 @@ async function runPolicyPicker(action, io = {}) {
30783
30918
  return ok(stack2(emptyState2({
30784
30919
  what: "No policies are installed yet.",
30785
30920
  hint: "Take ours, or anyone's:",
30786
- cmd: "failproofai policies add core"
30921
+ cmd: "failproofai policies add FailproofAI/policies"
30787
30922
  }, opts), note2("Someone else's: failproofai policies add <owner>/<repo>", opts), note2("Look first: failproofai policies show <owner>/<repo>", opts)));
30788
30923
  }
30789
30924
  const rows3 = [];
@@ -31448,7 +31583,9 @@ async function pickFromSource(source, io) {
31448
31583
  async function add(rest) {
31449
31584
  const source = packAddSource(rest);
31450
31585
  const selection = selectionFrom(rest);
31451
- const resolvedSource = source && CORE_ALIASES.has(source.toLowerCase()) ? CORE_SOURCE : source;
31586
+ if (selection.error)
31587
+ return fail(selection.error);
31588
+ const resolvedSource = source;
31452
31589
  if (!resolvedSource) {
31453
31590
  return fail(["Usage: failproofai policies add <source> [--policy a,b] [--category x,y] [--all]"]);
31454
31591
  }
@@ -31630,7 +31767,15 @@ var init_pack_cli = __esm(() => {
31630
31767
  init_pack_store();
31631
31768
  init_custom_hooks_loader();
31632
31769
  init_tui2();
31633
- VALUE_FLAGS = new Set(["--only", "--policy", "--category", "--cli"]);
31770
+ VALUE_FLAGS = new Set([
31771
+ "--only",
31772
+ "--policy",
31773
+ "--category",
31774
+ "--cli",
31775
+ "--scope",
31776
+ "--custom",
31777
+ "-c"
31778
+ ]);
31634
31779
  PUBLISH_VALUE_FLAGS = new Set([
31635
31780
  "--repo",
31636
31781
  "--version",
@@ -33669,11 +33814,11 @@ var init_uninstall_cli = __esm(() => {
33669
33814
  // src/hooks/pack-store.ts
33670
33815
  var exports_pack_store = {};
33671
33816
  __export(exports_pack_store, {
33672
- CORE_ALIASES: () => CORE_ALIASES2,
33673
33817
  CORE_SOURCE: () => CORE_SOURCE2,
33674
33818
  PACK_CHECKSUMS_ASSET: () => PACK_CHECKSUMS_ASSET2,
33675
33819
  PACK_ENTRY_ASSET: () => PACK_ENTRY_ASSET2,
33676
33820
  PACK_MANIFEST_ASSET: () => PACK_MANIFEST_ASSET2,
33821
+ RETIRED_CORE_ALIASES: () => RETIRED_CORE_ALIASES2,
33677
33822
  addPack: () => addPack2,
33678
33823
  addPackFromSource: () => addPackFromSource,
33679
33824
  checkPackArtifact: () => checkPackArtifact2,
@@ -33717,6 +33862,9 @@ function parsePackSpec2(source) {
33717
33862
  const tag = at3 > 0 ? withoutScheme.slice(at3 + 1) : null;
33718
33863
  const slash = repoPart.indexOf("/");
33719
33864
  if (slash <= 0 || slash === repoPart.length - 1) {
33865
+ if (RETIRED_CORE_ALIASES2.has(repoPart.trim().toLowerCase())) {
33866
+ throw new Error(`${JSON.stringify(source)} is no longer a pack name — ours is a pack like anyone else's now. Use ${CORE_SOURCE2} instead.`);
33867
+ }
33720
33868
  throw new Error(`pack source must be owner/repo, owner/repo@tag, or a github.com URL — got ${JSON.stringify(source)}`);
33721
33869
  }
33722
33870
  return validated2(repoPart.slice(0, slash), repoPart.slice(slash + 1), tag);
@@ -33819,8 +33967,7 @@ function writeAtomic2(target, bytes) {
33819
33967
  }
33820
33968
  }
33821
33969
  async function addPackFromSource(source, opts = {}) {
33822
- const resolved2 = CORE_ALIASES2.has(source.trim().toLowerCase()) ? CORE_SOURCE2 : source;
33823
- const result = await addPack2(resolved2, opts);
33970
+ const result = await addPack2(source, opts);
33824
33971
  return {
33825
33972
  id: result.id,
33826
33973
  version: result.version,
@@ -34111,13 +34258,13 @@ function upsertInstalled2(record) {
34111
34258
  function errText3(err) {
34112
34259
  return err instanceof Error ? err.message : String(err);
34113
34260
  }
34114
- var DEFAULT_BASE_URL3 = "https://github.com", FETCH_TIMEOUT_MS2 = 30000, MAX_ARTIFACT_BYTES2, PACK_MANIFEST_ASSET2 = "failproofai-pack.json", PACK_ENTRY_ASSET2 = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET2 = "SHA256SUMS", OWNER_RE2, TAG_RE2, CORE_SOURCE2 = "FailproofAI/policies", CORE_ALIASES2;
34261
+ var DEFAULT_BASE_URL3 = "https://github.com", FETCH_TIMEOUT_MS2 = 30000, MAX_ARTIFACT_BYTES2, PACK_MANIFEST_ASSET2 = "failproofai-pack.json", PACK_ENTRY_ASSET2 = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET2 = "SHA256SUMS", OWNER_RE2, TAG_RE2, CORE_SOURCE2 = "FailproofAI/policies", RETIRED_CORE_ALIASES2;
34115
34262
  var init_pack_store2 = __esm(() => {
34116
34263
  init_pack_manifest();
34117
34264
  MAX_ARTIFACT_BYTES2 = 8 * 1024 * 1024;
34118
34265
  OWNER_RE2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
34119
34266
  TAG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
34120
- CORE_ALIASES2 = new Set(["core", "failproofai", "official"]);
34267
+ RETIRED_CORE_ALIASES2 = new Set(["core", "failproofai", "official"]);
34121
34268
  });
34122
34269
 
34123
34270
  // src/hooks/install-prompt.ts
@@ -34819,7 +34966,7 @@ Validated ${validatedHooks.length} custom hook(s) from ${path3}: ${validatedHook
34819
34966
  } catch (err) {
34820
34967
  console.log(`
34821
34968
  Warning: could not fetch the policy pack (${err instanceof Error ? err.message : String(err)}).
34822
- ` + `Run \`failproofai policies add core\` once you are online.`);
34969
+ ` + `Run \`failproofai policies add FailproofAI/policies\` once you are online.`);
34823
34970
  }
34824
34971
  } else {
34825
34972
  for (const name of fromPack) {
@@ -35098,6 +35245,9 @@ async function listHooks(cwd) {
35098
35245
  ], opts) : nextStep2("failproofai config", config.enabledPolicies.length > 0 ? "These are configured but NOT installed — no hook is running them:" : "Nothing is set up yet. Get started with:", opts));
35099
35246
  }
35100
35247
  const footer = [note2(`Config: ${globalPolicyConfigFile()}`, opts)];
35248
+ if (packCount === 0) {
35249
+ footer.unshift(nextStep2(`failproofai policies add ${CORE_SOURCE}`, "Nothing is enforcing yet. Take ours, or anyone's:", opts), note2("Someone else's: failproofai policies add <owner>/<repo>", opts), note2("Look first: failproofai policies show <owner>/<repo>", opts));
35250
+ }
35101
35251
  if (installedScopes.length > 1) {
35102
35252
  footer.push(warning2([
35103
35253
  `Hooks in multiple scopes (${installedScopes.join(", ")}).`,
@@ -35530,6 +35680,12 @@ async function runPostSetupAudit2() {
35530
35680
  printSummary2(result);
35531
35681
  process.stdout.write(`
35532
35682
  `);
35683
+ if (!hasInstalledPacks()) {
35684
+ process.stdout.write(` ${c2(DIM2, "none of this is being enforced yet. take ours, or anyone's:")}
35685
+ ` + ` ${c2(CYAN2, `failproofai policies add ${CORE_SOURCE}`)}
35686
+
35687
+ `);
35688
+ }
35533
35689
  } finally {
35534
35690
  attempt.lock.release();
35535
35691
  }
@@ -35659,6 +35815,8 @@ Two audits write the same cache files, so this one won't start. Try again shortl
35659
35815
  var DASHBOARD_PORT2 = 8020, EXIT_AUDIT_ALREADY_RUNNING2 = 75, AUDIT_STAGES2, HELP_DESC_COL2 = 40, RESET2, DIM2, BOLD2, PINK2, GREEN2, CYAN2, SPINNER2;
35660
35816
  var init_cli2 = __esm(() => {
35661
35817
  init_audit();
35818
+ init_pack_manifest();
35819
+ init_pack_store();
35662
35820
  init_audit_lock();
35663
35821
  init_dashboard_cache();
35664
35822
  init_hook_telemetry2();
@@ -36148,7 +36306,7 @@ import { realpathSync as realpathSync6 } from "fs";
36148
36306
  import { dirname as dirname27, resolve as resolve37 } from "path";
36149
36307
  import { fileURLToPath as fileURLToPath4 } from "url";
36150
36308
  // package.json
36151
- var version = "1.0.2-beta.5";
36309
+ var version = "1.0.2-beta.6";
36152
36310
 
36153
36311
  // bin/failproofai.mjs
36154
36312
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
@@ -36547,10 +36705,10 @@ Run \`failproofai backfill --help\` for usage.`);
36547
36705
  failproofai pack \u2014 install policy packs published as GitHub releases
36548
36706
 
36549
36707
  Usage:
36550
- failproofai pack add core the Failproof AI policies
36551
- failproofai pack add core --policy <a,b> just these
36552
- failproofai pack add core --category <x,y> whole categories
36553
- failproofai pack add core --all everything in it
36708
+ failproofai policies add FailproofAI/policies the Failproof AI policies
36709
+ failproofai policies add FailproofAI/policies --policy <a,b> just these
36710
+ failproofai policies add FailproofAI/policies --category <x,y> whole categories
36711
+ failproofai policies add FailproofAI/policies --all everything in it
36554
36712
  failproofai pack list packs installed here
36555
36713
  failproofai pack list <source> what a pack out there contains
36556
36714
  failproofai pack add <source> [--policy <a,b>] [--category <x,y>] [--all]
@@ -36561,7 +36719,7 @@ A pack is one entry artifact plus a manifest, verified against the release's
36561
36719
  SHA256SUMS at install time. The digest is recorded, and re-verified before every
36562
36720
  import \u2014 so a pack cannot change under this machine after you installed it.
36563
36721
 
36564
- <source> is core for the Failproof AI policies, or any of these:
36722
+ <source> is any GitHub repo publishing a pack \u2014 ours included:
36565
36723
  acme/support-agent newest release, pinned to its exact tag
36566
36724
  acme/support-agent@v2.1.0 that release
36567
36725
  github:acme/support-agent@v2.1.0 same, explicit
@@ -36583,7 +36741,7 @@ rest back on.
36583
36741
 
36584
36742
  Examples:
36585
36743
  failproofai pack add FailproofAI/policies
36586
- failproofai pack add core --category sanitize,git
36744
+ failproofai policies add FailproofAI/policies --category sanitize,git
36587
36745
  failproofai pack add github:acme/support-agent@v2.1.0 --only block-refunds
36588
36746
  failproofai pack remove acme/support-agent
36589
36747
 
@@ -37093,7 +37251,7 @@ USAGE
37093
37251
  failproofai policies add Pick from what is installed here
37094
37252
  failproofai policies add <name> Turn one policy on
37095
37253
  failproofai policies add <owner>/<repo> Install someone's pack
37096
- failproofai policies add core Our pack, fetched like any other
37254
+ failproofai policies add FailproofAI/policies Our pack, fetched like any other
37097
37255
  failproofai policies remove <name> Turn one policy off
37098
37256
  failproofai policies remove <pack-id> Uninstall a pack
37099
37257
  failproofai policies show <owner>/<repo> What a pack contains, before you take it
@@ -37126,7 +37284,7 @@ EXAMPLES
37126
37284
  failproofai policies add
37127
37285
  failproofai policies add block-sudo
37128
37286
  failproofai policies add sanitize-api-keys --scope project
37129
- failproofai policies add core --category sanitize,git
37287
+ failproofai policies add FailproofAI/policies --category sanitize,git
37130
37288
  failproofai policies add acme/deploy-guard --policy block-prod-deploy
37131
37289
  failproofai policies show acme/deploy-guard
37132
37290
  failproofai policies remove block-sudo
@@ -37139,11 +37297,22 @@ keep enforcing. FAILPROOFAI_PACK_BASE_URL points it all at a mirror.
37139
37297
  }
37140
37298
  const action = subArgs[0];
37141
37299
  const rest = subArgs.slice(1);
37142
- const firstPositional = rest.find((a) => !a.startsWith("-"));
37300
+ const VALUE_TAKING = new Set(["--scope", "--policy", "--category", "--only", "--custom", "-c"]);
37301
+ const skip = new Set;
37302
+ for (let i = 0;i < rest.length; i += 1) {
37303
+ if (VALUE_TAKING.has(rest[i]))
37304
+ skip.add(i + 1);
37305
+ if (rest[i] !== "--cli")
37306
+ continue;
37307
+ for (let j = i + 1;j < rest.length && !rest[j].startsWith("-") && !rest[j].includes("/"); j += 1) {
37308
+ skip.add(j);
37309
+ }
37310
+ }
37311
+ const firstPositional = rest.find((a, i) => !a.startsWith("-") && !skip.has(i));
37143
37312
  let looksLikeSource = !!firstPositional && (firstPositional.includes("/") || firstPositional.startsWith("github:"));
37144
37313
  if (!looksLikeSource && firstPositional) {
37145
- const { CORE_ALIASES: CORE_ALIASES3 } = await Promise.resolve().then(() => (init_pack_store2(), exports_pack_store));
37146
- looksLikeSource = CORE_ALIASES3.has(firstPositional.toLowerCase());
37314
+ const { RETIRED_CORE_ALIASES: RETIRED_CORE_ALIASES3 } = await Promise.resolve().then(() => (init_pack_store2(), exports_pack_store));
37315
+ looksLikeSource = RETIRED_CORE_ALIASES3.has(firstPositional.toLowerCase());
37147
37316
  }
37148
37317
  if (action === "show" || looksLikeSource) {
37149
37318
  if (action === "show" && !firstPositional) {
@@ -37322,8 +37491,8 @@ EXAMPLES
37322
37491
  failproofai policies --uninstall --cli pi
37323
37492
  failproofai policies -u
37324
37493
  failproofai policies --uninstall --custom
37325
- failproofai pack add core
37326
- failproofai pack add core --category git,database
37494
+ failproofai policies add FailproofAI/policies
37495
+ failproofai policies add FailproofAI/policies --category git,database
37327
37496
  failproofai pack list acme/support-agent
37328
37497
  failproofai pack build ./my-policies.mjs --id acme/support --version 1.0.0
37329
37498
  `.trimStart());
@@ -37479,19 +37648,38 @@ Run \`failproofai policies --help\` for usage.`);
37479
37648
  if (args[0] === "config") {
37480
37649
  if (args.includes("--help") || args.includes("-h")) {
37481
37650
  console.log(`
37482
- failproofai config \u2014 interactive setup
37651
+ failproofai config \u2014 set this machine up
37483
37652
 
37484
37653
  USAGE
37485
- failproofai config Guided setup: choose scope, agents, and policies
37654
+ failproofai config Guided setup
37486
37655
  failproofai configure Alias for config
37487
37656
  failproofai setup Alias for config
37488
37657
 
37489
37658
  WHAT IT DOES
37490
- Walks you through 4 quick steps and writes everything for you:
37491
- 1. Where \u2014 global (all projects) or just this project
37492
- 2. Harnesses \u2014 which agent CLIs to protect (Claude, Codex, ...)
37493
- 3. Policies \u2014 presets (combine any), Everything, or a custom pick
37494
- 4. Review \u2014 confirms the exact files it will change, then applies
37659
+ Installs the failproofaid service (needs root once), wires hooks into every
37660
+ agent CLI it supports, and optionally connects this machine to Cloud. It
37661
+ chooses NO policies \u2014 take some with 'failproofai policies add <owner>/<repo>'.
37662
+
37663
+ WITH NO TERMINAL (CI, containers, an agent driving it)
37664
+ It just runs. There is nothing to confirm when nobody is watching, so it
37665
+ applies rather than asking \u2014 no flag needed.
37666
+
37667
+ failproofai config set up, stay local
37668
+ failproofai config --token <key> set up and connect to Cloud
37669
+ --url <url> somewhere other than app.befailproof.ai
37670
+ --machine-id <id> defaults to a stable per-machine key
37671
+ --machine-label <name> defaults to this host's name
37672
+ --no-transcripts decisions only, no session transcripts
37673
+
37674
+ The key can come from FAILPROOFAI_CLOUD_TOKEN instead of the command line,
37675
+ and the url from FAILPROOFAI_CLOUD_URL \u2014 the same variable the daemon reads.
37676
+ Prefer the environment: an argument is readable from 'ps' by every user on
37677
+ the box, and lands in shell history and CI logs.
37678
+
37679
+ Exit 1 if anything it was asked to do did not happen \u2014 including a key the
37680
+ server refused, and a machine that could not reach root. 'sudo' is never
37681
+ prompted for here; it either works without a password or you are told the
37682
+ exact commands to run.
37495
37683
 
37496
37684
  FAILPROOF CLOUD
37497
37685
  failproofai config --connect <url> --token <key> [--machine-id <id>]
@@ -37630,8 +37818,23 @@ PAUSING ENFORCEMENT (one session, always time-boxed)
37630
37818
  await exitAfterFlush(result2.exitCode);
37631
37819
  return;
37632
37820
  }
37821
+ const valueFor = (flag) => {
37822
+ const i = args.indexOf(flag);
37823
+ if (i < 0)
37824
+ return;
37825
+ const v = args[i + 1];
37826
+ if (!v || v.startsWith("-"))
37827
+ throw new CliError3(`Missing value after ${flag}.`);
37828
+ return v;
37829
+ };
37633
37830
  const { runConfigureWizard: runConfigureWizard2 } = await Promise.resolve().then(() => (init_configure_wizard(), exports_configure_wizard));
37634
- const result = await runConfigureWizard2();
37831
+ const result = await runConfigureWizard2({}, {
37832
+ token: valueFor("--token") ?? process.env.FAILPROOFAI_CLOUD_TOKEN,
37833
+ url: valueFor("--url") ?? process.env.FAILPROOFAI_CLOUD_URL,
37834
+ machineId: valueFor("--machine-id"),
37835
+ machineLabel: valueFor("--machine-label"),
37836
+ noTranscripts: args.includes("--no-transcripts")
37837
+ });
37635
37838
  await track("cli_configure_invoked", {
37636
37839
  applied: result.applied,
37637
37840
  target: result.target ?? null,