@aayambansal/squint 0.4.5 → 0.4.6

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
@@ -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
+ - **Persistent checks**: assertions the engine verifies once persist as
178
+ `.squint/checks/*.js` and replay against the live page every turn — one-off
179
+ verifications compound into repo-versioned regression checks.
177
180
  - **Visual approval**: engines ask before contested changes — the request renders
178
181
  with its screenshot, `/yes` / `/no` answer it, the ledger remembers.
179
182
  - **The design ledger**: `/decide` (plus chosen variants, rollbacks, accepted
@@ -4,7 +4,7 @@ import {
4
4
  hasWebSocket,
5
5
  pixelDiffPct,
6
6
  runFlow
7
- } from "./chunk-RQHOE5MV.js";
7
+ } from "./chunk-X2RZJT4L.js";
8
8
  export {
9
9
  cdpCapture,
10
10
  hasWebSocket,
@@ -159,7 +159,11 @@ If the task appears to require changing them, stop and explain instead.`
159
159
  parts.push(
160
160
  `## Requesting visual approval
161
161
 
162
- For a visual decision you should not make alone (a redesign direction, removing something deliberate, reversing a design decision on record): write .squint/approval-request.json containing {"summary": "<one line>", "screenshot": "<path, optional>"} and end your turn immediately without making the change. The user's verdict arrives as the next message.`
162
+ For a visual decision you should not make alone (a redesign direction, removing something deliberate, reversing a design decision on record): write .squint/approval-request.json containing {"summary": "<one line>", "screenshot": "<path, optional>"} and end your turn immediately without making the change. The user's verdict arrives as the next message.
163
+
164
+ ## Persistent checks
165
+
166
+ When you verify something about the page that should stay true (an element exists, a state renders, a metric holds), persist it as .squint/checks/<name>.js \u2014 plain JS that evaluates IN THE PAGE to an array of failure strings (empty array = pass). squint replays every check against the live page after each turn.`
163
167
  );
164
168
  const matched = matchSkills(loadSkills(cwd), ask);
165
169
  for (const skill of matched) {
@@ -9,12 +9,39 @@ import {
9
9
  import {
10
10
  cdpCapture,
11
11
  hasWebSocket
12
- } from "./chunk-RQHOE5MV.js";
12
+ } from "./chunk-X2RZJT4L.js";
13
13
 
14
14
  // src/preview/preview.ts
15
- import fs from "fs";
15
+ import fs2 from "fs";
16
16
  import os from "os";
17
+ import path2 from "path";
18
+
19
+ // src/preview/checks.ts
20
+ import fs from "fs";
17
21
  import path from "path";
22
+ var MAX_CHECKS = 20;
23
+ var MAX_BYTES = 1e4;
24
+ function loadChecks(cwd) {
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
+ checks.push({ name: entry.replace(/\.js$/, ""), source });
38
+ } catch {
39
+ }
40
+ }
41
+ return checks;
42
+ }
43
+
44
+ // src/preview/preview.ts
18
45
  var VIEWPORTS = [
19
46
  { name: "mobile", width: 390, height: 844 },
20
47
  { name: "tablet", width: 768, height: 1024 },
@@ -23,7 +50,7 @@ var VIEWPORTS = [
23
50
  function loadRoutes(cwd) {
24
51
  let lines = [];
25
52
  try {
26
- lines = fs.readFileSync(path.join(cwd, ".squint", "routes"), "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
53
+ lines = fs2.readFileSync(path2.join(cwd, ".squint", "routes"), "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
27
54
  } catch {
28
55
  }
29
56
  const routes = ["/", ...lines.filter((l) => l !== "/")];
@@ -34,8 +61,8 @@ function routeShotName(route) {
34
61
  return clean.length > 0 ? clean : "root";
35
62
  }
36
63
  function previewDir(cwd) {
37
- const dir = path.join(cwd, ".squint", "preview");
38
- fs.mkdirSync(dir, { recursive: true });
64
+ const dir = path2.join(cwd, ".squint", "preview");
65
+ fs2.mkdirSync(dir, { recursive: true });
39
66
  ensureSquintIgnore(cwd);
40
67
  return dir;
41
68
  }
@@ -47,7 +74,8 @@ async function captureViewports(cwd, url) {
47
74
  const base = url.replace(/\/+$/, "");
48
75
  if (hasWebSocket()) {
49
76
  try {
50
- const { report, shots: shots2, a11y, slop, narration, phantoms, viewTransitions, components } = await cdpCapture(chrome, url, dir, VIEWPORTS, 2500, true);
77
+ const checks = loadChecks(cwd);
78
+ const { report, shots: shots2, a11y, slop, narration, phantoms, viewTransitions, components, checkFailures, webmcp } = await cdpCapture(chrome, url, dir, VIEWPORTS, 2500, true, checks);
51
79
  const errors2 = [];
52
80
  for (const route of routes.slice(1)) {
53
81
  try {
@@ -62,14 +90,14 @@ async function captureViewports(cwd, url) {
62
90
  errors2.push(`${route}: capture failed`);
63
91
  }
64
92
  }
65
- return { shots: shots2, errors: errors2, runtime: report, a11y, slop, narration, phantoms, viewTransitions, components };
93
+ return { shots: shots2, errors: errors2, runtime: report, a11y, slop, narration, phantoms, viewTransitions, components, checkFailures, webmcp };
66
94
  } catch {
67
95
  }
68
96
  }
69
97
  const shots = [];
70
98
  const errors = [];
71
99
  for (const viewport of VIEWPORTS) {
72
- const outPath = path.join(dir, `${viewport.name}.png`);
100
+ const outPath = path2.join(dir, `${viewport.name}.png`);
73
101
  const result = await screenshot(chrome, url, outPath, {
74
102
  width: viewport.width,
75
103
  height: viewport.height
@@ -118,14 +146,16 @@ async function probeRuntime(url, cwd) {
118
146
  if (!chrome || !hasWebSocket()) return null;
119
147
  try {
120
148
  const dir = cwd ? previewDir(cwd) : os.tmpdir();
121
- const { report, shots, perf } = await cdpCapture(
149
+ const { report, shots, perf, checkFailures } = await cdpCapture(
122
150
  chrome,
123
151
  url,
124
152
  dir,
125
153
  cwd ? [{ name: "pulse", width: 1280, height: 800 }] : [],
126
- 1500
154
+ 1500,
155
+ false,
156
+ cwd ? loadChecks(cwd) : []
127
157
  );
128
- return { report, pulsePath: shots[0]?.path, perf };
158
+ return { report, pulsePath: shots[0]?.path, perf, checkFailures };
129
159
  } catch {
130
160
  return null;
131
161
  }
@@ -133,7 +163,7 @@ async function probeRuntime(url, cwd) {
133
163
  async function comparePulse(previous, current) {
134
164
  const chrome = findChrome();
135
165
  if (!chrome || !hasWebSocket()) return null;
136
- const { pixelDiffPct } = await import("./cdp-Q4H6ZHPT.js");
166
+ const { pixelDiffPct } = await import("./cdp-OYRHOPVP.js");
137
167
  return pixelDiffPct(chrome, previous, current);
138
168
  }
139
169
  function buildRuntimeFixPrompt(report) {
@@ -161,6 +191,16 @@ ${narration.join("\n")}
161
191
 
162
192
  Judge this narration as an experience: does the reading order make sense? do names actually describe their targets? is anything announced as "(no accessible name)"? Fix real incoherence \u2014 this is how non-visual users meet the page.`;
163
193
  }
