@aayambansal/squint 0.4.8 → 0.5.1

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.
Files changed (36) hide show
  1. package/README.md +3 -0
  2. package/dist/App-KTNAMHY6.js +24 -0
  3. package/dist/{cdp-2VVQOHWM.js → cdp-D2F4P7UQ.js} +1 -1
  4. package/dist/{chrome-SBV3H77F.js → chrome-EEXJCG6W.js} +2 -2
  5. package/dist/{chunk-QCHBDP46.js → chunk-2I3YHT6P.js} +3 -3
  6. package/dist/{chunk-JZMJGLIZ.js → chunk-2NYNTVMX.js} +2 -2
  7. package/dist/{chunk-BGBG2MNY.js → chunk-3YCG234A.js} +80 -2
  8. package/dist/{flows-6LK7NGWS.js → chunk-4XSHHGA6.js} +2 -1
  9. package/dist/{chunk-VH7OOFQP.js → chunk-6NPUVPQD.js} +2 -2
  10. package/dist/{chunk-ATNTD3V5.js → chunk-AMA3F2Y2.js} +1 -1
  11. package/dist/{chunk-7RFQWOQV.js → chunk-ERF2SPUW.js} +4 -4
  12. package/dist/{chunk-YGPVP4YB.js → chunk-G5NS2IFQ.js} +68 -17
  13. package/dist/{chunk-KVYGPLWW.js → chunk-GV2HAALP.js} +4 -0
  14. package/dist/chunk-IEQGTVWN.js +52 -0
  15. package/dist/{chunk-YGSF2TSO.js → chunk-J7ITMK6X.js} +1 -1
  16. package/dist/{contextDoctor-A26C2ZDN.js → chunk-LESZB7DT.js} +6 -3
  17. package/dist/{chunk-IMDRXXFU.js → chunk-PYC53J7D.js} +1 -1
  18. package/dist/{chunk-ARDV4XH6.js → chunk-UBADCBPT.js} +14 -59
  19. package/dist/{chunk-43NQNIJY.js → chunk-VJ2QKHYK.js} +2 -0
  20. package/dist/cli.js +105 -27
  21. package/dist/{commands-BY44HDQ6.js → commands-WC7V6EAE.js} +1 -1
  22. package/dist/contextDoctor-LHKGSIEM.js +12 -0
  23. package/dist/designLog-S67J7SO2.js +11 -0
  24. package/dist/flows-MXJL45HW.js +11 -0
  25. package/dist/{preview-FBNVDQIV.js → preview-JN5PTWLX.js} +4 -4
  26. package/dist/{registry-MIJ6LSAY.js → registry-JBP7J3JB.js} +1 -1
  27. package/dist/{remote-OH2P4S2H.js → remote-UOV4ZXWU.js} +11 -10
  28. package/dist/{run-NDSNTVYP.js → run-GDDIYEBP.js} +2 -2
  29. package/dist/{sentinel-42NZRTTS.js → sentinel-4SKSSCXX.js} +0 -0
  30. package/dist/server-2WPGU3KQ.js +156 -0
  31. package/dist/shots-6DRTXFDI.js +13 -0
  32. package/dist/{skills-POB4ZZY5.js → skills-DQTE5Q77.js} +2 -1
  33. package/dist/{variants-3IEP7DFY.js → variants-VURXVIQO.js} +3 -3
  34. package/package.json +1 -1
  35. package/dist/App-766KSGON.js +0 -23
  36. package/dist/shots-OKWOYF7F.js +0 -13
package/README.md CHANGED
@@ -174,6 +174,9 @@ squint doctor --probe # run every engine end to end, verify auth act
174
174
  flagged from the live page, and on Next 16+ the framework's own `/_next/mcp`
175
175
  channel feeds structured errors straight into the fix loop. `/context` itemizes
176
176
  the injected-context bill per source, with staleness warnings.
