@aayambansal/squint 0.7.0 → 0.7.2

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.
@@ -1,24 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  App
4
- } from "./chunk-6OFUO3UT.js";
4
+ } from "./chunk-W72LUSYS.js";
5
+ import "./chunk-AM7WBP3P.js";
6
+ import "./chunk-KNPLVGZB.js";
5
7
  import "./chunk-LNJ7O5O4.js";
6
- import "./chunk-7PKIVVRB.js";
8
+ import "./chunk-GE6UL6C7.js";
7
9
  import "./chunk-UBADCBPT.js";
8
10
  import "./chunk-IEQGTVWN.js";
9
11
  import "./chunk-AUJJGMZG.js";
10
12
  import "./chunk-YHRAOBI2.js";
11
- import "./chunk-KNPLVGZB.js";
12
13
  import "./chunk-GDGKEMBS.js";
13
14
  import "./chunk-DOWWBLX2.js";
14
15
  import "./chunk-K5QJMSJH.js";
15
16
  import "./chunk-WAJXATCO.js";
16
17
  import "./chunk-62JNF5M2.js";
17
- import "./chunk-UMDV4ITQ.js";
18
18
  import "./chunk-7CAGWFAQ.js";
19
- import "./chunk-N7YEWQGZ.js";
19
+ import "./chunk-S7LDOWMS.js";
20
20
  import "./chunk-PZV2FJE5.js";
21
21
  import "./chunk-DABIMOMV.js";
22
+ import "./chunk-ZNV63ZIK.js";
22
23
  export {
23
24
  App
24
25
  };
@@ -5,7 +5,7 @@ import {
5
5
  pixelDiffAttributed,
6
6
  pixelDiffPct,
7
7
  runFlow
8
- } from "./chunk-N7YEWQGZ.js";
8
+ } from "./chunk-S7LDOWMS.js";
9
9
  export {
10
10
  cdpCapture,
11
11
  hasWebSocket,
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadChecks,
4
+ runIntervalSweep
5
+ } from "./chunk-ZNV63ZIK.js";
6
+ export {
7
+ loadChecks,
8
+ runIntervalSweep
9
+ };
@@ -1,4 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ DevServer,
4
+ buildFixPrompt,
5
+ detectDevCommand,
6
+ screenshotVariants
7
+ } from "./chunk-KNPLVGZB.js";
8
+ import {
9
+ buildReviewPrompt,
10
+ buildRuntimeFixPrompt,
11
+ captureViewports,
12
+ comparePulseAttributed,
13
+ probeRuntime,
14
+ runtimeSummary
15
+ } from "./chunk-GE6UL6C7.js";
2
16
  import {
3
17
  enrich
4
18
  } from "./chunk-UBADCBPT.js";
@@ -20,12 +34,6 @@ import {
20
34
  restoreSnapshot,
21
35
  takeSnapshot
22
36
  } from "./chunk-YHRAOBI2.js";
