@aayambansal/squint 0.4.8 → 0.5.1
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-KTNAMHY6.js +24 -0
- package/dist/{cdp-2VVQOHWM.js → cdp-D2F4P7UQ.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/{chunk-JZMJGLIZ.js → chunk-2NYNTVMX.js} +2 -2
- package/dist/{chunk-BGBG2MNY.js → chunk-3YCG234A.js} +80 -2
- package/dist/{flows-6LK7NGWS.js → chunk-4XSHHGA6.js} +2 -1
- package/dist/{chunk-VH7OOFQP.js → chunk-6NPUVPQD.js} +2 -2
- package/dist/{chunk-ATNTD3V5.js → chunk-AMA3F2Y2.js} +1 -1
- package/dist/{chunk-7RFQWOQV.js → chunk-ERF2SPUW.js} +4 -4
- package/dist/{chunk-YGPVP4YB.js → chunk-G5NS2IFQ.js} +68 -17
- package/dist/{chunk-KVYGPLWW.js → chunk-GV2HAALP.js} +4 -0
- package/dist/chunk-IEQGTVWN.js +52 -0
- package/dist/{chunk-YGSF2TSO.js → chunk-J7ITMK6X.js} +1 -1
- package/dist/{contextDoctor-A26C2ZDN.js → chunk-LESZB7DT.js} +6 -3
- package/dist/{chunk-IMDRXXFU.js → chunk-PYC53J7D.js} +1 -1
- package/dist/{chunk-ARDV4XH6.js → chunk-UBADCBPT.js} +14 -59
- package/dist/{chunk-43NQNIJY.js → chunk-VJ2QKHYK.js} +2 -0
- package/dist/cli.js +105 -27
- package/dist/{commands-BY44HDQ6.js → commands-WC7V6EAE.js} +1 -1
- package/dist/contextDoctor-LHKGSIEM.js +12 -0
- package/dist/designLog-S67J7SO2.js +11 -0
- package/dist/flows-MXJL45HW.js +11 -0
- package/dist/{preview-FBNVDQIV.js → preview-JN5PTWLX.js} +4 -4
- package/dist/{registry-MIJ6LSAY.js → registry-JBP7J3JB.js} +1 -1
- package/dist/{remote-OH2P4S2H.js → remote-UOV4ZXWU.js} +11 -10
- package/dist/{run-NDSNTVYP.js → run-GDDIYEBP.js} +2 -2
- package/dist/{sentinel-42NZRTTS.js → sentinel-4SKSSCXX.js} +0 -0
- package/dist/server-2WPGU3KQ.js +156 -0
- package/dist/shots-6DRTXFDI.js +13 -0
- package/dist/{skills-POB4ZZY5.js → skills-DQTE5Q77.js} +2 -1
- package/dist/{variants-3IEP7DFY.js → variants-VURXVIQO.js} +3 -3
- package/package.json +1 -1
- package/dist/App-766KSGON.js +0 -23
- 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.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
App
|
|
4
|
+
} from "./chunk-2NYNTVMX.js";
|
|
5
|
+
import "./chunk-VJ2QKHYK.js";
|
|
6
|
+
import "./chunk-G5NS2IFQ.js";
|
|
7
|
+
import "./chunk-UBADCBPT.js";
|
|
8
|
+
import "./chunk-IEQGTVWN.js";
|
|
9
|
+
import "./chunk-AUJJGMZG.js";
|
|
10
|
+
import "./chunk-YHRAOBI2.js";
|
|
11
|
+
import "./chunk-2I3YHT6P.js";
|
|
12
|
+
import "./chunk-J7ITMK6X.js";
|
|
13
|
+
import "./chunk-6NPUVPQD.js";
|
|
14
|
+
import "./chunk-K5QJMSJH.js";
|
|
15
|
+
import "./chunk-WAJXATCO.js";
|
|
16
|
+
import "./chunk-62JNF5M2.js";
|
|
17
|
+
import "./chunk-ERF2SPUW.js";
|
|
18
|
+
import "./chunk-7CAGWFAQ.js";
|
|
19
|
+
import "./chunk-3YCG234A.js";
|
|
20
|
+
import "./chunk-PYC53J7D.js";
|
|
21
|
+
import "./chunk-GV2HAALP.js";
|
|
22
|
+
export {
|
|
23
|
+
App
|
|
24
|
+
};
|
|
@@ -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";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
completeCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VJ2QKHYK.js";
|
|
5
5
|
import {
|
|
6
6
|
Session
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-G5NS2IFQ.js";
|
|
8
8
|
|
|
9
9
|
// src/tui/App.tsx
|
|
10
10
|
import { Box as Box3, Static, Text as Text3, useApp, useInput } from "ink";
|
|
@@ -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();
|
|
@@ -735,6 +771,44 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
|
|
|
735
771
|
if (checkFailures.length >= 15) break;
|
|
736
772
|
}
|
|
737
773
|
if (audit) {
|
|
774
|
+
const kbdFindings = [];
|
|
775
|
+
try {
|
|
776
|
+
const seen = [];
|
|
777
|
+
let trapped = null;
|
|
778
|
+
for (let i = 0; i < 12; i++) {
|
|
779
|
+
await connection.send("Input.dispatchKeyEvent", { type: "rawKeyDown", key: "Tab", code: "Tab", windowsVirtualKeyCode: 9 }, sessionId);
|
|
780
|
+
await connection.send("Input.dispatchKeyEvent", { type: "keyUp", key: "Tab", code: "Tab", windowsVirtualKeyCode: 9 }, sessionId);
|
|
781
|
+
const { result } = await connection.send(
|
|
782
|
+
"Runtime.evaluate",
|
|
783
|
+
{
|
|
784
|
+
expression: `(() => {
|
|
785
|
+
const el = document.activeElement;
|
|
786
|
+
if (!el || el === document.body || el === document.documentElement) return null;
|
|
787
|
+
let label = el.tagName.toLowerCase();
|
|
788
|
+
if (el.id) label += '#' + el.id;
|
|
789
|
+
else if (el.classList[0]) label += '.' + el.classList[0];
|
|
790
|
+
const cs = getComputedStyle(el);
|
|
791
|
+
const visible = (cs.outlineStyle !== 'none' && parseFloat(cs.outlineWidth) > 0) || cs.boxShadow !== 'none';
|
|
792
|
+
return { label, visible };
|
|
793
|
+
})()`,
|
|
794
|
+
returnByValue: true
|
|
795
|
+
},
|
|
796
|
+
sessionId
|
|
797
|
+
);
|
|
798
|
+
const stop = result?.value;
|
|
799
|
+
if (!stop) continue;
|
|
800
|
+
if (seen.length > 0 && seen[seen.length - 1] === stop.label && seen.filter((s) => s === stop.label).length >= 2) {
|
|
801
|
+
trapped = stop.label;
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
seen.push(stop.label);
|
|
805
|
+
if (!stop.visible) kbdFindings.push(`keyboard: focus invisible on <${stop.label}> (tab stop ${seen.length}) \u2014 outline and box-shadow both none`);
|
|
806
|
+
}
|
|
807
|
+
if (trapped) kbdFindings.push(`keyboard: focus trapped at <${trapped}> \u2014 Tab cannot leave it`);
|
|
808
|
+
if (seen.length === 0) kbdFindings.push("keyboard: no tabbable elements \u2014 the page is unreachable without a mouse");
|
|
809
|
+
await connection.send("Runtime.evaluate", { expression: "document.activeElement && document.activeElement.blur()" }, sessionId).catch(() => null);
|
|
810
|
+
} catch {
|
|
811
|
+
}
|
|
738
812
|
try {
|
|
739
813
|
const { result } = await connection.send(
|
|
740
814
|
"Runtime.evaluate",
|
|
@@ -742,6 +816,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
|
|
|
742
816
|
sessionId
|
|
743
817
|
);
|
|
744
818
|
if (Array.isArray(result?.value)) a11y = result.value.map(String);
|
|
819
|
+
a11y.push(...kbdFindings);
|
|
745
820
|
} catch {
|
|
746
821
|
}
|
|
747
822
|
try {
|
|
@@ -828,7 +903,10 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
|
|
|
828
903
|
{
|
|
829
904
|
width: viewport.width,
|
|
830
905
|
height: viewport.height,
|
|
831
|
-
|
|
906
|
+
// 2x for review shots: sub-pixel border/kerning slop is
|
|
907
|
+
// invisible at 1x. The pulse stays 1x — its diff math and
|
|
908
|
+
// hit-testing assume CSS pixels.
|
|
909
|
+
deviceScaleFactor: viewport.name === "pulse" ? 1 : 2,
|
|
832
910
|
mobile: viewport.width < 500
|
|
833
911
|
},
|
|
834
912
|
sessionId
|
|
@@ -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-3YCG234A.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-D2F4P7UQ.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-D2F4P7UQ.js");
|
|
173
173
|
return pixelDiffAttributed(chrome, previous, current, url);
|
|
174
174
|
}
|
|
175
175
|
function buildRuntimeFixPrompt(report) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
appendDecision,
|
|
4
3
|
enrich
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UBADCBPT.js";
|
|
5
|
+
import {
|
|
6
|
+
appendDecision
|
|
7
|
+
} from "./chunk-IEQGTVWN.js";
|
|
6
8
|
import {
|
|
7
9
|
applySandbox,
|
|
8
10
|
discardSandbox,
|
|
@@ -23,16 +25,16 @@ import {
|
|
|
23
25
|
buildFixPrompt,
|
|
24
26
|
detectDevCommand,
|
|
25
27
|
screenshotVariants
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-2I3YHT6P.js";
|
|
27
29
|
import {
|
|
28
30
|
applyVariant,
|
|
29
31
|
cleanVariants,
|
|
30
32
|
listVariants,
|
|
31
33
|
runVariants
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-J7ITMK6X.js";
|
|
33
35
|
import {
|
|
34
36
|
runAgent
|
|
35
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-6NPUVPQD.js";
|
|
36
38
|
import {
|
|
37
39
|
composePrompt
|
|
38
40
|
} from "./chunk-WAJXATCO.js";
|
|
@@ -49,7 +51,7 @@ import {
|
|
|
49
51
|
comparePulseAttributed,
|
|
50
52
|
probeRuntime,
|
|
51
53
|
runtimeSummary
|
|
52
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-ERF2SPUW.js";
|
|
53
55
|
import {
|
|
54
56
|
clearState,
|
|
55
57
|
loadState,
|
|
@@ -57,10 +59,10 @@ import {
|
|
|
57
59
|
} from "./chunk-7CAGWFAQ.js";
|
|
58
60
|
import {
|
|
59
61
|
findChrome
|
|
60
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-PYC53J7D.js";
|
|
61
63
|
import {
|
|
62
64
|
getEngine
|
|
63
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-GV2HAALP.js";
|
|
64
66
|
|
|
65
67
|
// src/session/engine.ts
|
|
66
68
|
import fs2 from "fs";
|
|
@@ -102,6 +104,7 @@ function prefixed(payload) {
|
|
|
102
104
|
|
|
103
105
|
// src/session/engine.ts
|
|
104
106
|
var MAX_AUTO_FIX_ATTEMPTS = 2;
|
|
107
|
+
var MAX_GOAL_FIX_ATTEMPTS = 6;
|
|
105
108
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
106
109
|
var Session = class {
|
|
107
110
|
constructor(opts) {
|
|
@@ -153,6 +156,7 @@ var Session = class {
|
|
|
153
156
|
fixAttempts = 0;
|
|
154
157
|
reviewTipShown = false;
|
|
155
158
|
pendingApproval = null;
|
|
159
|
+
goal = null;
|
|
156
160
|
lastPulse = null;
|
|
157
161
|
lastPerf = null;
|
|
158
162
|
autoReviewedThisAsk = false;
|
|
@@ -210,10 +214,11 @@ var Session = class {
|
|
|
210
214
|
}
|
|
211
215
|
/** Launch a capped auto-fix turn over all open problems. Returns true if launched. */
|
|
212
216
|
maybeAutoFix() {
|
|
213
|
-
|
|
217
|
+
const cap = this.goal ? MAX_GOAL_FIX_ATTEMPTS : MAX_AUTO_FIX_ATTEMPTS;
|
|
218
|
+
if (!this.opts.autoFix || this.fixAttempts >= cap) return false;
|
|
214
219
|
if (this.state.problems.length === 0) return false;
|
|
215
220
|
this.fixAttempts += 1;
|
|
216
|
-
this.push("status", `auto-fix attempt ${this.fixAttempts}/${
|
|
221
|
+
this.push("status", `auto-fix attempt ${this.fixAttempts}/${cap}${this.goal ? " (goal armed)" : ""}`);
|
|
217
222
|
this.notify({ running: false });
|
|
218
223
|
this.dispatchFix([...this.state.problems]);
|
|
219
224
|
return true;
|
|
@@ -510,7 +515,7 @@ ${driftSummary(drift)}`);
|
|
|
510
515
|
} catch {
|
|
511
516
|
}
|
|
512
517
|
try {
|
|
513
|
-
const { scanEvasion, sentinelSummary } = await import("./sentinel-
|
|
518
|
+
const { scanEvasion, sentinelSummary } = await import("./sentinel-4SKSSCXX.js");
|
|
514
519
|
const evasions = scanEvasion(this.execCwd(), checkpoint.snapshot.stashHash ?? "HEAD");
|
|
515
520
|
if (evasions.length > 0) {
|
|
516
521
|
this.push("error", `\u26A0 sentinel: ${evasions.length} gate-evasion pattern(s) this turn
|
|
@@ -694,6 +699,15 @@ ${probe.checkFailures.slice(0, 5).join("\n")}`);
|
|
|
694
699
|
this.push("status", `skills: ${enrichment.matchedSkills.join(", ")}`);
|
|
695
700
|
}
|
|
696
701
|
prompt += enrichment.sections;
|
|
702
|
+
if (this.goal) {
|
|
703
|
+
prompt += `
|
|
704
|
+
|
|
705
|
+
## Standing goal (machine-checked)
|
|
706
|
+
|
|
707
|
+
${this.goal}
|
|
708
|
+
|
|
709
|
+
squint verifies every turn \u2014 gates, runtime probe, page audits. Do not declare this done while any of its checks fail; squint will keep sending failures back until they are clean.`;
|
|
710
|
+
}
|
|
697
711
|
await this.runTurn(prompt, ask);
|
|
698
712
|
}
|
|
699
713
|
/** Restore files to the state before checkpoint `index`; drop it and everything after. */
|
|
@@ -859,7 +873,7 @@ They are objective defects, not style preferences.`
|
|
|
859
873
|
const arg = rest.join(" ").trim();
|
|
860
874
|
switch (name) {
|
|
861
875
|
case "engines": {
|
|
862
|
-
void import("./registry-
|
|
876
|
+
void import("./registry-JBP7J3JB.js").then(({ detectEngines }) => {
|
|
863
877
|
const lines = detectEngines().map(({ engine, path: binaryPath }) => {
|
|
864
878
|
const mark = binaryPath ? "\u2713" : "\u2717";
|
|
865
879
|
const traits = [engine.createParser ? "stream" : "text", engine.supportsResume ? "resume" : null].filter(Boolean).join(" \xB7 ");
|
|
@@ -947,8 +961,45 @@ They are objective defects, not style preferences.`
|
|
|
947
961
|
}
|
|
948
962
|
break;
|
|
949
963
|
}
|
|
964
|
+
case "distill": {
|
|
965
|
+
void (async () => {
|
|
966
|
+
const { loadDecisions } = await import("./designLog-S67J7SO2.js");
|
|
967
|
+
const decisions = loadDecisions(this.opts.cwd, 24);
|
|
968
|
+
if (decisions.length < 3) {
|
|
969
|
+
this.push("status", `only ${decisions.length} decision(s) on record \u2014 distillation needs a few more (/decide, variants, approvals, rollbacks all feed the ledger)`);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const ledger = decisions.map((d) => `- [${d.source}] ${d.decision}`).join("\n");
|
|
973
|
+
const prompt = `The design ledger below is every decision this project has recorded \u2014 approvals, rejections, chosen variants, rollbacks. Distill the accumulated taste into standing enforcement:
|
|
974
|
+
|
|
975
|
+
${ledger}
|
|
976
|
+
|
|
977
|
+
1. Append at most 3 SHORT always-on rules to .squint/rules.md capturing patterns that repeat across decisions (rejections matter most \u2014 "no gradient buttons" beats restating one-off choices). Create the file if missing; never delete existing rules.
|
|
978
|
+
2. Where a decision is mechanically checkable in the DOM, write a persistent check at .squint/checks/<name>.js \u2014 plain JS that evaluates IN THE PAGE to an array of failure strings (empty = pass). At most 2 new checks; skip anything that needs judgment rather than a selector.
|
|
979
|
+
3. Reply with exactly what you wrote and why, briefly.
|
|
980
|
+
|
|
981
|
+
Do not restyle anything \u2014 this task only writes rules and checks.`;
|
|
982
|
+
await this.runTurn(prompt, "/distill \u2014 compress the ledger into rules and checks", this.opts.fixModel);
|
|
983
|
+
})();
|
|
984
|
+
break;
|
|
985
|
+
}
|
|
986
|
+
case "goal": {
|
|
987
|
+
if (!arg || arg === "show") {
|
|
988
|
+
this.push("status", this.goal ? `standing goal: ${this.goal}` : "no standing goal \u2014 /goal <objective> arms one; /goal off clears");
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
if (arg === "off") {
|
|
992
|
+
this.goal = null;
|
|
993
|
+
this.push("status", "goal cleared \u2014 auto-fix back to the normal cap");
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
this.goal = arg;
|
|
997
|
+
this.push("status", `goal armed: ${arg}
|
|
998
|
+
every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts until squint's checks come back clean`);
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
950
1001
|
case "context": {
|
|
951
|
-
import("./contextDoctor-
|
|
1002
|
+
import("./contextDoctor-LHKGSIEM.js").then(({ contextReport, formatContextReport }) => {
|
|
952
1003
|
this.push("status", formatContextReport(contextReport(this.execCwd())));
|
|
953
1004
|
}).catch((error) => {
|
|
954
1005
|
this.push("status", `context report failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1072,7 +1123,7 @@ They are objective defects, not style preferences.`
|
|
|
1072
1123
|
break;
|
|
1073
1124
|
}
|
|
1074
1125
|
void (async () => {
|
|
1075
|
-
const { loadFlows } = await import("./flows-
|
|
1126
|
+
const { loadFlows } = await import("./flows-MXJL45HW.js");
|
|
1076
1127
|
const flows = loadFlows(this.opts.cwd);
|
|
1077
1128
|
if (flows.length === 0) {
|
|
1078
1129
|
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 +1134,8 @@ They are objective defects, not style preferences.`
|
|
|
1083
1134
|
this.push("error", "no Chrome/Chromium found for flows");
|
|
1084
1135
|
return;
|
|
1085
1136
|
}
|
|
1086
|
-
const { runFlow } = await import("./cdp-
|
|
1087
|
-
const { previewDir } = await import("./preview-
|
|
1137
|
+
const { runFlow } = await import("./cdp-D2F4P7UQ.js");
|
|
1138
|
+
const { previewDir } = await import("./preview-JN5PTWLX.js");
|
|
1088
1139
|
this.push("status", `replaying ${flows.length} flow(s)\u2026`);
|
|
1089
1140
|
this.notify({ running: true, runStartedAt: Date.now() });
|
|
1090
1141
|
const failures = [];
|
|
@@ -1408,7 +1459,7 @@ ${sandboxFiles(this.opts.cwd).join("\n")}`);
|
|
|
1408
1459
|
this.notify({ items: [], totals: { costUsd: 0, turns: 0 } });
|
|
1409
1460
|
break;
|
|
1410
1461
|
case "help": {
|
|
1411
|
-
void import("./commands-
|
|
1462
|
+
void import("./commands-WC7V6EAE.js").then(({ commandHelp }) => this.push("status", commandHelp()));
|
|
1412
1463
|
break;
|
|
1413
1464
|
}
|
|
1414
1465
|
case "quit":
|
|
@@ -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 = [
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/session/designLog.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
function logFile(cwd) {
|
|
7
|
+
return path.join(cwd, ".squint", "design-log.jsonl");
|
|
8
|
+
}
|
|
9
|
+
function appendDecision(cwd, entry) {
|
|
10
|
+
try {
|
|
11
|
+
fs.mkdirSync(path.join(cwd, ".squint"), { recursive: true });
|
|
12
|
+
const record = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry };
|
|
13
|
+
fs.appendFileSync(logFile(cwd), JSON.stringify(record) + "\n");
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function loadDecisions(cwd, limit = 8) {
|
|
18
|
+
try {
|
|
19
|
+
const lines = fs.readFileSync(logFile(cwd), "utf8").trim().split("\n");
|
|
20
|
+
const decisions = [];
|
|
21
|
+
for (const line of lines.slice(-limit * 2)) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(line);
|
|
24
|
+
if (typeof parsed?.decision === "string" && typeof parsed?.ts === "string") decisions.push(parsed);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return decisions.slice(-limit);
|
|
29
|
+
} catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function decisionsSection(cwd) {
|
|
34
|
+
const decisions = loadDecisions(cwd);
|
|
35
|
+
if (decisions.length === 0) return "";
|
|
36
|
+
const lines = decisions.map((d) => {
|
|
37
|
+
const days = Math.floor((Date.now() - Date.parse(d.ts)) / 864e5);
|
|
38
|
+
const age = days <= 0 ? "today" : `${days}d ago`;
|
|
39
|
+
return `- ${d.decision} (${d.source}, ${age}${d.screenshot ? `, evidence: ${d.screenshot}` : ""})`;
|
|
40
|
+
});
|
|
41
|
+
return `## Design decisions on record
|
|
42
|
+
|
|
43
|
+
${lines.join("\n")}
|
|
44
|
+
|
|
45
|
+
These were decided deliberately. Do not silently undo them; if a task genuinely requires reversing one, say so explicitly first.`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
appendDecision,
|
|
50
|
+
loadDecisions,
|
|
51
|
+
decisionsSection
|
|
52
|
+
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
decisionsSection,
|
|
4
3
|
inventorySection,
|
|
5
4
|
loadComponentInventory,
|
|
6
|
-
loadDecisions,
|
|
7
5
|
loadLocks,
|
|
8
6
|
loadRules,
|
|
9
7
|
loadSkills
|
|
10
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UBADCBPT.js";
|
|
9
|
+
import {
|
|
10
|
+
decisionsSection,
|
|
11
|
+
loadDecisions
|
|
12
|
+
} from "./chunk-IEQGTVWN.js";
|
|
11
13
|
import {
|
|
12
14
|
loadBrief
|
|
13
15
|
} from "./chunk-WAJXATCO.js";
|
|
@@ -68,6 +70,7 @@ function formatContextReport(report) {
|
|
|
68
70
|
}
|
|
69
71
|
return out.join("\n");
|
|
70
72
|
}
|
|
73
|
+
|
|
71
74
|
export {
|
|
72
75
|
contextReport,
|
|
73
76
|
formatContextReport
|