@aayambansal/squint 0.4.8 → 0.5.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.
Files changed (28) hide show
  1. package/README.md +3 -0
  2. package/dist/{App-766KSGON.js → App-MC2XEM7K.js} +9 -9
  3. package/dist/{cdp-2VVQOHWM.js → cdp-WSWOSDEX.js} +1 -1
  4. package/dist/{chrome-SBV3H77F.js → chrome-EEXJCG6W.js} +2 -2
  5. package/dist/{chunk-QCHBDP46.js → chunk-2I3YHT6P.js} +3 -3
  6. package/dist/{flows-6LK7NGWS.js → chunk-4XSHHGA6.js} +2 -1
  7. package/dist/{chunk-VH7OOFQP.js → chunk-6NPUVPQD.js} +2 -2
  8. package/dist/{contextDoctor-A26C2ZDN.js → chunk-BKOGVFCO.js} +1 -0
  9. package/dist/{chunk-7RFQWOQV.js → chunk-CRDRWTON.js} +4 -4
  10. package/dist/{chunk-BGBG2MNY.js → chunk-DCFNX2CQ.js} +41 -2
  11. package/dist/{chunk-JZMJGLIZ.js → chunk-EB4OSUB7.js} +1 -1
  12. package/dist/{chunk-KVYGPLWW.js → chunk-GV2HAALP.js} +4 -0
  13. package/dist/{chunk-YGSF2TSO.js → chunk-J7ITMK6X.js} +1 -1
  14. package/dist/{chunk-IMDRXXFU.js → chunk-PYC53J7D.js} +1 -1
  15. package/dist/{chunk-YGPVP4YB.js → chunk-WASLLXEZ.js} +11 -11
  16. package/dist/{chunk-ATNTD3V5.js → chunk-YK5DEB7T.js} +1 -1
  17. package/dist/cli.js +99 -22
  18. package/dist/contextDoctor-3Y36YURE.js +11 -0
  19. package/dist/flows-MXJL45HW.js +11 -0
  20. package/dist/{preview-FBNVDQIV.js → preview-CDEMPHWV.js} +4 -4
  21. package/dist/{registry-MIJ6LSAY.js → registry-JBP7J3JB.js} +1 -1
  22. package/dist/{remote-OH2P4S2H.js → remote-C4BACCKX.js} +9 -9
  23. package/dist/{run-NDSNTVYP.js → run-GDDIYEBP.js} +2 -2
  24. package/dist/server-ENT2OFNL.js +155 -0
  25. package/dist/shots-6DRTXFDI.js +13 -0
  26. package/dist/{variants-3IEP7DFY.js → variants-VURXVIQO.js} +3 -3
  27. package/package.json +1 -1
  28. package/dist/shots-OKWOYF7F.js +0 -13
package/README.md CHANGED
@@ -174,6 +174,9 @@ squint doctor --probe # run every engine end to end, verify auth act
174
174
  flagged from the live page, and on Next 16+ the framework's own `/_next/mcp`
175
175
  channel feeds structured errors straight into the fix loop. `/context` itemizes
176
176
  the injected-context bill per source, with staleness warnings.
177
+ - **Two more doors in**: `squint mcp` serves the gates as MCP tools (any
178
+ 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.
177
180
  - **The sentinel**: gate evasion (deleted tests, added skips, suppressed
178
181
  diagnostics, weakened checks, locked-path touches) detected deterministically per
179
182
  turn and reported to you — never sent back to the engine that did it.
@@ -1,23 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  App
4
- } from "./chunk-JZMJGLIZ.js";
4
+ } from "./chunk-EB4OSUB7.js";
5
5
  import "./chunk-43NQNIJY.js";
6
- import "./chunk-YGPVP4YB.js";
6
+ import "./chunk-WASLLXEZ.js";
7
7
  import "./chunk-ARDV4XH6.js";
8
8
  import "./chunk-AUJJGMZG.js";
9
9
  import "./chunk-YHRAOBI2.js";
10
- import "./chunk-QCHBDP46.js";
11
- import "./chunk-YGSF2TSO.js";
12
- import "./chunk-VH7OOFQP.js";
10
+ import "./chunk-2I3YHT6P.js";
11
+ import "./chunk-J7ITMK6X.js";
12
+ import "./chunk-6NPUVPQD.js";
13
13
  import "./chunk-K5QJMSJH.js";
14
14
  import "./chunk-WAJXATCO.js";
15
15
  import "./chunk-62JNF5M2.js";
16
- import "./chunk-7RFQWOQV.js";
16
+ import "./chunk-CRDRWTON.js";
17
17
  import "./chunk-7CAGWFAQ.js";
18
- import "./chunk-BGBG2MNY.js";
19
- import "./chunk-IMDRXXFU.js";
20
- import "./chunk-KVYGPLWW.js";
18
+ import "./chunk-DCFNX2CQ.js";
19
+ import "./chunk-PYC53J7D.js";
20
+ import "./chunk-GV2HAALP.js";
21
21
  export {
22
22
  App
23
23
  };
