@viberaven/cli 1.7.0 → 1.7.1

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 (35) hide show
  1. package/AGENTS.md +64 -64
  2. package/LICENSE +21 -21
  3. package/README.md +67 -67
  4. package/SECURITY.md +59 -59
  5. package/dist/cli.js +697 -632
  6. package/dist/console/app.js +251 -251
  7. package/dist/console/index.html +46 -46
  8. package/dist/console/styles.css +267 -267
  9. package/dist/github-ci.js +1 -1
  10. package/dist/playbooks/_stack-default.json +11 -11
  11. package/dist/playbooks/auth-supabase.json +31 -31
  12. package/dist/playbooks/stripe.json +66 -66
  13. package/dist/playbooks/supabase.json +51 -51
  14. package/dist/playbooks/vercel.json +42 -42
  15. package/dist/report/assets/provider-authjs.svg +5 -5
  16. package/dist/report/assets/provider-aws.svg +5 -5
  17. package/dist/report/assets/provider-logrocket.svg +4 -4
  18. package/dist/report/report-cli.css +1466 -1466
  19. package/dist/report/station.css +11854 -11854
  20. package/dist/report/station.js +8319 -8319
  21. package/dist/stack/scene.css +522 -522
  22. package/dist/stack/stack.css +1691 -1691
  23. package/dist/vendor/anime.umd.min.js +7 -7
  24. package/package.json +96 -96
  25. package/playbooks/_stack-default.json +11 -11
  26. package/playbooks/auth-supabase.json +31 -31
  27. package/playbooks/stripe.json +66 -66
  28. package/playbooks/supabase.json +51 -51
  29. package/playbooks/vercel.json +42 -42
  30. package/schemas/permissions-matrix.schema.json +90 -90
  31. package/schemas/verification-plan.schema.json +350 -350
  32. package/schemas/verification-report.schema.json +550 -550
  33. package/templates/AGENTS.snippet.md +56 -56
  34. package/templates/CLAUDE.snippet.md +14 -14
  35. package/templates/CURSOR.snippet.md +14 -14
package/dist/cli.js CHANGED
@@ -14406,9 +14406,9 @@ var init_clipboard = __esm({
14406
14406
  }
14407
14407
  });
14408
14408
 
