@aayambansal/squint 0.1.0 → 0.1.2
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 +161 -104
- package/dist/{cdp-5OHNGQXV.js → cdp-R7Y2XSZQ.js} +1 -1
- package/dist/{chunk-OJTW5SYY.js → chunk-ABF4QAMZ.js} +45 -2
- package/dist/{chunk-4XHTAWTH.js → chunk-MOYLED4S.js} +15 -5
- package/dist/cli.js +12 -7
- package/dist/{preview-5PCHDJL7.js → preview-AA6YIE5W.js} +2 -2
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,58 +1,98 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<pre>
|
|
4
|
+
____ ___ _ _ ___ _ _ _____
|
|
5
|
+
/ ___| / _ \| | | |_ _| \ | |_ _|
|
|
6
|
+
\___ \| | | | | | || || \| | | |
|
|
7
|
+
___) | |_| | |_| || || |\ | | |
|
|
8
|
+
|____/ \__\_\\___/|___|_| \_| |_|
|
|
9
|
+
</pre>
|
|
4
10
|
|
|
5
|
-
Lovable
|
|
11
|
+
### Lovable for your terminal
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
┌──────────────────────────────────────────────────────────┐
|
|
9
|
-
│ ❯ make the pricing page feel premium │
|
|
10
|
-
│ ⚙ Read · src/pages/Pricing.tsx │
|
|
11
|
-
│ ⚙ Edit · src/pages/Pricing.tsx │
|
|
12
|
-
│ Reworked the tier cards around a real type scale and │
|
|
13
|
-
│ tightened the palette to ink + one accent… │
|
|
14
|
-
│ · done · 41s · $0.18 │
|
|
15
|
-
│ ✗ dev server: 1 error line(s) │
|
|
16
|
-
│ error TS2304: Cannot find name 'Tier' │
|
|
17
|
-
│ · auto-fix attempt 1/2 │
|
|
18
|
-
│ ⛑ fix dev server errors │
|
|
19
|
-
│ · done · 12s · $0.05 │
|
|
20
|
-
│ │
|
|
21
|
-
│ ❯ ▏ │
|
|
22
|
-
│ claude · my-app · http://localhost:5173 · /help │
|
|
23
|
-
└──────────────────────────────────────────────────────────┘
|
|
24
|
-
```
|
|
13
|
+
Any repo. Any coding agent. The whole loop, owned by you.
|
|
25
14
|
|
|
26
|
-
|
|
15
|
+
[](https://www.npmjs.com/package/@aayambansal/squint)
|
|
16
|
+
[](https://github.com/aayambansal/squint/actions/workflows/ci.yml)
|
|
17
|
+
[](./LICENSE)
|
|
18
|
+
[](#engines)
|
|
19
|
+
[](./CONTRIBUTING.md)
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
squint is a frontend harness that sits on top of the coding agents you already have — Claude
|
|
26
|
+
Code, Codex CLI, Gemini CLI, OpenCode, Amp, Cursor CLI, Copilot CLI, Aider — and turns them
|
|
27
|
+
into a design-obsessed app builder that works on any repo, local-first.
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
5. **The agent looks at its work.** `/review` screenshots the running app at mobile/tablet/desktop and re-prompts the engine to critique what it can *see* — then fix it.
|
|
33
|
-
6. **Gates keep it honest.** `/check` runs typecheck → lint → test → build and feeds failures back, with instructions not to weaken the checks.
|
|
34
|
-
7. **Everything is reversible.** Each ask is snapshotted via git plumbing; `/undo` reverts the whole turn — while your own uncommitted work survives.
|
|
35
|
-
8. **Point at things.** `squint tag` (wired automatically in new apps) adds an element picker to your dev server: **Alt+S**, click any element, and a self-locating reference (`src/App.tsx <h1 class="…"> "Ready."`) lands on your clipboard to paste into squint.
|
|
36
|
-
9. **Explore in parallel.** `squint variants gen 3 "landing page for …"` builds the same ask three ways — three git worktrees, three *different* committed aesthetic directions, one screenshot each. Pick with your eyes, `squint variants apply <id>`.
|
|
29
|
+
Lovable proved the loop: prompt, generate, preview, auto-fix, iterate. But it's a closed SaaS.
|
|
30
|
+
You can't point it at an existing codebase, choose your agent or model, or own the loop.
|
|
31
|
+
squint is that missing layer, in your terminal. No cloud project, no lock-in, no secrets
|
|
32
|
+
stored — API keys stay wherever your agent CLIs already read them.
|
|
37
33
|
|
|
38
|
-
##
|
|
34
|
+
## Run it
|
|
39
35
|
|
|
40
36
|
```sh
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
npx @aayambansal/squint # run without installing
|
|
38
|
+
npm install -g @aayambansal/squint # or install; then just: squint
|
|
43
39
|
```
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
Start from nothing, like Lovable:
|
|
46
42
|
|
|
47
43
|
```sh
|
|
48
|
-
|
|
49
|
-
cd squint
|
|
50
|
-
npm install && npm run build && npm link
|
|
44
|
+
squint init my-app # Vite + React + TS + Tailwind v4, token-first CSS
|
|
45
|
+
cd my-app && squint # open the TUI, describe what to build
|
|
51
46
|
```
|
|
52
47
|
|
|
53
|
-
|
|
48
|
+
Or on any existing repo:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
cd your-project && squint
|
|
52
|
+
```
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
From source: `git clone https://github.com/aayambansal/squint.git && cd squint && npm install && npm run build && npm link`.
|
|
55
|
+
|
|
56
|
+
## The loop
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
you describe ---> design brief ---> engine ---> edits land in your repo
|
|
60
|
+
^ |
|
|
61
|
+
| +-----------------------------+
|
|
62
|
+
| | dev server build errors caught
|
|
63
|
+
| | CDP probe exceptions . console . 404s
|
|
64
|
+
| | a11y sweep alt text . labels . headings
|
|
65
|
+
| | screenshots 390 / 768 / 1440
|
|
66
|
+
| +-----------------------------+
|
|
67
|
+
| | |
|
|
68
|
+
+--- /undo reverts /fix (auto) /review: the agent
|
|
69
|
+
/variants explores critiques what it SEES
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
1. **You describe.** Every ask is wrapped in squint's design brief — built from studying
|
|
73
|
+
Lovable's leaked prompts, v0's design rules, and the documented catalog of AI-slop tells:
|
|
74
|
+
direction before code, tokens are the system, banned generic patterns. Override per
|
|
75
|
+
project with `.squint/brief.md`.
|
|
76
|
+
2. **Your agent builds.** squint drives whichever engine you choose, headlessly, streaming
|
|
77
|
+
token by token.
|
|
78
|
+
3. **The dev server judges.** Build errors after every turn route straight back to the
|
|
79
|
+
engine (`/fix`, or automatic with `autoFix`).
|
|
80
|
+
4. **The runtime is watched.** After every clean turn squint loads the page headlessly (~2s)
|
|
81
|
+
and catches what the server never prints: blank pages, exceptions, failed requests.
|
|
82
|
+
5. **The agent looks at its work.** `/review` screenshots mobile/tablet/desktop and
|
|
83
|
+
re-prompts the engine to critique what it can see — then fix it.
|
|
84
|
+
6. **Gates keep it honest.** `/check` runs typecheck, lint, test, build — failures come
|
|
85
|
+
back with orders not to weaken the checks.
|
|
86
|
+
7. **Everything is reversible.** Each ask is snapshotted via git plumbing; `/undo` reverts
|
|
87
|
+
the whole turn while your own uncommitted work survives.
|
|
88
|
+
8. **Point at things.** Alt+S in the browser, click any element, and a self-locating
|
|
89
|
+
reference lands on your clipboard to paste into squint.
|
|
90
|
+
9. **Explore in parallel.** `squint variants gen 3 "<ask>"` builds the same ask three ways —
|
|
91
|
+
three worktrees, three committed aesthetic directions. Pick with your eyes.
|
|
92
|
+
|
|
93
|
+
## Engines
|
|
94
|
+
|
|
95
|
+
| engine | id | install |
|
|
56
96
|
| --- | --- | --- |
|
|
57
97
|
| Claude Code | `claude` | `npm i -g @anthropic-ai/claude-code` |
|
|
58
98
|
| Codex CLI | `codex` | `npm i -g @openai/codex` |
|
|
@@ -63,88 +103,105 @@ You need at least one engine installed (`squint doctor` shows what's found):
|
|
|
63
103
|
| Copilot CLI | `copilot` | `npm i -g @github/copilot` |
|
|
64
104
|
| Aider | `aider` | `pip install aider-install && aider-install` |
|
|
65
105
|
|
|
66
|
-
|
|
106
|
+
`squint doctor` shows what's found. One engine is enough; session resume works everywhere
|
|
107
|
+
the backend supports it (Claude, Codex, OpenCode, Amp, Cursor).
|
|
67
108
|
|
|
68
|
-
##
|
|
69
|
-
|
|
70
|
-
**Start fresh** (Lovable-style, from nothing):
|
|
109
|
+
## Commands
|
|
71
110
|
|
|
72
111
|
```sh
|
|
73
|
-
squint
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
**Or on any existing repo:**
|
|
78
|
-
|
|
79
|
-
```sh
|
|
80
|
-
cd your-project && squint
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Headless / scriptable:**
|
|
84
|
-
|
|
85
|
-
```sh
|
|
86
|
-
squint run "add a dark mode toggle to the navbar"
|
|
112
|
+
squint # the TUI, in the current repo
|
|
113
|
+
squint run "add a dark mode toggle"
|
|
87
114
|
squint run -e codex -m gpt-5 "tighten the hero spacing"
|
|
88
|
-
squint run --json "
|
|
89
|
-
squint check #
|
|
115
|
+
squint run --json "..." # normalized ndjson events for scripting
|
|
116
|
+
squint check # gates: typecheck -> lint -> test -> build
|
|
90
117
|
squint shot http://localhost:5173 # screenshots at 390/768/1440
|
|
91
|
-
squint brief # list design directions
|
|
92
|
-
squint
|
|
93
|
-
squint doctor # engines + Chrome + WebSocket check
|
|
118
|
+
squint brief # list design directions
|
|
119
|
+
squint brief cinematic-dark # commit one for this repo
|
|
94
120
|
squint tag # add the Alt+S element picker to any Vite app
|
|
95
|
-
squint variants gen 3 "<ask>" # 3 parallel design explorations
|
|
96
|
-
squint variants apply terminal # keep the winner
|
|
121
|
+
squint variants gen 3 "<ask>" # 3 parallel design explorations
|
|
122
|
+
squint variants apply terminal # keep the winner
|
|
123
|
+
squint config set engine claude
|
|
124
|
+
squint config set models.claude claude-sonnet-5
|
|
125
|
+
squint config set autoDev true # dev server starts with the TUI
|
|
126
|
+
squint config set autoFix true # errors auto-route back (max 2 tries)
|
|
127
|
+
squint doctor # engines + Chrome + WebSocket check
|
|
97
128
|
```
|
|
98
129
|
|
|
99
|
-
|
|
130
|
+
Inside the TUI: `/dev` `/check` `/fix` `/shot` `/review [focus]` `/undo` `/resume`
|
|
131
|
+
`/engine <id>` `/model <name>` `/clear` — `Esc` interrupts, arrows recall history.
|
|
100
132
|
|
|
101
|
-
|
|
102
|
-
squint brief # editorial-minimal · terminal · warm-editorial · data-dense
|
|
103
|
-
# cinematic-dark · playful · brutalist
|
|
104
|
-
squint brief cinematic-dark # writes .squint/brief.md — plain markdown, made to remix
|
|
105
|
-
```
|
|
133
|
+
## Design directions
|
|
106
134
|
|
|
107
|
-
|
|
135
|
+
`squint brief` writes a committed direction to `.squint/brief.md` so every session holds the
|
|
136
|
+
same look. Seven families, organized by aesthetic rather than industry — the researched
|
|
137
|
+
cheapest route to non-generic work:
|
|
108
138
|
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
squint config set autoDev true # start the dev server with the TUI
|
|
113
|
-
squint config set autoFix true # auto-send build/runtime errors back (max 2 tries)
|
|
114
|
-
squint config set autoProbe false # disable the post-turn runtime probe
|
|
115
|
-
squint config set --project engine codex # per-repo override
|
|
139
|
+
```
|
|
140
|
+
editorial-minimal terminal warm-editorial data-dense
|
|
141
|
+
cinematic-dark playful brutalist
|
|
116
142
|
```
|
|
117
143
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
| Command | What it does |
|
|
121
|
-
| --- | --- |
|
|
122
|
-
| `/dev` | start/stop the project dev server (auto-detected script + package manager) |
|
|
123
|
-
| `/check` | run quality gates |
|
|
124
|
-
| `/fix` | send captured errors / failed gates to the engine |
|
|
125
|
-
| `/shot` | screenshot the app at mobile/tablet/desktop |
|
|
126
|
-
| `/review [focus]` | screenshots + the engine critiques its own rendered work, then fixes it |
|
|
127
|
-
| `/undo` | revert the whole last ask (your own uncommitted work survives) |
|
|
128
|
-
| `/resume` | pick the previous session back up after a restart |
|
|
129
|
-
| `/engine <id>` · `/model <name>` | switch backend or model mid-session |
|
|
130
|
-
| `/clear` | new session |
|
|
131
|
-
| `Esc` | interrupt the running turn · `↑/↓` prompt history |
|
|
144
|
+
Each is a concrete commitment — type pairing, color strategy, spacing rhythm, motion stance,
|
|
145
|
+
and an avoid-list tuned to that family's failure modes. Plain markdown, made to be remixed.
|
|
132
146
|
|
|
133
|
-
##
|
|
147
|
+
## How it fits together
|
|
134
148
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
149
|
+
```
|
|
150
|
+
tui (ink) cli (run / check / shot / variants ...)
|
|
151
|
+
\ /
|
|
152
|
+
`-----------+---------------'
|
|
153
|
+
|
|
|
154
|
+
+------------------------+
|
|
155
|
+
| squint harness | brief . gates . fix loops
|
|
156
|
+
| prompt enrichment | snapshots . sessions
|
|
157
|
+
+-----+------------+-----+
|
|
158
|
+
| |
|
|
159
|
+
+----------+---+ +---+---------------------+
|
|
160
|
+
| engine layer | | eyes |
|
|
161
|
+
| 8 adapters, | | dev-server watcher |
|
|
162
|
+
| one event | | CDP runtime + a11y |
|
|
163
|
+
| stream | | headless screenshots |
|
|
164
|
+
+------+-------+ +-------------------------+
|
|
165
|
+
|
|
|
166
|
+
claude . codex . gemini . opencode . amp . cursor . copilot . aider
|
|
167
|
+
```
|
|
139
168
|
|
|
140
|
-
|
|
169
|
+
Engines are dumb translators: build a headless invocation, parse the stream into one
|
|
170
|
+
normalized event model. Claude Code, Amp, and Cursor share a single wire-protocol parser.
|
|
171
|
+
All product behavior lives in the harness, so a new engine is ~80 lines.
|
|
141
172
|
|
|
142
|
-
|
|
143
|
-
- Variants inside the TUI with an inline picker
|
|
144
|
-
- Deeper gates: axe accessibility audits, Lighthouse budgets, visual regression baselines
|
|
173
|
+
## Repo layout
|
|
145
174
|
|
|
146
|
-
|
|
175
|
+
| path | what it is |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `src/engines` | 8 adapters + the shared Claude wire-protocol parser |
|
|
178
|
+
| `src/runner` | subprocess spawn → normalized event stream, abort support |
|
|
179
|
+
| `src/prompt` | the design brief + 7 aesthetic families |
|
|
180
|
+
| `src/devserver` | dev-server manager + build-error detection |
|
|
181
|
+
| `src/preview` | Chrome discovery, CDP client, screenshots, a11y sweep |
|
|
182
|
+
| `src/gates` | quality gates (typecheck / lint / test / build) |
|
|
183
|
+
| `src/tagger` | the Alt+S element picker (embedded Vite plugin) |
|
|
184
|
+
| `src/variants` | parallel exploration worktrees |
|
|
185
|
+
| `src/scaffold` | the `squint init` template |
|
|
186
|
+
| `src/vcs` | `/undo` git snapshots |
|
|
187
|
+
| `src/state` | per-project session persistence |
|
|
188
|
+
| `src/tui` | the Ink app |
|
|
189
|
+
| `docs/research` | the knowledge base squint is built from |
|
|
190
|
+
|
|
191
|
+
## Docs
|
|
192
|
+
|
|
193
|
+
- [Architecture](./docs/design/2026-07-25-architecture.md)
|
|
194
|
+
- [How Lovable works under the hood](./docs/research/lovable.md)
|
|
195
|
+
- [Making agents produce excellent frontend work](./docs/research/frontend-quality.md)
|
|
196
|
+
- [Engine headless interfaces + TUI stack](./docs/research/engines-and-tui.md)
|
|
197
|
+
- [Changelog](./CHANGELOG.md)
|
|
198
|
+
|
|
199
|
+
## Contributing
|
|
200
|
+
|
|
201
|
+
Built in the open, moving fast. Every change ships as a pull request with CI green. See
|
|
202
|
+
[CONTRIBUTING.md](./CONTRIBUTING.md) — adding an engine adapter is the best first issue
|
|
203
|
+
there is.
|
|
147
204
|
|
|
148
205
|
## License
|
|
149
206
|
|
|
150
|
-
MIT © Aayam Bansal
|
|
207
|
+
[MIT](./LICENSE) © Aayam Bansal. Take it, run it, ship it.
|
|
@@ -5,6 +5,37 @@ import { spawn } from "child_process";
|
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import os from "os";
|
|
7
7
|
import path from "path";
|
|
8
|
+
var A11Y_AUDIT = `(() => {
|
|
9
|
+
const out = [];
|
|
10
|
+
const name = (el) => (el.getAttribute('aria-label') || el.textContent || el.getAttribute('title') || '').trim();
|
|
11
|
+
const short = (el) => '<' + el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') + (el.className && typeof el.className === 'string' ? '.' + el.className.split(/\\s+/).slice(0, 2).join('.') : '') + '>';
|
|
12
|
+
if (!document.documentElement.getAttribute('lang')) out.push('document missing lang attribute');
|
|
13
|
+
if (!document.title.trim()) out.push('document missing <title>');
|
|
14
|
+
for (const img of document.querySelectorAll('img:not([alt])')) out.push('img missing alt: ' + (img.getAttribute('src') || '').split('/').pop());
|
|
15
|
+
for (const el of document.querySelectorAll('button, a[href]')) {
|
|
16
|
+
if (!name(el) && !el.querySelector('img[alt]')) out.push(el.tagName.toLowerCase() + ' without accessible name ' + short(el));
|
|
17
|
+
}
|
|
18
|
+
for (const el of document.querySelectorAll('input:not([type=hidden]), select, textarea')) {
|
|
19
|
+
const labelled = el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') || el.closest('label') || (el.id && document.querySelector('label[for="' + el.id + '"]'));
|
|
20
|
+
if (!labelled) out.push('form control without label ' + short(el));
|
|
21
|
+
}
|
|
22
|
+
let last = 0;
|
|
23
|
+
for (const h of document.querySelectorAll('h1,h2,h3,h4,h5,h6')) {
|
|
24
|
+
const level = Number(h.tagName[1]);
|
|
25
|
+
if (last && level > last + 1) out.push('heading order jumps h' + last + ' \u2192 h' + level + ' ' + short(h));
|
|
26
|
+
last = level;
|
|
27
|
+
}
|
|
28
|
+
let tiny = 0;
|
|
29
|
+
for (const el of document.querySelectorAll('button, a[href]')) {
|
|
30
|
+
const r = el.getBoundingClientRect();
|
|
31
|
+
if (r.width > 0 && (r.width < 24 || r.height < 24)) tiny++;
|
|
32
|
+
}
|
|
33
|
+
if (tiny > 0) out.push(tiny + ' interactive element(s) smaller than 24x24px');
|
|
34
|
+
for (const el of document.querySelectorAll('[tabindex]')) {
|
|
35
|
+
if (Number(el.getAttribute('tabindex')) > 0) out.push('positive tabindex ' + short(el));
|
|
36
|
+
}
|
|
37
|
+
return out.slice(0, 20);
|
|
38
|
+
})()`;
|
|
8
39
|
function hasWebSocket() {
|
|
9
40
|
return typeof globalThis.WebSocket === "function";
|
|
10
41
|
}
|
|
@@ -118,10 +149,11 @@ var describe = (value) => {
|
|
|
118
149
|
}
|
|
119
150
|
return String(value);
|
|
120
151
|
};
|
|
121
|
-
async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500) {
|
|
152
|
+
async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, audit = false) {
|
|
122
153
|
const { child, wsUrl, profileDir } = await launchChrome(chromePath);
|
|
123
154
|
const report = { consoleErrors: [], pageErrors: [], failedRequests: [] };
|
|
124
155
|
const shots = [];
|
|
156
|
+
let a11y = [];
|
|
125
157
|
const requests = /* @__PURE__ */ new Map();
|
|
126
158
|
let connection = null;
|
|
127
159
|
try {
|
|
@@ -166,6 +198,17 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500) {
|
|
|
166
198
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
167
199
|
}
|
|
168
200
|
await new Promise((resolve) => setTimeout(resolve, settleMs));
|
|
201
|
+
if (audit) {
|
|
202
|
+
try {
|
|
203
|
+
const { result } = await connection.send(
|
|
204
|
+
"Runtime.evaluate",
|
|
205
|
+
{ expression: A11Y_AUDIT, returnByValue: true },
|
|
206
|
+
sessionId
|
|
207
|
+
);
|
|
208
|
+
if (Array.isArray(result?.value)) a11y = result.value.map(String);
|
|
209
|
+
} catch {
|
|
210
|
+
}
|
|
211
|
+
}
|
|
169
212
|
for (const viewport of viewports) {
|
|
170
213
|
await connection.send(
|
|
171
214
|
"Emulation.setDeviceMetricsOverride",
|
|
@@ -188,7 +231,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500) {
|
|
|
188
231
|
child.kill("SIGKILL");
|
|
189
232
|
setTimeout(() => fs.rmSync(profileDir, { recursive: true, force: true }), 500).unref?.();
|
|
190
233
|
}
|
|
191
|
-
return { report, shots };
|
|
234
|
+
return { report, shots, a11y };
|
|
192
235
|
}
|
|
193
236
|
|
|
194
237
|
export {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
cdpCapture,
|
|
4
4
|
hasWebSocket
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ABF4QAMZ.js";
|
|
6
6
|
import {
|
|
7
7
|
findChrome,
|
|
8
8
|
screenshot
|
|
@@ -74,8 +74,8 @@ async function captureViewports(cwd, url) {
|
|
|
74
74
|
const dir = previewDir(cwd);
|
|
75
75
|
if (hasWebSocket()) {
|
|
76
76
|
try {
|
|
77
|
-
const { report, shots: shots2 } = await cdpCapture(chrome, url, dir, VIEWPORTS);
|
|
78
|
-
return { shots: shots2, errors: [], runtime: report };
|
|
77
|
+
const { report, shots: shots2, a11y } = await cdpCapture(chrome, url, dir, VIEWPORTS, 2500, true);
|
|
78
|
+
return { shots: shots2, errors: [], runtime: report, a11y };
|
|
79
79
|
} catch {
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -141,13 +141,23 @@ function buildRuntimeFixPrompt(report) {
|
|
|
141
141
|
|
|
142
142
|
Diagnose and fix the root causes, then confirm the app loads clean.`;
|
|
143
143
|
}
|
|
144
|
-
function
|
|
144
|
+
function a11ySection(findings) {
|
|
145
|
+
if (!findings || findings.length === 0) return "";
|
|
146
|
+
return `
|
|
147
|
+
|
|
148
|
+
## Accessibility sweep findings
|
|
149
|
+
|
|
150
|
+
${findings.join("\n")}
|
|
151
|
+
|
|
152
|
+
Fix these as part of the pass \u2014 they are objective defects, not style preferences.`;
|
|
153
|
+
}
|
|
154
|
+
function buildReviewPrompt(shots, extra, runtime, a11y) {
|
|
145
155
|
const list = shots.map((s) => `- ${s.name}: ${s.path}`).join("\n");
|
|
146
156
|
return `Screenshots of the running app were just captured:
|
|
147
157
|
|
|
148
158
|
${list}
|
|
149
159
|
|
|
150
|
-
Read each screenshot and review the rendered UI against the design standards you were given. Check: visual hierarchy and spacing rhythm, typography, color and contrast, alignment, empty-looking or broken regions, and whether the mobile capture shows horizontal overflow or cramped layout. List the concrete issues you can SEE (not hypothetical ones), ranked by visual impact${extra ? `, with special attention to: ${extra}` : ""}. Then fix them and verify the app still builds.${runtimeSection(runtime)}`;
|
|
160
|
+
Read each screenshot and review the rendered UI against the design standards you were given. Check: visual hierarchy and spacing rhythm, typography, color and contrast, alignment, empty-looking or broken regions, and whether the mobile capture shows horizontal overflow or cramped layout. List the concrete issues you can SEE (not hypothetical ones), ranked by visual impact${extra ? `, with special attention to: ${extra}` : ""}. Then fix them and verify the app still builds.${runtimeSection(runtime)}${a11ySection(a11y)}`;
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
export {
|
package/dist/cli.js
CHANGED
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
probeRuntime,
|
|
25
25
|
runtimeSummary,
|
|
26
26
|
saveState
|
|
27
|
-
} from "./chunk-
|
|
28
|
-
import "./chunk-
|
|
27
|
+
} from "./chunk-MOYLED4S.js";
|
|
28
|
+
import "./chunk-ABF4QAMZ.js";
|
|
29
29
|
import "./chunk-MURKQ7SI.js";
|
|
30
30
|
import {
|
|
31
31
|
detectEngines,
|
|
@@ -51,7 +51,7 @@ var ConfigSchema = z.object({
|
|
|
51
51
|
/** Default engine id (claude, codex, gemini, opencode, amp, cursor, copilot, aider). */
|
|
52
52
|
engine: z.string().optional(),
|
|
53
53
|
/** Per-engine model overrides, e.g. { claude: "claude-sonnet-5" }. */
|
|
54
|
-
models: z.record(z.string()).optional(),
|
|
54
|
+
models: z.record(z.string(), z.string()).optional(),
|
|
55
55
|
/** Start the project's dev server automatically when the TUI opens. */
|
|
56
56
|
autoDev: z.boolean().optional(),
|
|
57
57
|
/** Automatically send dev-server errors back to the engine (max 2 attempts). */
|
|
@@ -409,6 +409,11 @@ ${errors.slice(-5).join("\n")}`);
|
|
|
409
409
|
push("status", "runtime clean \u2014 no console errors, exceptions, or failed requests");
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
|
+
if (result.a11y && result.a11y.length > 0) {
|
|
413
|
+
push("error", `a11y: ${result.a11y.length} finding(s)
|
|
414
|
+
${result.a11y.slice(0, 5).join("\n")}`);
|
|
415
|
+
push("status", "/review folds these into the fix pass");
|
|
416
|
+
}
|
|
412
417
|
return result.shots.length > 0 ? result : null;
|
|
413
418
|
}, [cwd, devUrl, push]);
|
|
414
419
|
const submit = useCallback(
|
|
@@ -505,7 +510,7 @@ ${errors.slice(-5).join("\n")}`);
|
|
|
505
510
|
const result = await capture();
|
|
506
511
|
if (result) {
|
|
507
512
|
await runTurn(
|
|
508
|
-
buildReviewPrompt(result.shots, arg || void 0, result.runtime),
|
|
513
|
+
buildReviewPrompt(result.shots, arg || void 0, result.runtime, result.a11y),
|
|
509
514
|
`\u{1F441} review rendered UI${arg ? ` \xB7 ${arg}` : ""}`
|
|
510
515
|
);
|
|
511
516
|
}
|
|
@@ -646,7 +651,7 @@ ${errors.slice(-5).join("\n")}`);
|
|
|
646
651
|
|
|
647
652
|
// src/cli.tsx
|
|
648
653
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
649
|
-
var VERSION = "0.1.
|
|
654
|
+
var VERSION = "0.1.2";
|
|
650
655
|
var program = new Command();
|
|
651
656
|
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
652
657
|
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(
|
|
@@ -690,7 +695,7 @@ program.command("doctor").description("Check squint prerequisites and engine ava
|
|
|
690
695
|
console.log(`${status} ${engine.name}${binaryPath ? "" : pc.dim(` \u2014 install: ${engine.install}`)}`);
|
|
691
696
|
}
|
|
692
697
|
const { findChrome } = await import("./chrome-4WNYZZ42.js");
|
|
693
|
-
const { hasWebSocket } = await import("./cdp-
|
|
698
|
+
const { hasWebSocket } = await import("./cdp-R7Y2XSZQ.js");
|
|
694
699
|
const chrome = findChrome();
|
|
695
700
|
console.log(
|
|
696
701
|
chrome ? `${pc.green("\u2713")} Chrome ${pc.dim(chrome)}` : `${pc.yellow("\u25CB")} Chrome ${pc.dim("\u2014 screenshots and runtime probing disabled")}`
|
|
@@ -881,7 +886,7 @@ program.command("check").description("Run this project\u2019s quality gates (typ
|
|
|
881
886
|
if (results.some((r) => !r.ok)) process.exitCode = 1;
|
|
882
887
|
});
|
|
883
888
|
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) => {
|
|
884
|
-
const { captureViewports: captureViewports2 } = await import("./preview-
|
|
889
|
+
const { captureViewports: captureViewports2 } = await import("./preview-AA6YIE5W.js");
|
|
885
890
|
const result = await captureViewports2(process.cwd(), url);
|
|
886
891
|
if (!result) {
|
|
887
892
|
console.error(pc.red("\u2717 no Chrome/Chromium found"));
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
previewDir,
|
|
8
8
|
probeRuntime,
|
|
9
9
|
runtimeSummary
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-MOYLED4S.js";
|
|
11
|
+
import "./chunk-ABF4QAMZ.js";
|
|
12
12
|
import "./chunk-MURKQ7SI.js";
|
|
13
13
|
import "./chunk-YIVPCWSG.js";
|
|
14
14
|
export {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aayambansal/squint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
7
|
-
"author": "Aayam Bansal
|
|
7
|
+
"author": "Aayam Bansal",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/aayambansal/squint.git"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
39
|
+
"node": ">=22"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsup",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
"prepublishOnly": "npm run check"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"commander": "^
|
|
51
|
-
"ink": "^
|
|
50
|
+
"commander": "^15.0.0",
|
|
51
|
+
"ink": "^7.1.1",
|
|
52
52
|
"picocolors": "^1.1.1",
|
|
53
53
|
"react": "^19.1.0",
|
|
54
|
-
"zod": "^
|
|
54
|
+
"zod": "^4.4.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@types/node": "^
|
|
57
|
+
"@types/node": "^26.1.1",
|
|
58
58
|
"@types/react": "^19.1.0",
|
|
59
59
|
"tsup": "^8.4.0",
|
|
60
|
-
"typescript": "^
|
|
61
|
-
"vitest": "^
|
|
60
|
+
"typescript": "^7.0.2",
|
|
61
|
+
"vitest": "^4.1.10"
|
|
62
62
|
}
|
|
63
63
|
}
|