194
+ function webmcpSection(webmcp) {
195
+ if (!webmcp || webmcp.length === 0) return "";
196
+ return `
197
+
198
+ ## Page-declared WebMCP tools
199
+
200
+ ${webmcp.join("\n")}
201
+
202
+ The page registers these for agents via navigator.modelContext \u2014 keep them working, and prefer extending them over inventing parallel affordances.`;
203
+ }
164
204
  function componentSection(components) {
165
205
  if (!components || components.length === 0) return "";
166
206
  return `
@@ -201,13 +241,13 @@ ${findings.join("\n")}
201
241
 
202
242
  These patterns make the page read as template output. Rework them within the committed design direction \u2014 this is style debt, not a defect list.`;
203
243
  }
204
- function buildReviewPrompt(shots, extra, runtime, a11y, slop, narration, phantoms, viewTransitions, components) {
244
+ function buildReviewPrompt(shots, extra, runtime, a11y, slop, narration, phantoms, viewTransitions, components, webmcp) {
205
245
  const list = shots.map((s) => `- ${s.name}: ${s.path}`).join("\n");
206
246
  return `Screenshots of the running app were just captured:
207
247
 
208
248
  ${list}
209
249
 
210
- Read each screenshot and review the rendered UI against the design standards you were given. Check: visual hierarchy and spacing rhythm, typography, color and contrast, alignment, empty-looking or broken regions, and whether the mobile capture shows horizontal overflow or cramped layout. List the concrete issues you can SEE (not hypothetical ones), ranked by visual impact${extra ? `, with special attention to: ${extra}` : ""}. Then fix them and verify the app still builds.${runtimeSection(runtime)}${a11ySection(a11y)}${slopSection(slop)}${narrationSection(narration)}${phantomSection(phantoms)}${vtSection(viewTransitions)}${componentSection(components)}`;
250
+ Read each screenshot and review the rendered UI against the design standards you were given. Check: visual hierarchy and spacing rhythm, typography, color and contrast, alignment, empty-looking or broken regions, and whether the mobile capture shows horizontal overflow or cramped layout. List the concrete issues you can SEE (not hypothetical ones), ranked by visual impact${extra ? `, with special attention to: ${extra}` : ""}. Then fix them and verify the app still builds.${runtimeSection(runtime)}${a11ySection(a11y)}${slopSection(slop)}${narrationSection(narration)}${phantomSection(phantoms)}${vtSection(viewTransitions)}${componentSection(components)}${webmcpSection(webmcp)}`;
211
251
  }
212
252
 
213
253
  export {
@@ -188,6 +188,7 @@ async function runFlow(chromePath, baseUrl, flow, outDir) {
188
188
  const { targetId } = await connection.send("Target.createTarget", { url: "about:blank" });
189
189
  const { sessionId } = await connection.send("Target.attachToTarget", { targetId, flatten: true });
190
190
  await connection.send("Page.enable", {}, sessionId);
191
+ await connection.send("Page.addScriptToEvaluateOnNewDocument", { source: WEBMCP_SHIM }, sessionId).catch(() => null);
191
192
  await connection.send(
192
193
  "Emulation.setDeviceMetricsOverride",
193
194
  { width: 1280, height: 800, deviceScaleFactor: 1, mobile: false },
@@ -437,7 +438,20 @@ var FIBER_AUDIT = `(() => {
437
438
  }
438
439
  return out;
439
440
  })()`;
440
- async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, audit = false) {
441
+ var WEBMCP_SHIM = `(() => {
442
+ window.__squintWebMcp = [];
443
+ const record = (tools) => {
444
+ for (const t of tools || []) {
445
+ if (t && t.name) window.__squintWebMcp.push(t.name + (t.description ? ' \u2014 ' + t.description : ''));
446
+ }
447
+ };
448
+ const target = navigator.modelContext || (navigator.modelContext = {});
449
+ const provide = target.provideContext && target.provideContext.bind(target);
450
+ target.provideContext = (params) => { record(params && params.tools); return provide ? provide(params) : undefined; };
451
+ const register = target.registerTool && target.registerTool.bind(target);
452
+ target.registerTool = (tool) => { record([tool]); return register ? register(tool) : undefined; };
453
+ })()`;
454
+ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, audit = false, checks = []) {
441
455
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
442
456
  const report = { consoleErrors: [], pageErrors: [], failedRequests: [] };
443
457
  const shots = [];
@@ -448,6 +462,8 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
448
462
  let phantoms = [];
449
463
  let viewTransitions = [];
450
464
  let components = [];
465
+ const checkFailures = [];
466
+ let webmcp = [];
451
467
  const requests = /* @__PURE__ */ new Map();
452
468
  let connection = null;
453
469
  try {
@@ -486,6 +502,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
486
502
  await connection.send("Runtime.enable", {}, sessionId);
487
503
  await connection.send("Network.enable", {}, sessionId);
488
504
  await connection.send("Page.enable", {}, sessionId);
505
+ await connection.send("Page.addScriptToEvaluateOnNewDocument", { source: WEBMCP_SHIM }, sessionId).catch(() => null);
489
506
  await connection.send("Page.navigate", { url }, sessionId);
490
507
  const deadline = Date.now() + 12e3;
491
508
  while (!loaded && Date.now() < deadline) {
@@ -501,6 +518,31 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
501
518
  if (result?.value && typeof result.value === "object") perf = result.value;
502
519
  } catch {
503
520
  }
521
+ try {
522
+ const { result } = await connection.send(
523
+ "Runtime.evaluate",
524
+ { expression: "window.__squintWebMcp || []", returnByValue: true },
525
+ sessionId
526
+ );
527
+ if (Array.isArray(result?.value)) webmcp = result.value.map(String).slice(0, 12);
528
+ } catch {
529
+ }
530
+ for (const check of checks) {
531
+ try {
532
+ const { result, exceptionDetails } = await connection.send(
533
+ "Runtime.evaluate",
534
+ { expression: check.source, returnByValue: true, timeout: 2e3 },
535
+ sessionId
536
+ );
537
+ if (exceptionDetails) {
538
+ checkFailures.push(`${check.name}: threw ${exceptionDetails.exception?.description?.split("\n")[0] ?? exceptionDetails.text ?? "an error"}`);
539
+ } else if (Array.isArray(result?.value)) {
540
+ for (const finding of result.value.slice(0, 5)) checkFailures.push(`${check.name}: ${String(finding)}`);
541
+ }
542
+ } catch {
543
+ }
544
+ if (checkFailures.length >= 15) break;
545
+ }
504
546
  if (audit) {
505
547
  try {
506
548
  const { result } = await connection.send(
@@ -611,7 +653,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
611
653
  child.kill("SIGKILL");
612
654
  setTimeout(() => fs.rmSync(profileDir, { recursive: true, force: true }), 500).unref?.();
613
655
  }
614
- return { report, shots, a11y, slop, perf, narration, phantoms, viewTransitions, components };
656
+ return { report, shots, a11y, slop, perf, narration, phantoms, viewTransitions, components, checkFailures, webmcp };
615
657
  }
616
658
 
617
659
  export {
package/dist/cli.js CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  comparePulse,
43
43
  probeRuntime,
44
44
  runtimeSummary
45
- } from "./chunk-CV5WVKHU.js";
45
+ } from "./chunk-D7Q3M6PA.js";
46
46
  import {
47
47
  clearState,
48
48
  loadState,
@@ -58,11 +58,11 @@ import {
58
58
  detectEngines,
59
59
  getEngine
60
60
  } from "./chunk-KVYGPLWW.js";
61
- import "./chunk-RQHOE5MV.js";
61
+ import "./chunk-X2RZJT4L.js";
62
62
  import {
63
63
  appendDecision,
64
64
  enrich
65
- } from "./chunk-ZLEP2TWF.js";
65
+ } from "./chunk-ARDV4XH6.js";
66
66
  import "./chunk-K5QJMSJH.js";
67
67
  import {
68
68
  composePrompt
@@ -240,7 +240,7 @@ function registerEnv(program2) {
240
240
  }
241
241
  }
242
242
  const { findChrome: findChrome2 } = await import("./chrome-SBV3H77F.js");
243
- const { hasWebSocket } = await import("./cdp-Q4H6ZHPT.js");
243
+ const { hasWebSocket } = await import("./cdp-OYRHOPVP.js");
244
244
  const chrome = findChrome2();
245
245
  console.log(
246
246
  chrome ? `${pc2.green("\u2713")} Chrome ${pc2.dim(chrome)}` : `${pc2.yellow("\u25CB")} Chrome ${pc2.dim("\u2014 screenshots and runtime probing disabled")}`
@@ -264,7 +264,7 @@ import pc3 from "picocolors";
264
264
  function registerProject(program2) {
265
265
  const skillsCommand = program2.command("skills").description("Project knowledge injected into asks (.squint/rules.md + .squint/skills/)");
266
266
  skillsCommand.command("list").description("Show always-on rules and trigger-matched skills").action(async () => {
267
- const { loadRules, loadSkills } = await import("./skills-6NZP67QT.js");
267
+ const { loadRules, loadSkills } = await import("./skills-POB4ZZY5.js");
268
268
  const cwd = process.cwd();
269
269
  const rules = loadRules(cwd);
270
270
  console.log(
@@ -463,7 +463,7 @@ function registerQuality(program2) {
463
463
  if (results.some((r) => !r.ok)) process.exitCode = 1;
464
464
  });
465
465
  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) => {
466
- const { captureViewports: captureViewports2 } = await import("./preview-ZUCVWGZG.js");
466
+ const { captureViewports: captureViewports2 } = await import("./preview-XREFPMHX.js");
467
467
  const result = await captureViewports2(process.cwd(), url);
468
468
  if (!result) {
469
469
  console.error(pc4.red("\u2717 no Chrome/Chromium found"));
@@ -827,7 +827,7 @@ ${question}`,
827
827
  return;
828
828
  }