23
- import {
24
- DevServer,
25
- buildFixPrompt,
26
- detectDevCommand,
27
- screenshotVariants
28
- } from "./chunk-KNPLVGZB.js";
29
37
  import {
30
38
  applyVariant,
31
39
  cleanVariants,
@@ -44,14 +52,6 @@ import {
44
52
  detectGates,
45
53
  runGates
46
54
  } from "./chunk-62JNF5M2.js";
47
- import {
48
- buildReviewPrompt,
49
- buildRuntimeFixPrompt,
50
- captureViewports,
51
- comparePulseAttributed,
52
- probeRuntime,
53
- runtimeSummary
54
- } from "./chunk-UMDV4ITQ.js";
55
55
  import {
56
56
  clearState,
57
57
  loadState,
@@ -517,7 +517,7 @@ ${driftSummary(drift)}`);
517
517
  } catch {
518
518
  }
519
519
  try {
520
- const { scanEvasion, sentinelSummary } = await import("./sentinel-4SKSSCXX.js");
520
+ const { scanEvasion, sentinelSummary } = await import("./sentinel-VR44TKIC.js");
521
521
  const evasions = scanEvasion(this.execCwd(), checkpoint.snapshot.stashHash ?? "HEAD");
522
522
  if (evasions.length > 0) {
523
523
  this.push("error", `\u26A0 sentinel: ${evasions.length} gate-evasion pattern(s) this turn
@@ -812,7 +812,7 @@ squint verifies every turn \u2014 gates, runtime probe, page audits. Do not decl
812
812
  this.push("image", pulsePath);
813
813
  return null;
814
814
  }
815
- const { previewDir } = await import("./preview-4OF6JB5B.js");
815
+ const { previewDir } = await import("./preview-NPLDCOP6.js");
816
816
  const diff = await comparePulseAttributed(
817
817
  previous,
818
818
  current,
@@ -1190,7 +1190,7 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
1190
1190
  this.push("error", "no Chrome/Chromium found");
1191
1191
  return;
1192
1192
  }
1193
- const { suggestFlows } = await import("./flows-OY4BSCKD.js");
1193
+ const { suggestFlows } = await import("./flows-SYDNB5IW.js");
1194
1194
  this.push("status", "drafting flows from the live routes\u2026");
1195
1195
  const { created, skipped } = await suggestFlows(this.opts.cwd, this.state.devUrl, chrome);
1196
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`);
@@ -1200,7 +1200,7 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
1200
1200
  break;
1201
1201
  }
1202
1202
  void (async () => {
1203
- const { loadFlows } = await import("./flows-OY4BSCKD.js");
1203
+ const { loadFlows } = await import("./flows-SYDNB5IW.js");
1204
1204
  const flows = loadFlows(this.opts.cwd);
1205
1205
  if (flows.length === 0) {
1206
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");
@@ -1211,8 +1211,8 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
1211
1211
  this.push("error", "no Chrome/Chromium found for flows");
1212
1212
  return;
1213
1213
  }
1214
- const { runFlow } = await import("./cdp-IGPGFMC6.js");
1215
- const { previewDir } = await import("./preview-4OF6JB5B.js");
1214
+ const { runFlow } = await import("./cdp-V47GTLXG.js");
1215
+ const { previewDir } = await import("./preview-NPLDCOP6.js");
1216
1216
  this.push("status", `replaying ${flows.length} flow(s)\u2026`);
1217
1217
  this.notify({ running: true, runStartedAt: Date.now() });
1218
1218
  const failures = [];
@@ -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-IGPGFMC6.js");
147
- const { loadRoutes } = await import("./preview-4OF6JB5B.js");
146
+ const { cdpCapture } = await import("./cdp-V47GTLXG.js");
147
+ const { loadRoutes } = await import("./preview-NPLDCOP6.js");
148
148
  const dir = path2.join(cwd, ".squint", "flows");
149
149
  fs2.mkdirSync(dir, { recursive: true });
150
150
  const created = [];
@@ -5,44 +5,19 @@ import {
5
5
  import {
6
6
  cdpCapture,
7
7
  hasWebSocket
8
- } from "./chunk-N7YEWQGZ.js";
8
+ } from "./chunk-S7LDOWMS.js";
9
9
  import {
10
10
  findChrome,
11
11
  screenshot
12
12
  } from "./chunk-PZV2FJE5.js";
13
+ import {
14
+ loadChecks
15
+ } from "./chunk-ZNV63ZIK.js";
13
16
 
14
17
  // src/preview/preview.ts
15
- import fs2 from "fs";
16
- import os from "os";
17
- import path2 from "path";
18
-
19
- // src/preview/checks.ts
20
18
  import fs from "fs";
19
+ import os from "os";
21
20
  import path from "path";
22
- var MAX_CHECKS = 20;
23
- var MAX_BYTES = 1e4;
24
- function loadChecks(cwd, context = "audit") {
25
- const dir = path.join(cwd, ".squint", "checks");
26
- let entries;
27
- try {
28
- entries = fs.readdirSync(dir).filter((f) => f.endsWith(".js"));
29
- } catch {
30
- return [];
31
- }
32
- const checks = [];
33
- for (const entry of entries.sort().slice(0, MAX_CHECKS)) {
34
- try {
35
- const source = fs.readFileSync(path.join(dir, entry), "utf8");
36
- if (source.trim().length === 0 || Buffer.byteLength(source) > MAX_BYTES) continue;
37
- const pragma = /^\s*\/\/\s*squint-trigger:\s*(turn|audit)/.exec(source);
38
- checks.push({ name: entry.replace(/\.js$/, ""), source, trigger: pragma?.[1] === "audit" ? "audit" : "turn" });
39
- } catch {
40
- }
41
- }
42
- return context === "audit" ? checks : checks.filter((c) => c.trigger === "turn");
43
- }
44
-
45
- // src/preview/preview.ts
46
21
  var VIEWPORTS = [
47
22
  { name: "mobile", width: 390, height: 844 },
48
23
  { name: "tablet", width: 768, height: 1024 },
@@ -51,7 +26,7 @@ var VIEWPORTS = [
51
26
  function loadRoutes(cwd) {
52
27
  let lines = [];
53
28
  try {
54
- lines = fs2.readFileSync(path2.join(cwd, ".squint", "routes"), "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
29
+ lines = fs.readFileSync(path.join(cwd, ".squint", "routes"), "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
55
30
  } catch {
56
31
  }
57
32
  const routes = ["/", ...lines.filter((l) => l !== "/")];
@@ -62,8 +37,8 @@ function routeShotName(route) {
62
37
  return clean.length > 0 ? clean : "root";
63
38
  }
64
39
  function previewDir(cwd) {
65
- const dir = path2.join(cwd, ".squint", "preview");
66
- fs2.mkdirSync(dir, { recursive: true });
40
+ const dir = path.join(cwd, ".squint", "preview");
41
+ fs.mkdirSync(dir, { recursive: true });
67
42
  ensureSquintIgnore(cwd);
68
43
  return dir;
69
44
  }
@@ -98,7 +73,7 @@ async function captureViewports(cwd, url) {
98
73
  const shots = [];
99
74
  const errors = [];
100
75
  for (const viewport of VIEWPORTS) {
101
- const outPath = path2.join(dir, `${viewport.name}.png`);
76
+ const outPath = path.join(dir, `${viewport.name}.png`);
102
77
  const result = await screenshot(chrome, url, outPath, {
103
78
  width: viewport.width,
104
79
  height: viewport.height
@@ -164,13 +139,13 @@ async function probeRuntime(url, cwd) {
164
139
  async function comparePulse(previous, current) {
165
140
  const chrome = findChrome();
166
141
  if (!chrome || !hasWebSocket()) return null;
167
- const { pixelDiffPct } = await import("./cdp-IGPGFMC6.js");
142
+ const { pixelDiffPct } = await import("./cdp-V47GTLXG.js");
168
143
  return pixelDiffPct(chrome, previous, current);
169
144
  }
170
145
  async function comparePulseAttributed(previous, current, url, outPath) {
171
146
  const chrome = findChrome();
172
147
  if (!chrome || !hasWebSocket()) return null;
173
- const { pixelDiffAttributed } = await import("./cdp-IGPGFMC6.js");
148
+ const { pixelDiffAttributed } = await import("./cdp-V47GTLXG.js");
174
149
  return pixelDiffAttributed(chrome, previous, current, url, outPath);
175
150
  }
176
151
  function buildRuntimeFixPrompt(report) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Session
4
- } from "./chunk-7PKIVVRB.js";
4
+ } from "./chunk-AM7WBP3P.js";
5
5
 
6
6
  // src/daemon/client.ts
7
7
  import net from "net";
@@ -117,6 +117,21 @@ function startDaemon(opts) {
117
117
  socket.on("close", drop);
118
118
  socket.on("error", drop);
119
119
  });
120
+ const lastRun = /* @__PURE__ */ new Map();
121
+ const sweep = setInterval(async () => {
122
+ const state = session.getState();
123
+ if (!state.devUrl || state.running) return;
124
+ try {
125
+ const { runIntervalSweep } = await import("./checks-2XOF2K3L.js");
126
+ const failures = await runIntervalSweep(opts.cwd, state.devUrl, lastRun);
127
+ if (failures.length > 0) {
128
+ session.note(`\u23F0 interval check(s) failing:
129
+ ${failures.join("\n")}`);
130
+ }
131
+ } catch {
132
+ }
133
+ }, opts.intervalSweepMs ?? 6e4);
134
+ sweep.unref?.();
120
135
  const unsubscribe = session.subscribe(() => {
121
136
  const payload = serialize();
122
137
  for (const client of clients) {
@@ -130,6 +145,7 @@ function startDaemon(opts) {
130
145
  session,
131
146
  clientCount: () => clients.length,
132
147
  close: () => {
148
+ clearInterval(sweep);
133
149
  unsubscribe();
134
150
  for (const client of clients) client.destroy();
135
151
  server.close();
@@ -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-OY4BSCKD.js");
182
+ const { stepExpression } = await import("./flows-SYDNB5IW.js");
183
183
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
184
184
  const shots = [];
185
185
  let connection = null;
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ Session
4
+ } from "./chunk-AM7WBP3P.js";
2
5
  import {
3
6
  completeCommand
4
7
  } from "./chunk-LNJ7O5O4.js";
5
- import {
6
- Session
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";
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/preview/checks.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+ var MAX_CHECKS = 20;
7
+ var MAX_BYTES = 1e4;
8
+ function loadChecks(cwd, context = "audit") {
9
+ const dir = path.join(cwd, ".squint", "checks");
10
+ let entries;
11
+ try {
12
+ entries = fs.readdirSync(dir).filter((f) => f.endsWith(".js"));
13
+ } catch {
14
+ return [];
15
+ }
16
+ const checks = [];
17
+ for (const entry of entries.sort().slice(0, MAX_CHECKS)) {
18
+ try {
19
+ const source = fs.readFileSync(path.join(dir, entry), "utf8");
20
+ if (source.trim().length === 0 || Buffer.byteLength(source) > MAX_BYTES) continue;
21
+ const pragma = /^\s*\/\/\s*squint-trigger:\s*(turn|audit|interval)(?::(\d+))?/.exec(source);
22
+ const trigger = pragma?.[1] === "audit" ? "audit" : pragma?.[1] === "interval" ? "interval" : "turn";
23
+ checks.push({
24
+ name: entry.replace(/\.js$/, ""),
25
+ source,
26
+ trigger,
27
+ intervalSec: trigger === "interval" ? Number.parseInt(pragma?.[2] ?? "300", 10) : void 0
28
+ });
29
+ } catch {
30
+ }
31
+ }
32
+ if (context === "audit") return checks.filter((c) => c.trigger !== "interval");
33
+ if (context === "interval") return checks.filter((c) => c.trigger === "interval");
34
+ return checks.filter((c) => c.trigger === "turn");
35
+ }
36
+ async function runIntervalSweep(cwd, url, lastRun) {
37
+ const now = Date.now();
38
+ const due = loadChecks(cwd, "interval").filter(
39
+ (c) => now - (lastRun.get(c.name) ?? 0) >= (c.intervalSec ?? 300) * 1e3
40
+ );
41
+ if (due.length === 0) return [];
42
+ for (const check of due) lastRun.set(check.name, now);
43
+ const { findChrome } = await import("./chrome-CAN7TVH6.js");
44
+ const chrome = findChrome();
45
+ if (!chrome) return [];
46
+ const os = await import("os");
47
+ const { cdpCapture } = await import("./cdp-V47GTLXG.js");
48
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "squint-interval-"));
49
+ try {
50
+ const result = await cdpCapture(chrome, url, tmp, [], 800, false, due);
51
+ return result.checkFailures;
52
+ } finally {
53
+ fs.rmSync(tmp, { recursive: true, force: true });
54
+ }
55
+ }
56
+
57
+ export {
58
+ loadChecks,
59
+ runIntervalSweep
60
+ };
package/dist/cli.js CHANGED
@@ -1,19 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- App
4
- } from "./chunk-6OFUO3UT.js";
5
- import "./chunk-LNJ7O5O4.js";
6
2
  import {
7
3
  connectDaemon,
8
4
  socketPath,
9
5
  startDaemon
10
- } from "./chunk-ORJPNSTE.js";
11
- import "./chunk-7PKIVVRB.js";
6
+ } from "./chunk-JGBWTFU6.js";
7
+ import {
8
+ App
9
+ } from "./chunk-W72LUSYS.js";
10
+ import "./chunk-AM7WBP3P.js";
11
+ import "./chunk-KNPLVGZB.js";
12
+ import "./chunk-LNJ7O5O4.js";
13
+ import "./chunk-GE6UL6C7.js";
12
14
  import "./chunk-UBADCBPT.js";
13
15
  import "./chunk-IEQGTVWN.js";
14
16
  import "./chunk-AUJJGMZG.js";
15
17
  import "./chunk-YHRAOBI2.js";
16
- import "./chunk-KNPLVGZB.js";
17
18
  import "./chunk-GDGKEMBS.js";
18
19
  import {
19
20
  runAgent
@@ -23,14 +24,14 @@ import {
23
24
  composePrompt
24
25
  } from "./chunk-WAJXATCO.js";
25
26
  import "./chunk-62JNF5M2.js";
26
- import "./chunk-UMDV4ITQ.js";
27
27
  import "./chunk-7CAGWFAQ.js";
28
- import "./chunk-N7YEWQGZ.js";
28
+ import "./chunk-S7LDOWMS.js";
29
29
  import "./chunk-PZV2FJE5.js";
30
30
  import {
31
31
  detectEngines,
32
32
  getEngine
33
33
  } from "./chunk-DABIMOMV.js";
34
+ import "./chunk-ZNV63ZIK.js";
34
35
 
35
36
  // src/cli.tsx
36
37
  import { Command } from "commander";
@@ -187,18 +188,18 @@ function registerDaemon(program2) {
187
188
  process.on("SIGTERM", stop);
188
189
  });
189
190
  program2.command("mcp").description("serve the gates as MCP tools over stdio (check, shot, flows, context)").action(async () => {
190
- const { runMcpServer } = await import("./server-N2PGAV2C.js");
191
+ const { runMcpServer } = await import("./server-QSU4H564.js");
191
192
  runMcpServer(process.cwd());
192
193
  });
193
194
  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
195
  const cwd = process.cwd();
195
196
  if (!opts.plain) {
196
197
  try {
197
- const { RemoteSession } = await import("./remote-TOZNJWGE.js");
198
+ const { RemoteSession } = await import("./remote-TDNUUOUN.js");
198
199
  const remote = await RemoteSession.connect(cwd);
199
200
  const config = loadConfig(defaultPaths(cwd));
200
201
  const { render: render2 } = await import("ink");
201
- const { App: App2 } = await import("./App-OHKGQTDL.js");
202
+ const { App: App2 } = await import("./App-W2U3EUM6.js");
202
203
  const React = await import("react");
203
204
  render2(
204
205
  React.createElement(App2, {
@@ -315,7 +316,7 @@ function registerEnv(program2) {
315
316
  }
316
317
  }
317
318
  const { findChrome } = await import("./chrome-CAN7TVH6.js");
318
- const { hasWebSocket } = await import("./cdp-IGPGFMC6.js");
319
+ const { hasWebSocket } = await import("./cdp-V47GTLXG.js");
319
320
  const chrome = findChrome();
320
321
  console.log(
321
322
  chrome ? `${pc3.green("\u2713")} Chrome ${pc3.dim(chrome)}` : `${pc3.yellow("\u25CB")} Chrome ${pc3.dim("\u2014 screenshots and runtime probing disabled")}`
@@ -551,7 +552,7 @@ function registerQuality(program2) {
551
552
  report.gates = gateResults.map((r) => ({ id: r.gate.id, ok: r.ok, durationMs: r.durationMs, outputTail: r.ok ? void 0 : r.outputTail }));
552
553
  if (gateResults.some((r) => !r.ok)) failed = true;
553
554
  if (opts.url) {
554
- const { captureViewports, runtimeSummary, previewDir } = await import("./preview-4OF6JB5B.js");
555
+ const { captureViewports, runtimeSummary, previewDir } = await import("./preview-NPLDCOP6.js");
555
556
  const capture = await captureViewports(cwd, opts.url);
556
557
  if (!capture) {
557
558
  console.log(pc5.red("\u2717 audit: capture failed (Chrome missing or URL unreachable)"));
@@ -583,14 +584,14 @@ function registerQuality(program2) {
583
584
  console.log(`${pc5.green("\u2713")} audit ${pc5.dim(`(${advisories.a11y.length} a11y, ${advisories.slop.length} slop, ${advisories.jank.length} jank advisories)`)}`);
584
585
  }
585
586
  }
586
- const { loadFlows } = await import("./flows-OY4BSCKD.js");
587
+ const { loadFlows } = await import("./flows-SYDNB5IW.js");
587
588
  const flows = loadFlows(cwd);
588
589
  if (flows.length > 0) {
589
590
  const { findChrome } = await import("./chrome-CAN7TVH6.js");
590
591
  const chrome = findChrome();
591
592
  const flowReport = [];
592
593
  if (chrome) {
593
- const { runFlow } = await import("./cdp-IGPGFMC6.js");
594
+ const { runFlow } = await import("./cdp-V47GTLXG.js");
594
595
  for (const flow of flows) {
595
596
  const result = await runFlow(chrome, opts.url, flow, previewDir(cwd));
596
597
  flowReport.push({ name: flow.name, ok: result.ok, detail: result.ok ? void 0 : `step ${result.failedStep}: ${result.detail}` });
@@ -604,7 +605,7 @@ function registerQuality(program2) {
604
605
  report.ok = !failed;
605
606
  report.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
606
607
  try {
607
- const { writeReceipt } = await import("./receipts-EFFO3NYZ.js");
608
+ const { writeReceipt } = await import("./receipts-MCEUXL6I.js");
608
609
  console.log(pc5.dim(`receipt \u2192 ${writeReceipt(cwd, report)}`));
609
610
  } catch {
610
611
  }
@@ -616,7 +617,7 @@ function registerQuality(program2) {
616
617
  if (failed) process.exitCode = 1;
617
618
  });
618
619
  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-4OF6JB5B.js");
620
+ const { captureViewports } = await import("./preview-NPLDCOP6.js");
620
621
  const result = await captureViewports(process.cwd(), url);
621
622
  if (!result) {
622
623
  console.error(pc5.red("\u2717 no Chrome/Chromium found"));
@@ -709,7 +710,7 @@ function registerRun(program2) {
709
710
  import pc7 from "picocolors";
710
711
  function registerScaffold(program2) {
711
712
  program2.command("init").description("Scaffold a new Vite + React + TS + Tailwind app with token-first CSS").argument("[dir]", "target directory", ".").option("--force", "write into a non-empty directory").option("--no-install", "skip npm install").action(async (dir, options) => {
712
- const { installDependencies, writeTemplate } = await import("./init-YB2IMA4N.js");
713
+ const { installDependencies, writeTemplate } = await import("./init-FASBBKC6.js");
713
714
  let result;
714
715
  try {
715
716
  result = writeTemplate(dir, { force: options.force });
@@ -803,7 +804,7 @@ function registerTui(program2) {
803
804
  }
804
805
 
805
806
  // src/cli.tsx
806
- var VERSION = true ? "0.7.0" : "0.0.0-dev";
807
+ var VERSION = true ? "0.7.2" : "0.0.0-dev";
807
808
  var program = new Command();
808
809
  program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
809
810
  registerRun(program);
@@ -4,7 +4,7 @@ import {
4
4
  parseFlow,
5
5
  stepExpression,
6
6
  suggestFlows
7
- } from "./chunk-3XO3GF5X.js";
7
+ } from "./chunk-CGOFK6EM.js";
8
8
  export {
9
9
  loadFlows,
10
10
  parseFlow,
@@ -145,6 +145,20 @@ dist/
145
145
  goto /
146
146
  expect Ready
147
147
  shot home
148
+ `,
149
+ ".squint/checks/root-renders.js": `// The starter check: replayed against the live page after every turn.
150
+ // Contract: this file evaluates IN THE PAGE to an array of failure
151
+ // strings \u2014 empty means pass. First-line pragmas move a check to full
152
+ // audits (// squint-trigger: audit) or the daemon's clock (interval:300).
153
+ (() => {
154
+ const root = document.querySelector('#root');
155
+ if (!root) return ['#root is missing from the page'];
156
+ if (root.children.length === 0) return ['#root rendered empty \u2014 the app did not mount'];
157
+ return [];
158
+ })()
159
+ `,
160
+ ".squint/rules.md": `Build from the design tokens in src/index.css \u2014 never hardcode a color a token covers.
161
+ Keep every interactive element reachable by keyboard with a visible focus state.
148
162
  `
149
163
  };
