@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
|
@@ -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,
|
|
@@ -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" },
|
package/dist/cli.js
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
App
|
|
4
|
+
} from "./chunk-2NYNTVMX.js";
|
|
5
|
+
import "./chunk-VJ2QKHYK.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-YGPVP4YB.js";
|
|
12
|
-
import "./chunk-ARDV4XH6.js";
|
|
10
|
+
} from "./chunk-AMA3F2Y2.js";
|
|
11
|
+
import "./chunk-G5NS2IFQ.js";
|
|
12
|
+
import "./chunk-UBADCBPT.js";
|
|
13
|
+
import "./chunk-IEQGTVWN.js";
|
|
13
14
|
import "./chunk-AUJJGMZG.js";
|
|
14
15
|
import "./chunk-YHRAOBI2.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-2I3YHT6P.js";
|
|
17
|
+
import "./chunk-J7ITMK6X.js";
|
|
17
18
|
import {
|
|
18
19
|
runAgent
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-6NPUVPQD.js";
|
|
20
21
|
import "./chunk-K5QJMSJH.js";
|
|
21
22
|
import {
|
|
22
23
|
composePrompt
|
|
23
24
|
} from "./chunk-WAJXATCO.js";
|
|
24
25
|
import "./chunk-62JNF5M2.js";
|
|
25
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-ERF2SPUW.js";
|
|
26
27
|
import "./chunk-7CAGWFAQ.js";
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-3YCG234A.js";
|
|
29
|
+
import "./chunk-PYC53J7D.js";
|
|
29
30
|
import {
|
|
30
31
|
detectEngines,
|
|
31
32
|
getEngine
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-GV2HAALP.js";
|
|
33
34
|
|
|
34
35
|
// src/cli.tsx
|
|
35
36
|
import { Command } from "commander";
|
|
@@ -185,15 +186,19 @@ function registerDaemon(program2) {
|
|
|
185
186
|
process.on("SIGINT", stop);
|
|
186
187
|
process.on("SIGTERM", stop);
|
|
187
188
|
});
|
|
189
|
+
program2.command("mcp").description("serve the gates as MCP tools over stdio (check, shot, flows, context)").action(async () => {
|
|
190
|
+
const { runMcpServer } = await import("./server-2WPGU3KQ.js");
|
|
191
|
+
runMcpServer(process.cwd());
|
|
192
|
+
});
|
|
188
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) => {
|
|
189
194
|
const cwd = process.cwd();
|
|
190
195
|
if (!opts.plain) {
|
|
191
196
|
try {
|
|
192
|
-
const { RemoteSession } = await import("./remote-
|
|
197
|
+
const { RemoteSession } = await import("./remote-UOV4ZXWU.js");
|
|
193
198
|
const remote = await RemoteSession.connect(cwd);
|
|
194
199
|
const config = loadConfig(defaultPaths(cwd));
|
|
195
200
|
const { render: render2 } = await import("ink");
|
|
196
|
-
const { App: App2 } = await import("./App-
|
|
201
|
+
const { App: App2 } = await import("./App-KTNAMHY6.js");
|
|
197
202
|
const React = await import("react");
|
|
198
203
|
render2(
|
|
199
204
|
React.createElement(App2, {
|
|
@@ -287,7 +292,7 @@ function registerEnv(program2) {
|
|
|
287
292
|
console.log(`${status} ${engine.name}${binaryPath ? "" : pc3.dim(` \u2014 install: ${engine.install}`)}`);
|
|
288
293
|
}
|
|
289
294
|
if (options.probe) {
|
|
290
|
-
const { runAgent: runAgent2 } = await import("./run-
|
|
295
|
+
const { runAgent: runAgent2 } = await import("./run-GDDIYEBP.js");
|
|
291
296
|
console.log(pc3.dim("\nprobing engines with a one-word prompt (verifies auth end to end)\u2026"));
|
|
292
297
|
for (const { engine, path: binaryPath } of detected) {
|
|
293
298
|
if (!binaryPath) continue;
|
|
@@ -309,8 +314,8 @@ function registerEnv(program2) {
|
|
|
309
314
|
);
|
|
310
315
|
}
|
|
311
316
|
}
|
|
312
|
-
const { findChrome } = await import("./chrome-
|
|
313
|
-
const { hasWebSocket } = await import("./cdp-
|
|
317
|
+
const { findChrome } = await import("./chrome-EEXJCG6W.js");
|
|
318
|
+
const { hasWebSocket } = await import("./cdp-D2F4P7UQ.js");
|
|
314
319
|
const chrome = findChrome();
|
|
315
320
|
console.log(
|
|
316
321
|
chrome ? `${pc3.green("\u2713")} Chrome ${pc3.dim(chrome)}` : `${pc3.yellow("\u25CB")} Chrome ${pc3.dim("\u2014 screenshots and runtime probing disabled")}`
|
|
@@ -334,7 +339,7 @@ import pc4 from "picocolors";
|
|
|
334
339
|
function registerProject(program2) {
|
|
335
340
|
const skillsCommand = program2.command("skills").description("Project knowledge injected into asks (.squint/rules.md + .squint/skills/)");
|
|
336
341
|
skillsCommand.command("list").description("Show always-on rules and trigger-matched skills").action(async () => {
|
|
337
|
-
const { loadRules, loadSkills } = await import("./skills-
|
|
342
|
+
const { loadRules, loadSkills } = await import("./skills-DQTE5Q77.js");
|
|
338
343
|
const cwd = process.cwd();
|
|
339
344
|
const rules = loadRules(cwd);
|
|
340
345
|
console.log(
|
|
@@ -451,7 +456,7 @@ function registerProject(program2) {
|
|
|
451
456
|
process.exitCode = 1;
|
|
452
457
|
return;
|
|
453
458
|
}
|
|
454
|
-
const { runVariants, cleanVariants } = await import("./variants-
|
|
459
|
+
const { runVariants, cleanVariants } = await import("./variants-VURXVIQO.js");
|
|
455
460
|
const config = loadConfig(defaultPaths(cwd));
|
|
456
461
|
const engineId = resolveEngineId(config, options.engine);
|
|
457
462
|
const engine = getEngine(engineId);
|
|
@@ -469,7 +474,7 @@ function registerProject(program2) {
|
|
|
469
474
|
);
|
|
470
475
|
const succeeded = runs.filter((r) => r.result.ok);
|
|
471
476
|
if (options.shots && succeeded.length > 0) {
|
|
472
|
-
const { screenshotVariants } = await import("./shots-
|
|
477
|
+
const { screenshotVariants } = await import("./shots-6DRTXFDI.js");
|
|
473
478
|
console.log(pc4.dim("capturing screenshots\u2026"));
|
|
474
479
|
const shots = await screenshotVariants(cwd, succeeded.map((r) => r.variant));
|
|
475
480
|
for (const shot of shots) {
|
|
@@ -484,7 +489,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
|
|
|
484
489
|
}
|
|
485
490
|
);
|
|
486
491
|
variantsCommand.command("list").description("List generated variants").action(async () => {
|
|
487
|
-
const { listVariants } = await import("./variants-
|
|
492
|
+
const { listVariants } = await import("./variants-VURXVIQO.js");
|
|
488
493
|
const ids = listVariants(process.cwd());
|
|
489
494
|
if (ids.length === 0) {
|
|
490
495
|
console.log(pc4.dim('no variants \u2014 squint variants gen <n> "<ask>"'));
|
|
@@ -493,7 +498,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
|
|
|
493
498
|
for (const id of ids) console.log(id);
|
|
494
499
|
});
|
|
495
500
|
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-
|
|
501
|
+
const { applyVariant, cleanVariants } = await import("./variants-VURXVIQO.js");
|
|
497
502
|
const cwd = process.cwd();
|
|
498
503
|
const result = applyVariant(cwd, id);
|
|
499
504
|
if (!result.ok) {
|
|
@@ -505,7 +510,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
|
|
|
505
510
|
console.log(pc4.green(`\u2713 applied ${id} to the working tree`) + pc4.dim(" \u2014 review with git diff"));
|
|
506
511
|
});
|
|
507
512
|
variantsCommand.command("clean").description("Discard all variants").action(async () => {
|
|
508
|
-
const { cleanVariants } = await import("./variants-
|
|
513
|
+
const { cleanVariants } = await import("./variants-VURXVIQO.js");
|
|
509
514
|
const count = cleanVariants(process.cwd());
|
|
510
515
|
console.log(pc4.dim(`removed ${count} variant(s)`));
|
|
511
516
|
});
|
|
@@ -532,8 +537,81 @@ function registerQuality(program2) {
|
|
|
532
537
|
});
|
|
533
538
|
if (results.some((r) => !r.ok)) process.exitCode = 1;
|
|
534
539
|
});
|
|
540
|
+
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) => {
|
|
541
|
+
const cwd = process.cwd();
|
|
542
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
543
|
+
const report = { startedAt, cwd };
|
|
544
|
+
let failed = false;
|
|
545
|
+
const { detectGates, runGates } = await import("./gates-ZC67NMW3.js");
|
|
546
|
+
const gates = detectGates(cwd);
|
|
547
|
+
const gateResults = await runGates(cwd, gates, (result) => {
|
|
548
|
+
console.log(`${result.ok ? pc5.green("\u2713") : pc5.red("\u2717")} ${result.gate.id} ${pc5.dim(`${(result.durationMs / 1e3).toFixed(1)}s`)}`);
|
|
549
|
+
if (!result.ok) console.log(pc5.dim(result.outputTail.split("\n").slice(-12).join("\n")));
|
|
550
|
+
});
|
|
551
|
+
report.gates = gateResults.map((r) => ({ id: r.gate.id, ok: r.ok, durationMs: r.durationMs, outputTail: r.ok ? void 0 : r.outputTail }));
|
|
552
|
+
if (gateResults.some((r) => !r.ok)) failed = true;
|
|
553
|
+
if (opts.url) {
|
|
554
|
+
const { captureViewports, runtimeSummary, previewDir } = await import("./preview-JN5PTWLX.js");
|
|
555
|
+
const capture = await captureViewports(cwd, opts.url);
|
|
556
|
+
if (!capture) {
|
|
557
|
+
console.log(pc5.red("\u2717 audit: capture failed (Chrome missing or URL unreachable)"));
|
|
558
|
+
report.audit = { ok: false };
|
|
559
|
+
failed = true;
|
|
560
|
+
} else {
|
|
561
|
+
const runtime = capture.runtime ? runtimeSummary(capture.runtime) : null;
|
|
562
|
+
const hard = {
|
|
563
|
+
runtime,
|
|
564
|
+
phantoms: capture.phantoms ?? [],
|
|
565
|
+
viewTransitions: (capture.viewTransitions ?? []).filter((v) => v.startsWith("duplicate")),
|
|
566
|
+
checkFailures: capture.checkFailures ?? []
|
|
567
|
+
};
|
|
568
|
+
const advisories = {
|
|
569
|
+
a11y: capture.a11y ?? [],
|
|
570
|
+
slop: capture.slop ?? [],
|
|
571
|
+
jank: capture.jank ?? []
|
|
572
|
+
};
|
|
573
|
+
const hardCount = (runtime ? 1 : 0) + hard.phantoms.length + hard.viewTransitions.length + hard.checkFailures.length;
|
|
574
|
+
report.audit = { ok: hardCount === 0, hard, advisories, shots: capture.shots.map((s) => s.path) };
|
|
575
|
+
if (hardCount > 0) {
|
|
576
|
+
failed = true;
|
|
577
|
+
console.log(pc5.red(`\u2717 audit: ${hardCount} hard finding(s)`));
|
|
578
|
+
for (const [k, v] of Object.entries(hard)) {
|
|
579
|
+
if (Array.isArray(v) && v.length > 0) console.log(pc5.dim(` ${k}: ${v.join(" \xB7 ")}`));
|
|
580
|
+
else if (typeof v === "string" && v) console.log(pc5.dim(` ${k}: ${v}`));
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
console.log(`${pc5.green("\u2713")} audit ${pc5.dim(`(${advisories.a11y.length} a11y, ${advisories.slop.length} slop, ${advisories.jank.length} jank advisories)`)}`);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const { loadFlows } = await import("./flows-MXJL45HW.js");
|
|
587
|
+
const flows = loadFlows(cwd);
|
|
588
|
+
if (flows.length > 0) {
|
|
589
|
+
const { findChrome } = await import("./chrome-EEXJCG6W.js");
|
|
590
|
+
const chrome = findChrome();
|
|
591
|
+
const flowReport = [];
|
|
592
|
+
if (chrome) {
|
|
593
|
+
const { runFlow } = await import("./cdp-D2F4P7UQ.js");
|
|
594
|
+
for (const flow of flows) {
|
|
595
|
+
const result = await runFlow(chrome, opts.url, flow, previewDir(cwd));
|
|
596
|
+
flowReport.push({ name: flow.name, ok: result.ok, detail: result.ok ? void 0 : `step ${result.failedStep}: ${result.detail}` });
|
|
597
|
+
console.log(result.ok ? `${pc5.green("\u2713")} flow ${flow.name}` : pc5.red(`\u2717 flow ${flow.name} \u2014 step ${result.failedStep}: ${result.detail}`));
|
|
598
|
+
if (!result.ok) failed = true;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
report.flows = flowReport;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
report.ok = !failed;
|
|
605
|
+
report.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
606
|
+
if (opts.json) {
|
|
607
|
+
const fs2 = await import("fs");
|
|
608
|
+
fs2.writeFileSync(opts.json, JSON.stringify(report, null, 2));
|
|
609
|
+
console.log(pc5.dim(`report \u2192 ${opts.json}`));
|
|
610
|
+
}
|
|
611
|
+
if (failed) process.exitCode = 1;
|
|
612
|
+
});
|
|
535
613
|
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-
|
|
614
|
+
const { captureViewports } = await import("./preview-JN5PTWLX.js");
|
|
537
615
|
const result = await captureViewports(process.cwd(), url);
|
|
538
616
|
if (!result) {
|
|
539
617
|
console.error(pc5.red("\u2717 no Chrome/Chromium found"));
|
|
@@ -720,7 +798,7 @@ function registerTui(program2) {
|
|
|
720
798
|
}
|
|
721
799
|
|
|
722
800
|
// src/cli.tsx
|
|
723
|
-
var VERSION = true ? "0.
|
|
801
|
+
var VERSION = true ? "0.5.1" : "0.0.0-dev";
|
|
724
802
|
var program = new Command();
|
|
725
803
|
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
726
804
|
registerRun(program);
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
probeRuntime,
|
|
12
12
|
routeShotName,
|
|
13
13
|
runtimeSummary
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ERF2SPUW.js";
|
|
15
15
|
import "./chunk-7CAGWFAQ.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-3YCG234A.js";
|
|
17
|
+
import "./chunk-PYC53J7D.js";
|
|
18
|
+
import "./chunk-GV2HAALP.js";
|
|
19
19
|
export {
|
|
20
20
|
VIEWPORTS,
|
|
21
21
|
buildReviewPrompt,
|
|
@@ -2,22 +2,23 @@
|
|
|
2
2
|
import {
|
|
3
3
|
connectDaemon,
|
|
4
4
|
socketPath
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-AMA3F2Y2.js";
|
|
6
|
+
import "./chunk-G5NS2IFQ.js";
|
|
7
|
+
import "./chunk-UBADCBPT.js";
|
|
8
|
+
import "./chunk-IEQGTVWN.js";
|
|
8
9
|
import "./chunk-AUJJGMZG.js";
|
|
9
10
|
import "./chunk-YHRAOBI2.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-2I3YHT6P.js";
|
|
12
|
+
import "./chunk-J7ITMK6X.js";
|
|
13
|
+
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-ERF2SPUW.js";
|
|
17
18
|
import "./chunk-7CAGWFAQ.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-3YCG234A.js";
|
|
20
|
+
import "./chunk-PYC53J7D.js";
|
|
21
|
+
import "./chunk-GV2HAALP.js";
|
|
21
22
|
|
|
22
23
|
// src/daemon/remote.ts
|
|
23
24
|
var noteId = 0;
|
|
Binary file
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
contextReport,
|
|
4
|
+
formatContextReport
|
|
5
|
+
} from "./chunk-LESZB7DT.js";
|
|
6
|
+
import "./chunk-UBADCBPT.js";
|
|
7
|
+
import "./chunk-IEQGTVWN.js";
|
|
8
|
+
import "./chunk-WAJXATCO.js";
|
|
9
|
+
import {
|
|
10
|
+
detectGates,
|
|
11
|
+
runGates
|
|
12
|
+
} from "./chunk-62JNF5M2.js";
|
|
13
|
+
import {
|
|
14
|
+
loadFlows
|
|
15
|
+
} from "./chunk-4XSHHGA6.js";
|
|
16
|
+
import {
|
|
17
|
+
captureViewports,
|
|
18
|
+
previewDir,
|
|
19
|
+
runtimeSummary
|
|
20
|
+
} from "./chunk-ERF2SPUW.js";
|
|
21
|
+
import "./chunk-7CAGWFAQ.js";
|
|
22
|
+
import "./chunk-3YCG234A.js";
|
|
23
|
+
import {
|
|
24
|
+
findChrome
|
|
25
|
+
} from "./chunk-PYC53J7D.js";
|
|
26
|
+
import "./chunk-GV2HAALP.js";
|
|
27
|
+
|
|
28
|
+
// src/mcp/server.ts
|
|
29
|
+
import readline from "readline";
|
|
30
|
+
var TOOLS = [
|
|
31
|
+
{
|
|
32
|
+
name: "squint_check",
|
|
33
|
+
description: "Run the repo quality gates (typecheck, lint, format, test, build as detected). Returns each gate with pass/fail and failure output.",
|
|
34
|
+
inputSchema: { type: "object", properties: {} },
|
|
35
|
+
async run(_args, cwd) {
|
|
36
|
+
const gates = detectGates(cwd);
|
|
37
|
+
if (gates.length === 0) return "no gates detected (no package.json scripts or known tools)";
|
|
38
|
+
const results = await runGates(cwd, gates);
|
|
39
|
+
return results.map((r) => `${r.ok ? "\u2713" : "\u2717"} ${r.gate.display}${r.ok ? "" : `
|
|
40
|
+
${r.outputTail.slice(-1500)}`}`).join("\n");
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "squint_shot",
|
|
45
|
+
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/.",
|
|
46
|
+
inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
|
|
47
|
+
async run(args, cwd) {
|
|
48
|
+
const url = String(args.url ?? "");
|
|
49
|
+
const result = await captureViewports(cwd, url);
|
|
50
|
+
if (!result) return "capture failed \u2014 is Chrome installed and the URL reachable?";
|
|
51
|
+
const sections = [`shots: ${result.shots.map((s) => s.path).join(", ")}`];
|
|
52
|
+
const runtime = result.runtime ? runtimeSummary(result.runtime) : null;
|
|
53
|
+
if (runtime) sections.push(`runtime: ${runtime}`);
|
|
54
|
+
for (const [label, list] of [
|
|
55
|
+
["a11y", result.a11y],
|
|
56
|
+
["slop", result.slop],
|
|
57
|
+
["phantom classes", result.phantoms],
|
|
58
|
+
["view transitions", result.viewTransitions],
|
|
59
|
+
["jank", result.jank],
|
|
60
|
+
["components", result.components]
|
|
61
|
+
]) {
|
|
62
|
+
if (list && list.length > 0) sections.push(`${label}:
|
|
63
|
+
${list.join("\n")}`);
|
|
64
|
+
}
|
|
65
|
+
return sections.join("\n\n");
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "squint_flows",
|
|
70
|
+
description: "Replay the repo's declared user journeys (.squint/flows/*.flow) headlessly against a URL. Returns pass/fail per flow with the failing step.",
|
|
71
|
+
inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
|
|
72
|
+
async run(args, cwd) {
|
|
73
|
+
const url = String(args.url ?? "");
|
|
74
|
+
const flows = loadFlows(cwd);
|
|
75
|
+
if (flows.length === 0) return "no flows declared \u2014 add .squint/flows/<name>.flow";
|
|
76
|
+
const chrome = findChrome();
|
|
77
|
+
if (!chrome) return "no Chrome/Chromium found";
|
|
78
|
+
const { runFlow } = await import("./cdp-D2F4P7UQ.js");
|
|
79
|
+
const lines = [];
|
|
80
|
+
for (const flow of flows) {
|
|
81
|
+
const result = await runFlow(chrome, url, flow, previewDir(cwd));
|
|
82
|
+
lines.push(
|
|
83
|
+
result.ok ? `\u2713 ${flow.name} (${flow.steps.length} steps)` : `\u2717 ${flow.name} at step ${result.failedStep}: ${result.detail}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return lines.join("\n");
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "squint_context",
|
|
91
|
+
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).",
|
|
92
|
+
inputSchema: { type: "object", properties: {} },
|
|
93
|
+
async run(_args, cwd) {
|
|
94
|
+
return formatContextReport(contextReport(cwd));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
function runMcpServer(cwd, input = process.stdin, output = process.stdout) {
|
|
99
|
+
const write = (msg) => output.write(`${JSON.stringify(msg)}
|
|
100
|
+
`);
|
|
101
|
+
const rl = readline.createInterface({ input });
|
|
102
|
+
rl.on("line", (line) => {
|
|
103
|
+
if (!line.trim()) return;
|
|
104
|
+
let req;
|
|
105
|
+
try {
|
|
106
|
+
req = JSON.parse(line);
|
|
107
|
+
} catch {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
void handle(req, cwd, write);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async function handle(req, cwd, write) {
|
|
114
|
+
const reply = (result) => {
|
|
115
|
+
if (req.id !== void 0) write({ jsonrpc: "2.0", id: req.id, result });
|
|
116
|
+
};
|
|
117
|
+
const fail = (code, message) => {
|
|
118
|
+
if (req.id !== void 0) write({ jsonrpc: "2.0", id: req.id, error: { code, message } });
|
|
119
|
+
};
|
|
120
|
+
switch (req.method) {
|
|
121
|
+
case "initialize":
|
|
122
|
+
reply({
|
|
123
|
+
protocolVersion: "2025-06-18",
|
|
124
|
+
capabilities: { tools: {} },
|
|
125
|
+
serverInfo: { name: "squint", version: "0" }
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
case "notifications/initialized":
|
|
129
|
+
case "ping":
|
|
130
|
+
if (req.method === "ping") reply({});
|
|
131
|
+
return;
|
|
132
|
+
case "tools/list":
|
|
133
|
+
reply({ tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })) });
|
|
134
|
+
return;
|
|
135
|
+
case "tools/call": {
|
|
136
|
+
const name = String(req.params?.name ?? "");
|
|
137
|
+
const tool = TOOLS.find((t) => t.name === name);
|
|
138
|
+
if (!tool) return fail(-32602, `unknown tool: ${name}`);
|
|
139
|
+
try {
|
|
140
|
+
const text = await tool.run(req.params?.arguments ?? {}, cwd);
|
|
141
|
+
reply({ content: [{ type: "text", text }] });
|
|
142
|
+
} catch (error) {
|
|
143
|
+
reply({
|
|
144
|
+
content: [{ type: "text", text: `tool failed: ${error instanceof Error ? error.message : String(error)}` }],
|
|
145
|
+
isError: true
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
default:
|
|
151
|
+
fail(-32601, `method not found: ${req.method}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export {
|
|
155
|
+
runMcpServer
|
|
156
|
+
};
|
|
@@ -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
|
+
};
|