@aayambansal/squint 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-43L4QXCM.js → chunk-5VKUJPOL.js} +1 -1
- package/dist/{chunk-V3M4WQLH.js → chunk-RZOQPFXC.js} +1 -1
- package/dist/cli.js +521 -429
- package/dist/{commands-7DTZB4JE.js → commands-TYAKF276.js} +1 -1
- package/dist/{preview-UCH7H5R5.js → preview-XGASKUXR.js} +2 -2
- package/dist/{shots-J7JG2FC4.js → shots-FVG4CDVK.js} +3 -3
- package/dist/{variants-2IM274AL.js → variants-UULPQ7Q5.js} +2 -2
- package/package.json +1 -1
- package/dist/{chunk-YZEFBPBJ.js → chunk-CHZE3UHK.js} +4 -4
- package/dist/{chunk-LVA2OQSR.js → chunk-X6MDBKED.js} +3 -3
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/session/commands.ts
|
|
4
4
|
var COMMANDS = [
|
|
5
|
-
{ name: "dev", description: "start/stop the
|
|
5
|
+
{ name: "dev", args: "[restart|logs]", description: "start/stop the dev server; restart or show recent output" },
|
|
6
6
|
{ name: "check", description: "run all quality gates (typecheck, lint, test, build)" },
|
|
7
7
|
{ name: "problems", description: "list open findings from gates, dev server, runtime, a11y" },
|
|
8
8
|
{ name: "fix", args: "[n]", description: "send all open problems to the engine, or just problem n" },
|
package/dist/cli.js
CHANGED
|
@@ -1,34 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
enrich
|
|
4
|
-
} from "./chunk-4LAU5TGK.js";
|
|
5
|
-
import {
|
|
6
|
-
diffStatSince,
|
|
7
|
-
isGitRepo,
|
|
8
|
-
restoreSnapshot,
|
|
9
|
-
takeSnapshot
|
|
10
|
-
} from "./chunk-YHRAOBI2.js";
|
|
11
2
|
import {
|
|
12
3
|
DevServer,
|
|
13
4
|
buildFixPrompt,
|
|
14
5
|
detectDevCommand,
|
|
15
6
|
screenshotVariants
|
|
16
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5VKUJPOL.js";
|
|
17
8
|
import {
|
|
18
9
|
applyVariant,
|
|
19
10
|
cleanVariants,
|
|
20
11
|
listVariants,
|
|
21
12
|
runVariants
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import {
|
|
24
|
-
composePrompt
|
|
25
|
-
} from "./chunk-P3H4N2EN.js";
|
|
26
|
-
import {
|
|
27
|
-
runAgent
|
|
28
|
-
} from "./chunk-C7WKNJG6.js";
|
|
29
|
-
import {
|
|
30
|
-
completeCommand
|
|
31
|
-
} from "./chunk-V3M4WQLH.js";
|
|
13
|
+
} from "./chunk-X6MDBKED.js";
|
|
32
14
|
import {
|
|
33
15
|
buildGatePrompt,
|
|
34
16
|
detectFastGates,
|
|
@@ -45,8 +27,13 @@ import {
|
|
|
45
27
|
probeRuntime,
|
|
46
28
|
runtimeSummary,
|
|
47
29
|
saveState
|
|
48
|
-
} from "./chunk-
|
|
49
|
-
import
|
|
30
|
+
} from "./chunk-CHZE3UHK.js";
|
|
31
|
+
import {
|
|
32
|
+
completeCommand
|
|
33
|
+
} from "./chunk-RZOQPFXC.js";
|
|
34
|
+
import {
|
|
35
|
+
runAgent
|
|
36
|
+
} from "./chunk-C7WKNJG6.js";
|
|
50
37
|
import {
|
|
51
38
|
findChrome
|
|
52
39
|
} from "./chunk-B7LOERSP.js";
|
|
@@ -54,10 +41,24 @@ import {
|
|
|
54
41
|
detectEngines,
|
|
55
42
|
getEngine
|
|
56
43
|
} from "./chunk-BWZFACBT.js";
|
|
44
|
+
import "./chunk-OC6RU6XH.js";
|
|
45
|
+
import {
|
|
46
|
+
enrich
|
|
47
|
+
} from "./chunk-4LAU5TGK.js";
|
|
48
|
+
import {
|
|
49
|
+
composePrompt
|
|
50
|
+
} from "./chunk-P3H4N2EN.js";
|
|
51
|
+
import {
|
|
52
|
+
diffStatSince,
|
|
53
|
+
isGitRepo,
|
|
54
|
+
restoreSnapshot,
|
|
55
|
+
takeSnapshot
|
|
56
|
+
} from "./chunk-YHRAOBI2.js";
|
|
57
57
|
|
|
58
58
|
// src/cli.tsx
|
|
59
59
|
import { Command } from "commander";
|
|
60
|
-
|
|
60
|
+
|
|
61
|
+
// src/cli/configCmd.ts
|
|
61
62
|
import pc from "picocolors";
|
|
62
63
|
|
|
63
64
|
// src/config/config.ts
|
|
@@ -82,6 +83,8 @@ var ConfigSchema = z.object({
|
|
|
82
83
|
bell: z.boolean().optional(),
|
|
83
84
|
/** Session budget in USD; crossing it warns (never blocks). */
|
|
84
85
|
budgetUsd: z.number().positive().optional(),
|
|
86
|
+
/** Auto-run /review when the visual pulse shows a big change (default off). */
|
|
87
|
+
autoReview: z.boolean().optional(),
|
|
85
88
|
/** TUI theme name (amber, ocean, moss, rose, mono). */
|
|
86
89
|
theme: z.string().optional()
|
|
87
90
|
});
|
|
@@ -126,7 +129,7 @@ function setConfigValue(file, key, value) {
|
|
|
126
129
|
let next;
|
|
127
130
|
if (key === "engine" || key === "theme") {
|
|
128
131
|
next = { ...current, [key]: value };
|
|
129
|
-
} else if (key === "autoDev" || key === "autoFix" || key === "autoProbe" || key === "autoCheck" || key === "bell") {
|
|
132
|
+
} else if (key === "autoDev" || key === "autoFix" || key === "autoProbe" || key === "autoCheck" || key === "autoReview" || key === "bell") {
|
|
130
133
|
if (value !== "true" && value !== "false") {
|
|
131
134
|
throw new Error(`"${key}" must be true or false`);
|
|
132
135
|
}
|
|
@@ -143,7 +146,7 @@ function setConfigValue(file, key, value) {
|
|
|
143
146
|
next = { ...current, models: { ...current.models, [engineId]: value } };
|
|
144
147
|
} else {
|
|
145
148
|
throw new Error(
|
|
146
|
-
`Unknown config key "${key}". Supported: engine, theme, autoDev, autoFix, autoProbe, autoCheck, bell, budgetUsd, models.<engineId>`
|
|
149
|
+
`Unknown config key "${key}". Supported: engine, theme, autoDev, autoFix, autoProbe, autoCheck, autoReview, bell, budgetUsd, models.<engineId>`
|
|
147
150
|
);
|
|
148
151
|
}
|
|
149
152
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -151,6 +154,420 @@ function setConfigValue(file, key, value) {
|
|
|
151
154
|
return next;
|
|
152
155
|
}
|
|
153
156
|
|
|
157
|
+
// src/cli/configCmd.ts
|
|
158
|
+
function registerConfig(program2) {
|
|
159
|
+
const configCommand = program2.command("config").description("Read or change squint configuration");
|
|
160
|
+
configCommand.command("get").description("Print the resolved configuration").action(() => {
|
|
161
|
+
const paths = defaultPaths(process.cwd());
|
|
162
|
+
console.log(JSON.stringify(loadConfig(paths), null, 2));
|
|
163
|
+
});
|
|
164
|
+
configCommand.command("set").description("Set a value (see docs/configuration.md for every key)").argument("<key>").argument("<value>").option("--project", "write to this project (.squint/config.json) instead of global").action((key, value, options) => {
|
|
165
|
+
const paths = defaultPaths(process.cwd());
|
|
166
|
+
const file = options.project ? paths.projectFile : paths.globalFile;
|
|
167
|
+
setConfigValue(file, key, value);
|
|
168
|
+
console.log(pc.green(`\u2713 ${key} = ${value}`) + pc.dim(` (${file})`));
|
|
169
|
+
});
|
|
170
|
+
configCommand.command("path").description("Show config file locations").action(() => {
|
|
171
|
+
const paths = defaultPaths(process.cwd());
|
|
172
|
+
console.log(`global ${paths.globalFile}`);
|
|
173
|
+
console.log(`project ${paths.projectFile}`);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/cli/env.ts
|
|
178
|
+
import pc2 from "picocolors";
|
|
179
|
+
function registerEnv(program2) {
|
|
180
|
+
program2.command("engines").description("List engines: installed, streaming, session resume").action(() => {
|
|
181
|
+
console.log(pc2.dim(" id name stream resume where"));
|
|
182
|
+
for (const { engine, path: binaryPath } of detectEngines()) {
|
|
183
|
+
const status = binaryPath ? pc2.green("\u2713") : pc2.red("\u2717");
|
|
184
|
+
const stream = engine.createParser ? pc2.green("yes") : pc2.dim("text");
|
|
185
|
+
const resume = engine.supportsResume ? pc2.green("yes") : pc2.dim("no");
|
|
186
|
+
const location = binaryPath ?? pc2.dim(`not found \u2014 ${engine.install}`);
|
|
187
|
+
console.log(
|
|
188
|
+
`${status} ${engine.id.padEnd(10)} ${engine.name.padEnd(14)} ${stream.padEnd(15)} ${resume.padEnd(14)} ${location}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
console.log(pc2.dim("\nplan/safe/yolo modes map onto every engine \xB7 squint doctor --probe verifies auth"));
|
|
192
|
+
});
|
|
193
|
+
program2.command("doctor").description("Check squint prerequisites and engine availability").option("--probe", "run each detected engine with a one-word prompt to verify auth works").action(async (options) => {
|
|
194
|
+
const [major] = process.versions.node.split(".");
|
|
195
|
+
const nodeOk = Number(major) >= 22;
|
|
196
|
+
console.log(
|
|
197
|
+
`${nodeOk ? pc2.green("\u2713") : pc2.red("\u2717")} node ${process.versions.node}${nodeOk ? "" : " (need >= 22)"}`
|
|
198
|
+
);
|
|
199
|
+
const detected = detectEngines();
|
|
200
|
+
for (const { engine, path: binaryPath } of detected) {
|
|
201
|
+
const status = binaryPath ? pc2.green("\u2713") : pc2.yellow("\u25CB");
|
|
202
|
+
console.log(`${status} ${engine.name}${binaryPath ? "" : pc2.dim(` \u2014 install: ${engine.install}`)}`);
|
|
203
|
+
}
|
|
204
|
+
if (options.probe) {
|
|
205
|
+
const { runAgent: runAgent2 } = await import("./run-S772BPMZ.js");
|
|
206
|
+
console.log(pc2.dim("\nprobing engines with a one-word prompt (verifies auth end to end)\u2026"));
|
|
207
|
+
for (const { engine, path: binaryPath } of detected) {
|
|
208
|
+
if (!binaryPath) continue;
|
|
209
|
+
const startedAt = Date.now();
|
|
210
|
+
const abort = new AbortController();
|
|
211
|
+
const timer = setTimeout(() => abort.abort(), 9e4);
|
|
212
|
+
const result = await runAgent2(
|
|
213
|
+
engine,
|
|
214
|
+
{ prompt: "Reply with exactly: ok", cwd: process.cwd() },
|
|
215
|
+
() => {
|
|
216
|
+
},
|
|
217
|
+
abort.signal
|
|
218
|
+
);
|
|
219
|
+
clearTimeout(timer);
|
|
220
|
+
const secs = ((Date.now() - startedAt) / 1e3).toFixed(1);
|
|
221
|
+
const detail = (result.error ?? "failed").split("\n").filter((l) => l.trim()).at(-1) ?? "failed";
|
|
222
|
+
console.log(
|
|
223
|
+
result.ok ? `${pc2.green("\u2713")} ${engine.id.padEnd(10)} responded in ${secs}s` : `${pc2.red("\u2717")} ${engine.id.padEnd(10)} ${pc2.dim(detail.slice(0, 110))}`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const { findChrome: findChrome2 } = await import("./chrome-NVU47PLK.js");
|
|
228
|
+
const { hasWebSocket } = await import("./cdp-SFWNP7OA.js");
|
|
229
|
+
const chrome = findChrome2();
|
|
230
|
+
console.log(
|
|
231
|
+
chrome ? `${pc2.green("\u2713")} Chrome ${pc2.dim(chrome)}` : `${pc2.yellow("\u25CB")} Chrome ${pc2.dim("\u2014 screenshots and runtime probing disabled")}`
|
|
232
|
+
);
|
|
233
|
+
console.log(
|
|
234
|
+
hasWebSocket() ? `${pc2.green("\u2713")} WebSocket ${pc2.dim("runtime console/network capture available")}` : `${pc2.yellow("\u25CB")} WebSocket ${pc2.dim("\u2014 node 22+ enables runtime capture; screenshots still work")}`
|
|
235
|
+
);
|
|
236
|
+
const available = detected.filter((d) => d.path !== null);
|
|
237
|
+
if (available.length === 0) {
|
|
238
|
+
console.log(pc2.red("\nNo engines found. Install at least one to use squint."));
|
|
239
|
+
process.exitCode = 1;
|
|
240
|
+
} else {
|
|
241
|
+
console.log(pc2.dim(`
|
|
242
|
+
${available.length} engine(s) ready.`));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/cli/project.ts
|
|
248
|
+
import pc3 from "picocolors";
|
|
249
|
+
function registerProject(program2) {
|
|
250
|
+
const skillsCommand = program2.command("skills").description("Project knowledge injected into asks (.squint/rules.md + .squint/skills/)");
|
|
251
|
+
skillsCommand.command("list").description("Show always-on rules and trigger-matched skills").action(async () => {
|
|
252
|
+
const { loadRules, loadSkills } = await import("./skills-DNBHZAAU.js");
|
|
253
|
+
const cwd = process.cwd();
|
|
254
|
+
const rules = loadRules(cwd);
|
|
255
|
+
console.log(
|
|
256
|
+
rules ? `${pc3.green("\u2713")} rules.md ${pc3.dim(`(${rules.split("\n").length} lines, always on)`)}` : pc3.dim("\u25CB no .squint/rules.md")
|
|
257
|
+
);
|
|
258
|
+
const skills = loadSkills(cwd);
|
|
259
|
+
if (skills.length === 0) {
|
|
260
|
+
console.log(pc3.dim("\u25CB no skills \u2014 squint skills init writes an example"));
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
for (const skill of skills) {
|
|
264
|
+
console.log(`${pc3.green("\u2713")} ${skill.name.padEnd(20)} ${pc3.dim(`triggers: ${skill.triggers.join(", ")}`)}`);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
skillsCommand.command("init").description("Scaffold .squint/rules.md and an example skill").action(async () => {
|
|
268
|
+
const fs2 = await import("fs");
|
|
269
|
+
const nodePath = await import("path");
|
|
270
|
+
const cwd = process.cwd();
|
|
271
|
+
const skillsDir = nodePath.join(cwd, ".squint", "skills");
|
|
272
|
+
fs2.mkdirSync(skillsDir, { recursive: true });
|
|
273
|
+
const rules = nodePath.join(cwd, ".squint", "rules.md");
|
|
274
|
+
if (!fs2.existsSync(rules)) {
|
|
275
|
+
fs2.writeFileSync(
|
|
276
|
+
rules,
|
|
277
|
+
"# Project rules\n\nThese ride along on every squint ask. Keep them short \u2014 cut anything that would not cause a mistake if removed.\n"
|
|
278
|
+
);
|
|
279
|
+
console.log(pc3.green("\u2713 .squint/rules.md"));
|
|
280
|
+
}
|
|
281
|
+
const example = nodePath.join(skillsDir, "example.md");
|
|
282
|
+
if (!fs2.existsSync(example)) {
|
|
283
|
+
fs2.writeFileSync(
|
|
284
|
+
example,
|
|
285
|
+
"---\ntriggers: example, sample\n---\n\nThis note is injected only when an ask mentions one of the triggers above.\nDocument the parts of this repo an agent would otherwise rediscover every time:\nwhere state lives, which helpers to reuse, what not to touch.\n"
|
|
286
|
+
);
|
|
287
|
+
console.log(pc3.green("\u2713 .squint/skills/example.md"));
|
|
288
|
+
}
|
|
289
|
+
console.log(pc3.dim("rules are always-on; skills inject when an ask mentions a trigger"));
|
|
290
|
+
});
|
|
291
|
+
program2.command("brief").description("Set a committed design direction for this project (.squint/brief.md)").argument("[family]", "aesthetic family id (omit to list)").option("--force", "overwrite an existing project brief").action(async (familyId, options) => {
|
|
292
|
+
const fs2 = await import("fs");
|
|
293
|
+
const nodePath = await import("path");
|
|
294
|
+
const { FAMILIES, getFamily, renderFamilyBrief } = await import("./families-2G5SLLY7.js");
|
|
295
|
+
if (!familyId) {
|
|
296
|
+
console.log(pc3.bold("Aesthetic families") + pc3.dim(" \u2014 squint brief <id>\n"));
|
|
297
|
+
for (const family2 of FAMILIES) {
|
|
298
|
+
console.log(`${pc3.green(family2.id.padEnd(18))} ${family2.name.padEnd(22)} ${pc3.dim(family2.summary)}`);
|
|
299
|
+
}
|
|
300
|
+
console.log(pc3.dim("\nThe brief wraps every ask; edit .squint/brief.md to remix."));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const family = getFamily(familyId);
|
|
304
|
+
if (!family) {
|
|
305
|
+
console.error(pc3.red(`\u2717 unknown family "${familyId}" \u2014 run squint brief to list`));
|
|
306
|
+
process.exitCode = 1;
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const target = nodePath.join(process.cwd(), ".squint", "brief.md");
|
|
310
|
+
if (fs2.existsSync(target) && !options.force) {
|
|
311
|
+
console.error(pc3.red(`\u2717 ${target} exists \u2014 use --force to overwrite`));
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
fs2.mkdirSync(nodePath.dirname(target), { recursive: true });
|
|
316
|
+
fs2.writeFileSync(target, renderFamilyBrief(family) + "\n");
|
|
317
|
+
console.log(pc3.green(`\u2713 ${family.name} direction written to .squint/brief.md`));
|
|
318
|
+
console.log(pc3.dim("every squint ask in this repo now holds this direction \u2014 edit the file to remix"));
|
|
319
|
+
});
|
|
320
|
+
const variantsCommand = program2.command("variants").description("Parallel design explorations \u2014 one aesthetic family each, pick with your eyes");
|
|
321
|
+
variantsCommand.command("gen").description("Generate n variants of one ask in parallel (n engine runs \u2014 n\xD7 cost)").argument("<n>", "how many variants (max 4)").argument("<prompt...>", "what to build").option("-e, --engine <id>", "engine to use").option("-m, --model <name>", "model override").option("--no-shots", "skip the screenshot pass").action(
|
|
322
|
+
async (nRaw, promptWords, options) => {
|
|
323
|
+
const cwd = process.cwd();
|
|
324
|
+
const n = Number.parseInt(nRaw, 10);
|
|
325
|
+
if (!Number.isInteger(n) || n < 2 || n > 4) {
|
|
326
|
+
console.error(pc3.red("\u2717 n must be 2\u20134"));
|
|
327
|
+
process.exitCode = 1;
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const { isGitRepo: isGitRepo2 } = await import("./snapshot-H6VYFHLN.js");
|
|
331
|
+
if (!isGitRepo2(cwd)) {
|
|
332
|
+
console.error(pc3.red("\u2717 variants need a git repo with at least one commit"));
|
|
333
|
+
process.exitCode = 1;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const { runVariants: runVariants2, cleanVariants: cleanVariants2 } = await import("./variants-UULPQ7Q5.js");
|
|
337
|
+
const config = loadConfig(defaultPaths(cwd));
|
|
338
|
+
const engineId = resolveEngineId(config, options.engine);
|
|
339
|
+
const engine = getEngine(engineId);
|
|
340
|
+
const model = resolveModel(config, engineId, options.model);
|
|
341
|
+
const ask = promptWords.join(" ");
|
|
342
|
+
cleanVariants2(cwd);
|
|
343
|
+
console.log(pc3.dim(`generating ${n} directions in parallel via ${engine.id} \u2014 this runs ${n} engine sessions`));
|
|
344
|
+
const runs = await runVariants2(
|
|
345
|
+
cwd,
|
|
346
|
+
ask,
|
|
347
|
+
n,
|
|
348
|
+
engine,
|
|
349
|
+
model,
|
|
350
|
+
(familyId, text) => console.log(`${pc3.cyan(familyId.padEnd(18))} ${pc3.dim(text)}`)
|
|
351
|
+
);
|
|
352
|
+
const succeeded = runs.filter((r) => r.result.ok);
|
|
353
|
+
if (options.shots && succeeded.length > 0) {
|
|
354
|
+
const { screenshotVariants: screenshotVariants2 } = await import("./shots-FVG4CDVK.js");
|
|
355
|
+
console.log(pc3.dim("capturing screenshots\u2026"));
|
|
356
|
+
const shots = await screenshotVariants2(cwd, succeeded.map((r) => r.variant));
|
|
357
|
+
for (const shot of shots) {
|
|
358
|
+
console.log(`${pc3.green("\u2713")} ${shot.familyId.padEnd(18)} ${shot.path ?? pc3.dim(shot.error ?? "")}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
console.log(
|
|
362
|
+
`
|
|
363
|
+
${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 ` + pc3.bold("squint variants apply <id>") + pc3.dim(" applies the winner, squint variants clean discards all")
|
|
364
|
+
);
|
|
365
|
+
if (succeeded.length === 0) process.exitCode = 1;
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
variantsCommand.command("list").description("List generated variants").action(async () => {
|
|
369
|
+
const { listVariants: listVariants2 } = await import("./variants-UULPQ7Q5.js");
|
|
370
|
+
const ids = listVariants2(process.cwd());
|
|
371
|
+
if (ids.length === 0) {
|
|
372
|
+
console.log(pc3.dim('no variants \u2014 squint variants gen <n> "<ask>"'));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
for (const id of ids) console.log(id);
|
|
376
|
+
});
|
|
377
|
+
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) => {
|
|
378
|
+
const { applyVariant: applyVariant2, cleanVariants: cleanVariants2 } = await import("./variants-UULPQ7Q5.js");
|
|
379
|
+
const cwd = process.cwd();
|
|
380
|
+
const result = applyVariant2(cwd, id);
|
|
381
|
+
if (!result.ok) {
|
|
382
|
+
console.error(pc3.red(`\u2717 ${result.detail}`));
|
|
383
|
+
process.exitCode = 1;
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
cleanVariants2(cwd);
|
|
387
|
+
console.log(pc3.green(`\u2713 applied ${id} to the working tree`) + pc3.dim(" \u2014 review with git diff"));
|
|
388
|
+
});
|
|
389
|
+
variantsCommand.command("clean").description("Discard all variants").action(async () => {
|
|
390
|
+
const { cleanVariants: cleanVariants2 } = await import("./variants-UULPQ7Q5.js");
|
|
391
|
+
const count = cleanVariants2(process.cwd());
|
|
392
|
+
console.log(pc3.dim(`removed ${count} variant(s)`));
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/cli/quality.ts
|
|
397
|
+
import pc4 from "picocolors";
|
|
398
|
+
function registerQuality(program2) {
|
|
399
|
+
program2.command("check").description("Run this project\u2019s quality gates (typecheck, lint, format, test, build)").action(async () => {
|
|
400
|
+
const { detectGates: detectGates2, runGates: runGates2 } = await import("./gates-ZC67NMW3.js");
|
|
401
|
+
const cwd = process.cwd();
|
|
402
|
+
const gates = detectGates2(cwd);
|
|
403
|
+
if (gates.length === 0) {
|
|
404
|
+
console.log(pc4.dim("no gates detected (no package.json scripts, tsconfig, or eslint config)"));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
console.log(pc4.dim(`running ${gates.map((g) => g.id).join(" \u2192 ")}`));
|
|
408
|
+
const results = await runGates2(cwd, gates, (result) => {
|
|
409
|
+
const mark = result.ok ? pc4.green("\u2713") : pc4.red("\u2717");
|
|
410
|
+
console.log(
|
|
411
|
+
`${mark} ${result.gate.id.padEnd(10)} ${pc4.dim(`${(result.durationMs / 1e3).toFixed(1)}s \xB7 ${result.gate.display}`)}`
|
|
412
|
+
);
|
|
413
|
+
if (!result.ok) console.log(pc4.dim(result.outputTail.split("\n").slice(-12).join("\n")));
|
|
414
|
+
});
|
|
415
|
+
if (results.some((r) => !r.ok)) process.exitCode = 1;
|
|
416
|
+
});
|
|
417
|
+
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) => {
|
|
418
|
+
const { captureViewports: captureViewports2 } = await import("./preview-XGASKUXR.js");
|
|
419
|
+
const result = await captureViewports2(process.cwd(), url);
|
|
420
|
+
if (!result) {
|
|
421
|
+
console.error(pc4.red("\u2717 no Chrome/Chromium found"));
|
|
422
|
+
process.exitCode = 1;
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
for (const shot of result.shots) console.log(`${pc4.green("\u2713")} ${shot.name.padEnd(8)} ${shot.path}`);
|
|
426
|
+
for (const error of result.errors) console.error(pc4.red(`\u2717 ${error}`));
|
|
427
|
+
if (result.shots.length === 0) process.exitCode = 1;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/cli/run.ts
|
|
432
|
+
import pc5 from "picocolors";
|
|
433
|
+
function createPrinter() {
|
|
434
|
+
let streaming = false;
|
|
435
|
+
return (event) => {
|
|
436
|
+
switch (event.type) {
|
|
437
|
+
case "status":
|
|
438
|
+
console.log(pc5.dim(`\xB7 ${event.text}`));
|
|
439
|
+
break;
|
|
440
|
+
case "delta":
|
|
441
|
+
streaming = true;
|
|
442
|
+
process.stdout.write(event.text);
|
|
443
|
+
break;
|
|
444
|
+
case "text":
|
|
445
|
+
if (event.streamed && streaming) {
|
|
446
|
+
process.stdout.write("\n");
|
|
447
|
+
} else {
|
|
448
|
+
console.log(event.text);
|
|
449
|
+
}
|
|
450
|
+
streaming = false;
|
|
451
|
+
break;
|
|
452
|
+
case "thinking":
|
|
453
|
+
console.log(pc5.dim(pc5.italic(event.text)));
|
|
454
|
+
break;
|
|
455
|
+
case "tool":
|
|
456
|
+
if (streaming) {
|
|
457
|
+
process.stdout.write("\n");
|
|
458
|
+
streaming = false;
|
|
459
|
+
}
|
|
460
|
+
console.log(pc5.cyan(`\u2699 ${event.name}${event.detail ? ` \xB7 ${event.detail}` : ""}`));
|
|
461
|
+
break;
|
|
462
|
+
case "error":
|
|
463
|
+
console.error(pc5.red(`\u2717 ${event.text}`));
|
|
464
|
+
break;
|
|
465
|
+
case "result":
|
|
466
|
+
case "raw":
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function registerRun(program2) {
|
|
472
|
+
program2.command("run").description("Run one prompt headlessly and stream the result").argument("<prompt...>", "what to build or change").option("-e, --engine <id>", "engine to use (see squint engines)").option("-m, --model <name>", "model override for the engine").option("--mode <mode>", "plan (read-only) \xB7 safe (default) \xB7 yolo (no friction)").option("--no-brief", "send the prompt without the squint design brief").option("--json", "emit normalized agent events as ndjson").action(
|
|
473
|
+
async (promptWords, options) => {
|
|
474
|
+
const cwd = process.cwd();
|
|
475
|
+
if (options.mode && !["plan", "safe", "yolo"].includes(options.mode)) {
|
|
476
|
+
console.error(pc5.red("\u2717 --mode must be plan, safe, or yolo"));
|
|
477
|
+
process.exitCode = 1;
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const mode = options.mode;
|
|
481
|
+
const config = loadConfig(defaultPaths(cwd));
|
|
482
|
+
const engineId = resolveEngineId(config, options.engine);
|
|
483
|
+
const engine = getEngine(engineId);
|
|
484
|
+
const model = resolveModel(config, engineId, options.model);
|
|
485
|
+
const ask = promptWords.join(" ");
|
|
486
|
+
const prompt = composePrompt(ask, { cwd, noBrief: !options.brief });
|
|
487
|
+
const onEvent = options.json ? (event) => {
|
|
488
|
+
if (event.type !== "delta") console.log(JSON.stringify(event));
|
|
489
|
+
} : createPrinter();
|
|
490
|
+
if (!options.json)
|
|
491
|
+
console.log(
|
|
492
|
+
pc5.dim(`squint \xB7 ${engine.id}${model ? ` \xB7 ${model}` : ""}${mode && mode !== "safe" ? ` \xB7 ${mode}` : ""}`)
|
|
493
|
+
);
|
|
494
|
+
const result = await runAgent(engine, { prompt, cwd, model, mode }, onEvent);
|
|
495
|
+
if (options.json) {
|
|
496
|
+
console.log(JSON.stringify({ type: "summary", ...result }));
|
|
497
|
+
} else if (result.ok) {
|
|
498
|
+
const cost = result.costUsd !== void 0 ? ` \xB7 $${result.costUsd.toFixed(2)}` : "";
|
|
499
|
+
const secs = result.durationMs !== void 0 ? ` \xB7 ${(result.durationMs / 1e3).toFixed(0)}s` : "";
|
|
500
|
+
console.log(pc5.green(`\u2713 done${secs}${cost}`));
|
|
501
|
+
}
|
|
502
|
+
if (!result.ok) process.exitCode = 1;
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/cli/scaffold.ts
|
|
508
|
+
import pc6 from "picocolors";
|
|
509
|
+
function registerScaffold(program2) {
|
|
510
|
+
program2.command("init").description("Scaffold a new Vite + React + TS + Tailwind app with token-first CSS").argument("[dir]", "target directory", ".").option("--force", "write into a non-empty directory").option("--no-install", "skip npm install").action(async (dir, options) => {
|
|
511
|
+
const { installDependencies, writeTemplate } = await import("./init-LSYB32NS.js");
|
|
512
|
+
let result;
|
|
513
|
+
try {
|
|
514
|
+
result = writeTemplate(dir, { force: options.force });
|
|
515
|
+
} catch (err) {
|
|
516
|
+
console.error(pc6.red(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
|
|
517
|
+
process.exitCode = 1;
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
console.log(pc6.green(`\u2713 scaffolded ${result.files.length} files in ${result.dir}`));
|
|
521
|
+
if (options.install) {
|
|
522
|
+
console.log(pc6.dim("installing dependencies\u2026"));
|
|
523
|
+
const ok = await installDependencies(result.dir);
|
|
524
|
+
if (!ok) {
|
|
525
|
+
console.error(pc6.red("\u2717 npm install failed \u2014 run it manually"));
|
|
526
|
+
process.exitCode = 1;
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const cd = result.dir === "." ? "" : `cd ${result.dir} && `;
|
|
531
|
+
console.log(`
|
|
532
|
+
Next: ${pc6.bold(`${cd}${options.install ? "" : "npm install && "}squint`)}`);
|
|
533
|
+
console.log(pc6.dim("then describe what to build \u2014 /dev starts the preview server"));
|
|
534
|
+
});
|
|
535
|
+
program2.command("tag").description("Add the element picker to this Vite app (Alt+S in the browser \u2192 click \u2192 file:line:col)").action(async () => {
|
|
536
|
+
const fs2 = await import("fs");
|
|
537
|
+
const nodePath = await import("path");
|
|
538
|
+
const { patchViteConfig, TAGGER_FILENAME, TAGGER_SOURCE } = await import("./source-ZZU245VN.js");
|
|
539
|
+
const cwd = process.cwd();
|
|
540
|
+
const taggerPath = nodePath.join(cwd, TAGGER_FILENAME);
|
|
541
|
+
fs2.writeFileSync(taggerPath, TAGGER_SOURCE);
|
|
542
|
+
console.log(pc6.green(`\u2713 ${TAGGER_FILENAME} written`));
|
|
543
|
+
const configPath = ["vite.config.ts", "vite.config.js", "vite.config.mjs"].map((name) => nodePath.join(cwd, name)).find((candidate) => fs2.existsSync(candidate));
|
|
544
|
+
if (!configPath) {
|
|
545
|
+
console.log(pc6.yellow("\u25CB no vite config found \u2014 add the plugin manually:"));
|
|
546
|
+
console.log(pc6.dim(` import squintTagger from './${TAGGER_FILENAME}'
|
|
547
|
+
plugins: [squintTagger(), \u2026]`));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const source = fs2.readFileSync(configPath, "utf8");
|
|
551
|
+
const patched = patchViteConfig(source);
|
|
552
|
+
if (patched === "already") {
|
|
553
|
+
console.log(pc6.dim("vite config already wired"));
|
|
554
|
+
} else if (patched === null) {
|
|
555
|
+
console.log(pc6.yellow(`\u25CB could not patch ${nodePath.basename(configPath)} automatically \u2014 add:`));
|
|
556
|
+
console.log(pc6.dim(` import squintTagger from './${TAGGER_FILENAME}'
|
|
557
|
+
plugins: [squintTagger(), \u2026]`));
|
|
558
|
+
} else {
|
|
559
|
+
fs2.writeFileSync(configPath, patched);
|
|
560
|
+
console.log(pc6.green(`\u2713 ${nodePath.basename(configPath)} wired`));
|
|
561
|
+
}
|
|
562
|
+
console.log(
|
|
563
|
+
pc6.dim("\nin the running app: Alt+S \u2192 click elements to pin, alt+enter copies all \u2014 paste into squint")
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// src/cli/tui.tsx
|
|
569
|
+
import { render } from "ink";
|
|
570
|
+
|
|
154
571
|
// src/tui/App.tsx
|
|
155
572
|
import { Box as Box2, Static, Text as Text3, useApp, useInput } from "ink";
|
|
156
573
|
import path3 from "path";
|
|
@@ -209,6 +626,7 @@ var Session = class {
|
|
|
209
626
|
fixAttempts = 0;
|
|
210
627
|
reviewTipShown = false;
|
|
211
628
|
lastPulse = null;
|
|
629
|
+
autoReviewedThisAsk = false;
|
|
212
630
|
startedAt = Date.now();
|
|
213
631
|
subscribe(listener) {
|
|
214
632
|
this.listeners.add(listener);
|
|
@@ -523,7 +941,20 @@ ${errors.slice(-5).join("\n")}`);
|
|
|
523
941
|
this.push("status", "/fix sends open problems to the engine \xB7 /problems lists them");
|
|
524
942
|
} else if (probe) {
|
|
525
943
|
this.clearProblems("runtime");
|
|
526
|
-
await this.visualPulse(probe.pulsePath);
|
|
944
|
+
const pct = await this.visualPulse(probe.pulsePath);
|
|
945
|
+
if (this.opts.autoReview && pct !== null && pct >= 10 && !this.autoReviewedThisAsk) {
|
|
946
|
+
this.autoReviewedThisAsk = true;
|
|
947
|
+
this.push("status", `auto-review: the page changed ${pct.toFixed(0)}% \u2014 capturing for self-critique`);
|
|
948
|
+
this.notify({ running: false });
|
|
949
|
+
const captureResult = await this.capture();
|
|
950
|
+
if (captureResult) {
|
|
951
|
+
await this.runTurn(
|
|
952
|
+
buildReviewPrompt(captureResult.shots, void 0, captureResult.runtime, captureResult.a11y),
|
|
953
|
+
"\u{1F441} auto-review rendered UI"
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
527
958
|
}
|
|
528
959
|
}
|
|
529
960
|
if (!this.reviewTipShown && this.state.devUrl) {
|
|
@@ -537,6 +968,7 @@ ${errors.slice(-5).join("\n")}`);
|
|
|
537
968
|
}
|
|
538
969
|
async submit(ask) {
|
|
539
970
|
this.fixAttempts = 0;
|
|
971
|
+
this.autoReviewedThisAsk = false;
|
|
540
972
|
const snapshot = takeSnapshot(this.opts.cwd);
|
|
541
973
|
if (snapshot) {
|
|
542
974
|
this.checkpoints.push({
|
|
@@ -580,25 +1012,26 @@ ${errors.slice(-5).join("\n")}`);
|
|
|
580
1012
|
* Informational — changes are usually intended; surprises shouldn't be.
|
|
581
1013
|
*/
|
|
582
1014
|
async visualPulse(pulsePath) {
|
|
583
|
-
if (!pulsePath) return;
|
|
1015
|
+
if (!pulsePath) return null;
|
|
584
1016
|
let current;
|
|
585
1017
|
try {
|
|
586
1018
|
current = (await import("fs")).readFileSync(pulsePath);
|
|
587
1019
|
} catch {
|
|
588
|
-
return;
|
|
1020
|
+
return null;
|
|
589
1021
|
}
|
|
590
1022
|
const previous = this.lastPulse;
|
|
591
1023
|
this.lastPulse = current;
|
|
592
1024
|
if (!previous) {
|
|
593
1025
|
this.push("status", "visual pulse: baseline captured");
|
|
594
|
-
return;
|
|
1026
|
+
return null;
|
|
595
1027
|
}
|
|
596
1028
|
const pct = await comparePulse(previous, current);
|
|
597
|
-
if (pct === null) return;
|
|
1029
|
+
if (pct === null) return null;
|
|
598
1030
|
this.push(
|
|
599
1031
|
"status",
|
|
600
1032
|
pct < 0.5 ? "visual pulse: stable vs last turn" : `visual pulse: ${pct.toFixed(1)}% of the page changed vs last turn`
|
|
601
1033
|
);
|
|
1034
|
+
return pct;
|
|
602
1035
|
}
|
|
603
1036
|
/** Screenshot the running app (and watch its runtime where CDP is available). */
|
|
604
1037
|
async capture() {
|
|
@@ -691,6 +1124,23 @@ They are objective defects, not style preferences.`
|
|
|
691
1124
|
break;
|
|
692
1125
|
case "dev": {
|
|
693
1126
|
const dev = this.devServer();
|
|
1127
|
+
if (arg === "logs") {
|
|
1128
|
+
const tail = dev.tail(15);
|
|
1129
|
+
this.push("status", tail.length > 0 ? tail.join("\n") : "no dev server output captured yet");
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
if (arg === "restart") {
|
|
1133
|
+
if (dev.state !== "stopped") dev.stop();
|
|
1134
|
+
this.notify({ devUrl: null });
|
|
1135
|
+
const devCommand = detectDevCommand(this.opts.cwd);
|
|
1136
|
+
if (!devCommand) {
|
|
1137
|
+
this.push("error", "no dev/start script found in package.json");
|
|
1138
|
+
} else {
|
|
1139
|
+
dev.start(devCommand);
|
|
1140
|
+
this.push("status", `dev server restarting \xB7 ${devCommand.display}`);
|
|
1141
|
+
}
|
|
1142
|
+
break;
|
|
1143
|
+
}
|
|
694
1144
|
if (dev.state === "stopped" || dev.state === "crashed") {
|
|
695
1145
|
const devCommand = detectDevCommand(this.opts.cwd);
|
|
696
1146
|
if (!devCommand) {
|
|
@@ -945,7 +1395,7 @@ They are objective defects, not style preferences.`
|
|
|
945
1395
|
this.notify({ items: [], totals: { costUsd: 0, turns: 0 } });
|
|
946
1396
|
break;
|
|
947
1397
|
case "help": {
|
|
948
|
-
void import("./commands-
|
|
1398
|
+
void import("./commands-TYAKF276.js").then(({ commandHelp }) => this.push("status", commandHelp()));
|
|
949
1399
|
break;
|
|
950
1400
|
}
|
|
951
1401
|
case "quit":
|
|
@@ -1287,6 +1737,7 @@ function App({
|
|
|
1287
1737
|
autoFix,
|
|
1288
1738
|
autoProbe,
|
|
1289
1739
|
autoCheck,
|
|
1740
|
+
autoReview,
|
|
1290
1741
|
bell,
|
|
1291
1742
|
budgetUsd,
|
|
1292
1743
|
initialTheme
|
|
@@ -1304,6 +1755,7 @@ function App({
|
|
|
1304
1755
|
autoFix,
|
|
1305
1756
|
autoProbe,
|
|
1306
1757
|
autoCheck,
|
|
1758
|
+
autoReview,
|
|
1307
1759
|
budgetUsd,
|
|
1308
1760
|
// Delay lets the goodbye summary land in the Static scrollback.
|
|
1309
1761
|
onQuit: () => setTimeout(() => exit(), 60)
|
|
@@ -1492,409 +1944,49 @@ function App({
|
|
|
1492
1944
|
] }) });
|
|
1493
1945
|
}
|
|
1494
1946
|
|
|
1495
|
-
// src/cli.tsx
|
|
1947
|
+
// src/cli/tui.tsx
|
|
1496
1948
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
1500
|
-
program.command("run").description("Run one prompt headlessly and stream the result").argument("<prompt...>", "what to build or change").option("-e, --engine <id>", "engine to use (see squint engines)").option("-m, --model <name>", "model override for the engine").option("--mode <mode>", "plan (read-only) \xB7 safe (default) \xB7 yolo (no friction)").option("--no-brief", "send the prompt without the squint design brief").option("--json", "emit normalized agent events as ndjson").action(
|
|
1501
|
-
async (promptWords, options) => {
|
|
1949
|
+
function registerTui(program2) {
|
|
1950
|
+
program2.action(async () => {
|
|
1502
1951
|
const cwd = process.cwd();
|
|
1503
|
-
if (options.mode && !["plan", "safe", "yolo"].includes(options.mode)) {
|
|
1504
|
-
console.error(pc.red("\u2717 --mode must be plan, safe, or yolo"));
|
|
1505
|
-
process.exitCode = 1;
|
|
1506
|
-
return;
|
|
1507
|
-
}
|
|
1508
|
-
const mode = options.mode;
|
|
1509
1952
|
const config = loadConfig(defaultPaths(cwd));
|
|
1510
|
-
const engineId = resolveEngineId(config
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
if (event.type !== "delta") console.log(JSON.stringify(event));
|
|
1517
|
-
} : createPrinter();
|
|
1518
|
-
if (!options.json)
|
|
1519
|
-
console.log(pc.dim(`squint \xB7 ${engine.id}${model ? ` \xB7 ${model}` : ""}${mode && mode !== "safe" ? ` \xB7 ${mode}` : ""}`));
|
|
1520
|
-
const result = await runAgent(engine, { prompt, cwd, model, mode }, onEvent);
|
|
1521
|
-
if (options.json) {
|
|
1522
|
-
console.log(JSON.stringify({ type: "summary", ...result }));
|
|
1523
|
-
} else if (result.ok) {
|
|
1524
|
-
const cost = result.costUsd !== void 0 ? ` \xB7 $${result.costUsd.toFixed(2)}` : "";
|
|
1525
|
-
const secs = result.durationMs !== void 0 ? ` \xB7 ${(result.durationMs / 1e3).toFixed(0)}s` : "";
|
|
1526
|
-
console.log(pc.green(`\u2713 done${secs}${cost}`));
|
|
1527
|
-
}
|
|
1528
|
-
if (!result.ok) process.exitCode = 1;
|
|
1529
|
-
}
|
|
1530
|
-
);
|
|
1531
|
-
program.command("engines").description("List engines: installed, streaming, session resume").action(() => {
|
|
1532
|
-
console.log(pc.dim(" id name stream resume where"));
|
|
1533
|
-
for (const { engine, path: binaryPath } of detectEngines()) {
|
|
1534
|
-
const status = binaryPath ? pc.green("\u2713") : pc.red("\u2717");
|
|
1535
|
-
const stream = engine.createParser ? pc.green("yes") : pc.dim("text");
|
|
1536
|
-
const resume = engine.supportsResume ? pc.green("yes") : pc.dim("no");
|
|
1537
|
-
const location = binaryPath ?? pc.dim(`not found \u2014 ${engine.install}`);
|
|
1538
|
-
console.log(
|
|
1539
|
-
`${status} ${engine.id.padEnd(10)} ${engine.name.padEnd(14)} ${stream.padEnd(15)} ${resume.padEnd(14)} ${location}`
|
|
1540
|
-
);
|
|
1541
|
-
}
|
|
1542
|
-
console.log(pc.dim("\nplan/safe/yolo modes map onto every engine \xB7 squint doctor --probe verifies auth"));
|
|
1543
|
-
});
|
|
1544
|
-
program.command("doctor").description("Check squint prerequisites and engine availability").option("--probe", "run each detected engine with a one-word prompt to verify auth works").action(async (options) => {
|
|
1545
|
-
const [major] = process.versions.node.split(".");
|
|
1546
|
-
const nodeOk = Number(major) >= 22;
|
|
1547
|
-
console.log(`${nodeOk ? pc.green("\u2713") : pc.red("\u2717")} node ${process.versions.node}${nodeOk ? "" : " (need >= 22)"}`);
|
|
1548
|
-
const detected = detectEngines();
|
|
1549
|
-
for (const { engine, path: binaryPath } of detected) {
|
|
1550
|
-
const status = binaryPath ? pc.green("\u2713") : pc.yellow("\u25CB");
|
|
1551
|
-
console.log(`${status} ${engine.name}${binaryPath ? "" : pc.dim(` \u2014 install: ${engine.install}`)}`);
|
|
1552
|
-
}
|
|
1553
|
-
if (options.probe) {
|
|
1554
|
-
const { runAgent: runAgent2 } = await import("./run-S772BPMZ.js");
|
|
1555
|
-
console.log(pc.dim("\nprobing engines with a one-word prompt (verifies auth end to end)\u2026"));
|
|
1556
|
-
for (const { engine, path: binaryPath } of detected) {
|
|
1557
|
-
if (!binaryPath) continue;
|
|
1558
|
-
const startedAt = Date.now();
|
|
1559
|
-
const abort = new AbortController();
|
|
1560
|
-
const timer = setTimeout(() => abort.abort(), 9e4);
|
|
1561
|
-
const result = await runAgent2(
|
|
1562
|
-
engine,
|
|
1563
|
-
{ prompt: "Reply with exactly: ok", cwd: process.cwd() },
|
|
1564
|
-
() => {
|
|
1565
|
-
},
|
|
1566
|
-
abort.signal
|
|
1567
|
-
);
|
|
1568
|
-
clearTimeout(timer);
|
|
1569
|
-
const secs = ((Date.now() - startedAt) / 1e3).toFixed(1);
|
|
1570
|
-
const detail = (result.error ?? "failed").split("\n").filter((l) => l.trim()).at(-1) ?? "failed";
|
|
1571
|
-
console.log(
|
|
1572
|
-
result.ok ? `${pc.green("\u2713")} ${engine.id.padEnd(10)} responded in ${secs}s` : `${pc.red("\u2717")} ${engine.id.padEnd(10)} ${pc.dim(detail.slice(0, 110))}`
|
|
1573
|
-
);
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
const { findChrome: findChrome2 } = await import("./chrome-NVU47PLK.js");
|
|
1577
|
-
const { hasWebSocket } = await import("./cdp-SFWNP7OA.js");
|
|
1578
|
-
const chrome = findChrome2();
|
|
1579
|
-
console.log(
|
|
1580
|
-
chrome ? `${pc.green("\u2713")} Chrome ${pc.dim(chrome)}` : `${pc.yellow("\u25CB")} Chrome ${pc.dim("\u2014 screenshots and runtime probing disabled")}`
|
|
1581
|
-
);
|
|
1582
|
-
console.log(
|
|
1583
|
-
hasWebSocket() ? `${pc.green("\u2713")} WebSocket ${pc.dim("runtime console/network capture available")}` : `${pc.yellow("\u25CB")} WebSocket ${pc.dim("\u2014 node 22+ enables runtime capture; screenshots still work")}`
|
|
1584
|
-
);
|
|
1585
|
-
const available = detected.filter((d) => d.path !== null);
|
|
1586
|
-
if (available.length === 0) {
|
|
1587
|
-
console.log(pc.red("\nNo engines found. Install at least one to use squint."));
|
|
1588
|
-
process.exitCode = 1;
|
|
1589
|
-
} else {
|
|
1590
|
-
console.log(pc.dim(`
|
|
1591
|
-
${available.length} engine(s) ready.`));
|
|
1592
|
-
}
|
|
1593
|
-
});
|
|
1594
|
-
program.command("init").description("Scaffold a new Vite + React + TS + Tailwind app with token-first CSS").argument("[dir]", "target directory", ".").option("--force", "write into a non-empty directory").option("--no-install", "skip npm install").action(async (dir, options) => {
|
|
1595
|
-
const { installDependencies, writeTemplate } = await import("./init-LSYB32NS.js");
|
|
1596
|
-
let result;
|
|
1597
|
-
try {
|
|
1598
|
-
result = writeTemplate(dir, { force: options.force });
|
|
1599
|
-
} catch (err) {
|
|
1600
|
-
console.error(pc.red(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
|
|
1601
|
-
process.exitCode = 1;
|
|
1602
|
-
return;
|
|
1603
|
-
}
|
|
1604
|
-
console.log(pc.green(`\u2713 scaffolded ${result.files.length} files in ${result.dir}`));
|
|
1605
|
-
if (options.install) {
|
|
1606
|
-
console.log(pc.dim("installing dependencies\u2026"));
|
|
1607
|
-
const ok = await installDependencies(result.dir);
|
|
1608
|
-
if (!ok) {
|
|
1609
|
-
console.error(pc.red("\u2717 npm install failed \u2014 run it manually"));
|
|
1610
|
-
process.exitCode = 1;
|
|
1611
|
-
return;
|
|
1953
|
+
const engineId = resolveEngineId(config);
|
|
1954
|
+
const model = resolveModel(config, engineId);
|
|
1955
|
+
let theme2 = config.theme;
|
|
1956
|
+
if (!theme2 && !process.env.NO_COLOR) {
|
|
1957
|
+
const { detectBackground } = await import("./background-J7OQTYEC.js");
|
|
1958
|
+
if (await detectBackground() === "light") theme2 = "light";
|
|
1612
1959
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
for (const skill of skills) {
|
|
1631
|
-
console.log(`${pc.green("\u2713")} ${skill.name.padEnd(20)} ${pc.dim(`triggers: ${skill.triggers.join(", ")}`)}`);
|
|
1632
|
-
}
|
|
1633
|
-
});
|
|
1634
|
-
skillsCommand.command("init").description("Scaffold .squint/rules.md and an example skill").action(async () => {
|
|
1635
|
-
const fs2 = await import("fs");
|
|
1636
|
-
const nodePath = await import("path");
|
|
1637
|
-
const cwd = process.cwd();
|
|
1638
|
-
const skillsDir = nodePath.join(cwd, ".squint", "skills");
|
|
1639
|
-
fs2.mkdirSync(skillsDir, { recursive: true });
|
|
1640
|
-
const rules = nodePath.join(cwd, ".squint", "rules.md");
|
|
1641
|
-
if (!fs2.existsSync(rules)) {
|
|
1642
|
-
fs2.writeFileSync(
|
|
1643
|
-
rules,
|
|
1644
|
-
"# Project rules\n\nThese ride along on every squint ask. Keep them short \u2014 cut anything that would not cause a mistake if removed.\n"
|
|
1645
|
-
);
|
|
1646
|
-
console.log(pc.green("\u2713 .squint/rules.md"));
|
|
1647
|
-
}
|
|
1648
|
-
const example = nodePath.join(skillsDir, "example.md");
|
|
1649
|
-
if (!fs2.existsSync(example)) {
|
|
1650
|
-
fs2.writeFileSync(
|
|
1651
|
-
example,
|
|
1652
|
-
"---\ntriggers: example, sample\n---\n\nThis note is injected only when an ask mentions one of the triggers above.\nDocument the parts of this repo an agent would otherwise rediscover every time:\nwhere state lives, which helpers to reuse, what not to touch.\n"
|
|
1653
|
-
);
|
|
1654
|
-
console.log(pc.green("\u2713 .squint/skills/example.md"));
|
|
1655
|
-
}
|
|
1656
|
-
console.log(pc.dim("rules are always-on; skills inject when an ask mentions a trigger"));
|
|
1657
|
-
});
|
|
1658
|
-
program.command("tag").description("Add the element picker to this Vite app (Alt+S in the browser \u2192 click \u2192 file:line:col)").action(async () => {
|
|
1659
|
-
const fs2 = await import("fs");
|
|
1660
|
-
const nodePath = await import("path");
|
|
1661
|
-
const { patchViteConfig, TAGGER_FILENAME, TAGGER_SOURCE } = await import("./source-ZZU245VN.js");
|
|
1662
|
-
const cwd = process.cwd();
|
|
1663
|
-
const taggerPath = nodePath.join(cwd, TAGGER_FILENAME);
|
|
1664
|
-
fs2.writeFileSync(taggerPath, TAGGER_SOURCE);
|
|
1665
|
-
console.log(pc.green(`\u2713 ${TAGGER_FILENAME} written`));
|
|
1666
|
-
const configPath = ["vite.config.ts", "vite.config.js", "vite.config.mjs"].map((name) => nodePath.join(cwd, name)).find((candidate) => fs2.existsSync(candidate));
|
|
1667
|
-
if (!configPath) {
|
|
1668
|
-
console.log(pc.yellow("\u25CB no vite config found \u2014 add the plugin manually:"));
|
|
1669
|
-
console.log(pc.dim(` import squintTagger from './${TAGGER_FILENAME}'
|
|
1670
|
-
plugins: [squintTagger(), \u2026]`));
|
|
1671
|
-
return;
|
|
1672
|
-
}
|
|
1673
|
-
const source = fs2.readFileSync(configPath, "utf8");
|
|
1674
|
-
const patched = patchViteConfig(source);
|
|
1675
|
-
if (patched === "already") {
|
|
1676
|
-
console.log(pc.dim("vite config already wired"));
|
|
1677
|
-
} else if (patched === null) {
|
|
1678
|
-
console.log(pc.yellow(`\u25CB could not patch ${nodePath.basename(configPath)} automatically \u2014 add:`));
|
|
1679
|
-
console.log(pc.dim(` import squintTagger from './${TAGGER_FILENAME}'
|
|
1680
|
-
plugins: [squintTagger(), \u2026]`));
|
|
1681
|
-
} else {
|
|
1682
|
-
fs2.writeFileSync(configPath, patched);
|
|
1683
|
-
console.log(pc.green(`\u2713 ${nodePath.basename(configPath)} wired`));
|
|
1684
|
-
}
|
|
1685
|
-
console.log(pc.dim("\nin the running app: Alt+S \u2192 click an element \u2192 paste the copied file:line:col into squint"));
|
|
1686
|
-
});
|
|
1687
|
-
var variantsCommand = program.command("variants").description("Parallel design explorations \u2014 one aesthetic family each, pick with your eyes");
|
|
1688
|
-
variantsCommand.command("gen").description("Generate n variants of one ask in parallel (n engine runs \u2014 n\xD7 cost)").argument("<n>", "how many variants (max 4)").argument("<prompt...>", "what to build").option("-e, --engine <id>", "engine to use").option("-m, --model <name>", "model override").option("--no-shots", "skip the screenshot pass").action(
|
|
1689
|
-
async (nRaw, promptWords, options) => {
|
|
1690
|
-
const cwd = process.cwd();
|
|
1691
|
-
const n = Number.parseInt(nRaw, 10);
|
|
1692
|
-
if (!Number.isInteger(n) || n < 2 || n > 4) {
|
|
1693
|
-
console.error(pc.red("\u2717 n must be 2\u20134"));
|
|
1694
|
-
process.exitCode = 1;
|
|
1695
|
-
return;
|
|
1696
|
-
}
|
|
1697
|
-
const { isGitRepo: isGitRepo2 } = await import("./snapshot-H6VYFHLN.js");
|
|
1698
|
-
if (!isGitRepo2(cwd)) {
|
|
1699
|
-
console.error(pc.red("\u2717 variants need a git repo with at least one commit"));
|
|
1700
|
-
process.exitCode = 1;
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
const { runVariants: runVariants2, cleanVariants: cleanVariants2 } = await import("./variants-2IM274AL.js");
|
|
1704
|
-
const config = loadConfig(defaultPaths(cwd));
|
|
1705
|
-
const engineId = resolveEngineId(config, options.engine);
|
|
1706
|
-
const engine = getEngine(engineId);
|
|
1707
|
-
const model = resolveModel(config, engineId, options.model);
|
|
1708
|
-
const ask = promptWords.join(" ");
|
|
1709
|
-
cleanVariants2(cwd);
|
|
1710
|
-
console.log(pc.dim(`generating ${n} directions in parallel via ${engine.id} \u2014 this runs ${n} engine sessions`));
|
|
1711
|
-
const runs = await runVariants2(
|
|
1712
|
-
cwd,
|
|
1713
|
-
ask,
|
|
1714
|
-
n,
|
|
1715
|
-
engine,
|
|
1716
|
-
model,
|
|
1717
|
-
(familyId, text) => console.log(`${pc.cyan(familyId.padEnd(18))} ${pc.dim(text)}`)
|
|
1718
|
-
);
|
|
1719
|
-
const succeeded = runs.filter((r) => r.result.ok);
|
|
1720
|
-
if (options.shots && succeeded.length > 0) {
|
|
1721
|
-
const { screenshotVariants: screenshotVariants2 } = await import("./shots-J7JG2FC4.js");
|
|
1722
|
-
console.log(pc.dim("capturing screenshots\u2026"));
|
|
1723
|
-
const shots = await screenshotVariants2(cwd, succeeded.map((r) => r.variant));
|
|
1724
|
-
for (const shot of shots) {
|
|
1725
|
-
console.log(`${pc.green("\u2713")} ${shot.familyId.padEnd(18)} ${shot.path ?? pc.dim(shot.error ?? "")}`);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
console.log(
|
|
1729
|
-
`
|
|
1730
|
-
${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 ` + pc.bold("squint variants apply <id>") + pc.dim(" applies the winner, squint variants clean discards all")
|
|
1960
|
+
render(
|
|
1961
|
+
/* @__PURE__ */ jsx4(
|
|
1962
|
+
App,
|
|
1963
|
+
{
|
|
1964
|
+
cwd,
|
|
1965
|
+
initialEngine: engineId,
|
|
1966
|
+
initialModel: model,
|
|
1967
|
+
autoDev: config.autoDev,
|
|
1968
|
+
autoFix: config.autoFix,
|
|
1969
|
+
autoProbe: config.autoProbe,
|
|
1970
|
+
autoCheck: config.autoCheck,
|
|
1971
|
+
autoReview: config.autoReview,
|
|
1972
|
+
bell: config.bell,
|
|
1973
|
+
budgetUsd: config.budgetUsd,
|
|
1974
|
+
initialTheme: theme2
|
|
1975
|
+
}
|
|
1976
|
+
)
|
|
1731
1977
|
);
|
|
1732
|
-
if (succeeded.length === 0) process.exitCode = 1;
|
|
1733
|
-
}
|
|
1734
|
-
);
|
|
1735
|
-
variantsCommand.command("list").description("List generated variants").action(async () => {
|
|
1736
|
-
const { listVariants: listVariants2 } = await import("./variants-2IM274AL.js");
|
|
1737
|
-
const ids = listVariants2(process.cwd());
|
|
1738
|
-
if (ids.length === 0) {
|
|
1739
|
-
console.log(pc.dim('no variants \u2014 squint variants gen <n> "<ask>"'));
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
for (const id of ids) console.log(id);
|
|
1743
|
-
});
|
|
1744
|
-
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) => {
|
|
1745
|
-
const { applyVariant: applyVariant2, cleanVariants: cleanVariants2 } = await import("./variants-2IM274AL.js");
|
|
1746
|
-
const cwd = process.cwd();
|
|
1747
|
-
const result = applyVariant2(cwd, id);
|
|
1748
|
-
if (!result.ok) {
|
|
1749
|
-
console.error(pc.red(`\u2717 ${result.detail}`));
|
|
1750
|
-
process.exitCode = 1;
|
|
1751
|
-
return;
|
|
1752
|
-
}
|
|
1753
|
-
cleanVariants2(cwd);
|
|
1754
|
-
console.log(pc.green(`\u2713 applied ${id} to the working tree`) + pc.dim(" \u2014 review with git diff"));
|
|
1755
|
-
});
|
|
1756
|
-
variantsCommand.command("clean").description("Discard all variants").action(async () => {
|
|
1757
|
-
const { cleanVariants: cleanVariants2 } = await import("./variants-2IM274AL.js");
|
|
1758
|
-
const count = cleanVariants2(process.cwd());
|
|
1759
|
-
console.log(pc.dim(`removed ${count} variant(s)`));
|
|
1760
|
-
});
|
|
1761
|
-
program.command("brief").description("Set a committed design direction for this project (.squint/brief.md)").argument("[family]", "aesthetic family id (omit to list)").option("--force", "overwrite an existing project brief").action(async (familyId, options) => {
|
|
1762
|
-
const fs2 = await import("fs");
|
|
1763
|
-
const nodePath = await import("path");
|
|
1764
|
-
const { FAMILIES, getFamily, renderFamilyBrief } = await import("./families-2G5SLLY7.js");
|
|
1765
|
-
if (!familyId) {
|
|
1766
|
-
console.log(pc.bold("Aesthetic families") + pc.dim(" \u2014 squint brief <id>\n"));
|
|
1767
|
-
for (const family2 of FAMILIES) {
|
|
1768
|
-
console.log(`${pc.green(family2.id.padEnd(18))} ${family2.name.padEnd(22)} ${pc.dim(family2.summary)}`);
|
|
1769
|
-
}
|
|
1770
|
-
console.log(pc.dim("\nThe brief wraps every ask; edit .squint/brief.md to remix."));
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
const family = getFamily(familyId);
|
|
1774
|
-
if (!family) {
|
|
1775
|
-
console.error(pc.red(`\u2717 unknown family "${familyId}" \u2014 run squint brief to list`));
|
|
1776
|
-
process.exitCode = 1;
|
|
1777
|
-
return;
|
|
1778
|
-
}
|
|
1779
|
-
const target = nodePath.join(process.cwd(), ".squint", "brief.md");
|
|
1780
|
-
if (fs2.existsSync(target) && !options.force) {
|
|
1781
|
-
console.error(pc.red(`\u2717 ${target} exists \u2014 use --force to overwrite`));
|
|
1782
|
-
process.exitCode = 1;
|
|
1783
|
-
return;
|
|
1784
|
-
}
|
|
1785
|
-
fs2.mkdirSync(nodePath.dirname(target), { recursive: true });
|
|
1786
|
-
fs2.writeFileSync(target, renderFamilyBrief(family) + "\n");
|
|
1787
|
-
console.log(pc.green(`\u2713 ${family.name} direction written to .squint/brief.md`));
|
|
1788
|
-
console.log(pc.dim("every squint ask in this repo now holds this direction \u2014 edit the file to remix"));
|
|
1789
|
-
});
|
|
1790
|
-
program.command("check").description("Run this project\u2019s quality gates (typecheck, lint, test, build)").action(async () => {
|
|
1791
|
-
const { detectGates: detectGates2, runGates: runGates2 } = await import("./gates-ZC67NMW3.js");
|
|
1792
|
-
const cwd = process.cwd();
|
|
1793
|
-
const gates = detectGates2(cwd);
|
|
1794
|
-
if (gates.length === 0) {
|
|
1795
|
-
console.log(pc.dim("no gates detected (no package.json scripts, tsconfig, or eslint config)"));
|
|
1796
|
-
return;
|
|
1797
|
-
}
|
|
1798
|
-
console.log(pc.dim(`running ${gates.map((g) => g.id).join(" \u2192 ")}`));
|
|
1799
|
-
const results = await runGates2(cwd, gates, (result) => {
|
|
1800
|
-
const mark = result.ok ? pc.green("\u2713") : pc.red("\u2717");
|
|
1801
|
-
console.log(`${mark} ${result.gate.id.padEnd(10)} ${pc.dim(`${(result.durationMs / 1e3).toFixed(1)}s \xB7 ${result.gate.display}`)}`);
|
|
1802
|
-
if (!result.ok) console.log(pc.dim(result.outputTail.split("\n").slice(-12).join("\n")));
|
|
1803
1978
|
});
|
|
1804
|
-
if (results.some((r) => !r.ok)) process.exitCode = 1;
|
|
1805
|
-
});
|
|
1806
|
-
program.command("shot").description("Screenshot a running app at mobile/tablet/desktop viewports").argument("<url>", "URL of the running app (e.g. http://localhost:5173)").action(async (url) => {
|
|
1807
|
-
const { captureViewports: captureViewports2 } = await import("./preview-UCH7H5R5.js");
|
|
1808
|
-
const result = await captureViewports2(process.cwd(), url);
|
|
1809
|
-
if (!result) {
|
|
1810
|
-
console.error(pc.red("\u2717 no Chrome/Chromium found"));
|
|
1811
|
-
process.exitCode = 1;
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
for (const shot of result.shots) console.log(`${pc.green("\u2713")} ${shot.name.padEnd(8)} ${shot.path}`);
|
|
1815
|
-
for (const error of result.errors) console.error(pc.red(`\u2717 ${error}`));
|
|
1816
|
-
if (result.shots.length === 0) process.exitCode = 1;
|
|
1817
|
-
});
|
|
1818
|
-
var configCommand = program.command("config").description("Read or change squint configuration");
|
|
1819
|
-
configCommand.command("get").description("Print the resolved configuration").action(() => {
|
|
1820
|
-
const paths = defaultPaths(process.cwd());
|
|
1821
|
-
console.log(JSON.stringify(loadConfig(paths), null, 2));
|
|
1822
|
-
});
|
|
1823
|
-
configCommand.command("set").description("Set a value (keys: engine, models.<engineId>)").argument("<key>").argument("<value>").option("--project", "write to this project (.squint/config.json) instead of global").action((key, value, options) => {
|
|
1824
|
-
const paths = defaultPaths(process.cwd());
|
|
1825
|
-
const file = options.project ? paths.projectFile : paths.globalFile;
|
|
1826
|
-
setConfigValue(file, key, value);
|
|
1827
|
-
console.log(pc.green(`\u2713 ${key} = ${value}`) + pc.dim(` (${file})`));
|
|
1828
|
-
});
|
|
1829
|
-
configCommand.command("path").description("Show config file locations").action(() => {
|
|
1830
|
-
const paths = defaultPaths(process.cwd());
|
|
1831
|
-
console.log(`global ${paths.globalFile}`);
|
|
1832
|
-
console.log(`project ${paths.projectFile}`);
|
|
1833
|
-
});
|
|
1834
|
-
program.action(async () => {
|
|
1835
|
-
const cwd = process.cwd();
|
|
1836
|
-
const config = loadConfig(defaultPaths(cwd));
|
|
1837
|
-
const engineId = resolveEngineId(config);
|
|
1838
|
-
const model = resolveModel(config, engineId);
|
|
1839
|
-
let theme2 = config.theme;
|
|
1840
|
-
if (!theme2 && !process.env.NO_COLOR) {
|
|
1841
|
-
const { detectBackground } = await import("./background-J7OQTYEC.js");
|
|
1842
|
-
if (await detectBackground() === "light") theme2 = "light";
|
|
1843
|
-
}
|
|
1844
|
-
render(
|
|
1845
|
-
/* @__PURE__ */ jsx4(
|
|
1846
|
-
App,
|
|
1847
|
-
{
|
|
1848
|
-
cwd,
|
|
1849
|
-
initialEngine: engineId,
|
|
1850
|
-
initialModel: model,
|
|
1851
|
-
autoDev: config.autoDev,
|
|
1852
|
-
autoFix: config.autoFix,
|
|
1853
|
-
autoProbe: config.autoProbe,
|
|
1854
|
-
autoCheck: config.autoCheck,
|
|
1855
|
-
bell: config.bell,
|
|
1856
|
-
budgetUsd: config.budgetUsd,
|
|
1857
|
-
initialTheme: theme2
|
|
1858
|
-
}
|
|
1859
|
-
)
|
|
1860
|
-
);
|
|
1861
|
-
});
|
|
1862
|
-
function createPrinter() {
|
|
1863
|
-
let streaming = false;
|
|
1864
|
-
return (event) => {
|
|
1865
|
-
switch (event.type) {
|
|
1866
|
-
case "status":
|
|
1867
|
-
console.log(pc.dim(`\xB7 ${event.text}`));
|
|
1868
|
-
break;
|
|
1869
|
-
case "delta":
|
|
1870
|
-
streaming = true;
|
|
1871
|
-
process.stdout.write(event.text);
|
|
1872
|
-
break;
|
|
1873
|
-
case "text":
|
|
1874
|
-
if (event.streamed && streaming) {
|
|
1875
|
-
process.stdout.write("\n");
|
|
1876
|
-
} else {
|
|
1877
|
-
console.log(event.text);
|
|
1878
|
-
}
|
|
1879
|
-
streaming = false;
|
|
1880
|
-
break;
|
|
1881
|
-
case "thinking":
|
|
1882
|
-
console.log(pc.dim(pc.italic(event.text)));
|
|
1883
|
-
break;
|
|
1884
|
-
case "tool":
|
|
1885
|
-
if (streaming) {
|
|
1886
|
-
process.stdout.write("\n");
|
|
1887
|
-
streaming = false;
|
|
1888
|
-
}
|
|
1889
|
-
console.log(pc.cyan(`\u2699 ${event.name}${event.detail ? ` \xB7 ${event.detail}` : ""}`));
|
|
1890
|
-
break;
|
|
1891
|
-
case "error":
|
|
1892
|
-
console.error(pc.red(`\u2717 ${event.text}`));
|
|
1893
|
-
break;
|
|
1894
|
-
case "result":
|
|
1895
|
-
case "raw":
|
|
1896
|
-
break;
|
|
1897
|
-
}
|
|
1898
|
-
};
|
|
1899
1979
|
}
|
|
1980
|
+
|
|
1981
|
+
// src/cli.tsx
|
|
1982
|
+
var VERSION = "0.2.7";
|
|
1983
|
+
var program = new Command();
|
|
1984
|
+
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
1985
|
+
registerRun(program);
|
|
1986
|
+
registerEnv(program);
|
|
1987
|
+
registerScaffold(program);
|
|
1988
|
+
registerProject(program);
|
|
1989
|
+
registerQuality(program);
|
|
1990
|
+
registerConfig(program);
|
|
1991
|
+
registerTui(program);
|
|
1900
1992
|
program.parseAsync(process.argv);
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
probeRuntime,
|
|
11
11
|
routeShotName,
|
|
12
12
|
runtimeSummary
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-OC6RU6XH.js";
|
|
13
|
+
} from "./chunk-CHZE3UHK.js";
|
|
15
14
|
import "./chunk-B7LOERSP.js";
|
|
16
15
|
import "./chunk-BWZFACBT.js";
|
|
16
|
+
import "./chunk-OC6RU6XH.js";
|
|
17
17
|
export {
|
|
18
18
|
VIEWPORTS,
|
|
19
19
|
buildReviewPrompt,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
screenshotVariants
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-P3H4N2EN.js";
|
|
4
|
+
} from "./chunk-5VKUJPOL.js";
|
|
5
|
+
import "./chunk-X6MDBKED.js";
|
|
7
6
|
import "./chunk-C7WKNJG6.js";
|
|
8
7
|
import "./chunk-B7LOERSP.js";
|
|
9
8
|
import "./chunk-BWZFACBT.js";
|
|
9
|
+
import "./chunk-P3H4N2EN.js";
|
|
10
10
|
export {
|
|
11
11
|
screenshotVariants
|
|
12
12
|
};
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
runVariants,
|
|
10
10
|
variantPrompt,
|
|
11
11
|
variantsRoot
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-P3H4N2EN.js";
|
|
12
|
+
} from "./chunk-X6MDBKED.js";
|
|
14
13
|
import "./chunk-C7WKNJG6.js";
|
|
15
14
|
import "./chunk-BWZFACBT.js";
|
|
15
|
+
import "./chunk-P3H4N2EN.js";
|
|
16
16
|
export {
|
|
17
17
|
MAX_VARIANTS,
|
|
18
18
|
applyVariant,
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
cdpCapture,
|
|
4
|
-
hasWebSocket
|
|
5
|
-
} from "./chunk-OC6RU6XH.js";
|
|
6
2
|
import {
|
|
7
3
|
findChrome,
|
|
8
4
|
screenshot
|
|
9
5
|
} from "./chunk-B7LOERSP.js";
|
|
6
|
+
import {
|
|
7
|
+
cdpCapture,
|
|
8
|
+
hasWebSocket
|
|
9
|
+
} from "./chunk-OC6RU6XH.js";
|
|
10
10
|
|
|
11
11
|
// src/preview/preview.ts
|
|
12
12
|
import fs2 from "fs";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runAgent
|
|
4
|
+
} from "./chunk-C7WKNJG6.js";
|
|
2
5
|
import {
|
|
3
6
|
FAMILIES,
|
|
4
7
|
FIRST_TURN_ADDENDUM,
|
|
5
8
|
renderFamilyBrief
|
|
6
9
|
} from "./chunk-P3H4N2EN.js";
|
|
7
|
-
import {
|
|
8
|
-
runAgent
|
|
9
|
-
} from "./chunk-C7WKNJG6.js";
|
|
10
10
|
|
|
11
11
|
// src/variants/variants.ts
|
|
12
12
|
import { execFileSync } from "child_process";
|