@aayambansal/squint 0.2.4 → 0.2.5

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
@@ -225,6 +225,8 @@ All product behavior lives in the harness, so a new engine is ~80 lines.
225
225
 
226
226
  ## Docs
227
227
 
228
+ - [Engine setup guide](./docs/engines.md) — install + auth for all eight, and how to choose
229
+ - [Configuration](./docs/configuration.md) — every key, every `.squint/` file
228
230
  - [Architecture](./docs/design/2026-07-25-architecture.md)
229
231
  - [How Lovable works under the hood](./docs/research/lovable.md)
230
232
  - [Making agents produce excellent frontend work](./docs/research/frontend-quality.md)
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/tui/background.ts
4
+ function parseOsc11(reply) {
5
+ const match = /rgb:([0-9a-fA-F]{2,4})\/([0-9a-fA-F]{2,4})\/([0-9a-fA-F]{2,4})/.exec(reply);
6
+ if (!match) return "unknown";
7
+ const channel = (hex) => Number.parseInt(hex.slice(0, 2), 16);
8
+ const [r, g, b] = [channel(match[1]), channel(match[2]), channel(match[3])];
9
+ const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
10
+ return luminance > 140 ? "light" : "dark";
11
+ }
12
+ function detectBackground(timeoutMs = 300) {
13
+ return new Promise((resolve) => {
14
+ const { stdin, stdout } = process;
15
+ if (!stdin.isTTY || !stdout.isTTY) return resolve("unknown");
16
+ let buffer = "";
17
+ const finish = (result) => {
18
+ clearTimeout(timer);
19
+ stdin.off("data", onData);
20
+ resolve(result);
21
+ };
22
+ const onData = (chunk) => {
23
+ buffer += chunk.toString("utf8");
24
+ if (buffer.includes("rgb:")) {
25
+ const parsed = parseOsc11(buffer);
26
+ if (parsed !== "unknown") finish(parsed);
27
+ }
28
+ };
29
+ const timer = setTimeout(() => finish("unknown"), timeoutMs);
30
+ stdin.on("data", onData);
31
+ try {
32
+ stdout.write("\x1B]11;?\x07");
33
+ } catch {
34
+ finish("unknown");
35
+ }
36
+ });
37
+ }
38
+ export {
39
+ detectBackground,
40
+ parseOsc11
41
+ };
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  findChrome,
4
4
  screenshot