829
829
  await this.runTurn(
830
- buildReviewPrompt(result.shots, void 0, result.runtime, result.a11y, result.slop, result.narration, result.phantoms, result.viewTransitions, result.components),
830
+ buildReviewPrompt(result.shots, void 0, result.runtime, result.a11y, result.slop, result.narration, result.phantoms, result.viewTransitions, result.components, result.webmcp),
831
831
  `\u{1F441} polish round ${round}/${rounds}`
832
832
  );
833
833
  }
@@ -1175,6 +1175,21 @@ ${errors.slice(-5).join("\n")}`);
1175
1175
  this.clearProblems("dev");
1176
1176
  if (this.opts.autoProbe !== false && this.state.devUrl) {
1177
1177
  const probe = await probeRuntime(this.state.devUrl, this.opts.cwd);
1178
+ if (probe?.checkFailures && probe.checkFailures.length > 0) {
1179
+ this.addProblem(
1180
+ "check",
1181
+ `${probe.checkFailures.length} persistent check failure(s)`,
1182
+ `Repo checks in .squint/checks/ failed against the live page. Each line is <check>: <failure>:
1183
+
1184
+ ${probe.checkFailures.join("\n")}
1185
+
1186
+ Fix the page (or, only if the assertion itself is genuinely outdated, update that check file and say so).`
1187
+ );
1188
+ this.push("error", `checks: ${probe.checkFailures.length} failure(s)
1189
+ ${probe.checkFailures.slice(0, 5).join("\n")}`);
1190
+ } else {
1191
+ this.clearProblems("check");
1192
+ }
1178
1193
  const summary = probe ? runtimeSummary(probe.report) : null;
1179
1194
  if (probe && summary) {
1180
1195
  this.addProblem("runtime", summary, buildRuntimeFixPrompt(probe.report));
@@ -1192,7 +1207,7 @@ ${errors.slice(-5).join("\n")}`);
1192
1207
  const captureResult = await this.capture();
