failproofai 0.0.11-beta.9 → 0.0.11

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 (152) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  22. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/.next/standalone/.next/server/app/api/audit/invite/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/logout/route.js +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/status/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  42. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/index.html +1 -1
  45. package/.next/standalone/.next/server/app/index.rsc +15 -15
  46. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  47. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  48. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  49. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  50. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  55. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  65. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/chunks/[externals]__1_g_b3t._.js +3 -0
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0dwpg-h._.js +3 -0
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lnenda._.js +3 -0
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13i_sva._.js +3 -0
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_mqemn._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/node_modules_0-tu4ot._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/node_modules_1bnh1y0._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +3 -3
  75. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0e446gb._.js → [root-of-the-server]__00uwqi6._.js} +2 -2
  79. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0808sha._.js +2 -2
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e4-6d8._.js +2 -2
  81. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehe24g._.js +2 -2
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g253ve._.js +2 -2
  83. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0k65l27._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wprfyc._.js → [root-of-the-server]__0kjb_s4._.js} +2 -2
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vxf0_g._.js +2 -2
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12mcauo._.js +2 -2
  87. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mt35_w._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pcxxwg._.js +2 -2
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1uvfwgr._.js +2 -2
  90. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +49 -21
  92. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0y4a-0q.js → node_modules_html-to-image_es_index_0ihmbv4.js} +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  96. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  97. package/.next/standalone/.next/server/pages/404.html +1 -1
  98. package/.next/standalone/.next/server/pages/500.html +1 -1
  99. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  100. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  101. package/.next/standalone/.next/static/chunks/{3ty6dhcuogout.js → 02fywjt0by40a.js} +1 -1
  102. package/.next/standalone/.next/static/chunks/0xdx2ehtbdoeg.js +1 -0
  103. package/.next/standalone/.next/static/chunks/{07_d165p5h5ys.js → 1-a5rvq67k7ed.js} +1 -1
  104. package/.next/standalone/.next/static/chunks/{3nj6g3xu9uy78.js → 15csyj1_rf0-w.js} +1 -1
  105. package/.next/standalone/.next/static/chunks/1o0xa47736gi9.css +2 -0
  106. package/.next/standalone/.next/static/chunks/24cv31x607n7k.js +1 -0
  107. package/.next/standalone/.next/static/chunks/2n_s8v1ae38_a.js +69 -0
  108. package/.next/standalone/.next/static/chunks/{277oc363p56n6.js → 2y-jmvrjxz60x.js} +2 -2
  109. package/.next/standalone/.next/static/chunks/{1kvadxkgnapyj.js → 3eik_d9qrvoft.js} +1 -1
  110. package/.next/standalone/.next/static/chunks/{168k-8z6k7e8z.css → 3i27c3hcriawq.css} +1 -1
  111. package/.next/standalone/.next/static/chunks/{2z42u62k-8-_q.js → 3v61675vr6jav.js} +1 -1
  112. package/.next/standalone/app/api/audit/invite/route.ts +10 -1
  113. package/.next/standalone/app/api/audit/run/route.ts +35 -0
  114. package/.next/standalone/app/api/auth/login-request/route.ts +2 -2
  115. package/.next/standalone/app/api/auth/login-verify/route.ts +10 -2
  116. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +9 -1
  117. package/.next/standalone/app/audit/_components/audit-poster.tsx +11 -7
  118. package/.next/standalone/app/audit/_components/auth-dialog.tsx +6 -4
  119. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +23 -3
  120. package/.next/standalone/app/audit/_components/invite-dialog.tsx +6 -3
  121. package/.next/standalone/app/audit/_components/share-templates.ts +58 -28
  122. package/.next/standalone/app/audit/audit-styles.css +17 -22
  123. package/.next/standalone/app/globals.css +27 -2
  124. package/.next/standalone/app/policies/hooks-client.tsx +33 -24
  125. package/.next/standalone/components/reach-developers.tsx +10 -25
  126. package/.next/standalone/lib/auth/api-server-client.ts +5 -2
  127. package/.next/standalone/lib/client-telemetry.ts +4 -0
  128. package/.next/standalone/package.json +6 -4
  129. package/.next/standalone/server.js +1 -1
  130. package/README.md +2 -2
  131. package/bin/failproofai.mjs +24 -5
  132. package/dist/cli.mjs +2328 -381
  133. package/lib/auth/api-server-client.ts +5 -2
  134. package/lib/client-telemetry.ts +4 -0
  135. package/package.json +6 -4
  136. package/scripts/launch.ts +30 -4
  137. package/scripts/postinstall.mjs +10 -1
  138. package/scripts/skew-log-filter.ts +46 -0
  139. package/scripts/validate-mdx.ts +139 -0
  140. package/src/audit/cli.ts +330 -0
  141. package/src/audit/open-browser.ts +69 -0
  142. package/src/auth/cli.ts +16 -13
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1r1h8v9._.js +0 -3
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uatkiv._.js +0 -3
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1y6gxxb._.js +0 -3
  146. package/.next/standalone/.next/static/chunks/28mkxkl_d91-l.js +0 -1
  147. package/.next/standalone/.next/static/chunks/28x7jvo3kxd3u.js +0 -41
  148. package/.next/standalone/.next/static/chunks/29nrs5xs9c4hx.css +0 -2
  149. package/.next/standalone/.next/static/chunks/29tg7deqmq32l.js +0 -1
  150. /package/.next/standalone/.next/static/{NYPiJP6Rv_exQdSFVS8HP → P_MIRSeoE296wkbE-Icin}/_buildManifest.js +0 -0
  151. /package/.next/standalone/.next/static/{NYPiJP6Rv_exQdSFVS8HP → P_MIRSeoE296wkbE-Icin}/_clientMiddlewareManifest.js +0 -0
  152. /package/.next/standalone/.next/static/{NYPiJP6Rv_exQdSFVS8HP → P_MIRSeoE296wkbE-Icin}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -16,7 +16,7 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // package.json
19
- var version2 = "0.0.11-beta.9";
19
+ var version2 = "0.0.11";
20
20
  var init_package = () => {};
21
21
 
22
22
  // src/posthog-key.ts
@@ -656,6 +656,14 @@ function clearPolicies() {
656
656
  g[REGISTRY_KEY] = [];
657
657
  setIndexCache(null);
658
658
  }
659
+ function getAllPolicies() {
660
+ return [...getRegistry()];
661
+ }
662
+ function setAllPolicies(policies) {
663
+ const g = globalThis;
664
+ g[REGISTRY_KEY] = [...policies];
665
+ setIndexCache(null);
666
+ }
659
667
  var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
660
668
 
661
669
  // src/hooks/builtin-policies.ts
@@ -4883,6 +4891,10 @@ async function parseFileContent(fileContent, source) {
4883
4891
  entries.sort((a, b) => a.timestampMs - b.timestampMs);
4884
4892
  return { entries, rawLines, subagentIds: Array.from(subagentIdSet) };
4885
4893
  }