@@ -5,7 +5,7 @@ import {
5
5
  pixelDiffAttributed,
6
6
  pixelDiffPct,
7
7
  runFlow
8
- } from "./chunk-BGBG2MNY.js";
8
+ } from "./chunk-DCFNX2CQ.js";
9
9
  export {
10
10
  cdpCapture,
11
11
  hasWebSocket,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  findChrome,
4
4
  screenshot
5
- } from "./chunk-IMDRXXFU.js";
6
- import "./chunk-KVYGPLWW.js";
5
+ } from "./chunk-PYC53J7D.js";
6
+ import "./chunk-GV2HAALP.js";
7
7
  export {
8
8
  findChrome,
9
9
  screenshot
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  variantsRoot
4
- } from "./chunk-YGSF2TSO.js";
4
+ } from "./chunk-J7ITMK6X.js";
5
5
  import {
6
6
  findChrome,
7
7
  screenshot
8
- } from "./chunk-IMDRXXFU.js";
8
+ } from "./chunk-PYC53J7D.js";
9
9
  import {
10
10
  lineSplitter
11
- } from "./chunk-KVYGPLWW.js";
11
+ } from "./chunk-GV2HAALP.js";
12
12
 
13
13
  // src/variants/shots.ts
14
14
  import path2 from "path";
@@ -140,8 +140,9 @@ function stepExpression(step) {
140
140
  return null;
141
141
  }
142
142
  }
143
+
143
144
  export {
144
- loadFlows,
145
145
  parseFlow,
146
+ loadFlows,
146
147
  stepExpression
147
148
  };
@@ -3,7 +3,7 @@ import {
3
3
  findEngineBinary,
4
4
  lineSplitter,
5
5
  truncate
6
- } from "./chunk-KVYGPLWW.js";
6
+ } from "./chunk-GV2HAALP.js";
7
7
 
8
8
  // src/runner/run.ts
9
9
  import { spawn } from "child_process";
