@aayambansal/squint 0.2.2 → 0.2.4
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 +6 -3
- package/dist/{chunk-XZKQZKEE.js → chunk-4LAU5TGK.js} +18 -0
- package/dist/{chunk-73ULM6HF.js → chunk-CIQ3OUAF.js} +33 -2
- package/dist/chunk-EECGXFRX.js +39 -0
- package/dist/cli.js +112 -19
- package/dist/commands-6XDXUVO6.js +11 -0
- package/dist/{preview-JJJLDF7H.js → preview-LZDH65ID.js} +5 -1
- package/dist/{skills-UGHU22BS.js → skills-DNBHZAAU.js} +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,9 +151,12 @@ squint doctor --probe # run every engine end to end, verify auth act
|
|
|
151
151
|
`/fix <n>` targets one. The footer counts what's open.
|
|
152
152
|
- **Variants without leaving**: `/variants 3 <ask>` runs parallel explorations with
|
|
153
153
|
streaming per-family status; `/variants apply <id>` keeps the winner.
|
|
154
|
-
- **Commands**:
|
|
155
|
-
`/
|
|
156
|
-
`/
|
|
154
|
+
- **Commands**: type `/` and matching commands appear with descriptions; tab completes.
|
|
155
|
+
`/dev` `/check` `/problems` `/fix [n]` `/shot` `/review [focus]` `/variants` `/undo`
|
|
156
|
+
`/checkpoints` `/restore <n>` `/mode` `/theme` `/copy` `/save` `/resume` `/clear`.
|
|
157
|
+
- **Visual pulse**: every clean turn is screenshotted and pixel-compared with the last —
|
|
158
|
+
drift shows up as a number, not a surprise. `.squint/locks` lists paths the engine must
|
|
159
|
+
never touch; `/save` exports the transcript as markdown.
|
|
157
160
|
- Assistant output renders as markdown; the done line measures real work via git
|
|
158
161
|
(`3 files +42 −7`); the footer tracks session turns and cost; a bell rings when a
|
|
159
162
|
turn finishes.
|
|
@@ -51,12 +51,29 @@ function matchSkills(skills, ask) {
|
|
|
51
51
|
const haystack = ask.toLowerCase();
|
|
52
52
|
return skills.filter((skill) => skill.triggers.some((t) => haystack.includes(t)));
|
|
53
53
|
}
|
|
54
|
+
function loadLocks(cwd) {
|
|
55
|
+
try {
|
|
56
|
+
return fs.readFileSync(path.join(cwd, ".squint", "locks"), "utf8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
57
|
+
} catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
54
61
|
function enrich(cwd, ask) {
|
|
55
62
|
const parts = [];
|
|
56
63
|
const rules = loadRules(cwd);
|
|
57
64
|
if (rules) parts.push(`## Project rules (always apply)
|
|
58
65
|
|
|
59
66
|
${rules}`);
|
|
67
|
+
const locks = loadLocks(cwd);
|
|
68
|
+
if (locks.length > 0) {
|
|
69
|
+
parts.push(
|
|
70
|
+
`## Locked files (hard constraint)
|
|
71
|
+
|
|
72
|
+
Never modify these paths, no matter what the task seems to need:
|
|
73
|
+
${locks.map((l) => `- ${l}`).join("\n")}
|
|
74
|
+
If the task appears to require changing them, stop and explain instead.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
60
77
|
const matched = matchSkills(loadSkills(cwd), ask);
|
|
61
78
|
for (const skill of matched) {
|
|
62
79
|
parts.push(`## Project notes: ${skill.name}
|
|
@@ -76,5 +93,6 @@ export {
|
|
|
76
93
|
loadSkills,
|
|
77
94
|
loadRules,
|
|
78
95
|
matchSkills,
|
|
96
|
+
loadLocks,
|
|
79
97
|
enrich
|
|
80
98
|
};
|
|
@@ -16,7 +16,7 @@ import path2 from "path";
|
|
|
16
16
|
// src/state/state.ts
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
import path from "path";
|
|
19
|
-
var IGNORED = ["preview/", "state.json", "variants/"];
|
|
19
|
+
var IGNORED = ["preview/", "state.json", "variants/", "transcripts/"];
|
|
20
20
|
function ensureSquintIgnore(cwd) {
|
|
21
21
|
const dir = path.join(cwd, ".squint");
|
|
22
22
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -62,6 +62,19 @@ var VIEWPORTS = [
|
|
|
62
62
|
{ name: "tablet", width: 768, height: 1024 },
|
|
63
63
|
{ name: "desktop", width: 1440, height: 900 }
|
|
64
64
|
];
|
|
65
|
+
function loadRoutes(cwd) {
|
|
66
|
+
let lines = [];
|
|
67
|
+
try {
|
|
68
|
+
lines = fs2.readFileSync(path2.join(cwd, ".squint", "routes"), "utf8").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
const routes = ["/", ...lines.filter((l) => l !== "/")];
|
|
72
|
+
return routes.slice(0, 6).map((r) => r.startsWith("/") ? r : `/${r}`);
|
|
73
|
+
}
|
|
74
|
+
function routeShotName(route) {
|
|
75
|
+
const clean = route.replace(/^\/+|\/+$/g, "").replace(/[^a-zA-Z0-9]+/g, "-");
|
|
76
|
+
return clean.length > 0 ? clean : "root";
|
|
77
|
+
}
|
|
65
78
|
function previewDir(cwd) {
|
|
66
79
|
const dir = path2.join(cwd, ".squint", "preview");
|
|
67
80
|
fs2.mkdirSync(dir, { recursive: true });
|
|
@@ -72,10 +85,26 @@ async function captureViewports(cwd, url) {
|
|
|
72
85
|
const chrome = findChrome();
|
|
73
86
|
if (!chrome) return null;
|
|
74
87
|
const dir = previewDir(cwd);
|
|
88
|
+
const routes = loadRoutes(cwd);
|
|
89
|
+
const base = url.replace(/\/+$/, "");
|
|
75
90
|
if (hasWebSocket()) {
|
|
76
91
|
try {
|
|
77
92
|
const { report, shots: shots2, a11y } = await cdpCapture(chrome, url, dir, VIEWPORTS, 2500, true);
|
|
78
|
-
|
|
93
|
+
const errors2 = [];
|
|
94
|
+
for (const route of routes.slice(1)) {
|
|
95
|
+
try {
|
|
96
|
+
const routeCapture = await cdpCapture(chrome, `${base}${route}`, dir, [
|
|
97
|
+
{ name: routeShotName(route), width: 1440, height: 900 }
|
|
98
|
+
]);
|
|
99
|
+
shots2.push(...routeCapture.shots);
|
|
100
|
+
for (const err of routeCapture.report.pageErrors.slice(0, 3)) {
|
|
101
|
+
errors2.push(`${route}: ${err.split("\n")[0]}`);
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
errors2.push(`${route}: capture failed`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { shots: shots2, errors: errors2, runtime: report, a11y };
|
|
79
108
|
} catch {
|
|
80
109
|
}
|
|
81
110
|
}
|
|
@@ -178,6 +207,8 @@ export {
|
|
|
178
207
|
saveState,
|
|
179
208
|
clearState,
|
|
180
209
|
VIEWPORTS,
|
|
210
|
+
loadRoutes,
|
|
211
|
+
routeShotName,
|
|
181
212
|
previewDir,
|
|
182
213
|
captureViewports,
|
|
183
214
|
runtimeSummary,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/session/commands.ts
|
|
4
|
+
var COMMANDS = [
|
|
5
|
+
{ name: "dev", description: "start/stop the project dev server" },
|
|
6
|
+
{ name: "check", description: "run all quality gates (typecheck, lint, test, build)" },
|
|
7
|
+
{ name: "problems", description: "list open findings from gates, dev server, runtime, a11y" },
|
|
8
|
+
{ name: "fix", args: "[n]", description: "send all open problems to the engine, or just problem n" },
|
|
9
|
+
{ name: "shot", description: "screenshot the app at mobile/tablet/desktop" },
|
|
10
|
+
{ name: "review", args: "[focus]", description: "screenshots + the engine critiques its own rendered work" },
|
|
11
|
+
{ name: "variants", args: "<2-4> <ask>", description: "parallel design explorations; apply/list/clean" },
|
|
12
|
+
{ name: "undo", description: "revert the last ask (files only)" },
|
|
13
|
+
{ name: "checkpoints", description: "list per-ask checkpoints" },
|
|
14
|
+
{ name: "restore", args: "<n>", description: "rewind files to before ask n" },
|
|
15
|
+
{ name: "mode", args: "plan|safe|yolo", description: "how much the engine may do (shift+tab cycles)" },
|
|
16
|
+
{ name: "engine", args: "<id>", description: "switch backend (new session)" },
|
|
17
|
+
{ name: "model", args: "[name]", description: "model override for the engine" },
|
|
18
|
+
{ name: "theme", args: "[name]", description: "switch the TUI theme", viewLevel: true },
|
|
19
|
+
{ name: "copy", description: "copy the last reply to the clipboard" },
|
|
20
|
+
{ name: "save", description: "export the transcript to .squint/transcripts/" },
|
|
21
|
+
{ name: "queue", args: "clear", description: "drop queued asks" },
|
|
22
|
+
{ name: "resume", description: "pick up the previous session for this repo" },
|
|
23
|
+
{ name: "clear", description: "new session (transcript, totals, persisted state)" },
|
|
24
|
+
{ name: "help", description: "list commands" },
|
|
25
|
+
{ name: "quit", description: "exit with a session summary" }
|
|
26
|
+
];
|
|
27
|
+
function completeCommand(partial) {
|
|
28
|
+
const query = partial.toLowerCase();
|
|
29
|
+
return COMMANDS.filter((c) => c.name.startsWith(query));
|
|
30
|
+
}
|
|
31
|
+
function commandHelp() {
|
|
32
|
+
return COMMANDS.map((c) => `/${c.name}${c.args ? ` ${c.args}` : ""} \u2014 ${c.description}`).join("\n");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
COMMANDS,
|
|
37
|
+
completeCommand,
|
|
38
|
+
commandHelp
|
|
39
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
diffStatSince,
|
|
4
|
+
isGitRepo,
|
|
5
|
+
restoreSnapshot,
|
|
6
|
+
takeSnapshot
|
|
7
|
+
} from "./chunk-YHRAOBI2.js";
|
|
2
8
|
import {
|
|
3
9
|
DevServer,
|
|
4
10
|
buildFixPrompt,
|
|
@@ -17,6 +23,9 @@ import {
|
|
|
17
23
|
import {
|
|
18
24
|
runAgent
|
|
19
25
|
} from "./chunk-OTG4ZB4R.js";
|
|
26
|
+
import {
|
|
27
|
+
completeCommand
|
|
28
|
+
} from "./chunk-EECGXFRX.js";
|
|
20
29
|
import {
|
|
21
30
|
buildGatePrompt,
|
|
22
31
|
detectFastGates,
|
|
@@ -33,7 +42,7 @@ import {
|
|
|
33
42
|
probeRuntime,
|
|
34
43
|
runtimeSummary,
|
|
35
44
|
saveState
|
|
36
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-CIQ3OUAF.js";
|
|
37
46
|
import "./chunk-OC6RU6XH.js";
|
|
38
47
|
import {
|
|
39
48
|
findChrome
|
|
@@ -44,13 +53,7 @@ import {
|
|
|
44
53
|
} from "./chunk-2LRIKWBU.js";
|
|
45
54
|
import {
|
|
46
55
|
enrich
|
|
47
|
-
} from "./chunk-
|
|
48
|
-
import {
|
|
49
|
-
diffStatSince,
|
|
50
|
-
isGitRepo,
|
|
51
|
-
restoreSnapshot,
|
|
52
|
-
takeSnapshot
|
|
53
|
-
} from "./chunk-YHRAOBI2.js";
|
|
56
|
+
} from "./chunk-4LAU5TGK.js";
|
|
54
57
|
|
|
55
58
|
// src/cli.tsx
|
|
56
59
|
import { Command } from "commander";
|
|
@@ -77,6 +80,8 @@ var ConfigSchema = z.object({
|
|
|
77
80
|
autoCheck: z.boolean().optional(),
|
|
78
81
|
/** Terminal bell when a turn finishes (default on). */
|
|
79
82
|
bell: z.boolean().optional(),
|
|
83
|
+
/** Session budget in USD; crossing it warns (never blocks). */
|
|
84
|
+
budgetUsd: z.number().positive().optional(),
|
|
80
85
|
/** TUI theme name (amber, ocean, moss, rose, mono). */
|
|
81
86
|
theme: z.string().optional()
|
|
82
87
|
});
|
|
@@ -126,13 +131,19 @@ function setConfigValue(file, key, value) {
|
|
|
126
131
|
throw new Error(`"${key}" must be true or false`);
|
|
127
132
|
}
|
|
128
133
|
next = { ...current, [key]: value === "true" };
|
|
134
|
+
} else if (key === "budgetUsd") {
|
|
135
|
+
const budget = Number.parseFloat(value);
|
|
136
|
+
if (!Number.isFinite(budget) || budget <= 0) {
|
|
137
|
+
throw new Error('"budgetUsd" must be a positive number');
|
|
138
|
+
}
|
|
139
|
+
next = { ...current, budgetUsd: budget };
|
|
129
140
|
} else if (key.startsWith("models.")) {
|
|
130
141
|
const engineId = key.slice("models.".length);
|
|
131
142
|
if (!engineId) throw new Error("Usage: squint config set models.<engineId> <model>");
|
|
132
143
|
next = { ...current, models: { ...current.models, [engineId]: value } };
|
|
133
144
|
} else {
|
|
134
145
|
throw new Error(
|
|
135
|
-
`Unknown config key "${key}". Supported: engine, theme, autoDev, autoFix, autoProbe, autoCheck, bell, models.<engineId>`
|
|
146
|
+
`Unknown config key "${key}". Supported: engine, theme, autoDev, autoFix, autoProbe, autoCheck, bell, budgetUsd, models.<engineId>`
|
|
136
147
|
);
|
|
137
148
|
}
|
|
138
149
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -344,16 +355,32 @@ var Session = class {
|
|
|
344
355
|
}
|
|
345
356
|
turnEdits = 0;
|
|
346
357
|
turnTools = 0;
|
|
358
|
+
toolStreak = 0;
|
|
359
|
+
collapsedTools = 0;
|
|
360
|
+
/**
|
|
361
|
+
* Long tool cascades collapse: the first three of a consecutive burst
|
|
362
|
+
* render, the rest fold into one "+N more" line pushed when the burst
|
|
363
|
+
* ends — append-only, so the Static transcript stays valid.
|
|
364
|
+
*/
|
|
365
|
+
flushToolCollapse() {
|
|
366
|
+
if (this.collapsedTools > 0) {
|
|
367
|
+
this.push("tool", `+${this.collapsedTools} more tool call${this.collapsedTools === 1 ? "" : "s"}`);
|
|
368
|
+
this.collapsedTools = 0;
|
|
369
|
+
}
|
|
370
|
+
this.toolStreak = 0;
|
|
371
|
+
}
|
|
347
372
|
handleEvent = (event) => {
|
|
348
373
|
switch (event.type) {
|
|
349
374
|
case "status":
|
|
350
375
|
this.commitLive();
|
|
376
|
+
this.flushToolCollapse();
|
|
351
377
|
this.push("status", event.text);
|
|
352
378
|
break;
|
|
353
379
|
case "delta":
|
|
354
380
|
this.setLive(this.live + event.text);
|
|
355
381
|
break;
|
|
356
382
|
case "text":
|
|
383
|
+
this.flushToolCollapse();
|
|
357
384
|
if (event.streamed) {
|
|
358
385
|
this.live = "";
|
|
359
386
|
this.state = { ...this.state, liveText: "" };
|
|
@@ -364,17 +391,24 @@ var Session = class {
|
|
|
364
391
|
break;
|
|
365
392
|
case "thinking":
|
|
366
393
|
this.commitLive();
|
|
394
|
+
this.flushToolCollapse();
|
|
367
395
|
this.push("thinking", event.text);
|
|
368
396
|
break;
|
|
369
397
|
case "tool": {
|
|
370
398
|
this.commitLive();
|
|
371
399
|
this.turnTools += 1;
|
|
400
|
+
this.toolStreak += 1;
|
|
372
401
|
if (/edit|write|patch|apply/i.test(event.name)) this.turnEdits += 1;
|
|
373
|
-
this.
|
|
402
|
+
if (this.toolStreak <= 3) {
|
|
403
|
+
this.push("tool", event.detail ? `${event.name} \xB7 ${event.detail}` : event.name);
|
|
404
|
+
} else {
|
|
405
|
+
this.collapsedTools += 1;
|
|
406
|
+
}
|
|
374
407
|
break;
|
|
375
408
|
}
|
|
376
409
|
case "error":
|
|
377
410
|
this.commitLive();
|
|
411
|
+
this.flushToolCollapse();
|
|
378
412
|
this.push("error", event.text);
|
|
379
413
|
break;
|
|
380
414
|
case "result":
|
|
@@ -407,6 +441,7 @@ var Session = class {
|
|
|
407
441
|
);
|
|
408
442
|
this.abort = null;
|
|
409
443
|
this.commitLive();
|
|
444
|
+
this.flushToolCollapse();
|
|
410
445
|
if (result.ok) {
|
|
411
446
|
const cost = result.costUsd !== void 0 ? ` \xB7 $${result.costUsd.toFixed(2)}` : "";
|
|
412
447
|
const secs = result.durationMs !== void 0 ? ` \xB7 ${(result.durationMs / 1e3).toFixed(0)}s` : "";
|
|
@@ -414,12 +449,20 @@ var Session = class {
|
|
|
414
449
|
const stat = checkpoint ? diffStatSince(this.opts.cwd, checkpoint.snapshot) : null;
|
|
415
450
|
const work = stat ? ` \xB7 ${stat}` : this.turnEdits > 0 ? ` \xB7 ${this.turnEdits} edit${this.turnEdits === 1 ? "" : "s"}` : this.turnTools > 0 ? ` \xB7 ${this.turnTools} tool call${this.turnTools === 1 ? "" : "s"}` : "";
|
|
416
451
|
this.push("status", `done${secs}${cost}${work}`);
|
|
452
|
+
const before = this.state.totals.costUsd;
|
|
417
453
|
this.notify({
|
|
418
454
|
totals: {
|
|
419
|
-
costUsd:
|
|
455
|
+
costUsd: before + (result.costUsd ?? 0),
|
|
420
456
|
turns: this.state.totals.turns + 1
|
|
421
457
|
}
|
|
422
458
|
});
|
|
459
|
+
const budget = this.opts.budgetUsd;
|
|
460
|
+
if (budget && before < budget && this.state.totals.costUsd >= budget) {
|
|
461
|
+
this.push(
|
|
462
|
+
"error",
|
|
463
|
+
`session cost $${this.state.totals.costUsd.toFixed(2)} crossed your $${budget.toFixed(2)} budget \u2014 squint keeps working, this is just the flag you asked for`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
423
466
|
if (this.sessionId) {
|
|
424
467
|
saveState(this.opts.cwd, {
|
|
425
468
|
engine: this.state.engineId,
|
|
@@ -669,6 +712,38 @@ They are objective defects, not style preferences.`
|
|
|
669
712
|
}
|
|
670
713
|
break;
|
|
671
714
|
}
|
|
715
|
+
case "save": {
|
|
716
|
+
void (async () => {
|
|
717
|
+
const fs2 = await import("fs");
|
|
718
|
+
const path4 = await import("path");
|
|
719
|
+
const dir = path4.join(this.opts.cwd, ".squint", "transcripts");
|
|
720
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
721
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
722
|
+
const file = path4.join(dir, `${stamp}.md`);
|
|
723
|
+
const lines = [`# squint session \u2014 ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`, ""];
|
|
724
|
+
for (const item of this.state.items) {
|
|
725
|
+
switch (item.role) {
|
|
726
|
+
case "user":
|
|
727
|
+
lines.push(`## \u276F ${item.text}`, "");
|
|
728
|
+
break;
|
|
729
|
+
case "assistant":
|
|
730
|
+
lines.push(item.text, "");
|
|
731
|
+
break;
|
|
732
|
+
case "tool":
|
|
733
|
+
lines.push(`- \u2699 ${item.text}`);
|
|
734
|
+
break;
|
|
735
|
+
case "thinking":
|
|
736
|
+
break;
|
|
737
|
+
default:
|
|
738
|
+
lines.push(`> ${item.role === "error" ? "\u2717 " : ""}${item.text.split("\n").join("\n> ")}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
lines.push("", `> ${this.summary()}`);
|
|
742
|
+
fs2.writeFileSync(file, lines.join("\n") + "\n");
|
|
743
|
+
this.push("status", `saved transcript \u2192 ${path4.relative(this.opts.cwd, file)}`);
|
|
744
|
+
})();
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
672
747
|
case "copy": {
|
|
673
748
|
const last = this.state.items.findLast((i) => i.role === "assistant");
|
|
674
749
|
if (!last) {
|
|
@@ -857,12 +932,10 @@ They are objective defects, not style preferences.`
|
|
|
857
932
|
clearState(this.opts.cwd);
|
|
858
933
|
this.notify({ items: [], totals: { costUsd: 0, turns: 0 } });
|
|
859
934
|
break;
|
|
860
|
-
case "help":
|
|
861
|
-
this.push(
|
|
862
|
-
"status",
|
|
863
|
-
"/engine <id> \xB7 /model <name> \xB7 /mode plan|safe|yolo \xB7 /dev \xB7 /check (gates) \xB7 /problems \xB7 /fix [n] \xB7 /shot \xB7 /review [focus] \xB7 /variants <2-4> <ask> \xB7 /undo \xB7 /checkpoints \xB7 /restore <n> \xB7 /copy (last reply) \xB7 /resume \xB7 /clear \xB7 /quit"
|
|
864
|
-
);
|
|
935
|
+
case "help": {
|
|
936
|
+
void import("./commands-6XDXUVO6.js").then(({ commandHelp }) => this.push("status", commandHelp()));
|
|
865
937
|
break;
|
|
938
|
+
}
|
|
866
939
|
case "quit":
|
|
867
940
|
case "exit":
|
|
868
941
|
this.push("status", this.summary());
|
|
@@ -1194,6 +1267,7 @@ function App({
|
|
|
1194
1267
|
autoProbe,
|
|
1195
1268
|
autoCheck,
|
|
1196
1269
|
bell,
|
|
1270
|
+
budgetUsd,
|
|
1197
1271
|
initialTheme
|
|
1198
1272
|
}) {
|
|
1199
1273
|
const { exit } = useApp();
|
|
@@ -1209,6 +1283,7 @@ function App({
|
|
|
1209
1283
|
autoFix,
|
|
1210
1284
|
autoProbe,
|
|
1211
1285
|
autoCheck,
|
|
1286
|
+
budgetUsd,
|
|
1212
1287
|
// Delay lets the goodbye summary land in the Static scrollback.
|
|
1213
1288
|
onQuit: () => setTimeout(() => exit(), 60)
|
|
1214
1289
|
});
|
|
@@ -1248,6 +1323,13 @@ function App({
|
|
|
1248
1323
|
session.cycleMode();
|
|
1249
1324
|
return;
|
|
1250
1325
|
}
|
|
1326
|
+
if (key.tab && line.text.startsWith("/") && !line.text.includes(" ")) {
|
|
1327
|
+
const matches = completeCommand(line.text.slice(1));
|
|
1328
|
+
if (matches.length > 0) {
|
|
1329
|
+
setLine(fromText(`/${matches[0].name}${matches[0].args ? " " : ""}`));
|
|
1330
|
+
}
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1251
1333
|
if (key.return) {
|
|
1252
1334
|
const value = line.text.trim();
|
|
1253
1335
|
setLine(emptyLine);
|
|
@@ -1341,6 +1423,16 @@ function App({
|
|
|
1341
1423
|
"\u22EF queued: ",
|
|
1342
1424
|
queued
|
|
1343
1425
|
] }) }, index)),
|
|
1426
|
+
line.text.startsWith("/") && !line.text.includes(" ") && /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", children: completeCommand(line.text.slice(1)).slice(0, 5).map((command, index) => /* @__PURE__ */ jsxs3(Text3, { color: index === 0 ? theme2.accent : theme2.dim, children: [
|
|
1427
|
+
"/",
|
|
1428
|
+
command.name,
|
|
1429
|
+
command.args ? ` ${command.args}` : "",
|
|
1430
|
+
" ",
|
|
1431
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme2.dim, children: [
|
|
1432
|
+
"\u2014 ",
|
|
1433
|
+
command.description
|
|
1434
|
+
] })
|
|
1435
|
+
] }, command.name)) }),
|
|
1344
1436
|
/* @__PURE__ */ jsx3(Box2, { marginTop: state.running ? 0 : 1, children: /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
1345
1437
|
/* @__PURE__ */ jsx3(Text3, { color: theme2.accent, children: "\u276F " }),
|
|
1346
1438
|
line.text.slice(0, line.cursor),
|
|
@@ -1381,7 +1473,7 @@ function App({
|
|
|
1381
1473
|
|
|
1382
1474
|
// src/cli.tsx
|
|
1383
1475
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1384
|
-
var VERSION = "0.2.
|
|
1476
|
+
var VERSION = "0.2.4";
|
|
1385
1477
|
var program = new Command();
|
|
1386
1478
|
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
1387
1479
|
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 (claude, codex, gemini, opencode)").option("-m, --model <name>", "model override for the engine").option("--no-brief", "send the prompt without the squint design brief").option("--json", "emit normalized agent events as ndjson").action(
|
|
@@ -1492,7 +1584,7 @@ Next: ${pc.bold(`${cd}${options.install ? "" : "npm install && "}squint`)}`);
|
|
|
1492
1584
|
});
|
|
1493
1585
|
var skillsCommand = program.command("skills").description("Project knowledge injected into asks (.squint/rules.md + .squint/skills/)");
|
|
1494
1586
|
skillsCommand.command("list").description("Show always-on rules and trigger-matched skills").action(async () => {
|
|
1495
|
-
const { loadRules, loadSkills } = await import("./skills-
|
|
1587
|
+
const { loadRules, loadSkills } = await import("./skills-DNBHZAAU.js");
|
|
1496
1588
|
const cwd = process.cwd();
|
|
1497
1589
|
const rules = loadRules(cwd);
|
|
1498
1590
|
console.log(rules ? `${pc.green("\u2713")} rules.md ${pc.dim(`(${rules.split("\n").length} lines, always on)`)}` : pc.dim("\u25CB no .squint/rules.md"));
|
|
@@ -1678,7 +1770,7 @@ program.command("check").description("Run this project\u2019s quality gates (typ
|
|
|
1678
1770
|
if (results.some((r) => !r.ok)) process.exitCode = 1;
|
|
1679
1771
|
});
|
|
1680
1772
|
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) => {
|
|
1681
|
-
const { captureViewports: captureViewports2 } = await import("./preview-
|
|
1773
|
+
const { captureViewports: captureViewports2 } = await import("./preview-LZDH65ID.js");
|
|
1682
1774
|
const result = await captureViewports2(process.cwd(), url);
|
|
1683
1775
|
if (!result) {
|
|
1684
1776
|
console.error(pc.red("\u2717 no Chrome/Chromium found"));
|
|
@@ -1722,6 +1814,7 @@ program.action(() => {
|
|
|
1722
1814
|
autoProbe: config.autoProbe,
|
|
1723
1815
|
autoCheck: config.autoCheck,
|
|
1724
1816
|
bell: config.bell,
|
|
1817
|
+
budgetUsd: config.budgetUsd,
|
|
1725
1818
|
initialTheme: config.theme
|
|
1726
1819
|
}
|
|
1727
1820
|
)
|
|
@@ -5,10 +5,12 @@ import {
|
|
|
5
5
|
buildRuntimeFixPrompt,
|
|
6
6
|
captureViewports,
|
|
7
7
|
comparePulse,
|
|
8
|
+
loadRoutes,
|
|
8
9
|
previewDir,
|
|
9
10
|
probeRuntime,
|
|
11
|
+
routeShotName,
|
|
10
12
|
runtimeSummary
|
|
11
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CIQ3OUAF.js";
|
|
12
14
|
import "./chunk-OC6RU6XH.js";
|
|
13
15
|
import "./chunk-P3V5CWH5.js";
|
|
14
16
|
import "./chunk-2LRIKWBU.js";
|
|
@@ -18,7 +20,9 @@ export {
|
|
|
18
20
|
buildRuntimeFixPrompt,
|
|
19
21
|
captureViewports,
|
|
20
22
|
comparePulse,
|
|
23
|
+
loadRoutes,
|
|
21
24
|
previewDir,
|
|
22
25
|
probeRuntime,
|
|
26
|
+
routeShotName,
|
|
23
27
|
runtimeSummary
|
|
24
28
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
enrich,
|
|
4
|
+
loadLocks,
|
|
4
5
|
loadRules,
|
|
5
6
|
loadSkills,
|
|
6
7
|
matchSkills,
|
|
7
8
|
parseSkill
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4LAU5TGK.js";
|
|
9
10
|
export {
|
|
10
11
|
enrich,
|
|
12
|
+
loadLocks,
|
|
11
13
|
loadRules,
|
|
12
14
|
loadSkills,
|
|
13
15
|
matchSkills,
|
package/package.json
CHANGED