150
164
  }
@@ -11,11 +11,12 @@ import {
11
11
  probeRuntime,
12
12
  routeShotName,
13
13
  runtimeSummary
14
- } from "./chunk-UMDV4ITQ.js";
14
+ } from "./chunk-GE6UL6C7.js";
15
15
  import "./chunk-7CAGWFAQ.js";
16
- import "./chunk-N7YEWQGZ.js";
16
+ import "./chunk-S7LDOWMS.js";
17
17
  import "./chunk-PZV2FJE5.js";
18
18
  import "./chunk-DABIMOMV.js";
19
+ import "./chunk-ZNV63ZIK.js";
19
20
  export {
20
21
  VIEWPORTS,
21
22
  buildReviewPrompt,
@@ -31,7 +31,7 @@ function buildReceipt(cwd, report) {
31
31
  }
32
32
  }
33
33
  const unsigned = {
34
- version: true ? "0.7.0" : "0.0.0-dev",
34
+ version: true ? "0.7.2" : "0.0.0-dev",
35
35
  node: process.version,
36
36
  gitHead,
37
37
  report,
@@ -2,23 +2,24 @@
2
2
  import {
3
3
  connectDaemon,
4
4
  socketPath
5
- } from "./chunk-ORJPNSTE.js";
6
- import "./chunk-7PKIVVRB.js";
5
+ } from "./chunk-JGBWTFU6.js";
6
+ import "./chunk-AM7WBP3P.js";
7
+ import "./chunk-KNPLVGZB.js";
8
+ import "./chunk-GE6UL6C7.js";
7
9
  import "./chunk-UBADCBPT.js";
