@aayambansal/squint 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -102,6 +102,7 @@ From source: `git clone https://github.com/aayambansal/squint.git && cd squint &
102
102
  | --- | --- | --- |
103
103
  | Claude Code | `claude` | `npm i -g @anthropic-ai/claude-code` |
104
104
  | Codex CLI | `codex` | `npm i -g @openai/codex` |
105
+ | Codex (app-server) | `codex-app` | same binary — drives the published JSON-RPC protocol |
105
106
  | Gemini CLI | `gemini` | `npm i -g @google/gemini-cli` |
106
107
  | OpenCode | `opencode` | `npm i -g opencode-ai` |
107
108
  | Amp | `amp` | `npm i -g @sourcegraph/amp` |
@@ -149,8 +150,10 @@ squint doctor --probe # run every engine end to end, verify auth act
149
150
  - **Editing**: a real line editor — arrows move, `alt+←/→` jump words, `ctrl+a/e/k/u/w`,
150
151
  `↑/↓` history. `ctrl+c` twice exits with a session summary.
151
152
  - **Flows**: declare user journeys as six readable lines in `.squint/flows/`; `/flows`
152
- replays them headlessly and failing steps join the fix loop. `/score` snapshots quality
153
- deterministically.
153
+ replays them headlessly and failing steps join the fix loop; `/flows suggest` drafts
154
+ a smoke flow per route from the live page's own headings. `/score` snapshots quality
155
+ deterministically. `/goal <objective>` arms a standing goal — auto-fix presses to 6
156
+ attempts until squint's checks come back clean.
154
157
  - **Problems**: findings from gates, the dev server, the runtime probe, a11y sweeps, and flows
155
158
  collect into a list — `/problems` shows it, `/fix` sends everything as one turn,
156
159
  `/fix <n>` targets one. The footer counts what's open.
@@ -176,7 +179,11 @@ squint doctor --probe # run every engine end to end, verify auth act
176
179
  the injected-context bill per source, with staleness warnings.
177
180
  - **Two more doors in**: `squint mcp` serves the gates as MCP tools (any
178
181
  MCP-speaking agent calls squint's verification directly); `squint ci` runs the
179
- whole loop headlessly in a pipeline — JSON report, non-zero exit on hard findings.
182
+ whole loop headlessly in a pipeline — JSON report, non-zero exit on hard findings,
183
+ and a digest-sealed receipt per run at `.squint/receipts/` tying the green claim
184
+ to the exact pixels it was green about.
185
+ - **The review lane**: `/lane on` — every ask gets a second read-only reviewer in
186
+ fresh context over the diff alone; blind spots don't inherit.
180
187
  - **The sentinel**: gate evasion (deleted tests, added skips, suppressed
181
188
  diagnostics, weakened checks, locked-path touches) detected deterministically per
182
189
  turn and reported to you — never sent back to the engine that did it.
@@ -189,8 +196,10 @@ squint doctor --probe # run every engine end to end, verify auth act
189
196
  - **Visual approval**: engines ask before contested changes — the request renders
190
197
  with its screenshot, `/yes` / `/no` answer it, the ledger remembers.
191
198
  - **The design ledger**: `/decide` (plus chosen variants, rollbacks, accepted
192
- sandboxes) appends to a committed `.squint/design-log.jsonl`; recent decisions ride
193
- into every ask so they stop getting silently undone between sessions.
199
+ sandboxes, approvals) appends to a committed `.squint/design-log.jsonl`; recent
200
+ decisions ride into every ask so they stop getting silently undone between
201
+ sessions. `/distill` compresses the ledger into always-on rules and proposed
202
+ persistent checks — accumulated taste becomes deterministic gates.
194
203
  - **`/btw <question>`** asks about the codebase read-only without touching the main
195
204
  thread's context. `.squint/locks` lists paths the engine must never touch; `/save`
196
205
  exports the transcript as markdown.
@@ -235,11 +244,12 @@ and an avoid-list tuned to that family's failure modes. Plain markdown, made to
235
244
  | stream | | headless screenshots |
236
245
  +------+-------+ +-------------------------+
237
246
  |
238
- claude . codex . gemini . opencode . amp . cursor . copilot . aider
247
+ claude . codex . codex-app . gemini . opencode . amp . cursor . copilot . aider
239
248
  ```
