@aayambansal/squint 0.5.0 → 0.5.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.
- package/dist/{App-MC2XEM7K.js → App-QTB6FIJM.js} +7 -6
- package/dist/{cdp-WSWOSDEX.js → cdp-FKFCV2B5.js} +1 -1
- package/dist/{chunk-43NQNIJY.js → chunk-3GHZOVAA.js} +3 -1
- package/dist/{chunk-4XSHHGA6.js → chunk-DRMVCSUG.js} +38 -1
- package/dist/{chunk-CRDRWTON.js → chunk-HSQ7XLD2.js} +8 -7
- package/dist/chunk-IEQGTVWN.js +52 -0
- package/dist/{chunk-EB4OSUB7.js → chunk-LBQDTQ5A.js} +2 -2
- package/dist/{chunk-BKOGVFCO.js → chunk-LESZB7DT.js} +5 -3
- package/dist/{chunk-YK5DEB7T.js → chunk-NCEEMDD6.js} +1 -1
- package/dist/{chunk-ARDV4XH6.js → chunk-UBADCBPT.js} +14 -59
- package/dist/{chunk-WASLLXEZ.js → chunk-W5ZGF5JQ.js} +78 -11
- package/dist/{chunk-DCFNX2CQ.js → chunk-YRANCWY6.js} +40 -1
- package/dist/cli.js +25 -19
- package/dist/{commands-BY44HDQ6.js → commands-RVCKIMNV.js} +1 -1
- package/dist/{contextDoctor-3Y36YURE.js → contextDoctor-LHKGSIEM.js} +3 -2
- package/dist/designLog-S67J7SO2.js +11 -0
- package/dist/flows-OAQYGH77.js +13 -0
- package/dist/{preview-CDEMPHWV.js → preview-XWPCSQFT.js} +2 -2
- package/dist/receipts-Z55AHONO.js +60 -0
- package/dist/{remote-C4BACCKX.js → remote-QNRB5APU.js} +6 -5
- package/dist/{sentinel-42NZRTTS.js → sentinel-4SKSSCXX.js} +0 -0
- package/dist/{server-ENT2OFNL.js → server-AROYCMDF.js} +7 -6
- package/dist/{skills-POB4ZZY5.js → skills-DQTE5Q77.js} +2 -1
- package/package.json +1 -1
- package/dist/flows-MXJL45HW.js +0 -11
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
App
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-LBQDTQ5A.js";
|
|
5
|
+
import "./chunk-3GHZOVAA.js";
|
|
6
|
+
import "./chunk-W5ZGF5JQ.js";
|
|
7
|
+
import "./chunk-UBADCBPT.js";
|
|
8
|
+
import "./chunk-IEQGTVWN.js";
|
|
8
9
|
import "./chunk-AUJJGMZG.js";
|
|
9
10
|
import "./chunk-YHRAOBI2.js";
|
|
10
11
|
import "./chunk-2I3YHT6P.js";
|
|
@@ -13,9 +14,9 @@ import "./chunk-6NPUVPQD.js";
|
|
|
13
14
|
import "./chunk-K5QJMSJH.js";
|
|
14
15
|
import "./chunk-WAJXATCO.js";
|
|
15
16
|
import "./chunk-62JNF5M2.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-HSQ7XLD2.js";
|
|
17
18
|
import "./chunk-7CAGWFAQ.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-YRANCWY6.js";
|
|
19
20
|
import "./chunk-PYC53J7D.js";
|
|
20
21
|
import "./chunk-GV2HAALP.js";
|
|
21
22
|
export {
|
|
@@ -15,7 +15,7 @@ var COMMANDS = [
|
|
|
15
15
|
{ name: "review", args: "[focus]", group: "verify", description: "screenshots + the engine critiques its own rendered work" },
|
|
16
16
|
{ name: "polish", args: "[1-5]", group: "verify", description: "unattended rounds of review \u2192 fix (default 2)" },
|
|
17
17
|
{ name: "score", group: "verify", description: "deterministic quality snapshot (problems, a11y, tells, runtime, LCP)" },
|
|
18
|
-
{ name: "flows", args: "[name]", group: "verify", description: "replay declared
|
|
18
|
+
{ name: "flows", args: "[name]", group: "verify", description: "replay declared journeys; /flows suggest drafts them from live routes" },
|
|
19
19
|
{ name: "variants", args: "<2-4> <ask>", group: "explore", description: "parallel design explorations; apply/list/clean" },
|
|
20
20
|
{ name: "sandbox", args: "[on|diff|apply|discard]", group: "explore", description: "asks accumulate in a shadow worktree until you apply" },
|
|
21
21
|
{ name: "undo", group: "explore", description: "revert the last ask (files only)" },
|
|
@@ -27,6 +27,8 @@ var COMMANDS = [
|
|
|
27
27
|
{ name: "save", group: "session", description: "export the transcript to .squint/transcripts/" },
|
|
28
28
|
{ name: "find", args: "<term>", group: "session", description: "search this session and saved transcripts" },
|
|
29
29
|
{ name: "decide", args: "<text>", group: "session", description: "record a design decision; injected into every future ask" },
|
|
30
|
+
{ name: "goal", args: "[text|off]", group: "build", description: "arm a standing objective; auto-fix presses until checks are clean" },
|
|
31
|
+
{ name: "distill", group: "session", description: "compress the design ledger into rules.md lines and proposed checks" },
|
|
30
32
|
{ name: "context", group: "session", description: "what squint injects per ask, token-costed, with staleness warnings" },
|
|
31
33
|
{ name: "yes", args: "[note]", group: "build", description: "approve the engine's pending visual-approval request" },
|
|
32
34
|
{ name: "no", args: "[note]", group: "build", description: "reject the engine's pending visual-approval request" },
|
|
@@ -140,9 +140,46 @@ function stepExpression(step) {
|
|
|
140
140
|
return null;
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
+
async function suggestFlows(cwd, baseUrl, chromePath) {
|
|
144
|
+
const fs2 = await import("fs");
|
|
145
|
+
const path2 = await import("path");
|
|
146
|
+
const { cdpCapture } = await import("./cdp-FKFCV2B5.js");
|
|
147
|
+
const { loadRoutes } = await import("./preview-XWPCSQFT.js");
|
|
148
|
+
const dir = path2.join(cwd, ".squint", "flows");
|
|
149
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
150
|
+
const created = [];
|
|
151
|
+
const skipped = [];
|
|
152
|
+
for (const route of loadRoutes(cwd)) {
|
|
153
|
+
const slug = route === "/" ? "home" : route.replace(/^\//, "").replace(/[^a-z0-9]+/gi, "-").toLowerCase();
|
|
154
|
+
const file = path2.join(dir, `${slug}.flow`);
|
|
155
|
+
if (fs2.existsSync(file)) {
|
|
156
|
+
skipped.push(slug);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const url = new URL(route, baseUrl).toString();
|
|
160
|
+
const shotDir = fs2.mkdtempSync(path2.join((await import("os")).tmpdir(), "squint-suggest-"));
|
|
161
|
+
let heading = null;
|
|
162
|
+
try {
|
|
163
|
+
const capture = await cdpCapture(chromePath, url, shotDir, [], 1200, true);
|
|
164
|
+
const match = capture.narration.map((n) => /^heading \d: "(.+)"$/.exec(n)).find(Boolean);
|
|
165
|
+
heading = match?.[1] ?? null;
|
|
166
|
+
} catch {
|
|
167
|
+
} finally {
|
|
168
|
+
fs2.rmSync(shotDir, { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
const lines = [`goto ${route}`];
|
|
171
|
+
if (heading) lines.push(`expect ${heading}`);
|
|
172
|
+
lines.push(`shot ${slug}`);
|
|
173
|
+
fs2.writeFileSync(file, `${lines.join("\n")}
|
|
174
|
+
`);
|
|
175
|
+
created.push(slug);
|
|
176
|
+
}
|
|
177
|
+
return { created, skipped };
|
|
178
|
+
}
|
|
143
179
|
|
|
144
180
|
export {
|
|
145
181
|
parseFlow,
|
|
146
182
|
loadFlows,
|
|
147
|
-
stepExpression
|
|
183
|
+
stepExpression,
|
|
184
|
+
suggestFlows
|
|
148
185
|
};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
cdpCapture,
|
|
7
7
|
hasWebSocket
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YRANCWY6.js";
|
|
9
9
|
import {
|
|
10
10
|
findChrome,
|
|
11
11
|
screenshot
|
|
@@ -21,7 +21,7 @@ import fs from "fs";
|
|
|
21
21
|
import path from "path";
|
|
22
22
|
var MAX_CHECKS = 20;
|
|
23
23
|
var MAX_BYTES = 1e4;
|
|
24
|
-
function loadChecks(cwd) {
|
|
24
|
+
function loadChecks(cwd, context = "audit") {
|
|
25
25
|
const dir = path.join(cwd, ".squint", "checks");
|
|
26
26
|
let entries;
|
|
27
27
|
try {
|
|
@@ -34,11 +34,12 @@ function loadChecks(cwd) {
|
|
|
34
34
|
try {
|
|
35
35
|
const source = fs.readFileSync(path.join(dir, entry), "utf8");
|
|
36
36
|
if (source.trim().length === 0 || Buffer.byteLength(source) > MAX_BYTES) continue;
|
|
37
|
-
|
|
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" });
|
|
38
39
|
} catch {
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
return checks;
|
|
42
|
+
return context === "audit" ? checks : checks.filter((c) => c.trigger === "turn");
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
// src/preview/preview.ts
|
|
@@ -153,7 +154,7 @@ async function probeRuntime(url, cwd) {
|
|
|
153
154
|
cwd ? [{ name: "pulse", width: 1280, height: 800 }] : [],
|
|
154
155
|
1500,
|
|
155
156
|
false,
|
|
156
|
-
cwd ? loadChecks(cwd) : []
|
|
157
|
+
cwd ? loadChecks(cwd, "turn") : []
|
|
157
158
|
);
|
|
158
159
|
return { report, pulsePath: shots[0]?.path, perf, checkFailures };
|
|
159
160
|
} catch {
|
|
@@ -163,13 +164,13 @@ async function probeRuntime(url, cwd) {
|
|
|
163
164
|
async function comparePulse(previous, current) {
|
|
164
165
|
const chrome = findChrome();
|
|
165
166
|
if (!chrome || !hasWebSocket()) return null;
|
|
166
|
-
const { pixelDiffPct } = await import("./cdp-
|
|
167
|
+
const { pixelDiffPct } = await import("./cdp-FKFCV2B5.js");
|
|
167
168
|
return pixelDiffPct(chrome, previous, current);
|
|
168
169
|
}
|
|
169
170
|
async function comparePulseAttributed(previous, current, url) {
|
|
170
171
|
const chrome = findChrome();
|
|
171
172
|
if (!chrome || !hasWebSocket()) return null;
|
|
172
|
-
const { pixelDiffAttributed } = await import("./cdp-
|
|
173
|
+
const { pixelDiffAttributed } = await import("./cdp-FKFCV2B5.js");
|
|
173
174
|
return pixelDiffAttributed(chrome, previous, current, url);
|
|
174
175
|
}
|
|
175
176
|
function buildRuntimeFixPrompt(report) {
|
|
@@ -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,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
completeCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3GHZOVAA.js";
|
|
5
5
|
import {
|
|
6
6
|
Session
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-W5ZGF5JQ.js";
|
|
8
8
|
|
|
9
9
|
// src/tui/App.tsx
|
|
10
10
|
import { Box as Box3, Static, Text as Text3, useApp, useInput } from "ink";
|
|
@@ -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";
|
|
@@ -1,70 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
decisionsSection
|
|
4
|
+
} from "./chunk-IEQGTVWN.js";
|
|
2
5
|
|
|
3
6
|
// src/prompt/skills.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
7
|
+
import fs2 from "fs";
|
|
8
|
+
import path2 from "path";
|
|
6
9
|
|
|
7
|
-
// src/
|
|
10
|
+
// src/prompt/registry.ts
|
|
8
11
|
import fs from "fs";
|
|
9
12
|
import path from "path";
|
|
10
|
-
function logFile(cwd) {
|
|
11
|
-
return path.join(cwd, ".squint", "design-log.jsonl");
|
|
12
|
-
}
|
|
13
|
-
function appendDecision(cwd, entry) {
|
|
14
|
-
try {
|
|
15
|
-
fs.mkdirSync(path.join(cwd, ".squint"), { recursive: true });
|
|
16
|
-
const record = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry };
|
|
17
|
-
fs.appendFileSync(logFile(cwd), JSON.stringify(record) + "\n");
|
|
18
|
-
} catch {
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function loadDecisions(cwd, limit = 8) {
|
|
22
|
-
try {
|
|
23
|
-
const lines = fs.readFileSync(logFile(cwd), "utf8").trim().split("\n");
|
|
24
|
-
const decisions = [];
|
|
25
|
-
for (const line of lines.slice(-limit * 2)) {
|
|
26
|
-
try {
|
|
27
|
-
const parsed = JSON.parse(line);
|
|
28
|
-
if (typeof parsed?.decision === "string" && typeof parsed?.ts === "string") decisions.push(parsed);
|
|
29
|
-
} catch {
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return decisions.slice(-limit);
|
|
33
|
-
} catch {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function decisionsSection(cwd) {
|
|
38
|
-
const decisions = loadDecisions(cwd);
|
|
39
|
-
if (decisions.length === 0) return "";
|
|
40
|
-
const lines = decisions.map((d) => {
|
|
41
|
-
const days = Math.floor((Date.now() - Date.parse(d.ts)) / 864e5);
|
|
42
|
-
const age = days <= 0 ? "today" : `${days}d ago`;
|
|
43
|
-
return `- ${d.decision} (${d.source}, ${age}${d.screenshot ? `, evidence: ${d.screenshot}` : ""})`;
|
|
44
|
-
});
|
|
45
|
-
return `## Design decisions on record
|
|
46
|
-
|
|
47
|
-
${lines.join("\n")}
|
|
48
|
-
|
|
49
|
-
These were decided deliberately. Do not silently undo them; if a task genuinely requires reversing one, say so explicitly first.`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// src/prompt/registry.ts
|
|
53
|
-
import fs2 from "fs";
|
|
54
|
-
import path2 from "path";
|
|
55
13
|
function loadComponentInventory(cwd) {
|
|
56
14
|
let config;
|
|
57
15
|
try {
|
|
58
|
-
config = JSON.parse(
|
|
16
|
+
config = JSON.parse(fs.readFileSync(path.join(cwd, "components.json"), "utf8"));
|
|
59
17
|
} catch {
|
|
60
18
|
return null;
|
|
61
19
|
}
|
|
62
20
|
const alias = config.aliases?.ui ?? (config.aliases?.components ? `${config.aliases.components}/ui` : "@/components/ui");
|
|
63
21
|
const relative = alias.replace(/^@\//, "src/").replace(/^~\//, "");
|
|
64
|
-
const uiDir =
|
|
22
|
+
const uiDir = path.join(cwd, relative);
|
|
65
23
|
let entries;
|
|
66
24
|
try {
|
|
67
|
-
entries =
|
|
25
|
+
entries = fs.readdirSync(uiDir);
|
|
68
26
|
} catch {
|
|
69
27
|
return null;
|
|
70
28
|
}
|
|
@@ -100,17 +58,17 @@ function parseSkill(name, raw) {
|
|
|
100
58
|
return { name, triggers, body };
|
|
101
59
|
}
|
|
102
60
|
function loadSkills(cwd) {
|
|
103
|
-
const dir =
|
|
61
|
+
const dir = path2.join(cwd, ".squint", "skills");
|
|
104
62
|
let entries;
|
|
105
63
|
try {
|
|
106
|
-
entries =
|
|
64
|
+
entries = fs2.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
107
65
|
} catch {
|
|
108
66
|
return [];
|
|
109
67
|
}
|
|
110
68
|
const skills = [];
|
|
111
69
|
for (const entry of entries.sort()) {
|
|
112
70
|
try {
|
|
113
|
-
const skill = parseSkill(entry.replace(/\.md$/, ""),
|
|
71
|
+
const skill = parseSkill(entry.replace(/\.md$/, ""), fs2.readFileSync(path2.join(dir, entry), "utf8"));
|
|
114
72
|
if (skill) skills.push(skill);
|
|
115
73
|
} catch {
|
|
116
74
|
}
|
|
@@ -119,7 +77,7 @@ function loadSkills(cwd) {
|
|
|
119
77
|
}
|
|
120
78
|
function loadRules(cwd) {
|
|
121
79
|
try {
|
|
122
|
-
const text =
|
|
80
|
+
const text = fs2.readFileSync(path2.join(cwd, ".squint", "rules.md"), "utf8").trim();
|
|
123
81
|
return text.length > 0 ? text : null;
|
|
124
82
|
} catch {
|
|
125
83
|
return null;
|
|
@@ -131,7 +89,7 @@ function matchSkills(skills, ask) {
|
|
|
131
89
|
}
|
|
132
90
|
function loadLocks(cwd) {
|
|
133
91
|
try {
|
|
134
|
-
return
|
|
92
|
+
return fs2.readFileSync(path2.join(cwd, ".squint", "locks"), "utf8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
135
93
|
} catch {
|
|
136
94
|
return [];
|
|
137
95
|
}
|
|
@@ -180,9 +138,6 @@ ${parts.join("\n\n")}` : "",
|
|
|
180
138
|
}
|
|
181
139
|
|
|
182
140
|
export {
|
|
183
|
-
appendDecision,
|
|
184
|
-
loadDecisions,
|
|
185
|
-
decisionsSection,
|
|
186
141
|
loadComponentInventory,
|
|
187
142
|
inventorySection,
|
|
188
143
|
parseSkill,
|
|
@@ -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,
|
|
@@ -49,7 +51,7 @@ import {
|
|
|
49
51
|
comparePulseAttributed,
|
|
50
52
|
probeRuntime,
|
|
51
53
|
runtimeSummary
|
|
52
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-HSQ7XLD2.js";
|
|
53
55
|
import {
|
|
54
56
|
clearState,
|
|
55
57
|
loadState,
|
|
@@ -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. */
|
|
@@ -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)}`);
|
|
@@ -1071,8 +1122,24 @@ They are objective defects, not style preferences.`
|
|
|
1071
1122
|
this.push("error", "dev server not running \u2014 /dev first");
|
|
1072
1123
|
break;
|
|
1073
1124
|
}
|
|
1125
|
+
if (arg.trim() === "suggest") {
|
|
1126
|
+
void (async () => {
|
|
1127
|
+
const chrome = findChrome();
|
|
1128
|
+
if (!chrome) {
|
|
1129
|
+
this.push("error", "no Chrome/Chromium found");
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
const { suggestFlows } = await import("./flows-OAQYGH77.js");
|
|
1133
|
+
this.push("status", "drafting flows from the live routes\u2026");
|
|
1134
|
+
const { created, skipped } = await suggestFlows(this.opts.cwd, this.state.devUrl, chrome);
|
|
1135
|
+
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`);
|
|
1136
|
+
if (skipped.length > 0) this.push("status", `kept existing: ${skipped.join(", ")}`);
|
|
1137
|
+
if (created.length === 0 && skipped.length === 0) this.push("status", "no routes to draft from \u2014 add .squint/routes");
|
|
1138
|
+
})();
|
|
1139
|
+
break;
|
|
1140
|
+
}
|
|
1074
1141
|
void (async () => {
|
|
1075
|
-
const { loadFlows } = await import("./flows-
|
|
1142
|
+
const { loadFlows } = await import("./flows-OAQYGH77.js");
|
|
1076
1143
|
const flows = loadFlows(this.opts.cwd);
|
|
1077
1144
|
if (flows.length === 0) {
|
|
1078
1145
|
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 +1150,8 @@ They are objective defects, not style preferences.`
|
|
|
1083
1150
|
this.push("error", "no Chrome/Chromium found for flows");
|
|
1084
1151
|
return;
|
|
1085
1152
|
}
|
|
1086
|
-
const { runFlow } = await import("./cdp-
|
|
1087
|
-
const { previewDir } = await import("./preview-
|
|
1153
|
+
const { runFlow } = await import("./cdp-FKFCV2B5.js");
|
|
1154
|
+
const { previewDir } = await import("./preview-XWPCSQFT.js");
|
|
1088
1155
|
this.push("status", `replaying ${flows.length} flow(s)\u2026`);
|
|
1089
1156
|
this.notify({ running: true, runStartedAt: Date.now() });
|
|
1090
1157
|
const failures = [];
|
|
@@ -1408,7 +1475,7 @@ ${sandboxFiles(this.opts.cwd).join("\n")}`);
|
|
|
1408
1475
|
this.notify({ items: [], totals: { costUsd: 0, turns: 0 } });
|
|
1409
1476
|
break;
|
|
1410
1477
|
case "help": {
|
|
1411
|
-
void import("./commands-
|
|
1478
|
+
void import("./commands-RVCKIMNV.js").then(({ commandHelp }) => this.push("status", commandHelp()));
|
|
1412
1479
|
break;
|
|
1413
1480
|
}
|
|
1414
1481
|
case "quit":
|
|
@@ -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-OAQYGH77.js");
|
|
183
183
|
const { child, wsUrl, profileDir } = await launchChrome(chromePath);
|
|
184
184
|
const shots = [];
|
|
185
185
|
let connection = null;
|
|
@@ -771,6 +771,44 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
|
|
|
771
771
|
if (checkFailures.length >= 15) break;
|
|
772
772
|
}
|
|
773
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
|
+
}
|
|
774
812
|
try {
|
|
775
813
|
const { result } = await connection.send(
|
|
776
814
|
"Runtime.evaluate",
|
|
@@ -778,6 +816,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
|
|
|
778
816
|
sessionId
|
|
779
817
|
);
|
|
780
818
|
if (Array.isArray(result?.value)) a11y = result.value.map(String);
|
|
819
|
+
a11y.push(...kbdFindings);
|
|
781
820
|
} catch {
|
|
782
821
|
}
|
|
783
822
|
try {
|
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
App
|
|
4
|
+
} from "./chunk-LBQDTQ5A.js";
|
|
5
|
+
import "./chunk-3GHZOVAA.js";
|
|
2
6
|
import {
|
|
3
7
|
connectDaemon,
|
|
4
8
|
socketPath,
|
|
5
9
|
startDaemon
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import "./chunk-43NQNIJY.js";
|
|
11
|
-
import "./chunk-WASLLXEZ.js";
|
|
12
|
-
import "./chunk-ARDV4XH6.js";
|
|
10
|
+
} from "./chunk-NCEEMDD6.js";
|
|
11
|
+
import "./chunk-W5ZGF5JQ.js";
|
|
12
|
+
import "./chunk-UBADCBPT.js";
|
|
13
|
+
import "./chunk-IEQGTVWN.js";
|
|
13
14
|
import "./chunk-AUJJGMZG.js";
|
|
14
15
|
import "./chunk-YHRAOBI2.js";
|
|
15
16
|
import "./chunk-2I3YHT6P.js";
|
|
@@ -22,9 +23,9 @@ import {
|
|
|
22
23
|
composePrompt
|
|
23
24
|
} from "./chunk-WAJXATCO.js";
|
|
24
25
|
import "./chunk-62JNF5M2.js";
|
|
25
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-HSQ7XLD2.js";
|
|
26
27
|
import "./chunk-7CAGWFAQ.js";
|
|
27
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-YRANCWY6.js";
|
|
28
29
|
import "./chunk-PYC53J7D.js";
|
|
29
30
|
import {
|
|
30
31
|
detectEngines,
|
|
@@ -186,18 +187,18 @@ function registerDaemon(program2) {
|
|
|
186
187
|
process.on("SIGTERM", stop);
|
|
187
188
|
});
|
|
188
189
|
program2.command("mcp").description("serve the gates as MCP tools over stdio (check, shot, flows, context)").action(async () => {
|
|
189
|
-
const { runMcpServer } = await import("./server-
|
|
190
|
+
const { runMcpServer } = await import("./server-AROYCMDF.js");
|
|
190
191
|
runMcpServer(process.cwd());
|
|
191
192
|
});
|
|
192
193
|
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) => {
|
|
193
194
|
const cwd = process.cwd();
|
|
194
195
|
if (!opts.plain) {
|
|
195
196
|
try {
|
|
196
|
-
const { RemoteSession } = await import("./remote-
|
|
197
|
+
const { RemoteSession } = await import("./remote-QNRB5APU.js");
|
|
197
198
|
const remote = await RemoteSession.connect(cwd);
|
|
198
199
|
const config = loadConfig(defaultPaths(cwd));
|
|
199
200
|
const { render: render2 } = await import("ink");
|
|
200
|
-
const { App: App2 } = await import("./App-
|
|
201
|
+
const { App: App2 } = await import("./App-QTB6FIJM.js");
|
|
201
202
|
const React = await import("react");
|
|
202
203
|
render2(
|
|
203
204
|
React.createElement(App2, {
|
|
@@ -314,7 +315,7 @@ function registerEnv(program2) {
|
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
317
|
const { findChrome } = await import("./chrome-EEXJCG6W.js");
|
|
317
|
-
const { hasWebSocket } = await import("./cdp-
|
|
318
|
+
const { hasWebSocket } = await import("./cdp-FKFCV2B5.js");
|
|
318
319
|
const chrome = findChrome();
|
|
319
320
|
console.log(
|
|
320
321
|
chrome ? `${pc3.green("\u2713")} Chrome ${pc3.dim(chrome)}` : `${pc3.yellow("\u25CB")} Chrome ${pc3.dim("\u2014 screenshots and runtime probing disabled")}`
|
|
@@ -338,7 +339,7 @@ import pc4 from "picocolors";
|
|
|
338
339
|
function registerProject(program2) {
|
|
339
340
|
const skillsCommand = program2.command("skills").description("Project knowledge injected into asks (.squint/rules.md + .squint/skills/)");
|
|
340
341
|
skillsCommand.command("list").description("Show always-on rules and trigger-matched skills").action(async () => {
|
|
341
|
-
const { loadRules, loadSkills } = await import("./skills-
|
|
342
|
+
const { loadRules, loadSkills } = await import("./skills-DQTE5Q77.js");
|
|
342
343
|
const cwd = process.cwd();
|
|
343
344
|
const rules = loadRules(cwd);
|
|
344
345
|
console.log(
|
|
@@ -550,7 +551,7 @@ function registerQuality(program2) {
|
|
|
550
551
|
report.gates = gateResults.map((r) => ({ id: r.gate.id, ok: r.ok, durationMs: r.durationMs, outputTail: r.ok ? void 0 : r.outputTail }));
|
|
551
552
|
if (gateResults.some((r) => !r.ok)) failed = true;
|
|
552
553
|
if (opts.url) {
|
|
553
|
-
const { captureViewports, runtimeSummary, previewDir } = await import("./preview-
|
|
554
|
+
const { captureViewports, runtimeSummary, previewDir } = await import("./preview-XWPCSQFT.js");
|
|
554
555
|
const capture = await captureViewports(cwd, opts.url);
|
|
555
556
|
if (!capture) {
|
|
556
557
|
console.log(pc5.red("\u2717 audit: capture failed (Chrome missing or URL unreachable)"));
|
|
@@ -582,14 +583,14 @@ function registerQuality(program2) {
|
|
|
582
583
|
console.log(`${pc5.green("\u2713")} audit ${pc5.dim(`(${advisories.a11y.length} a11y, ${advisories.slop.length} slop, ${advisories.jank.length} jank advisories)`)}`);
|
|
583
584
|
}
|
|
584
585
|
}
|
|
585
|
-
const { loadFlows } = await import("./flows-
|
|
586
|
+
const { loadFlows } = await import("./flows-OAQYGH77.js");
|
|
586
587
|
const flows = loadFlows(cwd);
|
|
587
588
|
if (flows.length > 0) {
|
|
588
589
|
const { findChrome } = await import("./chrome-EEXJCG6W.js");
|
|
589
590
|
const chrome = findChrome();
|
|
590
591
|
const flowReport = [];
|
|
591
592
|
if (chrome) {
|
|
592
|
-
const { runFlow } = await import("./cdp-
|
|
593
|
+
const { runFlow } = await import("./cdp-FKFCV2B5.js");
|
|
593
594
|
for (const flow of flows) {
|
|
594
595
|
const result = await runFlow(chrome, opts.url, flow, previewDir(cwd));
|
|
595
596
|
flowReport.push({ name: flow.name, ok: result.ok, detail: result.ok ? void 0 : `step ${result.failedStep}: ${result.detail}` });
|
|
@@ -602,6 +603,11 @@ function registerQuality(program2) {
|
|
|
602
603
|
}
|
|
603
604
|
report.ok = !failed;
|
|
604
605
|
report.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
606
|
+
try {
|
|
607
|
+
const { writeReceipt } = await import("./receipts-Z55AHONO.js");
|
|
608
|
+
console.log(pc5.dim(`receipt \u2192 ${writeReceipt(cwd, report)}`));
|
|
609
|
+
} catch {
|
|
610
|
+
}
|
|
605
611
|
if (opts.json) {
|
|
606
612
|
const fs2 = await import("fs");
|
|
607
613
|
fs2.writeFileSync(opts.json, JSON.stringify(report, null, 2));
|
|
@@ -610,7 +616,7 @@ function registerQuality(program2) {
|
|
|
610
616
|
if (failed) process.exitCode = 1;
|
|
611
617
|
});
|
|
612
618
|
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) => {
|
|
613
|
-
const { captureViewports } = await import("./preview-
|
|
619
|
+
const { captureViewports } = await import("./preview-XWPCSQFT.js");
|
|
614
620
|
const result = await captureViewports(process.cwd(), url);
|
|
615
621
|
if (!result) {
|
|
616
622
|
console.error(pc5.red("\u2717 no Chrome/Chromium found"));
|
|
@@ -797,7 +803,7 @@ function registerTui(program2) {
|
|
|
797
803
|
}
|
|
798
804
|
|
|
799
805
|
// src/cli.tsx
|
|
800
|
-
var VERSION = true ? "0.5.
|
|
806
|
+
var VERSION = true ? "0.5.2" : "0.0.0-dev";
|
|
801
807
|
var program = new Command();
|
|
802
808
|
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
803
809
|
registerRun(program);
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
contextReport,
|
|
4
4
|
formatContextReport
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-LESZB7DT.js";
|
|
6
|
+
import "./chunk-UBADCBPT.js";
|
|
7
|
+
import "./chunk-IEQGTVWN.js";
|
|
7
8
|
import "./chunk-WAJXATCO.js";
|
|
8
9
|
export {
|
|
9
10
|
contextReport,
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
probeRuntime,
|
|
12
12
|
routeShotName,
|
|
13
13
|
runtimeSummary
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HSQ7XLD2.js";
|
|
15
15
|
import "./chunk-7CAGWFAQ.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-YRANCWY6.js";
|
|
17
17
|
import "./chunk-PYC53J7D.js";
|
|
18
18
|
import "./chunk-GV2HAALP.js";
|
|
19
19
|
export {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/quality/receipts.ts
|
|
4
|
+
import { execFileSync } from "child_process";
|
|
5
|
+
import crypto from "crypto";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
function sha256(data) {
|
|
9
|
+
return crypto.createHash("sha256").update(data).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
function canonical(value) {
|
|
12
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
13
|
+
if (value && typeof value === "object") {
|
|
14
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : 1);
|
|
15
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}`;
|
|
16
|
+
}
|
|
17
|
+
return JSON.stringify(value);
|
|
18
|
+
}
|
|
19
|
+
function buildReceipt(cwd, report) {
|
|
20
|
+
let gitHead = null;
|
|
21
|
+
try {
|
|
22
|
+
gitHead = execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
const screenshots = {};
|
|
26
|
+
const shots = report.audit?.shots ?? [];
|
|
27
|
+
for (const shot of shots) {
|
|
28
|
+
try {
|
|
29
|
+
screenshots[path.basename(shot)] = sha256(fs.readFileSync(shot));
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const unsigned = {
|
|
34
|
+
version: true ? "0.5.2" : "0.0.0-dev",
|
|
35
|
+
node: process.version,
|
|
36
|
+
gitHead,
|
|
37
|
+
report,
|
|
38
|
+
screenshots
|
|
39
|
+
};
|
|
40
|
+
return { ...unsigned, digest: sha256(canonical(unsigned)) };
|
|
41
|
+
}
|
|
42
|
+
function verifyReceipt(receipt) {
|
|
43
|
+
const { digest, ...unsigned } = receipt;
|
|
44
|
+
return sha256(canonical(unsigned)) === digest;
|
|
45
|
+
}
|
|
46
|
+
function writeReceipt(cwd, report) {
|
|
47
|
+
const receipt = buildReceipt(cwd, report);
|
|
48
|
+
const dir = path.join(cwd, ".squint", "receipts");
|
|
49
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
50
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
51
|
+
const file = path.join(dir, `${stamp}${receipt.report.ok ? "" : "-failed"}.json`);
|
|
52
|
+
fs.writeFileSync(file, JSON.stringify(receipt, null, 2));
|
|
53
|
+
return file;
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
buildReceipt,
|
|
57
|
+
canonical,
|
|
58
|
+
verifyReceipt,
|
|
59
|
+
writeReceipt
|
|
60
|
+
};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
connectDaemon,
|
|
4
4
|
socketPath
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-NCEEMDD6.js";
|
|
6
|
+
import "./chunk-W5ZGF5JQ.js";
|
|
7
|
+
import "./chunk-UBADCBPT.js";
|
|
8
|
+
import "./chunk-IEQGTVWN.js";
|
|
8
9
|
import "./chunk-AUJJGMZG.js";
|
|
9
10
|
import "./chunk-YHRAOBI2.js";
|
|
10
11
|
import "./chunk-2I3YHT6P.js";
|
|
@@ -13,9 +14,9 @@ import "./chunk-6NPUVPQD.js";
|
|
|
13
14
|
import "./chunk-K5QJMSJH.js";
|
|
14
15
|
import "./chunk-WAJXATCO.js";
|
|
15
16
|
import "./chunk-62JNF5M2.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-HSQ7XLD2.js";
|
|
17
18
|
import "./chunk-7CAGWFAQ.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-YRANCWY6.js";
|
|
19
20
|
import "./chunk-PYC53J7D.js";
|
|
20
21
|
import "./chunk-GV2HAALP.js";
|
|
21
22
|
|
|
Binary file
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
contextReport,
|
|
4
4
|
formatContextReport
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-LESZB7DT.js";
|
|
6
|
+
import "./chunk-UBADCBPT.js";
|
|
7
|
+
import "./chunk-IEQGTVWN.js";
|
|
7
8
|
import "./chunk-WAJXATCO.js";
|
|
8
9
|
import {
|
|
9
10
|
detectGates,
|
|
@@ -11,14 +12,14 @@ import {
|
|
|
11
12
|
} from "./chunk-62JNF5M2.js";
|
|
12
13
|
import {
|
|
13
14
|
loadFlows
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-DRMVCSUG.js";
|
|
15
16
|
import {
|
|
16
17
|
captureViewports,
|
|
17
18
|
previewDir,
|
|
18
19
|
runtimeSummary
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-HSQ7XLD2.js";
|
|
20
21
|
import "./chunk-7CAGWFAQ.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-YRANCWY6.js";
|
|
22
23
|
import {
|
|
23
24
|
findChrome
|
|
24
25
|
} from "./chunk-PYC53J7D.js";
|
|
@@ -74,7 +75,7 @@ ${list.join("\n")}`);
|
|
|
74
75
|
if (flows.length === 0) return "no flows declared \u2014 add .squint/flows/<name>.flow";
|
|
75
76
|
const chrome = findChrome();
|
|
76
77
|
if (!chrome) return "no Chrome/Chromium found";
|
|
77
|
-
const { runFlow } = await import("./cdp-
|
|
78
|
+
const { runFlow } = await import("./cdp-FKFCV2B5.js");
|
|
78
79
|
const lines = [];
|
|
79
80
|
for (const flow of flows) {
|
|
80
81
|
const result = await runFlow(chrome, url, flow, previewDir(cwd));
|
package/package.json
CHANGED