8
10
  import "./chunk-IEQGTVWN.js";
9
11
  import "./chunk-AUJJGMZG.js";
10
12
  import "./chunk-YHRAOBI2.js";
11
- import "./chunk-KNPLVGZB.js";
12
13
  import "./chunk-GDGKEMBS.js";
13
14
  import "./chunk-DOWWBLX2.js";
14
15
  import "./chunk-K5QJMSJH.js";
15
16
  import "./chunk-WAJXATCO.js";
16
17
  import "./chunk-62JNF5M2.js";
17
- import "./chunk-UMDV4ITQ.js";
18
18
  import "./chunk-7CAGWFAQ.js";
19
- import "./chunk-N7YEWQGZ.js";
19
+ import "./chunk-S7LDOWMS.js";
20
20
  import "./chunk-PZV2FJE5.js";
21
21
  import "./chunk-DABIMOMV.js";
22
+ import "./chunk-ZNV63ZIK.js";
22
23
 
23
24
  // src/daemon/remote.ts
24
25
  var noteId = 0;
@@ -3,6 +3,11 @@ import {
3
3
  contextReport,
4
4
  formatContextReport
5
5
  } from "./chunk-LESZB7DT.js";
6
+ import {
7
+ captureViewports,
8
+ previewDir,
9
+ runtimeSummary
10
+ } from "./chunk-GE6UL6C7.js";
6
11
  import "./chunk-UBADCBPT.js";