5
- } from "./chunk-P3V5CWH5.js";
6
- import "./chunk-2LRIKWBU.js";
5
+ } from "./chunk-CJXYSQNB.js";
6
+ import "./chunk-CH4GNJCZ.js";
7
7
  export {
8
8
  findChrome,
9
9
  screenshot
@@ -39,6 +39,20 @@ function detectGates(cwd) {
39
39
  } else if (hasEslintConfig) {
40
40
  gates.push({ id: "lint", command: "npx", args: ["eslint", ".", "--max-warnings", "0"], display: "eslint ." });
41
41
  }
42
+ const hasPrettier = [
43
+ ".prettierrc",
44
+ ".prettierrc.json",
45
+ ".prettierrc.js",
46
+ ".prettierrc.yaml",
47
+ ".prettierrc.yml",
48
+ "prettier.config.js",
49
+ "prettier.config.mjs"
50
+ ].some((file) => fs.existsSync(path.join(cwd, file)));
51
+ if (scripts.format && /--check|-c\b/.test(scripts.format)) {
52
+ gates.push({ id: "format", ...npmRun("format") });
53
+ } else if (hasPrettier) {
54
+ gates.push({ id: "format", command: "npx", args: ["prettier", "--check", "."], display: "prettier --check ." });
55
+ }
42
56
  const testScript = scripts.test;
43
57
  if (testScript && !/no test specified/i.test(testScript)) {
44
58
  gates.push({ id: "test", ...npmRun("test") });
@@ -37,6 +37,7 @@ var aider = {
37
37
  supportsResume: false,
38
38
  buildArgs(opts) {
39
39
  const args = ["--message", opts.prompt, "--yes-always", "--no-auto-commits"];
40
+ if (opts.mode === "plan") args.push("--dry-run");
40
41
  if (opts.model) args.push("--model", opts.model);
41
42
  return args;
42
43
  }
@@ -123,10 +124,9 @@ var amp = {
123
124
  install: "npm install -g @sourcegraph/amp",
124
125
  supportsResume: true,
125
126
  buildArgs(opts) {
126
- if (opts.sessionId) {
127
- return ["threads", "continue", "--execute", opts.prompt, "--stream-json"];
128
- }
129
- return ["-x", opts.prompt, "--stream-json"];
127
+ const args = opts.sessionId ? ["threads", "continue", "--execute", opts.prompt, "--stream-json"] : ["-x", opts.prompt, "--stream-json"];
128
+ if (opts.mode === "yolo") args.push("--dangerously-allow-all");
129
+ return args;
130
130
  },
131
131
  createParser: () => createClaudeStreamParser("amp")
132
132
  };
@@ -265,7 +265,8 @@ var copilot = {
265
265
  install: "npm install -g @github/copilot",
266
266
  supportsResume: false,
267
267
  buildArgs(opts) {
268
- const args = ["-p", opts.prompt, "-s", "--allow-all-tools"];
268
+ const args = ["-p", opts.prompt, "-s"];
269
+ if (opts.mode !== "plan") args.push("--allow-all-tools");
269
270
  if (opts.model) args.push("--model", opts.model);
270
271
  return args;
271
272
  }
@@ -314,6 +315,8 @@ var opencode = {
314
315
  supportsResume: true,
315
316
  buildArgs(opts) {
316
317
  const args = ["run", "--format", "json"];
318
+ if (opts.mode === "plan") args.push("--agent", "plan");
319
+ if (opts.mode === "yolo") args.push("--auto");
317
320
  if (opts.model) args.push("--model", opts.model);
318
321
  if (opts.sessionId) args.push("--session", opts.sessionId);
319
322
  args.push(opts.prompt);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  findBinary
4
- } from "./chunk-2LRIKWBU.js";
4
+ } from "./chunk-CH4GNJCZ.js";
5
5
 
6
6
  // src/preview/chrome.ts
7
7
  import { spawn } from "child_process";
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  findChrome,
8
8
  screenshot
9
- } from "./chunk-P3V5CWH5.js";
9
+ } from "./chunk-CJXYSQNB.js";
10
10
 
11
11
  // src/preview/preview.ts
12
12
  import fs2 from "fs";
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  variantsRoot
4
- } from "./chunk-LCS47EAG.js";
4
+ } from "./chunk-XLNTTZX4.js";
5
5
  import {
6
6
  findChrome,
7
7
  screenshot
8
- } from "./chunk-P3V5CWH5.js";
8
+ } from "./chunk-CJXYSQNB.js";
9
9
  import {
10
10
  lineSplitter
11
- } from "./chunk-2LRIKWBU.js";
11
+ } from "./chunk-CH4GNJCZ.js";
12
12
 
13
13
  // src/variants/shots.ts
14
14
  import path2 from "path";
@@ -3,7 +3,7 @@ import {
3
3
  findEngineBinary,
4
4
  lineSplitter,
5
5
  truncate
6
- } from "./chunk-2LRIKWBU.js";
6
+ } from "./chunk-CH4GNJCZ.js";
7
7
 
8
8
  // src/runner/run.ts
9
9
  import { spawn } from "child_process";
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-P3H4N2EN.js";
7
7
  import {
8
8
  runAgent
9
- } from "./chunk-OTG4ZB4R.js";
9
+ } from "./chunk-TI2R7QRL.js";
10
10
 
11
11
  // src/variants/variants.ts
12
12
  import { execFileSync } from "child_process";
package/dist/cli.js CHANGED
@@ -10,19 +10,19 @@ import {
10
10
  buildFixPrompt,
11
11
  detectDevCommand,
12
12
  screenshotVariants
13
- } from "./chunk-2IW2MMCY.js";
13
+ } from "./chunk-QNNT6FP4.js";
14
14
  import {
15
15
  applyVariant,
16
16
  cleanVariants,
17
17
  listVariants,
18
18
  runVariants
19
- } from "./chunk-LCS47EAG.js";
19
+ } from "./chunk-XLNTTZX4.js";
20
20
  import {
21
21
  composePrompt
22
22
  } from "./chunk-P3H4N2EN.js";
23
23
  import {
24
24
  runAgent
25
- } from "./chunk-OTG4ZB4R.js";
25
+ } from "./chunk-TI2R7QRL.js";
26
26
  import {
27
27
  completeCommand
28
28
  } from "./chunk-EECGXFRX.js";
@@ -31,7 +31,7 @@ import {
31
31
  detectFastGates,
32
32
  detectGates,
33
33
  runGates
34
- } from "./chunk-DLCRYRWQ.js";
34
+ } from "./chunk-62JNF5M2.js";
35
35
  import {
36
36
  buildReviewPrompt,
37
37
  buildRuntimeFixPrompt,
@@ -42,15 +42,15 @@ import {
42
42
  probeRuntime,
43
43
  runtimeSummary,
44
44
  saveState
45
- } from "./chunk-CIQ3OUAF.js";
45
+ } from "./chunk-DKQ5KJD7.js";
46
46
  import "./chunk-OC6RU6XH.js";
