@aayambansal/squint 0.4.5 → 0.4.7

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.
@@ -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) {
@@ -15,7 +15,7 @@ function sandboxExists(cwd) {
15
15
  }
16
16
  function openSandbox(cwd) {
17
17
  const dir = sandboxDir(cwd);
18
- void import("./state-QOS7WCZO.js").then(({ ensureSquintIgnore }) => ensureSquintIgnore(cwd)).catch(() => {
18
+ void import("./state-PLY7YAD2.js").then(({ ensureSquintIgnore }) => ensureSquintIgnore(cwd)).catch(() => {
19
19
  });
20
20
  if (sandboxExists(cwd)) return { dir, reused: true };
21
21
  fs.rmSync(dir, { recursive: true, force: true });
@@ -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,26 @@ 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 wrap = (target) => {
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
+ return target;
454
+ };
455
+ // The spec moved the API to document.modelContext (Chrome 150 drops
456
+ // the navigator location); shim both so either registration is seen.
457
+ wrap(document.modelContext || (document.modelContext = {}));
458
+ wrap(navigator.modelContext || (navigator.modelContext = {}));
459
+ })()`;
460
+ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, audit = false, checks = []) {
441
461
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
442
462
  const report = { consoleErrors: [], pageErrors: [], failedRequests: [] };
443
463
  const shots = [];
@@ -448,6 +468,8 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
448
468
  let phantoms = [];
449
469
  let viewTransitions = [];
450
470
  let components = [];
471
+ const checkFailures = [];
472
+ let webmcp = [];
451
473
  const requests = /* @__PURE__ */ new Map();
452
474
  let connection = null;
453
475
  try {
@@ -486,6 +508,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
486
508
  await connection.send("Runtime.enable", {}, sessionId);
487
509
  await connection.send("Network.enable", {}, sessionId);
488
510
  await connection.send("Page.enable", {}, sessionId);
511
+ await connection.send("Page.addScriptToEvaluateOnNewDocument", { source: WEBMCP_SHIM }, sessionId).catch(() => null);
489
512
  await connection.send("Page.navigate", { url }, sessionId);
490
513
  const deadline = Date.now() + 12e3;
491
514
  while (!loaded && Date.now() < deadline) {
@@ -501,6 +524,31 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
501
524
  if (result?.value && typeof result.value === "object") perf = result.value;
502
525
  } catch {
503
526
  }
527
+ try {
528
+ const { result } = await connection.send(
529
+ "Runtime.evaluate",
530
+ { expression: "window.__squintWebMcp || []", returnByValue: true },
531
+ sessionId
532
+ );
533
+ if (Array.isArray(result?.value)) webmcp = result.value.map(String).slice(0, 12);
534
+ } catch {
535
+ }
536
+ for (const check of checks) {
537
+ try {
538
+ const { result, exceptionDetails } = await connection.send(
539
+ "Runtime.evaluate",
540
+ { expression: check.source, returnByValue: true, timeout: 2e3 },
541
+ sessionId
542
+ );
543
+ if (exceptionDetails) {
544
+ checkFailures.push(`${check.name}: threw ${exceptionDetails.exception?.description?.split("\n")[0] ?? exceptionDetails.text ?? "an error"}`);
545
+ } else if (Array.isArray(result?.value)) {
546
+ for (const finding of result.value.slice(0, 5)) checkFailures.push(`${check.name}: ${String(finding)}`);
547
+ }
548
+ } catch {
549
+ }
550
+ if (checkFailures.length >= 15) break;
551
+ }
504
552
  if (audit) {
505
553
  try {
506
554
  const { result } = await connection.send(
@@ -611,7 +659,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
611
659
  child.kill("SIGKILL");
612
660
  setTimeout(() => fs.rmSync(profileDir, { recursive: true, force: true }), 500).unref?.();
613
661
  }
614
- return { report, shots, a11y, slop, perf, narration, phantoms, viewTransitions, components };
662
+ return { report, shots, a11y, slop, perf, narration, phantoms, viewTransitions, components, checkFailures, webmcp };
615
663
  }
616
664
 
617
665
  export {