failproofai 0.0.10-beta.9 → 0.0.10

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 (116) 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/download/[project]/[session]/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/index.html +1 -1
  29. package/.next/standalone/.next/server/app/index.rsc +15 -15
  30. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  32. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  33. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  34. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  35. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  36. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  38. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  39. package/.next/standalone/.next/server/app/policies/page.js +1 -1
  40. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  43. package/.next/standalone/.next/server/app/project/[name]/page.js +1 -1
  44. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  47. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0fjhqi9._.js → [root-of-the-server]__044xt9.._.js} +2 -2
  55. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_ob4n._.js +1 -1
  56. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vlhtkc._.js +1 -1
  57. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yfq1yr._.js +1 -1
  58. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  59. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07_-mkc._.js +3 -0
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e9o9ri._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0mnba92._.js → [root-of-the-server]__0l6swv1._.js} +2 -2
  62. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0logebz._.js +3 -0
  63. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mi5ejy._.js +2 -2
  64. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0podumr._.js +2 -2
  65. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rkxer-._.js +3 -0
  66. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rl2kwi._.js +2 -2
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vg0uey._.js +2 -2
  68. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ye1w50._.js +4 -0
  69. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ymlddl._.js +32 -7
  70. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__10._f0s._.js +2 -2
  71. package/.next/standalone/.next/server/chunks/ssr/app_0cdqd9w._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +2 -2
  74. package/.next/standalone/.next/server/chunks/ssr/lib_gemini-projects_ts_0sl~yqr._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/ssr/lib_opencode-projects_ts_0op9gyp._.js +1 -1
  76. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  77. package/.next/standalone/.next/server/pages/404.html +1 -1
  78. package/.next/standalone/.next/server/pages/500.html +1 -1
  79. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  81. package/.next/standalone/.next/static/chunks/{0.a7kxwvbre7q.js → 0j171xiqge4rv.js} +1 -1
  82. package/.next/standalone/.next/static/chunks/0kqar56yl~41o.js +6 -0
  83. package/.next/standalone/.next/static/chunks/{0bndjr3n70vjn.js → 0lt8ko3lw.5yt.js} +1 -1
  84. package/.next/standalone/.next/static/chunks/{0el2m08z2u5jm.js → 0ml1.ck_5t36i.js} +1 -1
  85. package/.next/standalone/.next/static/chunks/{11zd_~~dqyu39.js → 0pkl..xgo-qox.js} +1 -1
  86. package/.next/standalone/.next/static/chunks/12l2t63hkyo2q.js +1 -0
  87. package/.next/standalone/.next/static/chunks/{0-17za-4x~4gj.js → 14lii11wmo450.js} +1 -1
  88. package/.next/standalone/.next/static/chunks/{05i2pvqx75mg1.js → 179yytvmam0ug.js} +1 -1
  89. package/.next/standalone/.next/static/chunks/17rm86uz2nd5a.css +2 -0
  90. package/.next/standalone/.opencode/plugins/failproofai.mjs +75 -15
  91. package/.next/standalone/app/actions/get-hooks-config.ts +25 -1
  92. package/.next/standalone/app/policies/hooks-client.tsx +228 -44
  93. package/.next/standalone/lib/gemini-projects.ts +64 -24
  94. package/.next/standalone/lib/opencode-projects.ts +9 -7
  95. package/.next/standalone/package.json +2 -2
  96. package/.next/standalone/pi-extension/index.ts +113 -12
  97. package/.next/standalone/server.js +1 -1
  98. package/dist/cli.mjs +193 -68
  99. package/lib/gemini-projects.ts +64 -24
  100. package/lib/opencode-projects.ts +9 -7
  101. package/package.json +2 -2
  102. package/pi-extension/index.ts +113 -12
  103. package/scripts/launch.ts +5 -29
  104. package/src/hooks/handler.ts +63 -6
  105. package/src/hooks/integrations.ts +31 -6
  106. package/src/hooks/policy-evaluator.ts +34 -2
  107. package/src/hooks/types.ts +52 -0
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0.2-_y.._.js +0 -3
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okpm5k._.js +0 -3
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zwce~o._.js +0 -4
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0~tyclf._.js +0 -3
  112. package/.next/standalone/.next/static/chunks/0xd2k83e47~cx.js +0 -6
  113. package/.next/standalone/.next/static/chunks/0ywdtmk13p9_i.css +0 -2
  114. /package/.next/standalone/.next/static/{rOMtBtMm3y5vK0uC-Nzvi → dAuQps6jUwCz9X1Q5FFOO}/_buildManifest.js +0 -0
  115. /package/.next/standalone/.next/static/{rOMtBtMm3y5vK0uC-Nzvi → dAuQps6jUwCz9X1Q5FFOO}/_clientMiddlewareManifest.js +0 -0
  116. /package/.next/standalone/.next/static/{rOMtBtMm3y5vK0uC-Nzvi → dAuQps6jUwCz9X1Q5FFOO}/_ssgManifest.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.10-beta.9",
3
+ "version": "0.0.10",
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"
@@ -71,6 +71,7 @@
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
+ "@anthropic-ai/sdk": "^0.93.0",
74
75
  "@tailwindcss/postcss": "^4.1.18",
75
76
  "@tanstack/react-virtual": "^3.13.18",
76
77
  "@testing-library/jest-dom": "^6.9.1",
@@ -91,7 +92,6 @@
91
92
  "tailwind-merge": "^3.4.0",
92
93
  "tailwindcss": "^4.1.18",
93
94
  "typescript": "^6.0.2",
94
- "@anthropic-ai/sdk": "^0.93.0",
95
95
  "vitest": "^4.0.18"
96
96
  },
97
97
  "dependencies": {
@@ -129,6 +129,36 @@ function canonicalizeToolName(piToolName: string | undefined): string | undefine
129
129
  return PI_TOOL_MAP[piToolName] ?? piToolName;
130
130
  }
131
131
 
132
+ /**
133
+ * Per-tool input-key translation. Pi's Read / Write / Edit tools deliver
134
+ * `path` (not `file_path`); failproofai's `block-env-files` and
135
+ * `block-secrets-write` builtins only read `file_path`, so without this map
136
+ * they silently no-op on Pi. `block-read-outside-cwd` already has a `path`
137
+ * fallback so it works either way. Pi's Edit tool nests `edits[{oldText,
138
+ * newText}]` which doesn't translate flatly to Claude's `{old_string,
139
+ * new_string}` — we only map the top-level `path`; the nested array stays
140
+ * Pi-shape (no current builtin reads it).
141
+ *
142
+ * Keep in sync with PI_TOOL_INPUT_MAP in src/hooks/types.ts.
143
+ */
144
+ const PI_TOOL_INPUT_MAP: Record<string, Record<string, string>> = {
145
+ Read: { path: "file_path" },
146
+ Write: { path: "file_path" },
147
+ Edit: { path: "file_path" },
148
+ };
149
+
150
+ function canonicalizeToolInput(
151
+ canonicalToolName: string | undefined,
152
+ args: Record<string, unknown> | undefined,
153
+ ): Record<string, unknown> | undefined {
154
+ if (!args || typeof args !== "object" || !canonicalToolName) return args;
155
+ const map = PI_TOOL_INPUT_MAP[canonicalToolName];
156
+ if (!map) return args;
157
+ const out: Record<string, unknown> = {};
158
+ for (const k of Object.keys(args)) out[map[k] ?? k] = args[k];
159
+ return out;
160
+ }
161
+
132
162
  /** Resolve the cwd for the policy payload. Pi events don't include cwd, so
133
163
  * fall back to the extension's process.cwd() — which is where Pi was
134
164
  * launched and where `.failproofai/` config lives. */
@@ -201,6 +231,24 @@ function discoverPiSessionId(cwd: string): string | undefined {
201
231
  * across multiple workspace roots) can't cross-attribute. Cleared on
202
232
  * session_shutdown reasons `new`/`resume`/`fork` (Pi reuses the process). */
203
233
  const cachedSessionIdByCwd = new Map<string, string>();