47
47
  import {
48
48
  findChrome
49
- } from "./chunk-P3V5CWH5.js";
49
+ } from "./chunk-CJXYSQNB.js";
50
50
  import {
51
51
  detectEngines,
52
52
  getEngine
53
- } from "./chunk-2LRIKWBU.js";
53
+ } from "./chunk-CH4GNJCZ.js";
54
54
  import {
55
55
  enrich
56
56
  } from "./chunk-4LAU5TGK.js";
@@ -1051,6 +1051,15 @@ var THEMES = {
1051
1051
  success: "#31748f",
1052
1052
  tool: "#c4a7e7"
1053
1053
  },
1054
+ light: {
1055
+ name: "light",
1056
+ accent: "#9a6b1f",
1057
+ dim: "#6b6f76",
1058
+ user: "#2a5db0",
1059
+ error: "#c4322e",
1060
+ success: "#3d7a37",
1061
+ tool: "#0f7b8a"
1062
+ },
1054
1063
  mono: {
1055
1064
  name: "mono",
1056
1065
  accent: "white",
@@ -1473,12 +1482,18 @@ function App({
1473
1482
 
1474
1483
  // src/cli.tsx
1475
1484
  import { jsx as jsx4 } from "react/jsx-runtime";
1476
- var VERSION = "0.2.4";
1485
+ var VERSION = "0.2.5";
1477
1486
  var program = new Command();
1478
1487
  program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
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(
1488
+ 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(
1480
1489
  async (promptWords, options) => {
1481
1490
  const cwd = process.cwd();
1491
+ if (options.mode && !["plan", "safe", "yolo"].includes(options.mode)) {
1492
+ console.error(pc.red("\u2717 --mode must be plan, safe, or yolo"));
1493
+ process.exitCode = 1;
1494
+ return;
1495
+ }
1496
+ const mode = options.mode;
1482
1497
  const config = loadConfig(defaultPaths(cwd));
1483
1498
  const engineId = resolveEngineId(config, options.engine);
1484
1499
  const engine = getEngine(engineId);
@@ -1488,8 +1503,9 @@ program.command("run").description("Run one prompt headlessly and stream the res
1488
1503
  const onEvent = options.json ? (event) => {
1489
1504
  if (event.type !== "delta") console.log(JSON.stringify(event));
1490
1505
  } : createPrinter();
1491
- if (!options.json) console.log(pc.dim(`squint \xB7 ${engine.id}${model ? ` \xB7 ${model}` : ""}`));
1492
- const result = await runAgent(engine, { prompt, cwd, model }, onEvent);
1506
+ if (!options.json)
1507
+ console.log(pc.dim(`squint \xB7 ${engine.id}${model ? ` \xB7 ${model}` : ""}${mode && mode !== "safe" ? ` \xB7 ${mode}` : ""}`));
1508
+ const result = await runAgent(engine, { prompt, cwd, model, mode }, onEvent);
1493
1509
  if (options.json) {
1494
1510
  console.log(JSON.stringify({ type: "summary", ...result }));
1495
1511
  } else if (result.ok) {
@@ -1517,7 +1533,7 @@ program.command("doctor").description("Check squint prerequisites and engine ava
1517
1533
  console.log(`${status} ${engine.name}${binaryPath ? "" : pc.dim(` \u2014 install: ${engine.install}`)}`);
1518
1534
  }
1519
1535
  if (options.probe) {
1520
- const { runAgent: runAgent2 } = await import("./run-XHLCTF6V.js");
1536
+ const { runAgent: runAgent2 } = await import("./run-TIGEZHMQ.js");
1521
1537
  console.log(pc.dim("\nprobing engines with a one-word prompt (verifies auth end to end)\u2026"));
1522
1538
  for (const { engine, path: binaryPath } of detected) {
1523
1539
  if (!binaryPath) continue;
@@ -1539,7 +1555,7 @@ program.command("doctor").description("Check squint prerequisites and engine ava
1539
1555
  );
1540
1556
  }
1541
1557
  }
1542
- const { findChrome: findChrome2 } = await import("./chrome-RD7XQ767.js");
1558
+ const { findChrome: findChrome2 } = await import("./chrome-RV25VKUA.js");
1543
1559
  const { hasWebSocket } = await import("./cdp-SFWNP7OA.js");
1544
1560
  const chrome = findChrome2();
1545
1561
  console.log(
@@ -1666,7 +1682,7 @@ variantsCommand.command("gen").description("Generate n variants of one ask in pa
1666
1682
  process.exitCode = 1;
1667
1683
  return;
1668
1684
  }
1669
- const { runVariants: runVariants2, cleanVariants: cleanVariants2 } = await import("./variants-7A7723L7.js");
1685
+ const { runVariants: runVariants2, cleanVariants: cleanVariants2 } = await import("./variants-SM6NRX4U.js");
1670
1686
  const config = loadConfig(defaultPaths(cwd));
1671
1687
  const engineId = resolveEngineId(config, options.engine);
1672
1688
  const engine = getEngine(engineId);
@@ -1684,7 +1700,7 @@ variantsCommand.command("gen").description("Generate n variants of one ask in pa
1684
1700
  );
1685
1701
  const succeeded = runs.filter((r) => r.result.ok);
1686
1702
  if (options.shots && succeeded.length > 0) {
1687
- const { screenshotVariants: screenshotVariants2 } = await import("./shots-LRYYMTPK.js");
1703
+ const { screenshotVariants: screenshotVariants2 } = await import("./shots-X3I6MRLX.js");
1688
1704
  console.log(pc.dim("capturing screenshots\u2026"));
1689
1705
  const shots = await screenshotVariants2(cwd, succeeded.map((r) => r.variant));
1690
1706
  for (const shot of shots) {
@@ -1699,7 +1715,7 @@ ${succeeded.length}/${runs.length} variants ready in .squint/variants/ \u2014 `
1699
1715
  }
1700
1716
  );
1701
1717
  variantsCommand.command("list").description("List generated variants").action(async () => {
1702
- const { listVariants: listVariants2 } = await import("./variants-7A7723L7.js");
1718
+ const { listVariants: listVariants2 } = await import("./variants-SM6NRX4U.js");
1703
1719
  const ids = listVariants2(process.cwd());
1704
1720
  if (ids.length === 0) {
1705
1721
  console.log(pc.dim('no variants \u2014 squint variants gen <n> "<ask>"'));
@@ -1708,7 +1724,7 @@ variantsCommand.command("list").description("List generated variants").action(as
1708
1724
  for (const id of ids) console.log(id);
1709
1725
  });
1710
1726
  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) => {
1711
- const { applyVariant: applyVariant2, cleanVariants: cleanVariants2 } = await import("./variants-7A7723L7.js");
1727
+ const { applyVariant: applyVariant2, cleanVariants: cleanVariants2 } = await import("./variants-SM6NRX4U.js");
1712
1728
  const cwd = process.cwd();
1713
1729
  const result = applyVariant2(cwd, id);
1714
1730
  if (!result.ok) {
@@ -1720,7 +1736,7 @@ variantsCommand.command("apply").description("Apply one variant\u2019s changes t
1720
1736
  console.log(pc.green(`\u2713 applied ${id} to the working tree`) + pc.dim(" \u2014 review with git diff"));
1721
1737
  });
1722
1738
  variantsCommand.command("clean").description("Discard all variants").action(async () => {
1723
- const { cleanVariants: cleanVariants2 } = await import("./variants-7A7723L7.js");
1739
+ const { cleanVariants: cleanVariants2 } = await import("./variants-SM6NRX4U.js");
1724
1740
  const count = cleanVariants2(process.cwd());
1725
1741
  console.log(pc.dim(`removed ${count} variant(s)`));
1726
1742
  });
@@ -1754,7 +1770,7 @@ program.command("brief").description("Set a committed design direction for this
1754
1770
  console.log(pc.dim("every squint ask in this repo now holds this direction \u2014 edit the file to remix"));
1755
1771
  });
1756
1772
  program.command("check").description("Run this project\u2019s quality gates (typecheck, lint, test, build)").action(async () => {
1757
- const { detectGates: detectGates2, runGates: runGates2 } = await import("./gates-I2GGQGMA.js");
1773
+ const { detectGates: detectGates2, runGates: runGates2 } = await import("./gates-ZC67NMW3.js");
1758
1774
  const cwd = process.cwd();
1759
1775
  const gates = detectGates2(cwd);
1760
1776
  if (gates.length === 0) {
@@ -1770,7 +1786,7 @@ program.command("check").description("Run this project\u2019s quality gates (typ
1770
1786
  if (results.some((r) => !r.ok)) process.exitCode = 1;
1771
1787
  });
1772
1788
  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) => {
1773
- const { captureViewports: captureViewports2 } = await import("./preview-LZDH65ID.js");
1789
+ const { captureViewports: captureViewports2 } = await import("./preview-ZHUQT7TJ.js");
1774
1790
  const result = await captureViewports2(process.cwd(), url);
1775
1791
  if (!result) {
1776
1792
  console.error(pc.red("\u2717 no Chrome/Chromium found"));
@@ -1797,11 +1813,16 @@ configCommand.command("path").description("Show config file locations").action((
1797
1813
  console.log(`global ${paths.globalFile}`);
1798
1814
  console.log(`project ${paths.projectFile}`);
1799
1815
  });
1800
- program.action(() => {
1816
+ program.action(async () => {
1801
1817
  const cwd = process.cwd();
1802
1818
  const config = loadConfig(defaultPaths(cwd));
1803
1819
  const engineId = resolveEngineId(config);
1804
1820
  const model = resolveModel(config, engineId);
1821
+ let theme2 = config.theme;
1822
+ if (!theme2 && !process.env.NO_COLOR) {
1823
+ const { detectBackground } = await import("./background-J7OQTYEC.js");
1824
+ if (await detectBackground() === "light") theme2 = "light";
1825
+ }
1805
1826
  render(
1806
1827
  /* @__PURE__ */ jsx4(
1807
1828
  App,
@@ -1815,7 +1836,7 @@ program.action(() => {
1815
1836
  autoCheck: config.autoCheck,
1816
1837
  bell: config.bell,
1817
1838
  budgetUsd: config.budgetUsd,
1818
- initialTheme: config.theme
1839
+ initialTheme: theme2
1819
1840
  }
1820
1841
  )
1821
1842
  );
@@ -5,7 +5,7 @@ import {
5
5
  detectGates,
6
6
  runGate,
7
7
  runGates
8
- } from "./chunk-DLCRYRWQ.js";
8
+ } from "./chunk-62JNF5M2.js";
9
9
  export {
10
10
  buildGatePrompt,
11
11
  detectFastGates,
@@ -10,10 +10,10 @@ import {
10
10
  probeRuntime,
11
11
  routeShotName,
12
12
  runtimeSummary
13
- } from "./chunk-CIQ3OUAF.js";
13
+ } from "./chunk-DKQ5KJD7.js";
14
14
  import "./chunk-OC6RU6XH.js";
15
- import "./chunk-P3V5CWH5.js";
16
- import "./chunk-2LRIKWBU.js";
15
+ import "./chunk-CJXYSQNB.js";
16
+ import "./chunk-CH4GNJCZ.js";
17
17
  export {
18
18
  VIEWPORTS,
19
19
  buildReviewPrompt,
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runAgent
4
- } from "./chunk-OTG4ZB4R.js";
5
- import "./chunk-2LRIKWBU.js";
4
+ } from "./chunk-TI2R7QRL.js";
5
+ import "./chunk-CH4GNJCZ.js";
6
6
  export {
7
7
  runAgent
8
8
  };
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ screenshotVariants
4
+ } from "./chunk-QNNT6FP4.js";
5
+ import "./chunk-XLNTTZX4.js";
6
+ import "./chunk-P3H4N2EN.js";
7
+ import "./chunk-TI2R7QRL.js";
8
+ import "./chunk-CJXYSQNB.js";
9
+ import "./chunk-CH4GNJCZ.js";
10
+ export {
11
+ screenshotVariants
12
+ };
@@ -9,10 +9,10 @@ import {
9
9
  runVariants,
10
10
  variantPrompt,
11
11
  variantsRoot
12
- } from "./chunk-LCS47EAG.js";
12
+ } from "./chunk-XLNTTZX4.js";
13
13
  import "./chunk-P3H4N2EN.js";
14
- import "./chunk-OTG4ZB4R.js";
15
- import "./chunk-2LRIKWBU.js";
14
+ import "./chunk-TI2R7QRL.js";
15
+ import "./chunk-CH4GNJCZ.js";
16
16
  export {
17
17
  MAX_VARIANTS,
18
18
  applyVariant,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aayambansal/squint",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
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",
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- screenshotVariants
4
- } from "./chunk-2IW2MMCY.js";
5
- import "./chunk-LCS47EAG.js";
6
- import "./chunk-P3H4N2EN.js";
7
- import "./chunk-OTG4ZB4R.js";
8
- import "./chunk-P3V5CWH5.js";
9
- import "./chunk-2LRIKWBU.js";
10
- export {
11
- screenshotVariants
12
- };