177
+ - **Two more doors in**: `squint mcp` serves the gates as MCP tools (any
178
+ MCP-speaking agent calls squint's verification directly); `squint ci` runs the
179
+ whole loop headlessly in a pipeline — JSON report, non-zero exit on hard findings.
177
180
  - **The sentinel**: gate evasion (deleted tests, added skips, suppressed
178
181
  diagnostics, weakened checks, locked-path touches) detected deterministically per
179
182
  turn and reported to you — never sent back to the engine that did it.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ App
4
+ } from "./chunk-2NYNTVMX.js";
5
+ import "./chunk-VJ2QKHYK.js";
6
+ import "./chunk-G5NS2IFQ.js";
7
+ import "./chunk-UBADCBPT.js";
8
+ import "./chunk-IEQGTVWN.js";
9
+ import "./chunk-AUJJGMZG.js";
10
+ import "./chunk-YHRAOBI2.js";
11
+ import "./chunk-2I3YHT6P.js";
12
+ import "./chunk-J7ITMK6X.js";
13
+ import "./chunk-6NPUVPQD.js";
14
+ import "./chunk-K5QJMSJH.js";
15
+ import "./chunk-WAJXATCO.js";
16
+ import "./chunk-62JNF5M2.js";
17
+ import "./chunk-ERF2SPUW.js";
18
+ import "./chunk-7CAGWFAQ.js";
19
+ import "./chunk-3YCG234A.js";
20
+ import "./chunk-PYC53J7D.js";
21
+ import "./chunk-GV2HAALP.js";
22
+ export {
23
+ App
24
+ };
@@ -5,7 +5,7 @@ import {
5
5
  pixelDiffAttributed,
6
6
  pixelDiffPct,
7
7
  runFlow
8
- } from "./chunk-BGBG2MNY.js";
8
+ } from "./chunk-3YCG234A.js";
9
9
  export {
10
10
  cdpCapture,
11
11
  hasWebSocket,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  findChrome,
4
4
  screenshot
5
- } from "./chunk-IMDRXXFU.js";
6
- import "./chunk-KVYGPLWW.js";
5
+ } from "./chunk-PYC53J7D.js";
6
+ import "./chunk-GV2HAALP.js";
7
7
  export {
8
8
  findChrome,
9
9
  screenshot
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  variantsRoot
4
- } from "./chunk-YGSF2TSO.js";
4
+ } from "./chunk-J7ITMK6X.js";
5
5
  import {
6
6
  findChrome,
7
7
  screenshot
8
- } from "./chunk-IMDRXXFU.js";
8
+ } from "./chunk-PYC53J7D.js";
9
9
  import {
10
10
  lineSplitter
11
- } from "./chunk-KVYGPLWW.js";
11
+ } from "./chunk-GV2HAALP.js";
12
12
 
13
13
  // src/variants/shots.ts
14
14
  import path2 from "path";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  completeCommand
4
- } from "./chunk-43NQNIJY.js";
4
+ } from "./chunk-VJ2QKHYK.js";
5
5
  import {
6
6
  Session
7
- } from "./chunk-YGPVP4YB.js";
7
+ } from "./chunk-G5NS2IFQ.js";
8
8
 
9
9
  // src/tui/App.tsx
10
10
  import { Box as Box3, Static, Text as Text3, useApp, useInput } from "ink";
@@ -179,7 +179,7 @@ var describe = (value) => {
179
179
  return String(value);
180
180
  };