1193
1208
  if (captureResult) {
1194
1209
  await this.runTurn(
1195
- buildReviewPrompt(captureResult.shots, void 0, captureResult.runtime, captureResult.a11y, captureResult.slop, captureResult.narration, captureResult.phantoms, captureResult.viewTransitions, captureResult.components),
1210
+ buildReviewPrompt(captureResult.shots, void 0, captureResult.runtime, captureResult.a11y, captureResult.slop, captureResult.narration, captureResult.phantoms, captureResult.viewTransitions, captureResult.components, captureResult.webmcp),
1196
1211
  "\u{1F441} auto-review rendered UI"
1197
1212
  );
1198
1213
  }
@@ -1480,7 +1495,7 @@ They are objective defects, not style preferences.`
1480
1495
  break;
1481
1496
  }
1482
1497
  case "context": {
1483
- import("./contextDoctor-U3YTDFVG.js").then(({ contextReport, formatContextReport }) => {
1498
+ import("./contextDoctor-A26C2ZDN.js").then(({ contextReport, formatContextReport }) => {
1484
1499
  this.push("status", formatContextReport(contextReport(this.execCwd())));
1485
1500
  }).catch((error) => {
1486
1501
  this.push("status", `context report failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -1615,8 +1630,8 @@ They are objective defects, not style preferences.`
1615
1630
  this.push("error", "no Chrome/Chromium found for flows");
1616
1631
  return;
1617
1632
  }
1618
- const { runFlow } = await import("./cdp-Q4H6ZHPT.js");
1619
- const { previewDir } = await import("./preview-ZUCVWGZG.js");
1633
+ const { runFlow } = await import("./cdp-OYRHOPVP.js");
1634
+ const { previewDir } = await import("./preview-XREFPMHX.js");
1620
1635
  this.push("status", `replaying ${flows.length} flow(s)\u2026`);
1621
1636
  this.notify({ running: true, runStartedAt: Date.now() });
1622
1637
  const failures = [];
@@ -1754,7 +1769,7 @@ They are objective defects, not style preferences.`
1754
1769
  const result = await this.capture();