234
+
235
+ /** Pending Stop-policy deny reason from agent_end, keyed by sessionId.
236
+ * Drained by before_agent_start on the next user turn in the same Pi
237
+ * process. Cleared on every session_shutdown.
238
+ *
239
+ * Why this exists: Pi's agent_end has no Result type — the agent loop
240
+ * has already exited when it fires, so a deny return cannot keep Pi
241
+ * running the way Claude's exit-2-from-Stop does. The closest analog
242
+ * is to capture the deny here and re-inject it as a MANDATORY ACTION
243
+ * system-prompt addition on the NEXT before_agent_start, which fires
244
+ * after the user submits a prompt but before the agent loop runs.
245
+ * Best-effort: bounded by the Pi process lifetime — same bound Claude
246
+ * has on exit-2-from-Stop (kill the agent and the gate is missed).
247
+ *
248
+ * Why per-session not per-cwd: a Pi process can host multiple sessions
249
+ * via /resume and /fork; per-cwd would cross-attribute a stale block
250
+ * from a prior session into a fresh one. */
251
+ const pendingStopBlockBySession = new Map<string, string>();
204
252
  function resolveSessionId(eventSessionId: string | undefined, cwd: string): string | undefined {
205
253
  if (eventSessionId) {
206
254
  cachedSessionIdByCwd.set(cwd, eventSessionId);
@@ -272,6 +320,17 @@ interface PiAgentEndEvent {
272
320
  sessionId?: string;
273
321
  }
274
322
 
323
+ /** Pi v0.73.x before_agent_start event payload. Fires once per turn,
324
+ * after the user submits a prompt but before the agent loop runs. */
325
+ interface PiBeforeAgentStartEvent {
326
+ type?: string;
327
+ prompt?: string;
328
+ /** The fully assembled system prompt for this turn — we append to it. */
329
+ systemPrompt?: string;
330
+ cwd?: string;
331
+ sessionId?: string;
332
+ }
333
+
275
334
  interface PiExtensionApi {
276
335
  on(event: string, handler: (event: unknown) => unknown): void;
277
336
  }
@@ -280,9 +339,10 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
280
339
  // tool_call → PreToolUse. Block tool execution when failproofai denies.
281
340
  pi.on("tool_call", (event: unknown): unknown => {
282
341
  const e = event as PiToolCallEvent;
342
+ const canonicalTool = canonicalizeToolName(e.toolName);
283
343
  const decision = callPolicy("tool_call", {
284
- tool_name: canonicalizeToolName(e.toolName),
285
- tool_input: e.input,
344
+ tool_name: canonicalTool,
345
+ tool_input: canonicalizeToolInput(canonicalTool, e.input),
286
346
  session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
287
347
  cwd: resolveCwd(e.cwd),
288
348
  hook_event_name: "PreToolUse",
@@ -341,9 +401,10 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
341
401
  // the activity store + stderr — but Pi keeps the original tool result.
342
402
  pi.on("tool_result", (event: unknown): unknown => {
343
403
  const e = event as PiToolResultEvent;
404
+ const canonicalTool = canonicalizeToolName(e.toolName);
344
405
  callPolicy("tool_result", {
345
- tool_name: canonicalizeToolName(e.toolName),
346
- tool_input: e.input ?? {},
406
+ tool_name: canonicalTool,
407
+ tool_input: canonicalizeToolInput(canonicalTool, e.input) ?? {},
347
408
  tool_response: { content: e.content, isError: e.isError },
348
409
  session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
349
410
  cwd: resolveCwd(e.cwd),
@@ -352,21 +413,51 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
352
413
  return undefined;
353
414
  });
354
415
 
355
- // agent_end → Stop. Observation-only on Pi: the agent loop has already
356
- // exited when this fires, so a deny decision cannot keep Pi running the
357
- // way Claude's exit-2-from-Stop can. We still forward so the 5
358
- // require-*-before-stop builtins run and log their findings (visible in
359
- // the dashboard's activity feed and stderr) best-effort visibility.
416
+ // agent_end → Stop. Pi cannot veto agent_end (the agent loop has already
417
+ // exited when this fires see the AgentEndEvent typedef in pi-coding-agent
418
+ // which has NO Result type). Instead we capture any deny reason and stash
419
+ // it keyed by sessionId for the next before_agent_start handler to drain.
420
+ // The 5 require-*-before-stop builtins thus enforce by gating the NEXT
421
+ // user turn's system prompt rather than by retrying the same loop. If the
422
+ // user kills Pi between turns, the gate is missed — same bound Claude has.
360
423
  pi.on("agent_end", (event: unknown): unknown => {
361
424
  const e = event as PiAgentEndEvent;
362
- callPolicy("agent_end", {
363
- session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
364
- cwd: resolveCwd(e.cwd),
425
+ const cwd = resolveCwd(e.cwd);
426
+ const sessionId = resolveSessionId(e.sessionId, cwd);
427
+ const decision = callPolicy("agent_end", {
428
+ session_id: sessionId,
429
+ cwd,
365
430
  hook_event_name: "Stop",
366
431
  });
432
+ if (decision.block && decision.reason && sessionId) {
433
+ pendingStopBlockBySession.set(sessionId, decision.reason);
434
+ debug(`agent_end deny stored for session=${sessionId}`);
435
+ }
367
436
  return undefined;
368
437
  });
369
438
 
439
+ // before_agent_start → drain any pending Stop-policy deny captured at
440
+ // agent_end. This is Pi's only first-class channel to influence the next
441
+ // turn before the LLM call: the result type accepts a `systemPrompt`
442
+ // replacement (chained across extensions) and an optional injected
443
+ // CustomMessage. We only return systemPrompt — sufficient for the LLM to
444
+ // see the MANDATORY ACTION directive immediately, and avoids polluting the
445
+ // visible conversation history with framework chrome. The reason text
446
+ // already carries the policy-attributed MANDATORY ACTION wording from
447
+ // policy-evaluator's Pi-Stop branch.
448
+ pi.on("before_agent_start", (event: unknown): unknown => {
449
+ const e = event as PiBeforeAgentStartEvent;
450
+ const cwd = resolveCwd(e.cwd);
451
+ const sessionId = resolveSessionId(e.sessionId, cwd);
452
+ if (!sessionId) return undefined;
453
+ const pending = pendingStopBlockBySession.get(sessionId);
454
+ if (!pending) return undefined;
455
+ pendingStopBlockBySession.delete(sessionId);
456
+ debug(`before_agent_start drains stop-block for session=${sessionId}`);
457
+ const base = e.systemPrompt ?? "";
458
+ return { systemPrompt: `${base}\n\n${pending}` };
459
+ });
460
+
370
461
  // session_shutdown → SessionEnd. Observation-only; emits a SessionEnd
371
462
  // record so per-session telemetry has a clean close. Reset the per-cwd
372
463
  // sessionId cache for shutdown reasons that mean "Pi is starting a new
@@ -382,9 +473,19 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
382
473
  reason: e.reason,
383
474
  hook_event_name: "SessionEnd",
384
475
  });
476
+ // Capture sessionId BEFORE the cache reset so we delete the pending
477
+ // entry under the just-ending session's id. After resetSessionIdCache,
478
+ // a subsequent resolveSessionId would re-discover from disk and could
479
+ // bind to a different (stale) file — wrong key for the cleanup below.
480
+ const sessionId = resolveSessionId(e.sessionId, cwd);
385
481
  if (e.reason === "new" || e.reason === "resume" || e.reason === "fork") {
386
482
  resetSessionIdCache(cwd);
387
483
  }
484
+ // Drop any pending Stop-policy deny for this session on every shutdown
485
+ // reason — `quit` ends the session for good (don't leak the entry into
486
+ // GC); `new`/`resume`/`fork` start a different session in the same
487
+ // process and must not inherit the prior session's gate.
488
+ if (sessionId) pendingStopBlockBySession.delete(sessionId);
388
489
  return undefined;
389
490
  });
390
491
  }
package/scripts/launch.ts CHANGED
@@ -12,35 +12,11 @@ import { version } from "../package.json";
12
12
  export function launch(mode: "dev" | "start"): void {
13
13
  const { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs } = parseScriptArgs(process.argv.slice(2));
14
14
 
15
- // Hand-crafted pixel-block wordmark mirroring the hosted PNG logo at
16
- // https://d2wq11aau0arks.cloudfront.net/failproof/logo-wordmark.png
17
- // chunky lowercase "failproof ai" compressed with Unicode 2x2 quadrant
18
- // block characters (▖▗▘▙▚▛▜▝▞▟ + ▀ ▄ █ ▌ ▐) and then horizontally
19
- // scaled 4:3 (every 4th source-pixel column dropped) so the full
20
- // wordmark fits in ~75 cols × ~10 rows — clean on any standard ≥80-col
21
- // terminal.
22
- const bannerLines = [
23
- " ███ ▐███ ▐█",
24
- " ▐█▛▀▀ ▟█▖ ▟█▛▀▀ ▝▀",
25
- " ██████ ▗████▌ ▝██▛ ██ ███ ▐██▙ ▗███ ▐██▙ ▗█████▙ ████▌ ▐█",
26
- " ▀▜█▛▀▀ ▝▀▀▀▀█▙ ▄▙ ██ ▗▟▀▀▜▄▖ ▄▟▀▀▘ ▄▟▛▀▜▙▖ ▄█▀▀█▄▖▝▀██▛▀▀ ▀▀▀▀▙▄ ▐█",
27
- " ▐█▌ ▗██████ ███ ██ ▐█ ▐█▌ ██ ██▌ ▐█▌ ██ ██▌ ██▌ ██████ ▐█",
28
- " ▐█▌ ▐█▛▀▀██ ██▀ ██ ▐█ ▐█▌ ██ ██▌ ▐█▌ ██ ██▌ ██▌ █▛▀▀██ ▐█",
29
- " ▐█▌ ▝▀█████ ██▄▄██ ▐████▀▘ ██ ▀▜███▀▘ ▀▜███▀▘ ██▌ ▀█████ ▐█",
30
- " ▝▀▘ ▀▀▀▀▀ ▀▀▀▀▀▀ ▐█▀▀▀ ▀▀ ▝▀▀▀ ▝▀▀▀ ▀▀▘ ▀▀▀▀▀ ▝▀",
31
- " ▐█",
32
- " ▝▀",
33
- ];
34
- // Fall back to plain text on narrow terminals so the wide pixel-block art
35
- // doesn't wrap and shred itself. process.stdout.columns is undefined when
36
- // stdout isn't a TTY (piped, captured, redirected to a file), in which case
37
- // there's no width to compare against and we keep the full art as-is.
38
- const bannerWidth = bannerLines.reduce((w, l) => Math.max(w, l.length), 0);
39
- const cols = process.stdout.columns;
40
- const banner = cols !== undefined && cols < bannerWidth
41
- ? " failproof ai"
42
- : bannerLines.join("\n");
43
- console.log(`\n${banner}\n\n v${version}\n`);
15
+ // Plain-text title + a labeled `Version` line that lines up with the
16
+ // `Star us` / `Docs` / `Slack` lines below (all four labels pad to the
17
+ // same column so the values form a clean right-hand column).
18
+ console.log(`\n failproof ai\n`);
19
+ console.log(` 📦 Version: ${version}`);
44
20
  console.log(` ⭐ Star us: https://github.com/exospherehost/failproofai`);
45
21
  console.log(` 📖 Docs: https://befailproof.ai`);
46
22
  console.log(` 💬 Slack: https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ\n`);
@@ -23,6 +23,10 @@ import {
23
23
  COPILOT_TOOL_MAP,
24
24
  CURSOR_TOOL_MAP,
25
25
  CODEX_TOOL_MAP,
26
+ OPENCODE_TOOL_MAP,
27
+ OPENCODE_TOOL_INPUT_MAP,
28
+ PI_TOOL_MAP,
29
+ PI_TOOL_INPUT_MAP,
26
30
  } from "./types";
27
31
  import type { PolicyFunction, PolicyResult } from "./policy-types";
28
32
  import { readMergedHooksConfig } from "./hooks-config";
@@ -87,9 +91,13 @@ function canonicalizeEventType(raw: string, cli: IntegrationType): HookEventType
87
91
  * • Cursor: PascalCase per Cursor docs but uses `Shell` for the bash-
88
92
  * equivalent — CURSOR_TOOL_MAP rewrites `Shell → Bash`; other
89
93
  * tool names already canonical and pass through
90
- * • OpenCode: handled in the OpenCode plugin shim (in-process,
91
- * self-contained) before the JSON crosses to this binary
92
- * Pi: handled in the Pi extension shim (same)
94
+ * • OpenCode: lowercase IDs (`bash`, `read`, …) OPENCODE_TOOL_MAP. The
95
+ * OpenCode plugin shim ALSO canonicalizes inline as defense-in-
96
+ * depth; both passes are idempotent. Handler-side coverage
97
+ * here means a stale user-scope shim that pre-dates #337 still
98
+ * gets the canonicalization, without forcing a re-install.
99
+ * • Pi: lowercase IDs (`bash`, `read`, …) — PI_TOOL_MAP. Same dual-
100
+ * canonicalization story as OpenCode (shim + handler).
93
101
  * • Gemini: snake_case — GEMINI_TOOL_MAP
94
102
  *
95
103
  * Unknown tool names (MCP `mcp_*`, third-party extensions, Skills) pass
@@ -101,9 +109,48 @@ function canonicalizeToolName(raw: string | undefined, cli: IntegrationType): st
101
109
  if (cli === "cursor") return CURSOR_TOOL_MAP[raw] ?? raw;
102
110
  if (cli === "codex") return CODEX_TOOL_MAP[raw] ?? raw;
103
111
  if (cli === "gemini") return GEMINI_TOOL_MAP[raw] ?? raw;
112
+ if (cli === "opencode") return OPENCODE_TOOL_MAP[raw] ?? raw;
113
+ if (cli === "pi") return PI_TOOL_MAP[raw] ?? raw;
104
114
  return raw;
105
115
  }
106
116
 
117
+ /**
118
+ * Canonicalize per-CLI tool-input keys to the snake_case shape that builtin
119
+ * policies read (e.g. `file_path`, `old_string`). OpenCode delivers args as
120
+ * camelCase (`filePath`, `oldString`, `newString`, `replaceAll`); Pi delivers
121
+ * `path` for Read/Write/Edit. Without translation, `getFilePath()` reads "" and
122
+ * the path-checking builtins (`block-read-outside-cwd`, `block-env-files`,
123
+ * `block-secrets-write`) silently no-op.
124
+ *
125
+ * Both CLIs' shims canonicalize inline before the JSON crosses to this binary.
126
+ * Handler-side coverage here is defense-in-depth: a user-scope shim that pre-
127
+ * dates #337 still passes the raw camelCase keys, and we want those installs
128
+ * to start enforcing the moment failproofai upgrades — without requiring a
129
+ * `failproofai policies --install --cli opencode` re-run.
130
+ *
131
+ * Idempotent: when the shim already canonicalized, the keys are snake_case
132
+ * and the per-tool map's camelCase keys don't match, so the loop is a no-op.
133
+ *
134
+ * Tools outside the per-CLI map (MCP `mcp_*`, third-party extensions) pass
135
+ * through unchanged so their schemas aren't corrupted.
136
+ */
137
+ function canonicalizeToolInput(
138
+ toolName: string | undefined,
139
+ rawInput: unknown,
140
+ cli: IntegrationType,
141
+ ): unknown {
142
+ if (!toolName || !rawInput || typeof rawInput !== "object") return rawInput;
143
+ let perToolMap: Record<string, string> | undefined;
144
+ if (cli === "opencode") perToolMap = OPENCODE_TOOL_INPUT_MAP[toolName];
145
+ else if (cli === "pi") perToolMap = PI_TOOL_INPUT_MAP[toolName];
146
+ if (!perToolMap) return rawInput;
147
+ const out: Record<string, unknown> = {};
148
+ for (const [k, v] of Object.entries(rawInput as Record<string, unknown>)) {
149
+ out[perToolMap[k] ?? k] = v;
150
+ }
151
+ return out;
152
+ }
153
+
107
154
  export async function handleHookEvent(
108
155
  eventType: string,
109
156
  cli: IntegrationType = "claude",
@@ -151,15 +198,25 @@ export async function handleHookEvent(
151
198
 
152
199
  // Canonicalize tool name in place so both the policy-registry tool-name
153
200
  // filter and policy bodies (`ctx.toolName === "Bash"`) see the canonical
154
- // form. Today only Gemini's snake_case names need translation; other CLIs
155
- // are no-ops here. Mutating `parsed.tool_name` keeps the activity store +
156
- // telemetry tagging consistent (they read from `parsed.tool_name`).
201
+ // form. Mutating `parsed.tool_name` keeps the activity store + telemetry
202
+ // tagging consistent (they read from `parsed.tool_name`).
157
203
  const rawToolName = parsed.tool_name as string | undefined;
158
204
  const canonicalToolName = canonicalizeToolName(rawToolName, cli);
159
205
  if (canonicalToolName !== rawToolName) {
160
206
  parsed.tool_name = canonicalToolName;
161
207
  }
162
208
 
209
+ // Canonicalize tool-input keys for OpenCode + Pi (no-op for other CLIs).
210
+ // Defense-in-depth against stale shims that still pass camelCase /
211
+ // Pi-shape keys to the binary. The per-CLI shim ALSO canonicalizes; both
212
+ // passes are idempotent because the camelCase keys won't match a
213
+ // snake_case input.
214
+ const rawInput = parsed.tool_input;
215
+ const canonicalInput = canonicalizeToolInput(canonicalToolName, rawInput, cli);
216
+ if (canonicalInput !== rawInput) {
217
+ parsed.tool_input = canonicalInput;
218
+ }
219
+
163
220
  // Extract session metadata from payload
164
221
  const sessionId = parsed.session_id as string | undefined;
165
222
  const session: SessionMetadata = {
@@ -750,6 +750,28 @@ function canonicalizeTool(raw) {
750
750
  return TOOL_NAME_MAP[raw] != null ? TOOL_NAME_MAP[raw] : raw;
751
751
  }
752
752
 
753
+ // Per-tool input-key translation: opencode native tools deliver args as
754
+ // camelCase (\`filePath\`, \`oldString\`, …) but failproofai builtin policies
755
+ // (\`block-read-outside-cwd\`, \`block-env-files\`, \`block-secrets-write\`)
756
+ // read \`ctx.toolInput.file_path\` etc. Without this map every Read/Write/Edit
757
+ // path-check silently no-ops on opencode. Keys are PascalCase canonical tool
758
+ // names so the lookup pairs with canonicalizeTool's output. Tools outside the
759
+ // map (MCP \`mcp_*\`, plugins) pass through unchanged. Keep in sync with
760
+ // OPENCODE_TOOL_INPUT_MAP in failproofai/src/hooks/types.ts.
761
+ const TOOL_INPUT_MAP = {
762
+ Read: { filePath: "file_path" },
763
+ Write: { filePath: "file_path" },
764
+ Edit: { filePath: "file_path", oldString: "old_string", newString: "new_string", replaceAll: "replace_all" },
765
+ };
766
+ function canonicalizeToolInput(canonicalToolName, args) {
767
+ if (!args || typeof args !== "object") return args;
768
+ const map = TOOL_INPUT_MAP[canonicalToolName];
769
+ if (!map) return args;
770
+ const out = {};
771
+ for (const k of Object.keys(args)) out[map[k] != null ? map[k] : k] = args[k];
772
+ return out;
773
+ }
774
+
753
775
  const FAILPROOFAI_BIN = ${escapedBin};
754
776
  const USE_NPX = ${useNpx};
755
777
 
@@ -848,11 +870,12 @@ export default async function failproofaiPlugin({ client, directory }) {
848
870
 
849
871
  // First-class PreToolUse hook. Note: tool args live on output.args (mutable).
850
872
  "tool.execute.before": async (input, output) => {
873
+ const canonicalTool = canonicalizeTool(input.tool);
851
874
  const r = runFailproofai("PreToolUse", {
852
875
  session_id: input.sessionID,
853
876
  cwd: directory,
854
- tool_name: canonicalizeTool(input.tool),
855
- tool_input: output.args,
877
+ tool_name: canonicalTool,
878
+ tool_input: canonicalizeToolInput(canonicalTool, output.args),
856
879
  hook_event_name: "PreToolUse",
857
880
  }, directory);
858
881
  await applyDecision(r, { client, sessionID: input.sessionID }, "PreToolUse");
@@ -860,11 +883,12 @@ export default async function failproofaiPlugin({ client, directory }) {
860
883
 
861
884
  // First-class PostToolUse hook. Note: tool args live on input.args here.
862
885
  "tool.execute.after": async (input, output) => {
886
+ const canonicalTool = canonicalizeTool(input.tool);
863
887
  const r = runFailproofai("PostToolUse", {
864
888
  session_id: input.sessionID,
865
889
  cwd: directory,
866
- tool_name: canonicalizeTool(input.tool),
867
- tool_input: input.args,
890
+ tool_name: canonicalTool,
891
+ tool_input: canonicalizeToolInput(canonicalTool, input.args),
868
892
  tool_response: { title: output.title, output: output.output, metadata: output.metadata },
869
893
  hook_event_name: "PostToolUse",
870
894
  }, directory);
@@ -873,11 +897,12 @@ export default async function failproofaiPlugin({ client, directory }) {
873
897
 
874
898
  // Cleaner deny UX for prompted tools — mutate output.status instead of throwing.
875
899
  "permission.ask": async (input, output) => {
900
+ const canonicalTool = canonicalizeTool(input.tool);
876
901
  const r = runFailproofai("PermissionRequest", {
877
902
  session_id: input.sessionID,
878
903
  cwd: directory,
879
- tool_name: canonicalizeTool(input.tool) || input.command || "permission",
880
- tool_input: input,
904
+ tool_name: canonicalTool || input.command || "permission",
905
+ tool_input: canonicalizeToolInput(canonicalTool, input),
881
906
  hook_event_name: "PermissionRequest",
882
907
  }, directory);
883
908
  try {
@@ -184,8 +184,22 @@ export async function evaluatePolicies(
184
184
  // and translates `permission === "deny"` into a `{block: true, reason}`
185
185
  // return value from its `pi.on("tool_call", ...)` handler. Pi has no
186
186
  // event-specific decision wrappers, so all events flow through the
187
- // same flat shape.
187
+ // same flat shape — except Stop, where we emit the MANDATORY ACTION
188
+ // wording so the shim can re-inject it as a system-prompt suffix on
189
+ // the next before_agent_start (Pi cannot veto agent_end directly).
190
+ // Mirrors the Cursor/Gemini/Copilot/OpenCode Stop branches above.
188
191
  if (session?.cli === "pi") {
192
+ if (eventType === "Stop") {
193
+ const reasonText = `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.`;
194
+ return {
195
+ exitCode: 0,
196
+ stdout: JSON.stringify({ permission: "deny", reason: reasonText }),
197
+ stderr: "",
198
+ policyName: policy.name,
199
+ reason,
200
+ decision: "deny",
201
+ };
202
+ }
189
203
  const response = {
190
204
  permission: "deny",
191
205
  reason: blockedMessage,
@@ -416,8 +430,26 @@ export async function evaluatePolicies(
416
430
  // Pi: instruct emits `{permission: "allow", reason}`. The shim won't
417
431
  // block (no `"deny"`); it surfaces `reason` to the user where possible
418
432
  // (Pi has no first-class `additional_context` channel in its tool-call
419
- // return shape, so we log it).
433
+ // return shape, so we log it). Stop is the exception — we emit a
434
+ // `permission: "deny"` with the MANDATORY ACTION wording so the shim
435
+ // captures it for next-turn before_agent_start injection. Same handoff
436
+ // contract as the deny branch above.
420
437
  if (session?.cli === "pi") {
438
+ if (eventType === "Stop") {
439
+ const policyAttribution = policyNames.length === 1
440
+ ? `policy: ${policyNames[0]}`
441
+ : `policies: ${policyNames.join(", ")}`;
442
+ const reasonText = `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.`;
443
+ return {
444
+ exitCode: 0,
445
+ stdout: JSON.stringify({ permission: "deny", reason: reasonText }),
446
+ stderr: "",
447
+ policyName: policyNames[0],
448
+ policyNames,
449
+ reason: combined,
450
+ decision: "instruct",
451
+ };
452
+ }
421
453
  const response = {
422
454
  permission: "allow",
423
455
  reason: `Instruction from failproofai: ${combined}`,
@@ -279,6 +279,32 @@ export const OPENCODE_TOOL_MAP: Record<string, string> = {
279
279
  todoread: "TodoRead",
280
280
  };
281
281
 
282
+ /**
283
+ * Per-tool input-key translation: OpenCode camelCase → Claude snake_case,
284
+ * keyed by canonical (PascalCase) tool name so it pairs naturally with the
285
+ * output of OPENCODE_TOOL_MAP. Without this, builtin policies that read
286
+ * `ctx.toolInput.file_path` (`block-read-outside-cwd`, `block-env-files`,
287
+ * `block-secrets-write`) silently no-op on every OpenCode Read/Write/Edit
288
+ * call because OpenCode's native tools deliver args as `filePath` / `oldString`
289
+ * / `newString` / `replaceAll`.
290
+ *
291
+ * Tools outside this set (MCP `mcp_*`, third-party plugins) pass through
292
+ * unchanged so their schemas aren't corrupted. Mirrored inline in the shim
293
+ * template at src/hooks/integrations.ts:buildOpenCodePluginShim — the shim
294
+ * must be self-contained because opencode loads it as a JS module — so any
295
+ * change here MUST be mirrored there.
296
+ */
297
+ export const OPENCODE_TOOL_INPUT_MAP: Record<string, Record<string, string>> = {
298
+ Read: { filePath: "file_path" },
299
+ Write: { filePath: "file_path" },
300
+ Edit: {
301
+ filePath: "file_path",
302
+ oldString: "old_string",
303
+ newString: "new_string",
304
+ replaceAll: "replace_all",
305
+ },
306
+ };
307
+
282
308
  // ── Pi (pi-coding-agent) ───────────────────────────────────────────────────
283
309
  //
284
310
  // Pi loads TypeScript extensions from packages registered in `.pi/settings.json`
@@ -364,6 +390,32 @@ export const PI_TOOL_MAP: Record<string, string> = {
364
390
  grep: "Grep",
365
391
  };
366
392
 
393
+ /**
394
+ * Per-tool input-key translation: Pi's tool args use `path` for Read / Write /
395
+ * Edit (see https://github.com/earendil-works/pi packages/coding-agent/src/core/tools)
396
+ * but failproofai builtins read `ctx.toolInput.file_path`. `block-read-outside-cwd`
397
+ * already has a `ctx.toolInput.path` fallback (`src/hooks/builtin-policies.ts:796`)
398
+ * so it works on Pi via that path; without this map, however,
399
+ * `block-env-files` and `block-secrets-write` — which only check
400
+ * `ctx.toolInput.file_path` via `getFilePath()` — silently no-op on Pi.
401
+ *
402
+ * Pi's Edit tool delivers a nested `edits: [{oldText, newText}, …]` array
403
+ * shape that doesn't translate flatly to Claude's `{old_string, new_string,
404
+ * replace_all}`, so only the top-level `path` is mapped. Edit-content
405
+ * checks (sanitize-* on the edit body) remain Pi-shape — none of today's
406
+ * builtins look at the edit body. Tools outside this set pass through
407
+ * unchanged.
408
+ *
409
+ * Mirrored inline in pi-extension/index.ts (the extension must be self-
410
+ * contained — Pi loads it as an in-process JS module), so any change here
411
+ * MUST be mirrored there.
412
+ */
413
+ export const PI_TOOL_INPUT_MAP: Record<string, Record<string, string>> = {
414
+ Read: { path: "file_path" },
415
+ Write: { path: "file_path" },
416
+ Edit: { path: "file_path" },
417
+ };
418
+
367
419
  // ── Gemini CLI ─────────────────────────────────────────────────────────────
368
420
  //
369
421
  // Gemini CLI's hook contract is the closest thing to a Claude Code clone we've
@@ -1,3 +0,0 @@
1
- module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},56704,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},20635,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/action-async-storage.external.js",()=>require("next/dist/server/app-render/action-async-storage.external.js"))},24725,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},43285,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/dynamic-access-async-storage.external.js",()=>require("next/dist/server/app-render/dynamic-access-async-storage.external.js"))},42602,(a,b,c)=>{"use strict";b.exports=a.r(18622)},87924,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactJsxRuntime},72131,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].React},38783,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactServerDOMTurbopackClient},9270,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.AppRouterContext},36313,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.HooksClientContext},18341,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.ServerInsertedHtml},35112,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactDOM},66036,a=>{"use strict";a.s(["captureClientEvent",0,function(a,b){},"setClientTelemetryConfig",0,function(a){}])},6236,a=>{"use strict";var b=a.i(87924),c=a.i(72131);let d=(0,c.createContext)(void 0);a.s(["AutoRefreshProvider",0,function({children:a}){let[e,f]=(0,c.useState)(0);return(0,b.jsx)(d.Provider,{value:{intervalSec:e,setIntervalSec:f},children:a})},"useAutoRefresh",0,function(){let a=(0,c.useContext)(d);if(!a)throw Error("useAutoRefresh must be used within AutoRefreshProvider");return a}])},14258,89027,40695,a=>{"use strict";var b=a.i(64831);let c=(0,b.default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);a.s(["Shield",0,c],14258);let d=(0,b.default)("chevron-down",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);a.s(["ChevronDown",0,d],89027);var e=a.i(87924),f=a.i(72131),g=a.i(97895);let h=f.forwardRef(({className:a,variant:b="default",size:c="default",...d},f)=>(0,e.jsx)("button",{className:(0,g.cn)("inline-flex items-center justify-center rounded-md text-sm font-medium transition-[background-color,color,box-shadow,border-color] duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:opacity-50 disabled:pointer-events-none",{"bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-[0_0_0_1px_var(--primary)]":"default"===b,"text-foreground hover:bg-muted":"ghost"===b,"border border-border bg-transparent hover:border-primary/60 hover:bg-card":"outline"===b,"h-10 py-2 px-4":"default"===c,"h-10 w-10":"icon"===c,"h-9 px-3":"sm"===c,"h-11 px-8":"lg"===c},a),ref:f,...d}));h.displayName="Button",a.s(["Button",0,h],40695)},46058,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},88347,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d,e,f={ACTION_HMR_REFRESH:function(){return l},ACTION_NAVIGATE:function(){return i},ACTION_REFRESH:function(){return h},ACTION_RESTORE:function(){return j},ACTION_SERVER_ACTION:function(){return m},ACTION_SERVER_PATCH:function(){return k},PrefetchKind:function(){return n},ScrollBehavior:function(){return o}};for(var g in f)Object.defineProperty(c,g,{enumerable:!0,get:f[g]});let h="refresh",i="navigate",j="restore",k="server-patch",l="hmr-refresh",m="server-action";var n=((d={}).AUTO="auto",d.FULL="full",d),o=((e={})[e.Default=0]="Default",e[e.NoScroll=1]="NoScroll",e);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},67009,(a,b,c)=>{"use strict";function d(a){return null!==a&&"object"==typeof a&&"then"in a&&"function"==typeof a.then}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"isThenable",{enumerable:!0,get:function(){return d}})},90841,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={dispatchAppRouterAction:function(){return i},dispatchGestureState:function(){return j},refreshOnInstantNavigationUnlock:function(){return h},useActionQueue:function(){return k}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(46058)._(a.r(72131)),g=a.r(67009);a.r(88347);function h(){}function i(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function j(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function k(a){let[b,c]=f.default.useState(a.state),[d,e]=(0,f.useOptimistic)(b),h=(0,f.useMemo)(()=>d,[d]);return(0,g.isThenable)(h)?(0,f.use)(h):h}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},20611,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"callServer",{enumerable:!0,get:function(){return g}});let d=a.r(72131),e=a.r(88347),f=a.r(90841);async function g(a,b){return new Promise((c,g)=>{(0,d.startTransition)(()=>{(0,f.dispatchAppRouterAction)({type:e.ACTION_SERVER_ACTION,actionId:a,actionArgs:b,resolve:c,reject:g})})})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},1722,(a,b,c)=>{"use strict";let d;Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"findSourceMapURL",{enumerable:!0,get:function(){return d}});("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},5050,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={callServer:function(){return f.callServer},createServerReference:function(){return h.createServerReference},findSourceMapURL:function(){return g.findSourceMapURL}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20611),g=a.r(1722),h=a.r(38783)},48647,a=>{"use strict";var b=a.i(5050);let c=(0,b.createServerReference)("00845dd78ea22f1d0d0244c310878f454d9f9ef311",b.callServer,void 0,b.findSourceMapURL,"getTelemetryConfig");a.s(["getTelemetryConfig",0,c])},10765,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(50944),e=a.i(48647),f=a.i(66036);let g=(0,c.createContext)(void 0);a.s(["PostHogProvider",0,function({children:a}){let h=(0,d.usePathname)(),i=(0,c.useRef)(null),j=(0,c.useRef)(!1);(0,c.useEffect)(()=>{let a=!1;return(0,e.getTelemetryConfig)().then(b=>{a||((0,f.setClientTelemetryConfig)(b),j.current=!0,(0,f.captureClientEvent)("$pageview"),i.current=window.location.pathname)}),()=>{a=!0}},[]),(0,c.useEffect)(()=>{j.current&&i.current!==h&&(i.current=h,(0,f.captureClientEvent)("$pageview"))},[h]);let k=c.default.useCallback((a,b)=>{(0,f.captureClientEvent)(a,b)},[]);return(0,b.jsx)(g.Provider,{value:{capture:k},children:a})},"usePostHog",0,function(){let a=(0,c.useContext)(g);if(void 0===a)throw Error("usePostHog must be used within a PostHogProvider");return a}])},45550,a=>{"use strict";var b=a.i(72131),c=a.i(66036);a.s(["GlobalErrorListeners",0,function(){return(0,b.useEffect)(()=>{let a=a=>{(0,c.captureClientEvent)("unhandled_exception",{error_message:a.message,error_name:a.error?.name,error_filename:a.filename,error_lineno:a.lineno,error_colno:a.colno})},b=a=>{let b=a.reason;(0,c.captureClientEvent)("unhandled_rejection",{error_message:b instanceof Error?b.message:String(b),error_name:b instanceof Error?b.name:void 0})};return window.addEventListener("error",a),window.addEventListener("unhandledrejection",b),()=>{window.removeEventListener("error",a),window.removeEventListener("unhandledrejection",b)}},[]),null}])},10815,a=>{"use strict";var b=a.i(87924),c=a.i(38246),d=a.i(50944),e=a.i(64831);let f=(0,e.default)("folder-open",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);var g=a.i(14258),h=a.i(72131);let i=(0,e.default)("git-branch",[["path",{d:"M15 6a9 9 0 0 0-9 9V3",key:"1cii5b"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}]]),j=(0,e.default)("lightbulb",[["path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5",key:"1gvzjb"}],["path",{d:"M9 18h6",key:"x1upvd"}],["path",{d:"M10 22h4",key:"ceow96"}]]),k=(0,e.default)("bug",[["path",{d:"M12 20v-9",key:"1qisl0"}],["path",{d:"M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z",key:"uouzyp"}],["path",{d:"M14.12 3.88 16 2",key:"qol33r"}],["path",{d:"M21 21a4 4 0 0 0-3.81-4",key:"1b0z45"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97",key:"5cxbf6"}],["path",{d:"M22 13h-4",key:"1jl80f"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4",key:"1fjd4g"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97",key:"1d7oge"}],["path",{d:"M6 13H2",key:"82j7cp"}],["path",{d:"m8 2 1.88 1.88",key:"fmnt4t"}],["path",{d:"M9 7.13V6a3 3 0 1 1 6 0v1.13",key:"1vgav8"}]]),l=(0,e.default)("message-square",[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]]);var m=a.i(89027);let n=(0,e.default)("star",[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",key:"r04s7s"}]]),o=(0,e.default)("book-open",[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]]),p=(0,e.default)("hash",[["line",{x1:"4",x2:"20",y1:"9",y2:"9",key:"4lhtct"}],["line",{x1:"4",x2:"20",y1:"15",y2:"15",key:"vyu0kd"}],["line",{x1:"10",x2:"8",y1:"3",y2:"21",key:"1ggp8o"}],["line",{x1:"16",x2:"14",y1:"3",y2:"21",key:"weycgp"}]]);var q=a.i(40695);let r="https://github.com/exospherehost/failproofai",s="failproofai@exosphere.host",t=[{label:"Star us on GitHub",icon:n,href:"https://github.com/exospherehost/failproofai"},{label:"Documentation",icon:o,href:"https://befailproof.ai"},{label:"Join our Slack",icon:p,href:"https://join.slack.com/t/failproofai/shared_invite/zt-3v63b7k5e-O3NBHmj8X6n9gZSGDx6ggQ"},{label:"Request a Feature",icon:j,href:`${r}/issues/new?labels=enhancement&title=Feature+Request%3A+`},{label:"Report an Issue",icon:k,href:`${r}/issues/new?labels=bug&title=Bug+Report%3A+`},{label:"Ask a Question",icon:l,href:`${r}/discussions/new?category=q-a`}],u=()=>{let[a,c]=(0,h.useState)(!1),d=(0,h.useCallback)(()=>c(!1),[]),e=(0,h.useCallback)(a=>{"Escape"===a.key&&c(!1)},[]);return(0,b.jsxs)("div",{className:"relative",onKeyDown:e,children:[a&&(0,b.jsx)("div",{className:"fixed inset-0 z-40",onClick:d,"aria-hidden":"true"}),(0,b.jsxs)(q.Button,{type:"button",variant:"ghost",size:"sm",onClick:()=>c(a=>!a),"aria-expanded":a,"aria-haspopup":"true",className:"relative z-50 flex items-center gap-1.5 text-muted-foreground hover:text-foreground",children:[(0,b.jsx)(i,{className:"h-4 w-4"}),(0,b.jsx)("span",{className:"hidden sm:inline text-xs",children:"Reach Us"}),(0,b.jsx)(m.ChevronDown,{className:`h-3 w-3 transition-transform ${a?"rotate-180":""}`})]}),a&&(0,b.jsxs)("div",{className:"absolute right-0 mt-2 w-56 rounded-lg border border-border bg-card shadow-lg z-50",role:"menu",children:[(0,b.jsxs)("div",{className:"px-3 py-2 border-b border-border",children:[(0,b.jsx)("p",{className:"text-xs font-medium text-foreground",children:"Reach Developers"}),(0,b.jsx)("p",{className:"text-[0.65rem] text-muted-foreground mt-0.5",children:"We'd love to hear from you"})]}),(0,b.jsx)("div",{className:"py-1",children:t.map(({label:a,icon:c,href:e})=>(0,b.jsxs)("a",{href:e,target:"_blank",rel:"noopener noreferrer",role:"menuitem",className:"flex items-center gap-2.5 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors",onClick:d,children:[(0,b.jsx)(c,{className:"h-3.5 w-3.5 flex-shrink-0"}),a]},a))}),(0,b.jsx)("div",{className:"px-3 py-2 border-t border-border",children:(0,b.jsxs)("p",{className:"text-[0.65rem] text-muted-foreground",children:["or email"," ",(0,b.jsx)("a",{href:`mailto:${s}`,className:"text-primary hover:text-primary/80 transition-colors",children:s})]})})]})]})},v=(0,e.default)("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);var w=a.i(97895),x=a.i(6236);let y=[{label:"Off",value:0},{label:"5s",value:5},{label:"10s",value:10},{label:"30s",value:30}];function z({className:a}){let c=(0,d.useRouter)(),[e,f]=(0,h.useTransition)(),{intervalSec:g,setIntervalSec:i}=(0,x.useAutoRefresh)(),j=(0,h.useCallback)(()=>{f(()=>c.refresh())},[c]);(0,h.useEffect)(()=>{if(g<=0)return;let a=setInterval(j,1e3*g);return()=>clearInterval(a)},[g,j]);let k=g>0;return(0,b.jsxs)("div",{className:(0,w.cn)("inline-flex items-center rounded-lg border border-border bg-muted/50 p-0.5 gap-0.5",a),children:[(0,b.jsx)("button",{onClick:j,title:"Refresh",className:(0,w.cn)("inline-flex items-center justify-center rounded-md p-1.5 transition-colors",k?"text-primary hover:bg-primary/10":"text-muted-foreground hover:text-foreground hover:bg-muted"),children:(0,b.jsx)(v,{className:(0,w.cn)("w-3.5 h-3.5",e&&"animate-spin")})}),(0,b.jsx)("div",{className:"w-px h-4 bg-border"}),(0,b.jsx)("div",{className:"inline-flex items-center gap-0.5",role:"group","aria-label":"Auto-refresh interval",children:y.map(a=>(0,b.jsx)("button",{onClick:()=>i(a.value),"aria-pressed":g===a.value,className:(0,w.cn)("px-2 py-1 text-[11px] font-medium rounded-md transition-colors",g===a.value?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground hover:bg-muted"),children:a.label},a.value))})]})}let A=[{href:"/policies",label:"Policies",icon:g.Shield},{href:"/projects",label:"Projects",icon:f}];a.s(["Navbar",0,({disabledPages:a=[]})=>{let e=(0,d.usePathname)();return(0,b.jsx)("header",{className:"relative z-50 border-b border-border bg-card/60 backdrop-blur supports-[backdrop-filter]:bg-card/60",children:(0,b.jsx)("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",children:(0,b.jsxs)("div",{className:"flex items-center justify-between h-16",children:[(0,b.jsxs)("div",{className:"flex items-center gap-3",children:[(0,b.jsx)("a",{href:"https://github.com/exospherehost/failproofai",target:"_blank",rel:"noopener noreferrer",className:"flex items-center hover:opacity-80 transition-opacity","aria-label":"failproof ai · GitHub",children:(0,b.jsx)("img",{src:"https://d2wq11aau0arks.cloudfront.net/failproof/logo-wordmark.png",alt:"failproof ai",style:{height:24,width:"auto"},className:"select-none"})}),(0,b.jsxs)("span",{className:"font-mono text-[0.6rem] leading-none text-muted-foreground/70 border border-border/60 rounded px-1.5 py-0.5 select-none tracking-wider uppercase",children:["v","0.0.10-beta.9"]}),(0,b.jsx)("div",{className:"w-px h-8 bg-border ml-2"}),(0,b.jsx)("nav",{className:"flex items-center h-16",children:A.filter(({href:b})=>{let c=b.slice(1);return!a.includes(c)}).map(({href:a,label:d,icon:f})=>{let g="/projects"===a?"/projects"===e||e.startsWith("/project/"):e.startsWith(a);return(0,b.jsxs)(c.default,{href:a,className:`relative flex items-center gap-1.5 px-3 h-full text-sm transition-colors ${g?"text-foreground font-medium":"text-muted-foreground hover:text-foreground"}`,children:[(0,b.jsx)(f,{className:`w-4 h-4 ${g?"text-primary":""}`}),d,(0,b.jsx)("span",{className:`absolute inset-x-1 bottom-0 h-[2px] transition-all ${g?"bg-primary":"bg-transparent"}`})]},a)})})]}),(0,b.jsxs)("div",{className:"flex items-center gap-1",children:[(0,b.jsx)(z,{}),(0,b.jsx)("div",{className:"w-px h-6 bg-border mx-1"}),(0,b.jsx)(u,{})]})]})})})}],10815)},18952,a=>{"use strict";var b=a.i(87924),c=a.i(72131);let d=[],e=0,f=new Set;function g(){for(let a of f)a()}function h(a){return f.add(a),()=>f.delete(a)}function i(){return d}function j({message:a}){let[d,e]=(0,c.useState)(!1);return(0,c.useEffect)(()=>{requestAnimationFrame(()=>e(!0));let a=setTimeout(()=>e(!1),2e3);return()=>clearTimeout(a)},[]),(0,b.jsx)("div",{className:`bg-card border border-border text-foreground text-sm px-4 py-2 rounded-lg shadow-lg transition-opacity duration-300 ${d?"opacity-100":"opacity-0"}`,children:a})}a.s(["Toaster",0,function(){let a=(0,c.useSyncExternalStore)(h,i,i);return(0,b.jsx)("div",{className:"fixed bottom-4 right-4 z-50 flex flex-col gap-2",children:a.map(a=>(0,b.jsx)(j,{message:a.message},a.id))})},"toast",0,function(a){let b=e++;d=[...d,{id:b,message:a}],g(),setTimeout(()=>{d=d.filter(a=>a.id!==b),g()},2500)}])}];
2
-
3
- //# sourceMappingURL=%5Broot-of-the-server%5D__0.2-_y.._.js.map
@@ -1,3 +0,0 @@
1
- module.exports=[46786,(a,b,c)=>{b.exports=a.x("os",()=>require("os"))},12714,(a,b,c)=>{b.exports=a.x("node:fs/promises",()=>require("node:fs/promises"))},24868,(a,b,c)=>{b.exports=a.x("fs/promises",()=>require("fs/promises"))},90798,a=>{"use strict";var b=a.i(46786),c=a.i(14747);a.s(["decodeFolderName",0,function(a){return/^[A-Za-z]--/.test(a)?a[0]+":/"+a.slice(3).replace(/-/g,"/"):a.replace(/-/g,"/")},"encodeFolderName",0,function(a){let b=/^([A-Za-z]):[\\/](.*)$/.exec(a);return b?b[1]+"--"+b[2].replace(/[\\/]/g,"-"):a.replace(/[\\/]/g,"-")},"getClaudeProjectsPath",0,function(){let a=process.env.CLAUDE_PROJECTS_PATH;return a||(0,c.join)((0,b.homedir)(),".claude","projects")}])},34900,a=>{"use strict";a.s(["runtimeCache",0,function(a,b,c){let d=new Map,e=new Map,f=c?.maxSize;return async(...c)=>{let g=JSON.stringify(c),h=d.get(g);if(h&&Date.now()<h.expiry)return h.data;let i=e.get(g);if(i)return i;let j=a(...c).then(a=>{if(e.delete(g),f&&d.size>=f){let a=d.keys().next().value;d.delete(a)}return d.set(g,{data:a,expiry:Date.now()+1e3*b}),a},a=>{throw e.delete(g),a});return e.set(g,j),j}}])},76668,a=>{"use strict";let b=1;function c(a){return`[failproofai${new Date().toISOString()}] ${a}`}a.s(["logError",0,function(a,b){void 0!==b?console.error(c("ERROR"),a,b):console.error(c("ERROR"),a)},"logWarn",0,function(a,d){b>=b&&(void 0!==d?console.warn(c("WARN"),a,d):console.warn(c("WARN"),a))}])},33432,a=>{"use strict";a.s(["formatDate",0,function(a){return new Intl.DateTimeFormat("en-US",{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).format(a)}])},26192,a=>{"use strict";async function b(a,b){let c=Array(a.length),d=0;async function e(){for(;d<a.length;){let b=d++;try{c[b]={status:"fulfilled",value:await a[b]()}}catch(a){c[b]={status:"rejected",reason:a}}}}let f=Array.from({length:Math.min(b,a.length)},()=>e());return await Promise.all(f),c}a.s(["batchAll",0,b])},50640,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"InvariantError",{enumerable:!0,get:function(){return d}});class d extends Error{constructor(a,b){super(`Invariant: ${a.endsWith(".")?a:a+"."} This is a bug in Next.js.`,b),this.name="InvariantError"}}},64240,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},93695,(a,b,c)=>{b.exports=a.x("next/dist/shared/lib/no-fallback-error.external.js",()=>require("next/dist/shared/lib/no-fallback-error.external.js"))},71306,(a,b,c)=>{b.exports=a.r(18622)},79847,a=>{a.n(a.i(3343))},9185,a=>{a.n(a.i(29432))},72842,a=>{a.n(a.i(75164))},54897,a=>{a.n(a.i(30106))},56157,a=>{a.n(a.i(18970))},94331,a=>{a.n(a.i(60644))},15988,a=>{a.n(a.i(56952))},25766,a=>{a.n(a.i(77341))},29725,a=>{a.n(a.i(94290))},5785,a=>{a.n(a.i(90588))},74793,a=>{a.n(a.i(33169))},85826,a=>{a.n(a.i(37111))},21565,a=>{a.n(a.i(41763))},65911,a=>{a.n(a.i(8950))},25128,a=>{a.n(a.i(91562))},40781,a=>{a.n(a.i(49670))},69411,a=>{a.n(a.i(75700))},63081,a=>{a.n(a.i(276))},62837,a=>{a.n(a.i(40795))},34607,a=>{a.n(a.i(11614))},96338,a=>{a.n(a.i(21751))},50642,a=>{a.n(a.i(12213))},32242,a=>{a.n(a.i(22693))},88530,a=>{a.n(a.i(10531))},8583,a=>{a.n(a.i(1082))},38534,a=>{a.n(a.i(98175))},70408,a=>{a.n(a.i(9095))},22922,a=>{a.n(a.i(96772))},78294,a=>{a.n(a.i(71717))},16625,a=>{a.n(a.i(85034))},88648,a=>{a.n(a.i(68113))},51914,a=>{a.n(a.i(66482))},25466,a=>{a.n(a.i(91505))},69191,a=>{"use strict";var b=a.i(12714),c=a.i(50227),d=a.i(90798),e=a.i(34900),f=a.i(26192),g=a.i(76668),h=a.i(33432);async function i(a,c){try{return(await (0,b.stat)(a)).mtime}catch(a){return(0,g.logWarn)(`Failed to stat ${c}:`,a),new Date(0)}}async function j(a){try{if(!(await (0,b.stat)(a)).isDirectory())return null;return await (0,b.readdir)(a,{withFileTypes:!0})}catch{return null}}async function k(){try{let a=(0,d.getClaudeProjectsPath)(),b=await j(a);if(!b)return[];let e=(await (0,f.batchAll)(b.filter(a=>a.isDirectory()).map(b=>async()=>{let d=(0,c.join)(a,b.name),e=await i(d,b.name);return{name:b.name,path:d,isDirectory:!0,lastModified:e,lastModifiedFormatted:(0,h.formatDate)(e),cli:["claude"]}}),16)).filter(a=>"fulfilled"===a.status).map(a=>a.value);return e.sort((a,b)=>b.lastModified.getTime()-a.lastModified.getTime()),e}catch(a){return(0,g.logError)("Error reading Claude project folders:",a),[]}}async function l(){let[{getCodexProjects:b},{getCopilotProjects:c},{getCursorProjects:d},{getOpenCodeProjects:e},{getPiProjects:f},{getGeminiProjects:h}]=await Promise.all([a.A(93658),a.A(60956),a.A(26244),a.A(2164),a.A(92351),a.A(26396)]),[i,j,l,m,n,o,p]=await Promise.all([k(),b().catch(a=>((0,g.logError)("Error reading Codex projects:",a),[])),c().catch(a=>((0,g.logError)("Error reading Copilot projects:",a),[])),d().catch(a=>((0,g.logError)("Error reading Cursor projects:",a),[])),e().catch(a=>((0,g.logError)("Error reading OpenCode projects:",a),[])),f().catch(a=>((0,g.logError)("Error reading Pi projects:",a),[])),h().catch(a=>((0,g.logError)("Error reading Gemini projects:",a),[]))]);return function(...a){let b=new Map;for(let c of a)for(let a of c){let c=b.get(a.name);if(!c){b.set(a.name,{...a,cli:[...a.cli]});continue}let d=[...c.cli];for(let b of a.cli)d.includes(b)||d.push(b);let e=a.lastModified.getTime()>c.lastModified.getTime()?a:c;b.set(a.name,{...c,cli:d,lastModified:e.lastModified,lastModifiedFormatted:e.lastModifiedFormatted})}let c=Array.from(b.values());return c.sort((a,b)=>b.lastModified.getTime()-a.lastModified.getTime()),c}(i,j,l,m,n,o,p)}function m(a){let b=a.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);return b?b[0]:void 0}async function n(a){try{let b=await j(a);if(!b)return[];let d=b.filter(a=>a.isFile()&&a.name.endsWith(".jsonl")&&m(a.name)),e=(await (0,f.batchAll)(d.map(b=>async()=>{let d=(0,c.join)(a,b.name),e=await i(d,b.name);return{name:b.name,path:d,lastModified:e,lastModifiedFormatted:(0,h.formatDate)(e),sessionId:m(b.name),cli:"claude"}}),16)).filter(a=>"fulfilled"===a.status).map(a=>a.value);return e.sort((a,b)=>b.lastModified.getTime()-a.lastModified.getTime()),e}catch(a){return(0,g.logError)("Error reading session files:",a),[]}}let o=(0,e.runtimeCache)(l,30),p=(0,e.runtimeCache)(a=>n(a),30);a.s(["UUID_RE",0,/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,"getCachedProjectFolders",0,o,"getCachedSessionFiles",0,p,"resolveProjectPath",0,function(a){if(!a)throw RangeError("Empty project name");if(/^[/\\]/.test(a))throw RangeError("Absolute project name");let b=(0,c.resolve)((0,d.getClaudeProjectsPath)()),e=(0,c.resolve)((0,c.join)(b,a));if(!e.startsWith(b+c.sep))throw RangeError("Project path escapes root");return e}])},790,(a,b,c)=>{let{createClientModuleProxy:d}=a.r(11857);a.n(d("[project]/node_modules/next/dist/client/app-dir/link.js <module evaluation>"))},84707,(a,b,c)=>{let{createClientModuleProxy:d}=a.r(11857);a.n(d("[project]/node_modules/next/dist/client/app-dir/link.js"))},97647,a=>{"use strict";a.i(790);var b=a.i(84707);a.n(b)},95936,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={default:function(){return i},useLinkStatus:function(){return h.useLinkStatus}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(64240),g=a.r(7997),h=f._(a.r(97647));function i(a){let b=a.legacyBehavior,c="string"==typeof a.children||"number"==typeof a.children||"string"==typeof a.children?.type,d=a.children?.type?.$$typeof===Symbol.for("react.client.reference");return!b||c||d||(a.children?.type?.$$typeof===Symbol.for("react.lazy")?console.error("Using a Lazy Component as a direct child of `<Link legacyBehavior>` from a Server Component is not supported. If you need legacyBehavior, wrap your Lazy Component in a Client Component that renders the Link's `<a>` tag."):console.error("Using a Server Component as a direct child of `<Link legacyBehavior>` is not supported. If you need legacyBehavior, wrap your Server Component in a Client Component that renders the Link's `<a>` tag.")),(0,g.jsx)(h.default,{...a})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},8174,a=>{"use strict";a.s(["default",()=>b]);let b=(0,a.i(11857).registerClientReference)(function(){throw Error("Attempted to call the default export of [project]/node_modules/lucide-react/dist/esm/Icon.mjs <module evaluation> from the server, but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/node_modules/lucide-react/dist/esm/Icon.mjs <module evaluation>","default")},90697,a=>{"use strict";a.s(["default",()=>b]);let b=(0,a.i(11857).registerClientReference)(function(){throw Error("Attempted to call the default export of [project]/node_modules/lucide-react/dist/esm/Icon.mjs from the server, but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/node_modules/lucide-react/dist/esm/Icon.mjs","default")},53808,a=>{"use strict";a.i(8174);var b=a.i(90697);a.n(b)},84930,92277,a=>{"use strict";var b=a.i(717);let c=a=>{let b=a.replace(/^([A-Z])|[\s-_]+(\w)/g,(a,b,c)=>c?c.toUpperCase():b.toLowerCase());return b.charAt(0).toUpperCase()+b.slice(1)};var d=a.i(53808);let e=(a,e)=>{let f=(0,b.forwardRef)(({className:f,...g},h)=>(0,b.createElement)(d.default,{ref:h,iconNode:e,className:((...a)=>a.filter((a,b,c)=>!!a&&""!==a.trim()&&c.indexOf(a)===b).join(" ").trim())(`lucide-${c(a).replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}`,`lucide-${a}`,f),...g}));return f.displayName=c(a),f};a.s(["default",0,e],92277);let f=e("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);a.s(["ArrowLeft",0,f],84930)},71224,a=>{a.v("/_next/static/media/icon.0a.gigb3_x5pd.png"+(globalThis.NEXT_CLIENT_ASSET_SUFFIX||""))},1022,a=>{"use strict";let b={src:a.i(71224).default,width:1920,height:1920};a.s(["default",0,b])},93658,a=>{a.v(b=>Promise.all(["server/chunks/ssr/lib_codex-projects_ts_0eosib~._.js"].map(b=>a.l(b))).then(()=>b(1024)))},60956,a=>{a.v(b=>Promise.all(["server/chunks/ssr/lib_copilot-projects_ts_0r8xkn8._.js"].map(b=>a.l(b))).then(()=>b(88890)))},26244,a=>{a.v(b=>Promise.all(["server/chunks/ssr/lib_cursor-projects_ts_0qt1scg._.js"].map(b=>a.l(b))).then(()=>b(93087)))},2164,a=>{a.v(b=>Promise.all(["server/chunks/ssr/lib_opencode-projects_ts_0op9gyp._.js"].map(b=>a.l(b))).then(()=>b(64458)))},92351,a=>{a.v(b=>Promise.all(["server/chunks/ssr/lib_pi-projects_ts_103tsh1._.js"].map(b=>a.l(b))).then(()=>b(2727)))},26396,a=>{a.v(b=>Promise.all(["server/chunks/ssr/lib_gemini-projects_ts_0sl~yqr._.js"].map(b=>a.l(b))).then(()=>b(62074)))}];
2
-
3
- //# sourceMappingURL=%5Broot-of-the-server%5D__0okpm5k._.js.map