4894
+ async function parseLogContent(fileContent, source = "session") {
4895
+ const result = await parseFileContent(fileContent, source);
4896
+ return result.entries;
4897
+ }
4886
4898
  async function parseSessionLog(projectName, sessionId) {
4887
4899
  const projectDir = resolveProjectPath(projectName);
4888
4900
  const projectsPath = getClaudeProjectsPath();
@@ -9039,7 +9051,7 @@ async function runLogin() {
9039
9051
  const nowSecs = Math.floor(Date.now() / 1000);
9040
9052
  const refreshUsable = existing.refresh_expires_at > nowSecs;
9041
9053
  if (refreshUsable) {
9042
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9054
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9043
9055
  source: "cli",
9044
9056
  status: "already_signed_in",
9045
9057
  user_id: existing.user.id
@@ -9072,7 +9084,7 @@ async function runLogin() {
9072
9084
  email = "";
9073
9085
  }
9074
9086
  if (!email) {
9075
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9087
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9076
9088
  source: "cli",
9077
9089
  status: "aborted_invalid_email"
9078
9090
  });
@@ -9091,7 +9103,7 @@ ${GREEN}code sent.${RESET} ${DIM}check ${email} — expires in ${r.expires_in}s.
9091
9103
  `);
9092
9104
  } catch (err) {
9093
9105
  const isApi = err instanceof AuthApiError;
9094
- trackHookEvent2(getInstanceId2(), "audit_otp_requested", {
9106
+ await trackHookEvent2(getInstanceId2(), "audit_otp_requested", {
9095
9107
  source: "cli",
9096
9108
  status: "failed",
9097
9109
  error_code: isApi ? err.code : "upstream_unreachable",
@@ -9118,7 +9130,7 @@ ${GREEN}code sent.${RESET} ${DIM}check ${email} — expires in ${r.expires_in}s.
9118
9130
  break;
9119
9131
  } catch (err) {
9120
9132
  const isApi = err instanceof AuthApiError;
9121
- trackHookEvent2(getInstanceId2(), "audit_otp_verified", {
9133
+ await trackHookEvent2(getInstanceId2(), "audit_otp_verified", {
9122
9134
  source: "cli",
9123
9135
  status: "failed",
9124
9136
  attempt: verifyAttempts,
@@ -9137,7 +9149,7 @@ ${GREEN}code sent.${RESET} ${DIM}check ${email} — expires in ${r.expires_in}s.
9137
9149
  }
9138
9150
  }
9139
9151
  if (!tokenResp) {
9140
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9152
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9141
9153
  source: "cli",
9142
9154
  status: "exhausted_attempts",
9143
9155
  attempts: verifyAttempts
@@ -9145,21 +9157,21 @@ ${GREEN}code sent.${RESET} ${DIM}check ${email} — expires in ${r.expires_in}s.
9145
9157
  throw new CliError("Too many bad codes — start over.");
9146
9158
  }
9147
9159
  writeAuth(authFromTokenResponse(tokenResp));
9148
- trackHookEvent2(getInstanceId2(), "audit_otp_verified", {
9160
+ await trackHookEvent2(getInstanceId2(), "audit_otp_verified", {
9149
9161
  source: "cli",
9150
9162
  status: "success",
9151
9163
  attempt: verifyAttempts,
9152
9164
  user_id: tokenResp.user.id,
9153
9165
  email: tokenResp.user.email
9154
9166
  });
9155
- trackHookEvent2(getInstanceId2(), "audit_user_identity_linked", {
9167
+ await trackHookEvent2(getInstanceId2(), "audit_user_identity_linked", {
9156
9168
  source: "cli",
9157
9169
  user_id: tokenResp.user.id,
9158
9170
  email: tokenResp.user.email,
9159
9171
  local_random_id: getInstanceId2(),
9160
9172
  $set: { email: tokenResp.user.email, user_id: tokenResp.user.id }
9161
9173
  });
9162
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9174
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_login_completed", {
9163
9175
  source: "cli",
9164
9176
  status: "success",
9165
9177
  attempts: verifyAttempts,
@@ -9173,7 +9185,7 @@ ${GREEN}✓ signed in as ${tokenResp.user.email}${RESET}
9173
9185
  async function runLogout() {
9174
9186
  const existing = readAuth();
9175
9187
  if (!existing) {
9176
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_logout_completed", {
9188
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_logout_completed", {
9177
9189
  source: "cli",
9178
9190
  had_session: false,
9179
9191
  upstream: "noop"
@@ -9191,7 +9203,7 @@ async function runLogout() {
9191
9203
  }
9192
9204
  }
9193
9205
  deleteAuth();
9194
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_logout_completed", {
9206
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_logout_completed", {
9195
9207
  source: "cli",
9196
9208
  had_session: true,
9197
9209
  upstream,
@@ -9200,10 +9212,10 @@ async function runLogout() {
9200
9212
  process.stdout.write(`${GREEN}✓ signed out as ${existing.user.email}.${RESET}
9201
9213
  `);
9202
9214
  }
9203
- function runWhoami() {
9215
+ async function runWhoami() {
9204
9216
  const existing = readAuth();
9205
9217
  if (!existing) {
9206
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_whoami", {
9218
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_whoami", {
9207
9219
  source: "cli",
9208
9220
  authenticated: false
9209
9221
  });
@@ -9212,7 +9224,7 @@ function runWhoami() {
9212
9224
  process.exitCode = 1;
9213
9225
  return;
9214
9226
  }
9215
- trackHookEvent2(getInstanceId2(), "audit_cli_auth_whoami", {
9227
+ await trackHookEvent2(getInstanceId2(), "audit_cli_auth_whoami", {
9216
9228
  source: "cli",
9217
9229
  authenticated: true,
9218
9230
  user_id: existing.user.id
@@ -9267,180 +9279,2269 @@ EXAMPLES
9267
9279
  SUBCOMMANDS = new Set(["login", "logout", "whoami", "help"]);
9268
9280
  });
9269
9281
 
9270
- // src/hooks/manager.ts
9271
- import { execSync as execSync6 } from "node:child_process";
9272
- import { resolve as resolve12, basename as basename4 } from "node:path";
9273
- import { homedir as homedir20, platform as platform2, arch as arch2, release as release2, hostname as hostname2 } from "node:os";
9274
- function getSettingsPath2(scope, cwd) {
9275
- return claudeCode.getSettingsPath(scope, cwd);
9276
- }
9277
- function scopeLabel2(scope) {
9278
- switch (scope) {
9279
- case "user":
9280
- return `~/.claude/settings.json`;
9281
- case "project":
9282
- return `{cwd}/.claude/settings.json`;
9283
- case "local":
9284
- return `{cwd}/.claude/settings.local.json`;
9285
- }
9286
- }
9287
- function resolveFailproofaiBinary2() {
9288
- const override = process.env.FAILPROOFAI_BINARY_OVERRIDE;
9289
- if (override && override.trim())
9290
- return override.trim();
9282
+ // lib/claude-sessions.ts
9283
+ import { readdirSync as readdirSync8, statSync as statSync10, existsSync as existsSync14 } from "node:fs";
9284
+ import { join as join19, basename as basename4 } from "node:path";
9285
+ function getClaudeProjectsRoot() {
9286
+ return getClaudeProjectsPath();
9287
+ }
9288
+ function listClaudeProjects() {
9289
+ const root = getClaudeProjectsRoot();
9290
+ let entries;
9291
9291
  try {
9292
- const cmd = process.platform === "win32" ? "where failproofai" : "which failproofai";
9293
- const result = execSync6(cmd, { encoding: "utf8" }).trim();
9294
- return result.split(`
9295
- `)[0].trim();
9292
+ entries = readdirSync8(root, { withFileTypes: true });
9296
9293
  } catch {
9297
- throw new CliError(`failproofai binary not found in PATH.
9298
- ` + "Install it globally first: npm install -g failproofai");
9299
- }
9300
- }
9301
- function validatePolicyNames2(names) {
9302
- const invalid = names.filter((n) => !VALID_POLICY_NAMES2.has(n));
9303
- if (invalid.length > 0) {
9304
- const validList = [...VALID_POLICY_NAMES2].join(", ");
9305
- throw new CliError(`Unknown policy name(s): ${invalid.join(", ")}
9306
- ` + `Valid policies: ${validList}`);
9294
+ return [];
9307
9295
  }
9296
+ return entries.filter((e) => e.isDirectory()).map((e) => ({
9297
+ name: e.name,
9298
+ cwd: decodeFolderName(e.name),
9299
+ path: join19(root, e.name)
9300
+ }));
9308
9301
  }
9309
- function deduplicateScopes2(scopes, cwd) {
9310
- const seen = new Set;
9311
- return scopes.filter((s) => {
9312
- const p = getSettingsPath2(s, cwd);
9313
- if (seen.has(p))
9314
- return false;
9315
- seen.add(p);
9316
- return true;
9317
- });
9318
- }
9319
- function hooksInstalledInSettings2(scope, cwd) {
9320
- return claudeCode.hooksInstalledInSettings(scope, cwd);
9321
- }
9322
- async function installHooks2(policyNames, scope = "user", cwd, includeBeta = false, source, customPoliciesPath, removeCustomHooks = false, cli) {
9323
- if (policyNames !== undefined && policyNames.length > 0) {
9324
- const nonAllNames = policyNames.filter((n) => n !== "all");
9325
- if (nonAllNames.length > 0)
9326
- validatePolicyNames2(nonAllNames);
9327
- if (policyNames.includes("all") && nonAllNames.length > 0) {
9328
- throw new CliError(`"all" cannot be combined with specific policy names.
9329
- ` + `Use either: --install all or --install block-sudo sanitize-jwt ...`);
9330
- }
9302
+ function listClaudeTranscripts(project) {
9303
+ const out = [];
9304
+ let entries;
9305
+ try {
9306
+ entries = readdirSync8(project.path, { withFileTypes: true });
9307
+ } catch {
9308
+ return out;
9331
9309
  }
9332
- const selectedClis = cli && cli.length > 0 ? [...new Set(cli)] : ["claude"];
9333
- for (const cliId of selectedClis) {
9334
- const integration = getIntegration(cliId);
9335
- if (!integration.scopes.includes(scope)) {
9310
+ for (const entry of entries) {
9311
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) {
9312
+ const sessionId = entry.name.slice(0, -".jsonl".length);
9313
+ if (!UUID_RE4.test(sessionId))
9314
+ continue;
9315
+ const transcriptPath = join19(project.path, entry.name);
9336
9316
  try {
9337
- await trackHookEvent2(getInstanceId2(), "scope_validation_failed", {
9338
- cli: cliId,
9339
- scope,
9340
- supported_scopes: integration.scopes
9317
+ const s = statSync10(transcriptPath);
9318
+ out.push({
9319
+ projectName: project.name,
9320
+ cwd: project.cwd,
9321
+ sessionId,
9322
+ transcriptPath,
9323
+ mtimeMs: s.mtimeMs,
9324
+ sizeBytes: s.size,
9325
+ isSubagent: false
9341
9326
  });
9342
9327
  } catch {}
9343
- throw new CliError(`Scope "${scope}" is not supported by ${integration.displayName}. ` + `Valid scopes: ${integration.scopes.join(", ")}`);
9328
+ } else if (entry.isDirectory() && UUID_RE4.test(entry.name)) {
9329
+ const subDir = join19(project.path, entry.name, "subagents");
9330
+ if (!existsSync14(subDir))
9331
+ continue;
9332
+ let subEntries;
9333
+ try {
9334
+ subEntries = readdirSync8(subDir, { withFileTypes: true });
9335
+ } catch {
9336
+ continue;
9337
+ }
9338
+ for (const sub of subEntries) {
9339
+ if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
9340
+ continue;
9341
+ const agentId = sub.name.slice(0, -".jsonl".length);
9342
+ const transcriptPath = join19(subDir, sub.name);
9343
+ try {
9344
+ const s = statSync10(transcriptPath);
9345
+ out.push({
9346
+ projectName: project.name,
9347
+ cwd: project.cwd,
9348
+ sessionId: agentId,
9349
+ transcriptPath,
9350
+ mtimeMs: s.mtimeMs,
9351
+ sizeBytes: s.size,
9352
+ isSubagent: true
9353
+ });
9354
+ } catch {}
9355
+ }
9344
9356
  }
9345
9357
  }
9346
- const binaryPath = resolveFailproofaiBinary2();
9347
- const previousConfig = readScopedHooksConfig(scope, cwd);
9348
- const previousEnabled = new Set(previousConfig.enabledPolicies);
9349
- let selectedPolicies;
9350
- if (policyNames !== undefined) {
9351
- let incoming;
9352
- if (policyNames.length === 1 && policyNames[0] === "all") {
9353
- incoming = BUILTIN_POLICIES.filter((p) => includeBeta || !p.beta).map((p) => p.name);
9354
- } else {
9355
- incoming = policyNames;
9358
+ return out;
9359
+ }
9360
+ var UUID_RE4;
9361
+ var init_claude_sessions = __esm(() => {
9362
+ init_paths();
9363
+ UUID_RE4 = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
9364
+ });
9365
+
9366
+ // src/audit/types.ts
9367
+ var AUDIT_TOOL_RESULT_MAX_BYTES, AUDIT_EXAMPLE_MAX_CHARS = 80, AUDIT_MAX_EXAMPLES_PER_NAME = 3;
9368
+ var init_types2 = __esm(() => {
9369
+ AUDIT_TOOL_RESULT_MAX_BYTES = 64 * 1024;
9370
+ });
9371
+
9372
+ // src/audit/cli-adapters/shared.ts
9373
+ function truncateToUtf8Bytes(s, maxBytes) {
9374
+ const buf = Buffer.from(s, "utf-8");
9375
+ if (buf.byteLength <= maxBytes)
9376
+ return s;
9377
+ let end = maxBytes;
9378
+ while (end > 0 && (buf[end] & 192) === 128)
9379
+ end--;
9380
+ return buf.subarray(0, end).toString("utf-8");
9381
+ }
9382
+ function logEntriesToEvents(entries, ctx) {
9383
+ const events = [];
9384
+ for (const entry of entries) {
9385
+ if (entry.type !== "assistant")
9386
+ continue;
9387
+ for (const block of entry.message.content) {
9388
+ if (block.type !== "tool_use")
9389
+ continue;
9390
+ const rawName = block.name;
9391
+ const canonicalName = canonicalizeToolName(rawName, ctx.cli) ?? rawName;
9392
+ const canonicalInput = canonicalizeToolInput(canonicalName, block.input, ctx.cli);
9393
+ let toolResultText;
9394
+ if (block.result?.content) {
9395
+ toolResultText = truncateToUtf8Bytes(block.result.content, AUDIT_TOOL_RESULT_MAX_BYTES);
9396
+ }
9397
+ events.push({
9398
+ cli: ctx.cli,
9399
+ sessionId: ctx.sessionId,
9400
+ transcriptPath: ctx.transcriptPath,
9401
+ cwd: ctx.cwd,
9402
+ timestamp: entry.timestamp,
9403
+ toolName: canonicalName,
9404
+ rawToolName: rawName,
9405
+ toolInput: canonicalInput ?? {},
9406
+ toolResultText
9407
+ });
9356
9408
  }
9357
- selectedPolicies = [...new Set([...previousConfig.enabledPolicies, ...incoming])];
9358
- } else {
9359
- const preSelected = previousConfig.enabledPolicies.length > 0 ? previousConfig.enabledPolicies : undefined;
9360
- selectedPolicies = await promptPolicySelection(preSelected, { includeBeta });
9361
9409
  }
9362
- const configToWrite = { ...previousConfig, enabledPolicies: selectedPolicies };
9363
- if (removeCustomHooks) {
9364
- delete configToWrite.customPoliciesPath;
9365
- } else if (customPoliciesPath) {
9366
- configToWrite.customPoliciesPath = resolve12(customPoliciesPath);
9367
- let validatedHooks = [];
9410
+ return events;
9411
+ }
9412
+ var init_shared = __esm(() => {
9413
+ init_tool_name_canonicalize();
9414
+ init_types2();
9415
+ });
9416
+
9417
+ // src/audit/cli-adapters/claude.ts
9418
+ import { readFile as readFile13 } from "node:fs/promises";
9419
+ async function listClaudeTranscriptMetadata(opts = {}) {
9420
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9421
+ const sinceMs = opts.sinceMs ?? 0;
9422
+ const out = [];
9423
+ for (const project of listClaudeProjects()) {
9424
+ if (projectFilter && !projectFilter.has(project.cwd))
9425
+ continue;
9426
+ let transcripts;
9368
9427
  try {
9369
- validatedHooks = await loadCustomHooks(configToWrite.customPoliciesPath, { strict: true });
9370
- } catch (err) {
9371
- const msg = err instanceof Error ? err.message : String(err);
9428
+ transcripts = listClaudeTranscripts(project);
9429
+ } catch {
9430
+ continue;
9431
+ }
9432
+ for (const t of transcripts) {
9433
+ if (t.mtimeMs < sinceMs)
9434
+ continue;
9435
+ out.push({
9436
+ cli: "claude",
9437
+ projectName: project.name,
9438
+ sessionId: t.sessionId,
9439
+ transcriptPath: t.transcriptPath,
9440
+ mtimeMs: t.mtimeMs,
9441
+ sizeBytes: t.sizeBytes
9442
+ });
9443
+ }
9444
+ }
9445
+ return out;
9446
+ }
9447
+ async function streamClaudeEvents(meta) {
9448
+ let content;
9449
+ try {
9450
+ content = await readFile13(meta.transcriptPath, "utf-8");
9451
+ } catch {
9452
+ return [];
9453
+ }
9454
+ const source = "session";
9455
+ let entries;
9456
+ try {
9457
+ entries = await parseLogContent(content, source);
9458
+ } catch {
9459
+ return [];
9460
+ }
9461
+ let cwd = "";
9462
+ for (const line of content.split(`
9463
+ `, 50)) {
9464
+ if (!line.trim())
9465
+ continue;
9466
+ try {
9467
+ const parsed = JSON.parse(line);
9468
+ if (typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
9469
+ cwd = parsed.cwd;
9470
+ break;
9471
+ }
9472
+ } catch {}
9473
+ }
9474
+ return logEntriesToEvents(entries, {
9475
+ cli: "claude",
9476
+ sessionId: meta.sessionId,
9477
+ transcriptPath: meta.transcriptPath,
9478
+ cwd
9479
+ });
9480
+ }
9481
+ var init_claude = __esm(() => {
9482
+ init_claude_sessions();
9483
+ init_log_entries();
9484
+ init_shared();
9485
+ });
9486
+
9487
+ // src/audit/cli-adapters/codex.ts
9488
+ import { statSync as statSync11 } from "node:fs";
9489
+ async function listCodexTranscriptMetadata(opts = {}) {
9490
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9491
+ const sinceMs = opts.sinceMs ?? 0;
9492
+ const out = [];
9493
+ const projects = await getCodexProjects();
9494
+ for (const project of projects) {
9495
+ const { cwd, sessions } = await getCodexSessionsByEncodedName(project.name);
9496
+ const effectiveCwd = cwd ?? "";
9497
+ if (projectFilter && !projectFilter.has(effectiveCwd))
9498
+ continue;
9499
+ for (const s of sessions) {
9500
+ const mtimeMs = s.lastModified.getTime();
9501
+ if (mtimeMs < sinceMs)
9502
+ continue;
9503
+ let sizeBytes = 0;
9372
9504
  try {
9373
- await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
9374
- scope,
9375
- error_type: /not found/i.test(msg) ? "file_not_found" : "load_error"
9376
- });
9505
+ sizeBytes = statSync11(s.path).size;
9377
9506
  } catch {}
9378
- console.error(`Error: ${msg}`);
9379
- process.exit(1);
9507
+ if (!s.sessionId)
9508
+ continue;
9509
+ out.push({
9510
+ cli: "codex",
9511
+ projectName: project.name,
9512
+ sessionId: s.sessionId,
9513
+ transcriptPath: s.path,
9514
+ mtimeMs,
9515
+ sizeBytes
9516
+ });
9380
9517
  }
9381
- if (validatedHooks.length === 0) {
9518
+ }
9519
+ return out;
9520
+ }
9521
+ async function streamCodexEvents(meta) {
9522
+ const log = await getCodexSessionLog(meta.sessionId);
9523
+ if (!log)
9524
+ return [];
9525
+ return logEntriesToEvents(log.entries, {
9526
+ cli: "codex",
9527
+ sessionId: meta.sessionId,
9528
+ transcriptPath: meta.transcriptPath,
9529
+ cwd: log.cwd ?? ""
9530
+ });
9531
+ }
9532
+ var init_codex = __esm(() => {
9533
+ init_codex_projects();
9534
+ init_codex_sessions();
9535
+ init_shared();
9536
+ });
9537
+
9538
+ // src/audit/cli-adapters/copilot.ts
9539
+ import { statSync as statSync12 } from "node:fs";
9540
+ async function listCopilotTranscriptMetadata(opts = {}) {
9541
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9542
+ const sinceMs = opts.sinceMs ?? 0;
9543
+ const out = [];
9544
+ const projects = await getCopilotProjects();
9545
+ for (const project of projects) {
9546
+ const { cwd, sessions } = await getCopilotSessionsByEncodedName(project.name);
9547
+ const effectiveCwd = cwd ?? "";
9548
+ if (projectFilter && !projectFilter.has(effectiveCwd))
9549
+ continue;
9550
+ for (const s of sessions) {
9551
+ const mtimeMs = s.lastModified.getTime();
9552
+ if (mtimeMs < sinceMs)
9553
+ continue;
9554
+ let sizeBytes = 0;
9382
9555
  try {
9383
- await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
9384
- scope,
9385
- error_type: "no_hooks_registered"
9386
- });
9556
+ sizeBytes = statSync12(s.path).size;
9387
9557
  } catch {}
9388
- console.error(`Error: no hooks registered in ${customPoliciesPath}. ` + `Make sure your file calls customPolicies.add(...) at least once.`);
9389
- process.exit(1);
9558
+ if (!s.sessionId)
9559
+ continue;
9560
+ out.push({
9561
+ cli: "copilot",
9562
+ projectName: project.name,
9563
+ sessionId: s.sessionId,
9564
+ transcriptPath: s.path,
9565
+ mtimeMs,
9566
+ sizeBytes
9567
+ });
9390
9568
  }
9391
- console.log(`
9392
- Validated ${validatedHooks.length} custom hook(s): ${validatedHooks.map((h) => h.name).join(", ")}`);
9393
- }
9394
- writeScopedHooksConfig(configToWrite, scope, cwd);
9395
- console.log(`
9396
- Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
9397
- `);
9398
- if (removeCustomHooks) {
9399
- console.log("Custom hooks path cleared.");
9400
- } else if (configToWrite.customPoliciesPath) {
9401
- console.log(`Custom hooks path: ${configToWrite.customPoliciesPath}`);
9402
9569
  }
9403
- const writtenSettingsPaths = [];
9404
- for (const cliId of selectedClis) {
9405
- const integration = getIntegration(cliId);
9406
- const settingsPath = integration.getSettingsPath(scope, cwd);
9407
- try {
9408
- const settings = integration.readSettings(settingsPath);
9409
- integration.writeHookEntries(settings, binaryPath, scope);
9410
- integration.writeSettings(settingsPath, settings);
9411
- writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
9412
- } catch (err) {
9413
- const errorType = err instanceof Error && /EACCES|EPERM/.test(err.message) ? "permission_denied" : err instanceof Error && /ENOENT|ENOTDIR/.test(err.message) ? "path_not_found" : "write_error";
9570
+ return out;
9571
+ }
9572
+ async function streamCopilotEvents(meta) {
9573
+ const log = await getCopilotSessionLog(meta.sessionId);
9574
+ if (!log)
9575
+ return [];
9576
+ return logEntriesToEvents(log.entries, {
9577
+ cli: "copilot",
9578
+ sessionId: meta.sessionId,
9579
+ transcriptPath: meta.transcriptPath,
9580
+ cwd: log.cwd ?? ""
9581
+ });
9582
+ }
9583
+ var init_copilot = __esm(() => {
9584
+ init_copilot_projects();
9585
+ init_copilot_sessions();
9586
+ init_shared();
9587
+ });
9588
+
9589
+ // src/audit/cli-adapters/cursor.ts
9590
+ import { statSync as statSync13 } from "node:fs";
9591
+ async function listCursorTranscriptMetadata(opts = {}) {
9592
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9593
+ const sinceMs = opts.sinceMs ?? 0;
9594
+ const out = [];
9595
+ const projects = await getCursorProjects();
9596
+ for (const project of projects) {
9597
+ const { cwd, sessions } = await getCursorSessionsByEncodedName(project.name);
9598
+ const effectiveCwd = cwd ?? "";
9599
+ if (projectFilter && !projectFilter.has(effectiveCwd))
9600
+ continue;
9601
+ for (const s of sessions) {
9602
+ const mtimeMs = s.lastModified.getTime();
9603
+ if (mtimeMs < sinceMs)
9604
+ continue;
9605
+ let sizeBytes = 0;
9414
9606
  try {
9415
- await trackHookEvent2(getInstanceId2(), "hook_write_failed", {
9416
- cli: cliId,
9417
- scope,
9418
- error_type: errorType
9419
- });
9607
+ sizeBytes = statSync13(s.path).size;
9420
9608
  } catch {}
9421
- throw err;
9609
+ if (!s.sessionId)
9610
+ continue;
9611
+ out.push({
9612
+ cli: "cursor",
9613
+ projectName: project.name,
9614
+ sessionId: s.sessionId,
9615
+ transcriptPath: s.path,
9616
+ mtimeMs,
9617
+ sizeBytes
9618
+ });
9422
9619
  }
9423
9620
  }
9621
+ return out;
9622
+ }
9623
+ async function streamCursorEvents(meta) {
9624
+ const log = await getCursorSessionLog(meta.sessionId);
9625
+ if (!log)
9626
+ return [];
9627
+ return logEntriesToEvents(log.entries, {
9628
+ cli: "cursor",
9629
+ sessionId: meta.sessionId,
9630
+ transcriptPath: meta.transcriptPath,
9631
+ cwd: log.cwd ?? ""
9632
+ });
9633
+ }
9634
+ var init_cursor = __esm(() => {
9635
+ init_cursor_projects();
9636
+ init_cursor_sessions();
9637
+ init_shared();
9638
+ });
9639
+
9640
+ // lib/opencode-sessions.ts
9641
+ import { execFileSync as execFileSync3 } from "node:child_process";
9642
+ function runOpenCodeDb2(sql) {
9424
9643
  try {
9425
- const newSet = new Set(selectedPolicies);
9426
- const policiesAdded = selectedPolicies.filter((p) => !previousEnabled.has(p));
9427
- const policiesRemoved = [...previousEnabled].filter((p) => !newSet.has(p));
9428
- const distinctId = getInstanceId2();
9429
- await trackHookEvent2(distinctId, "hooks_installed", {
9430
- scope,
9431
- cli: selectedClis,
9432
- cli_count: selectedClis.length,
9433
- policies: selectedPolicies,
9434
- policy_count: selectedPolicies.length,
9435
- policies_added: policiesAdded,
9436
- policies_removed: policiesRemoved,
9437
- ...source ? { source } : {},
9438
- platform: platform2(),
9439
- arch: arch2(),
9440
- os_release: release2(),
9441
- hostname_hash: hashToId2(hostname2()),
9442
- has_custom_hooks_path: !!configToWrite.customPoliciesPath,
9443
- has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
9644
+ const stdout = execFileSync3("opencode", ["db", "--format", "json", sql], {
9645
+ encoding: "utf8",
9646
+ timeout: 5000,
9647
+ stdio: ["ignore", "pipe", "pipe"]
9648
+ });
9649
+ if (!stdout.trim())
9650
+ return [];
9651
+ const parsed = JSON.parse(stdout);
9652
+ if (!Array.isArray(parsed))
9653
+ return null;
9654
+ return parsed;
9655
+ } catch {
9656
+ return null;
9657
+ }
9658
+ }
9659
+ function isPlainObject(value) {
9660
+ return !!value && typeof value === "object" && !Array.isArray(value);
9661
+ }
9662
+ function parseDataColumn(raw) {
9663
+ if (typeof raw !== "string" || raw.length === 0)
9664
+ return null;
9665
+ try {
9666
+ const parsed = JSON.parse(raw);
9667
+ return isPlainObject(parsed) ? parsed : null;
9668
+ } catch {
9669
+ return null;
9670
+ }
9671
+ }
9672
+ function readContentText(data) {
9673
+ if (typeof data.text === "string")
9674
+ return data.text;
9675
+ if (typeof data.content === "string")
9676
+ return data.content;
9677
+ return "";
9678
+ }
9679
+ function translateMessage(msgRow, partRows, source) {
9680
+ const msgData = parseDataColumn(msgRow.data) ?? {};
9681
+ const role = typeof msgData.role === "string" ? msgData.role : "system";
9682
+ const date = new Date(msgRow.time_created);
9683
+ const timestamp = date.toISOString();
9684
+ const raw = { uuid: msgRow.id, parentUuid: null };
9685
+ const base = baseEntry(raw, timestamp, date, source);
9686
+ const content = [];
9687
+ let userText = "";
9688
+ for (const p of partRows) {
9689
+ const data = parseDataColumn(p.data);
9690
+ if (!data)
9691
+ continue;
9692
+ const type = typeof data.type === "string" ? data.type : "unknown";
9693
+ if (type === "text") {
9694
+ const text = readContentText(data);
9695
+ if (text) {
9696
+ content.push({ type: "text", text });
9697
+ userText += (userText ? `
9698
+ ` : "") + text;
9699
+ }
9700
+ continue;
9701
+ }
9702
+ if (type === "tool") {
9703
+ const toolName = typeof data.tool === "string" ? data.tool : typeof data.name === "string" ? data.name : "tool";
9704
+ const state = isPlainObject(data.state) ? data.state : null;
9705
+ const input = state && isPlainObject(state.input) ? state.input : isPlainObject(data.input) ? data.input : isPlainObject(data.args) ? data.args : {};
9706
+ const block = {
9707
+ type: "tool_use",
9708
+ id: p.id,
9709
+ name: toolName,
9710
+ input
9711
+ };
9712
+ const status = state && typeof state.status === "string" ? state.status : "";
9713
+ if (state && (status === "completed" || status === "error")) {
9714
+ const errorText = status === "error" && typeof state.error === "string" ? state.error : null;
9715
+ const rawOutput = errorText ?? state.output;
9716
+ const contentText = typeof rawOutput === "string" ? rawOutput : rawOutput != null ? JSON.stringify(rawOutput) : "";
9717
+ const time = isPlainObject(state.time) ? state.time : {};
9718
+ const startMs = typeof time.start === "number" ? time.start : p.time_created;
9719
+ const endMs = typeof time.end === "number" ? time.end : p.time_updated;
9720
+ const durationMs = Math.max(0, endMs - startMs);
9721
+ const date2 = new Date(endMs);
9722
+ block.result = {
9723
+ timestamp: date2.toISOString(),
9724
+ timestampFormatted: formatTimestamp(date2),
9725
+ content: contentText,
9726
+ durationMs,
9727
+ durationFormatted: formatDuration(durationMs)
9728
+ };
9729
+ }
9730
+ content.push(block);
9731
+ continue;
9732
+ }
9733
+ content.push({ type: "text", text: `[opencode ${type}]` });
9734
+ }
9735
+ if (role === "user") {
9736
+ const entry2 = {
9737
+ ...base,
9738
+ type: "user",
9739
+ message: { role: "user", content: userText }
9740
+ };
9741
+ return entry2;
9742
+ }
9743
+ if (role === "assistant") {
9744
+ const modelInfo = isPlainObject(msgData.model) ? msgData.model : null;
9745
+ const modelStr = modelInfo && typeof modelInfo.modelID === "string" ? modelInfo.modelID : undefined;
9746
+ const entry2 = {
9747
+ ...base,
9748
+ type: "assistant",
9749
+ message: { role: "assistant", content, model: modelStr }
9750
+ };
9751
+ return entry2;
9752
+ }
9753
+ const entry = {
9754
+ ...base,
9755
+ type: "system",
9756
+ raw: { id: msgRow.id, role, parts: content }
9757
+ };
9758
+ return entry;
9759
+ }
9760
+ async function getOpenCodeSessionLog(sessionId) {
9761
+ if (!sessionId || !/^[A-Za-z0-9_-]+$/.test(sessionId))
9762
+ return null;
9763
+ const sessions = runOpenCodeDb2(`SELECT id, project_id, slug, directory, title, time_created, time_updated FROM session WHERE id = '${sessionId}'`);
9764
+ if (!sessions || sessions.length === 0)
9765
+ return null;
9766
+ const session = sessions[0];
9767
+ const messages = runOpenCodeDb2(`SELECT id, session_id, time_created, time_updated, data FROM message WHERE session_id = '${sessionId}' ORDER BY time_created ASC`);
9768
+ const parts = runOpenCodeDb2(`SELECT id, message_id, session_id, time_created, time_updated, data FROM part WHERE session_id = '${sessionId}' ORDER BY time_created ASC`);
9769
+ if (!messages)
9770
+ return { entries: [], rawLines: [], cwd: session.directory ?? undefined, filePath: `opencode://${sessionId}` };
9771
+ const partsByMessage = new Map;
9772
+ for (const p of parts ?? []) {
9773
+ let bucket = partsByMessage.get(p.message_id);
9774
+ if (!bucket) {
9775
+ bucket = [];
9776
+ partsByMessage.set(p.message_id, bucket);
9777
+ }
9778
+ bucket.push(p);
9779
+ }
9780
+ const entries = [];
9781
+ const rawLines = [];
9782
+ for (const msg of messages) {
9783
+ const partRows = partsByMessage.get(msg.id) ?? [];
9784
+ entries.push(translateMessage(msg, partRows, "session"));
9785
+ const data = parseDataColumn(msg.data);
9786
+ rawLines.push({
9787
+ id: msg.id,
9788
+ session_id: msg.session_id,
9789
+ time_created: msg.time_created,
9790
+ data: data ?? msg.data
9791
+ });
9792
+ }
9793
+ return {
9794
+ entries,
9795
+ rawLines,
9796
+ cwd: session.directory ?? undefined,
9797
+ filePath: `opencode://${sessionId}`
9798
+ };
9799
+ }
9800
+ var getCachedOpenCodeSessionLog;
9801
+ var init_opencode_sessions = __esm(() => {
9802
+ init_log_entries();
9803
+ getCachedOpenCodeSessionLog = runtimeCache((sessionId) => getOpenCodeSessionLog(sessionId), 30, { maxSize: 50 });
9804
+ });
9805
+
9806
+ // src/audit/cli-adapters/opencode.ts
9807
+ async function listOpenCodeTranscriptMetadata(opts = {}) {
9808
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9809
+ const sinceMs = opts.sinceMs ?? 0;
9810
+ const out = [];
9811
+ const projects = await getOpenCodeProjects();
9812
+ for (const project of projects) {
9813
+ const { cwd, sessions } = await getOpenCodeSessionsByEncodedName(project.name);
9814
+ const effectiveCwd = cwd ?? "";
9815
+ if (projectFilter && !projectFilter.has(effectiveCwd))
9816
+ continue;
9817
+ for (const s of sessions) {
9818
+ const mtimeMs = s.lastModified.getTime();
9819
+ if (mtimeMs < sinceMs)
9820
+ continue;
9821
+ if (!s.sessionId)
9822
+ continue;
9823
+ out.push({
9824
+ cli: "opencode",
9825
+ projectName: project.name,
9826
+ sessionId: s.sessionId,
9827
+ transcriptPath: s.path,
9828
+ mtimeMs,
9829
+ sizeBytes: 0
9830
+ });
9831
+ }
9832
+ }
9833
+ return out;
9834
+ }
9835
+ async function streamOpenCodeEvents(meta) {
9836
+ const log = await getOpenCodeSessionLog(meta.sessionId);
9837
+ if (!log)
9838
+ return [];
9839
+ return logEntriesToEvents(log.entries, {
9840
+ cli: "opencode",
9841
+ sessionId: meta.sessionId,
9842
+ transcriptPath: meta.transcriptPath,
9843
+ cwd: log.cwd ?? ""
9844
+ });
9845
+ }
9846
+ var init_opencode = __esm(() => {
9847
+ init_opencode_projects();
9848
+ init_opencode_sessions();
9849
+ init_shared();
9850
+ });
9851
+
9852
+ // src/audit/cli-adapters/pi.ts
9853
+ import { statSync as statSync14 } from "node:fs";
9854
+ async function listPiTranscriptMetadata(opts = {}) {
9855
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9856
+ const sinceMs = opts.sinceMs ?? 0;
9857
+ const out = [];
9858
+ const projects = await getPiProjects();
9859
+ for (const project of projects) {
9860
+ const { cwd, sessions } = await getPiSessionsByEncodedName(project.name);
9861
+ const effectiveCwd = cwd ?? "";
9862
+ if (projectFilter && !projectFilter.has(effectiveCwd))
9863
+ continue;
9864
+ for (const s of sessions) {
9865
+ const mtimeMs = s.lastModified.getTime();
9866
+ if (mtimeMs < sinceMs)
9867
+ continue;
9868
+ let sizeBytes = 0;
9869
+ try {
9870
+ sizeBytes = statSync14(s.path).size;
9871
+ } catch {}
9872
+ if (!s.sessionId)
9873
+ continue;
9874
+ out.push({
9875
+ cli: "pi",
9876
+ projectName: project.name,
9877
+ sessionId: s.sessionId,
9878
+ transcriptPath: s.path,
9879
+ mtimeMs,
9880
+ sizeBytes
9881
+ });
9882
+ }
9883
+ }
9884
+ return out;
9885
+ }
9886
+ async function streamPiEvents(meta) {
9887
+ const log = await getPiSessionLog(meta.sessionId);
9888
+ if (!log)
9889
+ return [];
9890
+ return logEntriesToEvents(log.entries, {
9891
+ cli: "pi",
9892
+ sessionId: meta.sessionId,
9893
+ transcriptPath: meta.transcriptPath,
9894
+ cwd: log.cwd ?? ""
9895
+ });
9896
+ }
9897
+ var init_pi = __esm(() => {
9898
+ init_pi_projects();
9899
+ init_pi_sessions();
9900
+ init_shared();
9901
+ });
9902
+
9903
+ // src/audit/cli-adapters/gemini.ts
9904
+ import { statSync as statSync15 } from "node:fs";
9905
+ async function listGeminiTranscriptMetadata(opts = {}) {
9906
+ const projectFilter = opts.projects ? new Set(opts.projects) : null;
9907
+ const sinceMs = opts.sinceMs ?? 0;
9908
+ const out = [];
9909
+ const projects = await getGeminiProjects();
9910
+ for (const project of projects) {
9911
+ const { cwd, sessions } = await getGeminiSessionsByEncodedName(project.name);
9912
+ const effectiveCwd = cwd ?? "";
9913
+ if (projectFilter && !projectFilter.has(effectiveCwd))
9914
+ continue;
9915
+ for (const s of sessions) {
9916
+ const mtimeMs = s.lastModified.getTime();
9917
+ if (mtimeMs < sinceMs)
9918
+ continue;
9919
+ let sizeBytes = 0;
9920
+ try {
9921
+ sizeBytes = statSync15(s.path).size;
9922
+ } catch {}
9923
+ if (!s.sessionId)
9924
+ continue;
9925
+ out.push({
9926
+ cli: "gemini",
9927
+ projectName: project.name,
9928
+ sessionId: s.sessionId,
9929
+ transcriptPath: s.path,
9930
+ mtimeMs,
9931
+ sizeBytes
9932
+ });
9933
+ }
9934
+ }
9935
+ return out;
9936
+ }
9937
+ async function streamGeminiEvents(meta) {
9938
+ const log = await getGeminiSessionLog(meta.sessionId);
9939
+ if (!log)
9940
+ return [];
9941
+ return logEntriesToEvents(log.entries, {
9942
+ cli: "gemini",
9943
+ sessionId: meta.sessionId,
9944
+ transcriptPath: meta.transcriptPath,
9945
+ cwd: log.cwd ?? ""
9946
+ });
9947
+ }
9948
+ var init_gemini = __esm(() => {
9949
+ init_gemini_projects();
9950
+ init_gemini_sessions();
9951
+ init_shared();
9952
+ });
9953
+
9954
+ // src/audit/cli-adapters/index.ts
9955
+ var ADAPTERS;
9956
+ var init_cli_adapters = __esm(() => {
9957
+ init_claude();
9958
+ init_codex();
9959
+ init_copilot();
9960
+ init_cursor();
9961
+ init_opencode();
9962
+ init_pi();
9963
+ init_gemini();
9964
+ ADAPTERS = {
9965
+ claude: {
9966
+ cli: "claude",
9967
+ listTranscripts: listClaudeTranscriptMetadata,
9968
+ streamEvents: streamClaudeEvents
9969
+ },
9970
+ codex: {
9971
+ cli: "codex",
9972
+ listTranscripts: listCodexTranscriptMetadata,
9973
+ streamEvents: streamCodexEvents
9974
+ },
9975
+ copilot: {
9976
+ cli: "copilot",
9977
+ listTranscripts: listCopilotTranscriptMetadata,
9978
+ streamEvents: streamCopilotEvents
9979
+ },
9980
+ cursor: {
9981
+ cli: "cursor",
9982
+ listTranscripts: listCursorTranscriptMetadata,
9983
+ streamEvents: streamCursorEvents
9984
+ },
9985
+ opencode: {
9986
+ cli: "opencode",
9987
+ listTranscripts: listOpenCodeTranscriptMetadata,
9988
+ streamEvents: streamOpenCodeEvents
9989
+ },
9990
+ pi: {
9991
+ cli: "pi",
9992
+ listTranscripts: listPiTranscriptMetadata,
9993
+ streamEvents: streamPiEvents
9994
+ },
9995
+ gemini: {
9996
+ cli: "gemini",
9997
+ listTranscripts: listGeminiTranscriptMetadata,
9998
+ streamEvents: streamGeminiEvents
9999
+ }
10000
+ };
10001
+ });
10002
+
10003
+ // src/audit/detectors/redundant-cd-cwd.ts
10004
+ var redundantCdCwd;
10005
+ var init_redundant_cd_cwd = __esm(() => {
10006
+ redundantCdCwd = {
10007
+ name: "redundant-cd-cwd",
10008
+ description: "Bash commands prefixed with `cd <cwd> && …` even though commands already run in cwd.",
10009
+ category: "Wasteful",
10010
+ severity: "info",
10011
+ displayTitle: "Prepended cd <cwd> before commands",
10012
+ impact: "Pure waste — your agent's shell already runs in `cwd`.",
10013
+ detect(event) {
10014
+ if (event.toolName !== "Bash")
10015
+ return null;
10016
+ const command = event.toolInput.command;
10017
+ if (typeof command !== "string" || !event.cwd)
10018
+ return null;
10019
+ const trimmed = command.trimStart();
10020
+ const match = /^cd\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*&&\s*([\s\S]+)$/.exec(trimmed);
10021
+ if (!match)
10022
+ return null;
10023
+ const path3 = (match[1] ?? match[2] ?? match[3] ?? "").replace(/\/+$/, "");
10024
+ const cwd = event.cwd.replace(/\/+$/, "");
10025
+ if (path3 !== cwd)
10026
+ return null;
10027
+ const rest = match[4].trim();
10028
+ return { example: `cd ${path3} && ${rest}` };
10029
+ }
10030
+ };
10031
+ });
10032
+
10033
+ // src/audit/detectors/prefer-edit-over-read-cat.ts
10034
+ var SOURCE_EXT_RE, preferEditOverReadCat;
10035
+ var init_prefer_edit_over_read_cat = __esm(() => {
10036
+ SOURCE_EXT_RE = /\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|swift|rb|php|c|h|cc|cpp|hpp|cs|scala|sh|bash|zsh|json|yaml|yml|toml|md|txt|sql|html|css|scss|sass)$/i;
10037
+ preferEditOverReadCat = {
10038
+ name: "prefer-edit-over-read-cat",
10039
+ description: "Bash `cat`/`head`/`tail`/`less`/`more` on a single source file — use Read.",
10040
+ category: "Wasteful",
10041
+ severity: "info",
10042
+ displayTitle: "Used `cat`/`head`/`tail` on a source file",
10043
+ impact: "Burns tokens; the Read tool returns content directly without going through Bash output.",
10044
+ detect(event) {
10045
+ if (event.toolName !== "Bash")
10046
+ return null;
10047
+ const command = event.toolInput.command;
10048
+ if (typeof command !== "string")
10049
+ return null;
10050
+ const cmd = command.trim();
10051
+ if (/[|<>;&`$()]/.test(cmd))
10052
+ return null;
10053
+ const match = /^(cat|head|tail|less|more)\s+(?:-\S+\s+)*(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
10054
+ if (!match)
10055
+ return null;
10056
+ const path3 = match[2] ?? match[3] ?? match[4] ?? "";
10057
+ if (!path3)
10058
+ return null;
10059
+ if (/(?:^|\/)\.env(?:\..+)?$/.test(path3))
10060
+ return null;
10061
+ if (!SOURCE_EXT_RE.test(path3))
10062
+ return null;
10063
+ return { example: cmd };
10064
+ }
10065
+ };
10066
+ });
10067
+
10068
+ // src/audit/detectors/prefer-edit-over-sed-awk.ts
10069
+ var preferEditOverSedAwk;
10070
+ var init_prefer_edit_over_sed_awk = __esm(() => {
10071
+ preferEditOverSedAwk = {
10072
+ name: "prefer-edit-over-sed-awk",
10073
+ description: "Bash `sed -i`/`awk` in-place edits — use Edit.",
10074
+ category: "Wasteful",
10075
+ severity: "info",
10076
+ displayTitle: "Used sed -i or awk for an in-place edit",
10077
+ impact: "Edit tool is safer and produces a diff the agent can verify.",
10078
+ detect(event) {
10079
+ if (event.toolName !== "Bash")
10080
+ return null;
10081
+ const command = event.toolInput.command;
10082
+ if (typeof command !== "string")
10083
+ return null;
10084
+ const cmd = command.trim();
10085
+ if (/(?:^|\s|;|&&|\|\|)sed\b[^|]*\s-i(?=\b|['"])/.test(cmd)) {
10086
+ return { example: cmd };
10087
+ }
10088
+ if (/(?:^|\s|;|&&|\|\|)awk\b[^|]*\s>\s*\S+/.test(cmd) && !/\|/.test(cmd)) {
10089
+ return { example: cmd };
10090
+ }
10091
+ return null;
10092
+ }
10093
+ };
10094
+ });
10095
+
10096
+ // src/audit/detectors/prefer-write-over-heredoc.ts
10097
+ var preferWriteOverHeredoc;
10098
+ var init_prefer_write_over_heredoc = __esm(() => {
10099
+ preferWriteOverHeredoc = {
10100
+ name: "prefer-write-over-heredoc",
10101
+ description: "Bash heredoc / `echo > file` writing multi-line content — use Write.",
10102
+ category: "Wasteful",
10103
+ severity: "info",
10104
+ displayTitle: "Used heredoc / `echo > file` to write a multi-line file",
10105
+ impact: "Write tool handles escaping and is verifiable.",
10106
+ detect(event) {
10107
+ if (event.toolName !== "Bash")
10108
+ return null;
10109
+ const command = event.toolInput.command;
10110
+ if (typeof command !== "string")
10111
+ return null;
10112
+ const cmd = command;
10113
+ if (/<<-?\s*['"]?[A-Za-z_][A-Za-z0-9_]*['"]?\s*>\s*\S/.test(cmd)) {
10114
+ const summary = cmd.replace(/\s+/g, " ").trim().slice(0, 160);
10115
+ return { example: summary };
10116
+ }
10117
+ if (/(?:^|\s|;|&&|\|\|)(?:echo|printf)\s+["'][^"']*\n[^"']*["']\s*>\s*\S/.test(cmd)) {
10118
+ const summary = cmd.replace(/\s+/g, " ").trim().slice(0, 160);
10119
+ return { example: summary };
10120
+ }
10121
+ return null;
10122
+ }
10123
+ };
10124
+ });
10125
+
10126
+ // src/audit/detectors/sleep-polling-loop.ts
10127
+ var SLEEP_THRESHOLD_SECONDS = 30, sleepPollingLoop;
10128
+ var init_sleep_polling_loop = __esm(() => {
10129
+ sleepPollingLoop = {
10130
+ name: "sleep-polling-loop",
10131
+ description: "Bash long `sleep` or while-sleep polling loops.",
10132
+ category: "Wasteful",
10133
+ severity: "info",
10134
+ displayTitle: "Used a long sleep or while-sleep polling loop",
10135
+ impact: "Burns wall-clock; better to wait for an explicit signal.",
10136
+ detect(event) {
10137
+ if (event.toolName !== "Bash")
10138
+ return null;
10139
+ const command = event.toolInput.command;
10140
+ if (typeof command !== "string")
10141
+ return null;
10142
+ const cmd = command;
10143
+ if (/\bwhile\b[\s\S]*?\bsleep\b[\s\S]*?\bdone\b/.test(cmd)) {
10144
+ return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
10145
+ }
10146
+ const match = /\bsleep\s+(\d+(?:\.\d+)?)(m|h|d)?\b/.exec(cmd);
10147
+ if (match) {
10148
+ const n = parseFloat(match[1]);
10149
+ const unit = match[2] ?? "s";
10150
+ const seconds = unit === "m" ? n * 60 : unit === "h" ? n * 3600 : unit === "d" ? n * 86400 : n;
10151
+ if (seconds >= SLEEP_THRESHOLD_SECONDS) {
10152
+ return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
10153
+ }
10154
+ }
10155
+ return null;
10156
+ }
10157
+ };
10158
+ });
10159
+
10160
+ // src/audit/detectors/find-from-root.ts
10161
+ var RISKY_ROOTS, findFromRoot;
10162
+ var init_find_from_root = __esm(() => {
10163
+ RISKY_ROOTS = ["/", "/home", "/usr", "/etc", "/var", "/opt", "/Users"];
10164
+ findFromRoot = {
10165
+ name: "find-from-root",
10166
+ description: "Bash `find` against `/`, `/home`, `/usr`, etc. — scope to cwd instead.",
10167
+ category: "Risky",
10168
+ severity: "warn",
10169
+ displayTitle: "Ran find from /, /home, /usr, etc.",
10170
+ impact: "Filesystem-wide finds exhaust resources and rarely return useful results.",
10171
+ detect(event) {
10172
+ if (event.toolName !== "Bash")
10173
+ return null;
10174
+ const command = event.toolInput.command;
10175
+ if (typeof command !== "string")
10176
+ return null;
10177
+ const cmd = command.trim();
10178
+ const match = /(?:^|[\s;|&])find\s+(?:-\S+\s+)*("[^"]+"|'[^']+'|\S+)/.exec(cmd);
10179
+ if (!match)
10180
+ return null;
10181
+ const raw = match[1].replace(/^["']|["']$/g, "");
10182
+ const stripped = raw.replace(/\/+$/, "") || "/";
10183
+ if (!RISKY_ROOTS.includes(stripped))
10184
+ return null;
10185
+ return { example: cmd.slice(0, 160) };
10186
+ }
10187
+ };
10188
+ });
10189
+
10190
+ // src/audit/detectors/git-commit-no-verify.ts
10191
+ var gitCommitNoVerify;
10192
+ var init_git_commit_no_verify = __esm(() => {
10193
+ gitCommitNoVerify = {
10194
+ name: "git-commit-no-verify",
10195
+ description: "git commit invoked with --no-verify / -n, skipping hooks.",
10196
+ category: "Risky",
10197
+ severity: "warn",
10198
+ displayTitle: "Committed with --no-verify",
10199
+ impact: "Skips pre-commit hooks that exist to catch broken or unsafe code.",
10200
+ detect(event) {
10201
+ if (event.toolName !== "Bash")
10202
+ return null;
10203
+ const command = event.toolInput.command;
10204
+ if (typeof command !== "string")
10205
+ return null;
10206
+ const cmd = command;
10207
+ if (!/\bgit\s+commit\b/.test(cmd))
10208
+ return null;
10209
+ if (/\s--no-verify\b/.test(cmd) || /\s-n\b/.test(cmd)) {
10210
+ return { example: cmd.replace(/\s+/g, " ").trim().slice(0, 160) };
10211
+ }
10212
+ return null;
10213
+ }
10214
+ };
10215
+ });
10216
+
10217
+ // src/audit/detectors/reread-after-edit.ts
10218
+ function getState(state) {
10219
+ let s = state[STATE_KEY];
10220
+ if (!s) {
10221
+ s = { countdown: new Map };
10222
+ state[STATE_KEY] = s;
10223
+ }
10224
+ return s;
10225
+ }
10226
+ var STATE_KEY = "rereadAfterEdit", WINDOW = 5, rereadAfterEdit;
10227
+ var init_reread_after_edit = __esm(() => {
10228
+ rereadAfterEdit = {
10229
+ name: "reread-after-edit",
10230
+ description: "Read of a file that was just Edit'd or Write'n in the same session.",
10231
+ category: "Wasteful",
10232
+ severity: "info",
10233
+ displayTitle: "Re-read a file it just edited",
10234
+ impact: "Edit/Write already returned the updated content; the second Read is wasted tokens.",
10235
+ detect(event, sessionState) {
10236
+ const state = getState(sessionState);
10237
+ const filePath = event.toolInput.file_path;
10238
+ const pathStr = typeof filePath === "string" ? filePath : null;
10239
+ for (const [key, n] of state.countdown) {
10240
+ if (n <= 1)
10241
+ state.countdown.delete(key);
10242
+ else
10243
+ state.countdown.set(key, n - 1);
10244
+ }
10245
+ if (!pathStr)
10246
+ return null;
10247
+ if (event.toolName === "Edit" || event.toolName === "Write") {
10248
+ state.countdown.set(pathStr, WINDOW);
10249
+ return null;
10250
+ }
10251
+ if (event.toolName === "Read") {
10252
+ if (state.countdown.has(pathStr)) {
10253
+ state.countdown.delete(pathStr);
10254
+ return { example: `Read ${pathStr} immediately after Edit/Write` };
10255
+ }
10256
+ }
10257
+ return null;
10258
+ }
10259
+ };
10260
+ });
10261
+
10262
+ // src/audit/detectors/index.ts
10263
+ var AUDIT_DETECTORS;
10264
+ var init_detectors = __esm(() => {
10265
+ init_redundant_cd_cwd();
10266
+ init_prefer_edit_over_read_cat();
10267
+ init_prefer_edit_over_sed_awk();
10268
+ init_prefer_write_over_heredoc();
10269
+ init_sleep_polling_loop();
10270
+ init_find_from_root();
10271
+ init_git_commit_no_verify();
10272
+ init_reread_after_edit();
10273
+ AUDIT_DETECTORS = [
10274
+ redundantCdCwd,
10275
+ preferEditOverReadCat,
10276
+ preferEditOverSedAwk,
10277
+ preferWriteOverHeredoc,
10278
+ sleepPollingLoop,
10279
+ findFromRoot,
10280
+ gitCommitNoVerify,
10281
+ rereadAfterEdit
10282
+ ];
10283
+ });
10284
+
10285
+ // src/audit/features.ts
10286
+ function shortName(name) {
10287
+ const slash = name.indexOf("/");
10288
+ return slash >= 0 ? name.slice(slash + 1) : name;
10289
+ }
10290
+ function severityForBuiltin(name) {
10291
+ const n = shortName(name);
10292
+ if (n.startsWith("sanitize-"))
10293
+ return "sanitize";
10294
+ if (n.startsWith("block-"))
10295
+ return "deny";
10296
+ if (n.startsWith("warn-") || n.startsWith("protect-") || n.startsWith("prefer-") || n.startsWith("require-"))
10297
+ return "warn";
10298
+ return "deny";
10299
+ }
10300
+ function emptyWeights() {
10301
+ return {
10302
+ optimist: 0,
10303
+ cowboy: 0,
10304
+ explorer: 0,
10305
+ goldfish: 0,
10306
+ architect: 0,
10307
+ precision: 0,
10308
+ hammer: 0,
10309
+ ghost: 0
10310
+ };
10311
+ }
10312
+ var MAPPABLE_KEYS, ARCHITECT_CAUTION_SIGNALS, MIN_BASELINE = 0.05, EMPIRICAL_FIRING_SHARE, BASELINE_SHARE;
10313
+ var init_features = __esm(() => {
10314
+ MAPPABLE_KEYS = [
10315
+ "cowboy",
10316
+ "explorer",
10317
+ "ghost",
10318
+ "optimist",
10319
+ "hammer",
10320
+ "architect"
10321
+ ];
10322
+ ARCHITECT_CAUTION_SIGNALS = new Set(["reread-after-edit", "redundant-cd-cwd"]);
10323
+ EMPIRICAL_FIRING_SHARE = {
10324
+ explorer: 0.38,
10325
+ architect: 0.33,
10326
+ hammer: 0.11,
10327
+ cowboy: 0.11,
10328
+ optimist: 0.07,
10329
+ ghost: 0.01,
10330
+ precision: 0,
10331
+ goldfish: 0
10332
+ };
10333
+ BASELINE_SHARE = (() => {
10334
+ const out = emptyWeights();
10335
+ for (const k of MAPPABLE_KEYS)
10336
+ out[k] = Math.max(EMPIRICAL_FIRING_SHARE[k], MIN_BASELINE);
10337
+ return out;
10338
+ })();
10339
+ });
10340
+
10341
+ // src/audit/cache.ts
10342
+ import { createHash } from "node:crypto";
10343
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync6, chmodSync as chmodSync2 } from "node:fs";
10344
+ import { join as join20 } from "node:path";
10345
+ import { homedir as homedir20 } from "node:os";
10346
+ function getEngineVersion() {
10347
+ if (cachedEngineVersion)
10348
+ return cachedEngineVersion;
10349
+ const blob = BUILTIN_POLICIES.map((p) => `${p.name}|${p.fn.toString()}`).sort().join(`
10350
+ `);
10351
+ cachedEngineVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
10352
+ return cachedEngineVersion;
10353
+ }
10354
+ function getDetectorVersion() {
10355
+ if (cachedDetectorVersion)
10356
+ return cachedDetectorVersion;
10357
+ const blob = AUDIT_DETECTORS.map((d) => `${d.name}|${d.detect.toString()}`).sort().join(`
10358
+ `);
10359
+ cachedDetectorVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
10360
+ return cachedDetectorVersion;
10361
+ }
10362
+ function getCachePathFor(transcriptPath) {
10363
+ const root = join20(homedir20(), ".failproofai", "cache", "audit");
10364
+ const key = createHash("sha1").update(transcriptPath).digest("hex");
10365
+ return join20(root, `${key}.json`);
10366
+ }
10367
+ function readCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes) {
10368
+ if (sizeBytes === 0)
10369
+ return null;
10370
+ const cachePath = getCachePathFor(transcriptPath);
10371
+ if (!existsSync15(cachePath))
10372
+ return null;
10373
+ try {
10374
+ const raw = readFileSync11(cachePath, "utf-8");
10375
+ const entry = JSON.parse(raw);
10376
+ if (entry.schemaVersion !== CACHE_SCHEMA_VERSION)
10377
+ return null;
10378
+ if (entry.mtimeMs !== mtimeMs)
10379
+ return null;
10380
+ if (entry.sizeBytes !== sizeBytes)
10381
+ return null;
10382
+ if (entry.engineVersion !== getEngineVersion())
10383
+ return null;
10384
+ if (entry.detectorVersion !== getDetectorVersion())
10385
+ return null;
10386
+ if (!Number.isFinite(entry.cachedAt) || Date.now() - entry.cachedAt > CACHE_TTL_MS)
10387
+ return null;
10388
+ return entry.result ?? null;
10389
+ } catch {
10390
+ return null;
10391
+ }
10392
+ }
10393
+ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result) {
10394
+ if (sizeBytes === 0)
10395
+ return;
10396
+ const cachePath = getCachePathFor(transcriptPath);
10397
+ try {
10398
+ mkdirSync7(join20(homedir20(), ".failproofai", "cache", "audit"), { recursive: true });
10399
+ const entry = {
10400
+ schemaVersion: CACHE_SCHEMA_VERSION,
10401
+ cachedAt: Date.now(),
10402
+ mtimeMs,
10403
+ sizeBytes,
10404
+ engineVersion: getEngineVersion(),
10405
+ detectorVersion: getDetectorVersion(),
10406
+ result
10407
+ };
10408
+ writeFileSync6(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
10409
+ try {
10410
+ chmodSync2(cachePath, 384);
10411
+ } catch {}
10412
+ } catch {}
10413
+ }
10414
+ var cachedEngineVersion = null, cachedDetectorVersion = null, CACHE_SCHEMA_VERSION = 3, CACHE_TTL_MS;
10415
+ var init_cache = __esm(() => {
10416
+ init_builtin_policies();
10417
+ init_detectors();
10418
+ CACHE_TTL_MS = 7 * 24 * 60 * 60000;
10419
+ });
10420
+
10421
+ // src/audit/replay.ts
10422
+ function initReplay() {
10423
+ if (initialized)
10424
+ return;
10425
+ savedSnapshot = getAllPolicies();
10426
+ clearPolicies();
10427
+ const enabled = BUILTIN_POLICIES.map((p) => p.name).filter((n) => !SKIP_POLICIES.has(normalizePolicyName(n)));
10428
+ registerBuiltinPolicies(enabled);
10429
+ initialized = true;
10430
+ }
10431
+ function restoreReplay() {
10432
+ if (!initialized)
10433
+ return;
10434
+ if (savedSnapshot !== null) {
10435
+ setAllPolicies(savedSnapshot);
10436
+ savedSnapshot = null;
10437
+ }
10438
+ initialized = false;
10439
+ }
10440
+ async function replayEvent(event) {
10441
+ if (!initialized)
10442
+ initReplay();
10443
+ const session = {
10444
+ sessionId: event.sessionId,
10445
+ transcriptPath: event.transcriptPath,
10446
+ cwd: event.cwd,
10447
+ cli: event.cli
10448
+ };
10449
+ const baseToolPayload = {
10450
+ tool_name: event.toolName,
10451
+ tool_input: event.toolInput,
10452
+ session_id: event.sessionId,
10453
+ cwd: event.cwd,
10454
+ transcript_path: event.transcriptPath
10455
+ };
10456
+ const out = [];
10457
+ const pre = await evaluatePolicies("PreToolUse", baseToolPayload, session);
10458
+ collectHits(pre, "PreToolUse", out);
10459
+ if (event.toolResultText !== undefined) {
10460
+ const postPayload = { ...baseToolPayload, tool_response: event.toolResultText };
10461
+ const post = await evaluatePolicies("PostToolUse", postPayload, session);
10462
+ collectHits(post, "PostToolUse", out);
10463
+ }
10464
+ return out;
10465
+ }
10466
+ function collectHits(result, eventType, out) {
10467
+ const names = result.policyNames && result.policyNames.length > 0 ? result.policyNames : result.policyName ? [result.policyName] : [];
10468
+ for (const name of names) {
10469
+ out.push({
10470
+ policyName: name,
10471
+ decision: result.decision,
10472
+ reason: result.reason,
10473
+ eventType
10474
+ });
10475
+ }
10476
+ }
10477
+ var SKIP_POLICIES, initialized = false, savedSnapshot = null;
10478
+ var init_replay = __esm(() => {
10479
+ init_policy_evaluator();
10480
+ init_builtin_policies();
10481
+ SKIP_POLICIES = new Set(["warn-repeated-tool-calls"].map((n) => normalizePolicyName(n)));
10482
+ });
10483
+
10484
+ // src/audit/index.ts
10485
+ function shortPolicyName(name) {
10486
+ const slash = name.indexOf("/");
10487
+ return slash >= 0 ? name.slice(slash + 1) : name;
10488
+ }
10489
+ function findBuiltin(name) {
10490
+ const short = shortPolicyName(name);
10491
+ for (const p of BUILTIN_POLICIES) {
10492
+ if (p.name === name || shortPolicyName(p.name) === short)
10493
+ return p;
10494
+ }
10495
+ return null;
10496
+ }
10497
+ function buildInstallHint(name, source, enabled) {
10498
+ if (source === "audit-detector") {
10499
+ return "Audit-only — `failproofai audit` will keep tracking these.";
10500
+ }
10501
+ if (enabled) {
10502
+ return "Already enforced — failproofai is blocking these in real time.";
10503
+ }
10504
+ return `Enable in one command: failproofai policies --install ${shortPolicyName(name)}`;
10505
+ }
10506
+ function truncateExample(s) {
10507
+ if (s.length <= AUDIT_EXAMPLE_MAX_CHARS)
10508
+ return s;
10509
+ return s.slice(0, AUDIT_EXAMPLE_MAX_CHARS - 1) + "…";
10510
+ }
10511
+ function parseSinceOpt(since) {
10512
+ if (!since)
10513
+ return;
10514
+ const m = /^(\d+)\s*([dhm])$/i.exec(since.trim());
10515
+ if (m) {
10516
+ const n = parseInt(m[1], 10);
10517
+ const unit = m[2].toLowerCase();
10518
+ const ms = unit === "d" ? 86400000 : unit === "h" ? 3600000 : 60000;
10519
+ return Date.now() - n * ms;
10520
+ }
10521
+ const t = Date.parse(since);
10522
+ if (!Number.isNaN(t))
10523
+ return t;
10524
+ throw new Error(`Invalid --since value: "${since}" (expected e.g. "7d", "30d", or "2026-04-01")`);
10525
+ }
10526
+ async function scanOneTranscript(meta) {
10527
+ const empty = {
10528
+ transcriptPath: meta.transcriptPath,
10529
+ cli: meta.cli,
10530
+ projectName: meta.projectName,
10531
+ sessionId: meta.sessionId,
10532
+ mtimeMs: meta.mtimeMs,
10533
+ sizeBytes: meta.sizeBytes,
10534
+ cwd: "",
10535
+ eventsScanned: 0,
10536
+ hitsByName: {},
10537
+ examplesByName: {},
10538
+ rangeByName: {}
10539
+ };
10540
+ const events = await ADAPTERS[meta.cli].streamEvents(meta);
10541
+ if (events.length === 0)
10542
+ return empty;
10543
+ const result = empty;
10544
+ result.eventsScanned = events.length;
10545
+ result.cwd = events[0].cwd || "";
10546
+ const sessionState = {};
10547
+ for (const event of events) {
10548
+ for (const detector of AUDIT_DETECTORS) {
10549
+ const hit = detector.detect(event, sessionState);
10550
+ if (!hit)
10551
+ continue;
10552
+ recordHit(result, detector.name, event.timestamp, event.cwd, truncateExample(hit.example));
10553
+ }
10554
+ let replayHits;
10555
+ try {
10556
+ replayHits = await replayEvent(event);
10557
+ } catch {
10558
+ continue;
10559
+ }
10560
+ for (const hit of replayHits) {
10561
+ const example = formatPolicyExample(hit.policyName, event);
10562
+ recordHit(result, hit.policyName, event.timestamp, event.cwd, truncateExample(example));
10563
+ }
10564
+ }
10565
+ return result;
10566
+ }
10567
+ function formatPolicyExample(_policyName, event) {
10568
+ if (event.toolName === "Bash") {
10569
+ const command = event.toolInput.command;
10570
+ if (typeof command === "string")
10571
+ return command.replace(/\s+/g, " ");
10572
+ }
10573
+ const filePath = event.toolInput.file_path;
10574
+ if (typeof filePath === "string")
10575
+ return `${event.toolName} ${filePath}`;
10576
+ return `${event.toolName}`;
10577
+ }
10578
+ function recordHit(result, name, timestamp, cwd, example) {
10579
+ result.hitsByName[name] = (result.hitsByName[name] ?? 0) + 1;
10580
+ const exs = result.examplesByName[name] ?? [];
10581
+ if (exs.length < AUDIT_MAX_EXAMPLES_PER_NAME) {
10582
+ exs.push({ timestamp, cwd, example });
10583
+ result.examplesByName[name] = exs;
10584
+ }
10585
+ const range = result.rangeByName[name];
10586
+ if (!range) {
10587
+ result.rangeByName[name] = { first: timestamp, last: timestamp };
10588
+ } else {
10589
+ if (timestamp < range.first)
10590
+ range.first = timestamp;
10591
+ if (timestamp > range.last)
10592
+ range.last = timestamp;
10593
+ }
10594
+ }
10595
+ function aggregateResults(perTranscript, enabledBuiltins) {
10596
+ const byName = new Map;
10597
+ for (const t of perTranscript) {
10598
+ for (const [name, count] of Object.entries(t.hitsByName)) {
10599
+ const bucket = byName.get(name) ?? {
10600
+ hits: 0,
10601
+ projects: new Set,
10602
+ examples: []
10603
+ };
10604
+ bucket.hits += count;
10605
+ bucket.projects.add(t.projectName);
10606
+ const tExs = t.examplesByName[name] ?? [];
10607
+ for (const e of tExs) {
10608
+ if (bucket.examples.length < AUDIT_MAX_EXAMPLES_PER_NAME) {
10609
+ bucket.examples.push({ ...e, sessionId: t.sessionId });
10610
+ }
10611
+ }
10612
+ const range = t.rangeByName[name];
10613
+ if (range) {
10614
+ if (!bucket.first || range.first < bucket.first)
10615
+ bucket.first = range.first;
10616
+ if (!bucket.last || range.last > bucket.last)
10617
+ bucket.last = range.last;
10618
+ }
10619
+ byName.set(name, bucket);
10620
+ }
10621
+ }
10622
+ const detectorByName = new Map(AUDIT_DETECTORS.map((d) => [d.name, d]));
10623
+ const out = [];
10624
+ for (const [name, bucket] of byName) {
10625
+ const detector = detectorByName.get(name);
10626
+ const isDetector = !!detector;
10627
+ const builtin = isDetector ? null : findBuiltin(name);
10628
+ const source = isDetector ? "audit-detector" : "builtin";
10629
+ const enabled = isDetector ? false : enabledBuiltins.has(normalizePolicyName(name));
10630
+ const displayTitle = detector?.displayTitle ?? builtin?.displayTitle ?? detector?.description ?? builtin?.description ?? shortPolicyName(name);
10631
+ const impact = detector?.impact ?? builtin?.impact ?? "";
10632
+ out.push({
10633
+ name,
10634
+ source,
10635
+ category: detector?.category ?? builtin?.category ?? "Custom",
10636
+ severity: isDetector ? detector?.severity ?? "info" : severityForBuiltin(name),
10637
+ hits: bucket.hits,
10638
+ projects: bucket.projects.size,
10639
+ firstSeen: bucket.first,
10640
+ lastSeen: bucket.last,
10641
+ examples: bucket.examples,
10642
+ displayTitle,
10643
+ impact,
10644
+ enabledInConfig: enabled,
10645
+ installHint: buildInstallHint(name, source, enabled)
10646
+ });
10647
+ }
10648
+ out.sort((a, b) => b.hits - a.hits);
10649
+ return out;
10650
+ }
10651
+ async function runAudit(opts = {}) {
10652
+ const startedAt = Date.now();
10653
+ initReplay();
10654
+ try {
10655
+ return await runAuditInner(opts, startedAt);
10656
+ } finally {
10657
+ restoreReplay();
10658
+ }
10659
+ }
10660
+ async function runAuditInner(opts, startedAt) {
10661
+ const clis = opts.clis ?? Array.from(INTEGRATION_TYPES);
10662
+ const sinceMs = parseSinceOpt(opts.since);
10663
+ const userConfig = readMergedHooksConfig();
10664
+ const enabledBuiltins = new Set((userConfig.enabledPolicies ?? []).map((n) => normalizePolicyName(n)));
10665
+ const allTranscripts = [];
10666
+ for (const cli of clis) {
10667
+ const adapter = ADAPTERS[cli];
10668
+ let list;
10669
+ try {
10670
+ list = await adapter.listTranscripts({ projects: opts.projects, sinceMs });
10671
+ } catch {
10672
+ continue;
10673
+ }
10674
+ allTranscripts.push(...list);
10675
+ }
10676
+ let skipped = 0;
10677
+ let errors = 0;
10678
+ const tasks = allTranscripts.map((meta) => async () => {
10679
+ if (!opts.noCache) {
10680
+ const cached = readCachedTranscriptResult(meta.transcriptPath, meta.mtimeMs, meta.sizeBytes);
10681
+ if (cached)
10682
+ return cached;
10683
+ }
10684
+ try {
10685
+ const fresh = await scanOneTranscript(meta);
10686
+ if (!opts.noCache) {
10687
+ writeCachedTranscriptResult(meta.transcriptPath, meta.mtimeMs, meta.sizeBytes, fresh);
10688
+ }
10689
+ return fresh;
10690
+ } catch {
10691
+ errors++;
10692
+ return {
10693
+ transcriptPath: meta.transcriptPath,
10694
+ cli: meta.cli,
10695
+ projectName: meta.projectName,
10696
+ cwd: "",
10697
+ sessionId: meta.sessionId,
10698
+ mtimeMs: meta.mtimeMs,
10699
+ sizeBytes: meta.sizeBytes,
10700
+ eventsScanned: 0,
10701
+ hitsByName: {},
10702
+ examplesByName: {},
10703
+ rangeByName: {}
10704
+ };
10705
+ }
10706
+ });
10707
+ const settled = await batchAll(tasks, TRANSCRIPT_CONCURRENCY);
10708
+ const perTranscript = [];
10709
+ for (const s of settled) {
10710
+ if (s.status === "fulfilled")
10711
+ perTranscript.push(s.value);
10712
+ else
10713
+ skipped++;
10714
+ }
10715
+ let results = aggregateResults(perTranscript, enabledBuiltins);
10716
+ if (opts.policies?.length) {
10717
+ const wanted = new Set(opts.policies.map(shortPolicyName));
10718
+ results = results.filter((r) => wanted.has(shortPolicyName(r.name)));
10719
+ }
10720
+ const totalsHits = results.reduce((sum, r) => sum + r.hits, 0);
10721
+ const projectsWithHits = new Set;
10722
+ const projectsScannedSet = new Set;
10723
+ let eventsScanned = 0;
10724
+ for (const t of perTranscript) {
10725
+ if (Object.keys(t.hitsByName).length > 0)
10726
+ projectsWithHits.add(t.projectName);
10727
+ if (t.cwd)
10728
+ projectsScannedSet.add(t.cwd);
10729
+ eventsScanned += t.eventsScanned ?? 0;
10730
+ }
10731
+ const auditResult = {
10732
+ version: 2,
10733
+ scannedAt: new Date(startedAt).toISOString(),
10734
+ scope: {
10735
+ cli: clis,
10736
+ projects: opts.projects ?? "all",
10737
+ since: opts.since ?? null
10738
+ },
10739
+ transcripts: {
10740
+ scanned: allTranscripts.length,
10741
+ skipped,
10742
+ errors,
10743
+ durationMs: Date.now() - startedAt
10744
+ },
10745
+ results,
10746
+ totals: {
10747
+ hits: totalsHits,
10748
+ projectsWithHits: projectsWithHits.size
10749
+ },
10750
+ projectsScanned: [...projectsScannedSet].sort(),
10751
+ eventsScanned,
10752
+ enabledBuiltinNames: [...enabledBuiltins].map((n) => n.includes("/") ? n.slice(n.indexOf("/") + 1) : n)
10753
+ };
10754
+ return auditResult;
10755
+ }
10756
+ var TRANSCRIPT_CONCURRENCY = 8;
10757
+ var init_audit = __esm(() => {
10758
+ init_builtin_policies();
10759
+ init_hooks_config();
10760
+ init_types();
10761
+ init_cli_adapters();
10762
+ init_detectors();
10763
+ init_features();
10764
+ init_cache();
10765
+ init_replay();
10766
+ init_types2();
10767
+ });
10768
+
10769
+ // src/audit/dashboard-cache.ts
10770
+ import { join as join21 } from "node:path";
10771
+ import { homedir as homedir21 } from "node:os";
10772
+ function getCachePath() {
10773
+ return join21(homedir21(), ".failproofai", "audit-dashboard.json");
10774
+ }
10775
+ function writeDashboardCache(params, result) {
10776
+ try {
10777
+ const entry = {
10778
+ schemaVersion: DASHBOARD_CACHE_SCHEMA_VERSION,
10779
+ cachedAt: new Date().toISOString(),
10780
+ params,
10781
+ result
10782
+ };
10783
+ writeJsonAtomically(getCachePath(), entry);
10784
+ return true;
10785
+ } catch {
10786
+ return false;
10787
+ }
10788
+ }
10789
+ var DASHBOARD_CACHE_TTL_MINUTES, DASHBOARD_CACHE_SCHEMA_VERSION = 2;
10790
+ var init_dashboard_cache = __esm(() => {
10791
+ init_atomic_write();
10792
+ DASHBOARD_CACHE_TTL_MINUTES = 7 * 24 * 60;
10793
+ });
10794
+
10795
+ // src/audit/open-browser.ts
10796
+ import { spawn } from "node:child_process";
10797
+ function openUrl(url) {
10798
+ let cmd;
10799
+ let args;
10800
+ if (process.platform === "darwin") {
10801
+ cmd = "open";
10802
+ args = [url];
10803
+ } else if (process.platform === "win32") {
10804
+ cmd = "cmd";
10805
+ args = ["/c", "start", "", url];
10806
+ } else {
10807
+ cmd = "xdg-open";
10808
+ args = [url];
10809
+ }
10810
+ try {
10811
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
10812
+ child.on("error", () => {});
10813
+ child.unref();
10814
+ } catch {}
10815
+ }
10816
+ function openWhenReady(port, path3) {
10817
+ const base = `http://localhost:${port}`;
10818
+ const target = `${base}${path3}`;
10819
+ (async () => {
10820
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
10821
+ while (Date.now() < deadline) {
10822
+ try {
10823
+ await fetch(base, { method: "GET", redirect: "manual" });
10824
+ openUrl(target);
10825
+ return;
10826
+ } catch {
10827
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
10828
+ }
10829
+ }
10830
+ openUrl(target);
10831
+ })();
10832
+ }
10833
+ var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
10834
+ var init_open_browser = () => {};
10835
+
10836
+ // scripts/parse-script-args.ts
10837
+ import { resolve as resolve12 } from "path";
10838
+ function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
10839
+ const raw = inlineValue ?? args[index + 1];
10840
+ if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
10841
+ console.error(`Error: ${flagName} requires ${errorLabel}`);
10842
+ process.exit(1);
10843
+ }
10844
+ const value = options?.resolve ? resolve12(raw) : raw;
10845
+ return { value, spliceCount: inlineValue !== null ? 1 : 2 };
10846
+ }
10847
+ function parseScriptArgs(argv) {
10848
+ const args = [...argv];
10849
+ let loggingLevel;
10850
+ let disableTelemetry = false;
10851
+ let allowedDevOrigins;
10852
+ for (let i = 0;i < args.length; i++) {
10853
+ const arg = args[i];
10854
+ const eqIdx = arg.indexOf("=");
10855
+ const flag = eqIdx >= 0 ? arg.slice(0, eqIdx) : arg;
10856
+ const inlineValue = eqIdx >= 0 ? arg.slice(eqIdx + 1) : null;
10857
+ if (flag === "--logging") {
10858
+ const raw = inlineValue ?? args[i + 1];
10859
+ if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
10860
+ console.error("Error: --logging requires a level (info, warn, error)");
10861
+ process.exit(1);
10862
+ }
10863
+ const val = raw.toLowerCase();
10864
+ if (val !== "info" && val !== "warn" && val !== "error") {
10865
+ console.error("Error: --logging must be one of: info, warn, error");
10866
+ process.exit(1);
10867
+ }
10868
+ loggingLevel = val;
10869
+ args.splice(i, inlineValue !== null ? 1 : 2);
10870
+ i--;
10871
+ continue;
10872
+ }
10873
+ if (flag === "--disable-telemetry") {
10874
+ disableTelemetry = true;
10875
+ args.splice(i, 1);
10876
+ i--;
10877
+ continue;
10878
+ }
10879
+ if (flag === "--allowed-origins") {
10880
+ const { value, spliceCount } = parseStringFlag(flag, "a comma-separated list of origins", inlineValue, args, i);
10881
+ allowedDevOrigins = value.split(",").map((s) => s.trim()).filter(Boolean);
10882
+ args.splice(i, spliceCount);
10883
+ i--;
10884
+ continue;
10885
+ }
10886
+ }
10887
+ return { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs: args };
10888
+ }
10889
+ var init_parse_script_args = () => {};
10890
+
10891
+ // scripts/install-diagnosis.mjs
10892
+ import { existsSync as existsSync16, readFileSync as readFileSync12, realpathSync } from "node:fs";
10893
+ import { dirname as dirname6, resolve as resolve13 } from "node:path";
10894
+ import { homedir as homedir22, platform as platform2 } from "node:os";
10895
+ import { spawnSync } from "node:child_process";
10896
+ function findPackageRoot(start) {
10897
+ try {
10898
+ let dir = realpathSync(start);
10899
+ if (existsSync16(dir) && !existsSync16(resolve13(dir, "package.json"))) {
10900
+ dir = dirname6(dir);
10901
+ }
10902
+ while (true) {
10903
+ const pkgPath = resolve13(dir, "package.json");
10904
+ if (existsSync16(pkgPath)) {
10905
+ try {
10906
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
10907
+ if (pkg.name === PKG_NAME)
10908
+ return dir;
10909
+ } catch {}
10910
+ }
10911
+ const parent = dirname6(dir);
10912
+ if (parent === dir)
10913
+ return null;
10914
+ dir = parent;
10915
+ }
10916
+ } catch {
10917
+ return null;
10918
+ }
10919
+ }
10920
+ function readPackageVersion(packageRoot) {
10921
+ if (!packageRoot)
10922
+ return null;
10923
+ try {
10924
+ const pkg = JSON.parse(readFileSync12(resolve13(packageRoot, "package.json"), "utf8"));
10925
+ return typeof pkg.version === "string" ? pkg.version : null;
10926
+ } catch {
10927
+ return null;
10928
+ }
10929
+ }
10930
+ function resolvePathFirstBinary() {
10931
+ try {
10932
+ const isWin = platform2() === "win32";
10933
+ const res = isWin ? spawnSync("where", [PKG_NAME], { encoding: "utf8" }) : spawnSync("sh", ["-c", `command -v ${PKG_NAME}`], { encoding: "utf8" });
10934
+ if (res.status !== 0)
10935
+ return null;
10936
+ const first = (res.stdout || "").split(/\r?\n/).find((l) => l.trim().length > 0);
10937
+ return first ? first.trim() : null;
10938
+ } catch {
10939
+ return null;
10940
+ }
10941
+ }
10942
+ function locateNpmGlobal() {
10943
+ try {
10944
+ const res = spawnSync("npm", ["root", "-g"], { encoding: "utf8" });
10945
+ if (res.status !== 0)
10946
+ return null;
10947
+ const root = (res.stdout || "").trim();
10948
+ if (!root)
10949
+ return null;
10950
+ const candidate = resolve13(root, PKG_NAME);
10951
+ return existsSync16(resolve13(candidate, "package.json")) ? candidate : null;
10952
+ } catch {
10953
+ return null;
10954
+ }
10955
+ }
10956
+ function locateBunGlobal() {
10957
+ try {
10958
+ const candidate = resolve13(homedir22(), ".bun", "install", "global", "node_modules", PKG_NAME);
10959
+ return existsSync16(resolve13(candidate, "package.json")) ? candidate : null;
10960
+ } catch {
10961
+ return null;
10962
+ }
10963
+ }
10964
+ function buildRecommendation(pathFirstBin) {
10965
+ if (!pathFirstBin)
10966
+ return null;
10967
+ const bunBinPrefix = resolve13(homedir22(), ".bun", "bin") + "/";
10968
+ const bunGlobalPrefix = resolve13(homedir22(), ".bun", "install", "global") + "/";
10969
+ const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
10970
+ if (isBun) {
10971
+ return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
10972
+ }
10973
+ return `npm rm -g ${PKG_NAME}`;
10974
+ }
10975
+ function diagnoseShadow(self) {
10976
+ const selfPackageRoot = (() => {
10977
+ try {
10978
+ return self?.selfPackageRoot ? realpathSync(self.selfPackageRoot) : null;
10979
+ } catch {
10980
+ return self?.selfPackageRoot ?? null;
10981
+ }
10982
+ })();
10983
+ const selfVersion = self?.selfVersion ?? null;
10984
+ const pathFirstBin = resolvePathFirstBinary();
10985
+ const pathFirstPackageRoot = pathFirstBin ? findPackageRoot(pathFirstBin) : null;
10986
+ const pathFirstVersion = readPackageVersion(pathFirstPackageRoot);
10987
+ const npmGlobalPath = locateNpmGlobal();
10988
+ const npmGlobalVersion = readPackageVersion(npmGlobalPath);
10989
+ const bunGlobalPath = locateBunGlobal();
10990
+ const bunGlobalVersion = readPackageVersion(bunGlobalPath);
10991
+ let shadowed = false;
10992
+ if (selfPackageRoot && pathFirstPackageRoot && pathFirstPackageRoot !== selfPackageRoot) {
10993
+ shadowed = true;
10994
+ } else if (pathFirstPackageRoot) {
10995
+ if (npmGlobalPath && npmGlobalPath !== pathFirstPackageRoot)
10996
+ shadowed = true;
10997
+ else if (bunGlobalPath && bunGlobalPath !== pathFirstPackageRoot)
10998
+ shadowed = true;
10999
+ }
11000
+ const recommendation = shadowed ? buildRecommendation(pathFirstBin) : null;
11001
+ let shadowDescription = null;
11002
+ if (shadowed) {
11003
+ shadowDescription = `PATH resolves to ${pathFirstPackageRoot}` + (pathFirstVersion ? ` (v${pathFirstVersion})` : "") + `, but you just installed ${selfPackageRoot}` + (selfVersion ? ` (v${selfVersion})` : "") + ".";
11004
+ }
11005
+ return {
11006
+ selfPackageRoot,
11007
+ selfVersion,
11008
+ pathFirstBin,
11009
+ pathFirstPath: pathFirstPackageRoot,
11010
+ pathFirstVersion,
11011
+ npmGlobalPath,
11012
+ npmGlobalVersion,
11013
+ bunGlobalPath,
11014
+ bunGlobalVersion,
11015
+ shadowed,
11016
+ shadowDescription,
11017
+ recommendation
11018
+ };
11019
+ }
11020
+ var PKG_NAME = "failproofai";
11021
+ var init_install_diagnosis = () => {};
11022
+
11023
+ // scripts/skew-log-filter.ts
11024
+ function makeSkewLogFilter() {
11025
+ let inSkewBlock = false;
11026
+ return (line) => {
11027
+ if (line.includes("Failed to find Server Action")) {
11028
+ inSkewBlock = true;
11029
+ return null;
11030
+ }
11031
+ if (inSkewBlock) {
11032
+ const trimmed = line.trimStart();
11033
+ if (trimmed.startsWith("Read more:") || line.includes("failed-to-find-server-action") || line.includes("ignore-listed frames") || /^\s+at\s/.test(line)) {
11034
+ return null;
11035
+ }
11036
+ inSkewBlock = false;
11037
+ }
11038
+ return line;
11039
+ };
11040
+ }
11041
+
11042
+ // scripts/launch.ts
11043
+ var exports_launch = {};
11044
+ __export(exports_launch, {
11045
+ launch: () => launch
11046
+ });
11047
+ import { spawn as spawn2 } from "child_process";
11048
+ import { realpathSync as realpathSync2, existsSync as existsSync17 } from "node:fs";
11049
+ import { resolve as resolve14, dirname as dirname7 } from "node:path";
11050
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
11051
+ import { createInterface as createInterface2 } from "node:readline";
11052
+ function launch(mode) {
11053
+ const { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs } = parseScriptArgs(process.argv.slice(2));
11054
+ console.log(`
11055
+ failproof ai
11056
+ `);
11057
+ console.log(` \uD83D\uDCE6 Version: ${version2}`);
11058
+ console.log(` ⭐ Star us: https://github.com/failproofai/failproofai`);
11059
+ console.log(` \uD83D\uDCD6 Docs: https://docs.befailproof.ai/introduction`);
11060
+ console.log(` \uD83D\uDCAC Discord: https://discord.gg/2zjBZP7yQJ
11061
+ `);
11062
+ let cmd;
11063
+ let cmdArgs;
11064
+ if (mode === "start") {
11065
+ const portIdx = remainingArgs.indexOf("--port");
11066
+ const port = portIdx >= 0 ? remainingArgs[portIdx + 1] : "8020";
11067
+ process.env.PORT = port;
11068
+ process.env.HOSTNAME = "0.0.0.0";
11069
+ cmd = "node";
11070
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve14(dirname7(realpathSync2(fileURLToPath2(import.meta.url))), "..");
11071
+ const serverJsPath = resolve14(packageRoot, ".next/standalone/server.js");
11072
+ if (!existsSync17(serverJsPath)) {
11073
+ let shadowMessage = null;
11074
+ try {
11075
+ const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
11076
+ if (diag.shadowed) {
11077
+ const alt = (diag.npmGlobalPath && diag.npmGlobalPath !== diag.pathFirstPath ? { path: diag.npmGlobalPath, version: diag.npmGlobalVersion } : null) ?? (diag.bunGlobalPath && diag.bunGlobalPath !== diag.pathFirstPath ? { path: diag.bunGlobalPath, version: diag.bunGlobalVersion } : null);
11078
+ const newer = alt?.path ?? "(unknown)";
11079
+ const newerVer = alt?.version ?? "?";
11080
+ shadowMessage = `
11081
+ Error: failproofai on your PATH is a stale install that no longer has its build output.
11082
+ ` + ` Running: ${diag.pathFirstPath}` + (diag.pathFirstVersion ? ` (v${diag.pathFirstVersion})` : "") + `
11083
+ ` + ` Newer copy: ${newer} (v${newerVer})
11084
+
11085
+ ` + `Remove the shadow with:
11086
+ ${diag.recommendation}
11087
+ `;
11088
+ }
11089
+ } catch {}
11090
+ console.error(shadowMessage ?? `
11091
+ Error: Cannot find server.js at:
11092
+ ${serverJsPath}
11093
+
11094
+ ` + `The package may be missing its build output.
11095
+ ` + `Try reinstalling:
11096
+ npm install -g failproofai@latest
11097
+ `);
11098
+ process.exit(1);
11099
+ }
11100
+ cmdArgs = [serverJsPath];
11101
+ } else {
11102
+ cmd = "bunx";
11103
+ cmdArgs = ["--bun", "next", "dev", ...remainingArgs];
11104
+ }
11105
+ const filterLogs = mode === "start";
11106
+ const nextProcess = spawn2(cmd, cmdArgs, {
11107
+ stdio: filterLogs ? ["inherit", "pipe", "pipe"] : "inherit",
11108
+ env: {
11109
+ ...process.env,
11110
+ ...filterLogs ? { FORCE_COLOR: process.env.FORCE_COLOR ?? "1" } : {},
11111
+ ...loggingLevel ? { FAILPROOFAI_LOG_LEVEL: loggingLevel } : {},
11112
+ ...disableTelemetry ? { FAILPROOFAI_TELEMETRY_DISABLED: "1" } : {},
11113
+ ...allowedDevOrigins ? { FAILPROOFAI_ALLOWED_DEV_ORIGINS: allowedDevOrigins.join(",") } : {}
11114
+ }
11115
+ });
11116
+ if (filterLogs) {
11117
+ for (const [src, dest] of [
11118
+ [nextProcess.stdout, process.stdout],
11119
+ [nextProcess.stderr, process.stderr]
11120
+ ]) {
11121
+ if (!src)
11122
+ continue;
11123
+ const filter = makeSkewLogFilter();
11124
+ createInterface2({ input: src }).on("line", (line) => {
11125
+ const out = filter(line);
11126
+ if (out !== null)
11127
+ dest.write(out + `
11128
+ `);
11129
+ });
11130
+ }
11131
+ }
11132
+ nextProcess.on("error", (error) => {
11133
+ console.error("Error starting Next.js:", error);
11134
+ process.exit(1);
11135
+ });
11136
+ nextProcess.on("exit", (code) => {
11137
+ process.exit(code || 0);
11138
+ });
11139
+ }
11140
+ var init_launch = __esm(() => {
11141
+ init_parse_script_args();
11142
+ init_install_diagnosis();
11143
+ init_package();
11144
+ });
11145
+
11146
+ // src/audit/cli.ts
11147
+ var exports_cli2 = {};
11148
+ __export(exports_cli2, {
11149
+ runAuditCli: () => runAuditCli,
11150
+ buildSummary: () => buildSummary,
11151
+ AUDIT_STAGES: () => AUDIT_STAGES
11152
+ });
11153
+ function colorOn() {
11154
+ if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
11155
+ return false;
11156
+ if (process.env.FORCE_COLOR === "0")
11157
+ return false;
11158
+ if (process.env.FORCE_COLOR)
11159
+ return true;
11160
+ return !!process.stdout.isTTY;
11161
+ }
11162
+ function c(code, s) {
11163
+ return colorOn() ? `${code}${s}${RESET2}` : s;
11164
+ }
11165
+ function num(n) {
11166
+ return n.toLocaleString("en-US");
11167
+ }
11168
+ function die(message) {
11169
+ process.stderr.write(`Error: ${message}
11170
+ `);
11171
+ process.exit(1);
11172
+ }
11173
+ function startProgress() {
11174
+ const n = AUDIT_STAGES.length;
11175
+ let stage = 0;
11176
+ let tick = 0;
11177
+ let done = false;
11178
+ let printed = false;
11179
+ const lineFor = (i) => {
11180
+ const s = AUDIT_STAGES[i];
11181
+ if (done || i < stage)
11182
+ return ` ${c(GREEN2, "✓")} ${c(DIM2, s.label)}`;
11183
+ if (i === stage) {
11184
+ return ` ${c(PINK2, SPINNER[tick % SPINNER.length])} ${s.label} ${c(DIM2, s.detail)}`;
11185
+ }
11186
+ return ` ${c(DIM2, "○")} ${c(DIM2, s.label)}`;
11187
+ };
11188
+ const render = () => {
11189
+ const lines = Array.from({ length: n }, (_, i) => lineFor(i));
11190
+ if (printed)
11191
+ process.stdout.write(`\x1B[${n}A`);
11192
+ process.stdout.write(lines.map((l) => `\x1B[2K${l}`).join(`
11193
+ `) + `
11194
+ `);
11195
+ printed = true;
11196
+ };
11197
+ render();
11198
+ const spinTimer = setInterval(() => {
11199
+ tick++;
11200
+ render();
11201
+ }, 90);
11202
+ const stageTimer = setInterval(() => {
11203
+ if (stage < n - 1) {
11204
+ stage++;
11205
+ render();
11206
+ }
11207
+ }, 1100);
11208
+ const stop = () => {
11209
+ clearInterval(spinTimer);
11210
+ clearInterval(stageTimer);
11211
+ };
11212
+ return {
11213
+ finish() {
11214
+ stop();
11215
+ done = true;
11216
+ render();
11217
+ },
11218
+ fail() {
11219
+ stop();
11220
+ process.stdout.write(`
11221
+ `);
11222
+ }
11223
+ };
11224
+ }
11225
+ async function runWithProgress(opts) {
11226
+ if (!process.stdout.isTTY) {
11227
+ process.stdout.write(` scanning your agent session history — this can take a moment…
11228
+ `);
11229
+ return runAudit(opts);
11230
+ }
11231
+ const progress = startProgress();
11232
+ try {
11233
+ const result = await runAudit(opts);
11234
+ progress.finish();
11235
+ return result;
11236
+ } catch (err) {
11237
+ progress.fail();
11238
+ throw err;
11239
+ }
11240
+ }
11241
+ function printHeader() {
11242
+ process.stdout.write(`
11243
+ ${c(PINK2, "\uD83D\uDEE1 failproofai audit")} ${c(DIM2, "· beta")}
11244
+
11245
+ `);
11246
+ process.stdout.write(` ${c(DIM2, "starting audit…")}
11247
+
11248
+ `);
11249
+ }
11250
+ function buildSummary(result) {
11251
+ const sessions = result.transcripts.scanned;
11252
+ const events = result.eventsScanned;
11253
+ const projects = result.projectsScanned.length;
11254
+ const enabledRows = result.results.filter((r) => r.source === "builtin" && r.enabledInConfig);
11255
+ const slippingRows = result.results.filter((r) => !(r.source === "builtin" && r.enabledInConfig));
11256
+ const lines = [];
11257
+ lines.push(`${c(GREEN2, "✓ audit complete")} ${c(DIM2, "·")} ` + `${c(BOLD, num(events))} tool call${events === 1 ? "" : "s"} across ` + `${num(sessions)} session${sessions === 1 ? "" : "s"}` + (projects > 0 ? ` ${c(DIM2, "·")} ${num(projects)} project${projects === 1 ? "" : "s"}` : ""));
11258
+ if (result.totals.hits === 0) {
11259
+ if (events > 0)
11260
+ lines.push(c(DIM2, "clean run — nothing flagged. nice."));
11261
+ return lines;
11262
+ }
11263
+ const parts = [];
11264
+ if (slippingRows.length > 0) {
11265
+ parts.push(`${c(PINK2, String(slippingRows.length))} ${slippingRows.length === 1 ? "pattern" : "patterns"} slipping through`);
11266
+ }
11267
+ if (enabledRows.length > 0) {
11268
+ parts.push(`${c(GREEN2, String(enabledRows.length))} already blocked by your policies`);
11269
+ }
11270
+ if (parts.length > 0)
11271
+ lines.push(parts.join(` ${c(DIM2, "·")} `));
11272
+ return lines;
11273
+ }
11274
+ function printSummary(result) {
11275
+ process.stdout.write(`
11276
+ `);
11277
+ for (const line of buildSummary(result))
11278
+ process.stdout.write(` ${line}
11279
+ `);
11280
+ }
11281
+ async function runAuditCli(args) {
11282
+ if (args.includes("--help") || args.includes("-h")) {
11283
+ process.stdout.write(HELP2);
11284
+ process.exit(0);
11285
+ }
11286
+ const stray = args.find((a) => a !== "--help" && a !== "-h");
11287
+ if (stray) {
11288
+ die(`\`audit\` takes no arguments yet (got: ${stray}).
11289
+ ` + `Run \`failproofai audit\` to scan your history and open the dashboard.`);
11290
+ }
11291
+ const instanceId = getInstanceId2();
11292
+ trackHookEvent2(instanceId, "cli_audit_started", { source: "cli" });
11293
+ printHeader();
11294
+ const opts = {};
11295
+ let result;
11296
+ try {
11297
+ result = await runWithProgress(opts);
11298
+ } catch (err) {
11299
+ await trackHookEvent2(instanceId, "cli_audit_failed", {
11300
+ source: "cli",
11301
+ error_type: err instanceof Error ? err.name : "unknown"
11302
+ });
11303
+ die(`Audit failed: ${err instanceof Error ? err.message : String(err)}`);
11304
+ }
11305
+ printSummary(result);
11306
+ await trackHookEvent2(instanceId, "cli_audit_completed", {
11307
+ source: "cli",
11308
+ events_scanned: result.eventsScanned,
11309
+ sessions_scanned: result.transcripts.scanned,
11310
+ total_hits: result.totals.hits,
11311
+ findings: result.results.length
11312
+ });
11313
+ if (result.eventsScanned === 0) {
11314
+ process.stdout.write(`
11315
+ ${c(DIM2, "no agent sessions found yet.")}
11316
+ ` + ` install hooks with ${c(CYAN, "failproofai policies --install")} ` + `${c(DIM2, "and come back after using your agent.")}
11317
+
11318
+ `);
11319
+ process.exit(0);
11320
+ }
11321
+ const persisted = writeDashboardCache(opts, result);
11322
+ if (!persisted) {
11323
+ process.stdout.write(`
11324
+ ${c(PINK2, "!")} ${c(DIM2, "couldn't save the audit cache; the dashboard may show an empty state.")}
11325
+ `);
11326
+ }
11327
+ const url = `http://localhost:${DASHBOARD_PORT}/audit`;
11328
+ process.stdout.write(`
11329
+ ${c(DIM2, "starting the dashboard…")}
11330
+ ` + ` ${c(PINK2, "✦")} ${c(BOLD, "here's your audit")} ${c(DIM2, "→")} ${c(CYAN, url)}
11331
+ ` + ` ${c(DIM2, "(opening in your browser — press Ctrl+C to stop the server)")}
11332
+
11333
+ `);
11334
+ openWhenReady(DASHBOARD_PORT, "/audit");
11335
+ const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
11336
+ launch2("start");
11337
+ }
11338
+ var DASHBOARD_PORT = 8020, AUDIT_STAGES, HELP2, RESET2 = "\x1B[0m", DIM2 = "\x1B[2m", BOLD = "\x1B[1m", PINK2 = "\x1B[38;5;204m", GREEN2 = "\x1B[38;5;120m", CYAN = "\x1B[38;5;81m", SPINNER;
11339
+ var init_cli2 = __esm(() => {
11340
+ init_audit();
11341
+ init_dashboard_cache();
11342
+ init_hook_telemetry2();
11343
+ init_telemetry_id2();
11344
+ init_open_browser();
11345
+ AUDIT_STAGES = [
11346
+ { label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
11347
+ { label: "parsing session logs", detail: "reading JSONL + sqlite session stores" },
11348
+ { label: "running policy checks", detail: "replaying through 30 builtin policies" },
11349
+ { label: "aggregating results", detail: "counting hits, ranking by frequency" }
11350
+ ];
11351
+ HELP2 = `
11352
+ failproofai audit — audit your AI agent's behavior, then open the dashboard
11353
+
11354
+ USAGE
11355
+ failproofai audit Scan your agent-CLI session history for risky and
11356
+ wasteful patterns, then open the audit dashboard.
11357
+ failproofai audit --help Show this help.
11358
+
11359
+ WHAT IT DOES
11360
+ 1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
11361
+ Copilot, OpenCode, Pi, Gemini) — entirely on your machine.
11362
+ 2. Starts the local dashboard and opens
11363
+ http://localhost:${DASHBOARD_PORT}/audit with your results.
11364
+
11365
+ Runs fully offline — no account or network required. Press Ctrl+C to stop the
11366
+ dashboard server when you're done.
11367
+ `.trimStart();
11368
+ SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
11369
+ });
11370
+
11371
+ // src/hooks/manager.ts
11372
+ import { execSync as execSync6 } from "node:child_process";
11373
+ import { resolve as resolve15, basename as basename5 } from "node:path";
11374
+ import { homedir as homedir23, platform as platform3, arch as arch2, release as release2, hostname as hostname2 } from "node:os";
11375
+ function getSettingsPath2(scope, cwd) {
11376
+ return claudeCode.getSettingsPath(scope, cwd);
11377
+ }
11378
+ function scopeLabel2(scope) {
11379
+ switch (scope) {
11380
+ case "user":
11381
+ return `~/.claude/settings.json`;
11382
+ case "project":
11383
+ return `{cwd}/.claude/settings.json`;
11384
+ case "local":
11385
+ return `{cwd}/.claude/settings.local.json`;
11386
+ }
11387
+ }
11388
+ function resolveFailproofaiBinary2() {
11389
+ const override = process.env.FAILPROOFAI_BINARY_OVERRIDE;
11390
+ if (override && override.trim())
11391
+ return override.trim();
11392
+ try {
11393
+ const cmd = process.platform === "win32" ? "where failproofai" : "which failproofai";
11394
+ const result = execSync6(cmd, { encoding: "utf8" }).trim();
11395
+ return result.split(`
11396
+ `)[0].trim();
11397
+ } catch {
11398
+ throw new CliError(`failproofai binary not found in PATH.
11399
+ ` + "Install it globally first: npm install -g failproofai");
11400
+ }
11401
+ }
11402
+ function validatePolicyNames2(names) {
11403
+ const invalid = names.filter((n) => !VALID_POLICY_NAMES2.has(n));
11404
+ if (invalid.length > 0) {
11405
+ const validList = [...VALID_POLICY_NAMES2].join(", ");
11406
+ throw new CliError(`Unknown policy name(s): ${invalid.join(", ")}
11407
+ ` + `Valid policies: ${validList}`);
11408
+ }
11409
+ }
11410
+ function deduplicateScopes2(scopes, cwd) {
11411
+ const seen = new Set;
11412
+ return scopes.filter((s) => {
11413
+ const p = getSettingsPath2(s, cwd);
11414
+ if (seen.has(p))
11415
+ return false;
11416
+ seen.add(p);
11417
+ return true;
11418
+ });
11419
+ }
11420
+ function hooksInstalledInSettings2(scope, cwd) {
11421
+ return claudeCode.hooksInstalledInSettings(scope, cwd);
11422
+ }
11423
+ async function installHooks2(policyNames, scope = "user", cwd, includeBeta = false, source, customPoliciesPath, removeCustomHooks = false, cli) {
11424
+ if (policyNames !== undefined && policyNames.length > 0) {
11425
+ const nonAllNames = policyNames.filter((n) => n !== "all");
11426
+ if (nonAllNames.length > 0)
11427
+ validatePolicyNames2(nonAllNames);
11428
+ if (policyNames.includes("all") && nonAllNames.length > 0) {
11429
+ throw new CliError(`"all" cannot be combined with specific policy names.
11430
+ ` + `Use either: --install all or --install block-sudo sanitize-jwt ...`);
11431
+ }
11432
+ }
11433
+ const selectedClis = cli && cli.length > 0 ? [...new Set(cli)] : ["claude"];
11434
+ for (const cliId of selectedClis) {
11435
+ const integration = getIntegration(cliId);
11436
+ if (!integration.scopes.includes(scope)) {
11437
+ try {
11438
+ await trackHookEvent2(getInstanceId2(), "scope_validation_failed", {
11439
+ cli: cliId,
11440
+ scope,
11441
+ supported_scopes: integration.scopes
11442
+ });
11443
+ } catch {}
11444
+ throw new CliError(`Scope "${scope}" is not supported by ${integration.displayName}. ` + `Valid scopes: ${integration.scopes.join(", ")}`);
11445
+ }
11446
+ }
11447
+ const binaryPath = resolveFailproofaiBinary2();
11448
+ const previousConfig = readScopedHooksConfig(scope, cwd);
11449
+ const previousEnabled = new Set(previousConfig.enabledPolicies);
11450
+ let selectedPolicies;
11451
+ if (policyNames !== undefined) {
11452
+ let incoming;
11453
+ if (policyNames.length === 1 && policyNames[0] === "all") {
11454
+ incoming = BUILTIN_POLICIES.filter((p) => includeBeta || !p.beta).map((p) => p.name);
11455
+ } else {
11456
+ incoming = policyNames;
11457
+ }
11458
+ selectedPolicies = [...new Set([...previousConfig.enabledPolicies, ...incoming])];
11459
+ } else {
11460
+ const preSelected = previousConfig.enabledPolicies.length > 0 ? previousConfig.enabledPolicies : undefined;
11461
+ selectedPolicies = await promptPolicySelection(preSelected, { includeBeta });
11462
+ }
11463
+ const configToWrite = { ...previousConfig, enabledPolicies: selectedPolicies };
11464
+ if (removeCustomHooks) {
11465
+ delete configToWrite.customPoliciesPath;
11466
+ } else if (customPoliciesPath) {
11467
+ configToWrite.customPoliciesPath = resolve15(customPoliciesPath);
11468
+ let validatedHooks = [];
11469
+ try {
11470
+ validatedHooks = await loadCustomHooks(configToWrite.customPoliciesPath, { strict: true });
11471
+ } catch (err) {
11472
+ const msg = err instanceof Error ? err.message : String(err);
11473
+ try {
11474
+ await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
11475
+ scope,
11476
+ error_type: /not found/i.test(msg) ? "file_not_found" : "load_error"
11477
+ });
11478
+ } catch {}
11479
+ console.error(`Error: ${msg}`);
11480
+ process.exit(1);
11481
+ }
11482
+ if (validatedHooks.length === 0) {
11483
+ try {
11484
+ await trackHookEvent2(getInstanceId2(), "custom_policy_validation_failed", {
11485
+ scope,
11486
+ error_type: "no_hooks_registered"
11487
+ });
11488
+ } catch {}
11489
+ console.error(`Error: no hooks registered in ${customPoliciesPath}. ` + `Make sure your file calls customPolicies.add(...) at least once.`);
11490
+ process.exit(1);
11491
+ }
11492
+ console.log(`
11493
+ Validated ${validatedHooks.length} custom hook(s): ${validatedHooks.map((h) => h.name).join(", ")}`);
11494
+ }
11495
+ writeScopedHooksConfig(configToWrite, scope, cwd);
11496
+ console.log(`
11497
+ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
11498
+ `);
11499
+ if (removeCustomHooks) {
11500
+ console.log("Custom hooks path cleared.");
11501
+ } else if (configToWrite.customPoliciesPath) {
11502
+ console.log(`Custom hooks path: ${configToWrite.customPoliciesPath}`);
11503
+ }
11504
+ const writtenSettingsPaths = [];
11505
+ for (const cliId of selectedClis) {
11506
+ const integration = getIntegration(cliId);
11507
+ const settingsPath = integration.getSettingsPath(scope, cwd);
11508
+ try {
11509
+ const settings = integration.readSettings(settingsPath);
11510
+ integration.writeHookEntries(settings, binaryPath, scope);
11511
+ integration.writeSettings(settingsPath, settings);
11512
+ writtenSettingsPaths.push({ cli: cliId, path: settingsPath });
11513
+ } catch (err) {
11514
+ const errorType = err instanceof Error && /EACCES|EPERM/.test(err.message) ? "permission_denied" : err instanceof Error && /ENOENT|ENOTDIR/.test(err.message) ? "path_not_found" : "write_error";
11515
+ try {
11516
+ await trackHookEvent2(getInstanceId2(), "hook_write_failed", {
11517
+ cli: cliId,
11518
+ scope,
11519
+ error_type: errorType
11520
+ });
11521
+ } catch {}
11522
+ throw err;
11523
+ }
11524
+ }
11525
+ try {
11526
+ const newSet = new Set(selectedPolicies);
11527
+ const policiesAdded = selectedPolicies.filter((p) => !previousEnabled.has(p));
11528
+ const policiesRemoved = [...previousEnabled].filter((p) => !newSet.has(p));
11529
+ const distinctId = getInstanceId2();
11530
+ await trackHookEvent2(distinctId, "hooks_installed", {
11531
+ scope,
11532
+ cli: selectedClis,
11533
+ cli_count: selectedClis.length,
11534
+ policies: selectedPolicies,
11535
+ policy_count: selectedPolicies.length,
11536
+ policies_added: policiesAdded,
11537
+ policies_removed: policiesRemoved,
11538
+ ...source ? { source } : {},
11539
+ platform: platform3(),
11540
+ arch: arch2(),
11541
+ os_release: release2(),
11542
+ hostname_hash: hashToId2(hostname2()),
11543
+ has_custom_hooks_path: !!configToWrite.customPoliciesPath,
11544
+ has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
9444
11545
  param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
9445
11546
  command_format: scope === "project" ? "npx" : "absolute"
9446
11547
  });
@@ -9529,7 +11630,7 @@ function clisLabel(detected) {
9529
11630
  return detected.map((id) => getIntegration(id).displayName).join(", ");
9530
11631
  }
9531
11632
  async function promptYesNo(stdin, stdout) {
9532
- return new Promise((resolve13) => {
11633
+ return new Promise((resolve16) => {
9533
11634
  const rl = readline4.createInterface({ input: stdin, output: stdout });
9534
11635
  let settled = false;
9535
11636
  const finish = (answer) => {
@@ -9537,7 +11638,7 @@ async function promptYesNo(stdin, stdout) {
9537
11638
  return;
9538
11639
  settled = true;
9539
11640
  rl.close();
9540
- resolve13(answer);
11641
+ resolve16(answer);
9541
11642
  };
9542
11643
  rl.on("SIGINT", () => finish("sigint"));
9543
11644
  rl.question("Install policies now? [Y/n] ", (raw) => {
@@ -9609,211 +11710,25 @@ var init_first_run_nudge = __esm(() => {
9609
11710
  init_telemetry_id2();
9610
11711
  });
9611
11712
 
9612
- // scripts/parse-script-args.ts
9613
- import { resolve as resolve13 } from "path";
9614
- function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
9615
- const raw = inlineValue ?? args[index + 1];
9616
- if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
9617
- console.error(`Error: ${flagName} requires ${errorLabel}`);
9618
- process.exit(1);
9619
- }
9620
- const value = options?.resolve ? resolve13(raw) : raw;
9621
- return { value, spliceCount: inlineValue !== null ? 1 : 2 };
9622
- }
9623
- function parseScriptArgs(argv) {
9624
- const args = [...argv];
9625
- let loggingLevel;
9626
- let disableTelemetry = false;
9627
- let allowedDevOrigins;
9628
- for (let i = 0;i < args.length; i++) {
9629
- const arg = args[i];
9630
- const eqIdx = arg.indexOf("=");
9631
- const flag = eqIdx >= 0 ? arg.slice(0, eqIdx) : arg;
9632
- const inlineValue = eqIdx >= 0 ? arg.slice(eqIdx + 1) : null;
9633
- if (flag === "--logging") {
9634
- const raw = inlineValue ?? args[i + 1];
9635
- if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
9636
- console.error("Error: --logging requires a level (info, warn, error)");
9637
- process.exit(1);
9638
- }
9639
- const val = raw.toLowerCase();
9640
- if (val !== "info" && val !== "warn" && val !== "error") {
9641
- console.error("Error: --logging must be one of: info, warn, error");
9642
- process.exit(1);
9643
- }
9644
- loggingLevel = val;
9645
- args.splice(i, inlineValue !== null ? 1 : 2);
9646
- i--;
9647
- continue;
9648
- }
9649
- if (flag === "--disable-telemetry") {
9650
- disableTelemetry = true;
9651
- args.splice(i, 1);
9652
- i--;
9653
- continue;
9654
- }
9655
- if (flag === "--allowed-origins") {
9656
- const { value, spliceCount } = parseStringFlag(flag, "a comma-separated list of origins", inlineValue, args, i);
9657
- allowedDevOrigins = value.split(",").map((s) => s.trim()).filter(Boolean);
9658
- args.splice(i, spliceCount);
9659
- i--;
9660
- continue;
9661
- }
9662
- }
9663
- return { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs: args };
9664
- }
9665
- var init_parse_script_args = () => {};
9666
-
9667
- // scripts/install-diagnosis.mjs
9668
- import { existsSync as existsSync14, readFileSync as readFileSync11, realpathSync } from "node:fs";
9669
- import { dirname as dirname6, resolve as resolve14 } from "node:path";
9670
- import { homedir as homedir21, platform as platform3 } from "node:os";
9671
- import { spawnSync } from "node:child_process";
9672
- function findPackageRoot(start) {
9673
- try {
9674
- let dir = realpathSync(start);
9675
- if (existsSync14(dir) && !existsSync14(resolve14(dir, "package.json"))) {
9676
- dir = dirname6(dir);
9677
- }
9678
- while (true) {
9679
- const pkgPath = resolve14(dir, "package.json");
9680
- if (existsSync14(pkgPath)) {
9681
- try {
9682
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
9683
- if (pkg.name === PKG_NAME)
9684
- return dir;
9685
- } catch {}
9686
- }
9687
- const parent = dirname6(dir);
9688
- if (parent === dir)
9689
- return null;
9690
- dir = parent;
9691
- }
9692
- } catch {
9693
- return null;
9694
- }
9695
- }
9696
- function readPackageVersion(packageRoot) {
9697
- if (!packageRoot)
9698
- return null;
9699
- try {
9700
- const pkg = JSON.parse(readFileSync11(resolve14(packageRoot, "package.json"), "utf8"));
9701
- return typeof pkg.version === "string" ? pkg.version : null;
9702
- } catch {
9703
- return null;
9704
- }
9705
- }
9706
- function resolvePathFirstBinary() {
9707
- try {
9708
- const isWin = platform3() === "win32";
9709
- const res = isWin ? spawnSync("where", [PKG_NAME], { encoding: "utf8" }) : spawnSync("sh", ["-c", `command -v ${PKG_NAME}`], { encoding: "utf8" });
9710
- if (res.status !== 0)
9711
- return null;
9712
- const first = (res.stdout || "").split(/\r?\n/).find((l) => l.trim().length > 0);
9713
- return first ? first.trim() : null;
9714
- } catch {
9715
- return null;
9716
- }
9717
- }
9718
- function locateNpmGlobal() {
9719
- try {
9720
- const res = spawnSync("npm", ["root", "-g"], { encoding: "utf8" });
9721
- if (res.status !== 0)
9722
- return null;
9723
- const root = (res.stdout || "").trim();
9724
- if (!root)
9725
- return null;
9726
- const candidate = resolve14(root, PKG_NAME);
9727
- return existsSync14(resolve14(candidate, "package.json")) ? candidate : null;
9728
- } catch {
9729
- return null;
9730
- }
9731
- }
9732
- function locateBunGlobal() {
9733
- try {
9734
- const candidate = resolve14(homedir21(), ".bun", "install", "global", "node_modules", PKG_NAME);
9735
- return existsSync14(resolve14(candidate, "package.json")) ? candidate : null;
9736
- } catch {
9737
- return null;
9738
- }
9739
- }
9740
- function buildRecommendation(pathFirstBin) {
9741
- if (!pathFirstBin)
9742
- return null;
9743
- const bunBinPrefix = resolve14(homedir21(), ".bun", "bin") + "/";
9744
- const bunGlobalPrefix = resolve14(homedir21(), ".bun", "install", "global") + "/";
9745
- const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
9746
- if (isBun) {
9747
- return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
9748
- }
9749
- return `npm rm -g ${PKG_NAME}`;
9750
- }
9751
- function diagnoseShadow(self) {
9752
- const selfPackageRoot = (() => {
9753
- try {
9754
- return self?.selfPackageRoot ? realpathSync(self.selfPackageRoot) : null;
9755
- } catch {
9756
- return self?.selfPackageRoot ?? null;
9757
- }
9758
- })();
9759
- const selfVersion = self?.selfVersion ?? null;
9760
- const pathFirstBin = resolvePathFirstBinary();
9761
- const pathFirstPackageRoot = pathFirstBin ? findPackageRoot(pathFirstBin) : null;
9762
- const pathFirstVersion = readPackageVersion(pathFirstPackageRoot);
9763
- const npmGlobalPath = locateNpmGlobal();
9764
- const npmGlobalVersion = readPackageVersion(npmGlobalPath);
9765
- const bunGlobalPath = locateBunGlobal();
9766
- const bunGlobalVersion = readPackageVersion(bunGlobalPath);
9767
- let shadowed = false;
9768
- if (selfPackageRoot && pathFirstPackageRoot && pathFirstPackageRoot !== selfPackageRoot) {
9769
- shadowed = true;
9770
- } else if (pathFirstPackageRoot) {
9771
- if (npmGlobalPath && npmGlobalPath !== pathFirstPackageRoot)
9772
- shadowed = true;
9773
- else if (bunGlobalPath && bunGlobalPath !== pathFirstPackageRoot)
9774
- shadowed = true;
9775
- }
9776
- const recommendation = shadowed ? buildRecommendation(pathFirstBin) : null;
9777
- let shadowDescription = null;
9778
- if (shadowed) {
9779
- shadowDescription = `PATH resolves to ${pathFirstPackageRoot}` + (pathFirstVersion ? ` (v${pathFirstVersion})` : "") + `, but you just installed ${selfPackageRoot}` + (selfVersion ? ` (v${selfVersion})` : "") + ".";
9780
- }
9781
- return {
9782
- selfPackageRoot,
9783
- selfVersion,
9784
- pathFirstBin,
9785
- pathFirstPath: pathFirstPackageRoot,
9786
- pathFirstVersion,
9787
- npmGlobalPath,
9788
- npmGlobalVersion,
9789
- bunGlobalPath,
9790
- bunGlobalVersion,
9791
- shadowed,
9792
- shadowDescription,
9793
- recommendation
9794
- };
9795
- }
9796
- var PKG_NAME = "failproofai";
9797
- var init_install_diagnosis = () => {};
9798
-
9799
11713
  // scripts/launch.ts
9800
- var exports_launch = {};
9801
- __export(exports_launch, {
9802
- launch: () => launch
11714
+ var exports_launch2 = {};
11715
+ __export(exports_launch2, {
11716
+ launch: () => launch2
9803
11717
  });
9804
- import { spawn } from "child_process";
9805
- import { realpathSync as realpathSync2, existsSync as existsSync15 } from "node:fs";
9806
- import { resolve as resolve15, dirname as dirname7 } from "node:path";
9807
- import { fileURLToPath as fileURLToPath2 } from "node:url";
9808
- function launch(mode) {
11718
+ import { spawn as spawn3 } from "child_process";
11719
+ import { realpathSync as realpathSync3, existsSync as existsSync18 } from "node:fs";
11720
+ import { resolve as resolve16, dirname as dirname8 } from "node:path";
11721
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
11722
+ import { createInterface as createInterface4 } from "node:readline";
11723
+ function launch2(mode) {
9809
11724
  const { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs } = parseScriptArgs(process.argv.slice(2));
9810
11725
  console.log(`
9811
11726
  failproof ai
9812
11727
  `);
9813
11728
  console.log(` \uD83D\uDCE6 Version: ${version2}`);
9814
11729
  console.log(` ⭐ Star us: https://github.com/failproofai/failproofai`);
9815
- console.log(` \uD83D\uDCD6 Docs: https://befailproof.ai`);
9816
- console.log(` \uD83D\uDCAC Slack: https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ
11730
+ console.log(` \uD83D\uDCD6 Docs: https://docs.befailproof.ai/introduction`);
11731
+ console.log(` \uD83D\uDCAC Discord: https://discord.gg/2zjBZP7yQJ
9817
11732
  `);
9818
11733
  let cmd;
9819
11734
  let cmdArgs;
@@ -9823,9 +11738,9 @@ function launch(mode) {
9823
11738
  process.env.PORT = port;
9824
11739
  process.env.HOSTNAME = "0.0.0.0";
9825
11740
  cmd = "node";
9826
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve15(dirname7(realpathSync2(fileURLToPath2(import.meta.url))), "..");
9827
- const serverJsPath = resolve15(packageRoot, ".next/standalone/server.js");
9828
- if (!existsSync15(serverJsPath)) {
11741
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve16(dirname8(realpathSync3(fileURLToPath3(import.meta.url))), "..");
11742
+ const serverJsPath = resolve16(packageRoot, ".next/standalone/server.js");
11743
+ if (!existsSync18(serverJsPath)) {
9829
11744
  let shadowMessage = null;
9830
11745
  try {
9831
11746
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -9858,15 +11773,33 @@ Error: Cannot find server.js at:
9858
11773
  cmd = "bunx";
9859
11774
  cmdArgs = ["--bun", "next", "dev", ...remainingArgs];
9860
11775
  }
9861
- const nextProcess = spawn(cmd, cmdArgs, {
9862
- stdio: "inherit",
11776
+ const filterLogs = mode === "start";
11777
+ const nextProcess = spawn3(cmd, cmdArgs, {
11778
+ stdio: filterLogs ? ["inherit", "pipe", "pipe"] : "inherit",
9863
11779
  env: {
9864
11780
  ...process.env,
11781
+ ...filterLogs ? { FORCE_COLOR: process.env.FORCE_COLOR ?? "1" } : {},
9865
11782
  ...loggingLevel ? { FAILPROOFAI_LOG_LEVEL: loggingLevel } : {},
9866
11783
  ...disableTelemetry ? { FAILPROOFAI_TELEMETRY_DISABLED: "1" } : {},
9867
11784
  ...allowedDevOrigins ? { FAILPROOFAI_ALLOWED_DEV_ORIGINS: allowedDevOrigins.join(",") } : {}
9868
11785
  }
9869
11786
  });
11787
+ if (filterLogs) {
11788
+ for (const [src, dest] of [
11789
+ [nextProcess.stdout, process.stdout],
11790
+ [nextProcess.stderr, process.stderr]
11791
+ ]) {
11792
+ if (!src)
11793
+ continue;
11794
+ const filter = makeSkewLogFilter();
11795
+ createInterface4({ input: src }).on("line", (line) => {
11796
+ const out = filter(line);
11797
+ if (out !== null)
11798
+ dest.write(out + `
11799
+ `);
11800
+ });
11801
+ }
11802
+ }
9870
11803
  nextProcess.on("error", (error) => {
9871
11804
  console.error("Error starting Next.js:", error);
9872
11805
  process.exit(1);
@@ -9875,7 +11808,7 @@ Error: Cannot find server.js at:
9875
11808
  process.exit(code || 0);
9876
11809
  });
9877
11810
  }
9878
- var init_launch = __esm(() => {
11811
+ var init_launch2 = __esm(() => {
9879
11812
  init_parse_script_args();
9880
11813
  init_install_diagnosis();
9881
11814
  init_package();
@@ -9899,18 +11832,18 @@ var init_cli_error2 = __esm(() => {
9899
11832
  });
9900
11833
 
9901
11834
  // bin/failproofai.mjs
9902
- import { realpathSync as realpathSync3 } from "fs";
9903
- import { dirname as dirname8, resolve as resolve16 } from "path";
9904
- import { fileURLToPath as fileURLToPath3 } from "url";
11835
+ import { realpathSync as realpathSync4 } from "fs";
11836
+ import { dirname as dirname9, resolve as resolve17 } from "path";
11837
+ import { fileURLToPath as fileURLToPath4 } from "url";
9905
11838
  // package.json
9906
- var version = "0.0.11-beta.9";
11839
+ var version = "0.0.11";
9907
11840
 
9908
11841
  // bin/failproofai.mjs
9909
11842
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
9910
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve16(dirname8(realpathSync3(fileURLToPath3(import.meta.url))), "..");
11843
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve17(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..");
9911
11844
  }
9912
11845
  if (!process.env.FAILPROOFAI_DIST_PATH) {
9913
- process.env.FAILPROOFAI_DIST_PATH = resolve16(dirname8(realpathSync3(fileURLToPath3(import.meta.url))), "..", "dist");
11846
+ process.env.FAILPROOFAI_DIST_PATH = resolve17(dirname9(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
9914
11847
  }
9915
11848
  var args = process.argv.slice(2);
9916
11849
  if (args[0] === "p")
@@ -9957,7 +11890,7 @@ if (hookIdx >= 0) {
9957
11890
  }
9958
11891
  }
9959
11892
  async function runCli() {
9960
- const SUBCOMMANDS2 = ["policies", "policy", "auth"];
11893
+ const SUBCOMMANDS2 = ["policies", "policy", "auth", "audit"];
9961
11894
  if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS2.includes(args[0])) {
9962
11895
  const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
9963
11896
  if (extraArgs.length > 0) {
@@ -10004,6 +11937,10 @@ COMMANDS
10004
11937
  whoami Print the currently authenticated identity
10005
11938
  auth --help, -h Show this help for the auth command
10006
11939
 
11940
+ audit Audit your agent's behavior, then open the
11941
+ dashboard at http://localhost:8020/audit
11942
+ audit --help, -h Show this help for the audit command
11943
+
10007
11944
  --version, -v Print version and exit
10008
11945
  --help, -h Show this help message
10009
11946
 
@@ -10036,8 +11973,8 @@ EXAMPLES
10036
11973
 
10037
11974
  LINKS
10038
11975
  \u2B50 Star us: https://github.com/failproofai/failproofai
10039
- \uD83D\uDCD6 Docs: https://befailproof.ai
10040
- \uD83D\uDCAC Slack: https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ
11976
+ \uD83D\uDCD6 Docs: https://docs.befailproof.ai/introduction
11977
+ \uD83D\uDCAC Discord: https://discord.gg/2zjBZP7yQJ
10041
11978
  `.trimStart());
10042
11979
  process.exit(0);
10043
11980
  }
@@ -10254,9 +12191,19 @@ Run \`failproofai policies --help\` for usage.`);
10254
12191
  lastSubcommand = "auth";
10255
12192
  const { runAuthCli: runAuthCli2 } = await Promise.resolve().then(() => (init_cli(), exports_cli));
10256
12193
  await runAuthCli2(args.slice(1));
10257
- await track("cli_auth_invoked", { args_count: args.length - 1 });
12194
+ await track("cli_auth_invoked", {
12195
+ args_count: args.length - 1,
12196
+ subcommand: args[1] ?? "help",
12197
+ exit_code: process.exitCode ?? 0
12198
+ });
10258
12199
  process.exit(process.exitCode ?? 0);
10259
12200
  }
12201
+ if (args[0] === "audit") {
12202
+ lastSubcommand = "audit";
12203
+ const { runAuditCli: runAuditCli2 } = await Promise.resolve().then(() => (init_cli2(), exports_cli2));
12204
+ await runAuditCli2(args.slice(1));
12205
+ return;
12206
+ }
10260
12207
  if (args[0] === "policy") {
10261
12208
  lastSubcommand = "policy";
10262
12209
  const subArgs = args.slice(1);
@@ -10379,7 +12326,7 @@ For multiple policies use \`failproofai policies --${action === "add" ? "install
10379
12326
  dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
10380
12327
  return dp[m][n];
10381
12328
  };
10382
- const primary = ["--version", "--help", "--hook", "policies", "policy", "auth"];
12329
+ const primary = ["--version", "--help", "--hook", "policies", "policy", "auth", "audit"];
10383
12330
  const closest = primary.reduce((best, flag) => {
10384
12331
  const dist = levenshtein(unknownFlag, flag);
10385
12332
  return dist < best.dist ? { flag, dist } : best;
@@ -10400,8 +12347,8 @@ Run \`failproofai --help\` for usage details.`);
10400
12347
  await maybeRunFirstRunNudge2();
10401
12348
  } catch {}
10402
12349
  }
10403
- const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
10404
- launch2("start");
12350
+ const { launch: launch3 } = await Promise.resolve().then(() => (init_launch2(), exports_launch2));
12351
+ launch3("start");
10405
12352
  }
10406
12353
  var { CliError: CliError3 } = await Promise.resolve().then(() => (init_cli_error2(), exports_cli_error));
10407
12354
  try {