failproofai 0.0.6-beta.4 → 0.0.6-beta.5

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 (85) 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 +2 -2
  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 +1 -1
  26. package/.next/standalone/.next/server/app/index.html +1 -1
  27. package/.next/standalone/.next/server/app/index.rsc +15 -15
  28. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  30. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  31. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  32. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  33. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  34. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  37. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  40. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  43. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  44. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
  50. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  51. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
  52. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
  53. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
  54. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
  55. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
  56. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__111.vxi._.js → [root-of-the-server]__0u49i20._.js} +2 -2
  57. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
  58. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0om-5pe._.js → [root-of-the-server]__0xezw2w._.js} +2 -2
  59. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
  62. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0mebn66._.js +1 -1
  65. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  66. package/.next/standalone/.next/server/pages/404.html +2 -2
  67. package/.next/standalone/.next/server/pages/500.html +1 -1
  68. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  70. package/.next/standalone/.next/static/chunks/{0pk2h2.mjxy.m.js → 03xexm0jftw.x.js} +1 -1
  71. package/.next/standalone/.next/static/chunks/{140xx_tfr~lm_.js → 0_-7qmtiagi-~.js} +1 -1
  72. package/.next/standalone/.next/static/chunks/{0rcwkbh24w38b.js → 0af6vx3nh6v77.js} +1 -1
  73. package/.next/standalone/.next/static/chunks/{070orfsl6.xal.js → 0hrob6m-1.n1d.js} +1 -1
  74. package/.next/standalone/.next/static/chunks/{169_e4dq~1~b6.js → 0ms9sfkvw5h5i.js} +1 -1
  75. package/.next/standalone/.next/static/chunks/{02dqjyv6_9mhq.js → 0on5naeqa0w.x.js} +2 -2
  76. package/.next/standalone/.next/static/chunks/{0o547jv-k_k35.js → 0q~7ymasjp~n7.js} +1 -1
  77. package/.next/standalone/.next/static/chunks/{17ne4p.1sw1jy.js → 11vga8q3t~527.js} +1 -1
  78. package/.next/standalone/package.json +1 -1
  79. package/.next/standalone/server.js +1 -1
  80. package/dist/cli.mjs +90 -2
  81. package/package.json +1 -1
  82. package/src/hooks/builtin-policies.ts +117 -0
  83. /package/.next/standalone/.next/static/{wOkJXoch1UmRAmyIuKZWc → Q-y1SELeezrTyQx0E2uqg}/_buildManifest.js +0 -0
  84. /package/.next/standalone/.next/static/{wOkJXoch1UmRAmyIuKZWc → Q-y1SELeezrTyQx0E2uqg}/_clientMiddlewareManifest.js +0 -0
  85. /package/.next/standalone/.next/static/{wOkJXoch1UmRAmyIuKZWc → Q-y1SELeezrTyQx0E2uqg}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -1045,6 +1045,79 @@ function requirePrBeforeStop(ctx) {
1045
1045
  return allow("Could not check PR status, skipping.");
1046
1046
  }
1047
1047
  }
