failproofai 0.0.4 → 0.0.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0v1egkj._.js → [root-of-the-server]__0r6vvp5._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0lty_fo._.js → [root-of-the-server]__0z5dd-f._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/static/chunks/{0p5sfob-upg0g.js → 0.io32u7gjgsb.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0uxpbrcv44lga.js → 04fpsjft~cje..js} +1 -1
- package/.next/standalone/.next/static/chunks/{0y-bi_mp2rv4l.js → 04mtv9jnqknn3.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0tw_xfxb1tto..js → 0h4qcn40dunn7.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0de3q2juhg_dr.js → 0j_ivegn3i5wt.js} +1 -1
- package/.next/standalone/.next/static/chunks/{11zlh73ggln0w.js → 0kvldut6ndzyz.js} +1 -1
- package/.next/standalone/.next/static/chunks/0x8vagatq36_7.js +1 -0
- package/.next/standalone/.next/static/chunks/{0b2_069x5qnxg.js → 0yf_mmdukq6up.js} +2 -2
- package/.next/standalone/CHANGELOG.md +6 -0
- package/.next/standalone/dist/cli.mjs +52 -11
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/hooks/builtin-policies.ts +40 -8
- package/.next/standalone/src/hooks/policy-evaluator.ts +15 -1
- package/dist/cli.mjs +52 -11
- package/package.json +1 -1
- package/src/hooks/builtin-policies.ts +40 -8
- package/src/hooks/policy-evaluator.ts +15 -1
- package/.next/standalone/.next/static/chunks/0xjz3w.yw5tza.js +0 -1
- /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → ICeMHZ-8ZR7HY5GLm7oSJ}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → ICeMHZ-8ZR7HY5GLm7oSJ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → ICeMHZ-8ZR7HY5GLm7oSJ}/_ssgManifest.js +0 -0
|
@@ -9,7 +9,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
|
|
|
9
9
|
const hostname = process.env.HOSTNAME || '0.0.0.0'
|
|
10
10
|
|
|
11
11
|
let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
|
|
12
|
-
const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.
|
|
12
|
+
const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.5-beta.0"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":false,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"partialFallbacks":false,"dynamicOnHover":false,"varyParams":false,"prefetchInlining":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":false,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"distDirRoot":".next"}
|
|
13
13
|
|
|
14
14
|
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
|
|
15
15
|
|
|
@@ -212,6 +212,36 @@ function getThirdPartyCheckRuns(cwd: string, sha: string): CiCheck[] {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
/** Fetch commit statuses (legacy Status API) and normalize to CiCheck format. */
|
|
216
|
+
function getCommitStatuses(cwd: string, sha: string): CiCheck[] {
|
|
217
|
+
try {
|
|
218
|
+
const json = execFileSync(
|
|
219
|
+
"gh",
|
|
220
|
+
[
|
|
221
|
+
"api",
|
|
222
|
+
`repos/{owner}/{repo}/commits/${sha}/statuses`,
|
|
223
|
+
"--jq",
|
|
224
|
+
'map({name: .context, state: .state}) | unique_by(.name)',
|
|
225
|
+
],
|
|
226
|
+
{
|
|
227
|
+
cwd,
|
|
228
|
+
encoding: "utf8",
|
|
229
|
+
timeout: 15000,
|
|
230
|
+
},
|
|
231
|
+
).trim();
|
|
232
|
+
|
|
233
|
+
if (!json || json === "[]") return [];
|
|
234
|
+
const statuses = JSON.parse(json) as Array<{ name: string; state: string }>;
|
|
235
|
+
return statuses.map((s) => ({
|
|
236
|
+
name: s.name,
|
|
237
|
+
status: s.state === "pending" ? "in_progress" : "completed",
|
|
238
|
+
conclusion: s.state === "pending" ? "" : s.state === "success" ? "success" : "failure",
|
|
239
|
+
}));
|
|
240
|
+
} catch {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
215
245
|
/**
|
|
216
246
|
* Check if a command matches an allow pattern using token-by-token comparison.
|
|
217
247
|
* The "*" token is a wildcard. Extra command tokens beyond the pattern are allowed,
|
|
@@ -859,7 +889,7 @@ function requireCommitBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
859
889
|
|
|
860
890
|
if (status.length > 0) {
|
|
861
891
|
return deny(
|
|
862
|
-
"You have uncommitted changes in the working directory. Commit all changes
|
|
892
|
+
"You have uncommitted changes in the working directory. Commit all changes now.",
|
|
863
893
|
);
|
|
864
894
|
}
|
|
865
895
|
return allow("All changes are committed.");
|
|
@@ -937,7 +967,7 @@ function requirePushBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
937
967
|
if (!hasTracking) {
|
|
938
968
|
return deny(
|
|
939
969
|
`Branch "${branch}" has not been pushed to remote "${remote}". ` +
|
|
940
|
-
`
|
|
970
|
+
`Run now: git push -u ${remote} ${branch}`,
|
|
941
971
|
);
|
|
942
972
|
}
|
|
943
973
|
|
|
@@ -952,7 +982,7 @@ function requirePushBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
952
982
|
const commitCount = unpushed.split("\n").length;
|
|
953
983
|
return deny(
|
|
954
984
|
`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` +
|
|
955
|
-
`
|
|
985
|
+
`Run now: git push`,
|
|
956
986
|
);
|
|
957
987
|
}
|
|
958
988
|
|
|
@@ -1024,7 +1054,7 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1024
1054
|
// gh pr view exits non-zero when no PR exists
|
|
1025
1055
|
return deny(
|
|
1026
1056
|
`No pull request found for branch "${branch}". ` +
|
|
1027
|
-
`
|
|
1057
|
+
`Run now: gh pr create`,
|
|
1028
1058
|
);
|
|
1029
1059
|
}
|
|
1030
1060
|
|
|
@@ -1035,7 +1065,7 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1035
1065
|
}
|
|
1036
1066
|
|
|
1037
1067
|
return deny(
|
|
1038
|
-
`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}.
|
|
1068
|
+
`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Run now: gh pr create`,
|
|
1039
1069
|
);
|
|
1040
1070
|
} catch {
|
|
1041
1071
|
return allow("Could not check PR status, skipping.");
|
|
@@ -1075,13 +1105,15 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1075
1105
|
|
|
1076
1106
|
// 2. Third-party check runs (CodeRabbit, SonarCloud, Codecov, etc.)
|
|
1077
1107
|
let thirdPartyChecks: CiCheck[] = [];
|
|
1108
|
+
let commitStatuses: CiCheck[] = [];
|
|
1078
1109
|
const sha = getHeadSha(cwd);
|
|
1079
1110
|
if (sha) {
|
|
1080
1111
|
thirdPartyChecks = getThirdPartyCheckRuns(cwd, sha);
|
|
1112
|
+
commitStatuses = getCommitStatuses(cwd, sha);
|
|
1081
1113
|
}
|
|
1082
1114
|
|
|
1083
1115
|
// 3. Merge all checks
|
|
1084
|
-
const allChecks = [...workflowRuns, ...thirdPartyChecks];
|
|
1116
|
+
const allChecks = [...workflowRuns, ...thirdPartyChecks, ...commitStatuses];
|
|
1085
1117
|
|
|
1086
1118
|
if (allChecks.length === 0) return allow(`No CI runs found for branch "${branch}".`);
|
|
1087
1119
|
|
|
@@ -1091,7 +1123,7 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1091
1123
|
if (failing.length > 0) {
|
|
1092
1124
|
const names = failing.map((r) => `"${r.name}"`).join(", ");
|
|
1093
1125
|
return deny(
|
|
1094
|
-
`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks
|
|
1126
|
+
`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks now.`,
|
|
1095
1127
|
);
|
|
1096
1128
|
}
|
|
1097
1129
|
|
|
@@ -1101,7 +1133,7 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1101
1133
|
if (pending.length > 0) {
|
|
1102
1134
|
const names = pending.map((r) => `"${r.name}"`).join(", ");
|
|
1103
1135
|
return deny(
|
|
1104
|
-
`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete
|
|
1136
|
+
`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete, then verify they pass.`,
|
|
1105
1137
|
);
|
|
1106
1138
|
}
|
|
1107
1139
|
|
|
@@ -130,6 +130,17 @@ export async function evaluatePolicies(
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
if (eventType === "Stop") {
|
|
134
|
+
return {
|
|
135
|
+
exitCode: 2,
|
|
136
|
+
stdout: "",
|
|
137
|
+
stderr: `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`,
|
|
138
|
+
policyName: policy.name,
|
|
139
|
+
reason,
|
|
140
|
+
decision: "deny",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
// Other event types: exit 2
|
|
134
145
|
return {
|
|
135
146
|
exitCode: 2,
|
|
@@ -165,10 +176,13 @@ export async function evaluatePolicies(
|
|
|
165
176
|
if (eventType === "Stop") {
|
|
166
177
|
// Stop hook: exitCode 2 blocks Claude from stopping.
|
|
167
178
|
// Reason goes to stderr so Claude Code receives it as context.
|
|
179
|
+
const policyAttribution = policyNames.length === 1
|
|
180
|
+
? `policy: ${policyNames[0]}`
|
|
181
|
+
: `policies: ${policyNames.join(", ")}`;
|
|
168
182
|
return {
|
|
169
183
|
exitCode: 2,
|
|
170
184
|
stdout: "",
|
|
171
|
-
stderr: combined,
|
|
185
|
+
stderr: `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`,
|
|
172
186
|
policyName: policyNames[0],
|
|
173
187
|
policyNames,
|
|
174
188
|
reason: combined,
|
package/dist/cli.mjs
CHANGED
|
@@ -321,6 +321,30 @@ function getThirdPartyCheckRuns(cwd, sha) {
|
|
|
321
321
|
return [];
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
|
+
function getCommitStatuses(cwd, sha) {
|
|
325
|
+
try {
|
|
326
|
+
const json = execFileSync("gh", [
|
|
327
|
+
"api",
|
|
328
|
+
`repos/{owner}/{repo}/commits/${sha}/statuses`,
|
|
329
|
+
"--jq",
|
|
330
|
+
"map({name: .context, state: .state}) | unique_by(.name)"
|
|
331
|
+
], {
|
|
332
|
+
cwd,
|
|
333
|
+
encoding: "utf8",
|
|
334
|
+
timeout: 15000
|
|
335
|
+
}).trim();
|
|
336
|
+
if (!json || json === "[]")
|
|
337
|
+
return [];
|
|
338
|
+
const statuses = JSON.parse(json);
|
|
339
|
+
return statuses.map((s) => ({
|
|
340
|
+
name: s.name,
|
|
341
|
+
status: s.state === "pending" ? "in_progress" : "completed",
|
|
342
|
+
conclusion: s.state === "pending" ? "" : s.state === "success" ? "success" : "failure"
|
|
343
|
+
}));
|
|
344
|
+
} catch {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
324
348
|
function matchesAllowedPattern(cmd, pattern) {
|
|
325
349
|
const cmdTokens = parseArgvTokens(cmd);
|
|
326
350
|
const patTokens = parseArgvTokens(pattern);
|
|
@@ -830,7 +854,7 @@ function requireCommitBeforeStop(ctx) {
|
|
|
830
854
|
timeout: 5000
|
|
831
855
|
}).trim();
|
|
832
856
|
if (status.length > 0) {
|
|
833
|
-
return deny("You have uncommitted changes in the working directory. Commit all changes
|
|
857
|
+
return deny("You have uncommitted changes in the working directory. Commit all changes now.");
|
|
834
858
|
}
|
|
835
859
|
return allow("All changes are committed.");
|
|
836
860
|
} catch {
|
|
@@ -877,7 +901,7 @@ function requirePushBeforeStop(ctx) {
|
|
|
877
901
|
hasTracking = true;
|
|
878
902
|
} catch {}
|
|
879
903
|
if (!hasTracking) {
|
|
880
|
-
return deny(`Branch "${branch}" has not been pushed to remote "${remote}". ` + `
|
|
904
|
+
return deny(`Branch "${branch}" has not been pushed to remote "${remote}". ` + `Run now: git push -u ${remote} ${branch}`);
|
|
881
905
|
}
|
|
882
906
|
const unpushed = execFileSync("git", ["log", `${remote}/${branch}..HEAD`, "--oneline"], {
|
|
883
907
|
cwd,
|
|
@@ -887,7 +911,7 @@ function requirePushBeforeStop(ctx) {
|
|
|
887
911
|
if (unpushed.length > 0) {
|
|
888
912
|
const commitCount = unpushed.split(`
|
|
889
913
|
`).length;
|
|
890
|
-
return deny(`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` + `
|
|
914
|
+
return deny(`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` + `Run now: git push`);
|
|
891
915
|
}
|
|
892
916
|
return allow(`All commits pushed to "${remote}".`);
|
|
893
917
|
} catch {
|
|
@@ -929,13 +953,13 @@ function requirePrBeforeStop(ctx) {
|
|
|
929
953
|
timeout: 15000
|
|
930
954
|
}).trim();
|
|
931
955
|
} catch {
|
|
932
|
-
return deny(`No pull request found for branch "${branch}". ` + `
|
|
956
|
+
return deny(`No pull request found for branch "${branch}". ` + `Run now: gh pr create`);
|
|
933
957
|
}
|
|
934
958
|
const pr = JSON.parse(prJson);
|
|
935
959
|
if (pr.state === "OPEN") {
|
|
936
960
|
return allow(`PR #${pr.number} exists: ${pr.url}`);
|
|
937
961
|
}
|
|
938
|
-
return deny(`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}.
|
|
962
|
+
return deny(`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Run now: gh pr create`);
|
|
939
963
|
} catch {
|
|
940
964
|
return allow("Could not check PR status, skipping.");
|
|
941
965
|
}
|
|
@@ -961,22 +985,24 @@ function requireCiGreenBeforeStop(ctx) {
|
|
|
961
985
|
}
|
|
962
986
|
} catch {}
|
|
963
987
|
let thirdPartyChecks = [];
|
|
988
|
+
let commitStatuses = [];
|
|
964
989
|
const sha = getHeadSha(cwd);
|
|
965
990
|
if (sha) {
|
|
966
991
|
thirdPartyChecks = getThirdPartyCheckRuns(cwd, sha);
|
|
992
|
+
commitStatuses = getCommitStatuses(cwd, sha);
|
|
967
993
|
}
|
|
968
|
-
const allChecks = [...workflowRuns, ...thirdPartyChecks];
|
|
994
|
+
const allChecks = [...workflowRuns, ...thirdPartyChecks, ...commitStatuses];
|
|
969
995
|
if (allChecks.length === 0)
|
|
970
996
|
return allow(`No CI runs found for branch "${branch}".`);
|
|
971
997
|
const failing = allChecks.filter((r) => r.status === "completed" && r.conclusion !== "success" && r.conclusion !== "skipped");
|
|
972
998
|
if (failing.length > 0) {
|
|
973
999
|
const names = failing.map((r) => `"${r.name}"`).join(", ");
|
|
974
|
-
return deny(`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks
|
|
1000
|
+
return deny(`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks now.`);
|
|
975
1001
|
}
|
|
976
1002
|
const pending = allChecks.filter((r) => r.status === "in_progress" || r.status === "queued" || r.status === "waiting");
|
|
977
1003
|
if (pending.length > 0) {
|
|
978
1004
|
const names = pending.map((r) => `"${r.name}"`).join(", ");
|
|
979
|
-
return deny(`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete
|
|
1005
|
+
return deny(`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete, then verify they pass.`);
|
|
980
1006
|
}
|
|
981
1007
|
return allow(`All CI checks passed on branch "${branch}".`);
|
|
982
1008
|
} catch {
|
|
@@ -1457,6 +1483,18 @@ async function evaluatePolicies(eventType, payload, session, config) {
|
|
|
1457
1483
|
decision: "deny"
|
|
1458
1484
|
};
|
|
1459
1485
|
}
|
|
1486
|
+
if (eventType === "Stop") {
|
|
1487
|
+
return {
|
|
1488
|
+
exitCode: 2,
|
|
1489
|
+
stdout: "",
|
|
1490
|
+
stderr: `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}
|
|
1491
|
+
|
|
1492
|
+
You MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`,
|
|
1493
|
+
policyName: policy.name,
|
|
1494
|
+
reason,
|
|
1495
|
+
decision: "deny"
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1460
1498
|
return {
|
|
1461
1499
|
exitCode: 2,
|
|
1462
1500
|
stdout: "",
|
|
@@ -1480,10 +1518,13 @@ async function evaluatePolicies(eventType, payload, session, config) {
|
|
|
1480
1518
|
`);
|
|
1481
1519
|
const policyNames = instructEntries.map((e) => e.policyName);
|
|
1482
1520
|
if (eventType === "Stop") {
|
|
1521
|
+
const policyAttribution = policyNames.length === 1 ? `policy: ${policyNames[0]}` : `policies: ${policyNames.join(", ")}`;
|
|
1483
1522
|
return {
|
|
1484
1523
|
exitCode: 2,
|
|
1485
1524
|
stdout: "",
|
|
1486
|
-
stderr: combined
|
|
1525
|
+
stderr: `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}
|
|
1526
|
+
|
|
1527
|
+
You MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`,
|
|
1487
1528
|
policyName: policyNames[0],
|
|
1488
1529
|
policyNames,
|
|
1489
1530
|
reason: combined,
|
|
@@ -1910,7 +1951,7 @@ var init_hook_activity_store = __esm(() => {
|
|
|
1910
1951
|
});
|
|
1911
1952
|
|
|
1912
1953
|
// package.json
|
|
1913
|
-
var version2 = "0.0.
|
|
1954
|
+
var version2 = "0.0.5-beta.0";
|
|
1914
1955
|
var init_package = () => {};
|
|
1915
1956
|
|
|
1916
1957
|
// src/posthog-key.ts
|
|
@@ -3202,7 +3243,7 @@ import { realpathSync as realpathSync2 } from "fs";
|
|
|
3202
3243
|
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
3203
3244
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3204
3245
|
// package.json
|
|
3205
|
-
var version = "0.0.
|
|
3246
|
+
var version = "0.0.5-beta.0";
|
|
3206
3247
|
|
|
3207
3248
|
// bin/failproofai.mjs
|
|
3208
3249
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5-beta.0",
|
|
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"
|
|
@@ -212,6 +212,36 @@ function getThirdPartyCheckRuns(cwd: string, sha: string): CiCheck[] {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
/** Fetch commit statuses (legacy Status API) and normalize to CiCheck format. */
|
|
216
|
+
function getCommitStatuses(cwd: string, sha: string): CiCheck[] {
|
|
217
|
+
try {
|
|
218
|
+
const json = execFileSync(
|
|
219
|
+
"gh",
|
|
220
|
+
[
|
|
221
|
+
"api",
|
|
222
|
+
`repos/{owner}/{repo}/commits/${sha}/statuses`,
|
|
223
|
+
"--jq",
|
|
224
|
+
'map({name: .context, state: .state}) | unique_by(.name)',
|
|
225
|
+
],
|
|
226
|
+
{
|
|
227
|
+
cwd,
|
|
228
|
+
encoding: "utf8",
|
|
229
|
+
timeout: 15000,
|
|
230
|
+
},
|
|
231
|
+
).trim();
|
|
232
|
+
|
|
233
|
+
if (!json || json === "[]") return [];
|
|
234
|
+
const statuses = JSON.parse(json) as Array<{ name: string; state: string }>;
|
|
235
|
+
return statuses.map((s) => ({
|
|
236
|
+
name: s.name,
|
|
237
|
+
status: s.state === "pending" ? "in_progress" : "completed",
|
|
238
|
+
conclusion: s.state === "pending" ? "" : s.state === "success" ? "success" : "failure",
|
|
239
|
+
}));
|
|
240
|
+
} catch {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
215
245
|
/**
|
|
216
246
|
* Check if a command matches an allow pattern using token-by-token comparison.
|
|
217
247
|
* The "*" token is a wildcard. Extra command tokens beyond the pattern are allowed,
|
|
@@ -859,7 +889,7 @@ function requireCommitBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
859
889
|
|
|
860
890
|
if (status.length > 0) {
|
|
861
891
|
return deny(
|
|
862
|
-
"You have uncommitted changes in the working directory. Commit all changes
|
|
892
|
+
"You have uncommitted changes in the working directory. Commit all changes now.",
|
|
863
893
|
);
|
|
864
894
|
}
|
|
865
895
|
return allow("All changes are committed.");
|
|
@@ -937,7 +967,7 @@ function requirePushBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
937
967
|
if (!hasTracking) {
|
|
938
968
|
return deny(
|
|
939
969
|
`Branch "${branch}" has not been pushed to remote "${remote}". ` +
|
|
940
|
-
`
|
|
970
|
+
`Run now: git push -u ${remote} ${branch}`,
|
|
941
971
|
);
|
|
942
972
|
}
|
|
943
973
|
|
|
@@ -952,7 +982,7 @@ function requirePushBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
952
982
|
const commitCount = unpushed.split("\n").length;
|
|
953
983
|
return deny(
|
|
954
984
|
`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` +
|
|
955
|
-
`
|
|
985
|
+
`Run now: git push`,
|
|
956
986
|
);
|
|
957
987
|
}
|
|
958
988
|
|
|
@@ -1024,7 +1054,7 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1024
1054
|
// gh pr view exits non-zero when no PR exists
|
|
1025
1055
|
return deny(
|
|
1026
1056
|
`No pull request found for branch "${branch}". ` +
|
|
1027
|
-
`
|
|
1057
|
+
`Run now: gh pr create`,
|
|
1028
1058
|
);
|
|
1029
1059
|
}
|
|
1030
1060
|
|
|
@@ -1035,7 +1065,7 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1035
1065
|
}
|
|
1036
1066
|
|
|
1037
1067
|
return deny(
|
|
1038
|
-
`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}.
|
|
1068
|
+
`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Run now: gh pr create`,
|
|
1039
1069
|
);
|
|
1040
1070
|
} catch {
|
|
1041
1071
|
return allow("Could not check PR status, skipping.");
|
|
@@ -1075,13 +1105,15 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1075
1105
|
|
|
1076
1106
|
// 2. Third-party check runs (CodeRabbit, SonarCloud, Codecov, etc.)
|
|
1077
1107
|
let thirdPartyChecks: CiCheck[] = [];
|
|
1108
|
+
let commitStatuses: CiCheck[] = [];
|
|
1078
1109
|
const sha = getHeadSha(cwd);
|
|
1079
1110
|
if (sha) {
|
|
1080
1111
|
thirdPartyChecks = getThirdPartyCheckRuns(cwd, sha);
|
|
1112
|
+
commitStatuses = getCommitStatuses(cwd, sha);
|
|
1081
1113
|
}
|
|
1082
1114
|
|
|
1083
1115
|
// 3. Merge all checks
|
|
1084
|
-
const allChecks = [...workflowRuns, ...thirdPartyChecks];
|
|
1116
|
+
const allChecks = [...workflowRuns, ...thirdPartyChecks, ...commitStatuses];
|
|
1085
1117
|
|
|
1086
1118
|
if (allChecks.length === 0) return allow(`No CI runs found for branch "${branch}".`);
|
|
1087
1119
|
|
|
@@ -1091,7 +1123,7 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1091
1123
|
if (failing.length > 0) {
|
|
1092
1124
|
const names = failing.map((r) => `"${r.name}"`).join(", ");
|
|
1093
1125
|
return deny(
|
|
1094
|
-
`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks
|
|
1126
|
+
`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks now.`,
|
|
1095
1127
|
);
|
|
1096
1128
|
}
|
|
1097
1129
|
|
|
@@ -1101,7 +1133,7 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
1101
1133
|
if (pending.length > 0) {
|
|
1102
1134
|
const names = pending.map((r) => `"${r.name}"`).join(", ");
|
|
1103
1135
|
return deny(
|
|
1104
|
-
`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete
|
|
1136
|
+
`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete, then verify they pass.`,
|
|
1105
1137
|
);
|
|
1106
1138
|
}
|
|
1107
1139
|
|
|
@@ -130,6 +130,17 @@ export async function evaluatePolicies(
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
if (eventType === "Stop") {
|
|
134
|
+
return {
|
|
135
|
+
exitCode: 2,
|
|
136
|
+
stdout: "",
|
|
137
|
+
stderr: `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`,
|
|
138
|
+
policyName: policy.name,
|
|
139
|
+
reason,
|
|
140
|
+
decision: "deny",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
// Other event types: exit 2
|
|
134
145
|
return {
|
|
135
146
|
exitCode: 2,
|
|
@@ -165,10 +176,13 @@ export async function evaluatePolicies(
|
|
|
165
176
|
if (eventType === "Stop") {
|
|
166
177
|
// Stop hook: exitCode 2 blocks Claude from stopping.
|
|
167
178
|
// Reason goes to stderr so Claude Code receives it as context.
|
|
179
|
+
const policyAttribution = policyNames.length === 1
|
|
180
|
+
? `policy: ${policyNames[0]}`
|
|
181
|
+
: `policies: ${policyNames.join(", ")}`;
|
|
168
182
|
return {
|
|
169
183
|
exitCode: 2,
|
|
170
184
|
stdout: "",
|
|
171
|
-
stderr: combined,
|
|
185
|
+
stderr: `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`,
|
|
172
186
|
policyName: policyNames[0],
|
|
173
187
|
policyNames,
|
|
174
188
|
reason: combined,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,53348,e=>{"use strict";var r=e.i(43476),t=e.i(71645),n=e.i(5888),i=e.i(9969);e.s(["default",0,function({error:e,reset:o}){return(0,t.useEffect)(()=>{(0,n.getTelemetryConfig)().then(r=>{(0,i.setClientTelemetryConfig)(r),(0,i.captureClientEvent)("client_error",{error_message:e.message,error_name:e.name,error_digest:e.digest,boundary:"global"})}).catch(()=>{})},[e]),(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,r.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,r.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,r.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:e.message||"An unexpected error occurred."}),(0,r.jsx)("button",{onClick:o,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])}]);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|