7
12
  import "./chunk-IEQGTVWN.js";
8
13
  import "./chunk-WAJXATCO.js";
@@ -10,20 +15,16 @@ import {
10
15
  detectGates,
11
16
  runGates
12
17
  } from "./chunk-62JNF5M2.js";
18
+ import "./chunk-7CAGWFAQ.js";
13
19
  import {
14
20
  loadFlows
15
- } from "./chunk-3XO3GF5X.js";
16
- import {
17
- captureViewports,
18
- previewDir,
19
- runtimeSummary
20
- } from "./chunk-UMDV4ITQ.js";
21
- import "./chunk-7CAGWFAQ.js";
22
- import "./chunk-N7YEWQGZ.js";
21
+ } from "./chunk-CGOFK6EM.js";
22
+ import "./chunk-S7LDOWMS.js";
23
23
  import {
24
24
  findChrome
25
25
  } from "./chunk-PZV2FJE5.js";
26
26
  import "./chunk-DABIMOMV.js";
27
+ import "./chunk-ZNV63ZIK.js";
27
28
 
28
29
  // src/mcp/server.ts
29
30
  import readline from "readline";
@@ -75,7 +76,7 @@ ${list.join("\n")}`);
75
76
  if (flows.length === 0) return "no flows declared \u2014 add .squint/flows/<name>.flow";
76
77
  const chrome = findChrome();
77
78
  if (!chrome) return "no Chrome/Chromium found";
78
- const { runFlow } = await import("./cdp-IGPGFMC6.js");
79
+ const { runFlow } = await import("./cdp-V47GTLXG.js");
79
80
  const lines = [];
80
81
  for (const flow of flows) {
81
82
  const result = await runFlow(chrome, url, flow, previewDir(cwd));
@@ -86,6 +87,41 @@ ${list.join("\n")}`);
86
87
  return lines.join("\n");