181
181
  async function runFlow(chromePath, baseUrl, flow, outDir) {
182
- const { stepExpression } = await import("./flows-6LK7NGWS.js");
182
+ const { stepExpression } = await import("./flows-MXJL45HW.js");
183
183
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
184
184
  const shots = [];
185
185
  let connection = null;
@@ -437,6 +437,42 @@ var SLOP_AUDIT = `(() => {
437
437
  break;
438
438
  }
439
439
  }
440
+ // APCA (the contrast model that graduated in DevTools 149): body text
441
+ // under |Lc| 60 reads as fog even when it squeaks past WCAG AA.
442
+ const chan = (v) => Math.pow(v / 255, 2.4);
443
+ const lum = (rgb) => {
444
+ const m = rgb.match(/rgba?\\(([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/);
445
+ if (!m) return null;
446
+ return 0.2126729 * chan(+m[1]) + 0.7151522 * chan(+m[2]) + 0.0721750 * chan(+m[3]);
447
+ };
448
+ const apca = (txt, bg) => {
449
+ let yt = lum(txt), yb = lum(bg);
450
+ if (yt === null || yb === null) return null;
451
+ const clamp = (y) => (y < 0.022 ? y + Math.pow(0.022 - y, 1.414) : y);
452
+ yt = clamp(yt); yb = clamp(yb);
453
+ const c = yb > yt ? (Math.pow(yb, 0.56) - Math.pow(yt, 0.57)) * 1.14 : (Math.pow(yb, 0.65) - Math.pow(yt, 0.62)) * 1.14;
454
+ return Math.abs(c) < 0.1 ? 0 : Math.round((Math.abs(c) - 0.027) * 100);
455
+ };
456
+ let apcaFlagged = 0;
457
+ for (const p of document.querySelectorAll('p, li, td, span')) {
458
+ if (apcaFlagged >= 2) break;
459
+ const text = (p.textContent || '').trim();
460
+ if (text.length < 40) continue;
461
+ const cs = getComputedStyle(p);
462
+ if (parseFloat(cs.fontSize) >= 24) continue;
463
+ let bgEl = p, bg = 'rgba(0, 0, 0, 0)';
464
+ while (bgEl) {
465
+ const b = getComputedStyle(bgEl).backgroundColor;
466
+ if (b && !b.includes('0, 0, 0, 0')) { bg = b; break; }
467
+ bgEl = bgEl.parentElement;
468
+ }
469
+ if (bg.includes('0, 0, 0, 0')) bg = 'rgb(255, 255, 255)';
470
+ const lc = apca(cs.color, bg);
471
+ if (lc !== null && lc > 0 && lc < 60) {
472
+ out.push('low APCA contrast: Lc ' + lc + ' on <' + p.tagName.toLowerCase() + '> body text ("' + text.slice(0, 40) + '\u2026") \u2014 fog, not elegance');
473
+ apcaFlagged++;
474
+ }
475
+ }
440
476
  const vw = innerWidth, vh = innerHeight;
441
477
  for (const el of document.querySelectorAll('*')) {
442
478
  const r = el.getBoundingClientRect();
@@ -735,6 +771,44 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
735
771
  if (checkFailures.length >= 15) break;
736
772
  }
737
773
  if (audit) {
774
+ const kbdFindings = [];
775
+ try {
776
+ const seen = [];
777
+ let trapped = null;
778
+ for (let i = 0; i < 12; i++) {
779
+ await connection.send("Input.dispatchKeyEvent", { type: "rawKeyDown", key: "Tab", code: "Tab", windowsVirtualKeyCode: 9 }, sessionId);
780
+ await connection.send("Input.dispatchKeyEvent", { type: "keyUp", key: "Tab", code: "Tab", windowsVirtualKeyCode: 9 }, sessionId);
781
+ const { result } = await connection.send(
782
+ "Runtime.evaluate",
783
+ {
784
+ expression: `(() => {
785
+ const el = document.activeElement;
786
+ if (!el || el === document.body || el === document.documentElement) return null;
787
+ let label = el.tagName.toLowerCase();
788
+ if (el.id) label += '#' + el.id;
789
+ else if (el.classList[0]) label += '.' + el.classList[0];
790
+ const cs = getComputedStyle(el);
791
+ const visible = (cs.outlineStyle !== 'none' && parseFloat(cs.outlineWidth) > 0) || cs.boxShadow !== 'none';
792
+ return { label, visible };
793
+ })()`,
794
+ returnByValue: true
795
+ },
796
+ sessionId
797
+ );
798
+ const stop = result?.value;
799
+ if (!stop) continue;
800
+ if (seen.length > 0 && seen[seen.length - 1] === stop.label && seen.filter((s) => s === stop.label).length >= 2) {
801
+ trapped = stop.label;
802
+ break;
803
+ }
804
+ seen.push(stop.label);
805
+ if (!stop.visible) kbdFindings.push(`keyboard: focus invisible on <${stop.label}> (tab stop ${seen.length}) \u2014 outline and box-shadow both none`);
806
+ }
807
+ if (trapped) kbdFindings.push(`keyboard: focus trapped at <${trapped}> \u2014 Tab cannot leave it`);
808
+ if (seen.length === 0) kbdFindings.push("keyboard: no tabbable elements \u2014 the page is unreachable without a mouse");
809
+ await connection.send("Runtime.evaluate", { expression: "document.activeElement && document.activeElement.blur()" }, sessionId).catch(() => null);
810
+ } catch {
811
+ }
738
812
  try {
739
813
  const { result } = await connection.send(
740
814
  "Runtime.evaluate",
@@ -742,6 +816,7 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
742
816
  sessionId
743
817
  );
744
818
  if (Array.isArray(result?.value)) a11y = result.value.map(String);
819
+ a11y.push(...kbdFindings);
745
820
  } catch {
746
821
  }
747
822
  try {
@@ -828,7 +903,10 @@ async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, a
828
903
  {
829
904
  width: viewport.width,
830
905
  height: viewport.height,
831
- deviceScaleFactor: 1,
906
+ // 2x for review shots: sub-pixel border/kerning slop is
907
+ // invisible at 1x. The pulse stays 1x — its diff math and
908
+ // hit-testing assume CSS pixels.
909
+ deviceScaleFactor: viewport.name === "pulse" ? 1 : 2,
832
910
  mobile: viewport.width < 500
833
911
  },
834
912
  sessionId
@@ -140,8 +140,9 @@ function stepExpression(step) {
140
140
  return null;
141
141
  }
142
142
  }
143
+
143
144
  export {
144
- loadFlows,
145
145
  parseFlow,
146
+ loadFlows,
146
147
  stepExpression
147
148
  };
@@ -3,7 +3,7 @@ import {
3
3
  findEngineBinary,
4
4
  lineSplitter,
5
5
  truncate
6
- } from "./chunk-KVYGPLWW.js";
6
+ } from "./chunk-GV2HAALP.js";
7
7
 
8
8
  // src/runner/run.ts
9
9
  import { spawn } from "child_process";
@@ -21,7 +21,7 @@ function runAgent(engine, opts, onEvent, signal) {
21
21
  let stderrTail = "";
22
22
  const child = spawn(binaryPath, engine.buildArgs(opts), {
23
23
  cwd: opts.cwd,
24
- env: process.env,
24
+ env: { ...process.env, ...engine.env },
25
25
  stdio: ["ignore", "pipe", "pipe"]
26
26
  });
27
27
  const emit = (event) => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Session
4
- } from "./chunk-YGPVP4YB.js";
4
+ } from "./chunk-G5NS2IFQ.js";
5
5
 
6
6
  // src/daemon/client.ts
7
7
  import net from "net";
@@ -5,11 +5,11 @@ import {
5
5
  import {
6
6
  cdpCapture,
7
7
  hasWebSocket
8
- } from "./chunk-BGBG2MNY.js";
8
+ } from "./chunk-3YCG234A.js";
9
9
  import {
10
10
  findChrome,
11
11
  screenshot
12
- } from "./chunk-IMDRXXFU.js";
12
+ } from "./chunk-PYC53J7D.js";
13
13
 
14
14
  // src/preview/preview.ts
15
15
  import fs2 from "fs";
@@ -163,13 +163,13 @@ async function probeRuntime(url, cwd) {
163
163
  async function comparePulse(previous, current) {
164
164
  const chrome = findChrome();
165
165
  if (!chrome || !hasWebSocket()) return null;
166
- const { pixelDiffPct } = await import("./cdp-2VVQOHWM.js");
166
+ const { pixelDiffPct } = await import("./cdp-D2F4P7UQ.js");
167
167
  return pixelDiffPct(chrome, previous, current);
168
168
  }
169
169
  async function comparePulseAttributed(previous, current, url) {
170
170
  const chrome = findChrome();
171
171
  if (!chrome || !hasWebSocket()) return null;
172
- const { pixelDiffAttributed } = await import("./cdp-2VVQOHWM.js");
172
+ const { pixelDiffAttributed } = await import("./cdp-D2F4P7UQ.js");
173
173
  return pixelDiffAttributed(chrome, previous, current, url);
174
174
  }
175
175
  function buildRuntimeFixPrompt(report) {
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- appendDecision,
4
3
  enrich
5
- } from "./chunk-ARDV4XH6.js";
4
+ } from "./chunk-UBADCBPT.js";
5
+ import {
6
+ appendDecision
7
+ } from "./chunk-IEQGTVWN.js";
6
8
  import {
7
9
  applySandbox,
8
10
  discardSandbox,
@@ -23,16 +25,16 @@ import {
23
25
  buildFixPrompt,
24
26
  detectDevCommand,
25
27
  screenshotVariants
26
- } from "./chunk-QCHBDP46.js";
28
+ } from "./chunk-2I3YHT6P.js";
27
29
  import {
28
30
  applyVariant,
29
31
  cleanVariants,
30
32
  listVariants,
31
33
  runVariants
32
- } from "./chunk-YGSF2TSO.js";
34
+ } from "./chunk-J7ITMK6X.js";
33
35
  import {
34
36
  runAgent
35
- } from "./chunk-VH7OOFQP.js";
37
+ } from "./chunk-6NPUVPQD.js";
36
38
  import {
37
39
  composePrompt
38
40
  } from "./chunk-WAJXATCO.js";
@@ -49,7 +51,7 @@ import {
49
51
  comparePulseAttributed,
50
52
  probeRuntime,
51
53
  runtimeSummary
52
- } from "./chunk-7RFQWOQV.js";
54
+ } from "./chunk-ERF2SPUW.js";
53
55
  import {
54
56
  clearState,
55
57
  loadState,
@@ -57,10 +59,10 @@ import {
57
59
  } from "./chunk-7CAGWFAQ.js";
58
60
  import {
59
61
  findChrome
60
- } from "./chunk-IMDRXXFU.js";
62
+ } from "./chunk-PYC53J7D.js";
61
63
  import {
62
64
  getEngine
63
- } from "./chunk-KVYGPLWW.js";
65
+ } from "./chunk-GV2HAALP.js";
64
66
 
65
67
  // src/session/engine.ts
66
68
  import fs2 from "fs";
@@ -102,6 +104,7 @@ function prefixed(payload) {
102
104
 
103
105
  // src/session/engine.ts
104
106
  var MAX_AUTO_FIX_ATTEMPTS = 2;
107
+ var MAX_GOAL_FIX_ATTEMPTS = 6;
105
108
  var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
106
109
  var Session = class {
107
110
  constructor(opts) {
@@ -153,6 +156,7 @@ var Session = class {
153
156
  fixAttempts = 0;
154
157
  reviewTipShown = false;
155
158
  pendingApproval = null;
159
+ goal = null;
156
160
  lastPulse = null;
157
161
  lastPerf = null;
158
162
  autoReviewedThisAsk = false;
@@ -210,10 +214,11 @@ var Session = class {
210
214
  }
211
215
  /** Launch a capped auto-fix turn over all open problems. Returns true if launched. */
212
216
  maybeAutoFix() {
213
- if (!this.opts.autoFix || this.fixAttempts >= MAX_AUTO_FIX_ATTEMPTS) return false;
217
+ const cap = this.goal ? MAX_GOAL_FIX_ATTEMPTS : MAX_AUTO_FIX_ATTEMPTS;
218
+ if (!this.opts.autoFix || this.fixAttempts >= cap) return false;
214
219
  if (this.state.problems.length === 0) return false;
215
220
  this.fixAttempts += 1;
216
- this.push("status", `auto-fix attempt ${this.fixAttempts}/${MAX_AUTO_FIX_ATTEMPTS}`);
221
+ this.push("status", `auto-fix attempt ${this.fixAttempts}/${cap}${this.goal ? " (goal armed)" : ""}`);
217
222
  this.notify({ running: false });
218
223
  this.dispatchFix([...this.state.problems]);
219
224
  return true;
@@ -510,7 +515,7 @@ ${driftSummary(drift)}`);
510
515
  } catch {
511
516
  }