@@ -21,7 +21,7 @@ function runAgent(engine, opts, onEvent, signal) {
21
21
  let stderrTail = "";
22
22
  const child = spawn(binaryPath, engine.buildArgs(opts), {
23
23
  cwd: opts.cwd,
24
- env: process.env,
24
+ env: { ...process.env, ...engine.env },
25
25
  stdio: ["ignore", "pipe", "pipe"]
26
26
  });
27
27
  const emit = (event) => {
@@ -68,6 +68,7 @@ function formatContextReport(report) {
68
68
  }
69
69
  return out.join("\n");
70
70
  }
71
+
71
72
  export {
72
73
  contextReport,
73
74
  formatContextReport
@@ -5,11 +5,11 @@ import {
5
5
  import {
6
6
  cdpCapture,
7
7
  hasWebSocket
8
- } from "./chunk-BGBG2MNY.js";
8
+ } from "./chunk-DCFNX2CQ.js";
9
9
  import {
10
10
  findChrome,
11
11
  screenshot
12
- } from "./chunk-IMDRXXFU.js";
12
+ } from "./chunk-PYC53J7D.js";
13
13
 
14
14
  // src/preview/preview.ts
15
15
  import fs2 from "fs";
@@ -163,13 +163,13 @@ async function probeRuntime(url, cwd) {
163
163
  async function comparePulse(previous, current) {
164
164
  const chrome = findChrome();
165
165
  if (!chrome || !hasWebSocket()) return null;
166
- const { pixelDiffPct } = await import("./cdp-2VVQOHWM.js");
166
+ const { pixelDiffPct } = await import("./cdp-WSWOSDEX.js");
167
167
  return pixelDiffPct(chrome, previous, current);
168
168
  }
169
169
  async function comparePulseAttributed(previous, current, url) {
170
170
  const chrome = findChrome();
171
171
  if (!chrome || !hasWebSocket()) return null;
172
- const { pixelDiffAttributed } = await import("./cdp-2VVQOHWM.js");
172
+ const { pixelDiffAttributed } = await import("./cdp-WSWOSDEX.js");
173
173
  return pixelDiffAttributed(chrome, previous, current, url);
174
174
  }
175
175
  function buildRuntimeFixPrompt(report) {
@@ -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-6LK7NGWS.js");
182
+ const { stepExpression } = await import("./flows-MXJL45HW.js");
183
183
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
184
184
  const shots = [];
185
185
  let connection = null;
@@ -437,6 +437,42 @@ var SLOP_AUDIT = `(() => {
437
437
  break;
438
438
  }
439
439
  }
440
+ // APCA (the contrast model that graduated in DevTools 149): body text
441
+ // under |Lc| 60 reads as fog even when it squeaks past WCAG AA.
442
+ const chan = (v) => Math.pow(v / 255, 2.4);
443
+ const lum = (rgb) => {
444
+ const m = rgb.match(/rgba?\\(([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/);
445
+ if (!m) return null;
446
+ return 0.2126729 * chan(+m[1]) + 0.7151522 * chan(+m[2]) + 0.0721750 * chan(+m[3]);
447
+ };
448
+ const apca = (txt, bg) => {
449
+ let yt = lum(txt), yb = lum(bg);
450
+ if (yt === null || yb === null) return null;
451
+ const clamp = (y) => (y < 0.022 ? y + Math.pow(0.022 - y, 1.414) : y);
452
+ yt = clamp(yt); yb = clamp(yb);
453
+ const c = yb > yt ? (Math.pow(yb, 0.56) - Math.pow(yt, 0.57)) * 1.14 : (Math.pow(yb, 0.65) - Math.pow(yt, 0.62)) * 1.14;
454
+ return Math.abs(c) < 0.1 ? 0 : Math.round((Math.abs(c) - 0.027) * 100);
455
+ };
456
+ let apcaFlagged = 0;
457
+ for (const p of document.querySelectorAll('p, li, td, span')) {
458
+ if (apcaFlagged >= 2) break;
459
+ const text = (p.textContent || '').trim();
460
+ if (text.length < 40) continue;
461
+ const cs = getComputedStyle(p);
462
+ if (parseFloat(cs.fontSize) >= 24) continue;
463
+ let bgEl = p, bg = 'rgba(0, 0, 0, 0)';
464
+ while (bgEl) {
465
+ const b = getComputedStyle(bgEl).backgroundColor;
466
+ if (b && !b.includes('0, 0, 0, 0')) { bg = b; break; }
467
+ bgEl = bgEl.parentElement;
468
+ }
469
+ if (bg.includes('0, 0, 0, 0')) bg = 'rgb(255, 255, 255)';
470
+ const lc = apca(cs.color, bg);
471
+ if (lc !== null && lc > 0 && lc < 60) {
472
+ out.push('low APCA contrast: Lc ' + lc + ' on <' + p.tagName.toLowerCase() + '> body text ("' + text.slice(0, 40) + '\u2026") \u2014 fog, not elegance');
473
+ apcaFlagged++;
474
+ }
475
+ }
440
476
  const vw = innerWidth, vh = innerHeight;
441
477
  for (const el of document.querySelectorAll('*')) {
442
478
  const r = el.getBoundingClientRect();
@@ -828,7 +864,10 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
828
864
  {
829
865
  width: viewport.width,
830
866
  height: viewport.height,
831
- deviceScaleFactor: 1,
867
+ // 2x for review shots: sub-pixel border/kerning slop is
868
+ // invisible at 1x. The pulse stays 1x — its diff math and
869
+ // hit-testing assume CSS pixels.
870
+ deviceScaleFactor: viewport.name === "pulse" ? 1 : 2,
832
871
  mobile: viewport.width < 500
833
872
  },
834
873
  sessionId
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-43NQNIJY.js";
5
5
  import {
6
6
  Session
7
- } from "./chunk-YGPVP4YB.js";
7
+ } from "./chunk-WASLLXEZ.js";
8
8
 
9
9
  // src/tui/App.tsx
10
10
  import { Box as Box3, Static, Text as Text3, useApp, useInput } from "ink";
@@ -148,6 +148,10 @@ var claude = {
148
148
  binary: "claude",
149
149
  install: "npm install -g @anthropic-ai/claude-code",
150
150
  supportsResume: true,
151
+ // 2.1.211+: subagent output joins stream-json. The env-var form is a
152
+ // no-op on older CLIs, unlike the flag, so the loops see spawned work
153
+ // wherever it's supported.
154
+ env: { CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1" },
151
155
  buildArgs(opts) {
152
156
  const permissionMode = opts.mode === "plan" ? "plan" : opts.mode === "yolo" ? "bypassPermissions" : "acceptEdits";
153
157
  const args = [
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runAgent
4
- } from "./chunk-VH7OOFQP.js";
4
+ } from "./chunk-6NPUVPQD.js";
5
5
  import {
6
6
  FAMILIES,
7
7
  renderFamilyBrief
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  findBinary
4
- } from "./chunk-KVYGPLWW.js";
4
+ } from "./chunk-GV2HAALP.js";
5
5
 
6
6
  // src/preview/chrome.ts
7
7
  import { spawn } from "child_process";
@@ -23,16 +23,16 @@ import {
23
23
  buildFixPrompt,
24
24
  detectDevCommand,
25
25
  screenshotVariants
26
- } from "./chunk-QCHBDP46.js";
26
+ } from "./chunk-2I3YHT6P.js";
27
27
  import {
28
28
  applyVariant,
29
29
  cleanVariants,
30
30
  listVariants,
31
31
  runVariants
32
- } from "./chunk-YGSF2TSO.js";
32
+ } from "./chunk-J7ITMK6X.js";
33
33
  import {
34
34
  runAgent
35
- } from "./chunk-VH7OOFQP.js";
35
+ } from "./chunk-6NPUVPQD.js";
36
36
  import {
37
37
  composePrompt
38
38
  } from "./chunk-WAJXATCO.js";
@@ -49,7 +49,7 @@ import {
49
49
  comparePulseAttributed,
50
50
  probeRuntime,
51
51
  runtimeSummary
52
- } from "./chunk-7RFQWOQV.js";
52
+ } from "./chunk-CRDRWTON.js";
53
53
  import {
54
54
  clearState,
55
55
  loadState,
@@ -57,10 +57,10 @@ import {
57
57
  } from "./chunk-7CAGWFAQ.js";
58
58
  import {
59
59
  findChrome
60
- } from "./chunk-IMDRXXFU.js";
60
+ } from "./chunk-PYC53J7D.js";
61
61
  import {
62
62
  getEngine
63
- } from "./chunk-KVYGPLWW.js";
63
+ } from "./chunk-GV2HAALP.js";
64
64
 
65
65
  // src/session/engine.ts
66
66
  import fs2 from "fs";
@@ -859,7 +859,7 @@ They are objective defects, not style preferences.`
859
859
  const arg = rest.join(" ").trim();
860
860
  switch (name) {
861
861
  case "engines": {
862
- void import("./registry-MIJ6LSAY.js").then(({ detectEngines }) => {
862
+ void import("./registry-JBP7J3JB.js").then(({ detectEngines }) => {
863
863
  const lines = detectEngines().map(({ engine, path: binaryPath }) => {
864
864
  const mark = binaryPath ? "\u2713" : "\u2717";
865
865
  const traits = [engine.createParser ? "stream" : "text", engine.supportsResume ? "resume" : null].filter(Boolean).join(" \xB7 ");
@@ -948,7 +948,7 @@ They are objective defects, not style preferences.`
948
948
  break;
949
949
  }
950
950
  case "context": {
951
- import("./contextDoctor-A26C2ZDN.js").then(({ contextReport, formatContextReport }) => {
951
+ import("./contextDoctor-3Y36YURE.js").then(({ contextReport, formatContextReport }) => {
952
952
  this.push("status", formatContextReport(contextReport(this.execCwd())));
953
953
  }).catch((error) => {
954
954
  this.push("status", `context report failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -1072,7 +1072,7 @@ They are objective defects, not style preferences.`
1072
1072
  break;
1073
1073
  }
1074
1074
  void (async () => {
1075
- const { loadFlows } = await import("./flows-6LK7NGWS.js");
1075
+ const { loadFlows } = await import("./flows-MXJL45HW.js");
1076
1076
  const flows = loadFlows(this.opts.cwd);
1077
1077
  if (flows.length === 0) {
1078
1078
  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");
@@ -1083,8 +1083,8 @@ They are objective defects, not style preferences.`
1083
1083
  this.push("error", "no Chrome/Chromium found for flows");
1084
1084
  return;
1085
1085
  }
1086
- const { runFlow } = await import("./cdp-2VVQOHWM.js");
1087
- const { previewDir } = await import("./preview-FBNVDQIV.js");
1086
+ const { runFlow } = await import("./cdp-WSWOSDEX.js");
1087
+ const { previewDir } = await import("./preview-CDEMPHWV.js");
1088
1088
  this.push("status", `replaying ${flows.length} flow(s)\u2026`);
1089
1089
  this.notify({ running: true, runStartedAt: Date.now() });
1090
1090
  const failures = [];
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Session
4
- } from "./chunk-YGPVP4YB.js";
4
+ } from "./chunk-WASLLXEZ.js";
5
5
 
6
6
  // src/daemon/client.ts
7
7
  import net from "net";
package/dist/cli.js CHANGED
@@ -3,33 +3,33 @@ import {
3
3
  connectDaemon,
4
4
  socketPath,
5
5
  startDaemon
6
- } from "./chunk-ATNTD3V5.js";
6
+ } from "./chunk-YK5DEB7T.js";
7
7
  import {
8
8
  App
9
- } from "./chunk-JZMJGLIZ.js";
9
+ } from "./chunk-EB4OSUB7.js";
10
10
  import "./chunk-43NQNIJY.js";
11
- import "./chunk-YGPVP4YB.js";
11
+ import "./chunk-WASLLXEZ.js";
12
12
  import "./chunk-ARDV4XH6.js";
13
13
  import "./chunk-AUJJGMZG.js";
14
14
  import "./chunk-YHRAOBI2.js";
15
- import "./chunk-QCHBDP46.js";
16
- import "./chunk-YGSF2TSO.js";
15
+ import "./chunk-2I3YHT6P.js";
16
+ import "./chunk-J7ITMK6X.js";
17
17
  import {
18
18
  runAgent
19
- } from "./chunk-VH7OOFQP.js";
19
+ } from "./chunk-6NPUVPQD.js";
20
20
  import "./chunk-K5QJMSJH.js";
21
21
  import {
22
22
  composePrompt
23
23
  } from "./chunk-WAJXATCO.js";
24
24
  import "./chunk-62JNF5M2.js";
25
- import "./chunk-7RFQWOQV.js";
25
+ import "./chunk-CRDRWTON.js";
26
26
  import "./chunk-7CAGWFAQ.js";
27
- import "./chunk-BGBG2MNY.js";
28
- import "./chunk-IMDRXXFU.js";
27
+ import "./chunk-DCFNX2CQ.js";
28
+ import "./chunk-PYC53J7D.js";
29
29
  import {
30
30
  detectEngines,
31
31
  getEngine
32
- } from "./chunk-KVYGPLWW.js";
32
+ } from "./chunk-GV2HAALP.js";
33
33
 
34
34
  // src/cli.tsx
35
35
  import { Command } from "commander";
@@ -185,15 +185,19 @@ function registerDaemon(program2) {
185
185
  process.on("SIGINT", stop);
186
186
  process.on("SIGTERM", stop);
187
187
  });
188
+ program2.command("mcp").description("serve the gates as MCP tools over stdio (check, shot, flows, context)").action(async () => {
189
+ const { runMcpServer } = await import("./server-ENT2OFNL.js");
190
+ runMcpServer(process.cwd());
191
+ });
188
192
  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) => {
189
193
  const cwd = process.cwd();
190
194
  if (!opts.plain) {
191
195
  try {
192
- const { RemoteSession } = await import("./remote-OH2P4S2H.js");
196
+ const { RemoteSession } = await import("./remote-C4BACCKX.js");
193
197
  const remote = await RemoteSession.connect(cwd);
194
198
  const config = loadConfig(defaultPaths(cwd));
195
199
  const { render: render2 } = await import("ink");
196
- const { App: App2 } = await import("./App-766KSGON.js");
200
+ const { App: App2 } = await import("./App-MC2XEM7K.js");
197
201
  const React = await import("react");
198
202
  render2(
199
203
  React.createElement(App2, {
@@ -287,7 +291,7 @@ function registerEnv(program2) {
287
291
  console.log(`${status} ${engine.name}${binaryPath ? "" : pc3.dim(` \u2014 install: ${engine.install}`)}`);
288
292
  }
289
293
  if (options.probe) {
290
- const { runAgent: runAgent2 } = await import("./run-NDSNTVYP.js");
294
+ const { runAgent: runAgent2 } = await import("./run-GDDIYEBP.js");
291
295
  console.log(pc3.dim("\nprobing engines with a one-word prompt (verifies auth end to end)\u2026"));
292
296
  for (const { engine, path: binaryPath } of detected) {
293
297
  if (!binaryPath) continue;
@@ -309,8 +313,8 @@ function registerEnv(program2) {
309
313
  );
310
314
  }
311
315
  }
312
- const { findChrome } = await import("./chrome-SBV3H77F.js");
313
- const { hasWebSocket } = await import("./cdp-2VVQOHWM.js");
316
+ const { findChrome } = await import("./chrome-EEXJCG6W.js");
317
+ const { hasWebSocket } = await import("./cdp-WSWOSDEX.js");
314
318
  const chrome = findChrome();
315
319
  console.log(
316
320
  chrome ? `${pc3.green("\u2713")} Chrome ${pc3.dim(chrome)}` : `${pc3.yellow("\u25CB")} Chrome ${pc3.dim("\u2014 screenshots and runtime probing disabled")}`
@@ -451,7 +455,7 @@ function registerProject(program2) {
451
455
  process.exitCode = 1;
452
456
  return;
453
457
  }
454
- const { runVariants, cleanVariants } = await import("./variants-3IEP7DFY.js");
458
+ const { runVariants, cleanVariants } = await import("./variants-VURXVIQO.js");
455
459
  const config = loadConfig(defaultPaths(cwd));
456
460
  const engineId = resolveEngineId(config, options.engine);
457
461
  const engine = getEngine(engineId);
@@ -469,7 +473,7 @@ function registerProject(program2) {
469
473
  );
470
474
  const succeeded = runs.filter((r) => r.result.ok);
471
475
  if (options.shots && succeeded.length > 0) {
472
- const { screenshotVariants } = await import("./shots-OKWOYF7F.js");
476
+ const { screenshotVariants } = await import("./shots-6DRTXFDI.js");
473
477
  console.log(pc4.dim("capturing screenshots\u2026"));
474
478
  const shots = await screenshotVariants(cwd, succeeded.map((r) => r.variant));
475
479
  for (const shot of shots) {
@@ -484,7 +488,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
484
488
  }
485
489
  );
486
490
  variantsCommand.command("list").description("List generated variants").action(async () => {
487
- const { listVariants } = await import("./variants-3IEP7DFY.js");
491
+ const { listVariants } = await import("./variants-VURXVIQO.js");
488
492
  const ids = listVariants(process.cwd());
489
493
  if (ids.length === 0) {
490
494
  console.log(pc4.dim('no variants \u2014 squint variants gen <n> "<ask>"'));
@@ -493,7 +497,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
493
497
  for (const id of ids) console.log(id);
494
498
  });
495
499
  variantsCommand.command("apply").description("Apply one variant\u2019s changes to the main tree and discard the rest").argument("<id>", "family id of the winning variant").action(async (id) => {
496
- const { applyVariant, cleanVariants } = await import("./variants-3IEP7DFY.js");
500
+ const { applyVariant, cleanVariants } = await import("./variants-VURXVIQO.js");
497
501
  const cwd = process.cwd();
498
502
  const result = applyVariant(cwd, id);
499
503
  if (!result.ok) {
@@ -505,7 +509,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
505
509
  console.log(pc4.green(`\u2713 applied ${id} to the working tree`) + pc4.dim(" \u2014 review with git diff"));
506
510
  });
507
511
  variantsCommand.command("clean").description("Discard all variants").action(async () => {
508
- const { cleanVariants } = await import("./variants-3IEP7DFY.js");
512
+ const { cleanVariants } = await import("./variants-VURXVIQO.js");
509
513
  const count = cleanVariants(process.cwd());
510
514
  console.log(pc4.dim(`removed ${count} variant(s)`));
511
515
  });
@@ -532,8 +536,81 @@ function registerQuality(program2) {
532
536
  });
533
537
  if (results.some((r) => !r.ok)) process.exitCode = 1;
534
538
  });
539
+ program2.command("ci").description("One-shot verification for pipelines: gates (+ audits and flows with --url), JSON report, non-zero exit on failure").option("--url <url>", "also audit a running app URL (runtime, a11y, phantoms, jank) and replay flows").option("--json <path>", "write the machine-readable report here").action(async (opts) => {
540
+ const cwd = process.cwd();
541
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
542
+ const report = { startedAt, cwd };
543
+ let failed = false;
544
+ const { detectGates, runGates } = await import("./gates-ZC67NMW3.js");
545
+ const gates = detectGates(cwd);
546
+ const gateResults = await runGates(cwd, gates, (result) => {
547
+ console.log(`${result.ok ? pc5.green("\u2713") : pc5.red("\u2717")} ${result.gate.id} ${pc5.dim(`${(result.durationMs / 1e3).toFixed(1)}s`)}`);
548
+ if (!result.ok) console.log(pc5.dim(result.outputTail.split("\n").slice(-12).join("\n")));
549
+ });
550
+ report.gates = gateResults.map((r) => ({ id: r.gate.id, ok: r.ok, durationMs: r.durationMs, outputTail: r.ok ? void 0 : r.outputTail }));
551
+ if (gateResults.some((r) => !r.ok)) failed = true;
552
+ if (opts.url) {
553
+ const { captureViewports, runtimeSummary, previewDir } = await import("./preview-CDEMPHWV.js");
554
+ const capture = await captureViewports(cwd, opts.url);
555
+ if (!capture) {
556
+ console.log(pc5.red("\u2717 audit: capture failed (Chrome missing or URL unreachable)"));
557
+ report.audit = { ok: false };
558
+ failed = true;
559
+ } else {
560
+ const runtime = capture.runtime ? runtimeSummary(capture.runtime) : null;
561
+ const hard = {
562
+ runtime,
563
+ phantoms: capture.phantoms ?? [],
564
+ viewTransitions: (capture.viewTransitions ?? []).filter((v) => v.startsWith("duplicate")),
565
+ checkFailures: capture.checkFailures ?? []
566
+ };
567
+ const advisories = {
568
+ a11y: capture.a11y ?? [],
569
+ slop: capture.slop ?? [],
570
+ jank: capture.jank ?? []
571
+ };
572
+ const hardCount = (runtime ? 1 : 0) + hard.phantoms.length + hard.viewTransitions.length + hard.checkFailures.length;
573
+ report.audit = { ok: hardCount === 0, hard, advisories, shots: capture.shots.map((s) => s.path) };
574
+ if (hardCount > 0) {
575
+ failed = true;
576
+ console.log(pc5.red(`\u2717 audit: ${hardCount} hard finding(s)`));
577
+ for (const [k, v] of Object.entries(hard)) {
578
+ if (Array.isArray(v) && v.length > 0) console.log(pc5.dim(` ${k}: ${v.join(" \xB7 ")}`));
579
+ else if (typeof v === "string" && v) console.log(pc5.dim(` ${k}: ${v}`));
580
+ }
581
+ } else {
582
+ console.log(`${pc5.green("\u2713")} audit ${pc5.dim(`(${advisories.a11y.length} a11y, ${advisories.slop.length} slop, ${advisories.jank.length} jank advisories)`)}`);
583
+ }
584
+ }
585
+ const { loadFlows } = await import("./flows-MXJL45HW.js");
586
+ const flows = loadFlows(cwd);
587
+ if (flows.length > 0) {
588
+ const { findChrome } = await import("./chrome-EEXJCG6W.js");
589
+ const chrome = findChrome();
590
+ const flowReport = [];
591
+ if (chrome) {
592
+ const { runFlow } = await import("./cdp-WSWOSDEX.js");
593
+ for (const flow of flows) {
594
+ const result = await runFlow(chrome, opts.url, flow, previewDir(cwd));
595
+ flowReport.push({ name: flow.name, ok: result.ok, detail: result.ok ? void 0 : `step ${result.failedStep}: ${result.detail}` });
596
+ console.log(result.ok ? `${pc5.green("\u2713")} flow ${flow.name}` : pc5.red(`\u2717 flow ${flow.name} \u2014 step ${result.failedStep}: ${result.detail}`));
597
+ if (!result.ok) failed = true;
598
+ }
599
+ }
600
+ report.flows = flowReport;
601
+ }
602
+ }
603
+ report.ok = !failed;
604
+ report.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
605
+ if (opts.json) {
606
+ const fs2 = await import("fs");
607
+ fs2.writeFileSync(opts.json, JSON.stringify(report, null, 2));
608
+ console.log(pc5.dim(`report \u2192 ${opts.json}`));
609
+ }
610
+ if (failed) process.exitCode = 1;
611
+ });
535
612
  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) => {
536
- const { captureViewports } = await import("./preview-FBNVDQIV.js");
613
+ const { captureViewports } = await import("./preview-CDEMPHWV.js");
537
614
  const result = await captureViewports(process.cwd(), url);
538
615
  if (!result) {
539
616
  console.error(pc5.red("\u2717 no Chrome/Chromium found"));
@@ -720,7 +797,7 @@ function registerTui(program2) {
720
797
  }
721
798
 
722
799
  // src/cli.tsx
723
- var VERSION = true ? "0.4.8" : "0.0.0-dev";
800
+ var VERSION = true ? "0.5.0" : "0.0.0-dev";
724
801
  var program = new Command();
725
802
  program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
726
803
  registerRun(program);
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ contextReport,
4
+ formatContextReport
5
+ } from "./chunk-BKOGVFCO.js";
6
+ import "./chunk-ARDV4XH6.js";
7
+ import "./chunk-WAJXATCO.js";
8
+ export {
9
+ contextReport,
10
+ formatContextReport
11
+ };
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadFlows,
4
+ parseFlow,
5
+ stepExpression
6
+ } from "./chunk-4XSHHGA6.js";
7
+ export {
8
+ loadFlows,
9
+ parseFlow,
10
+ stepExpression
11
+ };
@@ -11,11 +11,11 @@ import {
11
11
  probeRuntime,
12
12
  routeShotName,
13
13
  runtimeSummary
14
- } from "./chunk-7RFQWOQV.js";
14
+ } from "./chunk-CRDRWTON.js";
15
15
  import "./chunk-7CAGWFAQ.js";
16
- import "./chunk-BGBG2MNY.js";
17
- import "./chunk-IMDRXXFU.js";
18
- import "./chunk-KVYGPLWW.js";
16
+ import "./chunk-DCFNX2CQ.js";
17
+ import "./chunk-PYC53J7D.js";
18
+ import "./chunk-GV2HAALP.js";
19
19
  export {
20
20
  VIEWPORTS,
21
21
  buildReviewPrompt,
@@ -5,7 +5,7 @@ import {
5
5
  findBinary,
6
6
  findEngineBinary,
7
7
  getEngine
8
- } from "./chunk-KVYGPLWW.js";
8
+ } from "./chunk-GV2HAALP.js";
9
9
  export {
10
10
  detectEngines,
11
11
  engines,
@@ -2,22 +2,22 @@
2
2
  import {
3
3
  connectDaemon,
4
4
  socketPath
5
- } from "./chunk-ATNTD3V5.js";
6
- import "./chunk-YGPVP4YB.js";
5
+ } from "./chunk-YK5DEB7T.js";
6
+ import "./chunk-WASLLXEZ.js";
7
7
  import "./chunk-ARDV4XH6.js";
8
8
  import "./chunk-AUJJGMZG.js";
9
9
  import "./chunk-YHRAOBI2.js";
10
- import "./chunk-QCHBDP46.js";
11
- import "./chunk-YGSF2TSO.js";
12
- import "./chunk-VH7OOFQP.js";
10
+ import "./chunk-2I3YHT6P.js";
11
+ import "./chunk-J7ITMK6X.js";
12
+ import "./chunk-6NPUVPQD.js";
13
13
  import "./chunk-K5QJMSJH.js";
14
14
  import "./chunk-WAJXATCO.js";
15
15
  import "./chunk-62JNF5M2.js";
16
- import "./chunk-7RFQWOQV.js";
16
+ import "./chunk-CRDRWTON.js";
17
17
  import "./chunk-7CAGWFAQ.js";
18
- import "./chunk-BGBG2MNY.js";
19
- import "./chunk-IMDRXXFU.js";
20
- import "./chunk-KVYGPLWW.js";
18
+ import "./chunk-DCFNX2CQ.js";
19
+ import "./chunk-PYC53J7D.js";
20
+ import "./chunk-GV2HAALP.js";
21
21
 
22
22
  // src/daemon/remote.ts
23
23
  var noteId = 0;
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runAgent
4
- } from "./chunk-VH7OOFQP.js";
5
- import "./chunk-KVYGPLWW.js";
4
+ } from "./chunk-6NPUVPQD.js";
5
+ import "./chunk-GV2HAALP.js";
6
6
  export {
7
7
  runAgent
8
8
  };
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ contextReport,
4
+ formatContextReport
5
+ } from "./chunk-BKOGVFCO.js";
6
+ import "./chunk-ARDV4XH6.js";
7
+ import "./chunk-WAJXATCO.js";
8
+ import {
9
+ detectGates,
10
+ runGates
11
+ } from "./chunk-62JNF5M2.js";
12
+ import {
13
+ loadFlows
14
+ } from "./chunk-4XSHHGA6.js";
15
+ import {
16
+ captureViewports,
17
+ previewDir,
18
+ runtimeSummary
19
+ } from "./chunk-CRDRWTON.js";
20
+ import "./chunk-7CAGWFAQ.js";
21
+ import "./chunk-DCFNX2CQ.js";
22
+ import {
23
+ findChrome
24
+ } from "./chunk-PYC53J7D.js";
25
+ import "./chunk-GV2HAALP.js";
26
+
27
+ // src/mcp/server.ts
28
+ import readline from "readline";
29
+ var TOOLS = [
30
+ {
31
+ name: "squint_check",
32
+ description: "Run the repo quality gates (typecheck, lint, format, test, build as detected). Returns each gate with pass/fail and failure output.",
33
+ inputSchema: { type: "object", properties: {} },
34
+ async run(_args, cwd) {
35
+ const gates = detectGates(cwd);
36
+ if (gates.length === 0) return "no gates detected (no package.json scripts or known tools)";
37
+ const results = await runGates(cwd, gates);
38
+ return results.map((r) => `${r.ok ? "\u2713" : "\u2717"} ${r.gate.display}${r.ok ? "" : `
39
+ ${r.outputTail.slice(-1500)}`}`).join("\n");
40
+ }
41
+ },
42
+ {
43
+ name: "squint_shot",
44
+ description: "Screenshot and audit a running app URL headlessly: runtime errors, accessibility sweep, anti-slop tells, phantom classes, view-transition breakage, jank attribution, screen-reader narration. Returns findings; screenshots land in .squint/preview/.",
45
+ inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
46
+ async run(args, cwd) {
47
+ const url = String(args.url ?? "");
48
+ const result = await captureViewports(cwd, url);
49
+ if (!result) return "capture failed \u2014 is Chrome installed and the URL reachable?";
50
+ const sections = [`shots: ${result.shots.map((s) => s.path).join(", ")}`];
51
+ const runtime = result.runtime ? runtimeSummary(result.runtime) : null;
52
+ if (runtime) sections.push(`runtime: ${runtime}`);
53
+ for (const [label, list] of [
54
+ ["a11y", result.a11y],
55
+ ["slop", result.slop],
56
+ ["phantom classes", result.phantoms],
57
+ ["view transitions", result.viewTransitions],
58
+ ["jank", result.jank],
59
+ ["components", result.components]
60
+ ]) {
61
+ if (list && list.length > 0) sections.push(`${label}:
62
+ ${list.join("\n")}`);
63
+ }
64
+ return sections.join("\n\n");
65
+ }
66
+ },
67
+ {
68
+ name: "squint_flows",
69
+ description: "Replay the repo's declared user journeys (.squint/flows/*.flow) headlessly against a URL. Returns pass/fail per flow with the failing step.",
70
+ inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
71
+ async run(args, cwd) {
72
+ const url = String(args.url ?? "");
73
+ const flows = loadFlows(cwd);
74
+ if (flows.length === 0) return "no flows declared \u2014 add .squint/flows/<name>.flow";
75
+ const chrome = findChrome();
76
+ if (!chrome) return "no Chrome/Chromium found";
77
+ const { runFlow } = await import("./cdp-WSWOSDEX.js");
78
+ const lines = [];
79
+ for (const flow of flows) {
80
+ const result = await runFlow(chrome, url, flow, previewDir(cwd));
81
+ lines.push(
82
+ result.ok ? `\u2713 ${flow.name} (${flow.steps.length} steps)` : `\u2717 ${flow.name} at step ${result.failedStep}: ${result.detail}`
83
+ );
84
+ }
85
+ return lines.join("\n");
86
+ }
87
+ },
88
+ {
89
+ name: "squint_context",
90
+ description: "Itemize what squint injects into engine asks \u2014 token cost per source with staleness warnings (stale locks, generic skill triggers, oversized always-on context).",
91
+ inputSchema: { type: "object", properties: {} },
92
+ async run(_args, cwd) {
93
+ return formatContextReport(contextReport(cwd));
94
+ }
95
+ }
96
+ ];
97
+ function runMcpServer(cwd, input = process.stdin, output = process.stdout) {
98
+ const write = (msg) => output.write(`${JSON.stringify(msg)}
99
+ `);
100
+ const rl = readline.createInterface({ input });
101
+ rl.on("line", (line) => {
102
+ if (!line.trim()) return;
103
+ let req;
104
+ try {
105
+ req = JSON.parse(line);
106
+ } catch {
107
+ return;
108
+ }
109
+ void handle(req, cwd, write);
110
+ });
111
+ }
112
+ async function handle(req, cwd, write) {
113
+ const reply = (result) => {
114
+ if (req.id !== void 0) write({ jsonrpc: "2.0", id: req.id, result });
115
+ };
116
+ const fail = (code, message) => {
117
+ if (req.id !== void 0) write({ jsonrpc: "2.0", id: req.id, error: { code, message } });
118
+ };
119
+ switch (req.method) {
120
+ case "initialize":
121
+ reply({
122
+ protocolVersion: "2025-06-18",
123
+ capabilities: { tools: {} },
124
+ serverInfo: { name: "squint", version: "0" }
125
+ });
126
+ return;
127
+ case "notifications/initialized":
128
+ case "ping":
129
+ if (req.method === "ping") reply({});
130
+ return;
131
+ case "tools/list":
132
+ reply({ tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })) });
133
+ return;
134
+ case "tools/call": {
135
+ const name = String(req.params?.name ?? "");
136
+ const tool = TOOLS.find((t) => t.name === name);
137
+ if (!tool) return fail(-32602, `unknown tool: ${name}`);
138
+ try {
139
+ const text = await tool.run(req.params?.arguments ?? {}, cwd);
140
+ reply({ content: [{ type: "text", text }] });
141
+ } catch (error) {
142
+ reply({
143
+ content: [{ type: "text", text: `tool failed: ${error instanceof Error ? error.message : String(error)}` }],
144
+ isError: true
145
+ });
146
+ }
147
+ return;
148
+ }
149
+ default:
150
+ fail(-32601, `method not found: ${req.method}`);
151
+ }
152
+ }
153
+ export {
154
+ runMcpServer
155
+ };
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ screenshotVariants
4
+ } from "./chunk-2I3YHT6P.js";
5
+ import "./chunk-J7ITMK6X.js";
6
+ import "./chunk-6NPUVPQD.js";
7
+ import "./chunk-K5QJMSJH.js";
8
+ import "./chunk-WAJXATCO.js";
9
+ import "./chunk-PYC53J7D.js";
10
+ import "./chunk-GV2HAALP.js";
11
+ export {
12
+ screenshotVariants
13
+ };
@@ -9,11 +9,11 @@ import {
9
9
  runVariants,
10
10
  variantPrompt,
11
11
  variantsRoot
12
- } from "./chunk-YGSF2TSO.js";
13
- import "./chunk-VH7OOFQP.js";
12
+ } from "./chunk-J7ITMK6X.js";
13
+ import "./chunk-6NPUVPQD.js";
14
14
  import "./chunk-K5QJMSJH.js";
15
15
  import "./chunk-WAJXATCO.js";
16
- import "./chunk-KVYGPLWW.js";
16
+ import "./chunk-GV2HAALP.js";
17
17
  export {
18
18
  MAX_VARIANTS,
19
19
  applyVariant,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aayambansal/squint",
3
- "version": "0.4.8",
3
+ "version": "0.5.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",
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- screenshotVariants
4
- } from "./chunk-QCHBDP46.js";
5
- import "./chunk-YGSF2TSO.js";
6
- import "./chunk-VH7OOFQP.js";
7
- import "./chunk-K5QJMSJH.js";
8
- import "./chunk-WAJXATCO.js";
9
- import "./chunk-IMDRXXFU.js";
10
- import "./chunk-KVYGPLWW.js";
11
- export {
12
- screenshotVariants
13
- };