87
88
  }
88
89
  },
90
+ {
91
+ name: "squint_flow_suggest",
92
+ description: "Draft a smoke flow per declared route (.squint/routes) from the live page's own headings \u2014 goto/expect/shot files in .squint/flows/, existing flows untouched.",
93
+ inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
94
+ async run(args, cwd) {
95
+ const chrome = findChrome();
96
+ if (!chrome) return "no Chrome/Chromium found";
97
+ const { suggestFlows } = await import("./flows-SYDNB5IW.js");
98
+ const { created, skipped } = await suggestFlows(cwd, String(args.url ?? ""), chrome);
99
+ return [
100
+ created.length > 0 ? `drafted: ${created.join(", ")}` : "nothing drafted",
101
+ skipped.length > 0 ? `kept existing: ${skipped.join(", ")}` : ""
102
+ ].filter(Boolean).join("\n");
103
+ }
104
+ },
105
+ {
106
+ name: "squint_receipt_verify",
107
+ description: "Verify a .squint/receipts/*.json verification receipt: recompute its digest and report whether the report or screenshots were edited after sealing.",
108
+ inputSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] },
109
+ async run(args, cwd) {
110
+ const fs = await import("fs");
111
+ const path = await import("path");
112
+ const { verifyReceipt } = await import("./receipts-MCEUXL6I.js");
113
+ const file = path.isAbsolute(String(args.path)) ? String(args.path) : path.join(cwd, String(args.path));
114
+ let receipt;
115
+ try {
116
+ receipt = JSON.parse(fs.readFileSync(file, "utf8"));
117
+ } catch (error) {
118
+ return `unreadable receipt: ${error instanceof Error ? error.message : String(error)}`;
119
+ }
120
+ const intact = verifyReceipt(receipt);
121
+ const ok = receipt.report?.ok;
122
+ return intact ? `digest intact \u2014 this receipt is what squint sealed (run ok: ${ok}, git ${receipt.gitHead ?? "n/a"}, squint ${receipt.version})` : "DIGEST MISMATCH \u2014 this receipt was edited after sealing; do not trust it";
123
+ }
124
+ },
89
125
  {
90
126
  name: "squint_context",
91
127
  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).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aayambansal/squint",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
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",