@viberaven/cli 1.6.9 → 1.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,10 +10,10 @@ Requires Node.js 20 or newer. Run from the app's project root:
10
10
 
11
11
  ```bash
12
12
  # Open the local Studio
13
- npx -y viberaven@1.6.9
13
+ npx -y viberaven@1.6.11
14
14
 
15
15
  # Fresh terminal check
16
- npx -y viberaven@1.6.9 check
16
+ npx -y viberaven@1.6.11 check
17
17
  ```
18
18
 
19
19
  The `viberaven` package launches this CLI. Studio combines findings, provider context and git comparisons. For chat, select an installed/authenticated Codex CLI, Claude Code or Gemini CLI and use **Test connection**. Access modes: `ask`, `approve`, `full`. Stop Studio with Ctrl+C.
@@ -27,10 +27,10 @@ Checks include migration tables without RLS, unrestricted policy conditions such
27
27
  `check --json` prints a findings summary and artifact paths. `--json` without `check` runs a fresh scan and prints the full gate object.
28
28
 
29
29
  ```bash
30
- npx -y viberaven@1.6.9 check --json
31
- npx -y viberaven@1.6.9 audit --vercel-supabase --json
32
- npx -y viberaven@1.6.9 fix --gap <gapId> --dry-run
33
- npx -y viberaven@1.6.9 init --agents all --dry-run
30
+ npx -y viberaven@1.6.11 check --json
31
+ npx -y viberaven@1.6.11 audit --vercel-supabase --json
32
+ npx -y viberaven@1.6.11 fix --gap <gapId> --dry-run
33
+ npx -y viberaven@1.6.11 init --agents all --dry-run
34
34
  ```
35
35
 
36
36
  Read tasks and source before targeted edits. Preview fixes first. The `rls_disabled` recipe adds an RLS migration without policies: agree before applying, because browser roles lose row access until policies exist. Apply/test through your database workflow; recheck after a batch. Provider actions require provider evidence.
@@ -41,9 +41,9 @@ Read tasks and source before targeted edits. Preview fixes first. The `rls_disab
41
41
 
42
42
  ```bash
43
43
  # Fail on blockers
44
- npx -y viberaven@1.6.9 --strict --json
44
+ npx -y viberaven@1.6.11 --strict --json
45
45
  # Also fail on warnings