512
517
  try {
513
- const { scanEvasion, sentinelSummary } = await import("./sentinel-42NZRTTS.js");
518
+ const { scanEvasion, sentinelSummary } = await import("./sentinel-4SKSSCXX.js");
514
519
  const evasions = scanEvasion(this.execCwd(), checkpoint.snapshot.stashHash ?? "HEAD");
515
520
  if (evasions.length > 0) {
516
521
  this.push("error", `\u26A0 sentinel: ${evasions.length} gate-evasion pattern(s) this turn
@@ -694,6 +699,15 @@ ${probe.checkFailures.slice(0, 5).join("\n")}`);
694
699
  this.push("status", `skills: ${enrichment.matchedSkills.join(", ")}`);
695
700
  }
696
701
  prompt += enrichment.sections;
702
+ if (this.goal) {
703
+ prompt += `
704
+
705
+ ## Standing goal (machine-checked)
706
+
707
+ ${this.goal}
708
+
709
+ squint verifies every turn \u2014 gates, runtime probe, page audits. Do not declare this done while any of its checks fail; squint will keep sending failures back until they are clean.`;
710
+ }
697
711
  await this.runTurn(prompt, ask);
698
712
  }
699
713
  /** Restore files to the state before checkpoint `index`; drop it and everything after. */