1755
1770
  if (result) {
1756
1771
  await this.runTurn(
1757
- buildReviewPrompt(result.shots, arg || void 0, result.runtime, result.a11y, result.slop, result.narration, result.phantoms, result.viewTransitions, result.components),
1772
+ buildReviewPrompt(result.shots, arg || void 0, result.runtime, result.a11y, result.slop, result.narration, result.phantoms, result.viewTransitions, result.components, result.webmcp),
1758
1773
  `\u{1F441} review rendered UI${arg ? ` \xB7 ${arg}` : ""}`
1759
1774
  );
1760
1775
  }
@@ -2559,7 +2574,7 @@ function registerTui(program2) {
2559
2574
  }
2560
2575
 
2561
2576
  // src/cli.tsx
2562
- var VERSION = true ? "0.4.5" : "0.0.0-dev";
2577
+ var VERSION = true ? "0.4.6" : "0.0.0-dev";
2563
2578
  var program = new Command();
2564
2579
  program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
2565
2580
  registerRun(program);
@@ -7,7 +7,7 @@ import {
7
7
  loadLocks,
8
8
  loadRules,
9
9
  loadSkills
10
- } from "./chunk-ZLEP2TWF.js";
10
+ } from "./chunk-ARDV4XH6.js";
11
11
  import {
12
12
  loadBrief
13
13
  } from "./chunk-WAJXATCO.js";
@@ -10,11 +10,11 @@ import {
10
10
  probeRuntime,
11
11
  routeShotName,
12
12
  runtimeSummary
13
- } from "./chunk-CV5WVKHU.js";
13
+ } from "./chunk-D7Q3M6PA.js";
14
14
  import "./chunk-O2S6PAJE.js";
15
15
  import "./chunk-IMDRXXFU.js";
16
16
  import "./chunk-KVYGPLWW.js";
17
- import "./chunk-RQHOE5MV.js";
17
+ import "./chunk-X2RZJT4L.js";
18
18
  export {
19
19
  VIEWPORTS,
20
20
  buildReviewPrompt,
@@ -6,7 +6,7 @@ import {
6
6
  loadSkills,
7
7
  matchSkills,
8
8
  parseSkill
9
- } from "./chunk-ZLEP2TWF.js";
9
+ } from "./chunk-ARDV4XH6.js";
10
10
  export {
11
11
  enrich,
12
12
  loadLocks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aayambansal/squint",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
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",