240
249
 
241
250
  Engines are dumb translators: build a headless invocation, parse the stream into one
242
- normalized event model. Claude Code, Amp, and Cursor share a single wire-protocol parser.
251
+ normalized event model. Claude Code, Amp, and Cursor share a single wire-protocol
252
+ parser; `codex-app` speaks the app-server JSON-RPC protocol through an embedded driver.
243
253
  All product behavior lives in the harness, so a new engine is ~80 lines.
244
254
 
245
255
  ## Repo layout
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  App
4
- } from "./chunk-2KJLEM53.js";
5
- import "./chunk-3GHZOVAA.js";
6
- import "./chunk-VCDZGQBB.js";
4
+ } from "./chunk-6OFUO3UT.js";
5
+ import "./chunk-LNJ7O5O4.js";
6
+ import "./chunk-7PKIVVRB.js";
7
7
  import "./chunk-UBADCBPT.js";
8
8
  import "./chunk-IEQGTVWN.js";
9
9
  import "./chunk-AUJJGMZG.js";
@@ -14,9 +14,9 @@ import "./chunk-DOWWBLX2.js";
14
14
  import "./chunk-K5QJMSJH.js";
15
15
  import "./chunk-WAJXATCO.js";
16
16
  import "./chunk-62JNF5M2.js";
17
- import "./chunk-7FAS2O3L.js";
17
+ import "./chunk-UMDV4ITQ.js";
18
18
  import "./chunk-7CAGWFAQ.js";
19
- import "./chunk-P6D4RSWD.js";
19
+ import "./chunk-N7YEWQGZ.js";
20
20
  import "./chunk-PZV2FJE5.js";
21
21
  import "./chunk-DABIMOMV.js";
22
22
  export {
@@ -5,7 +5,7 @@ import {
5
5
  pixelDiffAttributed,
6
6
  pixelDiffPct,
7
7
  runFlow
8
- } from "./chunk-P6D4RSWD.js";
8
+ } from "./chunk-N7YEWQGZ.js";
9
9
  export {
10
10
  cdpCapture,
11
11
  hasWebSocket,
@@ -143,8 +143,8 @@ function stepExpression(step) {
143
143
  async function suggestFlows(cwd, baseUrl, chromePath) {
144
144
  const fs2 = await import("fs");
145
145
  const path2 = await import("path");
146
- const { cdpCapture } = await import("./cdp-RFQCIQFP.js");
147
- const { loadRoutes } = await import("./preview-WYFBPFFL.js");
146
+ const { cdpCapture } = await import("./cdp-IGPGFMC6.js");
147
+ const { loadRoutes } = await import("./preview-4OF6JB5B.js");
148
148
  const dir = path2.join(cwd, ".squint", "flows");
149
149
  fs2.mkdirSync(dir, { recursive: true });
150
150
  const created = [];
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  completeCommand
4
- } from "./chunk-3GHZOVAA.js";
4
+ } from "./chunk-LNJ7O5O4.js";
5
5
  import {
6
6
  Session
7
- } from "./chunk-VCDZGQBB.js";
7
+ } from "./chunk-7PKIVVRB.js";
8
8
 
9
9
  // src/tui/App.tsx
10
10
  import { Box as Box3, Static, Text as Text3, useApp, useInput } from "ink";