@@ -859,7 +873,7 @@ They are objective defects, not style preferences.`
859
873
  const arg = rest.join(" ").trim();
860
874
  switch (name) {
861
875
  case "engines": {
862
- void import("./registry-MIJ6LSAY.js").then(({ detectEngines }) => {
876
+ void import("./registry-JBP7J3JB.js").then(({ detectEngines }) => {
863
877
  const lines = detectEngines().map(({ engine, path: binaryPath }) => {
864
878
  const mark = binaryPath ? "\u2713" : "\u2717";
865
879
  const traits = [engine.createParser ? "stream" : "text", engine.supportsResume ? "resume" : null].filter(Boolean).join(" \xB7 ");
@@ -947,8 +961,45 @@ They are objective defects, not style preferences.`
947
961
  }
948
962
  break;
949
963
  }
964
+ case "distill": {
965
+ void (async () => {
966
+ const { loadDecisions } = await import("./designLog-S67J7SO2.js");
967
+ const decisions = loadDecisions(this.opts.cwd, 24);
968
+ if (decisions.length < 3) {
969
+ this.push("status", `only ${decisions.length} decision(s) on record \u2014 distillation needs a few more (/decide, variants, approvals, rollbacks all feed the ledger)`);
970
+ return;
971
+ }
972
+ const ledger = decisions.map((d) => `- [${d.source}] ${d.decision}`).join("\n");
973
+ const prompt = `The design ledger below is every decision this project has recorded \u2014 approvals, rejections, chosen variants, rollbacks. Distill the accumulated taste into standing enforcement:
974
+
975
+ ${ledger}
976
+
977
+ 1. Append at most 3 SHORT always-on rules to .squint/rules.md capturing patterns that repeat across decisions (rejections matter most \u2014 "no gradient buttons" beats restating one-off choices). Create the file if missing; never delete existing rules.
978
+ 2. Where a decision is mechanically checkable in the DOM, write a persistent check at .squint/checks/<name>.js \u2014 plain JS that evaluates IN THE PAGE to an array of failure strings (empty = pass). At most 2 new checks; skip anything that needs judgment rather than a selector.
979
+ 3. Reply with exactly what you wrote and why, briefly.
980
+
981
+ Do not restyle anything \u2014 this task only writes rules and checks.`;
982
+ await this.runTurn(prompt, "/distill \u2014 compress the ledger into rules and checks", this.opts.fixModel);
983
+ })();
984
+ break;
985
+ }
986
+ case "goal": {
987
+ if (!arg || arg === "show") {
988
+ this.push("status", this.goal ? `standing goal: ${this.goal}` : "no standing goal \u2014 /goal <objective> arms one; /goal off clears");
989
+ break;
990
+ }
991
+ if (arg === "off") {
992
+ this.goal = null;
993
+ this.push("status", "goal cleared \u2014 auto-fix back to the normal cap");
994
+ break;
995
+ }
996
+ this.goal = arg;
997
+ this.push("status", `goal armed: ${arg}
998
+ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts until squint's checks come back clean`);
999
+ break;
1000
+ }
950
1001
  case "context": {
951
- import("./contextDoctor-A26C2ZDN.js").then(({ contextReport, formatContextReport }) => {
1002
+ import("./contextDoctor-LHKGSIEM.js").then(({ contextReport, formatContextReport }) => {
952
1003
  this.push("status", formatContextReport(contextReport(this.execCwd())));
953
1004
  }).catch((error) => {
954
1005
  this.push("status", `context report failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -1072,7 +1123,7 @@ They are objective defects, not style preferences.`
1072
1123
  break;
1073
1124
  }
1074
1125
  void (async () => {
1075
- const { loadFlows } = await import("./flows-6LK7NGWS.js");
1126
+ const { loadFlows } = await import("./flows-MXJL45HW.js");
1076
1127
  const flows = loadFlows(this.opts.cwd);
1077
1128
  if (flows.length === 0) {
1078
1129
  this.push("status", "no flows \u2014 add .squint/flows/<name>.flow (goto/click/fill/press/expect/shot lines), or ask the engine to write one");
@@ -1083,8 +1134,8 @@ They are objective defects, not style preferences.`
1083
1134
  this.push("error", "no Chrome/Chromium found for flows");
1084
1135
  return;
1085
1136
  }
1086
- const { runFlow } = await import("./cdp-2VVQOHWM.js");
1087
- const { previewDir } = await import("./preview-FBNVDQIV.js");
1137
+ const { runFlow } = await import("./cdp-D2F4P7UQ.js");
1138
+ const { previewDir } = await import("./preview-JN5PTWLX.js");
1088
1139
  this.push("status", `replaying ${flows.length} flow(s)\u2026`);
1089
1140
  this.notify({ running: true, runStartedAt: Date.now() });
1090
1141
  const failures = [];
@@ -1408,7 +1459,7 @@ ${sandboxFiles(this.opts.cwd).join("\n")}`);
1408
1459
  this.notify({ items: [], totals: { costUsd: 0, turns: 0 } });
1409
1460
  break;
1410
1461
  case "help": {
1411
- void import("./commands-BY44HDQ6.js").then(({ commandHelp }) => this.push("status", commandHelp()));
1462
+ void import("./commands-WC7V6EAE.js").then(({ commandHelp }) => this.push("status", commandHelp()));
1412
1463
  break;
1413
1464
  }
1414
1465
  case "quit":
@@ -148,6 +148,10 @@ var claude = {
148
148
  binary: "claude",
149
149
  install: "npm install -g @anthropic-ai/claude-code",
150
150
  supportsResume: true,
151
+ // 2.1.211+: subagent output joins stream-json. The env-var form is a
152
+ // no-op on older CLIs, unlike the flag, so the loops see spawned work
153
+ // wherever it's supported.
154
+ env: { CLAUDE_CODE_FORWARD_SUBAGENT_TEXT: "1" },
151
155
  buildArgs(opts) {
152
156
  const permissionMode = opts.mode === "plan" ? "plan" : opts.mode === "yolo" ? "bypassPermissions" : "acceptEdits";
153
157
  const args = [
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/session/designLog.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+ function logFile(cwd) {
7
+ return path.join(cwd, ".squint", "design-log.jsonl");
8
+ }
9
+ function appendDecision(cwd, entry) {
10
+ try {
11
+ fs.mkdirSync(path.join(cwd, ".squint"), { recursive: true });
12
+ const record = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry };
13
+ fs.appendFileSync(logFile(cwd), JSON.stringify(record) + "\n");
14
+ } catch {
15
+ }
16
+ }
17
+ function loadDecisions(cwd, limit = 8) {
18
+ try {
19
+ const lines = fs.readFileSync(logFile(cwd), "utf8").trim().split("\n");
20
+ const decisions = [];
21
+ for (const line of lines.slice(-limit * 2)) {
22
+ try {
23
+ const parsed = JSON.parse(line);
24
+ if (typeof parsed?.decision === "string" && typeof parsed?.ts === "string") decisions.push(parsed);
25
+ } catch {
26
+ }
27
+ }
28
+ return decisions.slice(-limit);
29
+ } catch {
30
+ return [];
31
+ }
32
+ }
33
+ function decisionsSection(cwd) {
34
+ const decisions = loadDecisions(cwd);
35
+ if (decisions.length === 0) return "";
36
+ const lines = decisions.map((d) => {
37
+ const days = Math.floor((Date.now() - Date.parse(d.ts)) / 864e5);
38
+ const age = days <= 0 ? "today" : `${days}d ago`;
39
+ return `- ${d.decision} (${d.source}, ${age}${d.screenshot ? `, evidence: ${d.screenshot}` : ""})`;
40
+ });
41
+ return `## Design decisions on record
42
+
43
+ ${lines.join("\n")}
44
+
45
+ These were decided deliberately. Do not silently undo them; if a task genuinely requires reversing one, say so explicitly first.`;
46
+ }
47
+
48
+ export {
49
+ appendDecision,
50
+ loadDecisions,
51
+ decisionsSection
52
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runAgent
4
- } from "./chunk-VH7OOFQP.js";
4
+ } from "./chunk-6NPUVPQD.js";
5
5
  import {
6
6
  FAMILIES,
7
7
  renderFamilyBrief
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- decisionsSection,
4
3
  inventorySection,
5
4
  loadComponentInventory,
6
- loadDecisions,
7
5
  loadLocks,
8
6
  loadRules,
9
7
  loadSkills
10
- } from "./chunk-ARDV4XH6.js";
8
+ } from "./chunk-UBADCBPT.js";
9
+ import {
10
+ decisionsSection,
11
+ loadDecisions
12
+ } from "./chunk-IEQGTVWN.js";
11
13
  import {
12
14
  loadBrief
13
15
  } from "./chunk-WAJXATCO.js";
@@ -68,6 +70,7 @@ function formatContextReport(report) {
68
70
  }
69
71
  return out.join("\n");
70
72
  }
73
+
71
74
  export {
72
75
  contextReport,
73
76
  formatContextReport
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  findBinary
4
- } from "./chunk-KVYGPLWW.js";
4
+ } from "./chunk-GV2HAALP.js";
5
5
 
6
6
  // src/preview/chrome.ts
7
7
  import { spawn } from "child_process";