14409
- // node_modules/picocolors/picocolors.js
14409
+ // ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/picocolors/picocolors.js
14410
14410
  var require_picocolors = __commonJS({
14411
- "node_modules/picocolors/picocolors.js"(exports2, module2) {
14411
+ "../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/picocolors/picocolors.js"(exports2, module2) {
14412
14412
  var p2 = process || {};
14413
14413
  var argv = p2.argv || [];
14414
14414
  var env = p2.env || {};
@@ -14599,9 +14599,9 @@ var init_terminalSummary = __esm({
14599
14599
  }
14600
14600
  });
14601
14601
 
14602
- // node_modules/sisteransi/src/index.js
14602
+ // ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/sisteransi/src/index.js
14603
14603
  var require_src = __commonJS({
14604
- "node_modules/sisteransi/src/index.js"(exports2, module2) {
14604
+ "../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/sisteransi/src/index.js"(exports2, module2) {
14605
14605
  "use strict";
14606
14606
  var ESC = "\x1B";
14607
14607
  var CSI = `${ESC}[`;
@@ -14655,7 +14655,7 @@ var require_src = __commonJS({
14655
14655
  }
14656
14656
  });
14657
14657
 
14658
- // node_modules/@clack/core/dist/index.mjs
14658
+ // ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs
14659
14659
  function DD({ onlyFirst: e2 = false } = {}) {
14660
14660
  const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
14661
14661
  return new RegExp(t, e2 ? void 0 : "g");
@@ -14767,7 +14767,7 @@ function fD({ input: e2 = import_node_process.stdin, output: u2 = import_node_pr
14767
14767
  }
14768
14768
  var import_sisteransi, import_node_process, g, import_node_readline, import_node_stream, import_picocolors2, uD, W, tD, eD, FD, sD, w, N, I, R, C, iD, rD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, A, TD, jD, U, MD, OD, PD, J, LD;
14769
14769
  var init_dist = __esm({
14770
- "node_modules/@clack/core/dist/index.mjs"() {
14770
+ "../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs"() {
14771
14771
  import_sisteransi = __toESM(require_src(), 1);
14772
14772
  import_node_process = require("node:process");
14773
14773
  g = __toESM(require("node:readline"), 1);
@@ -15040,13 +15040,13 @@ var init_dist = __esm({
15040
15040
  }
15041
15041
  });
15042
15042
 
15043
- // node_modules/@clack/prompts/dist/index.mjs
15043
+ // ../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs
15044
15044
  function ce() {
15045
15045
  return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
15046
15046
  }
15047
15047
  var import_node_process2, import_picocolors3, import_sisteransi2, V2, u, le, L2, W2, C2, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U2, K2, b2, G2, ge, ve, xe, Ie, Se, M2, J2, Y2;
15048
15048
  var init_dist2 = __esm({
15049
- "node_modules/@clack/prompts/dist/index.mjs"() {
15049
+ "../../../../pipeline-release-integration/VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs"() {
15050
15050
  init_dist();
15051
15051
  init_dist();
15052
15052
  import_node_process2 = __toESM(require("node:process"), 1);
@@ -15300,7 +15300,7 @@ var VERSION;
15300
15300
  var init_version = __esm({
15301
15301
  "src/version.ts"() {
15302
15302
  "use strict";
15303
- VERSION = "1.7.0";
15303
+ VERSION = "1.7.1";
15304
15304
  }
15305
15305
  });
15306
15306
 
@@ -15525,7 +15525,7 @@ This is advice, not a gate: the user decides when to ship. It is a repository ch
15525
15525
 
15526
15526
  Agent loop: run \`${PUBLIC_CHECK_COMMAND}\`, read \`.viberaven/agent-tasklist.md\`, fix one launch gap, re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch of fixes.`;
15527
15527
  STACK_FRAMING = `Supported stack: AI-built apps that deploy on Vercel and use Supabase, typically from Lovable, Bolt, Cursor, Claude Code or Codex.
15528
- Prefer \`viberaven_check_readiness\` when the VibeRaven MCP server is configured; otherwise \`${PUBLIC_CHECK_COMMAND}\`.`;
15528
+ Use \`viberaven_check_readiness\` only when that tool is in your tool list (the local \`@viberaven/mcp\` server); otherwise \`${PUBLIC_CHECK_COMMAND}\`. The hosted server at \`https://viberaven.dev/mcp\` cannot see the repository: pass it migration SQL or an exact error.`;
15529
15529
  AGENT_NEGATIVE_CONSTRAINTS = `## Working with VibeRaven results
15530
15530
 
15531
15531
  1. Do not call the app "production ready" or "secure" because \`gate.status\` is \`clear\`; it covers the repo checks only.
@@ -15609,7 +15609,7 @@ After \`${PUBLIC_CHECK_COMMAND}\`, read these artifacts in order:
15609
15609
  function buildShortAgentRulesBlock() {
15610
15610
  return wrapViberavenBlock(SHORT_AGENT_RULES_BODY);
15611
15611
  }
15612
- var SHORT_AGENT_RULES_BODY, AGENT_SKILL_FILE, AGENT_SKILL_CONTENT;
15612
+ var SHORT_AGENT_RULES_BODY, AGENT_SKILL_FILE, CODEX_AGENT_SKILL_FILE, AGENT_SKILL_CONTENT;
15613
15613
  var init_agentSkill = __esm({
15614
15614
  "src/commands/agentSkill.ts"() {
15615
15615
  "use strict";
@@ -15628,7 +15628,67 @@ When you change or review Supabase migrations or RLS, auth, Stripe webhooks, env
15628
15628
 
15629
15629
  This is advice, not a gate: the user decides when to ship. Full loop: \`.viberaven/agent-rules.md\`. For requested task proof, follow that guide using \`.viberaven/task-contract.template.json\`.`;
15630
15630
  AGENT_SKILL_FILE = ".claude/skills/viberaven/SKILL.md";
15631
- AGENT_SKILL_CONTENT = '---\nname: viberaven\ndescription: "Use VibeRaven when working on an AI-built app that must ship to real users; when the user asks to ship, deploy, go to production, connect auth, database, payments, RLS, env vars, webhooks, or monitoring; when production-only errors appear; or when the agent needs product context (versions, providers, launch risks) before patching. Fully local: no login, no API key."\n---\n\n# VibeRaven Skill\n\nVibeRaven is the control layer for AI-built products. Its repository checks run fully locally: no login, no API key, no telemetry. They map the product (stack, providers, releases, launch risks) into `.viberaven/` artifacts any agent can read, and list the launch gaps found in the repo, each with the file that caused it. They are advice, not a gate: the user decides when to ship. Repository checks are not live database or security tests. Optional live steps have a separate account, consent and allowance boundary.\n\n## When To Use\n\nUse when the user wants production-ready, launch-ready, "what is missing before ship", a production audit, or a safe deploy for an AI-coded codebase: auth, database, payments, monitoring, error handling, rate limits, env vars. Also use when you need product context before editing: which providers the app uses, what changed between releases, and where the danger zones are.\n\n## The Loop\n\n1. Run `npx -y viberaven check` from the project root. It runs offline checks and prints \u{1F534}/\u{1F7E1}/\u26AA findings with a verdict. Exit code 1 means blockers exist.\n2. Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/context-map.json`.\n3. Run `npx -y viberaven fix` to list gaps with safe automatic repo-code recipes. Preview a permitted fix with `npx -y viberaven fix --gap <id> --dry-run`, then apply it with `npx -y viberaven fix --gap <id>`. Respect `requiresUserAction`: finish other permitted repo fixes first, then ask before changes needing approval, such as enabling RLS without policies.\n4. For gaps without a recipe, use `npx -y viberaven prompt --gap <id>` for focused guidance and patch one gap yourself.\n5. Re-run `npx -y viberaven check` after a batch of fixes, not after every file patch.\n6. `npx -y viberaven --strict` returns the verdict as an exit code for CI when the user wants one.\n\nStop when `gate.status === "clear"` in `.viberaven/gate-result.json`, when only provider or user steps remain, or when the user decides to move on. A clear `gate.status` covers the repo checks only; the user decides when to ship. Local checks need no credentials. Do not ask for secret values or bypass a required approval.\n\n## Provider-aware feature planning\n\nFor feature work, get the relevant versioned pack with `provider_playbook` and `{"provider":"supabase"}` in MCP, or `viberaven provider supabase --json` in the CLI. Use the installed candidate version; this command is not claimed to exist in the published release yet. The pack is the single source for setup, gotchas, dated changes, provider seams, repo-check IDs and planned live checks. Do not duplicate its guidance in this Skill. Unsupported providers return the available packs; do not invent one.\n\nUse the pack to plan the feature, let the person\'s coding agent make the authorized edits, then run relevant repo checks and behavior tests. Pack retrieval is free, offline and needs no account. It is knowledge, not evidence that a check ran: report `not_checked` for unobserved provider state. Read the available pack list from the tool. Better feature results than ordinary Claude remain an experiment, not an established advantage.\n\n## Optional live catalog check (local candidate)\n\nAfter Supabase work, when actual provider metadata is relevant, offer `viberaven_live_start` with the exact project reference and selected schemas, or `viberaven live start --provider supabase --project <ref> --schemas public --json`. This only saves the scope locally. Use the installed candidate, not an assumed published version. Vercel live reads remain blocked until a metadata-only transport is available.\n\nExplain the named project/schema read and get the person\'s approval before `viberaven_live_resume` with `approvedRead: true` or `viberaven live resume --id <stepId> --approve-read --json`. The live-enabled account API must be configured. Show sign-in/payment action URLs to the person; keep the same step ID afterward. The provider token belongs in the hidden local terminal prompt, never in chat, MCP arguments or a command flag. An MCP resume without a locally stored encrypted credential returns instructions to continue in the terminal. Tokens are session-only by default; the person may choose Windows DPAPI persistence using `--persist-token` in their terminal.\n\nCatalog facts returned by the local tool may contain schema/object names and enter the connected coding agent\'s context. The account API receives project/scope IDs and bounded outcome counts, not catalog facts or provider credentials. A complete scoped result consumes one live step, including a detected mismatch; blocked or incomplete checks release the reservation. Local checks, plans and status remain free without login. `viberaven_live_status` reads the saved result; `viberaven_live_disconnect` removes this task\'s stored provider credential and evidence. A metadata result does not establish row-level behavior or completion of the feature; run the application\'s tests separately.\n\n## Studio\n\n`npx -y viberaven` opens the local Studio: the product auto-mapped (stack, providers, git releases, launch verdict), drag-context into agent chat, provider MCP connect, and release diffs. Suggest it when the user wants to see or control the product rather than read terminal output.\n\n## MCP\n\nWhen the VibeRaven MCP server is configured, prefer `viberaven_check_readiness`, then `viberaven_heal_apply` for supported safe repo-code fixes, then `viberaven_verify`. Before `npm install` of a new package, call `viberaven_validate_npm_package`.\n\n## Vercel + Supabase\n\n```bash\nnpx -y viberaven audit --vercel-supabase\n```\n\nCheck RLS, service-role exposure, and pooler ports `5432` / `6543` before making launch claims.\n\n## Local task proof onboarding (local candidate)\n\nFor requested feature proof, follow the shared `.viberaven/agent-rules.md` workflow installed by `init` for both Claude and Codex. Start from `.viberaven/task-contract.template.json`: copy it to a task-specific file, replace the request placeholder from the actual user\'s request, and fill testable acceptance conditions, preservation conditions and actual repository-relative scope. It is a schema-valid draft, not evidence or an executable task. Mark it ready only after review. Ready contracts are immutable; changed requirements need a newly reviewed task.\n\nThe shared guide leads through local `work start` with the selected CLI/access, a reviewed `work prepare` plan outside the project BEFORE editing, direct authorized coding-agent edits, then authorized `work verify` and `work report` with the same task ID. No `work resume`, login or account is needed for this local route. Keep the useful free check/fix/recheck loop. Unsupported provider/browser behavior stays `not_checked`; a report exit code of 0 means export succeeded, so inspect its outcome. The sections below describe optional account execution and the existing independent adapters in detail. Onboarding does not enable `init --github` or upload a report to its separate trusted static-check workflow.\n\n## Optional account-linked work (local candidate)\n\nOnly when the user chooses account-linked agent execution, first agree on one task contract: `schemaVersion: 1`, `status: "ready"`, the exact `request`, `acceptance` and `mustNotBreak` lists of `{id, description}`, and `scope` with `environment`, relative repository `paths`, and `providers`. Use `environment: "local"` and an empty provider list for local work. Do not include credentials or shell commands. Choose the person\'s installed agent; `ask` plans and `approve` permits local edits through the existing engine.\n\nSave it with `viberaven work start --contract task.json --cli codex --access approve --json`, or `viberaven_work_start` with the `contract` object. Start and status are local and need no login. Request-only start remains available but creates a draft that cannot execute. Before a remote session exists, upgrade the same draft with `viberaven work resume --id <taskId> --contract task.json --json`, keeping the original request. Ready definitions are immutable. Provider, preview and test-mode contracts can be saved for planning; their execution is not implemented in this candidate.\n\nShow the saved acceptance conditions, preservation conditions and scope to the person. Only after their approval, use `viberaven work resume --id <taskId> --approve-contract --json`, or `viberaven_work_resume` with `approvedContract: true`. Approval is tied to the exact contract hash. Explain that resume shares the short request and redacted results with the VibeRaven account; the full contract stays in the local engine context. Keep the same task ID and approved definition through sign-in, quota/payment and interrupted delivery. Show any action URL to the person; never authorize sign-in or payment for them. The matching work-enabled backend is required.\n\nRead the receipt with `viberaven work status --id <taskId> --json` or `viberaven_work_status`. Report execution state separately from condition evidence: an agent\'s successful exit, checkout return or account connection does not prove the task is complete. Conditions remain `not_checked` unless the separately prepared independent local verifier observes them. A historical passing receipt becomes stale after a source change; read current status before claiming it still applies. Review an interrupted or legacy task rather than starting a duplicate to hide failure. Scope paths describe intent, not an enforced filesystem sandbox.\n\nThis uses the person\'s local coding agent and computer, not an included hosted model. Free checks and direct agent repairs remain available without an account. Use the installed candidate; do not assume these contract commands exist in the published release.\n\n## Independent local observations (local candidate)\n\nBefore the coding agent edits an existing Node module, propose a declarative verification plan for the agreed task conditions. Have the person review expected behavior independently of the implementation. Use `viberaven_work_prepare` with `{taskId, verification}` or `viberaven work prepare --id <taskId> --verification /outside/project/plan.json --json`. This freezes dirty/untracked baseline bytes and the plan outside the project. The contract and plan are immutable; do not weaken them after seeing a failure.\n\nThe first plan format is `{schemaVersion: 1, checks: [...]}`. A module check has `id`, `conditionId`, `adapter: "module"`, `module` (relative path), `export` and `cases: [{args: [...], expected: <JSON>}]`. Use an existing baseline module and concrete expected values. A preservation check can instead use `adapter: "node-test"`, `entry` and `frozenPaths` for original test/helpers. An HTTP check uses `adapter: "http"`, an existing `module`/`export` returning an unlistened Node HTTP server, and sequential `cases: [{request: {method, path, headers?, body?}, expected: {status, json}}]` (or `text` instead of `json`). Only synthetic fixture headers and origin-relative paths are supported; expected values stay outside the container. The CLI accepts a bounded JSON file; MCP inline transport is limited to 4800 UTF-8 bytes. These are observations, not shell commands or supplied proof.\n\nAfter the authorized edit, run `viberaven_work_verify` with `approvedVerification: true` or `viberaven work verify --id <taskId> --approve-verification --json` only after the person approves the reviewed plan\'s isolated execution. This needs the local pinned Docker image, but no account, payment, provider token or network. It neither runs the coding agent again nor installs dependencies. Status returns each condition as `checked` with its outcome, or `not_checked` with a reason, plus snapshot/provenance. Read failures, uncovered conditions, source-scope changes and exclusions before reporting completion.\n\nSupport is bounded to Node JS/erasable TS module observations, local HTTP loopback and frozen Node preservation tests. HTTP observations share one server per check inside network-isolated Docker; they do not prove database persistence, real authentication, browser or provider flows. Unsupported dependencies, secrets/excluded required inputs or unavailable Docker do not establish completion. The plan\'s quality and coverage remain review decisions; do not invent a benchmark advantage from these checks.\n\n## Local proof export (local candidate)\n\nAfter reading current status, use `viberaven work report --id <taskId>` for Markdown, add `--json` for the structured report plus Markdown, or call `viberaven_work_report` with `{taskId}`. This reads the same immutable verifier state and current source freshness as status. It performs no login, network call, Docker run, agent execution or file write. Export goes to stdout only. A successful export exits 0 even when evidence is `failed` or `not_checked`; read `outcome`, condition counts and scope, not the export exit code. Missing or corrupt saved state fails with nonzero exit and no passing report.\n\nThe standalone MCP report route requires an already installed `@viberaven/cli` with the exact MCP version, resolvable from the MCP installation. For a local candidate, the host may set `VIBERAVEN_CLI_PATH` to the absolute matching `dist/cli.js` outside the task repository; this is host configuration, never an MCP argument. Report calls execute that installed CLI directly and never run npm or download/install packages. If the matching CLI is unavailable, reporting fails until it is installed/configured separately.\n\nThe local draft includes the request, acceptance and must-not-break conditions, checked/passed/failed/unknown counts, open items, scope/exclusion counts, snapshot hashes, descriptive HEAD SHAs and receipt hashes. Only current independently checked conditions and checked scope can yield `passed`. Execution state is separate. HEAD does not imply a clean source tree; actual-byte snapshot hashes identify proof. Human text is redacted and explicitly marked if truncated; the contract hash binds the original definition. No source, response bodies, output streams, private path lists, account actions or artifact file references are exported. Pattern redaction cannot find every secret: have a person review the entire draft before sharing.\n\nDo not treat this local report as authoritative CI or feed it into the trusted GitHub static-check comment path. Export posts no comment, proves no deployment/provider state and does not complete the L1.3 live GitHub pilot. An explicitly authorized live finding/fix/update-same-comment sequence remains separate future work.\n\n## Boundaries\n\n- Repo-code edits never prove provider dashboard state. Billing, DNS, webhooks, quotas, and live provider verification need dashboard or MCP proof; say so explicitly.\n- Never ask for passwords, tokens, cookies, or secret values.\n- `check`, `scan`, `audit` and `clean --plan` write only under `.viberaven/`. `init` and `fix --gap` edit repo files: `init` writes agent rule files such as `AGENTS.md` and adds `viberaven:*` scripts to `package.json` (preview with `init --dry-run`), and `fix --gap` edits code (preview with `npx -y viberaven fix --gap <id> --dry-run`).\n\nNext skill: `architecture-context` when feature work is vague and product questions are missing; `architecture-plan` once those questions are answered; `what-broke` when a working app regressed; `production-context` to record durable risk in `.viberaven/production-context.md`; `go-live` when the user wants the app pushed to GitHub and live on Vercel.\n';
15631
+ CODEX_AGENT_SKILL_FILE = ".agents/skills/viberaven/SKILL.md";
15632
+ AGENT_SKILL_CONTENT = '---\nname: viberaven\ndescription: "Use for Supabase RLS and migrations, grants and 42501 errors, a service role key in client code, Vercel env vars, Stripe webhook signature checks, or a Vercel + Supabase review before deploy. Choose local repository checks or hosted SQL/error guidance by the actual tools available. Local checks are free: no login or API key."\n---\n\n# VibeRaven Skill\n\nVibeRaven is the control layer for AI-built products. Its repository checks run fully locally: no login, no API key, no telemetry. They map the product (stack, providers, releases, launch risks) into `.viberaven/` artifacts any agent can read, and list the launch gaps found in the repo, each with the file that caused it. They are advice, not a gate: the user decides when to ship. Repository checks are not live database or security tests. Optional live steps have a separate account, consent and allowance boundary.\n\n## When To Use\n\nUse when the user wants production-ready, launch-ready, "what is missing before ship", a production audit, or a safe deploy for an AI-coded codebase: auth, database, payments, monitoring, error handling, rate limits, env vars. Also use when you need product context before editing: which providers the app uses, what changed between releases, and where the danger zones are.\n\n## The Loop\n\n1. Run `npx -y viberaven check` from the project root. It runs offline checks and prints \u{1F534}/\u{1F7E1}/\u26AA findings with a verdict. Exit code 1 means blockers exist.\n2. Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/context-map.json`.\n3. Run `npx -y viberaven fix` to list gaps with safe automatic repo-code recipes. Preview a permitted fix with `npx -y viberaven fix --gap <id> --dry-run`, then apply it with `npx -y viberaven fix --gap <id>`. Respect `requiresUserAction`: finish other permitted repo fixes first, then ask before changes needing approval, such as enabling RLS without policies.\n4. For gaps without a recipe, use `npx -y viberaven prompt --gap <id>` for focused guidance and patch one gap yourself.\n5. Re-run `npx -y viberaven check` after a batch of fixes, not after every file patch.\n6. `npx -y viberaven --strict` returns the verdict as an exit code for CI when the user wants one.\n\nStop when `gate.status === "clear"` in `.viberaven/gate-result.json`, when only provider or user steps remain, or when the user decides to move on. A clear `gate.status` covers the repo checks only; the user decides when to ship. Local checks need no credentials. Do not ask for secret values or bypass a required approval.\n\n## Provider-aware feature planning\n\nFor feature work, get the relevant versioned pack with `provider_playbook` and `{"provider":"supabase"}` in MCP, or `viberaven provider supabase --json` in the CLI. Use the installed candidate version; this command is not claimed to exist in the published release yet. The pack is the single source for setup, gotchas, dated changes, provider seams, repo-check IDs and planned live checks. Do not duplicate its guidance in this Skill. Unsupported providers return the available packs; do not invent one.\n\nUse the pack to plan the feature, let the person\'s coding agent make the authorized edits, then run relevant repo checks and behavior tests. Pack retrieval is free, offline and needs no account. It is knowledge, not evidence that a check ran: report `not_checked` for unobserved provider state. Read the available pack list from the tool. Better feature results than ordinary Claude remain an experiment, not an established advantage.\n\n## Optional local behavioral RLS test (local candidate)\n\nAfter reviewing a Supabase migration or ownership policy, optionally run already reviewed permissions scenarios. Derive expectations from intended access for owner, other_user and anon; do not rewrite expectations to match failures. Prefer `defaultGrants: none` in the matrix with actual GRANT SQL for new schemas; disclose `supabase-legacy` defaults otherwise.\n\nIf `viberaven_rls_test` is in your tool list, call it with `{matrix: "PATH", cwd: "PROJECT"}` and optional `migrations`. Otherwise use the installed candidate CLI: `viberaven rls-test --matrix PATH --json`. Read each Pass, Fail and Unknown with its evidence and coverage; Unknown is inconclusive. This is an experimental PGlite imitation, not live Supabase proof or a security claim.\n\nThe optional PGlite dependency must already be installed next to the CLI that runs the test. The local MCP host needs a matching installed CLI with PGlite alongside it; a dependency installed only in the target project may be unavailable to that host. Missing setup is a tool error with CLI guidance. Do not install dependencies without normal user authorization. This optional local test needs no login, provider access or payment; keep the free check/fix/recheck loop.\n\n## Optional live catalog check (local candidate)\n\nAfter Supabase work, when actual provider metadata is relevant, offer `viberaven_live_start` with the exact project reference and selected schemas, or `viberaven live start --provider supabase --project <ref> --schemas public --json`. This only saves the scope locally. Use the installed candidate, not an assumed published version. Vercel live reads remain blocked until a metadata-only transport is available.\n\nExplain the named project/schema read and get the person\'s approval before `viberaven_live_resume` with `approvedRead: true` or `viberaven live resume --id <stepId> --approve-read --json`. The live-enabled account API must be configured. Show sign-in/payment action URLs to the person; keep the same step ID afterward. The provider token belongs in the hidden local terminal prompt, never in chat, MCP arguments or a command flag. An MCP resume without a locally stored encrypted credential returns instructions to continue in the terminal. Tokens are session-only by default; the person may choose Windows DPAPI persistence using `--persist-token` in their terminal.\n\nCatalog facts returned by the local tool may contain schema/object names and enter the connected coding agent\'s context. The account API receives project/scope IDs and bounded outcome counts, not catalog facts or provider credentials. A complete scoped result consumes one live step, including a detected mismatch; blocked or incomplete checks release the reservation. Local checks, plans and status remain free without login. `viberaven_live_status` reads the saved result; `viberaven_live_disconnect` removes this task\'s stored provider credential and evidence. A metadata result does not establish row-level behavior or completion of the feature; run the application\'s tests separately.\n\n## Studio\n\n`npx -y viberaven` opens the local Studio: the product auto-mapped (stack, providers, git releases, launch verdict), drag-context into agent chat, provider MCP connect, and release diffs. Suggest it when the user wants to see or control the product rather than read terminal output.\n\n## MCP\n\nChoose by the tools you actually have, not by the server\'s name. Two different servers are both called VibeRaven:\n\n- If `viberaven_check_readiness` is in your tool list (the local `@viberaven/mcp` server, which runs the CLI in a repository), call it on the project root you are working in, then `viberaven_heal_apply` for supported safe repo-code fixes, then `viberaven_verify`. Before `npm install` of a new package, call `viberaven_validate_npm_package`.\n- If you only have the hosted server (`https://viberaven.dev/mcp`, with tools such as `check_supabase_migrations` and `search_guides`), it cannot see the repository. Pass it the migration SQL or the exact error text, and say that the result covers only what you passed. For a repository check, run `npx -y viberaven check` in the project root.\n- With no VibeRaven tools, run `npx -y viberaven check` for a Vercel + Supabase repository. Answer a general question without running anything.\n\n## Vercel + Supabase\n\n```bash\nnpx -y viberaven audit --vercel-supabase\n```\n\nCheck RLS, service-role exposure, and pooler ports `5432` / `6543` before making launch claims.\n\n## Local task proof onboarding (local candidate)\n\nFor requested feature proof, follow the shared `.viberaven/agent-rules.md` workflow installed by `init` for both Claude and Codex. Start from `.viberaven/task-contract.template.json`: copy it to a task-specific file, replace the request placeholder from the actual user\'s request, and fill testable acceptance conditions, preservation conditions and actual repository-relative scope. It is a schema-valid draft, not evidence or an executable task. Mark it ready only after review. Ready contracts are immutable; changed requirements need a newly reviewed task.\n\nThe shared guide leads through local `work start` with the selected CLI/access, a reviewed `work prepare` plan outside the project BEFORE editing, direct authorized coding-agent edits, then authorized `work verify` and `work report` with the same task ID. No `work resume`, login or account is needed for this local route. Keep the useful free check/fix/recheck loop. Unsupported provider/browser behavior stays `not_checked`; a report exit code of 0 means export succeeded, so inspect its outcome. The sections below describe optional account execution and the existing independent adapters in detail. Onboarding does not enable `init --github` or upload a report to its separate trusted static-check workflow.\n\n## Optional account-linked work (local candidate)\n\nOnly when the user chooses account-linked agent execution, first agree on one task contract: `schemaVersion: 1`, `status: "ready"`, the exact `request`, `acceptance` and `mustNotBreak` lists of `{id, description}`, and `scope` with `environment`, relative repository `paths`, and `providers`. Use `environment: "local"` and an empty provider list for local work. Do not include credentials or shell commands. Choose the person\'s installed agent; `ask` plans and `approve` permits local edits through the existing engine.\n\nSave it with `viberaven work start --contract task.json --cli codex --access approve --json`, or `viberaven_work_start` with the `contract` object. Start and status are local and need no login. Request-only start remains available but creates a draft that cannot execute. Before a remote session exists, upgrade the same draft with `viberaven work resume --id <taskId> --contract task.json --json`, keeping the original request. Ready definitions are immutable. Provider, preview and test-mode contracts can be saved for planning; their execution is not implemented in this candidate.\n\nShow the saved acceptance conditions, preservation conditions and scope to the person. Only after their approval, use `viberaven work resume --id <taskId> --approve-contract --json`, or `viberaven_work_resume` with `approvedContract: true`. Approval is tied to the exact contract hash. Explain that resume shares the short request and redacted results with the VibeRaven account; the full contract stays in the local engine context. Keep the same task ID and approved definition through sign-in, quota/payment and interrupted delivery. Show any action URL to the person; never authorize sign-in or payment for them. The matching work-enabled backend is required.\n\nRead the receipt with `viberaven work status --id <taskId> --json` or `viberaven_work_status`. Report execution state separately from condition evidence: an agent\'s successful exit, checkout return or account connection does not prove the task is complete. Conditions remain `not_checked` unless the separately prepared independent local verifier observes them. A historical passing receipt becomes stale after a source change; read current status before claiming it still applies. Review an interrupted or legacy task rather than starting a duplicate to hide failure. Scope paths describe intent, not an enforced filesystem sandbox.\n\nThis uses the person\'s local coding agent and computer, not an included hosted model. Free checks and direct agent repairs remain available without an account. Use the installed candidate; do not assume these contract commands exist in the published release.\n\n## Independent local observations (local candidate)\n\nBefore the coding agent edits an existing Node module, propose a declarative verification plan for the agreed task conditions. Have the person review expected behavior independently of the implementation. Use `viberaven_work_prepare` with `{taskId, verification}` or `viberaven work prepare --id <taskId> --verification /outside/project/plan.json --json`. This freezes dirty/untracked baseline bytes and the plan outside the project. The contract and plan are immutable; do not weaken them after seeing a failure.\n\nThe first plan format is `{schemaVersion: 1, checks: [...]}`. A module check has `id`, `conditionId`, `adapter: "module"`, `module` (relative path), `export` and `cases: [{args: [...], expected: <JSON>}]`. Use an existing baseline module and concrete expected values. A preservation check can instead use `adapter: "node-test"`, `entry` and `frozenPaths` for original test/helpers. An HTTP check uses `adapter: "http"`, an existing `module`/`export` returning an unlistened Node HTTP server, and sequential `cases: [{request: {method, path, headers?, body?}, expected: {status, json}}]` (or `text` instead of `json`). Only synthetic fixture headers and origin-relative paths are supported; expected values stay outside the container. The CLI accepts a bounded JSON file; MCP inline transport is limited to 4800 UTF-8 bytes. These are observations, not shell commands or supplied proof.\n\nAfter the authorized edit, run `viberaven_work_verify` with `approvedVerification: true` or `viberaven work verify --id <taskId> --approve-verification --json` only after the person approves the reviewed plan\'s isolated execution. This needs the local pinned Docker image, but no account, payment, provider token or network. It neither runs the coding agent again nor installs dependencies. Status returns each condition as `checked` with its outcome, or `not_checked` with a reason, plus snapshot/provenance. Read failures, uncovered conditions, source-scope changes and exclusions before reporting completion.\n\nSupport is bounded to Node JS/erasable TS module observations, local HTTP loopback and frozen Node preservation tests. HTTP observations share one server per check inside network-isolated Docker; they do not prove database persistence, real authentication, browser or provider flows. Unsupported dependencies, secrets/excluded required inputs or unavailable Docker do not establish completion. The plan\'s quality and coverage remain review decisions; do not invent a benchmark advantage from these checks.\n\n## Local proof export (local candidate)\n\nAfter reading current status, use `viberaven work report --id <taskId>` for Markdown, add `--json` for the structured report plus Markdown, or call `viberaven_work_report` with `{taskId}`. This reads the same immutable verifier state and current source freshness as status. It performs no login, network call, Docker run, agent execution or file write. Export goes to stdout only. A successful export exits 0 even when evidence is `failed` or `not_checked`; read `outcome`, condition counts and scope, not the export exit code. Missing or corrupt saved state fails with nonzero exit and no passing report.\n\nThe standalone MCP report route requires an already installed `@viberaven/cli` with the exact MCP version, resolvable from the MCP installation. For a local candidate, the host may set `VIBERAVEN_CLI_PATH` to the absolute matching `dist/cli.js` outside the task repository; this is host configuration, never an MCP argument. Report calls execute that installed CLI directly and never run npm or download/install packages. If the matching CLI is unavailable, reporting fails until it is installed/configured separately.\n\nThe local draft includes the request, acceptance and must-not-break conditions, checked/passed/failed/unknown counts, open items, scope/exclusion counts, snapshot hashes, descriptive HEAD SHAs and receipt hashes. Only current independently checked conditions and checked scope can yield `passed`. Execution state is separate. HEAD does not imply a clean source tree; actual-byte snapshot hashes identify proof. Human text is redacted and explicitly marked if truncated; the contract hash binds the original definition. No source, response bodies, output streams, private path lists, account actions or artifact file references are exported. Pattern redaction cannot find every secret: have a person review the entire draft before sharing.\n\nDo not treat this local report as authoritative CI or feed it into the trusted GitHub static-check comment path. Export posts no comment, proves no deployment/provider state and does not complete the L1.3 live GitHub pilot. An explicitly authorized live finding/fix/update-same-comment sequence remains separate future work.\n\n## Boundaries\n\n- Repo-code edits never prove provider dashboard state. Billing, DNS, webhooks, quotas, and live provider verification need dashboard or MCP proof; say so explicitly.\n- Never ask for passwords, tokens, cookies, or secret values.\n- `check`, `scan`, `audit` and `clean --plan` write only under `.viberaven/`. `init` and `fix --gap` edit repo files: `init` writes agent rule files such as `AGENTS.md` and adds `viberaven:*` scripts to `package.json` (preview with `init --dry-run`), and `fix --gap` edits code (preview with `npx -y viberaven fix --gap <id> --dry-run`).\n\nNext skill: `architecture-context` when feature work is vague and product questions are missing; `architecture-plan` once those questions are answered; `what-broke` when a working app regressed; `production-context` to record durable risk in `.viberaven/production-context.md`; `go-live` when the user wants the app pushed to GitHub and live on Vercel.\n';
15633
+ }
15634
+ });
15635
+
15636
+ // src/commands/initAgentSkill.ts
15637
+ async function initAgentSkill(options) {
15638
+ const path2 = (0, import_node_path18.join)(options.cwd, options.file);
15639
+ const parents = options.file.split("/").slice(0, -1);
15640
+ const regularParents = async (create) => {
15641
+ let parent = options.cwd;
15642
+ for (const part of parents) {
15643
+ parent = (0, import_node_path18.join)(parent, part);
15644
+ let stat12 = await (0, import_promises12.lstat)(parent).catch((error) => {
15645
+ if (error.code === "ENOENT") return null;
15646
+ throw error;
15647
+ });
15648
+ if (!stat12 && create) {
15649
+ try {
15650
+ await (0, import_promises12.mkdir)(parent);
15651
+ } catch (error) {
15652
+ if (error.code !== "EEXIST") throw error;
15653
+ }
15654
+ stat12 = await (0, import_promises12.lstat)(parent);
15655
+ }
15656
+ if (!stat12) return true;
15657
+ if (!stat12.isDirectory() || stat12.isSymbolicLink()) return false;
15658
+ }
15659
+ return true;
15660
+ };
15661
+ const existing = async () => {
15662
+ if (!await regularParents(false)) return "preserved";
15663
+ try {
15664
+ const stat12 = await (0, import_promises12.lstat)(path2);
15665
+ if (!stat12.isFile() || stat12.isSymbolicLink()) return "preserved";
15666
+ const content = await (0, import_promises12.readFile)(path2, "utf8");
15667
+ return content.replace(/\r\n/g, "\n") === AGENT_SKILL_CONTENT ? "unchanged" : "preserved";
15668
+ } catch (error) {
15669
+ if (error.code === "ENOENT") return null;
15670
+ throw error;
15671
+ }
15672
+ };
15673
+ const found = await existing();
15674
+ if (found) return found;
15675
+ if (options.dryRun) return "created";
15676
+ if (!await regularParents(true)) return "preserved";
15677
+ try {
15678
+ await (0, import_promises12.writeFile)(path2, AGENT_SKILL_CONTENT, { encoding: "utf8", flag: "wx" });
15679
+ return "created";
15680
+ } catch (error) {
15681
+ if (error.code !== "EEXIST") throw error;
15682
+ return await existing() ?? "preserved";
15683
+ }
15684
+ }
15685
+ var import_promises12, import_node_path18;
15686
+ var init_initAgentSkill = __esm({
15687
+ "src/commands/initAgentSkill.ts"() {
15688
+ "use strict";
15689
+ import_promises12 = require("node:fs/promises");
15690
+ import_node_path18 = require("node:path");
15691
+ init_agentSkill();
15632
15692
  }
15633
15693
  });
15634
15694
 
@@ -15715,25 +15775,25 @@ async function initCursorRulesPack(options) {
15715
15775
  const pack = buildCursorRulesPack();
15716
15776
  for (const rule of pack) {
15717
15777
  const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
15718
- const path2 = (0, import_node_path18.join)(options.cwd, file);
15778
+ const path2 = (0, import_node_path19.join)(options.cwd, file);
15719
15779
  const existing = await readExistingFile(path2);
15720
15780
  const changed = !existing.exists || existing.content !== rule.content;
15721
15781
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
15722
15782
  if (!options.dryRun && changed) {
15723
- await (0, import_promises12.mkdir)((0, import_node_path18.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15724
- await (0, import_promises12.writeFile)(path2, rule.content, "utf-8");
15783
+ await (0, import_promises13.mkdir)((0, import_node_path19.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15784
+ await (0, import_promises13.writeFile)(path2, rule.content, "utf-8");
15725
15785
  }
15726
15786
  results.push({ target: "cursor", file, path: path2, action });
15727
15787
  }
15728
- const legacyPath = (0, import_node_path18.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15788
+ const legacyPath = (0, import_node_path19.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15729
15789
  if (!options.dryRun && await fileExists(legacyPath)) {
15730
- await (0, import_promises12.rm)(legacyPath, { force: true });
15790
+ await (0, import_promises13.rm)(legacyPath, { force: true });
15731
15791
  }
15732
15792
  return results;
15733
15793
  }
15734
15794
  async function readExistingFile(path2) {
15735
15795
  try {
15736
- return { exists: true, content: await (0, import_promises12.readFile)(path2, "utf-8") };
15796
+ return { exists: true, content: await (0, import_promises13.readFile)(path2, "utf-8") };
15737
15797
  } catch (error) {
15738
15798
  if (isFileNotFoundError(error)) {
15739
15799
  return { exists: false, content: "" };
@@ -15743,7 +15803,7 @@ async function readExistingFile(path2) {
15743
15803
  }
15744
15804
  async function fileExists(path2) {
15745
15805
  try {
15746
- await (0, import_promises12.access)(path2);
15806
+ await (0, import_promises13.access)(path2);
15747
15807
  return true;
15748
15808
  } catch {
15749
15809
  return false;
@@ -15752,12 +15812,12 @@ async function fileExists(path2) {
15752
15812
  function isFileNotFoundError(error) {
15753
15813
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15754
15814
  }
15755
- var import_promises12, import_node_path18, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15815
+ var import_promises13, import_node_path19, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15756
15816
  var init_cursorRulesPack = __esm({
15757
15817
  "src/commands/cursorRulesPack.ts"() {
15758
15818
  "use strict";
15759
- import_promises12 = require("node:fs/promises");
15760
- import_node_path18 = require("node:path");
15819
+ import_promises13 = require("node:fs/promises");
15820
+ import_node_path19 = require("node:path");
15761
15821
  init_commands();
15762
15822
  CURSOR_RULES_DIR = ".cursor/rules";
15763
15823
  LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
@@ -15770,7 +15830,7 @@ function renderAgentRulesForTarget(target) {
15770
15830
  if (target === "claude") {
15771
15831
  return ["@AGENTS.md", "", buildShortAgentRulesBlock()].join("\n");
15772
15832
  }
15773
- if (target === "skill") {
15833
+ if (target === "skill" || target === "codexSkill") {
15774
15834
  return AGENT_SKILL_CONTENT;
15775
15835
  }
15776
15836
  if (target === "agentGuide") {
@@ -15826,6 +15886,7 @@ var init_agentTargets = __esm({
15826
15886
  missionMap: { file: ".viberaven/mission-map.md", aliases: ["mission-map"] },
15827
15887
  agentGuide: { file: ".viberaven/agent-rules.md", aliases: ["agent-rules"] },
15828
15888
  skill: { file: AGENT_SKILL_FILE, aliases: ["claude-skill"] },
15889
+ codexSkill: { file: CODEX_AGENT_SKILL_FILE, aliases: ["codex-skill"] },
15829
15890
  devin: { file: ".devin/rules/viberaven.md" },
15830
15891
  windsurf: { file: ".windsurf/rules/viberaven.md" },
15831
15892
  cline: { file: ".clinerules/viberaven.md" },
@@ -15842,7 +15903,8 @@ var init_agentTargets = __esm({
15842
15903
  "agentContext",
15843
15904
  "missionMap",
15844
15905
  "agentGuide",
15845
- "skill"
15906
+ "skill",
15907
+ "codexSkill"
15846
15908
  ];
15847
15909
  EXTENDED_AGENT_RULE_TARGETS = [
15848
15910
  "cursorLegacy",
@@ -15862,17 +15924,17 @@ var init_agentTargets = __esm({
15862
15924
  ...(AGENT_RULE_TARGETS[target].aliases ?? []).map((alias) => [alias, target])
15863
15925
  ]);
15864
15926
  AGENT_RULE_TARGET_ALIASES = new Map(AGENT_RULE_TARGET_ALIAS_ENTRIES);
15865
- VALID_AGENT_RULES_TARGET_TEXT = "all, codex, claude, cursor, cursor-legacy, copilot, github-copilot, gemini, agent-context, mission-map, agent-rules, claude-skill, devin, windsurf, cline, roo, junie, zed";
15927
+ VALID_AGENT_RULES_TARGET_TEXT = "all, codex, claude, cursor, cursor-legacy, copilot, github-copilot, gemini, agent-context, mission-map, agent-rules, claude-skill, codex-skill, devin, windsurf, cline, roo, junie, zed";
15866
15928
  }
15867
15929
  });
15868
15930
 
15869
15931
  // src/commands/seedPackageJsonScripts.ts
15870
15932
  async function seedPackageJsonScripts(options) {
15871
- const packageJsonPath = (0, import_node_path19.join)(options.cwd, "package.json");
15933
+ const packageJsonPath = (0, import_node_path20.join)(options.cwd, "package.json");
15872
15934
  if (!(0, import_node_fs10.existsSync)(packageJsonPath)) {
15873
15935
  return null;
15874
15936
  }
15875
- const raw = await (0, import_promises13.readFile)(packageJsonPath, "utf-8");
15937
+ const raw = await (0, import_promises14.readFile)(packageJsonPath, "utf-8");
15876
15938
  let pkg;
15877
15939
  try {
15878
15940
  pkg = JSON.parse(raw);
@@ -15902,17 +15964,17 @@ async function seedPackageJsonScripts(options) {
15902
15964
  pkg.scripts = scripts;
15903
15965
  const output = `${JSON.stringify(pkg, null, 2)}
15904
15966
  `;
15905
- await (0, import_promises13.writeFile)(packageJsonPath, output, "utf-8");
15967
+ await (0, import_promises14.writeFile)(packageJsonPath, output, "utf-8");
15906
15968
  }
15907
15969
  return { action: "updated", added, skipped, changed: true };
15908
15970
  }
15909
- var import_node_fs10, import_promises13, import_node_path19, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15971
+ var import_node_fs10, import_promises14, import_node_path20, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15910
15972
  var init_seedPackageJsonScripts = __esm({
15911
15973
  "src/commands/seedPackageJsonScripts.ts"() {
15912
15974
  "use strict";
15913
15975
  import_node_fs10 = require("node:fs");
15914
- import_promises13 = require("node:fs/promises");
15915
- import_node_path19 = require("node:path");
15976
+ import_promises14 = require("node:fs/promises");
15977
+ import_node_path20 = require("node:path");
15916
15978
  init_commands();
15917
15979
  VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
15918
15980
  "viberaven:check": PUBLIC_CHECK_COMMAND
@@ -16058,19 +16120,19 @@ var init_taskContract = __esm({
16058
16120
 
16059
16121
  // src/commands/initTaskContract.ts
16060
16122
  async function initTaskContractTemplate(options) {
16061
- const path2 = (0, import_node_path20.join)(options.cwd, TASK_CONTRACT_TEMPLATE_FILE);
16062
- const parent = (0, import_node_path20.dirname)(path2);
16123
+ const path2 = (0, import_node_path21.join)(options.cwd, TASK_CONTRACT_TEMPLATE_FILE);
16124
+ const parent = (0, import_node_path21.dirname)(path2);
16063
16125
  const result = (action) => ({ file: TASK_CONTRACT_TEMPLATE_FILE, path: path2, action });
16064
- const parentStat = await (0, import_promises14.lstat)(parent).catch((error) => {
16126
+ const parentStat = await (0, import_promises15.lstat)(parent).catch((error) => {
16065
16127
  if (error.code === "ENOENT") return null;
16066
16128
  throw error;
16067
16129
  });
16068
16130
  if (parentStat && (!parentStat.isDirectory() || parentStat.isSymbolicLink())) return result("preserved");
16069
16131
  const existing = async () => {
16070
16132
  try {
16071
- const stat12 = await (0, import_promises14.lstat)(path2);
16133
+ const stat12 = await (0, import_promises15.lstat)(path2);
16072
16134
  if (!stat12.isFile() || stat12.isSymbolicLink() || stat12.size !== Buffer.byteLength(TASK_CONTRACT_TEMPLATE_CONTENT, "utf8")) return result("preserved");
16073
- return result((await (0, import_promises14.readFile)(path2)).equals(Buffer.from(TASK_CONTRACT_TEMPLATE_CONTENT, "utf8")) ? "unchanged" : "preserved");
16135
+ return result((await (0, import_promises15.readFile)(path2)).equals(Buffer.from(TASK_CONTRACT_TEMPLATE_CONTENT, "utf8")) ? "unchanged" : "preserved");
16074
16136
  } catch (error) {
16075
16137
  if (error.code === "ENOENT") return null;
16076
16138
  throw error;
@@ -16079,23 +16141,23 @@ async function initTaskContractTemplate(options) {
16079
16141
  const found = await existing();
16080
16142
  if (found) return found;
16081
16143
  if (options.dryRun) return result("created");
16082
- await (0, import_promises14.mkdir)(parent, { recursive: true });
16083
- const checkedParent = await (0, import_promises14.lstat)(parent);
16144
+ await (0, import_promises15.mkdir)(parent, { recursive: true });
16145
+ const checkedParent = await (0, import_promises15.lstat)(parent);
16084
16146
  if (!checkedParent.isDirectory() || checkedParent.isSymbolicLink()) return result("preserved");
16085
16147
  try {
16086
- await (0, import_promises14.writeFile)(path2, TASK_CONTRACT_TEMPLATE_CONTENT, { encoding: "utf8", flag: "wx" });
16148
+ await (0, import_promises15.writeFile)(path2, TASK_CONTRACT_TEMPLATE_CONTENT, { encoding: "utf8", flag: "wx" });
16087
16149
  return result("created");
16088
16150
  } catch (error) {
16089
16151
  if (error.code !== "EEXIST") throw error;
16090
16152
  return await existing() ?? result("preserved");
16091
16153
  }
16092
16154
  }
16093
- var import_promises14, import_node_path20, TASK_CONTRACT_TEMPLATE_FILE, TASK_CONTRACT_TEMPLATE_CONTENT;
16155
+ var import_promises15, import_node_path21, TASK_CONTRACT_TEMPLATE_FILE, TASK_CONTRACT_TEMPLATE_CONTENT;
16094
16156
  var init_initTaskContract = __esm({
16095
16157
  "src/commands/initTaskContract.ts"() {
16096
16158
  "use strict";
16097
- import_promises14 = require("node:fs/promises");
16098
- import_node_path20 = require("node:path");
16159
+ import_promises15 = require("node:fs/promises");
16160
+ import_node_path21 = require("node:path");
16099
16161
  init_taskContract();
16100
16162
  TASK_CONTRACT_TEMPLATE_FILE = ".viberaven/task-contract.template.json";
16101
16163
  TASK_CONTRACT_TEMPLATE_CONTENT = `${JSON.stringify(draftTaskContract(
@@ -16115,22 +16177,18 @@ async function initAgentRules(options) {
16115
16177
  continue;
16116
16178
  }
16117
16179
  const file = AGENT_RULE_TARGETS[target].file;
16118
- const path2 = (0, import_node_path21.join)(options.cwd, file);
16119
- const existing = await readExistingFile2(path2);
16120
- if (target === "skill") {
16121
- const same = existing.content.replace(/\r\n/g, "\n") === AGENT_SKILL_CONTENT;
16122
- if (!options.dryRun && !existing.exists) {
16123
- await (0, import_promises15.mkdir)((0, import_node_path21.dirname)(path2), { recursive: true });
16124
- await (0, import_promises15.writeFile)(path2, AGENT_SKILL_CONTENT, "utf-8");
16125
- }
16126
- results.push({ target, file, path: path2, action: !existing.exists ? "created" : same ? "unchanged" : "preserved" });
16180
+ const path2 = (0, import_node_path22.join)(options.cwd, file);
16181
+ if (target === "skill" || target === "codexSkill") {
16182
+ const action2 = await initAgentSkill({ ...options, file });
16183
+ results.push({ target, file, path: path2, action: action2 });
16127
16184
  continue;
16128
16185
  }
16186
+ const existing = await readExistingFile2(path2);
16129
16187
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
16130
16188
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
16131
16189
  if (!options.dryRun && injected.changed) {
16132
- await (0, import_promises15.mkdir)((0, import_node_path21.dirname)(path2), { recursive: true });
16133
- await (0, import_promises15.writeFile)(path2, injected.content, "utf-8");
16190
+ await (0, import_promises16.mkdir)((0, import_node_path22.dirname)(path2), { recursive: true });
16191
+ await (0, import_promises16.writeFile)(path2, injected.content, "utf-8");
16134
16192
  }
16135
16193
  results.push({ target, file, path: path2, action });
16136
16194
  }
@@ -16144,6 +16202,7 @@ async function initAgentRules(options) {
16144
16202
  function withTargetDependencies(requested) {
16145
16203
  const targets = [...new Set(requested)];
16146
16204
  if (targets.includes("claude") && !targets.includes("skill")) targets.push("skill");
16205
+ if (targets.includes("codex") && !targets.includes("codexSkill")) targets.push("codexSkill");
16147
16206
  const standalone = ["cursor", "agentContext", "missionMap", "agentGuide"];
16148
16207
  if (targets.some((target) => !standalone.includes(target)) && !targets.includes("agentGuide")) {
16149
16208
  targets.push("agentGuide");
@@ -16228,7 +16287,7 @@ function formatAgentRulesInitSummary(output) {
16228
16287
  }
16229
16288
  async function readExistingFile2(path2) {
16230
16289
  try {
16231
- return { exists: true, content: await (0, import_promises15.readFile)(path2, "utf-8") };
16290
+ return { exists: true, content: await (0, import_promises16.readFile)(path2, "utf-8") };
16232
16291
  } catch (error) {
16233
16292
  if (isFileNotFoundError2(error)) {
16234
16293
  return { exists: false, content: "" };
@@ -16239,14 +16298,14 @@ async function readExistingFile2(path2) {
16239
16298
  function isFileNotFoundError2(error) {
16240
16299
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
16241
16300
  }
16242
- var import_promises15, import_node_path21;
16301
+ var import_promises16, import_node_path22;
16243
16302
  var init_initRules = __esm({
16244
16303
  "src/commands/initRules.ts"() {
16245
16304
  "use strict";
16246
- import_promises15 = require("node:fs/promises");
16247
- import_node_path21 = require("node:path");
16305
+ import_promises16 = require("node:fs/promises");
16306
+ import_node_path22 = require("node:path");
16248
16307
  init_agentRulesBlock();
16249
- init_agentSkill();
16308
+ init_initAgentSkill();
16250
16309
  init_agentTargets();
16251
16310
  init_cursorRulesPack();
16252
16311
  init_seedPackageJsonScripts();
@@ -16509,7 +16568,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
16509
16568
  Ie(`${import_picocolors5.default.bold("VibeRaven")} ${import_picocolors5.default.dim(VERSION)}`);
16510
16569
  const cwd = await resolveWorkspaceRoot(startDir);
16511
16570
  const artifactsAt = await findArtifactsWorkspace(startDir);
16512
- if (artifactsAt && (0, import_node_path22.resolve)(artifactsAt) !== (0, import_node_path22.resolve)(startDir)) {
16571
+ if (artifactsAt && (0, import_node_path23.resolve)(artifactsAt) !== (0, import_node_path23.resolve)(startDir)) {
16513
16572
  M2.message(import_picocolors5.default.dim(`Using scan from: ${artifactsAt}`));
16514
16573
  } else {
16515
16574
  M2.message(import_picocolors5.default.dim(`Project folder: ${cwd}`));
@@ -16568,11 +16627,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
16568
16627
  }
16569
16628
  Se(import_picocolors5.default.dim("Run viberaven anytime for the interactive menu."));
16570
16629
  }
16571
- var import_node_path22, import_picocolors5;
16630
+ var import_node_path23, import_picocolors5;
16572
16631
  var init_runInteractive = __esm({
16573
16632
  "src/tui/runInteractive.ts"() {
16574
16633
  "use strict";
16575
- import_node_path22 = require("node:path");
16634
+ import_node_path23 = require("node:path");
16576
16635
  init_dist2();
16577
16636
  import_picocolors5 = __toESM(require_picocolors());
16578
16637
  init_config();
@@ -16597,18 +16656,18 @@ var init_runInteractive = __esm({
16597
16656
  // src/commands/condense.ts
16598
16657
  async function runCondenseCommand(options) {
16599
16658
  const dir = getProjectArtifactsDir(options.cwd);
16600
- const artifact2 = JSON.parse(await (0, import_promises16.readFile)((0, import_node_path23.join)(dir, "last-scan.json"), "utf8"));
16601
- const contextMapPath = (0, import_node_path23.join)(dir, "context-map.json");
16602
- await (0, import_promises16.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact2), null, 2)}
16659
+ const artifact2 = JSON.parse(await (0, import_promises17.readFile)((0, import_node_path24.join)(dir, "last-scan.json"), "utf8"));
16660
+ const contextMapPath = (0, import_node_path24.join)(dir, "context-map.json");
16661
+ await (0, import_promises17.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact2), null, 2)}
16603
16662
  `, "utf8");
16604
16663
  return { contextMapPath };
16605
16664
  }
16606
- var import_promises16, import_node_path23;
16665
+ var import_promises17, import_node_path24;
16607
16666
  var init_condense = __esm({
16608
16667
  "src/commands/condense.ts"() {
16609
16668
  "use strict";
16610
- import_promises16 = require("node:fs/promises");
16611
- import_node_path23 = require("node:path");
16669
+ import_promises17 = require("node:fs/promises");
16670
+ import_node_path24 = require("node:path");
16612
16671
  init_config();
16613
16672
  init_contextMap();
16614
16673
  }
@@ -16616,9 +16675,9 @@ var init_condense = __esm({
16616
16675
 
16617
16676
  // src/heal/pathSafety.ts
16618
16677
  function assertSafeHealTarget(cwd, target) {
16619
- const root = (0, import_node_path24.resolve)(cwd);
16620
- const absolute = (0, import_node_path24.resolve)(root, target);
16621
- const rel = (0, import_node_path24.relative)(root, absolute);
16678
+ const root = (0, import_node_path25.resolve)(cwd);
16679
+ const absolute = (0, import_node_path25.resolve)(root, target);
16680
+ const rel = (0, import_node_path25.relative)(root, absolute);
16622
16681
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
16623
16682
  throw new Error("Heal target must stay inside the workspace");
16624
16683
  }
@@ -16628,11 +16687,11 @@ function assertSafeHealTarget(cwd, target) {
16628
16687
  }
16629
16688
  return absolute;
16630
16689
  }
16631
- var import_node_path24, BLOCKED_SEGMENTS;
16690
+ var import_node_path25, BLOCKED_SEGMENTS;
16632
16691
  var init_pathSafety = __esm({
16633
16692
  "src/heal/pathSafety.ts"() {
16634
16693
  "use strict";
16635
- import_node_path24 = require("node:path");
16694
+ import_node_path25 = require("node:path");
16636
16695
  BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
16637
16696
  }
16638
16697
  });
@@ -16658,13 +16717,13 @@ function newHealId(now2 = /* @__PURE__ */ new Date()) {
16658
16717
  return `heal_${now2.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto2.randomBytes)(3).toString("hex")}`;
16659
16718
  }
16660
16719
  async function createHealDir(cwd, makeId = () => newHealId()) {
16661
- const parent = (0, import_node_path25.join)(cwd, ".viberaven", "heal");
16662
- await (0, import_promises17.mkdir)(parent, { recursive: true });
16720
+ const parent = (0, import_node_path26.join)(cwd, ".viberaven", "heal");
16721
+ await (0, import_promises18.mkdir)(parent, { recursive: true });
16663
16722
  for (let attempt = 0; attempt < 10; attempt += 1) {
16664
16723
  const id2 = makeId();
16665
- const dir = (0, import_node_path25.join)(parent, id2);
16724
+ const dir = (0, import_node_path26.join)(parent, id2);
16666
16725
  try {
16667
- await (0, import_promises17.mkdir)(dir);
16726
+ await (0, import_promises18.mkdir)(dir);
16668
16727
  return { id: id2, dir };
16669
16728
  } catch (error) {
16670
16729
  if (error.code !== "EEXIST") throw error;
@@ -16672,13 +16731,13 @@ async function createHealDir(cwd, makeId = () => newHealId()) {
16672
16731
  }
16673
16732
  throw new Error("Could not create a new heal directory under .viberaven/heal.");
16674
16733
  }
16675
- var import_node_crypto2, import_promises17, import_node_path25;
16734
+ var import_node_crypto2, import_promises18, import_node_path26;
16676
16735
  var init_healId = __esm({
16677
16736
  "src/heal/healId.ts"() {
16678
16737
  "use strict";
16679
16738
  import_node_crypto2 = require("node:crypto");
16680
- import_promises17 = require("node:fs/promises");
16681
- import_node_path25 = require("node:path");
16739
+ import_promises18 = require("node:fs/promises");
16740
+ import_node_path26 = require("node:path");
16682
16741
  }
16683
16742
  });
16684
16743
 
@@ -17145,7 +17204,7 @@ export const config = {
17145
17204
  // src/heal/recipes/eslintRestrictedImports.ts
17146
17205
  function detectEslintConfigFile(cwd) {
17147
17206
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
17148
- if ((0, import_node_fs11.existsSync)((0, import_node_path26.join)(cwd, candidate))) {
17207
+ if ((0, import_node_fs11.existsSync)((0, import_node_path27.join)(cwd, candidate))) {
17149
17208
  return candidate;
17150
17209
  }
17151
17210
  }
@@ -17300,12 +17359,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
17300
17359
  }
17301
17360
  return applyLegacyModuleRecipe(source);
17302
17361
  }
17303
- var import_node_fs11, import_node_path26, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
17362
+ var import_node_fs11, import_node_path27, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
17304
17363
  var init_eslintRestrictedImports = __esm({
17305
17364
  "src/heal/recipes/eslintRestrictedImports.ts"() {
17306
17365
  "use strict";
17307
17366
  import_node_fs11 = require("node:fs");
17308
- import_node_path26 = require("node:path");
17367
+ import_node_path27 = require("node:path");
17309
17368
  init_commands();
17310
17369
  VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
17311
17370
  RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
@@ -17357,13 +17416,13 @@ function defaultTargetFile(gapId) {
17357
17416
  }
17358
17417
  async function readSourceOrEmpty(absolutePath) {
17359
17418
  if (!(0, import_node_fs12.existsSync)(absolutePath)) return "";
17360
- return (0, import_promises18.readFile)(absolutePath, "utf8");
17419
+ return (0, import_promises19.readFile)(absolutePath, "utf8");
17361
17420
  }
17362
17421
  async function detectUpstash(cwd) {
17363
17422
  try {
17364
- const pkgPath = (0, import_node_path27.join)(cwd, "package.json");
17423
+ const pkgPath = (0, import_node_path28.join)(cwd, "package.json");
17365
17424
  if (!(0, import_node_fs12.existsSync)(pkgPath)) return false;
17366
- const raw = await (0, import_promises18.readFile)(pkgPath, "utf8");
17425
+ const raw = await (0, import_promises19.readFile)(pkgPath, "utf8");
17367
17426
  const pkg = JSON.parse(raw);
17368
17427
  const deps = {
17369
17428
  ...pkg["dependencies"] ?? {},
@@ -17378,7 +17437,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
17378
17437
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
17379
17438
  if (targetFile === void 0) return null;
17380
17439
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
17381
- const absolutePath = (0, import_node_path27.join)(cwd, targetFile);
17440
+ const absolutePath = (0, import_node_path28.join)(cwd, targetFile);
17382
17441
  const source = await readSourceOrEmpty(absolutePath);
17383
17442
  let result;
17384
17443
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -17392,25 +17451,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
17392
17451
  };
17393
17452
  }
17394
17453
  if (gapId === "missing_error_boundary") {
17395
- const absolutePath = (0, import_node_path27.join)(cwd, "app/error.tsx");
17454
+ const absolutePath = (0, import_node_path28.join)(cwd, "app/error.tsx");
17396
17455
  const source = await readSourceOrEmpty(absolutePath);
17397
17456
  const result = applyErrorBoundaryRecipe(source);
17398
17457
  return { ...result, canAutoApply: true, recipeName: gapId };
17399
17458
  }
17400
17459
  if (gapId === "missing_health_route") {
17401
- const absolutePath = (0, import_node_path27.join)(cwd, "app/api/health/route.ts");
17460
+ const absolutePath = (0, import_node_path28.join)(cwd, "app/api/health/route.ts");
17402
17461
  const source = await readSourceOrEmpty(absolutePath);
17403
17462
  const result = applyHealthRouteRecipe(source);
17404
17463
  return { ...result, canAutoApply: true, recipeName: gapId };
17405
17464
  }
17406
17465
  if (gapId === "missing_loading_state") {
17407
- const absolutePath = (0, import_node_path27.join)(cwd, "app/loading.tsx");
17466
+ const absolutePath = (0, import_node_path28.join)(cwd, "app/loading.tsx");
17408
17467
  const source = await readSourceOrEmpty(absolutePath);
17409
17468
  const result = applyLoadingStateRecipe(source);
17410
17469
  return { ...result, canAutoApply: true, recipeName: gapId };
17411
17470
  }
17412
17471
  if (gapId === "missing_404_page") {
17413
- const absolutePath = (0, import_node_path27.join)(cwd, "app/not-found.tsx");
17472
+ const absolutePath = (0, import_node_path28.join)(cwd, "app/not-found.tsx");
17414
17473
  const source = await readSourceOrEmpty(absolutePath);
17415
17474
  const result = applyNotFoundRecipe(source);
17416
17475
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -17427,10 +17486,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
17427
17486
  }
17428
17487
  if (gapId === "missing_csp_header") {
17429
17488
  let configFile = "next.config.js";
17430
- let absolutePath = (0, import_node_path27.join)(cwd, configFile);
17489
+ let absolutePath = (0, import_node_path28.join)(cwd, configFile);
17431
17490
  if (!(0, import_node_fs12.existsSync)(absolutePath)) {
17432
17491
  configFile = "next.config.mjs";
17433
- absolutePath = (0, import_node_path27.join)(cwd, configFile);
17492
+ absolutePath = (0, import_node_path28.join)(cwd, configFile);
17434
17493
  }
17435
17494
  const source = await readSourceOrEmpty(absolutePath);
17436
17495
  const result = applyCspHeaderRecipe(source);
@@ -17442,7 +17501,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
17442
17501
  }
17443
17502
  if (gapId === "missing_rate_limit") {
17444
17503
  const hasUpstash = await detectUpstash(cwd);
17445
- const middlewarePath = (0, import_node_path27.join)(cwd, "middleware.ts");
17504
+ const middlewarePath = (0, import_node_path28.join)(cwd, "middleware.ts");
17446
17505
  const source = await readSourceOrEmpty(middlewarePath);
17447
17506
  const result = applyRateLimitRecipe(source, hasUpstash);
17448
17507
  return {
@@ -17464,7 +17523,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
17464
17523
  recipeName: gapId
17465
17524
  };
17466
17525
  }
17467
- const absolutePath = (0, import_node_path27.join)(cwd, configFile);
17526
+ const absolutePath = (0, import_node_path28.join)(cwd, configFile);
17468
17527
  const source = await readSourceOrEmpty(absolutePath);
17469
17528
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
17470
17529
  return {
@@ -17478,13 +17537,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
17478
17537
  }
17479
17538
  return null;
17480
17539
  }
17481
- var import_node_fs12, import_promises18, import_node_path27;
17540
+ var import_node_fs12, import_promises19, import_node_path28;
17482
17541
  var init_recipes = __esm({
17483
17542
  "src/heal/recipes/index.ts"() {
17484
17543
  "use strict";
17485
17544
  import_node_fs12 = require("node:fs");
17486
- import_promises18 = require("node:fs/promises");
17487
- import_node_path27 = require("node:path");
17545
+ import_promises19 = require("node:fs/promises");
17546
+ import_node_path28 = require("node:path");
17488
17547
  init_envAuthSecret();
17489
17548
  init_envNodeEnv();
17490
17549
  init_envDatabaseUrl();
@@ -17561,17 +17620,17 @@ async function applyHeal(options) {
17561
17620
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
17562
17621
  };
17563
17622
  }
17564
- const absoluteTarget = (0, import_node_path28.join)(options.cwd, dispatched.targetFile);
17623
+ const absoluteTarget = (0, import_node_path29.join)(options.cwd, dispatched.targetFile);
17565
17624
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
17566
- await (0, import_promises19.mkdir)((0, import_node_path28.dirname)(absoluteTarget), { recursive: true });
17625
+ await (0, import_promises20.mkdir)((0, import_node_path29.dirname)(absoluteTarget), { recursive: true });
17567
17626
  const created2 = await createHealDir(options.cwd);
17568
17627
  id2 = created2.id;
17569
17628
  const healDir2 = created2.dir;
17570
- await (0, import_promises19.mkdir)((0, import_node_path28.join)(healDir2, "before"));
17629
+ await (0, import_promises20.mkdir)((0, import_node_path29.join)(healDir2, "before"));
17571
17630
  const existedBefore = (0, import_node_fs13.existsSync)(absoluteTarget);
17572
- const beforeContent = existedBefore ? await (0, import_promises19.readFile)(absoluteTarget, "utf8") : "";
17573
- await (0, import_promises19.writeFile)((0, import_node_path28.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
17574
- await (0, import_promises19.writeFile)(absoluteTarget, dispatched.output, "utf8");
17631
+ const beforeContent = existedBefore ? await (0, import_promises20.readFile)(absoluteTarget, "utf8") : "";
17632
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
17633
+ await (0, import_promises20.writeFile)(absoluteTarget, dispatched.output, "utf8");
17575
17634
  const patch2 = [
17576
17635
  `--- ${dispatched.targetFile}`,
17577
17636
  `+++ ${dispatched.targetFile}`,
@@ -17581,7 +17640,7 @@ async function applyHeal(options) {
17581
17640
  dispatched.output,
17582
17641
  ""
17583
17642
  ].join("\n");
17584
- await (0, import_promises19.writeFile)((0, import_node_path28.join)(healDir2, "patch.diff"), patch2, "utf8");
17643
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir2, "patch.diff"), patch2, "utf8");
17585
17644
  const result2 = {
17586
17645
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
17587
17646
  schemaVersion: "v1",
@@ -17593,17 +17652,17 @@ async function applyHeal(options) {
17593
17652
  recipe: dispatched.recipeName,
17594
17653
  ...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
17595
17654
  target: dispatched.targetFile,
17596
- changedFiles: [(0, import_node_path28.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
17655
+ changedFiles: [(0, import_node_path29.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
17597
17656
  artifacts: {
17598
17657
  patch: `.viberaven/heal/${id2}/patch.diff`,
17599
17658
  result: `.viberaven/heal/${id2}/result.json`
17600
17659
  },
17601
17660
  rollback: {
17602
17661
  available: true,
17603
- instructions: rollbackInstructions(id2, (0, import_node_path28.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
17662
+ instructions: rollbackInstructions(id2, (0, import_node_path29.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
17604
17663
  }
17605
17664
  };
17606
- await (0, import_promises19.writeFile)((0, import_node_path28.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
17665
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
17607
17666
  `, "utf8");
17608
17667
  return result2;
17609
17668
  }
@@ -17623,7 +17682,7 @@ async function applyHeal(options) {
17623
17682
  };
17624
17683
  }
17625
17684
  const absolute = assertSafeHealTarget(options.cwd, options.target);
17626
- const before = await (0, import_promises19.readFile)(absolute, "utf8");
17685
+ const before = await (0, import_promises20.readFile)(absolute, "utf8");
17627
17686
  const recipe = applyEmptyCatchRecipe(before);
17628
17687
  if (!recipe.changed) {
17629
17688
  return {
@@ -17643,9 +17702,9 @@ async function applyHeal(options) {
17643
17702
  const created = await createHealDir(options.cwd);
17644
17703
  id2 = created.id;
17645
17704
  const healDir = created.dir;
17646
- await (0, import_promises19.mkdir)((0, import_node_path28.join)(healDir, "before"));
17647
- await (0, import_promises19.writeFile)((0, import_node_path28.join)(healDir, "before", "target.txt"), before, "utf8");
17648
- await (0, import_promises19.writeFile)(absolute, recipe.output, "utf8");
17705
+ await (0, import_promises20.mkdir)((0, import_node_path29.join)(healDir, "before"));
17706
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir, "before", "target.txt"), before, "utf8");
17707
+ await (0, import_promises20.writeFile)(absolute, recipe.output, "utf8");
17649
17708
  const patch = [
17650
17709
  `--- ${options.target}`,
17651
17710
  `+++ ${options.target}`,
@@ -17655,7 +17714,7 @@ async function applyHeal(options) {
17655
17714
  recipe.output,
17656
17715
  ""
17657
17716
  ].join("\n");
17658
- await (0, import_promises19.writeFile)((0, import_node_path28.join)(healDir, "patch.diff"), patch, "utf8");
17717
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir, "patch.diff"), patch, "utf8");
17659
17718
  const result = {
17660
17719
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
17661
17720
  schemaVersion: "v1",
@@ -17666,7 +17725,7 @@ async function applyHeal(options) {
17666
17725
  gapId: options.gapId,
17667
17726
  recipe: "empty-catch-safe-response",
17668
17727
  target: options.target,
17669
- changedFiles: [(0, import_node_path28.relative)(options.cwd, absolute).replace(/\\/g, "/")],
17728
+ changedFiles: [(0, import_node_path29.relative)(options.cwd, absolute).replace(/\\/g, "/")],
17670
17729
  artifacts: {
17671
17730
  patch: `.viberaven/heal/${id2}/patch.diff`,
17672
17731
  result: `.viberaven/heal/${id2}/result.json`
@@ -17674,20 +17733,20 @@ async function applyHeal(options) {
17674
17733
  rollback: {
17675
17734
  available: true,
17676
17735
  // The empty-catch recipe only edits a file that already exists.
17677
- instructions: rollbackInstructions(id2, (0, import_node_path28.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
17736
+ instructions: rollbackInstructions(id2, (0, import_node_path29.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
17678
17737
  }
17679
17738
  };
17680
- await (0, import_promises19.writeFile)((0, import_node_path28.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
17739
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
17681
17740
  `, "utf8");
17682
17741
  return result;
17683
17742
  }
17684
- var import_promises19, import_node_fs13, import_node_path28;
17743
+ var import_promises20, import_node_fs13, import_node_path29;
17685
17744
  var init_apply = __esm({
17686
17745
  "src/heal/apply.ts"() {
17687
17746
  "use strict";
17688
- import_promises19 = require("node:fs/promises");
17747
+ import_promises20 = require("node:fs/promises");
17689
17748
  import_node_fs13 = require("node:fs");
17690
- import_node_path28 = require("node:path");
17749
+ import_node_path29 = require("node:path");
17691
17750
  init_pathSafety();
17692
17751
  init_emptyCatch();
17693
17752
  init_healId();
@@ -17697,8 +17756,8 @@ var init_apply = __esm({
17697
17756
 
17698
17757
  // src/heal/plan.ts
17699
17758
  async function writeHealPlan(options) {
17700
- const dir = (0, import_node_path29.join)(options.cwd, ".viberaven");
17701
- await (0, import_promises20.mkdir)(dir, { recursive: true });
17759
+ const dir = (0, import_node_path30.join)(options.cwd, ".viberaven");
17760
+ await (0, import_promises21.mkdir)(dir, { recursive: true });
17702
17761
  const id2 = newHealId();
17703
17762
  const target = options.target ?? `gap:${options.gapId}`;
17704
17763
  const proposal = [];
@@ -17750,18 +17809,18 @@ async function writeHealPlan(options) {
17750
17809
  artifacts: { plan: ".viberaven/heal-plan.md" },
17751
17810
  rollback: { available: false, instructions: "No source files were changed." }
17752
17811
  };
17753
- await (0, import_promises20.writeFile)((0, import_node_path29.join)(dir, "heal-plan.md"), markdown, "utf8");
17754
- await (0, import_promises20.writeFile)((0, import_node_path29.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
17812
+ await (0, import_promises21.writeFile)((0, import_node_path30.join)(dir, "heal-plan.md"), markdown, "utf8");
17813
+ await (0, import_promises21.writeFile)((0, import_node_path30.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
17755
17814
  `, "utf8");
17756
17815
  return result;
17757
17816
  }
17758
- var import_promises20, import_node_path29;
17817
+ var import_promises21, import_node_path30;
17759
17818
  var init_plan = __esm({
17760
17819
  "src/heal/plan.ts"() {
17761
17820
  "use strict";
17762
- import_promises20 = require("node:fs/promises");
17821
+ import_promises21 = require("node:fs/promises");
17763
17822
  init_healId();
17764
- import_node_path29 = require("node:path");
17823
+ import_node_path30 = require("node:path");
17765
17824
  init_commands();
17766
17825
  init_supabaseRls2();
17767
17826
  }
@@ -17769,8 +17828,8 @@ var init_plan = __esm({
17769
17828
 
17770
17829
  // src/heal/prompt.ts
17771
17830
  async function writeHealPrompt(options) {
17772
- const dir = (0, import_node_path30.join)(options.cwd, ".viberaven");
17773
- await (0, import_promises21.mkdir)(dir, { recursive: true });
17831
+ const dir = (0, import_node_path31.join)(options.cwd, ".viberaven");
17832
+ await (0, import_promises22.mkdir)(dir, { recursive: true });
17774
17833
  const id2 = newHealId();
17775
17834
  const target = options.target ?? `gap:${options.gapId}`;
17776
17835
  const prompt = [
@@ -17797,16 +17856,16 @@ async function writeHealPrompt(options) {
17797
17856
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
17798
17857
  rollback: { available: false, instructions: "No source files were changed." }
17799
17858
  };
17800
- await (0, import_promises21.writeFile)((0, import_node_path30.join)(dir, "heal-prompt.md"), prompt, "utf8");
17859
+ await (0, import_promises22.writeFile)((0, import_node_path31.join)(dir, "heal-prompt.md"), prompt, "utf8");
17801
17860
  return result;
17802
17861
  }
17803
- var import_promises21, import_node_path30;
17862
+ var import_promises22, import_node_path31;
17804
17863
  var init_prompt = __esm({
17805
17864
  "src/heal/prompt.ts"() {
17806
17865
  "use strict";
17807
- import_promises21 = require("node:fs/promises");
17866
+ import_promises22 = require("node:fs/promises");
17808
17867
  init_healId();
17809
- import_node_path30 = require("node:path");
17868
+ import_node_path31 = require("node:path");
17810
17869
  init_commands();
17811
17870
  }
17812
17871
  });
@@ -17817,7 +17876,7 @@ function loopStatePath(workspaceRoot) {
17817
17876
  }
17818
17877
  async function loadLoopState(workspaceRoot) {
17819
17878
  try {
17820
- const raw = await (0, import_promises22.readFile)(loopStatePath(workspaceRoot), "utf8");
17879
+ const raw = await (0, import_promises23.readFile)(loopStatePath(workspaceRoot), "utf8");
17821
17880
  const parsed = JSON.parse(raw);
17822
17881
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
17823
17882
  const p2 = parsed;
@@ -17837,8 +17896,8 @@ async function loadLoopState(workspaceRoot) {
17837
17896
  async function saveLoopState(workspaceRoot, state) {
17838
17897
  try {
17839
17898
  const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
17840
- await (0, import_promises22.mkdir)(dir, { recursive: true });
17841
- await (0, import_promises22.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
17899
+ await (0, import_promises23.mkdir)(dir, { recursive: true });
17900
+ await (0, import_promises23.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
17842
17901
  } catch (err) {
17843
17902
  console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
17844
17903
  }
@@ -17864,11 +17923,11 @@ function resetBatch(state, newGapCount) {
17864
17923
  appliedGapIdsSinceScan: []
17865
17924
  };
17866
17925
  }
17867
- var import_promises22, import_path, DEFAULT_LOOP_STATE;
17926
+ var import_promises23, import_path, DEFAULT_LOOP_STATE;
17868
17927
  var init_loopState = __esm({
17869
17928
  "src/loopState.ts"() {
17870
17929
  "use strict";
17871
- import_promises22 = require("fs/promises");
17930
+ import_promises23 = require("fs/promises");
17872
17931
  import_path = require("path");
17873
17932
  DEFAULT_LOOP_STATE = {
17874
17933
  batchApplied: 0,
@@ -17909,11 +17968,11 @@ var init_heal = __esm({
17909
17968
 
17910
17969
  // src/stackRecommend.ts
17911
17970
  async function recommendStack(cwd = process.cwd()) {
17912
- const pkgPath = (0, import_node_path31.join)(cwd, "package.json");
17971
+ const pkgPath = (0, import_node_path32.join)(cwd, "package.json");
17913
17972
  if (!(0, import_node_fs14.existsSync)(pkgPath)) {
17914
17973
  return DEFAULT_STACK;
17915
17974
  }
17916
- const pkg = JSON.parse(await (0, import_promises23.readFile)(pkgPath, "utf-8"));
17975
+ const pkg = JSON.parse(await (0, import_promises24.readFile)(pkgPath, "utf-8"));
17917
17976
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
17918
17977
  const names = Object.keys(deps).join(" ").toLowerCase();
17919
17978
  const rec = {
@@ -17941,13 +18000,13 @@ async function recommendStack(cwd = process.cwd()) {
17941
18000
  }
17942
18001
  return rec;
17943
18002
  }
17944
- var import_promises23, import_node_fs14, import_node_path31, DEFAULT_STACK;
18003
+ var import_promises24, import_node_fs14, import_node_path32, DEFAULT_STACK;
17945
18004
  var init_stackRecommend = __esm({
17946
18005
  "src/stackRecommend.ts"() {
17947
18006
  "use strict";
17948
- import_promises23 = require("node:fs/promises");
18007
+ import_promises24 = require("node:fs/promises");
17949
18008
  import_node_fs14 = require("node:fs");
17950
- import_node_path31 = require("node:path");
18009
+ import_node_path32 = require("node:path");
17951
18010
  DEFAULT_STACK = {
17952
18011
  frontend: "react",
17953
18012
  ui: "tailwind + shadcn/ui",
@@ -18104,13 +18163,13 @@ async function hasGithubRemote(cwd) {
18104
18163
  }
18105
18164
  }
18106
18165
  async function githubWorkflowStatus(cwd) {
18107
- return { exists: (0, import_node_fs15.existsSync)((0, import_node_path32.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, publisherExists: (0, import_node_fs15.existsSync)((0, import_node_path32.join)(cwd, PUBLISHER_PATH)), publisherPath: DISPLAY_PUBLISHER, onGithub: await hasGithubRemote(cwd) };
18166
+ return { exists: (0, import_node_fs15.existsSync)((0, import_node_path33.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, publisherExists: (0, import_node_fs15.existsSync)((0, import_node_path33.join)(cwd, PUBLISHER_PATH)), publisherPath: DISPLAY_PUBLISHER, onGithub: await hasGithubRemote(cwd) };
18108
18167
  }
18109
18168
  async function writeGithubWorkflow(cwd, options = {}) {
18110
- const targets = [(0, import_node_path32.join)(cwd, WORKFLOW_PATH), (0, import_node_path32.join)(cwd, PUBLISHER_PATH)];
18111
- for (const parent of [(0, import_node_path32.join)(cwd, ".github"), (0, import_node_path32.join)(cwd, ".github", "workflows")]) {
18169
+ const targets = [(0, import_node_path33.join)(cwd, WORKFLOW_PATH), (0, import_node_path33.join)(cwd, PUBLISHER_PATH)];
18170
+ for (const parent of [(0, import_node_path33.join)(cwd, ".github"), (0, import_node_path33.join)(cwd, ".github", "workflows")]) {
18112
18171
  try {
18113
- const stat12 = await (0, import_promises24.lstat)(parent);
18172
+ const stat12 = await (0, import_promises25.lstat)(parent);
18114
18173
  if (!stat12.isDirectory() || stat12.isSymbolicLink()) throw new Error("Workflow directory must be a regular directory");
18115
18174
  } catch (error) {
18116
18175
  if (error.code !== "ENOENT") throw error;
@@ -18119,31 +18178,31 @@ async function writeGithubWorkflow(cwd, options = {}) {
18119
18178
  if (!options.force && targets.some(import_node_fs15.existsSync)) return { written: false, status: await githubWorkflowStatus(cwd) };
18120
18179
  const originals = await Promise.all(targets.map(async (p2) => {
18121
18180
  try {
18122
- if (!(await (0, import_promises24.lstat)(p2)).isFile()) throw new Error("Workflow target must be a regular file");
18123
- return await (0, import_promises24.readFile)(p2);
18181
+ if (!(await (0, import_promises25.lstat)(p2)).isFile()) throw new Error("Workflow target must be a regular file");
18182
+ return await (0, import_promises25.readFile)(p2);
18124
18183
  } catch (error) {
18125
18184
  if (error.code === "ENOENT") return null;
18126
18185
  throw error;
18127
18186
  }
18128
18187
  }));
18129
- await (0, import_promises24.mkdir)((0, import_node_path32.dirname)(targets[0]), { recursive: true });
18130
- const stagingDirectory = await (0, import_promises24.mkdtemp)((0, import_node_path32.join)((0, import_node_path32.dirname)(targets[0]), ".viberaven-stage-"));
18131
- const staged = targets.map((_3, i) => (0, import_node_path32.join)(stagingDirectory, `${i}.yml`));
18188
+ await (0, import_promises25.mkdir)((0, import_node_path33.dirname)(targets[0]), { recursive: true });
18189
+ const stagingDirectory = await (0, import_promises25.mkdtemp)((0, import_node_path33.join)((0, import_node_path33.dirname)(targets[0]), ".viberaven-stage-"));
18190
+ const staged = targets.map((_3, i) => (0, import_node_path33.join)(stagingDirectory, `${i}.yml`));
18132
18191
  let replaced = 0;
18133
18192
  try {
18134
- for (const [i, content] of [renderWorkflow(), renderPublisherWorkflow()].entries()) await (0, import_promises24.writeFile)(staged[i], content, { encoding: "utf8", flag: "wx" });
18193
+ for (const [i, content] of [renderWorkflow(), renderPublisherWorkflow()].entries()) await (0, import_promises25.writeFile)(staged[i], content, { encoding: "utf8", flag: "wx" });
18135
18194
  for (const [i, target] of targets.entries()) {
18136
- await (0, import_promises24.rename)(staged[i], target);
18195
+ await (0, import_promises25.rename)(staged[i], target);
18137
18196
  replaced++;
18138
18197
  }
18139
18198
  } catch (error) {
18140
18199
  for (let i = 0; i < replaced; i++) {
18141
- if (originals[i] === null) await (0, import_promises24.rm)(targets[i], { force: true });
18142
- else await (0, import_promises24.writeFile)(targets[i], originals[i]);
18200
+ if (originals[i] === null) await (0, import_promises25.rm)(targets[i], { force: true });
18201
+ else await (0, import_promises25.writeFile)(targets[i], originals[i]);
18143
18202
  }
18144
18203
  throw error;
18145
18204
  } finally {
18146
- await (0, import_promises24.rm)(stagingDirectory, { recursive: true, force: true });
18205
+ await (0, import_promises25.rm)(stagingDirectory, { recursive: true, force: true });
18147
18206
  }
18148
18207
  return { written: true, status: await githubWorkflowStatus(cwd) };
18149
18208
  }
@@ -18167,20 +18226,20 @@ ${renderPublisherWorkflow()}`);
18167
18226
  if (!status.onGithub) console.log("No github.com remote found; these workflows run after you push them to GitHub.");
18168
18227
  return 0;
18169
18228
  }
18170
- var import_node_child_process6, import_node_fs15, import_promises24, import_node_path32, import_node_util4, execFileAsync4, WORKFLOW_PATH, PUBLISHER_PATH, SCAN_NAME, PUBLISHER_NAME, DISPLAY_PATH, DISPLAY_PUBLISHER, CHECKOUT, NODE, UPLOAD;
18229
+ var import_node_child_process6, import_node_fs15, import_promises25, import_node_path33, import_node_util4, execFileAsync4, WORKFLOW_PATH, PUBLISHER_PATH, SCAN_NAME, PUBLISHER_NAME, DISPLAY_PATH, DISPLAY_PUBLISHER, CHECKOUT, NODE, UPLOAD;
18171
18230
  var init_initGithub = __esm({
18172
18231
  "src/commands/initGithub.ts"() {
18173
18232
  "use strict";
18174
18233
  import_node_child_process6 = require("node:child_process");
18175
18234
  import_node_fs15 = require("node:fs");
18176
- import_promises24 = require("node:fs/promises");
18177
- import_node_path32 = require("node:path");
18235
+ import_promises25 = require("node:fs/promises");
18236
+ import_node_path33 = require("node:path");
18178
18237
  import_node_util4 = require("node:util");
18179
18238
  init_safeGit();
18180
18239
  init_version();
18181
18240
  execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process6.execFile);
18182
- WORKFLOW_PATH = (0, import_node_path32.join)(".github", "workflows", "viberaven.yml");
18183
- PUBLISHER_PATH = (0, import_node_path32.join)(".github", "workflows", "viberaven-comment.yml");
18241
+ WORKFLOW_PATH = (0, import_node_path33.join)(".github", "workflows", "viberaven.yml");
18242
+ PUBLISHER_PATH = (0, import_node_path33.join)(".github", "workflows", "viberaven-comment.yml");
18184
18243
  SCAN_NAME = "VibeRaven";
18185
18244
  PUBLISHER_NAME = "VibeRaven feedback";
18186
18245
  DISPLAY_PATH = ".github/workflows/viberaven.yml";
@@ -18210,7 +18269,7 @@ function managedBlocks(content) {
18210
18269
  async function checkAgentInjection(cwd) {
18211
18270
  const checks = [];
18212
18271
  for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
18213
- const exists2 = await fileExists2((0, import_node_path33.join)(cwd, item4.file));
18272
+ const exists2 = await fileExists2((0, import_node_path34.join)(cwd, item4.file));
18214
18273
  checks.push({
18215
18274
  id: item4.id,
18216
18275
  status: exists2 ? "pass" : "fail",
@@ -18218,17 +18277,17 @@ async function checkAgentInjection(cwd) {
18218
18277
  });
18219
18278
  }
18220
18279
  for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
18221
- const exists2 = await fileExists2((0, import_node_path33.join)(cwd, item4.file));
18280
+ const exists2 = await fileExists2((0, import_node_path34.join)(cwd, item4.file));
18222
18281
  checks.push({
18223
18282
  id: item4.id,
18224
18283
  status: exists2 ? "pass" : "fail",
18225
18284
  message: exists2 ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
18226
18285
  });
18227
18286
  }
18228
- const legacyCursorPath = (0, import_node_path33.join)(cwd, ".cursor/rules/viberaven.mdc");
18287
+ const legacyCursorPath = (0, import_node_path34.join)(cwd, ".cursor/rules/viberaven.mdc");
18229
18288
  if (await fileExists2(legacyCursorPath)) {
18230
- const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
18231
- const hasCoreSplit = await fileExists2((0, import_node_path33.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
18289
+ const legacyContent = await (0, import_promises26.readFile)(legacyCursorPath, "utf-8");
18290
+ const hasCoreSplit = await fileExists2((0, import_node_path34.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
18232
18291
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
18233
18292
  checks.push({
18234
18293
  id: "cursor-legacy-mdc",
@@ -18239,7 +18298,7 @@ async function checkAgentInjection(cwd) {
18239
18298
  }
18240
18299
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
18241
18300
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
18242
- const path2 = (0, import_node_path33.join)(cwd, file);
18301
+ const path2 = (0, import_node_path34.join)(cwd, file);
18243
18302
  const exists2 = await fileExists2(path2);
18244
18303
  if (!exists2) {
18245
18304
  checks.push({
@@ -18249,7 +18308,7 @@ async function checkAgentInjection(cwd) {
18249
18308
  });
18250
18309
  continue;
18251
18310
  }
18252
- const content = await (0, import_promises25.readFile)(path2, "utf-8");
18311
+ const content = await (0, import_promises26.readFile)(path2, "utf-8");
18253
18312
  const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
18254
18313
  checks.push({
18255
18314
  id: `canonical-${target}`,
@@ -18292,18 +18351,18 @@ function formatDoctorAgentsReport(report2) {
18292
18351
  }
18293
18352
  async function fileExists2(path2) {
18294
18353
  try {
18295
- await (0, import_promises25.access)(path2);
18354
+ await (0, import_promises26.access)(path2);
18296
18355
  return true;
18297
18356
  } catch {
18298
18357
  return false;
18299
18358
  }
18300
18359
  }
18301
- var import_promises25, import_node_path33, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
18360
+ var import_promises26, import_node_path34, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
18302
18361
  var init_doctorAgents = __esm({
18303
18362
  "src/commands/doctorAgents.ts"() {
18304
18363
  "use strict";
18305
- import_promises25 = require("node:fs/promises");
18306
- import_node_path33 = require("node:path");
18364
+ import_promises26 = require("node:fs/promises");
18365
+ import_node_path34 = require("node:path");
18307
18366
  init_commands();
18308
18367
  init_agentTargets();
18309
18368
  REQUIRED_EXISTENCE_CHECKS = [
@@ -18574,7 +18633,7 @@ function buildContextCleanupPlan(input) {
18574
18633
  const items = [];
18575
18634
  const seen = /* @__PURE__ */ new Set();
18576
18635
  for (const file of input.files) {
18577
- const path2 = normalizePath5((0, import_node_path34.relative)(input.projectRoot, file.path) || file.path);
18636
+ const path2 = normalizePath5((0, import_node_path35.relative)(input.projectRoot, file.path) || file.path);
18578
18637
  const fullPath = normalizePath5(file.path);
18579
18638
  const category = categorizeCleanupFile(path2, fullPath, file);
18580
18639
  if (!category || seen.has(path2)) {
@@ -18600,10 +18659,10 @@ async function collectCleanupFiles(projectRoot) {
18600
18659
  return files;
18601
18660
  }
18602
18661
  async function writeCleanupPlan(projectRoot, plan) {
18603
- const outputDir = (0, import_node_path34.join)(projectRoot, ".viberaven");
18604
- const outputPath = (0, import_node_path34.join)(outputDir, "context-cleanup.md");
18605
- await (0, import_promises26.mkdir)(outputDir, { recursive: true });
18606
- await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
18662
+ const outputDir = (0, import_node_path35.join)(projectRoot, ".viberaven");
18663
+ const outputPath = (0, import_node_path35.join)(outputDir, "context-cleanup.md");
18664
+ await (0, import_promises27.mkdir)(outputDir, { recursive: true });
18665
+ await (0, import_promises27.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
18607
18666
  return outputPath;
18608
18667
  }
18609
18668
  function categorizeCleanupFile(relativePath, fullPath, file) {
@@ -18651,7 +18710,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
18651
18710
  }
18652
18711
  let entries;
18653
18712
  try {
18654
- entries = await (0, import_promises26.readdir)(dir, { withFileTypes: true });
18713
+ entries = await (0, import_promises27.readdir)(dir, { withFileTypes: true });
18655
18714
  } catch {
18656
18715
  return;
18657
18716
  }
@@ -18659,9 +18718,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
18659
18718
  if (SKIP_DIRECTORIES.has(entry.name)) {
18660
18719
  continue;
18661
18720
  }
18662
- const absolute = (0, import_node_path34.join)(dir, entry.name);
18721
+ const absolute = (0, import_node_path35.join)(dir, entry.name);
18663
18722
  if (entry.isDirectory()) {
18664
- if (isCachePath(normalizePath5((0, import_node_path34.relative)(root, absolute)))) {
18723
+ if (isCachePath(normalizePath5((0, import_node_path35.relative)(root, absolute)))) {
18665
18724
  result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
18666
18725
  continue;
18667
18726
  }
@@ -18672,7 +18731,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
18672
18731
  continue;
18673
18732
  }
18674
18733
  try {
18675
- const fileStat = await (0, import_promises26.stat)(absolute);
18734
+ const fileStat = await (0, import_promises27.stat)(absolute);
18676
18735
  result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
18677
18736
  } catch {
18678
18737
  continue;
@@ -18682,12 +18741,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
18682
18741
  function normalizePath5(path2) {
18683
18742
  return path2.replace(/\\/g, "/");
18684
18743
  }
18685
- var import_promises26, import_node_path34, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
18744
+ var import_promises27, import_node_path35, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
18686
18745
  var init_cleanPlan = __esm({
18687
18746
  "src/commands/cleanPlan.ts"() {
18688
18747
  "use strict";
18689
- import_promises26 = require("node:fs/promises");
18690
- import_node_path34 = require("node:path");
18748
+ import_promises27 = require("node:fs/promises");
18749
+ import_node_path35 = require("node:path");
18691
18750
  MAX_WALK_DEPTH = 5;
18692
18751
  LARGE_LOG_BYTES = 8192;
18693
18752
  SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -18807,10 +18866,10 @@ var init_render = __esm({
18807
18866
 
18808
18867
  // src/commands/actions.ts
18809
18868
  async function runActionsCommand(input) {
18810
- const actionsPath = (0, import_node_path35.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18869
+ const actionsPath = (0, import_node_path36.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18811
18870
  let raw;
18812
18871
  try {
18813
- raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
18872
+ raw = await (0, import_promises28.readFile)(actionsPath, "utf8");
18814
18873
  } catch {
18815
18874
  process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
18816
18875
  return 1;
@@ -18824,12 +18883,12 @@ async function runActionsCommand(input) {
18824
18883
  process.stdout.write(renderActionSurface(manifest));
18825
18884
  return 0;
18826
18885
  }
18827
- var import_promises27, import_node_path35;
18886
+ var import_promises28, import_node_path36;
18828
18887
  var init_actions = __esm({
18829
18888
  "src/commands/actions.ts"() {
18830
18889
  "use strict";
18831
- import_promises27 = require("node:fs/promises");
18832
- import_node_path35 = require("node:path");
18890
+ import_promises28 = require("node:fs/promises");
18891
+ import_node_path36 = require("node:path");
18833
18892
  init_render();
18834
18893
  init_config();
18835
18894
  }
@@ -18838,7 +18897,7 @@ var init_actions = __esm({
18838
18897
  // src/commands/verifyAction.ts
18839
18898
  async function currentActionExists(cwd, actionId) {
18840
18899
  try {
18841
- const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18900
+ const raw = await (0, import_promises29.readFile)((0, import_node_path37.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18842
18901
  const manifest = JSON.parse(raw);
18843
18902
  return manifest.actions.some((action) => action.id === actionId);
18844
18903
  } catch {
@@ -18882,12 +18941,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
18882
18941
  );
18883
18942
  return 0;
18884
18943
  }
18885
- var import_promises28, import_node_path36;
18944
+ var import_promises29, import_node_path37;
18886
18945
  var init_verifyAction = __esm({
18887
18946
  "src/commands/verifyAction.ts"() {
18888
18947
  "use strict";
18889
- import_promises28 = require("node:fs/promises");
18890
- import_node_path36 = require("node:path");
18948
+ import_promises29 = require("node:fs/promises");
18949
+ import_node_path37 = require("node:path");
18891
18950
  init_registry2();
18892
18951
  init_config();
18893
18952
  }
@@ -19111,19 +19170,19 @@ async function exportCommit(cwd, commit) {
19111
19170
  throw new Error("Git snapshot exceeds the supported size limit (32 MB per file, 128 MB total). The comparison is incomplete.");
19112
19171
  }
19113
19172
  const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
19114
- const root = await (0, import_promises29.mkdtemp)((0, import_node_path37.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
19173
+ const root = await (0, import_promises30.mkdtemp)((0, import_node_path38.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
19115
19174
  try {
19116
19175
  for (const entry of entries) {
19117
- const target = (0, import_node_path37.resolve)(root, ...entry.path.split("/"));
19118
- if (!target.startsWith(root + import_node_path37.sep)) throw new Error("Invalid Git snapshot path");
19176
+ const target = (0, import_node_path38.resolve)(root, ...entry.path.split("/"));
19177
+ if (!target.startsWith(root + import_node_path38.sep)) throw new Error("Invalid Git snapshot path");
19119
19178
  const content = blobs.get(entry.sha);
19120
19179
  if (!content || content.length !== entry.size) throw new Error("Git snapshot blob is missing or truncated");
19121
- await (0, import_promises29.mkdir)((0, import_node_path37.dirname)(target), { recursive: true });
19122
- await (0, import_promises29.writeFile)(target, content);
19180
+ await (0, import_promises30.mkdir)((0, import_node_path38.dirname)(target), { recursive: true });
19181
+ await (0, import_promises30.writeFile)(target, content);
19123
19182
  }
19124
19183
  return root;
19125
19184
  } catch (error) {
19126
- await (0, import_promises29.rm)(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
19185
+ await (0, import_promises30.rm)(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
19127
19186
  throw error;
19128
19187
  }
19129
19188
  }
@@ -19135,7 +19194,7 @@ async function checkChange(cwd, ref, head) {
19135
19194
  if (!result.ok) throw new Error(`The check on ${ref} did not finish: ${result.message}`);
19136
19195
  return compareFindings(result.artifact.gaps, head, ref, baseCommit);
19137
19196
  } finally {
19138
- await (0, import_promises29.rm)(folder, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
19197
+ await (0, import_promises30.rm)(folder, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
19139
19198
  }
19140
19199
  }
19141
19200
  function cell(text2) {
@@ -19192,14 +19251,14 @@ function renderCheckMarkdown({ head, change, version, score }) {
19192
19251
  return `${lines.join("\n")}
19193
19252
  `;
19194
19253
  }
19195
- var import_node_child_process7, import_promises29, import_node_os, import_node_path37, import_node_util5, execFileAsync5, MAX_BLOB_BYTES, MAX_SNAPSHOT_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
19254
+ var import_node_child_process7, import_promises30, import_node_os, import_node_path38, import_node_util5, execFileAsync5, MAX_BLOB_BYTES, MAX_SNAPSHOT_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
19196
19255
  var init_checkChange = __esm({
19197
19256
  "src/commands/checkChange.ts"() {
19198
19257
  "use strict";
19199
19258
  import_node_child_process7 = require("node:child_process");
19200
- import_promises29 = require("node:fs/promises");
19259
+ import_promises30 = require("node:fs/promises");
19201
19260
  import_node_os = require("node:os");
19202
- import_node_path37 = require("node:path");
19261
+ import_node_path38 = require("node:path");
19203
19262
  import_node_util5 = require("node:util");
19204
19263
  init_runScan();
19205
19264
  init_safeGit();
@@ -19356,7 +19415,7 @@ ${message}
19356
19415
  }
19357
19416
  async function readUserFollowUps(actionsPath) {
19358
19417
  try {
19359
- return JSON.parse(await (0, import_promises30.readFile)(actionsPath, "utf8")).userFollowUps ?? [];
19418
+ return JSON.parse(await (0, import_promises31.readFile)(actionsPath, "utf8")).userFollowUps ?? [];
19360
19419
  } catch {
19361
19420
  return [];
19362
19421
  }
@@ -19369,11 +19428,11 @@ async function printUserFollowUps(actionsPath) {
19369
19428
  for (const step of followUp.userSteps) console.log(` - ${step} (${followUp.gapId ?? followUp.id})`);
19370
19429
  }
19371
19430
  }
19372
- var import_promises30, SEVERITY_ICON, SEVERITY_ORDER3, CHECK_NEXT_STEP;
19431
+ var import_promises31, SEVERITY_ICON, SEVERITY_ORDER3, CHECK_NEXT_STEP;
19373
19432
  var init_check = __esm({
19374
19433
  "src/commands/check.ts"() {
19375
19434
  "use strict";
19376
- import_promises30 = require("node:fs/promises");
19435
+ import_promises31 = require("node:fs/promises");
19377
19436
  init_runScan();
19378
19437
  init_artifacts2();
19379
19438
  init_statusLabels();
@@ -19400,18 +19459,18 @@ var init_check = __esm({
19400
19459
  // src/hosted/credentials.ts
19401
19460
  function defaultConfigDir(env = process.env, platform = process.platform) {
19402
19461
  const override = env.VIBERAVEN_CONFIG_DIR?.trim();
19403
- if (override) return (0, import_node_path38.resolve)(override);
19462
+ if (override) return (0, import_node_path39.resolve)(override);
19404
19463
  if (platform === "win32") {
19405
- return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
19464
+ return (0, import_node_path39.join)(env.APPDATA?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
19406
19465
  }
19407
19466
  if (platform === "darwin") {
19408
- return (0, import_node_path38.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
19467
+ return (0, import_node_path39.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
19409
19468
  }
19410
- return (0, import_node_path38.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
19469
+ return (0, import_node_path39.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
19411
19470
  }
19412
19471
  function isInside(child, parent) {
19413
- const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
19414
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
19472
+ const rel = (0, import_node_path39.relative)((0, import_node_path39.resolve)(parent), (0, import_node_path39.resolve)(child));
19473
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path39.isAbsolute)(rel);
19415
19474
  }
19416
19475
  async function retryTransient(work) {
19417
19476
  for (let attempt = 1; ; attempt += 1) {
@@ -19424,15 +19483,15 @@ async function retryTransient(work) {
19424
19483
  }
19425
19484
  }
19426
19485
  }
19427
- var import_node_child_process8, import_node_crypto3, import_promises31, import_node_os2, import_node_path38, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
19486
+ var import_node_child_process8, import_node_crypto3, import_promises32, import_node_os2, import_node_path39, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
19428
19487
  var init_credentials = __esm({
19429
19488
  "src/hosted/credentials.ts"() {
19430
19489
  "use strict";
19431
19490
  import_node_child_process8 = require("node:child_process");
19432
19491
  import_node_crypto3 = require("node:crypto");
19433
- import_promises31 = require("node:fs/promises");
19492
+ import_promises32 = require("node:fs/promises");
19434
19493
  import_node_os2 = require("node:os");
19435
- import_node_path38 = require("node:path");
19494
+ import_node_path39 = require("node:path");
19436
19495
  CREDENTIALS_FILE_NAME = "credentials.json";
19437
19496
  CredentialLocationError = class extends Error {
19438
19497
  };
@@ -19459,9 +19518,9 @@ var init_credentials = __esm({
19459
19518
  constructor(configDir, workspacePath, options = {}) {
19460
19519
  this.configDir = configDir;
19461
19520
  this.workspacePath = workspacePath;
19462
- this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
19521
+ this.path = (0, import_node_path39.join)(configDir, CREDENTIALS_FILE_NAME);
19463
19522
  this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
19464
- this.fs = { readFile: (path2, encoding) => (0, import_promises31.readFile)(path2, encoding), rename: (from, to) => (0, import_promises31.rename)(from, to), rm: (path2) => (0, import_promises31.rm)(path2, { force: true }), ...options.fs };
19523
+ this.fs = { readFile: (path2, encoding) => (0, import_promises32.readFile)(path2, encoding), rename: (from, to) => (0, import_promises32.rename)(from, to), rm: (path2) => (0, import_promises32.rm)(path2, { force: true }), ...options.fs };
19465
19524
  }
19466
19525
  path;
19467
19526
  restrict;
@@ -19510,15 +19569,15 @@ var init_credentials = __esm({
19510
19569
  `Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
19511
19570
  );
19512
19571
  }
19513
- await (0, import_promises31.mkdir)(this.configDir, { recursive: true, mode: 448 });
19572
+ await (0, import_promises32.mkdir)(this.configDir, { recursive: true, mode: 448 });
19514
19573
  const temp = `${this.path}.${(0, import_node_crypto3.randomUUID)()}.tmp`;
19515
- await (0, import_promises31.writeFile)(temp, `${JSON.stringify(body, null, 2)}
19574
+ await (0, import_promises32.writeFile)(temp, `${JSON.stringify(body, null, 2)}
19516
19575
  `, { encoding: "utf8", mode: 384 });
19517
- await (0, import_promises31.chmod)(temp, 384).catch(() => void 0);
19576
+ await (0, import_promises32.chmod)(temp, 384).catch(() => void 0);
19518
19577
  try {
19519
19578
  if (this.restrict) await this.restrict(temp);
19520
19579
  } catch (error) {
19521
- await (0, import_promises31.rm)(temp, { force: true });
19580
+ await (0, import_promises32.rm)(temp, { force: true });
19522
19581
  const code = error?.code;
19523
19582
  const toolMissing = code === "ENOENT";
19524
19583
  throw new CredentialPermissionError(
@@ -19529,17 +19588,17 @@ var init_credentials = __esm({
19529
19588
  if (guard) {
19530
19589
  const onDisk = await this.readFor(guard.apiBaseUrl);
19531
19590
  if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
19532
- await (0, import_promises31.rm)(temp, { force: true });
19591
+ await (0, import_promises32.rm)(temp, { force: true });
19533
19592
  return false;
19534
19593
  }
19535
19594
  }
19536
19595
  try {
19537
19596
  await retryTransient(() => this.fs.rename(temp, this.path));
19538
19597
  } catch (error) {
19539
- await (0, import_promises31.rm)(temp, { force: true });
19598
+ await (0, import_promises32.rm)(temp, { force: true });
19540
19599
  throw error;
19541
19600
  }
19542
- await (0, import_promises31.chmod)(this.path, 384).catch(() => void 0);
19601
+ await (0, import_promises32.chmod)(this.path, 384).catch(() => void 0);
19543
19602
  return true;
19544
19603
  }
19545
19604
  /** Keeps the stored plan label in step with what the API last said. */
@@ -20566,7 +20625,7 @@ function buildMissionCards(artifact2, providers) {
20566
20625
  };
20567
20626
  }
20568
20627
  function projectNameFromPath(pathValue) {
20569
- return (0, import_node_path39.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20628
+ return (0, import_node_path40.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20570
20629
  }
20571
20630
  function gapSearchText(gap) {
20572
20631
  return [
@@ -20978,19 +21037,19 @@ function mapArtifactToLocalUiState(artifact2, presentProviderIds) {
20978
21037
  selectedProviderId: selectedProvider.id,
20979
21038
  currentPrompt: selectedProvider.nextFix?.prompt ?? "",
20980
21039
  artifacts: {
20981
- tasklist: (0, import_node_path39.join)(".viberaven", "agent-tasklist.md"),
20982
- contextMap: (0, import_node_path39.join)(".viberaven", "context-map.json"),
20983
- gateResult: (0, import_node_path39.join)(".viberaven", "gate-result.json"),
20984
- lastScan: (0, import_node_path39.join)(".viberaven", "last-scan.json")
21040
+ tasklist: (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"),
21041
+ contextMap: (0, import_node_path40.join)(".viberaven", "context-map.json"),
21042
+ gateResult: (0, import_node_path40.join)(".viberaven", "gate-result.json"),
21043
+ lastScan: (0, import_node_path40.join)(".viberaven", "last-scan.json")
20985
21044
  },
20986
21045
  empty: false
20987
21046
  };
20988
21047
  }
20989
- var import_node_path39, GATE_LABEL, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
21048
+ var import_node_path40, GATE_LABEL, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20990
21049
  var init_artifactAdapter = __esm({
20991
21050
  "src/local-ui/artifactAdapter.ts"() {
20992
21051
  "use strict";
20993
- import_node_path39 = require("node:path");
21052
+ import_node_path40 = require("node:path");
20994
21053
  init_providerLogos();
20995
21054
  init_buildTaskList();
20996
21055
  init_prompts();
@@ -21332,7 +21391,7 @@ async function buildHostedCheckPayload(workspacePath) {
21332
21391
  throw new Error("Could not assemble the full check request.");
21333
21392
  }
21334
21393
  function finalizePayload(payload) {
21335
- const folder = (0, import_node_path40.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21394
+ const folder = (0, import_node_path41.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21336
21395
  const redacted = redactPayload({ ...payload, workspacePath: folder });
21337
21396
  return { payload: redacted.payload, redactions: redacted.count };
21338
21397
  }
@@ -21363,13 +21422,13 @@ function splitSections(prompt) {
21363
21422
  chars: section.length
21364
21423
  }));
21365
21424
  }
21366
- var import_node_path40, PayloadCaptured;
21425
+ var import_node_path41, PayloadCaptured;
21367
21426
  var init_payload = __esm({
21368
21427
  "src/hosted/payload.ts"() {
21369
21428
  "use strict";
21370
21429
  init_fileScanner();
21371
21430
  init_orchestrator();
21372
- import_node_path40 = require("node:path");
21431
+ import_node_path41 = require("node:path");
21373
21432
  init_config();
21374
21433
  init_redact();
21375
21434
  PayloadCaptured = class extends Error {
@@ -21452,13 +21511,13 @@ function readServerMessage(body) {
21452
21511
  return body.slice(0, 300);
21453
21512
  }
21454
21513
  }
21455
- var import_node_crypto4, import_promises32, import_node_path41, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK4, isRecordValue, isFinding, FullCheckService;
21514
+ var import_node_crypto4, import_promises33, import_node_path42, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK4, isRecordValue, isFinding, FullCheckService;
21456
21515
  var init_fullCheck = __esm({
21457
21516
  "src/hosted/fullCheck.ts"() {
21458
21517
  "use strict";
21459
21518
  import_node_crypto4 = require("node:crypto");
21460
- import_promises32 = require("node:fs/promises");
21461
- import_node_path41 = require("node:path");
21519
+ import_promises33 = require("node:fs/promises");
21520
+ import_node_path42 = require("node:path");
21462
21521
  init_engines();
21463
21522
  init_planLimits();
21464
21523
  init_productionMapLanes();
@@ -21774,27 +21833,27 @@ var init_fullCheck = __esm({
21774
21833
  */
21775
21834
  async latest() {
21776
21835
  try {
21777
- const saved = JSON.parse(await (0, import_promises32.readFile)(this.latestPath(), "utf8"));
21836
+ const saved = JSON.parse(await (0, import_promises33.readFile)(this.latestPath(), "utf8"));
21778
21837
  return isSavedResult(saved) ? saved : null;
21779
21838
  } catch {
21780
21839
  return null;
21781
21840
  }
21782
21841
  }
21783
21842
  latestPath() {
21784
- const key = (0, import_node_crypto4.createHash)("sha256").update(process.platform === "win32" ? (0, import_node_path41.resolve)(this.workspacePath).toLowerCase() : (0, import_node_path41.resolve)(this.workspacePath)).digest("hex").slice(0, 24);
21785
- return (0, import_node_path41.join)(this.configDir, "full-checks", `${key}.json`);
21843
+ const key = (0, import_node_crypto4.createHash)("sha256").update(process.platform === "win32" ? (0, import_node_path42.resolve)(this.workspacePath).toLowerCase() : (0, import_node_path42.resolve)(this.workspacePath)).digest("hex").slice(0, 24);
21844
+ return (0, import_node_path42.join)(this.configDir, "full-checks", `${key}.json`);
21786
21845
  }
21787
21846
  /** Best effort: a result the user paid for is shown even if it cannot be written. */
21788
21847
  async saveLatest(result) {
21789
21848
  const path2 = this.latestPath();
21790
21849
  const temp = `${path2}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
21791
21850
  try {
21792
- await (0, import_promises32.mkdir)((0, import_node_path41.join)(this.configDir, "full-checks"), { recursive: true });
21793
- await (0, import_promises32.writeFile)(temp, `${JSON.stringify(result, null, 2)}
21851
+ await (0, import_promises33.mkdir)((0, import_node_path42.join)(this.configDir, "full-checks"), { recursive: true });
21852
+ await (0, import_promises33.writeFile)(temp, `${JSON.stringify(result, null, 2)}
21794
21853
  `, "utf8");
21795
- await (0, import_promises32.rename)(temp, path2);
21854
+ await (0, import_promises33.rename)(temp, path2);
21796
21855
  } catch {
21797
- await (0, import_promises32.unlink)(temp).catch(() => void 0);
21856
+ await (0, import_promises33.unlink)(temp).catch(() => void 0);
21798
21857
  }
21799
21858
  }
21800
21859
  signInView(pending) {
@@ -21954,12 +22013,12 @@ function publicRun(run) {
21954
22013
  const { events: _events, ...rest } = run;
21955
22014
  return rest;
21956
22015
  }
21957
- var import_promises33, import_node_path42, import_node_crypto5, TERMINAL_STATES, FINISHED_STEP, MAX_EVENTS, MAX_TEXT, RunStore;
22016
+ var import_promises34, import_node_path43, import_node_crypto5, TERMINAL_STATES, FINISHED_STEP, MAX_EVENTS, MAX_TEXT, RunStore;
21958
22017
  var init_runs = __esm({
21959
22018
  "src/local-ui/actions/runs.ts"() {
21960
22019
  "use strict";
21961
- import_promises33 = require("node:fs/promises");
21962
- import_node_path42 = require("node:path");
22020
+ import_promises34 = require("node:fs/promises");
22021
+ import_node_path43 = require("node:path");
21963
22022
  import_node_crypto5 = require("node:crypto");
21964
22023
  TERMINAL_STATES = /* @__PURE__ */ new Set(["succeeded", "partially_completed", "failed", "cancelled", "interrupted"]);
21965
22024
  FINISHED_STEP = /* @__PURE__ */ new Set(["done", "failed", "skipped", "unknown"]);
@@ -21981,13 +22040,13 @@ var init_runs = __esm({
21981
22040
  async readAll() {
21982
22041
  let names = [];
21983
22042
  try {
21984
- names = (await (0, import_promises33.readdir)(this.dir)).filter((name) => /^run_[a-z0-9_]+\.json$/.test(name));
22043
+ names = (await (0, import_promises34.readdir)(this.dir)).filter((name) => /^run_[a-z0-9_]+\.json$/.test(name));
21985
22044
  } catch {
21986
22045
  return;
21987
22046
  }
21988
22047
  for (const name of names) {
21989
22048
  try {
21990
- const parsed = JSON.parse(await (0, import_promises33.readFile)((0, import_node_path42.join)(this.dir, name), "utf8"));
22049
+ const parsed = JSON.parse(await (0, import_promises34.readFile)((0, import_node_path43.join)(this.dir, name), "utf8"));
21991
22050
  if (!parsed?.id || !Array.isArray(parsed.steps) || !Array.isArray(parsed.events)) continue;
21992
22051
  this.runs.set(parsed.id, parsed);
21993
22052
  if (!TERMINAL_STATES.has(parsed.state)) {
@@ -22113,11 +22172,11 @@ var init_runs = __esm({
22113
22172
  const previous = this.writes.get(id2) ?? Promise.resolve();
22114
22173
  const next = previous.then(async () => {
22115
22174
  try {
22116
- await (0, import_promises33.mkdir)(this.dir, { recursive: true });
22117
- const target = (0, import_node_path42.join)(this.dir, `${id2}.json`);
22175
+ await (0, import_promises34.mkdir)(this.dir, { recursive: true });
22176
+ const target = (0, import_node_path43.join)(this.dir, `${id2}.json`);
22118
22177
  const temp = `${target}.${process.pid}.tmp`;
22119
- await (0, import_promises33.writeFile)(temp, JSON.stringify(run), "utf8");
22120
- await (0, import_promises33.rename)(temp, target);
22178
+ await (0, import_promises34.writeFile)(temp, JSON.stringify(run), "utf8");
22179
+ await (0, import_promises34.rename)(temp, target);
22121
22180
  } catch {
22122
22181
  }
22123
22182
  });
@@ -22136,9 +22195,9 @@ function isSecretPath(path2) {
22136
22195
  return SECRET_FILE.test(path2);
22137
22196
  }
22138
22197
  function inside(root, path2) {
22139
- const base2 = (0, import_node_path43.resolve)(root);
22140
- const target = (0, import_node_path43.resolve)(base2, path2);
22141
- return target.startsWith(base2 + import_node_path43.sep) ? target : null;
22198
+ const base2 = (0, import_node_path44.resolve)(root);
22199
+ const target = (0, import_node_path44.resolve)(base2, path2);
22200
+ return target.startsWith(base2 + import_node_path44.sep) ? target : null;
22142
22201
  }
22143
22202
  function runGitProcess(cwd, invocation, timeoutMs) {
22144
22203
  return new Promise((resolveRun) => {
@@ -22175,7 +22234,7 @@ function hashOf(content) {
22175
22234
  }
22176
22235
  async function readMaybe(path2) {
22177
22236
  try {
22178
- return await (0, import_promises34.readFile)(path2);
22237
+ return await (0, import_promises35.readFile)(path2);
22179
22238
  } catch {
22180
22239
  return null;
22181
22240
  }
@@ -22216,7 +22275,7 @@ async function takeBaseline(cwd) {
22216
22275
  const dirty = {};
22217
22276
  let total = 0;
22218
22277
  for (const [index, entry] of entries.entries()) {
22219
- const content = await readMaybe((0, import_node_path43.join)(root, entry.path));
22278
+ const content = await readMaybe((0, import_node_path44.join)(root, entry.path));
22220
22279
  const secret = isSecretPath(entry.path);
22221
22280
  const small = !secret && content !== null && content.length <= MAX_SNAPSHOT_BYTES2 && index < MAX_SNAPSHOT_FILES && total + content.length <= MAX_SNAPSHOT_TOTAL;
22222
22281
  if (small) total += content.length;
@@ -22248,8 +22307,8 @@ async function checkoutForms(cwd, root, head, dirty, budget) {
22248
22307
  const forms = {};
22249
22308
  let scanned = 0;
22250
22309
  const look = async ({ path: path2, id: id2 }) => {
22251
- const target = (0, import_node_path43.join)(root, path2);
22252
- const size = await (0, import_promises34.stat)(target).then((info) => info.size, () => null);
22310
+ const target = (0, import_node_path44.join)(root, path2);
22311
+ const size = await (0, import_promises35.stat)(target).then((info) => info.size, () => null);
22253
22312
  if (size === null || scanned + size > MAX_CHECKOUT_SCAN) return;
22254
22313
  scanned += size;
22255
22314
  const content = await readMaybe(target);
@@ -22306,7 +22365,7 @@ async function observeChanges(baseline, cwd) {
22306
22365
  const candidates = /* @__PURE__ */ new Set([...Object.keys(baseline.dirty), ...nowStatus.keys()]);
22307
22366
  const changes = [];
22308
22367
  for (const path2 of [...candidates].sort()) {
22309
- const current = await readMaybe((0, import_node_path43.join)(baseline.root, path2));
22368
+ const current = await readMaybe((0, import_node_path44.join)(baseline.root, path2));
22310
22369
  const hash2 = current ? hashOf(current) : null;
22311
22370
  const before = baseline.dirty[path2];
22312
22371
  if (before && before.hash === hash2) continue;
@@ -22321,7 +22380,7 @@ async function observeChanges(baseline, cwd) {
22321
22380
  }
22322
22381
  async function diffChanges(baseline, paths) {
22323
22382
  if (!baseline.root) return [];
22324
- const scratch = await (0, import_promises34.mkdtemp)((0, import_node_path43.join)((0, import_node_os3.tmpdir)(), "viberaven-diff-"));
22383
+ const scratch = await (0, import_promises35.mkdtemp)((0, import_node_path44.join)((0, import_node_os3.tmpdir)(), "viberaven-diff-"));
22325
22384
  try {
22326
22385
  const out = [];
22327
22386
  for (const [index, path2] of paths.slice(0, 60).entries()) {
@@ -22330,11 +22389,11 @@ async function diffChanges(baseline, paths) {
22330
22389
  continue;
22331
22390
  }
22332
22391
  const before = await baseContent(baseline, path2);
22333
- const after = await readMaybe((0, import_node_path43.join)(baseline.root, path2));
22334
- const left = (0, import_node_path43.join)(scratch, `${index}-a`);
22335
- const right = (0, import_node_path43.join)(scratch, `${index}-b`);
22336
- await (0, import_promises34.writeFile)(left, before ?? Buffer.alloc(0));
22337
- await (0, import_promises34.writeFile)(right, after ?? Buffer.alloc(0));
22392
+ const after = await readMaybe((0, import_node_path44.join)(baseline.root, path2));
22393
+ const left = (0, import_node_path44.join)(scratch, `${index}-a`);
22394
+ const right = (0, import_node_path44.join)(scratch, `${index}-b`);
22395
+ await (0, import_promises35.writeFile)(left, before ?? Buffer.alloc(0));
22396
+ await (0, import_promises35.writeFile)(right, after ?? Buffer.alloc(0));
22338
22397
  const result = await runGitProcess(scratch, await safeNoIndexDiffInvocation(scratch, ["--no-color", "-U3", "--", left, right]), 8e3);
22339
22398
  const lines = result.stdout.toString("utf8").split("\n");
22340
22399
  const body = lines.slice(lines.findIndex((line) => line.startsWith("@@")));
@@ -22346,7 +22405,7 @@ ${body.join("\n")}`.trimEnd() });
22346
22405
  }
22347
22406
  return out;
22348
22407
  } finally {
22349
- await (0, import_promises34.rm)(scratch, { recursive: true, force: true }).catch(() => void 0);
22408
+ await (0, import_promises35.rm)(scratch, { recursive: true, force: true }).catch(() => void 0);
22350
22409
  }
22351
22410
  }
22352
22411
  function replayWrites(base2, writes) {
@@ -22375,9 +22434,9 @@ function pathKey(path2) {
22375
22434
  return process.platform === "win32" ? path2.toLowerCase() : path2;
22376
22435
  }
22377
22436
  function repoPath(root, cwd, path2) {
22378
- const rel = (0, import_node_path43.relative)((0, import_node_path43.resolve)(root), (0, import_node_path43.resolve)(cwd, path2));
22379
- if (!rel || rel.startsWith("..") || (0, import_node_path43.resolve)(rel) === rel) return null;
22380
- return rel.split(import_node_path43.sep).join("/");
22437
+ const rel = (0, import_node_path44.relative)((0, import_node_path44.resolve)(root), (0, import_node_path44.resolve)(cwd, path2));
22438
+ if (!rel || rel.startsWith("..") || (0, import_node_path44.resolve)(rel) === rel) return null;
22439
+ return rel.split(import_node_path44.sep).join("/");
22381
22440
  }
22382
22441
  async function attributeChanges(baseline, cwd, changes, writes) {
22383
22442
  const owned = {};
@@ -22392,7 +22451,7 @@ async function attributeChanges(baseline, cwd, changes, writes) {
22392
22451
  for (const change of changes) {
22393
22452
  const mine = byPath.get(pathKey(change.path));
22394
22453
  const base2 = mine?.length && !isSecretPath(change.path) ? await baseContent(baseline, change.path) : void 0;
22395
- const current = base2 !== void 0 ? await readMaybe((0, import_node_path43.join)(baseline.root, change.path)) : null;
22454
+ const current = base2 !== void 0 ? await readMaybe((0, import_node_path44.join)(baseline.root, change.path)) : null;
22396
22455
  const expected = base2 !== void 0 && mine ? replayWrites(base2 === null ? null : base2.toString("utf8"), mine) : void 0;
22397
22456
  if (current !== null && expected !== void 0 && lf(current.toString("utf8")) === expected) owned[change.path] = hashOf(current);
22398
22457
  else unattributed.push(change.path);
@@ -22409,10 +22468,10 @@ async function putBack(baseline, path2, target, current, keep) {
22409
22468
  }
22410
22469
  try {
22411
22470
  if (before === null) {
22412
- if (current !== null) await (0, import_promises34.unlink)(target);
22471
+ if (current !== null) await (0, import_promises35.unlink)(target);
22413
22472
  } else {
22414
- await (0, import_promises34.mkdir)((0, import_node_path43.dirname)(target), { recursive: true });
22415
- await (0, import_promises34.writeFile)(target, before);
22473
+ await (0, import_promises35.mkdir)((0, import_node_path44.dirname)(target), { recursive: true });
22474
+ await (0, import_promises35.writeFile)(target, before);
22416
22475
  }
22417
22476
  return null;
22418
22477
  } catch (error) {
@@ -22462,41 +22521,41 @@ async function restoreFile(baseline, path2, keep) {
22462
22521
  return failure ? { error: failure } : { ok: true };
22463
22522
  }
22464
22523
  function backupsPath(runsDir, runId) {
22465
- return (0, import_node_path43.join)(runsDir, `${runId}.backups.json`);
22524
+ return (0, import_node_path44.join)(runsDir, `${runId}.backups.json`);
22466
22525
  }
22467
22526
  async function saveBackup(runsDir, runId, backup) {
22468
- await (0, import_promises34.mkdir)(runsDir, { recursive: true });
22527
+ await (0, import_promises35.mkdir)(runsDir, { recursive: true });
22469
22528
  let list = [];
22470
22529
  try {
22471
- list = JSON.parse(await (0, import_promises34.readFile)(backupsPath(runsDir, runId), "utf8"));
22530
+ list = JSON.parse(await (0, import_promises35.readFile)(backupsPath(runsDir, runId), "utf8"));
22472
22531
  } catch {
22473
22532
  }
22474
22533
  list.push(backup);
22475
- await (0, import_promises34.writeFile)(backupsPath(runsDir, runId), JSON.stringify(list), "utf8");
22534
+ await (0, import_promises35.writeFile)(backupsPath(runsDir, runId), JSON.stringify(list), "utf8");
22476
22535
  }
22477
22536
  function baselinePath(runsDir, runId) {
22478
- return (0, import_node_path43.join)(runsDir, `${runId}.baseline.json`);
22537
+ return (0, import_node_path44.join)(runsDir, `${runId}.baseline.json`);
22479
22538
  }
22480
22539
  async function saveBaseline(runsDir, runId, baseline) {
22481
- await (0, import_promises34.mkdir)(runsDir, { recursive: true });
22482
- await (0, import_promises34.writeFile)(baselinePath(runsDir, runId), JSON.stringify(baseline), "utf8");
22540
+ await (0, import_promises35.mkdir)(runsDir, { recursive: true });
22541
+ await (0, import_promises35.writeFile)(baselinePath(runsDir, runId), JSON.stringify(baseline), "utf8");
22483
22542
  }
22484
22543
  async function loadBaseline(runsDir, runId) {
22485
22544
  try {
22486
- return JSON.parse(await (0, import_promises34.readFile)(baselinePath(runsDir, runId), "utf8"));
22545
+ return JSON.parse(await (0, import_promises35.readFile)(baselinePath(runsDir, runId), "utf8"));
22487
22546
  } catch {
22488
22547
  return null;
22489
22548
  }
22490
22549
  }
22491
- var import_node_child_process9, import_node_crypto6, import_promises34, import_node_os3, import_node_path43, STATUS_PATHSPEC, MAX_SNAPSHOT_FILES, MAX_SNAPSHOT_BYTES2, MAX_SNAPSHOT_TOTAL, MAX_CHECKOUT_SCAN, CHECKOUT_READS_AT_ONCE, SECRET_FILE, withCrlf, withoutCrlf, lf, NOT_PROVEN;
22550
+ var import_node_child_process9, import_node_crypto6, import_promises35, import_node_os3, import_node_path44, STATUS_PATHSPEC, MAX_SNAPSHOT_FILES, MAX_SNAPSHOT_BYTES2, MAX_SNAPSHOT_TOTAL, MAX_CHECKOUT_SCAN, CHECKOUT_READS_AT_ONCE, SECRET_FILE, withCrlf, withoutCrlf, lf, NOT_PROVEN;
22492
22551
  var init_workspace = __esm({
22493
22552
  "src/local-ui/actions/workspace.ts"() {
22494
22553
  "use strict";
22495
22554
  import_node_child_process9 = require("node:child_process");
22496
22555
  import_node_crypto6 = require("node:crypto");
22497
- import_promises34 = require("node:fs/promises");
22556
+ import_promises35 = require("node:fs/promises");
22498
22557
  import_node_os3 = require("node:os");
22499
- import_node_path43 = require("node:path");
22558
+ import_node_path44 = require("node:path");
22500
22559
  init_safeGit();
22501
22560
  STATUS_PATHSPEC = ["--", ".", ":(exclude).viberaven", ":(exclude)**/.viberaven/**"];
22502
22561
  MAX_SNAPSHOT_FILES = 300;
@@ -22519,14 +22578,14 @@ function reloadsItself(script) {
22519
22578
  async function detectPreviewCommand(cwd) {
22520
22579
  let pkg;
22521
22580
  try {
22522
- pkg = JSON.parse(await (0, import_promises35.readFile)((0, import_node_path44.join)(cwd, "package.json"), "utf8"));
22581
+ pkg = JSON.parse(await (0, import_promises36.readFile)((0, import_node_path45.join)(cwd, "package.json"), "utf8"));
22523
22582
  } catch {
22524
22583
  return null;
22525
22584
  }
22526
22585
  const scripts = pkg.scripts ?? {};
22527
22586
  const script = ["dev", "start", "preview"].find((name) => typeof scripts[name] === "string" && scripts[name].trim());
22528
22587
  if (!script) return null;
22529
- const exists2 = async (name) => (0, import_promises35.stat)((0, import_node_path44.join)(cwd, name)).then(() => true, () => false);
22588
+ const exists2 = async (name) => (0, import_promises36.stat)((0, import_node_path45.join)(cwd, name)).then(() => true, () => false);
22530
22589
  const packageManager = await exists2("pnpm-lock.yaml") ? "pnpm" : await exists2("yarn.lock") ? "yarn" : await exists2("bun.lockb") || await exists2("bun.lock") ? "bun" : "npm";
22531
22590
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
22532
22591
  const framework = FRAMEWORKS.find(([dep]) => dep in deps)?.[1] ?? null;
@@ -22574,14 +22633,14 @@ function previewFailureReason(log) {
22574
22633
  if (/Missing script/i.test(log)) return "The script VibeRaven picked is not in package.json.";
22575
22634
  return void 0;
22576
22635
  }
22577
- var import_node_child_process10, import_promises35, import_node_net, import_node_path44, URL_PATTERN, READY_TIMEOUT_MS, LOG_LIMIT, FRAMEWORKS, RELOADS_ITSELF, SEVERAL_PROCESSES, PreviewManager;
22636
+ var import_node_child_process10, import_promises36, import_node_net, import_node_path45, URL_PATTERN, READY_TIMEOUT_MS, LOG_LIMIT, FRAMEWORKS, RELOADS_ITSELF, SEVERAL_PROCESSES, PreviewManager;
22578
22637
  var init_preview2 = __esm({
22579
22638
  "src/local-ui/preview.ts"() {
22580
22639
  "use strict";
22581
22640
  import_node_child_process10 = require("node:child_process");
22582
- import_promises35 = require("node:fs/promises");
22641
+ import_promises36 = require("node:fs/promises");
22583
22642
  import_node_net = require("node:net");
22584
- import_node_path44 = require("node:path");
22643
+ import_node_path45 = require("node:path");
22585
22644
  init_runs();
22586
22645
  URL_PATTERN = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0):(\d{2,5})(?:\/[^\s'"`]*)?/i;
22587
22646
  READY_TIMEOUT_MS = 12e4;
@@ -22767,21 +22826,21 @@ var init_preview2 = __esm({
22767
22826
 
22768
22827
  // src/local-ui/actions/engine.ts
22769
22828
  async function exists(path2) {
22770
- return (0, import_promises36.stat)(path2).then(() => true, () => false);
22829
+ return (0, import_promises37.stat)(path2).then(() => true, () => false);
22771
22830
  }
22772
22831
  async function detectChecks(cwd) {
22773
22832
  let pkg;
22774
22833
  try {
22775
- pkg = JSON.parse(await (0, import_promises36.readFile)((0, import_node_path45.join)(cwd, "package.json"), "utf8"));
22834
+ pkg = JSON.parse(await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, "package.json"), "utf8"));
22776
22835
  } catch {
22777
22836
  return [];
22778
22837
  }
22779
22838
  const scripts = pkg.scripts ?? {};
22780
- const pm = await exists((0, import_node_path45.join)(cwd, "pnpm-lock.yaml")) ? "pnpm" : await exists((0, import_node_path45.join)(cwd, "yarn.lock")) ? "yarn" : "npm";
22839
+ const pm = await exists((0, import_node_path46.join)(cwd, "pnpm-lock.yaml")) ? "pnpm" : await exists((0, import_node_path46.join)(cwd, "yarn.lock")) ? "yarn" : "npm";
22781
22840
  const checks = [];
22782
22841
  if (scripts.typecheck) checks.push({ id: "typecheck", label: "Types", display: `${pm} run typecheck`, command: `${pm} run typecheck` });
22783
- else if (await exists((0, import_node_path45.join)(cwd, "tsconfig.json")) && await exists((0, import_node_path45.join)(cwd, "node_modules", "typescript", "bin", "tsc"))) {
22784
- checks.push({ id: "typecheck", label: "Types", display: "tsc --noEmit", command: `node "${(0, import_node_path45.join)("node_modules", "typescript", "bin", "tsc")}" --noEmit --incremental false -p .` });
22842
+ else if (await exists((0, import_node_path46.join)(cwd, "tsconfig.json")) && await exists((0, import_node_path46.join)(cwd, "node_modules", "typescript", "bin", "tsc"))) {
22843
+ checks.push({ id: "typecheck", label: "Types", display: "tsc --noEmit", command: `node "${(0, import_node_path46.join)("node_modules", "typescript", "bin", "tsc")}" --noEmit --incremental false -p .` });
22785
22844
  }
22786
22845
  if (scripts.test && !NPM_DEFAULT_TEST.test(scripts.test)) checks.push({ id: "test", label: "Tests", display: `${pm} run test`, command: `${pm} run test`, env: { CI: "1" } });
22787
22846
  return checks;
@@ -22852,14 +22911,14 @@ function envKeys(text2) {
22852
22911
  async function missingClientSettings(cwd) {
22853
22912
  let example = "";
22854
22913
  try {
22855
- example = await (0, import_promises36.readFile)((0, import_node_path45.join)(cwd, ".env.example"), "utf8");
22914
+ example = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, ".env.example"), "utf8");
22856
22915
  } catch {
22857
22916
  return [];
22858
22917
  }
22859
22918
  const present = new Set(Object.keys(process.env));
22860
22919
  for (const name of [".env", ".env.local", ".env.development", ".env.development.local"]) {
22861
22920
  try {
22862
- for (const key of envKeys(await (0, import_promises36.readFile)((0, import_node_path45.join)(cwd, name), "utf8"))) present.add(key);
22921
+ for (const key of envKeys(await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, name), "utf8"))) present.add(key);
22863
22922
  } catch {
22864
22923
  }
22865
22924
  }
@@ -22869,15 +22928,15 @@ async function isGitWorkTree(cwd) {
22869
22928
  const result = await safeGit(cwd, ["rev-parse", "--is-inside-work-tree"], 4e3);
22870
22929
  return result.code === 0 && result.stdout.toString("utf8").trim() === "true";
22871
22930
  }
22872
- var import_node_child_process11, import_node_crypto7, import_promises36, import_node_path45, CHECK_TIMEOUT_MS, NPM_DEFAULT_TEST, MAX_REQUEST, ActionEngine;
22931
+ var import_node_child_process11, import_node_crypto7, import_promises37, import_node_path46, CHECK_TIMEOUT_MS, NPM_DEFAULT_TEST, MAX_REQUEST, ActionEngine;
22873
22932
  var init_engine = __esm({
22874
22933
  "src/local-ui/actions/engine.ts"() {
22875
22934
  "use strict";
22876
22935
  init_taskContract();
22877
22936
  import_node_child_process11 = require("node:child_process");
22878
22937
  import_node_crypto7 = require("node:crypto");
22879
- import_promises36 = require("node:fs/promises");
22880
- import_node_path45 = require("node:path");
22938
+ import_promises37 = require("node:fs/promises");
22939
+ import_node_path46 = require("node:path");
22881
22940
  init_catalog();
22882
22941
  init_runs();
22883
22942
  init_workspace();
@@ -22899,7 +22958,7 @@ var init_engine = __esm({
22899
22958
  const [command, checks, hasPackageJson, git3] = await Promise.all([
22900
22959
  detectPreviewCommand(cwd),
22901
22960
  detectChecks(cwd),
22902
- exists((0, import_node_path45.join)(cwd, "package.json")),
22961
+ exists((0, import_node_path46.join)(cwd, "package.json")),
22903
22962
  isGitWorkTree(cwd)
22904
22963
  ]);
22905
22964
  return {
@@ -23418,10 +23477,10 @@ async function connectProviderMcp(cwd, providerId) {
23418
23477
  if (!recipe) {
23419
23478
  return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
23420
23479
  }
23421
- const configPath = (0, import_node_path46.join)(cwd, ".mcp.json");
23480
+ const configPath = (0, import_node_path47.join)(cwd, ".mcp.json");
23422
23481
  let config = {};
23423
23482
  try {
23424
- const raw = await (0, import_promises37.readFile)(configPath, "utf8");
23483
+ const raw = await (0, import_promises38.readFile)(configPath, "utf8");
23425
23484
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23426
23485
  if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
23427
23486
  return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
@@ -23438,7 +23497,7 @@ async function connectProviderMcp(cwd, providerId) {
23438
23497
  }
23439
23498
  servers[providerId] = recipe.entry;
23440
23499
  const next = { ...config, mcpServers: servers };
23441
- await (0, import_promises37.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
23500
+ await (0, import_promises38.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
23442
23501
  `, "utf8");
23443
23502
  const suffix = recipe.note ? ` ${recipe.note}` : "";
23444
23503
  return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
@@ -23491,7 +23550,7 @@ async function defaultScanRunner(flags, positional) {
23491
23550
  }
23492
23551
  async function readPackageJson(cwd) {
23493
23552
  try {
23494
- const raw = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, "package.json"), "utf8");
23553
+ const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, "package.json"), "utf8");
23495
23554
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23496
23555
  return isRecord5(parsed) ? parsed : {};
23497
23556
  } catch {
@@ -23514,7 +23573,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
23514
23573
  if (files.length >= maxFiles) return;
23515
23574
  let entries;
23516
23575
  try {
23517
- entries = await (0, import_promises37.readdir)(dir, { withFileTypes: true });
23576
+ entries = await (0, import_promises38.readdir)(dir, { withFileTypes: true });
23518
23577
  } catch {
23519
23578
  return;
23520
23579
  }
@@ -23522,7 +23581,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
23522
23581
  if (files.length >= maxFiles) return;
23523
23582
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
23524
23583
  if (entry.isDirectory()) {
23525
- if (!skipped.has(entry.name)) await walk((0, import_node_path46.join)(dir, entry.name), rel);
23584
+ if (!skipped.has(entry.name)) await walk((0, import_node_path47.join)(dir, entry.name), rel);
23526
23585
  continue;
23527
23586
  }
23528
23587
  if (entry.isFile()) files.push(rel.toLowerCase());
@@ -23541,7 +23600,7 @@ async function readEnvKeys(cwd) {
23541
23600
  const keys2 = /* @__PURE__ */ new Set();
23542
23601
  for (const name of [".env.example", ".env"]) {
23543
23602
  try {
23544
- const raw = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, name), "utf8");
23603
+ const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, name), "utf8");
23545
23604
  for (const line of raw.split(/\r?\n/)) {
23546
23605
  const trimmed = line.trim();
23547
23606
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -23859,7 +23918,7 @@ async function codexModelOptions() {
23859
23918
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
23860
23919
  ];
23861
23920
  try {
23862
- const raw = await (0, import_promises37.readFile)((0, import_node_path46.join)((0, import_node_os4.homedir)(), ".codex", "models_cache.json"), "utf8");
23921
+ const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)((0, import_node_os4.homedir)(), ".codex", "models_cache.json"), "utf8");
23863
23922
  const parsed = JSON.parse(raw);
23864
23923
  const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
23865
23924
  const options = list.filter((model) => typeof model?.slug === "string").filter((model) => !/auto-review|reserve/i.test(model.slug)).map((model) => ({ id: model.slug, label: model.display_name?.trim() || model.slug }));
@@ -24062,19 +24121,19 @@ function isPlaceholderProjectName(value) {
24062
24121
  async function readMcpEvidence(cwd) {
24063
24122
  const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
24064
24123
  source: relativePath.replace(/\\/g, "/"),
24065
- path: (0, import_node_path46.resolve)(cwd, relativePath)
24124
+ path: (0, import_node_path47.resolve)(cwd, relativePath)
24066
24125
  }));
24067
24126
  const home = process.env.USERPROFILE || process.env.HOME;
24068
24127
  if (home) {
24069
24128
  candidates.push({
24070
24129
  source: "~/.codex/config.toml",
24071
- path: (0, import_node_path46.resolve)(home, ".codex", "config.toml")
24130
+ path: (0, import_node_path47.resolve)(home, ".codex", "config.toml")
24072
24131
  });
24073
24132
  }
24074
24133
  const evidence = [];
24075
24134
  for (const candidate of candidates) {
24076
24135
  try {
24077
- const text2 = await (0, import_promises37.readFile)(candidate.path, "utf8");
24136
+ const text2 = await (0, import_promises38.readFile)(candidate.path, "utf8");
24078
24137
  if (/mcp/i.test(text2)) evidence.push({ source: candidate.source, text: text2 });
24079
24138
  } catch {
24080
24139
  }
@@ -24165,7 +24224,7 @@ async function enrichProjectMetadata(state, cwd) {
24165
24224
  async function readState(cwd) {
24166
24225
  let state;
24167
24226
  try {
24168
- const raw = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
24227
+ const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
24169
24228
  const packageJson = await readPackageJson(cwd);
24170
24229
  const deps = dependencyNames(packageJson);
24171
24230
  const files = await listProjectFiles(cwd);
@@ -24433,7 +24492,7 @@ async function whereExe(command, cwd) {
24433
24492
  }
24434
24493
  function expandShimPath(value, base2) {
24435
24494
  const relative11 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
24436
- return relative11 === value ? value : (0, import_node_path46.join)(base2, ...relative11.split(/[\\/]+/));
24495
+ return relative11 === value ? value : (0, import_node_path47.join)(base2, ...relative11.split(/[\\/]+/));
24437
24496
  }
24438
24497
  function findShimTarget(lines, pattern, accept = () => true) {
24439
24498
  for (const line of lines) {
@@ -24444,21 +24503,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
24444
24503
  }
24445
24504
  async function resolveWindowsShimTarget(shimPath) {
24446
24505
  try {
24447
- const content = await (0, import_promises37.readFile)(shimPath, "utf8");
24448
- const base2 = (0, import_node_path46.dirname)(shimPath);
24506
+ const content = await (0, import_promises38.readFile)(shimPath, "utf8");
24507
+ const base2 = (0, import_node_path47.dirname)(shimPath);
24449
24508
  const lines = content.split(/\r?\n/);
24450
24509
  const trailingArgs = (line, after) => [...line.slice(after).split("%*")[0].matchAll(/"([^"]*)"|([^\s"&|<>]+)/g)].map((token) => expandShimPath(token[1] ?? token[2], base2)).filter((value) => value.length > 0 && !value.includes("%"));
24451
24510
  const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
24452
24511
  if (script) {
24453
24512
  return {
24454
24513
  command: process.execPath,
24455
- args: [(0, import_node_path46.join)(base2, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
24514
+ args: [(0, import_node_path47.join)(base2, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
24456
24515
  };
24457
24516
  }
24458
- const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path46.basename)(target).toLowerCase() !== "node.exe");
24517
+ const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path47.basename)(target).toLowerCase() !== "node.exe");
24459
24518
  if (binary) {
24460
24519
  return {
24461
- command: (0, import_node_path46.join)(base2, ...binary.match[1].split(/[\\/]+/)),
24520
+ command: (0, import_node_path47.join)(base2, ...binary.match[1].split(/[\\/]+/)),
24462
24521
  args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
24463
24522
  };
24464
24523
  }
@@ -24583,8 +24642,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
24583
24642
  "",
24584
24643
  prompt
24585
24644
  ].filter((line) => typeof line === "string");
24586
- await (0, import_promises37.mkdir)((0, import_node_path46.join)(cwd, ".viberaven"), { recursive: true });
24587
- await (0, import_promises37.writeFile)((0, import_node_path46.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
24645
+ await (0, import_promises38.mkdir)((0, import_node_path47.join)(cwd, ".viberaven"), { recursive: true });
24646
+ await (0, import_promises38.writeFile)((0, import_node_path47.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
24588
24647
  `, "utf8");
24589
24648
  } catch {
24590
24649
  }
@@ -24607,13 +24666,13 @@ async function runAgentChat(cwd, request) {
24607
24666
  return executeAgentPrompt(cwd, request, prompt);
24608
24667
  }
24609
24668
  async function executeAgentPrompt(cwd, request, prompt, options = {}) {
24610
- const outputPath = request.cliId === "codex" ? (0, import_node_path46.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
24611
- if (outputPath) await (0, import_promises37.mkdir)((0, import_node_path46.dirname)(outputPath), { recursive: true });
24669
+ const outputPath = request.cliId === "codex" ? (0, import_node_path47.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
24670
+ if (outputPath) await (0, import_promises38.mkdir)((0, import_node_path47.dirname)(outputPath), { recursive: true });
24612
24671
  const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath, options);
24613
24672
  const result = await runAgentCommand(command, args, cwd, stdin, request.runId, options.editRecord ? 16e6 : 48e3);
24614
24673
  if (outputPath) {
24615
24674
  try {
24616
- const finalMessage = (await (0, import_promises37.readFile)(outputPath, "utf8")).trim();
24675
+ const finalMessage = (await (0, import_promises38.readFile)(outputPath, "utf8")).trim();
24617
24676
  if (finalMessage) return { ...result, output: finalMessage };
24618
24677
  } catch {
24619
24678
  }
@@ -24638,8 +24697,8 @@ function firstGapId(state) {
24638
24697
  return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
24639
24698
  }
24640
24699
  async function runLocalAction(cwd, intent) {
24641
- const artifactDir = (0, import_node_path46.join)(cwd, ".viberaven");
24642
- await (0, import_promises37.mkdir)(artifactDir, { recursive: true });
24700
+ const artifactDir = (0, import_node_path47.join)(cwd, ".viberaven");
24701
+ await (0, import_promises38.mkdir)(artifactDir, { recursive: true });
24643
24702
  if (intent === "preview_rehearsal") {
24644
24703
  const state2 = await readState(cwd);
24645
24704
  const topProvider = state2.providers[0];
@@ -24680,7 +24739,7 @@ async function runLocalAction(cwd, intent) {
24680
24739
  proof2,
24681
24740
  ""
24682
24741
  ].join("\n");
24683
- await (0, import_promises37.writeFile)((0, import_node_path46.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
24742
+ await (0, import_promises38.writeFile)((0, import_node_path47.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
24684
24743
  return {
24685
24744
  title: "Preview rehearsal ready",
24686
24745
  summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
@@ -24690,7 +24749,7 @@ async function runLocalAction(cwd, intent) {
24690
24749
  if (intent === "audit_vercel_supabase") {
24691
24750
  const auditInput = await collectVercelSupabaseAuditInput(cwd);
24692
24751
  const result2 = buildVercelSupabaseAudit(auditInput);
24693
- await (0, import_promises37.writeFile)((0, import_node_path46.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
24752
+ await (0, import_promises38.writeFile)((0, import_node_path47.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
24694
24753
  `, "utf8");
24695
24754
  return {
24696
24755
  title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
@@ -24718,7 +24777,7 @@ async function runLocalAction(cwd, intent) {
24718
24777
  const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
24719
24778
  const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
24720
24779
  if (mode === "apply") {
24721
- await (0, import_promises37.writeFile)((0, import_node_path46.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
24780
+ await (0, import_promises38.writeFile)((0, import_node_path47.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
24722
24781
  `, "utf8");
24723
24782
  }
24724
24783
  return {
@@ -24728,14 +24787,14 @@ async function runLocalAction(cwd, intent) {
24728
24787
  };
24729
24788
  }
24730
24789
  async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
24731
- const absolute = (0, import_node_path46.resolve)(cwd, rel);
24732
- const root = (0, import_node_path46.resolve)(cwd);
24790
+ const absolute = (0, import_node_path47.resolve)(cwd, rel);
24791
+ const root = (0, import_node_path47.resolve)(cwd);
24733
24792
  if (!absolute.startsWith(root)) {
24734
24793
  sendText(response, 403, "Forbidden");
24735
24794
  return;
24736
24795
  }
24737
24796
  try {
24738
- const text2 = await (0, import_promises37.readFile)(absolute, "utf8");
24797
+ const text2 = await (0, import_promises38.readFile)(absolute, "utf8");
24739
24798
  response.writeHead(200, { "content-type": contentType });
24740
24799
  response.end(text2);
24741
24800
  } catch {
@@ -24744,7 +24803,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
24744
24803
  }
24745
24804
  async function sendReportArtifact(cwd, response) {
24746
24805
  try {
24747
- const text2 = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, ".viberaven", "report.html"), "utf8");
24806
+ const text2 = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "report.html"), "utf8");
24748
24807
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
24749
24808
  response.end(text2);
24750
24809
  return;
@@ -24871,7 +24930,7 @@ async function sendReportArtifact(cwd, response) {
24871
24930
  }
24872
24931
  async function sendExtensionIcon(cwd, response) {
24873
24932
  try {
24874
- const body = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, "media", "extension-icon.png"));
24933
+ const body = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, "media", "extension-icon.png"));
24875
24934
  sendBinary(response, 200, "image/png", body);
24876
24935
  } catch {
24877
24936
  sendText(response, 404, "Icon not found");
@@ -24884,15 +24943,15 @@ async function sendMangaAsset(pathname, response) {
24884
24943
  return;
24885
24944
  }
24886
24945
  const candidates = [
24887
- (0, import_node_path46.join)(__dirname, "manga", name),
24946
+ (0, import_node_path47.join)(__dirname, "manga", name),
24888
24947
  // installed + dev: <pkg>/dist/manga
24889
- (0, import_node_path46.join)(__dirname, "..", "assets", "manga", name),
24948
+ (0, import_node_path47.join)(__dirname, "..", "assets", "manga", name),
24890
24949
  // fallback: <pkg>/assets/manga (source)
24891
- (0, import_node_path46.join)(__dirname, "..", "..", "assets", "manga", name)
24950
+ (0, import_node_path47.join)(__dirname, "..", "..", "assets", "manga", name)
24892
24951
  ];
24893
24952
  for (const candidate of candidates) {
24894
24953
  try {
24895
- const body = await (0, import_promises37.readFile)(candidate);
24954
+ const body = await (0, import_promises38.readFile)(candidate);
24896
24955
  response.setHeader("cache-control", "no-cache");
24897
24956
  sendBinary(response, 200, contentTypeForAsset(candidate), body);
24898
24957
  return;
@@ -24902,7 +24961,7 @@ async function sendMangaAsset(pathname, response) {
24902
24961
  sendText(response, 404, "Asset not found");
24903
24962
  }
24904
24963
  function contentTypeForAsset(pathname) {
24905
- switch ((0, import_node_path46.extname)(pathname).toLowerCase()) {
24964
+ switch ((0, import_node_path47.extname)(pathname).toLowerCase()) {
24906
24965
  case ".svg":
24907
24966
  return "image/svg+xml; charset=utf-8";
24908
24967
  case ".png":
@@ -24919,14 +24978,14 @@ function contentTypeForAsset(pathname) {
24919
24978
  }
24920
24979
  async function sendBundledReportAsset(pathname, response) {
24921
24980
  const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
24922
- const assetRoot = (0, import_node_path46.resolve)(getBundledReportAssetsDir());
24923
- const absolute = (0, import_node_path46.resolve)(assetRoot, rel);
24981
+ const assetRoot = (0, import_node_path47.resolve)(getBundledReportAssetsDir());
24982
+ const absolute = (0, import_node_path47.resolve)(assetRoot, rel);
24924
24983
  if (!absolute.startsWith(assetRoot)) {
24925
24984
  sendText(response, 403, "Forbidden");
24926
24985
  return;
24927
24986
  }
24928
24987
  try {
24929
- const body = await (0, import_promises37.readFile)(absolute);
24988
+ const body = await (0, import_promises38.readFile)(absolute);
24930
24989
  sendBinary(response, 200, contentTypeForAsset(absolute), body);
24931
24990
  } catch {
24932
24991
  sendText(response, 404, "Asset not found");
@@ -24948,18 +25007,18 @@ async function bundleStackClient(entryPath) {
24948
25007
  return output.text;
24949
25008
  }
24950
25009
  async function sendStackAppJs(response) {
24951
- const distPath = (0, import_node_path46.join)(__dirname, "stack", "app.js");
25010
+ const distPath = (0, import_node_path47.join)(__dirname, "stack", "app.js");
24952
25011
  try {
24953
- const body = await (0, import_promises37.readFile)(distPath);
25012
+ const body = await (0, import_promises38.readFile)(distPath);
24954
25013
  response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
24955
25014
  response.end(body);
24956
25015
  return;
24957
25016
  } catch {
24958
25017
  }
24959
- const entryPath = (0, import_node_path46.join)(__dirname, "stack", "main.ts");
25018
+ const entryPath = (0, import_node_path47.join)(__dirname, "stack", "main.ts");
24960
25019
  let mtimeMs;
24961
25020
  try {
24962
- mtimeMs = (await (0, import_promises37.stat)(entryPath)).mtimeMs;
25021
+ mtimeMs = (await (0, import_promises38.stat)(entryPath)).mtimeMs;
24963
25022
  } catch {
24964
25023
  sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
24965
25024
  return;
@@ -24979,8 +25038,8 @@ async function sendStackAppJs(response) {
24979
25038
  async function sendStackAppCss(response) {
24980
25039
  try {
24981
25040
  const [shell, scene] = await Promise.all([
24982
- (0, import_promises37.readFile)((0, import_node_path46.join)(__dirname, "stack", "stack.css"), "utf8"),
24983
- (0, import_promises37.readFile)((0, import_node_path46.join)(__dirname, "stack", "scene.css"), "utf8").catch(() => "")
25041
+ (0, import_promises38.readFile)((0, import_node_path47.join)(__dirname, "stack", "stack.css"), "utf8"),
25042
+ (0, import_promises38.readFile)((0, import_node_path47.join)(__dirname, "stack", "scene.css"), "utf8").catch(() => "")
24984
25043
  ]);
24985
25044
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
24986
25045
  response.end(`${shell}
@@ -24991,7 +25050,7 @@ ${scene}`);
24991
25050
  }
24992
25051
  async function readProviderVerifyLedger(cwd) {
24993
25052
  try {
24994
- const raw = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
25053
+ const raw = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
24995
25054
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
24996
25055
  return isRecord5(parsed) ? parsed : {};
24997
25056
  } catch {
@@ -25001,8 +25060,8 @@ async function readProviderVerifyLedger(cwd) {
25001
25060
  async function writeProviderVerifyEntry(cwd, providerId, entry) {
25002
25061
  const ledger = await readProviderVerifyLedger(cwd);
25003
25062
  ledger[providerId] = entry;
25004
- await (0, import_promises37.mkdir)((0, import_node_path46.join)(cwd, ".viberaven"), { recursive: true });
25005
- await (0, import_promises37.writeFile)((0, import_node_path46.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
25063
+ await (0, import_promises38.mkdir)((0, import_node_path47.join)(cwd, ".viberaven"), { recursive: true });
25064
+ await (0, import_promises38.writeFile)((0, import_node_path47.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
25006
25065
  `, "utf8");
25007
25066
  }
25008
25067
  function isCliId(value) {
@@ -25209,13 +25268,13 @@ async function handleRequest(request, response, options) {
25209
25268
  return;
25210
25269
  }
25211
25270
  if (request.method === "GET" && url.pathname === "/api/architecture-map") {
25212
- const mapPath = (0, import_node_path46.join)(options.cwd, ".viberaven", "architecture-map.md");
25213
- const markdown = await (0, import_promises37.readFile)(mapPath, "utf8").catch(() => null);
25271
+ const mapPath = (0, import_node_path47.join)(options.cwd, ".viberaven", "architecture-map.md");
25272
+ const markdown = await (0, import_promises38.readFile)(mapPath, "utf8").catch(() => null);
25214
25273
  if (markdown === null) {
25215
25274
  sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
25216
25275
  return;
25217
25276
  }
25218
- const dataRaw = await (0, import_promises37.readFile)((0, import_node_path46.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
25277
+ const dataRaw = await (0, import_promises38.readFile)((0, import_node_path47.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
25219
25278
  let data = null;
25220
25279
  if (dataRaw) {
25221
25280
  try {
@@ -25224,7 +25283,7 @@ async function handleRequest(request, response, options) {
25224
25283
  data = null;
25225
25284
  }
25226
25285
  }
25227
- const contextRaw = await (0, import_promises37.readFile)((0, import_node_path46.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
25286
+ const contextRaw = await (0, import_promises38.readFile)((0, import_node_path47.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
25228
25287
  let gaps = [];
25229
25288
  if (contextRaw) {
25230
25289
  try {
@@ -25491,35 +25550,35 @@ async function handleRequest(request, response, options) {
25491
25550
  return;
25492
25551
  }
25493
25552
  if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
25494
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "agent-tasklist.md"), response);
25553
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "agent-tasklist.md"), response);
25495
25554
  return;
25496
25555
  }
25497
25556
  if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
25498
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "launch-playbook.md"), response);
25557
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "launch-playbook.md"), response);
25499
25558
  return;
25500
25559
  }
25501
25560
  if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
25502
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "audit-vercel-supabase.txt"), response);
25561
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "audit-vercel-supabase.txt"), response);
25503
25562
  return;
25504
25563
  }
25505
25564
  if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
25506
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "context-cleanup.md"), response);
25565
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "context-cleanup.md"), response);
25507
25566
  return;
25508
25567
  }
25509
25568
  if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
25510
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "preview-rehearsal.md"), response);
25569
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "preview-rehearsal.md"), response);
25511
25570
  return;
25512
25571
  }
25513
25572
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
25514
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "heal-plan.md"), response);
25573
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "heal-plan.md"), response);
25515
25574
  return;
25516
25575
  }
25517
25576
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
25518
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "heal-prompt.md"), response);
25577
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "heal-prompt.md"), response);
25519
25578
  return;
25520
25579
  }
25521
25580
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
25522
- await sendArtifact(options.cwd, (0, import_node_path46.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
25581
+ await sendArtifact(options.cwd, (0, import_node_path47.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
25523
25582
  return;
25524
25583
  }
25525
25584
  if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
@@ -25545,12 +25604,12 @@ async function handleRequest(request, response, options) {
25545
25604
  }
25546
25605
  if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
25547
25606
  for (const candidate of [
25548
- (0, import_node_path46.join)(__dirname, "vendor", "anime.umd.min.js"),
25549
- (0, import_node_path46.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
25550
- (0, import_node_path46.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
25607
+ (0, import_node_path47.join)(__dirname, "vendor", "anime.umd.min.js"),
25608
+ (0, import_node_path47.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
25609
+ (0, import_node_path47.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
25551
25610
  ]) {
25552
25611
  try {
25553
- const body = await (0, import_promises37.readFile)(candidate);
25612
+ const body = await (0, import_promises38.readFile)(candidate);
25554
25613
  response.setHeader("cache-control", "public, max-age=604800, immutable");
25555
25614
  sendBinary(response, 200, "text/javascript; charset=utf-8", body);
25556
25615
  return;
@@ -25771,8 +25830,8 @@ function listen(server, startPort) {
25771
25830
  });
25772
25831
  }
25773
25832
  function createActionStudio(cwd, options = {}) {
25774
- const root = (0, import_node_path46.resolve)(cwd);
25775
- const runsDir = (0, import_node_path46.join)(root, ".viberaven", "runs");
25833
+ const root = (0, import_node_path47.resolve)(cwd);
25834
+ const runsDir = (0, import_node_path47.join)(root, ".viberaven", "runs");
25776
25835
  const runs = new RunStore(runsDir, (0, import_node_os4.homedir)());
25777
25836
  const preview = new PreviewManager(root, (0, import_node_os4.homedir)());
25778
25837
  const agentReady = new Map(Object.entries(options.agentReady ?? {}));
@@ -25807,7 +25866,7 @@ function createActionStudio(cwd, options = {}) {
25807
25866
  };
25808
25867
  }
25809
25868
  async function startLocalUiServer(options) {
25810
- const cwd = (0, import_node_path46.resolve)(options.cwd);
25869
+ const cwd = (0, import_node_path47.resolve)(options.cwd);
25811
25870
  const scanRunner = options.scanRunner ?? defaultScanRunner;
25812
25871
  const autoScan = { state: "idle" };
25813
25872
  const fullCheck = new FullCheckService(cwd, options.fullCheck);
@@ -25826,7 +25885,7 @@ async function startLocalUiServer(options) {
25826
25885
  });
25827
25886
  const port = await listen(server, options.port ?? 4177);
25828
25887
  guard.port = port;
25829
- const hasArtifact = await (0, import_promises37.readFile)((0, import_node_path46.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
25888
+ const hasArtifact = await (0, import_promises38.readFile)((0, import_node_path47.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
25830
25889
  () => true,
25831
25890
  () => false
25832
25891
  );
@@ -25867,16 +25926,16 @@ async function waitForServerShutdown() {
25867
25926
  process.once("SIGTERM", done);
25868
25927
  });
25869
25928
  }
25870
- var import_node_http, import_node_child_process12, import_node_crypto8, import_promises37, import_node_os4, import_node_path46, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache, AGENT_LABEL;
25929
+ var import_node_http, import_node_child_process12, import_node_crypto8, import_promises38, import_node_os4, import_node_path47, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache, AGENT_LABEL;
25871
25930
  var init_server = __esm({
25872
25931
  "src/local-ui/server.ts"() {
25873
25932
  "use strict";
25874
25933
  import_node_http = require("node:http");
25875
25934
  import_node_child_process12 = require("node:child_process");
25876
25935
  import_node_crypto8 = require("node:crypto");
25877
- import_promises37 = require("node:fs/promises");
25936
+ import_promises38 = require("node:fs/promises");
25878
25937
  import_node_os4 = require("node:os");
25879
- import_node_path46 = require("node:path");
25938
+ import_node_path47 = require("node:path");
25880
25939
  init_initGithub();
25881
25940
  init_cleanPlan();
25882
25941
  init_audit();
@@ -25922,8 +25981,8 @@ var init_server = __esm({
25922
25981
  MCP_CONFIG_PATHS = [
25923
25982
  ".mcp.json",
25924
25983
  "mcp.json",
25925
- (0, import_node_path46.join)(".cursor", "mcp.json"),
25926
- (0, import_node_path46.join)(".vscode", "mcp.json")
25984
+ (0, import_node_path47.join)(".cursor", "mcp.json"),
25985
+ (0, import_node_path47.join)(".vscode", "mcp.json")
25927
25986
  ];
25928
25987
  MCP_PROVIDER_NEEDLES = {
25929
25988
  supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
@@ -25978,11 +26037,11 @@ function readChangeInput(input) {
25978
26037
  return { request, accessMode, cliId };
25979
26038
  }
25980
26039
  function jobRunsPath(cwd) {
25981
- return (0, import_node_path47.join)(cwd, ".viberaven", "runner-jobs.json");
26040
+ return (0, import_node_path48.join)(cwd, ".viberaven", "runner-jobs.json");
25982
26041
  }
25983
26042
  async function readJobRuns(cwd) {
25984
26043
  try {
25985
- const parsed = JSON.parse(await (0, import_promises38.readFile)(jobRunsPath(cwd), "utf8"));
26044
+ const parsed = JSON.parse(await (0, import_promises39.readFile)(jobRunsPath(cwd), "utf8"));
25986
26045
  return parsed && typeof parsed === "object" ? parsed : {};
25987
26046
  } catch {
25988
26047
  return {};
@@ -25991,8 +26050,8 @@ async function readJobRuns(cwd) {
25991
26050
  async function rememberJobRun(cwd, jobId, runId) {
25992
26051
  const map = await readJobRuns(cwd);
25993
26052
  map[jobId] = runId;
25994
- await (0, import_promises38.mkdir)((0, import_node_path47.dirname)(jobRunsPath(cwd)), { recursive: true });
25995
- await (0, import_promises38.writeFile)(jobRunsPath(cwd), JSON.stringify(map, null, 2), "utf8");
26053
+ await (0, import_promises39.mkdir)((0, import_node_path48.dirname)(jobRunsPath(cwd)), { recursive: true });
26054
+ await (0, import_promises39.writeFile)(jobRunsPath(cwd), JSON.stringify(map, null, 2), "utf8");
25996
26055
  }
25997
26056
  function progressFromRun(run, previewUrl) {
25998
26057
  return {
@@ -26152,13 +26211,13 @@ async function runAgentUndoJob(job, ctx) {
26152
26211
  progress: final
26153
26212
  };
26154
26213
  }
26155
- var import_promises38, import_node_path47, CLI_IDS, MAX_REQUEST2, MAX_DIFF_FILES, MAX_DIFF_CHARS, TERMINAL, SIGNALS, now, clean;
26214
+ var import_promises39, import_node_path48, CLI_IDS, MAX_REQUEST2, MAX_DIFF_FILES, MAX_DIFF_CHARS, TERMINAL, SIGNALS, now, clean;
26156
26215
  var init_connectAgent = __esm({
26157
26216
  "src/commands/connectAgent.ts"() {
26158
26217
  "use strict";
26159
26218
  init_taskContract();
26160
- import_promises38 = require("node:fs/promises");
26161
- import_node_path47 = require("node:path");
26219
+ import_promises39 = require("node:fs/promises");
26220
+ import_node_path48 = require("node:path");
26162
26221
  init_redact();
26163
26222
  CLI_IDS = ["claude", "codex", "gemini"];
26164
26223
  MAX_REQUEST2 = 1200;
@@ -26204,9 +26263,9 @@ async function readLocalRepo(cwd) {
26204
26263
  }
26205
26264
  const branch = await git2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
26206
26265
  const status = await git2(cwd, ["status", "--porcelain"]);
26207
- const lock = (file) => (0, import_node_fs16.existsSync)((0, import_node_path48.join)(cwd, file));
26266
+ const lock = (file) => (0, import_node_fs16.existsSync)((0, import_node_path49.join)(cwd, file));
26208
26267
  return {
26209
- rootName: (0, import_node_path48.basename)(cwd) || "workspace",
26268
+ rootName: (0, import_node_path49.basename)(cwd) || "workspace",
26210
26269
  remotes: [...remotes.values()],
26211
26270
  branch: branch && branch !== "HEAD" ? branch : null,
26212
26271
  headSha: await git2(cwd, ["rev-parse", "HEAD"]),
@@ -26453,13 +26512,13 @@ function runConnectCommand(options) {
26453
26512
  await jobs.close();
26454
26513
  });
26455
26514
  }
26456
- var import_node_child_process13, import_node_fs16, import_node_path48, import_node_util6, execFileAsync6, HEARTBEAT_MS, SITE;
26515
+ var import_node_child_process13, import_node_fs16, import_node_path49, import_node_util6, execFileAsync6, HEARTBEAT_MS, SITE;
26457
26516
  var init_connect = __esm({
26458
26517
  "src/commands/connect.ts"() {
26459
26518
  "use strict";
26460
26519
  import_node_child_process13 = require("node:child_process");
26461
26520
  import_node_fs16 = require("node:fs");
26462
- import_node_path48 = require("node:path");
26521
+ import_node_path49 = require("node:path");
26463
26522
  import_node_util6 = require("node:util");
26464
26523
  init_safeGit();
26465
26524
  init_runScan();
@@ -26488,14 +26547,14 @@ function normalizeWorkContract(task) {
26488
26547
  if (task.result && task.job && task.result.jobId !== task.job.id) throw new Error("Saved result differs from its job identity.");
26489
26548
  if (task.contract.request !== task.request) throw new Error("Saved request differs from its frozen Task contract.");
26490
26549
  }
26491
- var import_node_crypto9, import_promises39, import_node_path49, WORK_ID, WorkStore;
26550
+ var import_node_crypto9, import_promises40, import_node_path50, WORK_ID, WorkStore;
26492
26551
  var init_workStore = __esm({
26493
26552
  "src/commands/workStore.ts"() {
26494
26553
  "use strict";
26495
26554
  init_taskContract();
26496
26555
  import_node_crypto9 = require("node:crypto");
26497
- import_promises39 = require("node:fs/promises");
26498
- import_node_path49 = require("node:path");
26556
+ import_promises40 = require("node:fs/promises");
26557
+ import_node_path50 = require("node:path");
26499
26558
  init_credentials();
26500
26559
  WORK_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
26501
26560
  WorkStore = class {
@@ -26505,23 +26564,23 @@ var init_workStore = __esm({
26505
26564
  this.namespace = namespace;
26506
26565
  }
26507
26566
  async dir(create = true) {
26508
- const root = await (0, import_promises39.realpath)(this.cwd);
26509
- const target = (0, import_node_path49.resolve)(this.configDir);
26510
- if (create) await (0, import_promises39.mkdir)(target, { recursive: true, mode: 448 });
26511
- const canonical2 = await (0, import_promises39.realpath)(target);
26512
- const rel = (0, import_node_path49.relative)(root, canonical2);
26513
- if (!rel || !rel.startsWith("..") && !(0, import_node_path49.isAbsolute)(rel)) throw new Error("Work storage must be outside the project.");
26514
- const dir = (0, import_node_path49.join)(canonical2, this.namespace, (0, import_node_crypto9.createHash)("sha256").update(root).digest("hex"));
26515
- if (create) await (0, import_promises39.mkdir)(dir, { recursive: true, mode: 448 });
26567
+ const root = await (0, import_promises40.realpath)(this.cwd);
26568
+ const target = (0, import_node_path50.resolve)(this.configDir);
26569
+ if (create) await (0, import_promises40.mkdir)(target, { recursive: true, mode: 448 });
26570
+ const canonical2 = await (0, import_promises40.realpath)(target);
26571
+ const rel = (0, import_node_path50.relative)(root, canonical2);
26572
+ if (!rel || !rel.startsWith("..") && !(0, import_node_path50.isAbsolute)(rel)) throw new Error("Work storage must be outside the project.");
26573
+ const dir = (0, import_node_path50.join)(canonical2, this.namespace, (0, import_node_crypto9.createHash)("sha256").update(root).digest("hex"));
26574
+ if (create) await (0, import_promises40.mkdir)(dir, { recursive: true, mode: 448 });
26516
26575
  return dir;
26517
26576
  }
26518
26577
  async path(id2, create = true) {
26519
26578
  if (!WORK_ID.test(id2)) throw new Error("Invalid task id.");
26520
- return (0, import_node_path49.join)(await this.dir(create), `${id2}.json`);
26579
+ return (0, import_node_path50.join)(await this.dir(create), `${id2}.json`);
26521
26580
  }
26522
26581
  async read(id2) {
26523
- const task = JSON.parse(await (0, import_promises39.readFile)(await this.path(id2, false), "utf8"));
26524
- if (task.version !== 1 || task.id !== id2 || task.cwd !== await (0, import_promises39.realpath)(this.cwd)) throw new Error("Task does not belong to this workspace.");
26582
+ const task = JSON.parse(await (0, import_promises40.readFile)(await this.path(id2, false), "utf8"));
26583
+ if (task.version !== 1 || task.id !== id2 || task.cwd !== await (0, import_promises40.realpath)(this.cwd)) throw new Error("Task does not belong to this workspace.");
26525
26584
  if (this.namespace === "work") normalizeWorkContract(task);
26526
26585
  return task;
26527
26586
  }
@@ -26532,7 +26591,7 @@ var init_workStore = __esm({
26532
26591
  normalizeWorkContract(next);
26533
26592
  let previous;
26534
26593
  try {
26535
- previous = JSON.parse(await (0, import_promises39.readFile)(path2, "utf8"));
26594
+ previous = JSON.parse(await (0, import_promises40.readFile)(path2, "utf8"));
26536
26595
  } catch (error) {
26537
26596
  if (error.code !== "ENOENT") throw error;
26538
26597
  }
@@ -26544,22 +26603,22 @@ var init_workStore = __esm({
26544
26603
  }
26545
26604
  const temp = `${path2}.${(0, import_node_crypto9.randomUUID)()}.tmp`;
26546
26605
  try {
26547
- await (0, import_promises39.writeFile)(temp, JSON.stringify(task), { encoding: "utf8", mode: 384, flag: "wx" });
26606
+ await (0, import_promises40.writeFile)(temp, JSON.stringify(task), { encoding: "utf8", mode: 384, flag: "wx" });
26548
26607
  if (process.platform === "win32") await restrictWithIcacls(temp);
26549
- await (0, import_promises39.rename)(temp, path2);
26608
+ await (0, import_promises40.rename)(temp, path2);
26550
26609
  } finally {
26551
- await (0, import_promises39.rm)(temp, { force: true });
26610
+ await (0, import_promises40.rm)(temp, { force: true });
26552
26611
  }
26553
26612
  }
26554
26613
  async lock(id2) {
26555
26614
  const path2 = `${await this.path(id2)}.lock`;
26556
26615
  try {
26557
- const file = await (0, import_promises39.open)(path2, "wx", 384);
26616
+ const file = await (0, import_promises40.open)(path2, "wx", 384);
26558
26617
  await file.writeFile(String(process.pid));
26559
26618
  await file.close();
26560
26619
  } catch (error) {
26561
26620
  if (error.code !== "EEXIST") throw error;
26562
- const pid = Number(await (0, import_promises39.readFile)(path2, "utf8"));
26621
+ const pid = Number(await (0, import_promises40.readFile)(path2, "utf8"));
26563
26622
  if (!Number.isInteger(pid) || pid < 1) throw new Error("Task lock needs manual review.");
26564
26623
  try {
26565
26624
  process.kill(pid, 0);
@@ -26571,7 +26630,7 @@ var init_workStore = __esm({
26571
26630
  throw new Error("This task is already being resumed.");
26572
26631
  }
26573
26632
  return async () => {
26574
- await (0, import_promises39.rm)(path2, { force: true });
26633
+ await (0, import_promises40.rm)(path2, { force: true });
26575
26634
  };
26576
26635
  }
26577
26636
  };
@@ -26873,41 +26932,41 @@ function snapshotHash(snapshot) {
26873
26932
  return hashJson(snapshot);
26874
26933
  }
26875
26934
  function inside2(root, target) {
26876
- const rel = (0, import_node_path50.relative)((0, import_node_path50.resolve)(root), (0, import_node_path50.resolve)(target));
26877
- return !!rel && !rel.startsWith(`..${import_node_path50.sep}`) && rel !== ".." && !(0, import_node_path50.isAbsolute)(rel);
26935
+ const rel = (0, import_node_path51.relative)((0, import_node_path51.resolve)(root), (0, import_node_path51.resolve)(target));
26936
+ return !!rel && !rel.startsWith(`..${import_node_path51.sep}`) && rel !== ".." && !(0, import_node_path51.isAbsolute)(rel);
26878
26937
  }
26879
26938
  async function headSha(root) {
26880
26939
  const regular = async (path2, limit = 4096) => {
26881
- const info = await (0, import_promises40.lstat)(path2);
26940
+ const info = await (0, import_promises41.lstat)(path2);
26882
26941
  if (!info.isFile() || info.isSymbolicLink() || info.size > limit) throw new Error("Unsupported Git metadata.");
26883
- return (0, import_promises40.readFile)(path2, "utf8");
26942
+ return (0, import_promises41.readFile)(path2, "utf8");
26884
26943
  };
26885
26944
  try {
26886
- let gitDir = (0, import_node_path50.join)(root, ".git");
26887
- const info = await (0, import_promises40.lstat)(gitDir);
26945
+ let gitDir = (0, import_node_path51.join)(root, ".git");
26946
+ const info = await (0, import_promises41.lstat)(gitDir);
26888
26947
  if (info.isSymbolicLink()) return null;
26889
26948
  if (info.isFile()) {
26890
26949
  const pointer = /^gitdir: (.+)\r?\n?$/.exec(await regular(gitDir));
26891
26950
  if (!pointer) return null;
26892
- gitDir = (0, import_node_path50.resolve)(root, pointer[1].trim());
26951
+ gitDir = (0, import_node_path51.resolve)(root, pointer[1].trim());
26893
26952
  }
26894
- if (!(await (0, import_promises40.lstat)(gitDir)).isDirectory() || (await (0, import_promises40.lstat)(gitDir)).isSymbolicLink()) return null;
26895
- const head = (await regular((0, import_node_path50.join)(gitDir, "HEAD"))).trim();
26953
+ if (!(await (0, import_promises41.lstat)(gitDir)).isDirectory() || (await (0, import_promises41.lstat)(gitDir)).isSymbolicLink()) return null;
26954
+ const head = (await regular((0, import_node_path51.join)(gitDir, "HEAD"))).trim();
26896
26955
  if (/^[a-f0-9]{40,64}$/.test(head)) return head;
26897
26956
  const ref = /^ref: (refs\/[A-Za-z0-9_./-]+)$/.exec(head)?.[1];
26898
26957
  if (!ref || !validVerificationPath(ref)) return null;
26899
26958
  let common2 = gitDir;
26900
26959
  try {
26901
- const value = (await regular((0, import_node_path50.join)(gitDir, "commondir"))).trim();
26902
- if (value) common2 = (0, import_node_path50.resolve)(gitDir, value);
26960
+ const value = (await regular((0, import_node_path51.join)(gitDir, "commondir"))).trim();
26961
+ if (value) common2 = (0, import_node_path51.resolve)(gitDir, value);
26903
26962
  } catch {
26904
26963
  }
26905
26964
  try {
26906
- const sha2 = (await regular((0, import_node_path50.join)(common2, ref))).trim();
26965
+ const sha2 = (await regular((0, import_node_path51.join)(common2, ref))).trim();
26907
26966
  if (/^[a-f0-9]{40,64}$/.test(sha2)) return sha2;
26908
26967
  } catch {
26909
26968
  }
26910
- const packed = await regular((0, import_node_path50.join)(common2, "packed-refs"), 4e6);
26969
+ const packed = await regular((0, import_node_path51.join)(common2, "packed-refs"), 4e6);
26911
26970
  for (const line of packed.split(/\r?\n/)) {
26912
26971
  const [sha2, name] = line.split(" ");
26913
26972
  if (name === ref && /^[a-f0-9]{40,64}$/.test(sha2)) return sha2;
@@ -26918,18 +26977,18 @@ async function headSha(root) {
26918
26977
  }
26919
26978
  }
26920
26979
  async function captureSnapshot(root, destination) {
26921
- root = await (0, import_promises40.realpath)(root);
26922
- if (destination && ((0, import_node_path50.resolve)(destination) === root || inside2(root, destination))) throw new Error("Verification snapshots must stay outside the repository.");
26980
+ root = await (0, import_promises41.realpath)(root);
26981
+ if (destination && ((0, import_node_path51.resolve)(destination) === root || inside2(root, destination))) throw new Error("Verification snapshots must stay outside the repository.");
26923
26982
  const files = [], excluded = [];
26924
26983
  let totalBytes = 0, entries = 0;
26925
- if (destination) await (0, import_promises40.mkdir)(destination, { recursive: true, mode: 493 });
26984
+ if (destination) await (0, import_promises41.mkdir)(destination, { recursive: true, mode: 493 });
26926
26985
  async function walk(folder, prefix, depth) {
26927
26986
  if (depth > SNAPSHOT_LIMITS.depth) throw new Error("Snapshot depth exceeds the complete-input limit.");
26928
- for (const name of (await (0, import_promises40.readdir)(folder)).sort()) {
26987
+ for (const name of (await (0, import_promises41.readdir)(folder)).sort()) {
26929
26988
  if (++entries > SNAPSHOT_LIMITS.files * 2) throw new Error("Snapshot directory entry limit exceeded; no complete proof is possible.");
26930
- const path2 = prefix ? `${prefix}/${name}` : name, absolute = (0, import_node_path50.join)(folder, name);
26931
- const info = await (0, import_promises40.lstat)(absolute);
26932
- if (info.isSymbolicLink() || await (0, import_promises40.realpath)(absolute) !== absolute) throw new Error("Snapshot refuses symlinks, junctions and noncanonical paths.");
26989
+ const path2 = prefix ? `${prefix}/${name}` : name, absolute = (0, import_node_path51.join)(folder, name);
26990
+ const info = await (0, import_promises41.lstat)(absolute);
26991
+ if (info.isSymbolicLink() || await (0, import_promises41.realpath)(absolute) !== absolute) throw new Error("Snapshot refuses symlinks, junctions and noncanonical paths.");
26933
26992
  if (!validVerificationPath(path2)) throw new Error("Snapshot contains an unsupported path.");
26934
26993
  if (excludedDirectories.has(name.toLowerCase())) {
26935
26994
  excluded.push({ path: path2, reason: "generated_or_private" });
@@ -26945,13 +27004,13 @@ async function captureSnapshot(root, destination) {
26945
27004
  }
26946
27005
  if (!info.isFile()) throw new Error("Snapshot refuses non-regular input files.");
26947
27006
  if (files.length >= SNAPSHOT_LIMITS.files || info.size > SNAPSHOT_LIMITS.fileBytes || totalBytes + info.size > SNAPSHOT_LIMITS.totalBytes) throw new Error("Snapshot byte/file limit exceeded; inputs are incomplete.");
26948
- const bytes = await (0, import_promises40.readFile)(absolute), after = await (0, import_promises40.lstat)(absolute);
27007
+ const bytes = await (0, import_promises41.readFile)(absolute), after = await (0, import_promises41.lstat)(absolute);
26949
27008
  if (after.isSymbolicLink() || info.size !== bytes.length || after.size !== info.size || after.mtimeMs !== info.mtimeMs || after.ino !== info.ino || after.ctimeMs !== info.ctimeMs) throw new Error("Repository changed while snapshotting; retry with a stable worktree.");
26950
27009
  totalBytes += bytes.length;
26951
27010
  files.push({ path: path2, sha256: hashBytes(bytes), bytes: bytes.length });
26952
27011
  if (destination) {
26953
- await (0, import_promises40.mkdir)((0, import_node_path50.join)(destination, ...path2.split("/").slice(0, -1)), { recursive: true, mode: 493 });
26954
- await (0, import_promises40.writeFile)((0, import_node_path50.join)(destination, path2), bytes, { mode: 292, flag: "wx" });
27012
+ await (0, import_promises41.mkdir)((0, import_node_path51.join)(destination, ...path2.split("/").slice(0, -1)), { recursive: true, mode: 493 });
27013
+ await (0, import_promises41.writeFile)((0, import_node_path51.join)(destination, path2), bytes, { mode: 292, flag: "wx" });
26955
27014
  }
26956
27015
  }
26957
27016
  }
@@ -26960,7 +27019,7 @@ async function captureSnapshot(root, destination) {
26960
27019
  return { ...data, hash: snapshotHash(data), headSha: await headSha(root) };
26961
27020
  }
26962
27021
  async function validateSnapshot(root, saved) {
26963
- if ((await (0, import_promises40.lstat)(root)).isSymbolicLink() || await (0, import_promises40.realpath)(root) !== (0, import_node_path50.resolve)(root)) throw new Error("Saved snapshot root cannot be a symlink or junction.");
27022
+ if ((await (0, import_promises41.lstat)(root)).isSymbolicLink() || await (0, import_promises41.realpath)(root) !== (0, import_node_path51.resolve)(root)) throw new Error("Saved snapshot root cannot be a symlink or junction.");
26964
27023
  if (!saved || saved.version !== 1 || saved.hash !== snapshotHash({ version: 1, files: saved.files, excluded: saved.excluded, totalBytes: saved.totalBytes })) throw new Error("Saved snapshot manifest hash mismatch.");
26965
27024
  const actual = await captureSnapshot(root);
26966
27025
  if (hashJson(actual.files) !== hashJson(saved.files) || actual.totalBytes !== saved.totalBytes || actual.excluded.length) throw new Error("Saved snapshot bytes were changed or are incomplete.");
@@ -26969,12 +27028,12 @@ function changedPaths(before, after) {
26969
27028
  const a = new Map(before.files.map((f) => [f.path, f.sha256])), b3 = new Map(after.files.map((f) => [f.path, f.sha256]));
26970
27029
  return [.../* @__PURE__ */ new Set([...a.keys(), ...b3.keys()])].filter((path2) => a.get(path2) !== b3.get(path2)).sort();
26971
27030
  }
26972
- var import_promises40, import_node_path50, SNAPSHOT_LIMITS, excludedDirectories, verificationSecretPath;
27031
+ var import_promises41, import_node_path51, SNAPSHOT_LIMITS, excludedDirectories, verificationSecretPath;
26973
27032
  var init_snapshot = __esm({
26974
27033
  "src/verification/snapshot.ts"() {
26975
27034
  "use strict";
26976
- import_promises40 = require("node:fs/promises");
26977
- import_node_path50 = require("node:path");
27035
+ import_promises41 = require("node:fs/promises");
27036
+ import_node_path51 = require("node:path");
26978
27037
  init_workspace();
26979
27038
  init_verificationPlan();
26980
27039
  SNAPSHOT_LIMITS = { files: 3e3, fileBytes: 2e6, totalBytes: 32e6, depth: 32 };
@@ -27045,13 +27104,13 @@ emit(JSON.stringify({protocol:1,nonce:input.nonce,...output})+'\\n',()=>exit(0))
27045
27104
 
27046
27105
  // src/verification/runtime.ts
27047
27106
  async function resolveDockerExecutable(searchPath = process.env.PATH ?? process.env.Path ?? "", projectCwd = process.cwd()) {
27048
- const project = await (0, import_promises41.realpath)(projectCwd);
27049
- for (let folder of searchPath.split(import_node_path51.delimiter)) {
27107
+ const project = await (0, import_promises42.realpath)(projectCwd);
27108
+ for (let folder of searchPath.split(import_node_path52.delimiter)) {
27050
27109
  folder = folder.replace(/^"(.*)"$/, "$1");
27051
- if (!(0, import_node_path51.isAbsolute)(folder) || /^(?:\\\\|\/\/)/.test(folder)) continue;
27110
+ if (!(0, import_node_path52.isAbsolute)(folder) || /^(?:\\\\|\/\/)/.test(folder)) continue;
27052
27111
  try {
27053
- const executable = await (0, import_promises41.realpath)((0, import_node_path51.join)(folder, process.platform === "win32" ? "docker.exe" : "docker"));
27054
- if (executable === project || inside2(project, executable) || !(await (0, import_promises41.lstat)(executable)).isFile()) continue;
27112
+ const executable = await (0, import_promises42.realpath)((0, import_node_path52.join)(folder, process.platform === "win32" ? "docker.exe" : "docker"));
27113
+ if (executable === project || inside2(project, executable) || !(await (0, import_promises42.lstat)(executable)).isFile()) continue;
27055
27114
  return executable;
27056
27115
  } catch {
27057
27116
  }
@@ -27059,7 +27118,7 @@ async function resolveDockerExecutable(searchPath = process.env.PATH ?? process.
27059
27118
  return null;
27060
27119
  }
27061
27120
  async function dockerCommand(args, config, timeoutMs, signal, projectCwd = process.cwd()) {
27062
- const executable = await resolveDockerExecutable(void 0, projectCwd), launchCwd = (0, import_node_path51.resolve)(config);
27121
+ const executable = await resolveDockerExecutable(void 0, projectCwd), launchCwd = (0, import_node_path52.resolve)(config);
27063
27122
  if (!executable) return { code: null, stdout: "", stderr: "", truncated: false, timedOut: false, cancelled: !!signal?.aborted };
27064
27123
  const env = { DOCKER_CONFIG: config };
27065
27124
  for (const key of ["SystemRoot", "SYSTEMROOT", "WINDIR", "TEMP", "TMP"]) if (process.env[key]) env[key] = process.env[key];
@@ -27257,14 +27316,14 @@ function verificationInputs(check, nonce) {
27257
27316
  if (check.adapter === "http") return { nonce, module: check.module, export: check.export, requests: check.cases.map((c) => c.request) };
27258
27317
  return { nonce, entry: check.entry };
27259
27318
  }
27260
- var import_node_child_process14, import_node_crypto11, import_promises41, import_node_path51, import_node_util7, DEFAULT_IMAGE, RUNTIME_LIMITS, HTTP_ERRORS, MODULE_RUNNER, TEST_RUNNER, dockerRuntime;
27319
+ var import_node_child_process14, import_node_crypto11, import_promises42, import_node_path52, import_node_util7, DEFAULT_IMAGE, RUNTIME_LIMITS, HTTP_ERRORS, MODULE_RUNNER, TEST_RUNNER, dockerRuntime;
27261
27320
  var init_runtime = __esm({
27262
27321
  "src/verification/runtime.ts"() {
27263
27322
  "use strict";
27264
27323
  import_node_child_process14 = require("node:child_process");
27265
27324
  import_node_crypto11 = require("node:crypto");
27266
- import_promises41 = require("node:fs/promises");
27267
- import_node_path51 = require("node:path");
27325
+ import_promises42 = require("node:fs/promises");
27326
+ import_node_path52 = require("node:path");
27268
27327
  import_node_util7 = require("node:util");
27269
27328
  init_verificationPlan();
27270
27329
  init_httpRunner();
@@ -27318,38 +27377,38 @@ try {
27318
27377
  dockerRuntime = {
27319
27378
  async cleanup(runId, scratchDir, projectCwd) {
27320
27379
  if (!/^[a-f0-9-]{36}$/.test(runId)) throw new Error("Invalid owned verification container ID.");
27321
- await dockerCommand(["rm", "--force", `vr-verify-${runId}`], (0, import_node_path51.join)(scratchDir, "docker-config"), 5e3, void 0, projectCwd);
27380
+ await dockerCommand(["rm", "--force", `vr-verify-${runId}`], (0, import_node_path52.join)(scratchDir, "docker-config"), 5e3, void 0, projectCwd);
27322
27381
  },
27323
27382
  async resolveImage(scratchDir, projectCwd) {
27324
- const config = (0, import_node_path51.join)(scratchDir, "docker-config");
27325
- await (0, import_promises41.mkdir)(config, { recursive: true, mode: 448 });
27383
+ const config = (0, import_node_path52.join)(scratchDir, "docker-config");
27384
+ await (0, import_promises42.mkdir)(config, { recursive: true, mode: 448 });
27326
27385
  const result = await dockerCommand(["image", "inspect", "--format", "{{.Id}}", DEFAULT_IMAGE], config, 5e3, void 0, projectCwd);
27327
27386
  return result.code === 0 && /^sha256:[a-f0-9]{64}$/.test(result.stdout.trim()) ? result.stdout.trim() : null;
27328
27387
  },
27329
27388
  async run({ check, snapshotDir, scratchDir, image, signal, runId = (0, import_node_crypto11.randomUUID)(), projectCwd = snapshotDir }) {
27330
27389
  if (!/^[a-f0-9-]{36}$/.test(runId)) throw new Error("Invalid owned verification container ID.");
27331
27390
  const startedAt = (/* @__PURE__ */ new Date()).toISOString(), start = Date.now(), name = `vr-verify-${runId}`;
27332
- const dir = await (0, import_promises41.mkdtemp)((0, import_node_path51.join)(scratchDir, "oracle-")), config = (0, import_node_path51.join)(scratchDir, "docker-config"), nonce = (0, import_node_crypto11.randomUUID)();
27391
+ const dir = await (0, import_promises42.mkdtemp)((0, import_node_path52.join)(scratchDir, "oracle-")), config = (0, import_node_path52.join)(scratchDir, "docker-config"), nonce = (0, import_node_crypto11.randomUUID)();
27333
27392
  try {
27334
- await (0, import_promises41.chmod)(dir, 493);
27335
- await (0, import_promises41.mkdir)(config, { recursive: true, mode: 448 });
27393
+ await (0, import_promises42.chmod)(dir, 493);
27394
+ await (0, import_promises42.mkdir)(config, { recursive: true, mode: 448 });
27336
27395
  const inputs = verificationInputs(check, nonce);
27337
- await (0, import_promises41.writeFile)((0, import_node_path51.join)(dir, "input.json"), JSON.stringify(inputs), { mode: 292 });
27338
- await (0, import_promises41.writeFile)((0, import_node_path51.join)(dir, "runner.mjs"), check.adapter === "module" ? MODULE_RUNNER : check.adapter === "http" ? HTTP_RUNNER : TEST_RUNNER, { mode: 292 });
27396
+ await (0, import_promises42.writeFile)((0, import_node_path52.join)(dir, "input.json"), JSON.stringify(inputs), { mode: 292 });
27397
+ await (0, import_promises42.writeFile)((0, import_node_path52.join)(dir, "runner.mjs"), check.adapter === "module" ? MODULE_RUNNER : check.adapter === "http" ? HTTP_RUNNER : TEST_RUNNER, { mode: 292 });
27339
27398
  const raw = await dockerCommand(dockerRunArgs(name, image, snapshotDir, dir), config, RUNTIME_LIMITS.timeoutMs, signal, projectCwd);
27340
27399
  const observation = compareObservation(check, raw, nonce, image, startedAt, Date.now() - start);
27341
27400
  const sanitized = verificationArtifactText(check, raw);
27342
- const artifactId = (0, import_node_crypto11.randomUUID)(), artifacts = (0, import_node_path51.join)(scratchDir, "artifacts", artifactId);
27343
- await (0, import_promises41.mkdir)(artifacts, { recursive: true, mode: 448 });
27344
- await (0, import_promises41.writeFile)((0, import_node_path51.join)(artifacts, "stdout.txt"), sanitized.stdout, { mode: 384 });
27345
- await (0, import_promises41.writeFile)((0, import_node_path51.join)(artifacts, "stderr.txt"), sanitized.stderr, { mode: 384 });
27401
+ const artifactId = (0, import_node_crypto11.randomUUID)(), artifacts = (0, import_node_path52.join)(scratchDir, "artifacts", artifactId);
27402
+ await (0, import_promises42.mkdir)(artifacts, { recursive: true, mode: 448 });
27403
+ await (0, import_promises42.writeFile)((0, import_node_path52.join)(artifacts, "stdout.txt"), sanitized.stdout, { mode: 384 });
27404
+ await (0, import_promises42.writeFile)((0, import_node_path52.join)(artifacts, "stderr.txt"), sanitized.stderr, { mode: 384 });
27346
27405
  observation.artifacts.stdoutRef = `runtime/artifacts/${artifactId}/stdout.txt`;
27347
27406
  observation.artifacts.stderrRef = `runtime/artifacts/${artifactId}/stderr.txt`;
27348
27407
  return observation;
27349
27408
  } finally {
27350
27409
  await dockerCommand(["rm", "--force", name], config, 5e3, void 0, projectCwd);
27351
27410
  if (!inside2(scratchDir, dir)) throw new Error("Invalid owned oracle directory.");
27352
- await (0, import_promises41.rm)(dir, { recursive: true, force: true });
27411
+ await (0, import_promises42.rm)(dir, { recursive: true, force: true });
27353
27412
  }
27354
27413
  }
27355
27414
  };
@@ -27363,12 +27422,12 @@ async function storage(store, id2) {
27363
27422
  async function saveState(dir, state, reservedBytes = 0) {
27364
27423
  const serialized = JSON.stringify(state);
27365
27424
  if (Buffer.byteLength(serialized, "utf8") + reservedBytes > MAX_VERIFICATION_STATE_BYTES) throw new Error("Verification aggregate state budget (8000000 UTF-8 bytes) reached. Previous receipts are preserved; create a new prepared task.");
27366
- const temp = (0, import_node_path52.join)(dir, `${(0, import_node_crypto12.randomUUID)()}.tmp`);
27425
+ const temp = (0, import_node_path53.join)(dir, `${(0, import_node_crypto12.randomUUID)()}.tmp`);
27367
27426
  try {
27368
- await (0, import_promises42.writeFile)(temp, serialized, { flag: "wx", mode: 384 });
27369
- await (0, import_promises42.rename)(temp, (0, import_node_path52.join)(dir, "state.json"));
27427
+ await (0, import_promises43.writeFile)(temp, serialized, { flag: "wx", mode: 384 });
27428
+ await (0, import_promises43.rename)(temp, (0, import_node_path53.join)(dir, "state.json"));
27370
27429
  } finally {
27371
- await (0, import_promises42.rm)(temp, { force: true });
27430
+ await (0, import_promises43.rm)(temp, { force: true });
27372
27431
  }
27373
27432
  }
27374
27433
  function receiptReserve(state, attempt) {
@@ -27378,25 +27437,25 @@ function receiptReserve(state, attempt) {
27378
27437
  }).length, 0) * RECEIPT_RESERVE_BYTES;
27379
27438
  }
27380
27439
  async function removeUnsavedAttempt(dir, id2) {
27381
- const parent = (0, import_node_path52.join)(dir, "attempts"), target = (0, import_node_path52.join)(parent, id2);
27440
+ const parent = (0, import_node_path53.join)(dir, "attempts"), target = (0, import_node_path53.join)(parent, id2);
27382
27441
  let actual;
27383
27442
  try {
27384
- actual = await (0, import_promises42.realpath)(target);
27443
+ actual = await (0, import_promises43.realpath)(target);
27385
27444
  } catch (error) {
27386
27445
  if (error.code === "ENOENT") return;
27387
27446
  throw error;
27388
27447
  }
27389
- if (!/^[a-f0-9-]{36}$/.test(id2) || actual !== (0, import_node_path52.resolve)(target) || !inside2(parent, actual)) throw new Error("Refusing cleanup outside the owned unsaved attempt.");
27390
- await (0, import_promises42.rm)(actual, { recursive: true, force: true });
27448
+ if (!/^[a-f0-9-]{36}$/.test(id2) || actual !== (0, import_node_path53.resolve)(target) || !inside2(parent, actual)) throw new Error("Refusing cleanup outside the owned unsaved attempt.");
27449
+ await (0, import_promises43.rm)(actual, { recursive: true, force: true });
27391
27450
  }
27392
27451
  async function readState2(store, task) {
27393
27452
  normalizeWorkContract(task);
27394
27453
  if (!task.verification) throw new Error("Prepare a reviewed verification plan before editing or verifying.");
27395
27454
  const dir = await storage(store, task.id);
27396
- const stateInfo = await (0, import_promises42.lstat)((0, import_node_path52.join)(dir, "state.json"));
27397
- if ((await (0, import_promises42.lstat)(dir)).isSymbolicLink() || stateInfo.isSymbolicLink() || !stateInfo.isFile()) throw new Error("Verification storage must be regular, not a symlink.");
27455
+ const stateInfo = await (0, import_promises43.lstat)((0, import_node_path53.join)(dir, "state.json"));
27456
+ if ((await (0, import_promises43.lstat)(dir)).isSymbolicLink() || stateInfo.isSymbolicLink() || !stateInfo.isFile()) throw new Error("Verification storage must be regular, not a symlink.");
27398
27457
  if (stateInfo.size > MAX_VERIFICATION_STATE_BYTES) throw new Error("Verification state exceeds its size bound.");
27399
- const bytes = await (0, import_promises42.readFile)((0, import_node_path52.join)(dir, "state.json"));
27458
+ const bytes = await (0, import_promises43.readFile)((0, import_node_path53.join)(dir, "state.json"));
27400
27459
  if (bytes.length > MAX_VERIFICATION_STATE_BYTES) throw new Error("Verification state exceeds its size bound.");
27401
27460
  const state = JSON.parse(bytes.toString("utf8"));
27402
27461
  if (state.version !== 1 || hashJson(state.prepared) !== hashJson(task.verification) || state.prepared.contractHash !== task.contractHash || hashJson(parseVerificationPlan(state.plan, task.contract)) !== state.prepared.planHash || state.baseline.hash !== state.prepared.baselineHash) throw new Error("Saved verification plan or baseline hash mismatch.");
@@ -27404,7 +27463,7 @@ async function readState2(store, task) {
27404
27463
  if (state.attempts.length && !state.approvedAt || state.approvedAt && !Number.isFinite(Date.parse(state.approvedAt))) throw new Error("Invalid saved verification approval.");
27405
27464
  for (const a of state.attempts) {
27406
27465
  if (!/^[a-f0-9-]{36}$/.test(a.id) || !/^sha256:[a-f0-9]{64}$/.test(a.image) || !Array.isArray(a.receipts)) throw new Error("Invalid verification attempt.");
27407
- await validateSnapshot((0, import_node_path52.join)(dir, "attempts", a.id, "source"), a.candidate);
27466
+ await validateSnapshot((0, import_node_path53.join)(dir, "attempts", a.id, "source"), a.candidate);
27408
27467
  }
27409
27468
  for (const list of [state.baselineReceipts, ...state.attempts.map((a) => a.receipts)]) {
27410
27469
  if (list.length > 32 || new Set(list.map((r) => r.checkId)).size !== list.length) throw new Error("Invalid verification receipt count.");
@@ -27415,13 +27474,13 @@ async function readState2(store, task) {
27415
27474
  const ref = receipt.observation?.artifacts[`${stream}Ref`];
27416
27475
  if (ref) {
27417
27476
  if (!new RegExp(`^runtime/artifacts/[a-f0-9-]{36}/${stream}\\.txt$`).test(ref)) throw new Error("Invalid verification artifact reference.");
27418
- const file = (0, import_node_path52.join)(dir, ref), info = await (0, import_promises42.lstat)(file);
27419
- if (!info.isFile() || info.isSymbolicLink() || info.size > 131072 || hashBytes(await (0, import_promises42.readFile)(file)) !== receipt.observation.artifacts[`${stream}Sha256`]) throw new Error("Saved verification artifact hash mismatch.");
27477
+ const file = (0, import_node_path53.join)(dir, ref), info = await (0, import_promises43.lstat)(file);
27478
+ if (!info.isFile() || info.isSymbolicLink() || info.size > 131072 || hashBytes(await (0, import_promises43.readFile)(file)) !== receipt.observation.artifacts[`${stream}Sha256`]) throw new Error("Saved verification artifact hash mismatch.");
27420
27479
  }
27421
27480
  }
27422
27481
  }
27423
27482
  }
27424
- await validateSnapshot((0, import_node_path52.join)(dir, "baseline"), state.baseline);
27483
+ await validateSnapshot((0, import_node_path53.join)(dir, "baseline"), state.baseline);
27425
27484
  return { dir, state };
27426
27485
  }
27427
27486
  async function prepareVerification(store, task, rawPlan) {
@@ -27437,8 +27496,8 @@ async function prepareVerification(store, task, rawPlan) {
27437
27496
  for (const path2 of check.adapter === "node-test" ? [check.entry, ...check.frozenPaths] : [check.module]) if (verificationSecretPath(path2)) throw new Error("Secret paths cannot be verification inputs.");
27438
27497
  }
27439
27498
  const dir = await storage(store, task.id);
27440
- await (0, import_promises42.mkdir)(dir, { mode: 448 });
27441
- const baseline = await captureSnapshot(task.cwd, (0, import_node_path52.join)(dir, "baseline"));
27499
+ await (0, import_promises43.mkdir)(dir, { mode: 448 });
27500
+ const baseline = await captureSnapshot(task.cwd, (0, import_node_path53.join)(dir, "baseline"));
27442
27501
  const stable = await captureSnapshot(task.cwd);
27443
27502
  if (stable.hash !== baseline.hash) throw new Error("Repository changed while preparing; create a fresh prepared task.");
27444
27503
  const prepared = { version: 1, contractHash: task.contractHash, planHash, baselineHash: baseline.hash, preparedAt: (/* @__PURE__ */ new Date()).toISOString() };
@@ -27532,19 +27591,19 @@ async function verificationView(store, task) {
27532
27591
  }
27533
27592
  }
27534
27593
  async function frozenTestSource(dir, state, attempt, check) {
27535
- const source = (0, import_node_path52.join)(dir, "attempts", attempt.id, "source");
27594
+ const source = (0, import_node_path53.join)(dir, "attempts", attempt.id, "source");
27536
27595
  if (check.adapter !== "node-test") return source;
27537
- const target = (0, import_node_path52.join)(dir, "attempts", attempt.id, `frozen-${check.id}`);
27596
+ const target = (0, import_node_path53.join)(dir, "attempts", attempt.id, `frozen-${check.id}`);
27538
27597
  if (!inside2(dir, target)) throw new Error("Invalid owned frozen-test directory.");
27539
- await (0, import_promises42.rm)(target, { recursive: true, force: true });
27540
- await (0, import_promises42.mkdir)(target, { recursive: true, mode: 493 });
27598
+ await (0, import_promises43.rm)(target, { recursive: true, force: true });
27599
+ await (0, import_promises43.mkdir)(target, { recursive: true, mode: 493 });
27541
27600
  const frozen = (path2) => frozenVerificationPath(path2) || check.frozenPaths.some((p2) => within(path2, p2));
27542
- for (const [snapshot, root, isBaseline] of [[attempt.candidate, source, false], [state.baseline, (0, import_node_path52.join)(dir, "baseline"), true]]) {
27601
+ for (const [snapshot, root, isBaseline] of [[attempt.candidate, source, false], [state.baseline, (0, import_node_path53.join)(dir, "baseline"), true]]) {
27543
27602
  for (const file of snapshot.files) {
27544
27603
  if (frozen(file.path) !== isBaseline) continue;
27545
- const to = (0, import_node_path52.join)(target, file.path);
27546
- await (0, import_promises42.mkdir)((0, import_node_path52.dirname)(to), { recursive: true, mode: 493 });
27547
- await (0, import_promises42.copyFile)((0, import_node_path52.join)(root, file.path), to);
27604
+ const to = (0, import_node_path53.join)(target, file.path);
27605
+ await (0, import_promises43.mkdir)((0, import_node_path53.dirname)(to), { recursive: true, mode: 493 });
27606
+ await (0, import_promises43.copyFile)((0, import_node_path53.join)(root, file.path), to);
27548
27607
  }
27549
27608
  }
27550
27609
  return target;
@@ -27554,8 +27613,8 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
27554
27613
  const { dir, state } = await readState2(store, task);
27555
27614
  state.approvedAt ??= (/* @__PURE__ */ new Date()).toISOString();
27556
27615
  await saveState(dir, state);
27557
- const scratch = (0, import_node_path52.join)(dir, "runtime");
27558
- await (0, import_promises42.mkdir)(scratch, { recursive: true, mode: 493 });
27616
+ const scratch = (0, import_node_path53.join)(dir, "runtime");
27617
+ await (0, import_promises43.mkdir)(scratch, { recursive: true, mode: 493 });
27559
27618
  const image = await runtime.resolveImage(scratch, task.cwd);
27560
27619
  if (!image) {
27561
27620
  const previous = state.attempts.at(-1);
@@ -27573,7 +27632,7 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
27573
27632
  if (state.attempts.length >= 16) throw new Error("Verification attempt limit (16) reached. Create a new task.");
27574
27633
  const id2 = (0, import_node_crypto12.randomUUID)();
27575
27634
  try {
27576
- const candidate = await captureSnapshot(task.cwd, (0, import_node_path52.join)(dir, "attempts", id2, "source"));
27635
+ const candidate = await captureSnapshot(task.cwd, (0, import_node_path53.join)(dir, "attempts", id2, "source"));
27577
27636
  if (candidate.hash !== current.hash) throw new Error("Repository changed while capturing candidate. Retry with a stable worktree.");
27578
27637
  attempt = { id: id2, candidate, image, createdAt: (/* @__PURE__ */ new Date()).toISOString(), receipts: [] };
27579
27638
  state.attempts.push(attempt);
@@ -27611,7 +27670,7 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
27611
27670
  const needed = check.adapter === "node-test" ? [check.entry, ...check.frozenPaths] : [check.module];
27612
27671
  if (needed.some((p2) => !state.baseline.files.some((f) => within(f.path, p2))) || check.adapter !== "node-test" && !snapshot.files.some((f) => f.path === check.module)) receipt.observation = unknownObservation("missing_frozen_input", image, check);
27613
27672
  else {
27614
- const snapshotDir = phase === "baseline" ? (0, import_node_path52.join)(dir, "baseline") : await frozenTestSource(dir, state, active, check);
27673
+ const snapshotDir = phase === "baseline" ? (0, import_node_path53.join)(dir, "baseline") : await frozenTestSource(dir, state, active, check);
27615
27674
  receipt.observation = await runtime.run({ check, snapshotDir, scratchDir: scratch, image, signal, runId: receipt.id, projectCwd: task.cwd });
27616
27675
  }
27617
27676
  } catch {
@@ -27625,13 +27684,13 @@ async function verifyVerification(store, task, approved, runtime = dockerRuntime
27625
27684
  const after = await captureSnapshot(task.cwd);
27626
27685
  return report(task, state, active, after.hash !== active.candidate.hash);
27627
27686
  }
27628
- var import_node_crypto12, import_promises42, import_node_path52, MAX_VERIFICATION_STATE_BYTES, RECEIPT_RESERVE_BYTES, within, emptyArtifacts, unknownObservation, receiptHash;
27687
+ var import_node_crypto12, import_promises43, import_node_path53, MAX_VERIFICATION_STATE_BYTES, RECEIPT_RESERVE_BYTES, within, emptyArtifacts, unknownObservation, receiptHash;
27629
27688
  var init_verification2 = __esm({
27630
27689
  "src/verification/index.ts"() {
27631
27690
  "use strict";
27632
27691
  import_node_crypto12 = require("node:crypto");
27633
- import_promises42 = require("node:fs/promises");
27634
- import_node_path52 = require("node:path");
27692
+ import_promises43 = require("node:fs/promises");
27693
+ import_node_path53 = require("node:path");
27635
27694
  init_workStore();
27636
27695
  init_verificationPlan();
27637
27696
  init_snapshot();
@@ -27680,7 +27739,7 @@ function workView(task, extra = {}) {
27680
27739
  async function runWork(options, deps) {
27681
27740
  const url = new URL(deps.api);
27682
27741
  if (url.username || url.password || url.search || url.hash || url.protocol !== "https:" && !(url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))) throw new Error("Work API must use HTTPS (HTTP is allowed only on loopback).");
27683
- const cwd = await (0, import_promises43.realpath)(options.cwd);
27742
+ const cwd = await (0, import_promises44.realpath)(options.cwd);
27684
27743
  const store = new WorkStore(cwd, deps.configDir);
27685
27744
  const credentials = new CredentialStore(deps.configDir, cwd);
27686
27745
  const call2 = async (method, path2, body, token) => {
@@ -27703,9 +27762,9 @@ async function runWork(options, deps) {
27703
27762
  if (options.approvedContract !== void 0 && typeof options.approvedContract !== "boolean") throw new Error("Contract approval must be a boolean.");
27704
27763
  let supplied;
27705
27764
  if (options.contractPath !== void 0) {
27706
- const path2 = (0, import_node_path53.resolve)(cwd, options.contractPath);
27707
- if ((await (0, import_promises43.stat)(path2)).size > TASK_CONTRACT_MAX_BYTES) throw new Error("Task contract file exceeds 4800 UTF-8 bytes.");
27708
- supplied = parseTaskContract(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0, import_promises43.readFile)(path2))));
27765
+ const path2 = (0, import_node_path54.resolve)(cwd, options.contractPath);
27766
+ if ((await (0, import_promises44.stat)(path2)).size > TASK_CONTRACT_MAX_BYTES) throw new Error("Task contract file exceeds 4800 UTF-8 bytes.");
27767
+ supplied = parseTaskContract(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0, import_promises44.readFile)(path2))));
27709
27768
  } else if (options.contractBase64 !== void 0) supplied = decodeTaskContract(options.contractBase64);
27710
27769
  else if (options.contract !== void 0) supplied = parseTaskContract(options.contract);
27711
27770
  let task;
@@ -27739,10 +27798,10 @@ async function runWork(options, deps) {
27739
27798
  if (options.action === "prepare") {
27740
27799
  let plan = options.verification;
27741
27800
  if (options.verificationPath !== void 0) {
27742
- const path2 = await (0, import_promises43.realpath)((0, import_node_path53.resolve)(cwd, options.verificationPath));
27801
+ const path2 = await (0, import_promises44.realpath)((0, import_node_path54.resolve)(cwd, options.verificationPath));
27743
27802
  if (path2 === cwd || inside2(cwd, path2)) throw new Error("Keep the reviewed verification plan outside the repository so expectations cannot enter candidate snapshots.");
27744
- if ((await (0, import_promises43.stat)(path2)).size > VERIFICATION_PLAN_MAX_BYTES) throw new Error("Verification plan exceeds 65536 UTF-8 bytes.");
27745
- plan = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0, import_promises43.readFile)(path2)));
27803
+ if ((await (0, import_promises44.stat)(path2)).size > VERIFICATION_PLAN_MAX_BYTES) throw new Error("Verification plan exceeds 65536 UTF-8 bytes.");
27804
+ plan = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await (0, import_promises44.readFile)(path2)));
27746
27805
  } else if (options.verificationBase64 !== void 0) plan = decodeVerificationPlan(options.verificationBase64);
27747
27806
  verification = await prepareVerification(store, task, plan);
27748
27807
  } else verification = await verifyVerification(store, task, options.approvedVerification, deps.verificationRuntime, deps.signal);
@@ -27961,12 +28020,12 @@ function contractExecutionBlock(contract) {
27961
28020
  function assertWorkJob(task, job, executing) {
27962
28021
  if (job.kind !== "agent_change" || task.job && task.job.id !== job.id || job.deploySessionId !== task.sessionId || job.input?.requestId !== task.id || executing && (job.input?.request !== task.request || job.input?.cliId !== task.cliId || job.input?.accessMode !== task.accessMode) || job.input?.request !== void 0 && job.input.request !== task.request || job.input?.cliId !== void 0 && job.input.cliId !== task.cliId || job.input?.accessMode !== void 0 && job.input.accessMode !== task.accessMode) throw new Error("Remote job differs from the saved Task contract or job identity. No execution was started.");
27963
28022
  }
27964
- var import_promises43, import_node_path53, import_node_crypto13, WorkHttpError, terminal, repoKey;
28023
+ var import_promises44, import_node_path54, import_node_crypto13, WorkHttpError, terminal, repoKey;
27965
28024
  var init_work = __esm({
27966
28025
  "src/commands/work.ts"() {
27967
28026
  "use strict";
27968
- import_promises43 = require("node:fs/promises");
27969
- import_node_path53 = require("node:path");
28027
+ import_promises44 = require("node:fs/promises");
28028
+ import_node_path54 = require("node:path");
27970
28029
  init_taskContract();
27971
28030
  import_node_crypto13 = require("node:crypto");
27972
28031
  init_credentials();
@@ -28589,7 +28648,7 @@ async function runLive(options, deps) {
28589
28648
  const api = new URL(deps.api);
28590
28649
  if (api.username || api.password || api.search || api.hash || api.protocol !== "https:" && !(api.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(api.hostname))) throw new Error("Account API must use HTTPS; HTTP is restricted to loopback.");
28591
28650
  if (!["start", "resume", "status", "disconnect"].includes(options.action)) throw new Error("Use live start, resume, status or disconnect.");
28592
- const cwd = await (0, import_promises44.realpath)(options.cwd);
28651
+ const cwd = await (0, import_promises45.realpath)(options.cwd);
28593
28652
  const store = new WorkStore(cwd, deps.configDir, "live");
28594
28653
  const credentials = new CredentialStore(deps.configDir, cwd);
28595
28654
  const call2 = async (method, path2, body, token) => {
@@ -28754,12 +28813,12 @@ async function runLiveCommand(options) {
28754
28813
  return 1;
28755
28814
  }
28756
28815
  }
28757
- var import_node_crypto15, import_promises44, LiveHttpError, terminal2;
28816
+ var import_node_crypto15, import_promises45, LiveHttpError, terminal2;
28758
28817
  var init_live = __esm({
28759
28818
  "src/commands/live.ts"() {
28760
28819
  "use strict";
28761
28820
  import_node_crypto15 = require("node:crypto");
28762
- import_promises44 = require("node:fs/promises");
28821
+ import_promises45 = require("node:fs/promises");
28763
28822
  init_credentials();
28764
28823
  init_hostedClient();
28765
28824
  init_workStore();
@@ -29207,8 +29266,8 @@ function isSqlError(error) {
29207
29266
  }
29208
29267
  function displayPath(projectDir, path2) {
29209
29268
  for (const base2 of [projectDir, process.cwd()]) {
29210
- const rel = (0, import_node_path54.relative)(base2, path2);
29211
- if (rel && !rel.startsWith("..") && !(0, import_node_path54.isAbsolute)(rel)) return rel.split("\\").join("/");
29269
+ const rel = (0, import_node_path55.relative)(base2, path2);
29270
+ if (rel && !rel.startsWith("..") && !(0, import_node_path55.isAbsolute)(rel)) return rel.split("\\").join("/");
29212
29271
  }
29213
29272
  return path2;
29214
29273
  }
@@ -29466,7 +29525,7 @@ function writeControl(filtered, changed) {
29466
29525
  detail: filtered ? `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}, expected 1` : "as the superuser the same statement did not change the owner row"
29467
29526
  };
29468
29527
  }
29469
- return { ok: true, detail: `as the superuser the same statement changed ${plural3(filtered ? run.affected ?? 0 : 1, "row")}` };
29528
+ return { ok: true, detail: `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}` };
29470
29529
  };
29471
29530
  }
29472
29531
  async function differingValue(db, shape, target, column) {
@@ -29566,6 +29625,9 @@ function insertPlans(c) {
29566
29625
  }
29567
29626
  ];
29568
29627
  }
29628
+ function writeFilters(pkSelectable, noPrivilege) {
29629
+ return noPrivilege ? [true] : pkSelectable ? [true, false] : [false];
29630
+ }
29569
29631
  function freeOtherUserSlot(db, shape) {
29570
29632
  if (!shape.ownerUnique) return void 0;
29571
29633
  return async () => void await db.query(`delete from ${shape.qualified} where ${quoteIdent2(shape.owner.name)}::text = $1`, [OTHER_USER_ID]);
@@ -29582,50 +29644,55 @@ async function updatePlans(c) {
29582
29644
  const { db, shape, target, privileges, actor } = c;
29583
29645
  const pkSelectable = shape.pk.every((col) => privileges.column.get(col.name)?.select);
29584
29646
  const noPrivilege = !privileges.table.update && !privileges.any.update;
29585
- const filtered = pkSelectable || noPrivilege;
29586
- const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
29587
- const baseParams = filtered ? target : [];
29588
29647
  const ownerUpdatable = Boolean(privileges.column.get(shape.owner.name)?.update) || noPrivilege;
29589
29648
  const column = pickUpdateColumn(shape, privileges);
29590
29649
  const value = column ? await differingValue(db, shape, target, column) : void 0;
29591
29650
  const plans = [];
29592
- const add = (name, sets, display, noop = false, prep) => {
29593
- let n = baseParams.length;
29594
- const assignments = sets.map((s) => `${quoteIdent2(s.column)} = ${s.value === SELF ? quoteIdent2(s.column) : `$${++n}`}`).join(", ");
29595
- const sql = `update ${shape.qualified} set ${assignments}${where}`;
29596
- const params = [...baseParams, ...sets.filter((s) => s.value !== SELF).map((s) => s.value)];
29597
- const changed = (run) => noop ? (run.affected ?? 0) >= 1 && run.after.target !== null : run.after.target !== null && run.after.target !== run.before.target;
29598
- plans.push({
29599
- stmt: { name, sql, params, display: `${display}${filtered ? "" : ", no row filter (the role cannot read the primary key)"}`, filtered },
29600
- prep,
29601
- controlOk: writeControl(filtered, changed),
29602
- judge: (run) => {
29603
- if (isRlsRefusal(run)) return { verdict: "unknown", detail: `refused by WITH CHECK: ${run.error.message}. USING matched the owner row, so this is not a denial` };
29604
- if (isPrivilegeRefusal(run)) {
29605
- return noPrivilege ? { verdict: "denied", detail: `refused: ${run.error.message} (42501), no UPDATE on any column` } : { verdict: "unknown", detail: `refused for column privileges (${run.error.message}), but the role can update some columns` };
29651
+ for (const filtered of writeFilters(pkSelectable, noPrivilege)) {
29652
+ const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
29653
+ const baseParams = filtered ? target : [];
29654
+ const planStart = plans.length;
29655
+ const add = (name, sets, display, noop = false, prep) => {
29656
+ let n = baseParams.length;
29657
+ const assignments = sets.map((s) => `${quoteIdent2(s.column)} = ${s.value === SELF ? quoteIdent2(s.column) : `$${++n}`}`).join(", ");
29658
+ const sql = `update ${shape.qualified} set ${assignments}${where}`;
29659
+ const params = [...baseParams, ...sets.filter((s) => s.value !== SELF).map((s) => s.value)];
29660
+ const changed = (run) => noop ? (run.affected ?? 0) >= 1 && run.after.target !== null : run.after.target !== null && run.after.target !== run.before.target;
29661
+ plans.push({
29662
+ stmt: { name: filtered ? name : `blind ${name}`, sql, params, display: `${display}${filtered ? "" : ", no row filter (blind write)"}`, filtered },
29663
+ prep,
29664
+ controlOk: writeControl(filtered, changed),
29665
+ judge: (run) => {
29666
+ if (isRlsRefusal(run)) return { verdict: "unknown", detail: `refused by WITH CHECK: ${run.error.message}. This does not establish denial of the owner row` };
29667
+ if (isPrivilegeRefusal(run)) {
29668
+ return noPrivilege ? { verdict: "denied", detail: `refused: ${run.error.message} (42501), no UPDATE on any column` } : { verdict: "unknown", detail: `refused for column privileges (${run.error.message}), but the role can update some columns` };
29669
+ }
29670
+ if (run.error) return { verdict: "unknown", detail: `error ${run.error.code}: ${run.error.message}` };
29671
+ if (noop && !filtered && (run.affected ?? 0) > 0) {
29672
+ return { verdict: "unknown", detail: `${changedRows(run.affected)}, but an unfiltered no-op cannot identify whether the owner row was updated` };
29673
+ }
29674
+ if (changed(run)) return { verdict: "allowed", detail: noop ? `${plural3(run.affected ?? 0, "row")} updated (rewrote its own value)` : `${changedRows(run.affected)} (${sets.map((s) => s.column).join(" and ")} rewritten; owner row changed)` };
29675
+ if (!filtered) return { verdict: "denied", detail: "the owner row is unchanged" };
29676
+ if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
29677
+ return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row ${run.after.target === null ? "is gone" : "did not change"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change` };
29606
29678
  }
29607
- if (run.error) return { verdict: "unknown", detail: `error ${run.error.code}: ${run.error.message}` };
29608
- if (changed(run)) return { verdict: "allowed", detail: noop ? "1 row updated (rewrote its own value)" : `${changedRows(filtered ? run.affected : 1)} (${sets.map((s) => s.column).join(" and ")} rewritten)` };
29609
- if (!filtered) return { verdict: "denied", detail: "the owner row is unchanged" };
29610
- if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
29611
- return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row ${run.after.target === null ? "is gone" : "did not change"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change` };
29612
- }
29613
- });
29614
- };
29615
- if (column && value !== void 0) add("update value", [{ column: column.name, value }], `update ${column.name} on the owner's row in ${shape.display}`);
29616
- if (actor === "other_user" && ownerUpdatable && !shape.ownerInPk) {
29617
- const sets = [{ column: shape.owner.name, value: OTHER_USER_ID }];
29618
- if (column && value !== void 0) sets.push({ column: column.name, value });
29619
- add(
29620
- "update claiming the row",
29621
- sets,
29622
- `update the owner's row setting ${shape.owner.name} to the other user${column && value !== void 0 ? ` and ${column.name}` : ""}`,
29623
- false,
29624
- freeOtherUserSlot(db, shape)
29625
- );
29626
- }
29627
- if (plans.length === 0) {
29628
- add("update noop", [{ column: shape.owner.name, value: SELF }], `update the owner's row in ${shape.display} (${shape.owner.name} = itself)`, true);
29679
+ });
29680
+ };
29681
+ if (column && value !== void 0) add("update value", [{ column: column.name, value }], `update ${column.name} on the owner's row in ${shape.display}`);
29682
+ if (actor === "other_user" && ownerUpdatable && !shape.ownerInPk) {
29683
+ const sets = [{ column: shape.owner.name, value: OTHER_USER_ID }];
29684
+ if (column && value !== void 0) sets.push({ column: column.name, value });
29685
+ add(
29686
+ "update claiming the row",
29687
+ sets,
29688
+ `update the owner's row setting ${shape.owner.name} to the other user${column && value !== void 0 ? ` and ${column.name}` : ""}`,
29689
+ false,
29690
+ freeOtherUserSlot(db, shape)
29691
+ );
29692
+ }
29693
+ if (plans.length === planStart) {
29694
+ add("update noop", [{ column: shape.owner.name, value: SELF }], `update the owner's row in ${shape.display} (${shape.owner.name} = itself)`, true);
29695
+ }
29629
29696
  }
29630
29697
  return plans;
29631
29698
  }
@@ -29634,20 +29701,19 @@ function reassignPlans(c) {
29634
29701
  const pkSelectable = shape.pk.every((col) => privileges.column.get(col.name)?.select);
29635
29702
  const ownerUpdatable = Boolean(privileges.column.get(shape.owner.name)?.update);
29636
29703
  const noPrivilege = !ownerUpdatable;
29637
- const filtered = pkSelectable || noPrivilege;
29638
- const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
29639
- const baseParams = filtered ? target : [];
29640
- const owner = quoteIdent2(shape.owner.name);
29641
- const moved = (run) => run.after.targetOwner === OTHER_USER_ID;
29642
- const stmt = {
29643
- name: "reassign",
29644
- sql: `update ${shape.qualified} set ${owner} = $${baseParams.length + 1}${where}`,
29645
- params: [...baseParams, OTHER_USER_ID],
29646
- display: `set ${shape.owner.name} on the owner's row in ${shape.display} to the other user${filtered ? "" : ", no row filter"}`,
29647
- filtered
29648
- };
29649
- return [
29650
- {
29704
+ return writeFilters(pkSelectable, noPrivilege).map((filtered) => {
29705
+ const where = filtered ? ` where ${pkCondition(shape, 1)}` : "";
29706
+ const baseParams = filtered ? target : [];
29707
+ const owner = quoteIdent2(shape.owner.name);
29708
+ const moved = (run) => run.after.targetOwner === OTHER_USER_ID;
29709
+ const stmt = {
29710
+ name: filtered ? "reassign" : "blind reassign",
29711
+ sql: `update ${shape.qualified} set ${owner} = $${baseParams.length + 1}${where}`,
29712
+ params: [...baseParams, OTHER_USER_ID],
29713
+ display: `set ${shape.owner.name} on the owner's row in ${shape.display} to the other user${filtered ? "" : ", no row filter (blind write)"}`,
29714
+ filtered
29715
+ };
29716
+ return {
29651
29717
  stmt,
29652
29718
  prep: freeOtherUserSlot(db, shape),
29653
29719
  controlOk: writeControl(filtered, moved),
@@ -29662,38 +29728,35 @@ function reassignPlans(c) {
29662
29728
  if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
29663
29729
  return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner column did not move` };
29664
29730
  }
29665
- }
29666
- ];
29731
+ };
29732
+ });
29667
29733
  }
29668
29734
  function deletePlans(c) {
29669
29735
  const { shape, target, privileges } = c;
29670
29736
  const pkSelectable = shape.pk.every((col) => privileges.column.get(col.name)?.select);
29671
29737
  const noPrivilege = !privileges.table.delete;
29672
- const filtered = pkSelectable || noPrivilege;
29673
29738
  const gone = (run) => run.after.target === null && run.before.target !== null;
29674
- return [
29675
- {
29676
- stmt: {
29677
- name: "delete",
29678
- sql: `delete from ${shape.qualified}${filtered ? ` where ${pkCondition(shape, 1)}` : ""}`,
29679
- params: filtered ? target : [],
29680
- display: `delete the owner's row from ${shape.display}${filtered ? "" : ", no row filter (the role cannot read the primary key)"}`,
29681
- filtered
29682
- },
29683
- controlOk: writeControl(filtered, gone),
29684
- judge: (run) => {
29685
- if (isPrivilegeRefusal(run) && noPrivilege) return { verdict: "denied", detail: `refused: ${run.error.message} (42501), no DELETE privilege` };
29686
- if (run.error) return { verdict: "unknown", detail: `error ${run.error.code}: ${run.error.message}` };
29687
- if (gone(run)) return { verdict: "allowed", detail: `${filtered ? "1 row deleted" : "deleted"}, the owner row is gone (${run.before.count} to ${run.after.count} rows)` };
29688
- if (!filtered) return { verdict: "denied", detail: "the owner row is still there" };
29689
- if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
29690
- return {
29691
- verdict: "unknown",
29692
- detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row is ${run.after.target === null ? "gone" : "still there"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change`
29693
- };
29694
- }
29739
+ return writeFilters(pkSelectable, noPrivilege).map((filtered) => ({
29740
+ stmt: {
29741
+ name: filtered ? "delete" : "blind delete",
29742
+ sql: `delete from ${shape.qualified}${filtered ? ` where ${pkCondition(shape, 1)}` : ""}`,
29743
+ params: filtered ? target : [],
29744
+ display: `delete the owner's row from ${shape.display}${filtered ? "" : ", no row filter (blind write)"}`,
29745
+ filtered
29746
+ },
29747
+ controlOk: writeControl(filtered, gone),
29748
+ judge: (run) => {
29749
+ if (isPrivilegeRefusal(run) && noPrivilege) return { verdict: "denied", detail: `refused: ${run.error.message} (42501), no DELETE privilege` };
29750
+ if (run.error) return { verdict: "unknown", detail: `error ${run.error.code}: ${run.error.message}` };
29751
+ if (gone(run)) return { verdict: "allowed", detail: `${plural3(run.affected ?? 0, "row")} deleted, the owner row is gone (${run.before.count} to ${run.after.count} rows)` };
29752
+ if (!filtered) return { verdict: "denied", detail: "the owner row is still there" };
29753
+ if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
29754
+ return {
29755
+ verdict: "unknown",
29756
+ detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row is ${run.after.target === null ? "gone" : "still there"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change`
29757
+ };
29695
29758
  }
29696
- ];
29759
+ }));
29697
29760
  }
29698
29761
  async function runCell(ctx, privilegesByRole, actor, action, expected) {
29699
29762
  const base2 = { table: ctx.shape.display, actor, action, expected };
@@ -29707,11 +29770,11 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
29707
29770
  const plans = action === "select" ? await selectPlans(c) : action === "insert" ? insertPlans(c) : action === "update" ? await updatePlans(c) : action === "reassign" ? reassignPlans(c) : deletePlans(c);
29708
29771
  const records = [];
29709
29772
  let decisive;
29710
- let firstRun;
29773
+ let first;
29774
+ let uncertain;
29711
29775
  for (const plan of plans) {
29712
29776
  const control = plan.controlOk(await runStatement(ctx.db, ctx.shape, ctx.target, plan.stmt, null, plan.prep));
29713
29777
  const run = await runStatement(ctx.db, ctx.shape, ctx.target, plan.stmt, session, plan.prep);
29714
- firstRun = firstRun ?? run;
29715
29778
  let judged;
29716
29779
  const id2 = run.identity;
29717
29780
  if (!id2 || id2.superuser || id2.bypass || id2.user !== session.role) {
@@ -29723,13 +29786,15 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
29723
29786
  }
29724
29787
  const record = { name: plan.stmt.name, statement: plan.stmt.display, verdict: judged.verdict, detail: judged.detail, affectedRows: run.affected, ...run.error ? { error: run.error } : {}, control };
29725
29788
  records.push(record);
29789
+ first = first ?? { run, record };
29790
+ if (judged.verdict === "unknown" && !uncertain) uncertain = { run, record };
29726
29791
  if (judged.verdict === "allowed" && !decisive) decisive = { run, record };
29727
29792
  }
29728
29793
  let verdict;
29729
29794
  if (decisive) verdict = "allowed";
29730
29795
  else if (records.length > 0 && records.every((r2) => r2.verdict === "denied")) verdict = "denied";
29731
29796
  else verdict = "unknown";
29732
- const shown = decisive ?? { run: firstRun, record: records.find((r2) => r2.verdict !== "denied") ?? records[0] };
29797
+ const shown = decisive ?? uncertain ?? first;
29733
29798
  const r = shown.run;
29734
29799
  const observed = {
29735
29800
  statement: shown.record.statement,
@@ -29812,26 +29877,26 @@ function verdictFor(summary) {
29812
29877
  }
29813
29878
  async function listMigrations(dir) {
29814
29879
  try {
29815
- const entries = await (0, import_promises45.readdir)(dir, { withFileTypes: true });
29880
+ const entries = await (0, import_promises46.readdir)(dir, { withFileTypes: true });
29816
29881
  return entries.filter((e2) => e2.isFile() && /\.sql$/i.test(e2.name) && !e2.name.startsWith(".")).map((e2) => e2.name).sort((a, b3) => a < b3 ? -1 : a > b3 ? 1 : 0);
29817
29882
  } catch {
29818
29883
  return [];
29819
29884
  }
29820
29885
  }
29821
29886
  async function runRlsTest(options) {
29822
- const projectDir = (0, import_node_path54.resolve)(options.projectDir);
29823
- const matrixPath = (0, import_node_path54.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
29824
- const migrationsDir = (0, import_node_path54.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path54.join)("supabase", "migrations"));
29887
+ const projectDir = (0, import_node_path55.resolve)(options.projectDir);
29888
+ const matrixPath = (0, import_node_path55.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
29889
+ const migrationsDir = (0, import_node_path55.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path55.join)("supabase", "migrations"));
29825
29890
  const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
29826
- if ((0, import_node_path54.basename)(matrixPath).toLowerCase().startsWith(".env")) {
29827
- throw new RlsInputError(`Refusing to read ${(0, import_node_path54.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
29891
+ if ((0, import_node_path55.basename)(matrixPath).toLowerCase().startsWith(".env")) {
29892
+ throw new RlsInputError(`Refusing to read ${(0, import_node_path55.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
29828
29893
  }
29829
29894
  const filesRead = [];
29830
29895
  let matrixText;
29831
29896
  try {
29832
- const info = await (0, import_promises45.stat)(matrixPath);
29897
+ const info = await (0, import_promises46.stat)(matrixPath);
29833
29898
  if (!info.isFile()) throw new Error("not a file");
29834
- matrixText = normalise(await (0, import_promises45.readFile)(matrixPath, "utf8"));
29899
+ matrixText = normalise(await (0, import_promises46.readFile)(matrixPath, "utf8"));
29835
29900
  } catch {
29836
29901
  throw new RlsInputError(
29837
29902
  `No permissions matrix at ${matrixPath}. Write one by hand (schema: schemas/permissions-matrix.schema.json in the @viberaven/cli package); rls-test never infers it from your policies.`
@@ -29842,8 +29907,8 @@ async function runRlsTest(options) {
29842
29907
  const names = await listMigrations(migrationsDir);
29843
29908
  const migrationFiles = [];
29844
29909
  for (const name of names) {
29845
- const sql = normalise(await (0, import_promises45.readFile)((0, import_node_path54.join)(migrationsDir, name), "utf8"));
29846
- filesRead.push(displayPath(projectDir, (0, import_node_path54.join)(migrationsDir, name)));
29910
+ const sql = normalise(await (0, import_promises46.readFile)((0, import_node_path55.join)(migrationsDir, name), "utf8"));
29911
+ filesRead.push(displayPath(projectDir, (0, import_node_path55.join)(migrationsDir, name)));
29847
29912
  migrationFiles.push({ name, sha256: sha256(sql), sql });
29848
29913
  }
29849
29914
  const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
@@ -29962,6 +30027,7 @@ async function runRlsTest(options) {
29962
30027
  } catch (error) {
29963
30028
  if (error instanceof RlsTimeoutError || !isSqlError(error)) {
29964
30029
  const message = firstLine2(error instanceof Error ? error.message : String(error));
30030
+ result.engine.error = message;
29965
30031
  return allUnknown(
29966
30032
  error instanceof RlsTimeoutError ? `A statement did not finish within ${Math.round(timeouts.statementMs / 1e3)} s, so the PGlite worker was stopped and no cell is reported as passed.` : `The PGlite worker stopped (${message}), so no cell is reported as passed.`,
29967
30033
  "not run: the database stopped"
@@ -30043,15 +30109,15 @@ async function runTable(db, table2, badRoles, roleChecks) {
30043
30109
  cells
30044
30110
  };
30045
30111
  }
30046
- var import_node_child_process16, import_node_crypto17, import_promises45, import_node_net2, import_node_path54, import_node_util8, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine2, plural3, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
30112
+ var import_node_child_process16, import_node_crypto17, import_promises46, import_node_net2, import_node_path55, import_node_util8, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine2, plural3, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
30047
30113
  var init_rlsRunner = __esm({
30048
30114
  "src/rls/rlsRunner.ts"() {
30049
30115
  "use strict";
30050
30116
  import_node_child_process16 = require("node:child_process");
30051
30117
  import_node_crypto17 = require("node:crypto");
30052
- import_promises45 = require("node:fs/promises");
30118
+ import_promises46 = require("node:fs/promises");
30053
30119
  import_node_net2 = __toESM(require("node:net"));
30054
- import_node_path54 = require("node:path");
30120
+ import_node_path55 = require("node:path");
30055
30121
  import_node_util8 = require("node:util");
30056
30122
  init_safeGit();
30057
30123
  init_permissionsMatrix();
@@ -30083,8 +30149,7 @@ var init_rlsRunner = __esm({
30083
30149
  PROBE_EXCLUSIONS = [
30084
30150
  "Policies that look up other tables (teams, organisations, memberships) are only exercised with the rows the migrations create: no membership rows are seeded.",
30085
30151
  "WITH CHECK expressions are tried with the generated values only; when every value tried is refused, the cell is Unknown, not a pass.",
30086
- "When a role can use some columns but cannot read the primary key, probes run without a row filter and are judged by the owner row alone.",
30087
- "When the primary key is readable, writes are filtered by it; unfiltered UPDATE and DELETE statements are not also tested.",
30152
+ "Writes with applicable privileges also run without a row filter to test writes that SELECT policies may hide, and are judged by the owner row. Filtered writes also run when the primary key is readable.",
30088
30153
  "One row per test user is used; a row the migrations create for the user (an auth.users trigger) is reused.",
30089
30154
  "Side effects of triggers on other tables are not checked."
30090
30155
  ];
@@ -30353,9 +30418,9 @@ var init_nextActionBlock = __esm({
30353
30418
  function defaultMcpConfigPaths() {
30354
30419
  const home = (0, import_node_os5.homedir)();
30355
30420
  return [
30356
- (0, import_node_path55.join)(home, ".config", "claude", "claude_desktop_config.json"),
30357
- (0, import_node_path55.join)(home, ".cursor", "mcp.json"),
30358
- (0, import_node_path55.join)(home, ".gemini", "antigravity", "mcp_config.json")
30421
+ (0, import_node_path56.join)(home, ".config", "claude", "claude_desktop_config.json"),
30422
+ (0, import_node_path56.join)(home, ".cursor", "mcp.json"),
30423
+ (0, import_node_path56.join)(home, ".gemini", "antigravity", "mcp_config.json")
30359
30424
  ];
30360
30425
  }
30361
30426
  function resolveConfigPaths() {
@@ -30432,13 +30497,13 @@ async function verifyProviderGap(options) {
30432
30497
  fallbackCommand: FALLBACK_COMMAND
30433
30498
  };
30434
30499
  }
30435
- var import_node_fs17, import_node_os5, import_node_path55, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
30500
+ var import_node_fs17, import_node_os5, import_node_path56, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
30436
30501
  var init_providerMcpBridge = __esm({
30437
30502
  "src/providerMcpBridge.ts"() {
30438
30503
  "use strict";
30439
30504
  import_node_fs17 = require("node:fs");
30440
30505
  import_node_os5 = require("node:os");
30441
- import_node_path55 = require("node:path");
30506
+ import_node_path56 = require("node:path");
30442
30507
  FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
30443
30508
  SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
30444
30509
  }
@@ -30675,7 +30740,7 @@ async function cmdProviderVerify(flags, positional) {
30675
30740
  return result.verified ? 0 : 1;
30676
30741
  }
30677
30742
  async function cmdStatus(flags, positional) {
30678
- const startDir = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
30743
+ const startDir = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
30679
30744
  let artifact2;
30680
30745
  try {
30681
30746
  artifact2 = await loadLastArtifact(startDir);
@@ -30714,7 +30779,7 @@ async function cmdStatus(flags, positional) {
30714
30779
  return 0;
30715
30780
  }
30716
30781
  async function runScanCommand(flags, positional, options) {
30717
- const workspacePath = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
30782
+ const workspacePath = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
30718
30783
  if (!options?.deferMachineOutput) {
30719
30784
  console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
30720
30785
  }
@@ -30740,7 +30805,7 @@ async function runScanCommand(flags, positional, options) {
30740
30805
  const block = buildNextActionBlock(tasks, updatedState);
30741
30806
  let manifest;
30742
30807
  try {
30743
- manifest = JSON.parse(await (0, import_promises46.readFile)(paths.actionsPath, "utf8"));
30808
+ manifest = JSON.parse(await (0, import_promises47.readFile)(paths.actionsPath, "utf8"));
30744
30809
  } catch (error) {
30745
30810
  console.warn(error instanceof Error ? error.message : String(error));
30746
30811
  }
@@ -30770,7 +30835,7 @@ async function runScanCommand(flags, positional, options) {
30770
30835
  return { exitCode: 0, artifacts: paths };
30771
30836
  }
30772
30837
  async function cmdReport(flags, positional) {
30773
- const startDir = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
30838
+ const startDir = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
30774
30839
  try {
30775
30840
  const paths = await refreshReportFromDisk(startDir);
30776
30841
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -30792,7 +30857,7 @@ async function cmdReport(flags, positional) {
30792
30857
  }
30793
30858
  }
30794
30859
  async function cmdPrompt(flags, positional) {
30795
- const startDir = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
30860
+ const startDir = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
30796
30861
  let artifact2;
30797
30862
  try {
30798
30863
  artifact2 = await loadLastArtifact(startDir);
@@ -30889,7 +30954,7 @@ async function main() {
30889
30954
  const wantsJsonl = hasFlag(flags, "jsonl");
30890
30955
  const wantsStrict = hasFlag(flags, "strict");
30891
30956
  if (flags.condense) {
30892
- const cwd = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
30957
+ const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
30893
30958
  const result = await runCondenseCommand({ cwd });
30894
30959
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
30895
30960
  return 0;
@@ -30909,7 +30974,7 @@ async function main() {
30909
30974
  }
30910
30975
  if (!command && flags.verify === true && typeof flags.action === "string") {
30911
30976
  return runVerifyActionCommand({
30912
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
30977
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
30913
30978
  actionId: flags.action
30914
30979
  });
30915
30980
  }
@@ -30920,7 +30985,7 @@ async function main() {
30920
30985
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
30921
30986
  return 3;
30922
30987
  }
30923
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises46.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
30988
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises47.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
30924
30989
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
30925
30990
  if (wantsJson && gateResult) {
30926
30991
  process.stdout.write(renderGateResultJson(gateResult));
@@ -30942,7 +31007,7 @@ async function main() {
30942
31007
  env: process.env
30943
31008
  });
30944
31009
  if (mode === "local-ui") {
30945
- const cwd = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
31010
+ const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
30946
31011
  const handle = await startLocalUiServer({ cwd, initialPath: stackInitialPath(flags) });
30947
31012
  console.log(`VibeRaven local UI: ${handle.url}`);
30948
31013
  await waitForServerShutdown();
@@ -30962,7 +31027,7 @@ async function main() {
30962
31027
  await runInteractiveSession();
30963
31028
  return 0;
30964
31029
  case "ui": {
30965
- const cwd = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
31030
+ const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
30966
31031
  const envPort = Number(process.env.PORT);
30967
31032
  const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0, initialPath: stackInitialPath(flags) });
30968
31033
  console.log(`VibeRaven local UI: ${handle.url}`);
@@ -31009,20 +31074,20 @@ async function main() {
31009
31074
  });
31010
31075
  case "connect":
31011
31076
  return runConnectCommand({
31012
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
31077
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
31013
31078
  session: typeof flags.session === "string" ? flags.session : void 0,
31014
31079
  token: typeof flags.token === "string" ? flags.token : void 0
31015
31080
  });
31016
31081
  case "check":
31017
31082
  return runCheckCommand({
31018
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
31083
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
31019
31084
  json: Boolean(flags.json),
31020
31085
  quiet: Boolean(flags.quiet),
31021
31086
  base: typeof flags.base === "string" ? flags.base : void 0,
31022
31087
  markdown: flags.markdown === true
31023
31088
  });
31024
31089
  case "fix": {
31025
- const fixCwd = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
31090
+ const fixCwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
31026
31091
  const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
31027
31092
  if (!gapId) {
31028
31093
  const artifact2 = await loadLastArtifact(fixCwd).catch(() => void 0);
@@ -31051,19 +31116,19 @@ async function main() {
31051
31116
  }
31052
31117
  case "actions":
31053
31118
  return runActionsCommand({
31054
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
31119
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
31055
31120
  json: Boolean(flags.json)
31056
31121
  });
31057
31122
  case "preview":
31058
31123
  return runPreviewCommand({
31059
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
31124
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
31060
31125
  agentMode: flags["agent-mode"] === true,
31061
31126
  json: Boolean(flags.json)
31062
31127
  });
31063
31128
  case "next":
31064
31129
  return runNextCommand({
31065
31130
  json: Boolean(flags.json),
31066
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd()
31131
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd()
31067
31132
  });
31068
31133
  case "guide": {
31069
31134
  const provider2 = positional[0];
@@ -31097,7 +31162,7 @@ async function main() {
31097
31162
  case "provider-verify":
31098
31163
  return cmdProviderVerify(flags, positional);
31099
31164
  case "init": {
31100
- const cwd = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
31165
+ const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
31101
31166
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
31102
31167
  if (flags.github === true) {
31103
31168
  return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
@@ -31114,7 +31179,7 @@ async function main() {
31114
31179
  return 1;
31115
31180
  }
31116
31181
  return runDoctorAgentsCommand({
31117
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd()
31182
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd()
31118
31183
  });
31119
31184
  case "validate-npm-package":
31120
31185
  return runValidateNpmPackageCommand({
@@ -31127,7 +31192,7 @@ async function main() {
31127
31192
  return 1;
31128
31193
  }
31129
31194
  return runAuditCommand({
31130
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
31195
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
31131
31196
  json: Boolean(flags.json)
31132
31197
  });
31133
31198
  case "clean": {
@@ -31135,7 +31200,7 @@ async function main() {
31135
31200
  console.error("Usage: viberaven clean --plan [path]");
31136
31201
  return 1;
31137
31202
  }
31138
- const cwd = positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd();
31203
+ const cwd = positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd();
31139
31204
  const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
31140
31205
  const outputPath = await writeCleanupPlan(cwd, plan);
31141
31206
  console.log(`Wrote ${outputPath}
@@ -31145,9 +31210,9 @@ async function main() {
31145
31210
  }
31146
31211
  case "rls-test":
31147
31212
  return runRlsTestCommand({
31148
- cwd: positional[0] ? (0, import_node_path56.resolve)(process.cwd(), positional[0]) : process.cwd(),
31149
- matrix: typeof flags.matrix === "string" ? (0, import_node_path56.resolve)(process.cwd(), flags.matrix) : void 0,
31150
- migrations: typeof flags.migrations === "string" ? (0, import_node_path56.resolve)(process.cwd(), flags.migrations) : void 0,
31213
+ cwd: positional[0] ? (0, import_node_path57.resolve)(process.cwd(), positional[0]) : process.cwd(),
31214
+ matrix: typeof flags.matrix === "string" ? (0, import_node_path57.resolve)(process.cwd(), flags.matrix) : void 0,
31215
+ migrations: typeof flags.migrations === "string" ? (0, import_node_path57.resolve)(process.cwd(), flags.migrations) : void 0,
31151
31216
  json: Boolean(flags.json)
31152
31217
  });
31153
31218
  default:
@@ -31156,11 +31221,11 @@ async function main() {
31156
31221
  return 1;
31157
31222
  }
31158
31223
  }
31159
- var import_promises46, import_node_path56, STACK_AREAS;
31224
+ var import_promises47, import_node_path57, STACK_AREAS;
31160
31225
  var init_cli = __esm({
31161
31226
  "src/cli.ts"() {
31162
- import_promises46 = require("node:fs/promises");
31163
- import_node_path56 = require("node:path");
31227
+ import_promises47 = require("node:fs/promises");
31228
+ import_node_path57 = require("node:path");
31164
31229
  init_config();
31165
31230
  init_runScan();
31166
31231
  init_artifacts2();