@@ -51,7 +51,7 @@ import {
51
51
  comparePulseAttributed,
52
52
  probeRuntime,
53
53
  runtimeSummary
54
- } from "./chunk-7FAS2O3L.js";
54
+ } from "./chunk-UMDV4ITQ.js";
55
55
  import {
56
56
  clearState,
57
57
  loadState,
@@ -157,6 +157,8 @@ var Session = class {
157
157
  reviewTipShown = false;
158
158
  pendingApproval = null;
159
159
  goal = null;
160
+ laneEnabled = false;
161
+ inLane = false;
160
162
  lastPulse = null;
161
163
  lastPerf = null;
162
164
  autoReviewedThisAsk = false;
@@ -678,8 +680,49 @@ ${probe.checkFailures.slice(0, 5).join("\n")}`);
678
680
  }
679
681
  }
680
682
  this.notify({ running: false });
683
+ await this.maybeLaneReview(display);
681
684
  this.drainQueue();
682
685
  }
686
+ /**
687
+ * The review lane: a second read-only pass in fresh context after
688
+ * each real ask — the diff, not the conversation, so it can't inherit
689
+ * the implementer's blind spots. Findings render as critique; nothing
690
+ * here edits. Costs a cheap turn (fixModel when set); off by default.
691
+ */
692
+ async maybeLaneReview(display) {
693
+ if (!this.laneEnabled || this.inLane) return;
694
+ if (display.startsWith("\u26D1") || display.startsWith("\u{1F441}") || display.startsWith("\u{1F50E}") || display.startsWith("/distill")) return;
695
+ const checkpoint = this.checkpoints.at(-1);
696
+ if (!checkpoint) return;
697
+ let diff;
698
+ try {
699
+ const { execFileSync } = await import("child_process");
700
+ diff = execFileSync("git", ["diff", checkpoint.snapshot.stashHash ?? "HEAD"], {
701
+ cwd: this.execCwd(),
702
+ encoding: "utf8",
703
+ stdio: ["ignore", "pipe", "pipe"]
704
+ });
705
+ } catch {
706
+ return;
707
+ }
708
+ if (diff.trim().length === 0) return;
709
+ const capped = diff.length > 2e4 ? `${diff.slice(0, 2e4)}
710
+ [diff truncated]` : diff;
711
+ this.inLane = true;
712
+ try {
713
+ await this.runTurn(
714
+ `You are a second reviewer in fresh context. The diff below just landed for the ask "${display}". Review ONLY the diff \u2014 do not edit anything, do not run commands that modify state. Report at most 3 concrete findings ranked by severity (bugs, regressions, accessibility, design-system violations), each with file and line. If it is clean, say so in one line.
715
+
716
+ \`\`\`diff
717
+ ${capped}
718
+ \`\`\``,
719
+ "\u{1F50E} lane review",
720
+ this.opts.fixModel
721
+ );
722
+ } finally {
723
+ this.inLane = false;
724
+ }
725
+ }
683
726
  async submit(ask) {
684
727
  this.fixAttempts = 0;
685
728
  this.autoReviewedThisAsk = false;
@@ -769,7 +812,13 @@ squint verifies every turn \u2014 gates, runtime probe, page audits. Do not decl
769
812
  this.push("image", pulsePath);
770
813
  return null;
771
814
  }
772
- const diff = await comparePulseAttributed(previous, current, this.state.devUrl ?? void 0);
815
+ const { previewDir } = await import("./preview-4OF6JB5B.js");
816
+ const diff = await comparePulseAttributed(
817
+ previous,
818
+ current,
819
+ this.state.devUrl ?? void 0,
820
+ (await import("path")).join(previewDir(this.opts.cwd), "triptych.png")
821
+ );
773
822
  if (diff === null) return null;
774
823
  const pct = diff.pct;
775
824
  this.push(
@@ -778,7 +827,7 @@ squint verifies every turn \u2014 gates, runtime probe, page audits. Do not decl
778
827
  ${diff.sentences.map((s) => ` ${s}`).join("\n")}` : ""}`
779
828
  );
780
829
  runHook(this.opts.cwd, "on-pulse-diff", { pct: pct.toFixed(1) });
781
- if (pct >= 0.5) this.push("image", pulsePath);
830
+ if (pct >= 0.5) this.push("image", diff.triptychPath ?? pulsePath);
782
831
  return pct;
783
832
  }
784
833
  /** Screenshot the running app (and watch its runtime where CDP is available). */
