@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.
- package/README.md +3 -0
- package/dist/{App-766KSGON.js → App-MC2XEM7K.js} +9 -9
- package/dist/{cdp-2VVQOHWM.js → cdp-WSWOSDEX.js} +1 -1
- package/dist/{chrome-SBV3H77F.js → chrome-EEXJCG6W.js} +2 -2
- package/dist/{chunk-QCHBDP46.js → chunk-2I3YHT6P.js} +3 -3
- package/dist/{flows-6LK7NGWS.js → chunk-4XSHHGA6.js} +2 -1
- package/dist/{chunk-VH7OOFQP.js → chunk-6NPUVPQD.js} +2 -2
- package/dist/{contextDoctor-A26C2ZDN.js → chunk-BKOGVFCO.js} +1 -0
- package/dist/{chunk-7RFQWOQV.js → chunk-CRDRWTON.js} +4 -4
- package/dist/{chunk-BGBG2MNY.js → chunk-DCFNX2CQ.js} +41 -2
- package/dist/{chunk-JZMJGLIZ.js → chunk-EB4OSUB7.js} +1 -1
- package/dist/{chunk-KVYGPLWW.js → chunk-GV2HAALP.js} +4 -0
- package/dist/{chunk-YGSF2TSO.js → chunk-J7ITMK6X.js} +1 -1
- package/dist/{chunk-IMDRXXFU.js → chunk-PYC53J7D.js} +1 -1
- package/dist/{chunk-YGPVP4YB.js → chunk-WASLLXEZ.js} +11 -11
- package/dist/{chunk-ATNTD3V5.js → chunk-YK5DEB7T.js} +1 -1
- package/dist/cli.js +99 -22
- package/dist/contextDoctor-3Y36YURE.js +11 -0
- package/dist/flows-MXJL45HW.js +11 -0
- package/dist/{preview-FBNVDQIV.js → preview-CDEMPHWV.js} +4 -4
- package/dist/{registry-MIJ6LSAY.js → registry-JBP7J3JB.js} +1 -1
- package/dist/{remote-OH2P4S2H.js → remote-C4BACCKX.js} +9 -9
- package/dist/{run-NDSNTVYP.js → run-GDDIYEBP.js} +2 -2
- package/dist/server-ENT2OFNL.js +155 -0
- package/dist/shots-6DRTXFDI.js +13 -0
- package/dist/{variants-3IEP7DFY.js → variants-VURXVIQO.js} +3 -3
- package/package.json +1 -1
- 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-
|
|
4
|
+
} from "./chunk-EB4OSUB7.js";
|
|
5
5
|
import "./chunk-43NQNIJY.js";
|
|
6
|
-
import "./chunk-
|
|
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-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
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-
|
|
16
|
+
import "./chunk-CRDRWTON.js";
|
|
17
17
|
import "./chunk-7CAGWFAQ.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-DCFNX2CQ.js";
|
|
19
|
+
import "./chunk-PYC53J7D.js";
|
|
20
|
+
import "./chunk-GV2HAALP.js";
|
|
21
21
|
export {
|
|
22
22
|
App
|
|
23
23
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
variantsRoot
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-J7ITMK6X.js";
|
|
5
5
|
import {
|
|
6
6
|
findChrome,
|
|
7
7
|
screenshot
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PYC53J7D.js";
|
|
9
9
|
import {
|
|
10
10
|
lineSplitter
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GV2HAALP.js";
|
|
12
12
|
|
|
13
13
|
// src/variants/shots.ts
|
|
14
14
|
import path2 from "path";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
findEngineBinary,
|
|
4
4
|
lineSplitter,
|
|
5
5
|
truncate
|
|
6
|
-
} from "./chunk-
|
|
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) => {
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
cdpCapture,
|
|
7
7
|
hasWebSocket
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DCFNX2CQ.js";
|
|
9
9
|
import {
|
|
10
10
|
findChrome,
|
|
11
11
|
screenshot
|
|
12
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
@@ -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 = [
|
|
@@ -23,16 +23,16 @@ import {
|
|
|
23
23
|
buildFixPrompt,
|
|
24
24
|
detectDevCommand,
|
|
25
25
|
screenshotVariants
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-2I3YHT6P.js";
|
|
27
27
|
import {
|
|
28
28
|
applyVariant,
|
|
29
29
|
cleanVariants,
|
|
30
30
|
listVariants,
|
|
31
31
|
runVariants
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-J7ITMK6X.js";
|
|
33
33
|
import {
|
|
34
34
|
runAgent
|
|
35
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
60
|
+
} from "./chunk-PYC53J7D.js";
|
|
61
61
|
import {
|
|
62
62
|
getEngine
|
|
63
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
1087
|
-
const { previewDir } = await import("./preview-
|
|
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 = [];
|
package/dist/cli.js
CHANGED
|
@@ -3,33 +3,33 @@ import {
|
|
|
3
3
|
connectDaemon,
|
|
4
4
|
socketPath,
|
|
5
5
|
startDaemon
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YK5DEB7T.js";
|
|
7
7
|
import {
|
|
8
8
|
App
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-EB4OSUB7.js";
|
|
10
10
|
import "./chunk-43NQNIJY.js";
|
|
11
|
-
import "./chunk-
|
|
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-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-2I3YHT6P.js";
|
|
16
|
+
import "./chunk-J7ITMK6X.js";
|
|
17
17
|
import {
|
|
18
18
|
runAgent
|
|
19
|
-
} from "./chunk-
|
|
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-
|
|
25
|
+
import "./chunk-CRDRWTON.js";
|
|
26
26
|
import "./chunk-7CAGWFAQ.js";
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-DCFNX2CQ.js";
|
|
28
|
+
import "./chunk-PYC53J7D.js";
|
|
29
29
|
import {
|
|
30
30
|
detectEngines,
|
|
31
31
|
getEngine
|
|
32
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
313
|
-
const { hasWebSocket } = await import("./cdp-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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);
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
probeRuntime,
|
|
12
12
|
routeShotName,
|
|
13
13
|
runtimeSummary
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CRDRWTON.js";
|
|
15
15
|
import "./chunk-7CAGWFAQ.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-DCFNX2CQ.js";
|
|
17
|
+
import "./chunk-PYC53J7D.js";
|
|
18
|
+
import "./chunk-GV2HAALP.js";
|
|
19
19
|
export {
|
|
20
20
|
VIEWPORTS,
|
|
21
21
|
buildReviewPrompt,
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
connectDaemon,
|
|
4
4
|
socketPath
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
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-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
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-
|
|
16
|
+
import "./chunk-CRDRWTON.js";
|
|
17
17
|
import "./chunk-7CAGWFAQ.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
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;
|
|
@@ -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-
|
|
13
|
-
import "./chunk-
|
|
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-
|
|
16
|
+
import "./chunk-GV2HAALP.js";
|
|
17
17
|
export {
|
|
18
18
|
MAX_VARIANTS,
|
|
19
19
|
applyVariant,
|
package/package.json
CHANGED
package/dist/shots-OKWOYF7F.js
DELETED
|
@@ -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
|
-
};
|