1048
+ function requireNoConflictsBeforeStop(ctx) {
1049
+ const cwd = ctx.session?.cwd;
1050
+ if (!cwd)
1051
+ return allow("No working directory available, skipping conflict check.");
1052
+ const branch = getCurrentBranch(cwd);
1053
+ if (!branch || branch === "HEAD")
1054
+ return allow("Detached HEAD, skipping conflict check.");
1055
+ const baseBranch = ctx.params?.baseBranch ?? "main";
1056
+ if (branch === baseBranch) {
1057
+ return allow(`On base branch "${baseBranch}", skipping conflict check.`);
1058
+ }
1059
+ let localSkipped = false;
1060
+ try {
1061
+ execFileSync("git", ["rev-parse", "--verify", `origin/${baseBranch}`], {
1062
+ cwd,
1063
+ encoding: "utf8",
1064
+ stdio: ["pipe", "pipe", "pipe"],
1065
+ timeout: 3000
1066
+ });
1067
+ const ahead = execFileSync("git", ["log", `origin/${baseBranch}..HEAD`, "--oneline"], { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }).trim();
1068
+ if (!ahead) {
1069
+ localSkipped = true;
1070
+ } else {
1071
+ execFileSync("git", ["merge-tree", "--write-tree", "--name-only", `origin/${baseBranch}`, "HEAD"], { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 });
1072
+ }
1073
+ } catch (err) {
1074
+ const e = err;
1075
+ if (e.status === 1) {
1076
+ const out = (typeof e.stdout === "string" ? e.stdout : e.stdout?.toString("utf8") ?? "").trim();
1077
+ const lines = out.split(`
1078
+ `);
1079
+ const files = [];
1080
+ for (let i = 1;i < lines.length; i++) {
1081
+ const line = lines[i];
1082
+ if (line === "")
1083
+ break;
1084
+ files.push(line);
1085
+ }
1086
+ const fileList = files.length ? files.join(", ") : "one or more files";
1087
+ return deny(`Branch "${branch}" has merge conflicts with ${baseBranch} in: ${fileList}. ` + `Rebase or merge origin/${baseBranch} now and resolve the conflicts.`);
1088
+ }
1089
+ localSkipped = true;
1090
+ }
1091
+ try {
1092
+ execSync("gh --version", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
1093
+ } catch {
1094
+ return allow(localSkipped ? "Local conflict check skipped and gh CLI not installed, skipping conflict check." : `Branch "${branch}" merges cleanly with ${baseBranch} locally (gh CLI not installed, PR mergeability not verified).`);
1095
+ }
1096
+ let prJson;
1097
+ try {
1098
+ prJson = execSync("gh pr view --json mergeable,number,url", {
1099
+ cwd,
1100
+ encoding: "utf8",
1101
+ stdio: ["pipe", "pipe", "pipe"],
1102
+ timeout: 15000
1103
+ }).trim();
1104
+ } catch {
1105
+ return allow(localSkipped ? "No pull request found for branch, skipping conflict check." : `Branch "${branch}" merges cleanly with ${baseBranch} locally (no PR to verify against).`);
1106
+ }
1107
+ let pr;
1108
+ try {
1109
+ pr = JSON.parse(prJson);
1110
+ } catch {
1111
+ return allow("Could not parse gh pr view output, skipping PR mergeability check.");
1112
+ }
1113
+ if (pr.mergeable === "CONFLICTING") {
1114
+ return deny(`PR #${pr.number} has merge conflicts per GitHub (${pr.url}). ` + `Rebase or merge origin/${baseBranch} now and resolve the conflicts.`);
1115
+ }
1116
+ if (pr.mergeable === "UNKNOWN") {
1117
+ return deny(`GitHub is still computing mergeability for PR #${pr.number} (${pr.url}). ` + `Wait ~10 seconds, then re-check with \`gh pr view --json mergeable\` before attempting to stop again.`);
1118
+ }
1119
+ return allow(`PR #${pr.number} merges cleanly per GitHub.`);
1120
+ }
1048
1121
  function requireCiGreenBeforeStop(ctx) {
1049
1122
  const cwd = ctx.session?.cwd;
1050
1123
  if (!cwd)
@@ -1502,6 +1575,21 @@ var init_builtin_policies = __esm(() => {
1502
1575
  }
1503
1576
  }
1504
1577
  },
1578
+ {
1579
+ name: "require-no-conflicts-before-stop",
1580
+ description: "Require the current branch to merge cleanly with the base branch before Claude stops",
1581
+ fn: requireNoConflictsBeforeStop,
1582
+ match: { events: ["Stop"] },
1583
+ defaultEnabled: false,
1584
+ category: "Workflow",
1585
+ params: {
1586
+ baseBranch: {
1587
+ type: "string",
1588
+ description: "Base branch to check for conflicts against (default: main)",
1589
+ default: "main"
1590
+ }
1591
+ }
1592
+ },
1505
1593
  {
1506
1594
  name: "require-ci-green-before-stop",
1507
1595
  description: "Require CI checks to pass on the current branch before Claude stops",
@@ -2065,7 +2153,7 @@ var init_hook_activity_store = __esm(() => {
2065
2153
  });
2066
2154
 
2067
2155
  // package.json
2068
- var version2 = "0.0.6-beta.4";
2156
+ var version2 = "0.0.6-beta.5";
2069
2157
  var init_package = () => {};
2070
2158
 
2071
2159
  // src/posthog-key.ts
@@ -4378,7 +4466,7 @@ import { realpathSync as realpathSync2 } from "fs";
4378
4466
  import { dirname as dirname7, resolve as resolve8 } from "path";
4379
4467
  import { fileURLToPath as fileURLToPath2 } from "url";
4380
4468
  // package.json
4381
- var version = "0.0.6-beta.4";
4469
+ var version = "0.0.6-beta.5";
4382
4470
 
4383
4471
  // bin/failproofai.mjs
4384
4472
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.6-beta.4",
3
+ "version": "0.0.6-beta.5",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -1185,6 +1185,108 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
1185
1185
  }
1186
1186
  }
1187
1187
 
