@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 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**: `/dev` `/check` `/problems` `/fix [n]` `/shot` `/review [focus]`
155
- `/variants` `/undo` `/checkpoints` `/restore <n>` `/mode` `/theme` `/resume`
156
- `/engine <id>` `/model <name>` `/clear`.
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
- return { shots: shots2, errors: [], runtime: report, a11y };
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-73ULM6HF.js";
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-XZKQZKEE.js";
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.push("tool", event.detail ? `${event.name} \xB7 ${event.detail}` : event.name);
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: this.state.totals.costUsd + (result.costUsd ?? 0),
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.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-UGHU22BS.js");
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-JJJLDF7H.js");
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
  )
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ COMMANDS,
4
+ commandHelp,
5
+ completeCommand
6
+ } from "./chunk-EECGXFRX.js";
7
+ export {
8
+ COMMANDS,
9
+ commandHelp,
10
+ completeCommand
11
+ };
@@ -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-73ULM6HF.js";
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-XZKQZKEE.js";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aayambansal/squint",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Lovable for your terminal — a frontend harness on top of Claude Code, Codex, and other coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",