46
- npx -y viberaven@1.6.9 --strict=warning --json
46
+ npx -y viberaven@1.6.11 --strict=warning --json
47
47
  ```
48
48
 
49
49
  Default `--strict` returns 0 for `clear`/`warning`, 1 for `not_clear`, 2 for `error`, 3 for `unknown` or unavailable results. `--strict=warning` also returns 1 for warnings.
package/dist/cli.js CHANGED
@@ -15051,7 +15051,7 @@ var VERSION;
15051
15051
  var init_version = __esm({
15052
15052
  "src/version.ts"() {
15053
15053
  "use strict";
15054
- VERSION = "1.6.9";
15054
+ VERSION = "1.6.11";
15055
15055
  }
15056
15056
  });
15057
15057
 
@@ -21533,6 +21533,9 @@ async function readMaybe(path) {
21533
21533
  return null;
21534
21534
  }
21535
21535
  }
21536
+ function blobId(content, like) {
21537
+ return (0, import_node_crypto5.createHash)(like.length === 64 ? "sha256" : "sha1").update(`blob ${content.length}\0`).update(content).digest("hex");
21538
+ }
21536
21539
  function parsePorcelain(raw) {
21537
21540
  const parts = raw.split("\0");
21538
21541
  const entries = [];
@@ -21578,17 +21581,61 @@ async function takeBaseline(cwd) {
21578
21581
  secret: secret || void 0
21579
21582
  };
21580
21583
  }
21581
- return { git: true, root, head: head.code === 0 ? head.stdout.toString("utf8").trim() : null, takenAt, dirty };
21584
+ const headId = head.code === 0 ? head.stdout.toString("utf8").trim() : null;
21585
+ const checkout = headId ? await checkoutForms(cwd, root, headId, dirty, MAX_SNAPSHOT_TOTAL - total) : {};
21586
+ return { git: true, root, head: headId, takenAt, dirty, checkout };
21587
+ }
21588
+ async function checkoutForms(cwd, root, head, dirty, budget) {
21589
+ const tree = await safeGit(cwd, ["ls-tree", "-r", "-z", "--full-name", head], 15e3);
21590
+ if (tree.code !== 0) return {};
21591
+ const files = [];
21592
+ for (const record of tree.stdout.toString("utf8").split("\0")) {
21593
+ const tab = record.indexOf(" ");
21594
+ if (tab < 0) continue;
21595
+ const [mode, type, id] = record.slice(0, tab).split(" ");
21596
+ const path = record.slice(tab + 1);
21597
+ if (type !== "blob" || mode !== "100644" && mode !== "100755") continue;
21598
+ if (path in dirty || isSecretPath(path) || /(?:^|\/)\.viberaven\//.test(path)) continue;
21599
+ files.push({ path, id });
21600
+ }
21601
+ const forms = {};
21602
+ let scanned = 0;
21603
+ const look = async ({ path, id }) => {
21604
+ const target = (0, import_node_path42.join)(root, path);
21605
+ const size = await (0, import_promises33.stat)(target).then((info) => info.size, () => null);
21606
+ if (size === null || scanned + size > MAX_CHECKOUT_SCAN) return;
21607
+ scanned += size;
21608
+ const content = await readMaybe(target);
21609
+ if (!content || blobId(content, id) === id) return;
21610
+ const asLf = withoutCrlf(content);
21611
+ if (asLf.length < content.length && blobId(asLf, id) === id && withCrlf(asLf).equals(content)) {
21612
+ forms[path] = { eol: "crlf" };
21613
+ } else if (content.length <= MAX_SNAPSHOT_BYTES && content.length <= budget) {
21614
+ budget -= content.length;
21615
+ forms[path] = { content: content.toString("base64") };
21616
+ } else {
21617
+ forms[path] = { tooLarge: true };
21618
+ }
21619
+ };
21620
+ let next = 0;
21621
+ await Promise.all(Array.from({ length: CHECKOUT_READS_AT_ONCE }, async () => {
21622
+ while (next < files.length) await look(files[next++]);
21623
+ }));
21624
+ return forms;
21582
21625
  }
21583
21626
  async function baseContent(baseline, path) {
21584
21627
  const entry = baseline.dirty[path];
21585
21628
  if (entry) {
21586
21629
  if (entry.hash === null) return null;
21587
- return entry.content ? Buffer.from(entry.content, "base64") : void 0;
21630
+ return entry.content !== void 0 ? Buffer.from(entry.content, "base64") : void 0;
21588
21631
  }
21589
21632
  if (!baseline.root || !baseline.head) return null;
21633
+ const checkout = baseline.checkout?.[path];
21634
+ if (checkout?.content !== void 0) return Buffer.from(checkout.content, "base64");
21635
+ if (checkout?.tooLarge) return void 0;
21590
21636
  const shown = await safeGit(baseline.root, ["show", `${baseline.head}:${path}`]);
21591
- return shown.code === 0 ? shown.stdout : null;
21637
+ if (shown.code !== 0) return null;
21638
+ return checkout?.eol === "crlf" ? withCrlf(shown.stdout) : shown.stdout;
21592
21639
  }
21593
21640
  function lineStats(before, after) {
21594
21641
  const count = (text2) => {
@@ -21794,7 +21841,7 @@ async function loadBaseline(runsDir, runId) {
21794
21841
  return null;
21795
21842
  }
21796
21843
  }
21797
- var import_node_child_process9, import_node_crypto5, import_promises33, import_node_os3, import_node_path42, STATUS_PATHSPEC, MAX_SNAPSHOT_FILES, MAX_SNAPSHOT_BYTES, MAX_SNAPSHOT_TOTAL, SECRET_FILE, lf, NOT_PROVEN;
21844
+ var import_node_child_process9, import_node_crypto5, import_promises33, import_node_os3, import_node_path42, STATUS_PATHSPEC, MAX_SNAPSHOT_FILES, MAX_SNAPSHOT_BYTES, MAX_SNAPSHOT_TOTAL, MAX_CHECKOUT_SCAN, CHECKOUT_READS_AT_ONCE, SECRET_FILE, withCrlf, withoutCrlf, lf, NOT_PROVEN;
21798
21845
  var init_workspace = __esm({
21799
21846
  "src/local-ui/actions/workspace.ts"() {
21800
21847
  "use strict";
@@ -21808,7 +21855,11 @@ var init_workspace = __esm({
21808
21855
  MAX_SNAPSHOT_FILES = 300;
21809
21856
  MAX_SNAPSHOT_BYTES = 1e6;
21810
21857
  MAX_SNAPSHOT_TOTAL = 2e7;
21858
+ MAX_CHECKOUT_SCAN = 2e8;
21859
+ CHECKOUT_READS_AT_ONCE = 16;
21811
21860
  SECRET_FILE = /(?:^|\/)\.env(?:\.[^/]*)?$|\.(?:pem|key|p12|pfx|keystore)$|(?:^|\/)id_(?:rsa|ed25519|ecdsa)$/i;
21861
+ withCrlf = (content) => Buffer.from(content.toString("latin1").replace(/\n/g, "\r\n"), "latin1");
21862
+ withoutCrlf = (content) => Buffer.from(content.toString("latin1").replace(/\r\n/g, "\n"), "latin1");
21812
21863
  lf = (text2) => text2.replace(/\r\n/g, "\n");
21813
21864
  NOT_PROVEN = "Changed during the run, but not proven to be only the agent\u2019s edit (you or another tool may have changed it), so Undo left it alone. Review it and put it back yourself if you want.";
21814
21865
  }
@@ -21829,7 +21880,7 @@ async function detectPreviewCommand(cwd) {
21829
21880
  const packageManager = await exists2("pnpm-lock.yaml") ? "pnpm" : await exists2("yarn.lock") ? "yarn" : await exists2("bun.lockb") || await exists2("bun.lock") ? "bun" : "npm";
21830
21881
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
21831
21882
  const framework = FRAMEWORKS.find(([dep]) => dep in deps)?.[1] ?? null;
21832
- return { packageManager, script, framework, display: `${packageManager} run ${script}` };
21883
+ return { packageManager, script, framework, display: `${packageManager} run ${script}`, reloadsItself: RELOADS_ITSELF.test(scripts[script]) };
21833
21884
  }
21834
21885
  function answers(host, port) {
21835
21886
  return new Promise((resolveAnswer) => {
@@ -21873,7 +21924,7 @@ function previewFailureReason(log) {
21873
21924
  if (/Missing script/i.test(log)) return "The script VibeRaven picked is not in package.json.";
21874
21925
  return void 0;
21875
21926
  }
21876
- var import_node_child_process10, import_promises34, import_node_net, import_node_path43, URL_PATTERN, READY_TIMEOUT_MS, LOG_LIMIT, FRAMEWORKS, PreviewManager;
21927
+ var import_node_child_process10, import_promises34, import_node_net, import_node_path43, URL_PATTERN, READY_TIMEOUT_MS, LOG_LIMIT, FRAMEWORKS, RELOADS_ITSELF, PreviewManager;
21877
21928
  var init_preview2 = __esm({
21878
21929
  "src/local-ui/preview.ts"() {
21879
21930
  "use strict";
@@ -21886,6 +21937,7 @@ var init_preview2 = __esm({
21886
21937
  READY_TIMEOUT_MS = 12e4;
21887
21938
  LOG_LIMIT = 6e3;
21888
21939
  FRAMEWORKS = [["next", "Next.js"], ["vite", "Vite"], ["@remix-run/dev", "Remix"], ["astro", "Astro"], ["@sveltejs/kit", "SvelteKit"], ["nuxt", "Nuxt"], ["react-scripts", "Create React App"]];
21940
+ RELOADS_ITSELF = /\b(?:next\s+dev|vite\b(?!\s+(?:build|preview))|astro\s+dev|nuxi?\s+dev|remix\s+(?:vite:)?dev|react-scripts\s+start|webpack(?:-dev-server|\s+serve)|nodemon|tsx\s+watch|ts-node-dev|svelte-kit\s+dev|ng\s+serve|parcel\b(?!\s+build))|--watch\b/i;
21889
21941
  PreviewManager = class {
21890
21942
  constructor(cwd, home, preferredPort = 3e3) {
21891
21943
  this.cwd = cwd;
@@ -21939,7 +21991,7 @@ var init_preview2 = __esm({
21939
21991
  return this.snapshot();
21940
21992
  }
21941
21993
  this.assignedPort = port;
21942
- this.status = { state: "starting", command: command.display, framework: command.framework, startedAt: (/* @__PURE__ */ new Date()).toISOString(), log: "", revision: this.status.revision };
21994
+ this.status = { state: "starting", command: command.display, framework: command.framework, reloadsItself: command.reloadsItself, startedAt: (/* @__PURE__ */ new Date()).toISOString(), log: "", revision: this.status.revision };
21943
21995
  const child = (0, import_node_child_process10.spawn)(`${command.packageManager} run ${command.script}`, {
21944
21996
  cwd: this.cwd,
21945
21997
  shell: true,
@@ -22027,6 +22079,25 @@ var init_preview2 = __esm({
22027
22079
  if (this.status.state !== "starting") return Promise.resolve(this.snapshot());
22028
22080
  return new Promise((resolveWait) => this.readyWaiters.push(() => resolveWait(this.snapshot())));
22029
22081
  }
22082
+ /**
22083
+ * The app's code changed. A dev server that reloads by itself only needs the frame reloaded; any other one is
22084
+ * restarted, so the preview never keeps serving the code from before the change.
22085
+ */
22086
+ async reload() {
22087
+ if (!this.isRunning()) return this.snapshot();
22088
+ if (this.status.reloadsItself) {
22089
+ this.bumpRevision();
22090
+ return this.snapshot();
22091
+ }
22092
+ const port = this.assignedPort;
22093
+ this.stop();
22094
+ for (let tries = 0; tries < 50 && port && await portTaken(port); tries += 1) {
22095
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, 200));
22096
+ }
22097
+ await this.start();
22098
+ this.bumpRevision();
22099
+ return this.waitForReady();
22100
+ }
22030
22101
  /** Marks that the app's code changed, so the Studio reloads the frame exactly once. */
22031
22102
  bumpRevision() {
22032
22103
  this.status = { ...this.status, revision: this.status.revision + 1 };
@@ -22382,7 +22453,7 @@ var init_engine = __esm({
22382
22453
  const outcome = await revertChanges(baseline, paths.map((path) => ({ path, kind: "modified", additions: 0, deletions: 0 })), keep);
22383
22454
  this.deps.store.update(runId, { reverted: { at: (/* @__PURE__ */ new Date()).toISOString(), ...outcome } });
22384
22455
  this.deps.store.emit(runId, "run.reverted", { restored: outcome.restored.length, skipped: outcome.skipped.length });
22385
- if (outcome.restored.length && this.deps.preview.isRunning()) this.deps.preview.bumpRevision();
22456
+ if (outcome.restored.length && this.deps.preview.isRunning()) await this.deps.preview.reload();
22386
22457
  return outcome;
22387
22458
  }
22388
22459
  /**
@@ -22401,7 +22472,7 @@ var init_engine = __esm({
22401
22472
  if ("error" in outcome) return outcome;
22402
22473
  this.deps.store.update(runId, { restoredFiles: [.../* @__PURE__ */ new Set([...run.restoredFiles ?? [], path])] });
22403
22474
  this.deps.store.emit(runId, "file.restored", { path });
22404
- if (this.deps.preview.isRunning()) this.deps.preview.bumpRevision();
22475
+ if (this.deps.preview.isRunning()) await this.deps.preview.reload();
22405
22476
  return outcome;
22406
22477
  }
22407
22478
  stopped(runId) {
@@ -22556,7 +22627,8 @@ var init_engine = __esm({
22556
22627
  const cancelledNow = () => this.stopped(runId);
22557
22628
  if (!checkSpecs.length) unknowns.push("No typecheck or test script, so the code was not checked.");
22558
22629
  if (this.deps.preview.isRunning()) {
22559
- this.deps.preview.bumpRevision();
22630
+ if (changes.length) await this.deps.preview.reload();
22631
+ else this.deps.preview.bumpRevision();
22560
22632
  const ready = await this.deps.preview.waitForReady();
22561
22633
  if (cancelledNow()) return this.finishCancelled(runId, changes, notProven);
22562
22634
  const answerHttp = ready.url ? await this.deps.preview.fetchStatus(ready.url, 6e4) : null;
@@ -24253,9 +24325,13 @@ async function sendStackAppJs(response) {
24253
24325
  }
24254
24326
  async function sendStackAppCss(response) {
24255
24327
  try {
24256
- const body = await (0, import_promises36.readFile)((0, import_node_path45.join)(__dirname, "stack", "stack.css"));
24328
+ const [shell, scene] = await Promise.all([
24329
+ (0, import_promises36.readFile)((0, import_node_path45.join)(__dirname, "stack", "stack.css"), "utf8"),
24330
+ (0, import_promises36.readFile)((0, import_node_path45.join)(__dirname, "stack", "scene.css"), "utf8").catch(() => "")
24331
+ ]);
24257
24332
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
24258
- response.end(body);
24333
+ response.end(`${shell}
24334
+ ${scene}`);
24259
24335
  } catch {
24260
24336
  sendText(response, 404, "Stack stylesheet not found: src/local-ui/stack/stack.css is missing.");
24261
24337
  }
@@ -25649,7 +25725,8 @@ async function runConnect(options, deps) {
25649
25725
  const reply = await call(deps, "POST", `/v1/runner/jobs/${encodeURIComponent(job.id)}/result`, result, runnerAccessToken);
25650
25726
  if (unauthorized(reply.status)) break;
25651
25727
  if (reply.status >= 200 && reply.status < 300) {
25652
- log(result.status === "succeeded" ? ` \u2713 ${result.outputSummary[0]} (sent to the site)` : ` \u2717 ${result.outputSummary[0] ?? "Could not finish."} (sent to the site)`);
25728
+ const sign = { succeeded: "\u2713", needs_user: "\u2022", cancelled: "\u25A0", failed: "\u2717" }[result.status] ?? "\u2717";
25729
+ log(` ${sign} ${result.outputSummary[0] ?? "Could not finish."} (sent to the site)`);
25653
25730
  return;
25654
25731
  }
25655
25732
  } catch {