1188
+ function requireNoConflictsBeforeStop(ctx: PolicyContext): PolicyResult {
1189
+ const cwd = ctx.session?.cwd;
1190
+ if (!cwd) return allow("No working directory available, skipping conflict check.");
1191
+
1192
+ const branch = getCurrentBranch(cwd);
1193
+ if (!branch || branch === "HEAD") return allow("Detached HEAD, skipping conflict check.");
1194
+
1195
+ const baseBranch = (ctx.params?.baseBranch as string) ?? "main";
1196
+ if (branch === baseBranch) {
1197
+ return allow(`On base branch "${baseBranch}", skipping conflict check.`);
1198
+ }
1199
+
1200
+ // -- Layer 1: local git merge-tree --
1201
+ let localSkipped = false;
1202
+ try {
1203
+ execFileSync("git", ["rev-parse", "--verify", `origin/${baseBranch}`], {
1204
+ cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3000,
1205
+ });
1206
+
1207
+ const ahead = execFileSync(
1208
+ "git", ["log", `origin/${baseBranch}..HEAD`, "--oneline"],
1209
+ { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 },
1210
+ ).trim();
1211
+
1212
+ if (!ahead) {
1213
+ // Nothing ahead of base — Layer 1 doesn't apply, fall through to Layer 2.
1214
+ localSkipped = true;
1215
+ } else {
1216
+ execFileSync(
1217
+ "git",
1218
+ ["merge-tree", "--write-tree", "--name-only", `origin/${baseBranch}`, "HEAD"],
1219
+ { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 },
1220
+ );
1221
+ // exit 0 → clean merge, fall through to Layer 2
1222
+ }
1223
+ } catch (err) {
1224
+ const e = err as { status?: number; stdout?: string | Buffer };
1225
+ if (e.status === 1) {
1226
+ // git merge-tree exit 1 = conflicts. stdout: <tree>\n<file>\n<file>\n\n<messages>
1227
+ const out = (typeof e.stdout === "string" ? e.stdout : e.stdout?.toString("utf8") ?? "").trim();
1228
+ const lines = out.split("\n");
1229
+ const files: string[] = [];
1230
+ for (let i = 1; i < lines.length; i++) {
1231
+ const line = lines[i];
1232
+ if (line === "") break;
1233
+ files.push(line);
1234
+ }
1235
+ const fileList = files.length ? files.join(", ") : "one or more files";
1236
+ return deny(
1237
+ `Branch "${branch}" has merge conflicts with ${baseBranch} in: ${fileList}. ` +
1238
+ `Rebase or merge origin/${baseBranch} now and resolve the conflicts.`,
1239
+ );
1240
+ }
1241
+ localSkipped = true;
1242
+ }
1243
+
1244
+ // -- Layer 2: GitHub PR mergeability --
1245
+ try {
1246
+ execSync("gh --version", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
1247
+ } catch {
1248
+ return allow(
1249
+ localSkipped
1250
+ ? "Local conflict check skipped and gh CLI not installed, skipping conflict check."
1251
+ : `Branch "${branch}" merges cleanly with ${baseBranch} locally (gh CLI not installed, PR mergeability not verified).`,
1252
+ );
1253
+ }
1254
+
1255
+ let prJson: string;
1256
+ try {
1257
+ prJson = execSync("gh pr view --json mergeable,number,url", {
1258
+ cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 15000,
1259
+ }).trim();
1260
+ } catch {
1261
+ return allow(
1262
+ localSkipped
1263
+ ? "No pull request found for branch, skipping conflict check."
1264
+ : `Branch "${branch}" merges cleanly with ${baseBranch} locally (no PR to verify against).`,
1265
+ );
1266
+ }
1267
+
1268
+ let pr: { mergeable: string; number: number; url: string };
1269
+ try {
1270
+ pr = JSON.parse(prJson);
1271
+ } catch {
1272
+ return allow("Could not parse gh pr view output, skipping PR mergeability check.");
1273
+ }
1274
+
1275
+ if (pr.mergeable === "CONFLICTING") {
1276
+ return deny(
1277
+ `PR #${pr.number} has merge conflicts per GitHub (${pr.url}). ` +
1278
+ `Rebase or merge origin/${baseBranch} now and resolve the conflicts.`,
1279
+ );
1280
+ }
1281
+ if (pr.mergeable === "UNKNOWN") {
1282
+ return deny(
1283
+ `GitHub is still computing mergeability for PR #${pr.number} (${pr.url}). ` +
1284
+ `Wait ~10 seconds, then re-check with \`gh pr view --json mergeable\` before attempting to stop again.`,
1285
+ );
1286
+ }
1287
+ return allow(`PR #${pr.number} merges cleanly per GitHub.`);
1288
+ }
1289
+
1188
1290
  function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
1189
1291
  const cwd = ctx.session?.cwd;
1190
1292
  if (!cwd) return allow("No working directory available, skipping CI check.");
@@ -1590,6 +1692,21 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
1590
1692
  },
1591
1693
  } satisfies PolicyParamsSchema,
1592
1694
  },
1695
+ {
1696
+ name: "require-no-conflicts-before-stop",
1697
+ description: "Require the current branch to merge cleanly with the base branch before Claude stops",
1698
+ fn: requireNoConflictsBeforeStop,
1699
+ match: { events: ["Stop"] },
1700
+ defaultEnabled: false,
1701
+ category: "Workflow",
1702
+ params: {
1703
+ baseBranch: {
1704
+ type: "string",
1705
+ description: "Base branch to check for conflicts against (default: main)",
1706
+ default: "main",
1707
+ },
1708
+ } satisfies PolicyParamsSchema,
1709
+ },
1593
1710
  {
1594
1711
  name: "require-ci-green-before-stop",
1595
1712
  description: "Require CI checks to pass on the current branch before Claude stops",