@@ -961,6 +1010,18 @@ They are objective defects, not style preferences.`
961
1010
  }
962
1011
  break;
963
1012
  }
1013
+ case "lane": {
1014
+ if (arg === "on") {
1015
+ this.laneEnabled = true;
1016
+ this.push("status", "review lane on \u2014 every ask gets a second read-only pass over its diff (fixModel when set)");
1017
+ } else if (arg === "off") {
1018
+ this.laneEnabled = false;
1019
+ this.push("status", "review lane off");
1020
+ } else {
1021
+ this.push("status", `review lane is ${this.laneEnabled ? "on" : "off"} \u2014 /lane on|off`);
1022
+ }
1023
+ break;
1024
+ }
964
1025
  case "distill": {
965
1026
  void (async () => {
966
1027
  const { loadDecisions } = await import("./designLog-S67J7SO2.js");
@@ -1129,7 +1190,7 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
1129
1190
  this.push("error", "no Chrome/Chromium found");
1130
1191
  return;
1131
1192
  }
1132
- const { suggestFlows } = await import("./flows-G5K3XYQZ.js");
1193
+ const { suggestFlows } = await import("./flows-OY4BSCKD.js");
1133
1194
  this.push("status", "drafting flows from the live routes\u2026");
1134
1195
  const { created, skipped } = await suggestFlows(this.opts.cwd, this.state.devUrl, chrome);
1135
1196
  if (created.length > 0) this.push("status", `drafted ${created.length} flow(s): ${created.join(", ")} \u2014 edit .squint/flows/*.flow or ask the engine to deepen them`);
@@ -1139,7 +1200,7 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
1139
1200
  break;
1140
1201
  }
1141
1202
  void (async () => {
1142
- const { loadFlows } = await import("./flows-G5K3XYQZ.js");
1203
+ const { loadFlows } = await import("./flows-OY4BSCKD.js");
1143
1204
  const flows = loadFlows(this.opts.cwd);
1144
1205
  if (flows.length === 0) {
1145
1206
  this.push("status", "no flows \u2014 add .squint/flows/<name>.flow (goto/click/fill/press/expect/shot lines), or ask the engine to write one");
@@ -1150,8 +1211,8 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
1150
1211
  this.push("error", "no Chrome/Chromium found for flows");
1151
1212
  return;
1152
1213
  }
1153
- const { runFlow } = await import("./cdp-RFQCIQFP.js");
1154
- const { previewDir } = await import("./preview-WYFBPFFL.js");
1214
+ const { runFlow } = await import("./cdp-IGPGFMC6.js");
1215
+ const { previewDir } = await import("./preview-4OF6JB5B.js");
1155
1216
  this.push("status", `replaying ${flows.length} flow(s)\u2026`);
1156
1217
  this.notify({ running: true, runStartedAt: Date.now() });
1157
1218
  const failures = [];
@@ -1475,7 +1536,7 @@ ${sandboxFiles(this.opts.cwd).join("\n")}`);
1475
1536
  this.notify({ items: [], totals: { costUsd: 0, turns: 0 } });
1476
1537
  break;
1477
1538
  case "help": {
1478
- void import("./commands-RVCKIMNV.js").then(({ commandHelp }) => this.push("status", commandHelp()));
1539
+ void import("./commands-OBCLYABX.js").then(({ commandHelp }) => this.push("status", commandHelp()));
1479
1540
  break;
1480
1541
  }
1481
1542
  case "quit":
@@ -29,6 +29,7 @@ var COMMANDS = [
29
29
  { name: "decide", args: "<text>", group: "session", description: "record a design decision; injected into every future ask" },
30
30
  { name: "goal", args: "[text|off]", group: "build", description: "arm a standing objective; auto-fix presses until checks are clean" },
31
31
  { name: "distill", group: "session", description: "compress the design ledger into rules.md lines and proposed checks" },
32
+ { name: "lane", args: "on|off", group: "verify", description: "second read-only reviewer over each turn's diff, fresh context" },
32
33
  { name: "context", group: "session", description: "what squint injects per ask, token-costed, with staleness warnings" },
33
34
  { name: "yes", args: "[note]", group: "build", description: "approve the engine's pending visual-approval request" },
34
35
  { name: "no", args: "[note]", group: "build", description: "reject the engine's pending visual-approval request" },
@@ -179,7 +179,7 @@ var describe = (value) => {
179
179
  return String(value);
180
180
  };
181
181
  async function runFlow(chromePath, baseUrl, flow, outDir) {
182
- const { stepExpression } = await import("./flows-G5K3XYQZ.js");
182
+ const { stepExpression } = await import("./flows-OY4BSCKD.js");
183
183
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
184
184
  const shots = [];
185
185
  let connection = null;
@@ -238,7 +238,7 @@ async function runFlow(chromePath, baseUrl, flow, outDir) {
238
238
  setTimeout(() => fs.rmSync(profileDir, { recursive: true, force: true }), 500).unref?.();
239
239
  }
240
240
  }
241
- async function pixelDiffAttributed(chromePath, pngA, pngB, url) {
241
+ async function pixelDiffAttributed(chromePath, pngA, pngB, url, outPath) {
242
242
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
243
243
  let connection = null;
244
244
  try {
@@ -312,7 +312,35 @@ async function pixelDiffAttributed(chromePath, pngA, pngB, url) {
312
312
  }
313
313
  regions.sort((p, q) => q.cells - p.cells);
314
314
  const sizePenalty = (a.width !== b.width || a.height !== b.height) ? 1 : 0;
315
- return { pct: Math.min(100, (differ / total) * 100 + sizePenalty), regions: regions.slice(0, 5), w, h };
315
+ const pct = Math.min(100, (differ / total) * 100 + sizePenalty);
316
+ let triptych = null;
317
+ if (pct >= 0.5) {
318
+ // before | after | heatmap, half scale, labeled.
319
+ const panelW = Math.round(w / 2), panelH = Math.round(h / 2), gap = 6;
320
+ const c = document.createElement('canvas');
321
+ c.width = panelW * 3 + gap * 2; c.height = panelH + 22;
322
+ const ctx = c.getContext('2d');
323
+ ctx.fillStyle = '#111'; ctx.fillRect(0, 0, c.width, c.height);
324
+ ctx.drawImage(a, 0, 22, panelW, panelH);
325
+ ctx.drawImage(b, panelW + gap, 22, panelW, panelH);
326
+ ctx.globalAlpha = 0.3;
327
+ ctx.drawImage(b, (panelW + gap) * 2, 22, panelW, panelH);
328
+ ctx.globalAlpha = 1;
329
+ ctx.fillStyle = 'rgba(255,60,40,0.75)';
330
+ const sx = panelW / w, sy = panelH / h;
331
+ for (let cy = 0; cy < gh; cy++) {
332
+ for (let cx = 0; cx < gw; cx++) {
333
+ if (!marked[cy * gw + cx]) continue;
334
+ ctx.fillRect((panelW + gap) * 2 + cx * CELL * sx, 22 + cy * CELL * sy, CELL * sx, CELL * sy);
335
+ }
336
+ }
337
+ ctx.fillStyle = '#ddd'; ctx.font = '12px system-ui';
338
+ ctx.fillText('before', 2, 14);
339
+ ctx.fillText('after', panelW + gap + 2, 14);
340
+ ctx.fillText('changed ' + pct.toFixed(1) + '%', (panelW + gap) * 2 + 2, 14);
341
+ triptych = c.toDataURL('image/png');
342
+ }
343
+ return { pct, regions: regions.slice(0, 5), w, h, triptych };
316
344
  })()`;
317
345
  const { result } = await connection.send(
318
346
  "Runtime.evaluate",
@@ -321,7 +349,15 @@ async function pixelDiffAttributed(chromePath, pngA, pngB, url) {
321
349
  );
322
350
  const value = result?.value;
323
351
  if (!value || typeof value.pct !== "number") return null;
324
- if (!url || value.regions.length === 0 || value.pct < 0.5) return { pct: value.pct, sentences: [] };
352
+ let triptychPath;
353
+ if (value.triptych && outPath) {
354
+ try {
355
+ fs.writeFileSync(outPath, Buffer.from(value.triptych.split(",")[1] ?? "", "base64"));
356
+ triptychPath = outPath;
357
+ } catch {
358
+ }
359
+ }
360
+ if (!url || value.regions.length === 0 || value.pct < 0.5) return { pct: value.pct, sentences: [], triptychPath };
325
361
  let sentences = [];
326
362
  try {
327
363
  const page = await connection.send("Target.createTarget", { url: "about:blank" });
@@ -368,7 +404,7 @@ async function pixelDiffAttributed(chromePath, pngA, pngB, url) {
368
404
  if (Array.isArray(hit.result?.value)) sentences = hit.result.value.map(String);
369
405
  } catch {
370
406
  }
371
- return { pct: value.pct, sentences };
407
+ return { pct: value.pct, sentences, triptychPath };
372
408
  } catch {
373
409
  return null;
374
410
  } finally {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Session
4
- } from "./chunk-VCDZGQBB.js";
4
+ } from "./chunk-7PKIVVRB.js";
5
5
 
6
6
  // src/daemon/client.ts
7
7
  import net from "net";
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  cdpCapture,
7
7
  hasWebSocket
8
- } from "./chunk-P6D4RSWD.js";
8
+ } from "./chunk-N7YEWQGZ.js";
9
9
  import {
10
10
  findChrome,
11
11
  screenshot
@@ -164,14 +164,14 @@ async function probeRuntime(url, cwd) {
164
164
  async function comparePulse(previous, current) {
165
165
  const chrome = findChrome();
166
166
  if (!chrome || !hasWebSocket()) return null;
167
- const { pixelDiffPct } = await import("./cdp-RFQCIQFP.js");
167
+ const { pixelDiffPct } = await import("./cdp-IGPGFMC6.js");
168
168
  return pixelDiffPct(chrome, previous, current);
169
169
  }
170
- async function comparePulseAttributed(previous, current, url) {
170
+ async function comparePulseAttributed(previous, current, url, outPath) {
171
171
  const chrome = findChrome();
172
172
  if (!chrome || !hasWebSocket()) return null;
173
- const { pixelDiffAttributed } = await import("./cdp-RFQCIQFP.js");
174
- return pixelDiffAttributed(chrome, previous, current, url);
173
+ const { pixelDiffAttributed } = await import("./cdp-IGPGFMC6.js");
174
+ return pixelDiffAttributed(chrome, previous, current, url, outPath);
175
175
  }
176
176
  function buildRuntimeFixPrompt(report) {
177
177
  return `The running app has runtime problems.${runtimeSection(report)}
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  App
4
- } from "./chunk-2KJLEM53.js";
5
- import "./chunk-3GHZOVAA.js";
4
+ } from "./chunk-6OFUO3UT.js";
5
+ import "./chunk-LNJ7O5O4.js";
6
6
  import {
7
7
  connectDaemon,
8
8
  socketPath,
9
9
  startDaemon
10
- } from "./chunk-BYT6OOGK.js";
11
- import "./chunk-VCDZGQBB.js";
10
+ } from "./chunk-ORJPNSTE.js";
11
+ import "./chunk-7PKIVVRB.js";
12
12
  import "./chunk-UBADCBPT.js";
13
13
  import "./chunk-IEQGTVWN.js";
14
14
  import "./chunk-AUJJGMZG.js";
@@ -23,9 +23,9 @@ import {
23
23
  composePrompt
24
24
  } from "./chunk-WAJXATCO.js";
25
25
  import "./chunk-62JNF5M2.js";
26
- import "./chunk-7FAS2O3L.js";
26
+ import "./chunk-UMDV4ITQ.js";
27
27
  import "./chunk-7CAGWFAQ.js";
28
- import "./chunk-P6D4RSWD.js";
28
+ import "./chunk-N7YEWQGZ.js";
29
29
  import "./chunk-PZV2FJE5.js";
30
30
  import {
31
31
  detectEngines,
@@ -187,18 +187,18 @@ function registerDaemon(program2) {
187
187
  process.on("SIGTERM", stop);
188
188
  });
189
189
  program2.command("mcp").description("serve the gates as MCP tools over stdio (check, shot, flows, context)").action(async () => {
190
- const { runMcpServer } = await import("./server-BCHVAJJQ.js");
190
+ const { runMcpServer } = await import("./server-N2PGAV2C.js");
191
191
  runMcpServer(process.cwd());
192
192
  });
193
193
  program2.command("attach").description("attach this terminal to a running squint daemon (full TUI; --plain for line mode)").option("--plain", "line-mode attach instead of the full TUI").action(async (opts) => {
194
194
  const cwd = process.cwd();
195
195
  if (!opts.plain) {
196
196
  try {
197
- const { RemoteSession } = await import("./remote-WS6XGUBU.js");
197
+ const { RemoteSession } = await import("./remote-TOZNJWGE.js");
198
198
  const remote = await RemoteSession.connect(cwd);
199
199
  const config = loadConfig(defaultPaths(cwd));
200
200
  const { render: render2 } = await import("ink");
201
- const { App: App2 } = await import("./App-HPUWFBRV.js");
201
+ const { App: App2 } = await import("./App-OHKGQTDL.js");
202
202
  const React = await import("react");
203
203
  render2(
204
204
  React.createElement(App2, {
@@ -315,7 +315,7 @@ function registerEnv(program2) {
315
315
  }
316
316
  }
317
317
  const { findChrome } = await import("./chrome-CAN7TVH6.js");
318
- const { hasWebSocket } = await import("./cdp-RFQCIQFP.js");
318
+ const { hasWebSocket } = await import("./cdp-IGPGFMC6.js");
319
319
  const chrome = findChrome();
320
320
  console.log(
321
321
  chrome ? `${pc3.green("\u2713")} Chrome ${pc3.dim(chrome)}` : `${pc3.yellow("\u25CB")} Chrome ${pc3.dim("\u2014 screenshots and runtime probing disabled")}`
@@ -551,7 +551,7 @@ function registerQuality(program2) {
551
551
  report.gates = gateResults.map((r) => ({ id: r.gate.id, ok: r.ok, durationMs: r.durationMs, outputTail: r.ok ? void 0 : r.outputTail }));
552
552
  if (gateResults.some((r) => !r.ok)) failed = true;
553
553
  if (opts.url) {
554
- const { captureViewports, runtimeSummary, previewDir } = await import("./preview-WYFBPFFL.js");
554
+ const { captureViewports, runtimeSummary, previewDir } = await import("./preview-4OF6JB5B.js");
555
555
  const capture = await captureViewports(cwd, opts.url);
556
556
  if (!capture) {
557
557
  console.log(pc5.red("\u2717 audit: capture failed (Chrome missing or URL unreachable)"));
@@ -583,14 +583,14 @@ function registerQuality(program2) {
583
583
  console.log(`${pc5.green("\u2713")} audit ${pc5.dim(`(${advisories.a11y.length} a11y, ${advisories.slop.length} slop, ${advisories.jank.length} jank advisories)`)}`);
584
584
  }
585
585
  }
586
- const { loadFlows } = await import("./flows-G5K3XYQZ.js");
586
+ const { loadFlows } = await import("./flows-OY4BSCKD.js");
587
587
  const flows = loadFlows(cwd);
588
588
  if (flows.length > 0) {
589
589
  const { findChrome } = await import("./chrome-CAN7TVH6.js");
590
590
  const chrome = findChrome();
591
591
  const flowReport = [];
592
592
  if (chrome) {
593
- const { runFlow } = await import("./cdp-RFQCIQFP.js");
593
+ const { runFlow } = await import("./cdp-IGPGFMC6.js");
594
594
  for (const flow of flows) {
595
595
  const result = await runFlow(chrome, opts.url, flow, previewDir(cwd));
596
596
  flowReport.push({ name: flow.name, ok: result.ok, detail: result.ok ? void 0 : `step ${result.failedStep}: ${result.detail}` });
@@ -604,7 +604,7 @@ function registerQuality(program2) {
604
604
  report.ok = !failed;
605
605
  report.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
606
606
  try {
607
- const { writeReceipt } = await import("./receipts-INSRID6M.js");
607
+ const { writeReceipt } = await import("./receipts-EFFO3NYZ.js");
608
608
  console.log(pc5.dim(`receipt \u2192 ${writeReceipt(cwd, report)}`));
609
609
  } catch {
610
610
  }
@@ -616,7 +616,7 @@ function registerQuality(program2) {
616
616
  if (failed) process.exitCode = 1;
617
617
  });
618
618
  program2.command("shot").description("Screenshot a running app at mobile/tablet/desktop viewports (+ .squint/routes)").argument("<url>", "URL of the running app (e.g. http://localhost:5173)").action(async (url) => {
619
- const { captureViewports } = await import("./preview-WYFBPFFL.js");
619
+ const { captureViewports } = await import("./preview-4OF6JB5B.js");
620
620
  const result = await captureViewports(process.cwd(), url);
621
621
  if (!result) {
622
622
  console.error(pc5.red("\u2717 no Chrome/Chromium found"));
@@ -803,7 +803,7 @@ function registerTui(program2) {
803
803
  }
804
804
 
805
805
  // src/cli.tsx
806
- var VERSION = true ? "0.6.0" : "0.0.0-dev";
806
+ var VERSION = true ? "0.7.0" : "0.0.0-dev";
807
807
  var program = new Command();
808
808
  program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
809
809
  registerRun(program);
@@ -3,7 +3,7 @@ import {
3
3
  COMMANDS,
4
4
  commandHelp,
5
5
  completeCommand
6
- } from "./chunk-3GHZOVAA.js";
6
+ } from "./chunk-LNJ7O5O4.js";
7
7
  export {
8
8
  COMMANDS,
9
9
  commandHelp,
@@ -4,7 +4,7 @@ import {
4
4
  parseFlow,
5
5
  stepExpression,
6
6
  suggestFlows
7
- } from "./chunk-K7JNVCCU.js";
7
+ } from "./chunk-3XO3GF5X.js";
8
8
  export {
9
9
  loadFlows,
10
10
  parseFlow,
@@ -11,9 +11,9 @@ import {
11
11
  probeRuntime,
12
12
  routeShotName,
13
13
  runtimeSummary
14
- } from "./chunk-7FAS2O3L.js";
14
+ } from "./chunk-UMDV4ITQ.js";
15
15
  import "./chunk-7CAGWFAQ.js";
16
- import "./chunk-P6D4RSWD.js";
16
+ import "./chunk-N7YEWQGZ.js";
17
17
  import "./chunk-PZV2FJE5.js";
18
18
  import "./chunk-DABIMOMV.js";
19
19
  export {
@@ -31,7 +31,7 @@ function buildReceipt(cwd, report) {
31
31
  }
32
32
  }
33
33
  const unsigned = {
34
- version: true ? "0.6.0" : "0.0.0-dev",
34
+ version: true ? "0.7.0" : "0.0.0-dev",
35
35
  node: process.version,
36
36
  gitHead,
37
37
  report,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  connectDaemon,
4
4
  socketPath
5
- } from "./chunk-BYT6OOGK.js";
6
- import "./chunk-VCDZGQBB.js";
5
+ } from "./chunk-ORJPNSTE.js";
6
+ import "./chunk-7PKIVVRB.js";
7
7
  import "./chunk-UBADCBPT.js";
8
8
  import "./chunk-IEQGTVWN.js";
9
9
  import "./chunk-AUJJGMZG.js";
@@ -14,9 +14,9 @@ import "./chunk-DOWWBLX2.js";
14
14
  import "./chunk-K5QJMSJH.js";
15
15
  import "./chunk-WAJXATCO.js";
16
16
  import "./chunk-62JNF5M2.js";
17
- import "./chunk-7FAS2O3L.js";
17
+ import "./chunk-UMDV4ITQ.js";
18
18
  import "./chunk-7CAGWFAQ.js";
19
- import "./chunk-P6D4RSWD.js";
19
+ import "./chunk-N7YEWQGZ.js";
20
20
  import "./chunk-PZV2FJE5.js";
21
21
  import "./chunk-DABIMOMV.js";
22
22
 
@@ -12,14 +12,14 @@ import {
12
12
  } from "./chunk-62JNF5M2.js";
13
13
  import {
14
14
  loadFlows
15
- } from "./chunk-K7JNVCCU.js";
15
+ } from "./chunk-3XO3GF5X.js";
16
16
  import {
17
17
  captureViewports,
18
18
  previewDir,
19
19
  runtimeSummary
20
- } from "./chunk-7FAS2O3L.js";
20
+ } from "./chunk-UMDV4ITQ.js";
21
21
  import "./chunk-7CAGWFAQ.js";
22
- import "./chunk-P6D4RSWD.js";
22
+ import "./chunk-N7YEWQGZ.js";
23
23
  import {
24
24
  findChrome
25
25
  } from "./chunk-PZV2FJE5.js";
@@ -75,7 +75,7 @@ ${list.join("\n")}`);
75
75
  if (flows.length === 0) return "no flows declared \u2014 add .squint/flows/<name>.flow";
76
76
  const chrome = findChrome();
77
77
  if (!chrome) return "no Chrome/Chromium found";
78
- const { runFlow } = await import("./cdp-RFQCIQFP.js");
78
+ const { runFlow } = await import("./cdp-IGPGFMC6.js");
79
79
  const lines = [];
80
80
  for (const flow of flows) {
81
81
  const result = await runFlow(chrome, url, flow, previewDir(cwd));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aayambansal/squint",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and other coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",