uilint 0.2.9 → 0.2.11

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/dist/index.js CHANGED
@@ -1,14 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ createSpinner,
3
4
  detectNextAppRouter,
4
- findNextAppRouterProjects
5
- } from "./chunk-RHTG6DUD.js";
5
+ findNextAppRouterProjects,
6
+ intro,
7
+ logError,
8
+ logInfo,
9
+ logSuccess,
10
+ logWarning,
11
+ note,
12
+ outro,
13
+ pc,
14
+ withSpinner
15
+ } from "./chunk-FRNXXIEM.js";
6
16
 
7
17
  // src/index.ts
8
18
  import { Command } from "commander";
9
19
 
10
20
  // src/commands/scan.ts
11
- import { dirname as dirname2, resolve as resolve2 } from "path";
21
+ import { dirname, resolve as resolve2 } from "path";
12
22
  import { existsSync as existsSync2, mkdirSync, statSync, writeFileSync } from "fs";
13
23
  import {
14
24
  createStyleSummary,
@@ -353,62 +363,6 @@ function maybeMs(ms) {
353
363
  return ms == null ? "n/a" : formatMs(ms);
354
364
  }
355
365
 
356
- // src/utils/prompts.ts
357
- import * as p from "@clack/prompts";
358
- import pc from "picocolors";
359
- import { readFileSync } from "fs";
360
- import { dirname, join as join2 } from "path";
361
- import { fileURLToPath } from "url";
362
- function getCLIVersion() {
363
- try {
364
- const __dirname = dirname(fileURLToPath(import.meta.url));
365
- const pkgPath = join2(__dirname, "..", "..", "package.json");
366
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
367
- return pkg.version || "0.0.0";
368
- } catch {
369
- return "0.0.0";
370
- }
371
- }
372
- function intro2(title) {
373
- const version = getCLIVersion();
374
- const header = pc.bold(pc.cyan("\u25C6 UILint")) + pc.dim(` v${version}`);
375
- console.log();
376
- p.intro(title ? `${header} ${pc.dim("\xB7")} ${title}` : header);
377
- }
378
- function outro2(message) {
379
- p.outro(pc.green(message));
380
- }
381
- async function withSpinner(message, fn) {
382
- const s = p.spinner();
383
- s.start(message);
384
- try {
385
- const result = fn.length >= 1 ? await fn(s) : await fn();
386
- s.stop(pc.green("\u2713 ") + message);
387
- return result;
388
- } catch (error) {
389
- s.stop(pc.red("\u2717 ") + message);
390
- throw error;
391
- }
392
- }
393
- function createSpinner() {
394
- return p.spinner();
395
- }
396
- function note2(message, title) {
397
- p.note(message, title);
398
- }
399
- function logInfo(message) {
400
- p.log.info(message);
401
- }
402
- function logSuccess(message) {
403
- p.log.success(message);
404
- }
405
- function logWarning(message) {
406
- p.log.warn(message);
407
- }
408
- function logError(message) {
409
- p.log.error(message);
410
- }
411
-
412
366
  // src/utils/output.ts
413
367
  import chalk from "chalk";
414
368
  import {
@@ -425,9 +379,9 @@ function envTruthy(name) {
425
379
  if (!v) return false;
426
380
  return v === "1" || v.toLowerCase() === "true" || v.toLowerCase() === "yes";
427
381
  }
428
- function preview(text2, maxLen) {
429
- if (text2.length <= maxLen) return text2;
430
- return text2.slice(0, maxLen) + "\n\u2026<truncated>\u2026\n" + text2.slice(-maxLen);
382
+ function preview(text, maxLen) {
383
+ if (text.length <= maxLen) return text;
384
+ return text.slice(0, maxLen) + "\n\u2026<truncated>\u2026\n" + text.slice(-maxLen);
431
385
  }
432
386
  function debugEnabled(options) {
433
387
  return Boolean(options.debug) || envTruthy("UILINT_DEBUG");
@@ -461,7 +415,7 @@ async function scan(options) {
461
415
  const dbgFull = debugFullEnabled(options);
462
416
  const dbgDump = debugDumpPath(options);
463
417
  if (!isJsonOutput) {
464
- intro2("Scan for UI Issues");
418
+ intro("Scan for UI Issues");
465
419
  }
466
420
  try {
467
421
  let snapshot;
@@ -562,7 +516,7 @@ async function scan(options) {
562
516
  } else {
563
517
  const startPath = snapshot.kind === "source" ? snapshot.inputPath : snapshot.kind === "dom" ? snapshot.inputPath : void 0;
564
518
  if (startPath) {
565
- styleguideLocation = findUILintStyleGuideUpwards(dirname2(startPath));
519
+ styleguideLocation = findUILintStyleGuideUpwards(dirname(startPath));
566
520
  }
567
521
  styleguideLocation = styleguideLocation ?? findStyleGuidePath(projectPath);
568
522
  if (styleguideLocation) {
@@ -576,12 +530,12 @@ async function scan(options) {
576
530
  } else if (!styleGuide) {
577
531
  if (!isJsonOutput) {
578
532
  logWarning("No styleguide found");
579
- note2(
533
+ note(
580
534
  [
581
535
  `Searched in: ${options.styleguide || projectPath}`,
582
536
  "",
583
537
  "Looked for:",
584
- ...STYLEGUIDE_PATHS.map((p2) => ` \u2022 ${p2}`),
538
+ ...STYLEGUIDE_PATHS.map((p) => ` \u2022 ${p}`),
585
539
  "",
586
540
  `Create ${pc.cyan(
587
541
  ".uilint/styleguide.md"
@@ -601,7 +555,7 @@ async function scan(options) {
601
555
  } else if (dbg && styleGuide) {
602
556
  debugLog(dbg, "Styleguide contents (preview)", preview(styleGuide, 800));
603
557
  }
604
- const tailwindSearchDir = (snapshot.kind === "source" || snapshot.kind === "dom") && snapshot.inputPath ? dirname2(snapshot.inputPath) : projectPath;
558
+ const tailwindSearchDir = (snapshot.kind === "source" || snapshot.kind === "dom") && snapshot.inputPath ? dirname(snapshot.inputPath) : projectPath;
605
559
  const tailwindTheme = readTailwindThemeTokens(tailwindSearchDir);
606
560
  const styleSummary = snapshot.kind === "dom" ? createStyleSummary(snapshot.snapshot.styles, {
607
561
  html: snapshot.snapshot.html,
@@ -667,7 +621,7 @@ async function scan(options) {
667
621
  const safeStamp = now.toISOString().replace(/[:.]/g, "-");
668
622
  const resolved = resolve2(process.cwd(), dbgDump);
669
623
  const dumpFile = resolved.endsWith(".json") || resolved.endsWith(".jsonl") ? resolved : resolve2(resolved, `scan-debug-${safeStamp}.json`);
670
- mkdirSync(dirname2(dumpFile), { recursive: true });
624
+ mkdirSync(dirname(dumpFile), { recursive: true });
671
625
  writeFileSync(
672
626
  dumpFile,
673
627
  JSON.stringify(
@@ -813,7 +767,7 @@ async function scan(options) {
813
767
  (firstAnswerNs ?? lastThinkingNs ?? analysisEndNs) - firstThinkingNs
814
768
  ) : null;
815
769
  const outputMs = firstAnswerNs && (lastAnswerNs || analysisEndNs) ? nsToMs((lastAnswerNs ?? analysisEndNs) - firstAnswerNs) : null;
816
- note2(
770
+ note(
817
771
  [
818
772
  `Prepare Ollama: ${formatMs(prepMs)}`,
819
773
  `Time to first token: ${maybeMs(ttftMs)}`,
@@ -863,7 +817,7 @@ async function scan(options) {
863
817
  }
864
818
 
865
819
  // src/commands/analyze.ts
866
- import { dirname as dirname3, resolve as resolve3 } from "path";
820
+ import { dirname as dirname2, resolve as resolve3 } from "path";
867
821
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
868
822
  import {
869
823
  buildSourceScanPrompt,
@@ -878,9 +832,9 @@ function envTruthy2(name) {
878
832
  if (!v) return false;
879
833
  return v === "1" || v.toLowerCase() === "true" || v.toLowerCase() === "yes";
880
834
  }
881
- function preview2(text2, maxLen) {
882
- if (text2.length <= maxLen) return text2;
883
- return text2.slice(0, maxLen) + "\n\u2026<truncated>\u2026\n" + text2.slice(-maxLen);
835
+ function preview2(text, maxLen) {
836
+ if (text.length <= maxLen) return text;
837
+ return text.slice(0, maxLen) + "\n\u2026<truncated>\u2026\n" + text.slice(-maxLen);
884
838
  }
885
839
  function debugEnabled2(options) {
886
840
  return Boolean(options.debug) || envTruthy2("UILINT_DEBUG");
@@ -913,22 +867,22 @@ async function resolveStyleGuideForAnalyze(options) {
913
867
  return { content: options.styleGuide, path: null };
914
868
  }
915
869
  if (options.styleguidePath && typeof options.styleguidePath === "string") {
916
- const p2 = resolvePathSpecifier(options.styleguidePath, process.cwd());
917
- if (existsSync3(p2)) {
918
- return { content: await readStyleGuide2(p2), path: p2 };
870
+ const p = resolvePathSpecifier(options.styleguidePath, process.cwd());
871
+ if (existsSync3(p)) {
872
+ return { content: await readStyleGuide2(p), path: p };
919
873
  }
920
874
  return { content: null, path: null };
921
875
  }
922
876
  const env = process.env.UILINT_STYLEGUIDE_PATH;
923
877
  if (env && env.trim()) {
924
- const p2 = resolvePathSpecifier(env.trim(), process.cwd());
925
- if (existsSync3(p2)) {
926
- return { content: await readStyleGuide2(p2), path: p2 };
878
+ const p = resolvePathSpecifier(env.trim(), process.cwd());
879
+ if (existsSync3(p)) {
880
+ return { content: await readStyleGuide2(p), path: p };
927
881
  }
928
882
  }
929
883
  if (options.inputFile) {
930
884
  const absInput = resolvePathSpecifier(options.inputFile, process.cwd());
931
- const found = findUILintStyleGuideUpwards2(dirname3(absInput));
885
+ const found = findUILintStyleGuideUpwards2(dirname2(absInput));
932
886
  if (found) return { content: await readStyleGuide2(found), path: found };
933
887
  }
934
888
  const cwdPath = findStyleGuidePath2(process.cwd());
@@ -953,7 +907,7 @@ async function analyze(options) {
953
907
  const dbgFull = debugFullEnabled2(options);
954
908
  const dbgDump = debugDumpPath2(options);
955
909
  if (!isJsonOutput) {
956
- intro2("Analyze Source Code");
910
+ intro("Analyze Source Code");
957
911
  }
958
912
  try {
959
913
  debugLog2(dbg, "Input options", {
@@ -1047,7 +1001,7 @@ async function analyze(options) {
1047
1001
  const safeStamp = now.toISOString().replace(/[:.]/g, "-");
1048
1002
  const resolved = resolve3(process.cwd(), dbgDump);
1049
1003
  const dumpFile = resolved.endsWith(".json") || resolved.endsWith(".jsonl") ? resolved : resolve3(resolved, `analyze-debug-${safeStamp}.json`);
1050
- mkdirSync2(dirname3(dumpFile), { recursive: true });
1004
+ mkdirSync2(dirname2(dumpFile), { recursive: true });
1051
1005
  writeFileSync2(
1052
1006
  dumpFile,
1053
1007
  JSON.stringify(
@@ -1150,7 +1104,7 @@ async function analyze(options) {
1150
1104
  (firstAnswerNs ?? lastThinkingNs ?? analysisEndNs) - firstThinkingNs
1151
1105
  ) : null;
1152
1106
  const outputMs = firstAnswerNs && (lastAnswerNs || analysisEndNs) ? nsToMs((lastAnswerNs ?? analysisEndNs) - firstAnswerNs) : null;
1153
- note2(
1107
+ note(
1154
1108
  [
1155
1109
  `Prepare Ollama: ${formatMs(prepMs)}`,
1156
1110
  `Time to first token: ${maybeMs(ttftMs)}`,
@@ -1228,7 +1182,7 @@ async function readStdin2() {
1228
1182
  async function consistency(options) {
1229
1183
  const isJsonOutput = options.output === "json";
1230
1184
  if (!isJsonOutput) {
1231
- intro2("UI Consistency Analysis");
1185
+ intro("UI Consistency Analysis");
1232
1186
  }
1233
1187
  try {
1234
1188
  let inputJson = options.inputJson;
@@ -1318,7 +1272,7 @@ async function consistency(options) {
1318
1272
  }
1319
1273
 
1320
1274
  // src/commands/update.ts
1321
- import { dirname as dirname4, resolve as resolve4 } from "path";
1275
+ import { dirname as dirname3, resolve as resolve4 } from "path";
1322
1276
  import {
1323
1277
  createStyleSummary as createStyleSummary2,
1324
1278
  parseStyleGuide,
@@ -1333,13 +1287,13 @@ import {
1333
1287
  readTailwindThemeTokens as readTailwindThemeTokens2
1334
1288
  } from "uilint-core/node";
1335
1289
  async function update(options) {
1336
- intro2("Update Style Guide");
1290
+ intro("Update Style Guide");
1337
1291
  try {
1338
1292
  const projectPath = process.cwd();
1339
1293
  const styleGuidePath = options.styleguide || findStyleGuidePath3(projectPath);
1340
1294
  if (!styleGuidePath) {
1341
1295
  logError("No style guide found");
1342
- note2(
1296
+ note(
1343
1297
  `Create ${pc.cyan(
1344
1298
  ".uilint/styleguide.md"
1345
1299
  )} first (recommended: run ${pc.cyan("/genstyleguide")} in Cursor).`,
@@ -1362,7 +1316,7 @@ async function update(options) {
1362
1316
  process.exit(1);
1363
1317
  }
1364
1318
  logInfo(`Found ${pc.cyan(String(snapshot.elementCount))} elements`);
1365
- const tailwindSearchDir = options.inputFile ? dirname4(resolve4(projectPath, options.inputFile)) : projectPath;
1319
+ const tailwindSearchDir = options.inputFile ? dirname3(resolve4(projectPath, options.inputFile)) : projectPath;
1366
1320
  const tailwindTheme = readTailwindThemeTokens2(tailwindSearchDir);
1367
1321
  if (options.llm) {
1368
1322
  await withSpinner("Preparing Ollama", async () => {
@@ -1388,15 +1342,15 @@ async function update(options) {
1388
1342
  }
1389
1343
  return line;
1390
1344
  });
1391
- note2(
1345
+ note(
1392
1346
  suggestions.join("\n\n"),
1393
1347
  `Found ${result.issues.length} suggestion(s)`
1394
1348
  );
1395
1349
  logInfo("Edit the styleguide manually to apply these changes.");
1396
- outro2("Analysis complete");
1350
+ outro("Analysis complete");
1397
1351
  } else {
1398
1352
  logSuccess("Style guide is up to date!");
1399
- outro2("No changes needed");
1353
+ outro("No changes needed");
1400
1354
  }
1401
1355
  } else {
1402
1356
  const updatedContent = await withSpinner("Merging styles", async () => {
@@ -1422,14 +1376,14 @@ async function update(options) {
1422
1376
  });
1423
1377
  if (updatedContent === existingContent) {
1424
1378
  logSuccess("Style guide is already up to date!");
1425
- outro2("No changes needed");
1379
+ outro("No changes needed");
1426
1380
  return;
1427
1381
  }
1428
1382
  await withSpinner("Writing styleguide", async () => {
1429
1383
  await writeStyleGuide(styleGuidePath, updatedContent);
1430
1384
  });
1431
- note2(`Updated: ${pc.dim(styleGuidePath)}`, "Success");
1432
- outro2("Style guide updated!");
1385
+ note(`Updated: ${pc.dim(styleGuidePath)}`, "Success");
1386
+ outro("Style guide updated!");
1433
1387
  }
1434
1388
  } catch (error) {
1435
1389
  logError(error instanceof Error ? error.message : "Update failed");
@@ -1440,9 +1394,9 @@ async function update(options) {
1440
1394
  }
1441
1395
 
1442
1396
  // src/commands/serve.ts
1443
- import { existsSync as existsSync5, statSync as statSync3, readdirSync, readFileSync as readFileSync2 } from "fs";
1397
+ import { existsSync as existsSync5, statSync as statSync3, readdirSync, readFileSync } from "fs";
1444
1398
  import { createRequire } from "module";
1445
- import { dirname as dirname6, resolve as resolve5, relative, join as join4, parse as parse2 } from "path";
1399
+ import { dirname as dirname5, resolve as resolve5, relative, join as join3, parse as parse2 } from "path";
1446
1400
  import { WebSocketServer, WebSocket } from "ws";
1447
1401
  import { watch } from "chokidar";
1448
1402
  import {
@@ -1451,7 +1405,7 @@ import {
1451
1405
  } from "uilint-core/node";
1452
1406
 
1453
1407
  // src/utils/vision-run.ts
1454
- import { dirname as dirname5, join as join3, parse } from "path";
1408
+ import { dirname as dirname4, join as join2, parse } from "path";
1455
1409
  import { existsSync as existsSync4, statSync as statSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
1456
1410
  import {
1457
1411
  ensureOllamaReady as ensureOllamaReady5,
@@ -1496,12 +1450,12 @@ async function ensureOllamaReadyCached(params) {
1496
1450
  const key = `${params.baseUrl}::${params.model}`;
1497
1451
  const existing = ollamaReadyOnce.get(key);
1498
1452
  if (existing) return existing;
1499
- const p2 = ensureOllamaReady5({ model: params.model, baseUrl: params.baseUrl }).then(() => void 0).catch((e) => {
1453
+ const p = ensureOllamaReady5({ model: params.model, baseUrl: params.baseUrl }).then(() => void 0).catch((e) => {
1500
1454
  ollamaReadyOnce.delete(key);
1501
1455
  throw e;
1502
1456
  });
1503
- ollamaReadyOnce.set(key, p2);
1504
- return p2;
1457
+ ollamaReadyOnce.set(key, p);
1458
+ return p;
1505
1459
  }
1506
1460
  function writeVisionDebugDump(params) {
1507
1461
  const resolvedDirOrFile = resolvePathSpecifier(
@@ -1510,7 +1464,7 @@ function writeVisionDebugDump(params) {
1510
1464
  );
1511
1465
  const safeStamp = params.now.toISOString().replace(/[:.]/g, "-");
1512
1466
  const dumpFile = resolvedDirOrFile.endsWith(".json") || resolvedDirOrFile.endsWith(".jsonl") ? resolvedDirOrFile : `${resolvedDirOrFile}/vision-debug-${safeStamp}.json`;
1513
- mkdirSync3(dirname5(dumpFile), { recursive: true });
1467
+ mkdirSync3(dirname4(dumpFile), { recursive: true });
1514
1468
  writeFileSync3(
1515
1469
  dumpFile,
1516
1470
  JSON.stringify(
@@ -1598,12 +1552,12 @@ async function runVisionAnalysis(args) {
1598
1552
  };
1599
1553
  }
1600
1554
  function writeVisionMarkdownReport(args) {
1601
- const p2 = parse(args.imagePath);
1602
- const outPath = args.outPath ?? join3(p2.dir, `${p2.name || p2.base}.vision.md`);
1555
+ const p = parse(args.imagePath);
1556
+ const outPath = args.outPath ?? join2(p.dir, `${p.name || p.base}.vision.md`);
1603
1557
  const lines = [];
1604
1558
  lines.push(`# UILint Vision Report`);
1605
1559
  lines.push(``);
1606
- lines.push(`- Image: \`${p2.base}\``);
1560
+ lines.push(`- Image: \`${p.base}\``);
1607
1561
  if (args.route) lines.push(`- Route: \`${args.route}\``);
1608
1562
  if (typeof args.timestamp === "number") {
1609
1563
  lines.push(`- Timestamp: \`${new Date(args.timestamp).toISOString()}\``);
@@ -1635,7 +1589,7 @@ function writeVisionMarkdownReport(args) {
1635
1589
  lines.push("```");
1636
1590
  lines.push(``);
1637
1591
  const content = lines.join("\n");
1638
- mkdirSync3(dirname5(outPath), { recursive: true });
1592
+ mkdirSync3(dirname4(outPath), { recursive: true });
1639
1593
  writeFileSync3(outPath, content, "utf-8");
1640
1594
  return { outPath, content };
1641
1595
  }
@@ -1749,17 +1703,17 @@ function findESLintCwd(startDir) {
1749
1703
  let dir = startDir;
1750
1704
  for (let i = 0; i < 30; i++) {
1751
1705
  for (const cfg of ESLINT_CONFIG_FILES) {
1752
- if (existsSync5(join4(dir, cfg))) return dir;
1706
+ if (existsSync5(join3(dir, cfg))) return dir;
1753
1707
  }
1754
- if (existsSync5(join4(dir, "package.json"))) return dir;
1755
- const parent = dirname6(dir);
1708
+ if (existsSync5(join3(dir, "package.json"))) return dir;
1709
+ const parent = dirname5(dir);
1756
1710
  if (parent === dir) break;
1757
1711
  dir = parent;
1758
1712
  }
1759
1713
  return startDir;
1760
1714
  }
1761
- function normalizePathSlashes(p2) {
1762
- return p2.replace(/\\/g, "/");
1715
+ function normalizePathSlashes(p) {
1716
+ return p.replace(/\\/g, "/");
1763
1717
  }
1764
1718
  function normalizeDataLocFilePath(absoluteFilePath, projectCwd) {
1765
1719
  const abs = normalizePathSlashes(resolve5(absoluteFilePath));
@@ -1788,16 +1742,16 @@ function resolveRequestedFilePath(filePath) {
1788
1742
  return fromWs;
1789
1743
  }
1790
1744
  for (const top of ["apps", "packages"]) {
1791
- const base = join4(wsRoot, top);
1745
+ const base = join3(wsRoot, top);
1792
1746
  if (!existsSync5(base)) continue;
1793
1747
  try {
1794
1748
  const entries = readdirSync(base, { withFileTypes: true });
1795
1749
  for (const ent of entries) {
1796
1750
  if (!ent.isDirectory()) continue;
1797
- const p2 = resolve5(base, ent.name, filePath);
1798
- if (existsSync5(p2)) {
1799
- resolvedPathCache.set(filePath, p2);
1800
- return p2;
1751
+ const p = resolve5(base, ent.name, filePath);
1752
+ if (existsSync5(p)) {
1753
+ resolvedPathCache.set(filePath, p);
1754
+ return p;
1801
1755
  }
1802
1756
  }
1803
1757
  } catch {
@@ -1810,7 +1764,7 @@ async function getESLintForProject(projectCwd) {
1810
1764
  const cached = eslintInstances.get(projectCwd);
1811
1765
  if (cached) return cached;
1812
1766
  try {
1813
- const req = createRequire(join4(projectCwd, "package.json"));
1767
+ const req = createRequire(join3(projectCwd, "package.json"));
1814
1768
  const mod = req("eslint");
1815
1769
  const ESLintCtor = mod?.ESLint ?? mod?.default?.ESLint ?? mod?.default ?? mod;
1816
1770
  if (!ESLintCtor) return null;
@@ -1839,7 +1793,7 @@ async function lintFile(filePath, onProgress) {
1839
1793
  onProgress("Cache hit (unchanged)");
1840
1794
  return cached.issues;
1841
1795
  }
1842
- const fileDir = dirname6(absolutePath);
1796
+ const fileDir = dirname5(absolutePath);
1843
1797
  const projectCwd = findESLintCwd(fileDir);
1844
1798
  onProgress(`Resolving ESLint project... ${pc.dim(projectCwd)}`);
1845
1799
  const eslint = await getESLintForProject(projectCwd);
@@ -1862,7 +1816,7 @@ async function lintFile(filePath, onProgress) {
1862
1816
  let codeLength = 0;
1863
1817
  try {
1864
1818
  onProgress("Building JSX map...");
1865
- const code = readFileSync2(absolutePath, "utf-8");
1819
+ const code = readFileSync(absolutePath, "utf-8");
1866
1820
  codeLength = code.length;
1867
1821
  lineStarts = buildLineStarts(code);
1868
1822
  spans = buildJsxElementSpans(code, dataLocFile);
@@ -2106,12 +2060,12 @@ async function handleMessage(ws, data) {
2106
2060
  )}`
2107
2061
  );
2108
2062
  } else {
2109
- const screenshotsDir = join4(
2063
+ const screenshotsDir = join3(
2110
2064
  serverAppRootForVision,
2111
2065
  ".uilint",
2112
2066
  "screenshots"
2113
2067
  );
2114
- const imagePath = join4(screenshotsDir, screenshotFile);
2068
+ const imagePath = join3(screenshotsDir, screenshotFile);
2115
2069
  try {
2116
2070
  if (!existsSync5(imagePath)) {
2117
2071
  logWarning(
@@ -2274,10 +2228,10 @@ async function serve(options) {
2274
2228
  }
2275
2229
 
2276
2230
  // src/commands/vision.ts
2277
- import { dirname as dirname7, resolve as resolve6, join as join5 } from "path";
2231
+ import { dirname as dirname6, resolve as resolve6, join as join4 } from "path";
2278
2232
  import {
2279
2233
  existsSync as existsSync6,
2280
- readFileSync as readFileSync3,
2234
+ readFileSync as readFileSync2,
2281
2235
  readdirSync as readdirSync2
2282
2236
  } from "fs";
2283
2237
  import {
@@ -2290,9 +2244,9 @@ function envTruthy3(name) {
2290
2244
  if (!v) return false;
2291
2245
  return v === "1" || v.toLowerCase() === "true" || v.toLowerCase() === "yes";
2292
2246
  }
2293
- function preview3(text2, maxLen) {
2294
- if (text2.length <= maxLen) return text2;
2295
- return text2.slice(0, maxLen) + "\n\u2026<truncated>\u2026\n" + text2.slice(-maxLen);
2247
+ function preview3(text, maxLen) {
2248
+ if (text.length <= maxLen) return text;
2249
+ return text.slice(0, maxLen) + "\n\u2026<truncated>\u2026\n" + text.slice(-maxLen);
2296
2250
  }
2297
2251
  function debugEnabled3(options) {
2298
2252
  return Boolean(options.debug) || envTruthy3("UILINT_DEBUG");
@@ -2323,9 +2277,9 @@ function debugLog3(enabled, message, obj) {
2323
2277
  function findScreenshotsDirUpwards(startDir) {
2324
2278
  let dir = startDir;
2325
2279
  for (let i = 0; i < 20; i++) {
2326
- const candidate = join5(dir, ".uilint", "screenshots");
2280
+ const candidate = join4(dir, ".uilint", "screenshots");
2327
2281
  if (existsSync6(candidate)) return candidate;
2328
- const parent = dirname7(dir);
2282
+ const parent = dirname6(dir);
2329
2283
  if (parent === dir) break;
2330
2284
  dir = parent;
2331
2285
  }
@@ -2333,23 +2287,23 @@ function findScreenshotsDirUpwards(startDir) {
2333
2287
  }
2334
2288
  function listScreenshotSidecars(dirPath) {
2335
2289
  if (!existsSync6(dirPath)) return [];
2336
- const entries = readdirSync2(dirPath).filter((f) => f.endsWith(".json")).map((f) => join5(dirPath, f));
2290
+ const entries = readdirSync2(dirPath).filter((f) => f.endsWith(".json")).map((f) => join4(dirPath, f));
2337
2291
  const out = [];
2338
- for (const p2 of entries) {
2292
+ for (const p of entries) {
2339
2293
  try {
2340
- const json = loadJsonFile(p2);
2294
+ const json = loadJsonFile(p);
2341
2295
  const issues = Array.isArray(json?.issues) ? json.issues : json?.analysisResult?.issues;
2342
2296
  out.push({
2343
- path: p2,
2344
- filename: json?.filename || json?.screenshotFile || p2.split("/").pop() || p2,
2297
+ path: p,
2298
+ filename: json?.filename || json?.screenshotFile || p.split("/").pop() || p,
2345
2299
  timestamp: typeof json?.timestamp === "number" ? json.timestamp : void 0,
2346
2300
  route: typeof json?.route === "string" ? json.route : void 0,
2347
2301
  issueCount: Array.isArray(issues) ? issues.length : void 0
2348
2302
  });
2349
2303
  } catch {
2350
2304
  out.push({
2351
- path: p2,
2352
- filename: p2.split("/").pop() || p2
2305
+ path: p,
2306
+ filename: p.split("/").pop() || p
2353
2307
  });
2354
2308
  }
2355
2309
  }
@@ -2362,11 +2316,11 @@ function listScreenshotSidecars(dirPath) {
2362
2316
  return out;
2363
2317
  }
2364
2318
  function readImageAsBase64(imagePath) {
2365
- const bytes = readFileSync3(imagePath);
2319
+ const bytes = readFileSync2(imagePath);
2366
2320
  return { base64: bytes.toString("base64"), sizeBytes: bytes.byteLength };
2367
2321
  }
2368
2322
  function loadJsonFile(filePath) {
2369
- const raw = readFileSync3(filePath, "utf-8");
2323
+ const raw = readFileSync2(filePath, "utf-8");
2370
2324
  return JSON.parse(raw);
2371
2325
  }
2372
2326
  function formatIssuesText(issues) {
@@ -2383,7 +2337,7 @@ async function vision(options) {
2383
2337
  const dbg = debugEnabled3(options);
2384
2338
  const dbgFull = debugFullEnabled3(options);
2385
2339
  const dbgDump = debugDumpPath3(options);
2386
- if (!isJsonOutput) intro2("Vision (Screenshot) Analysis");
2340
+ if (!isJsonOutput) intro("Vision (Screenshot) Analysis");
2387
2341
  try {
2388
2342
  const projectPath = process.cwd();
2389
2343
  if (options.list) {
@@ -2471,7 +2425,7 @@ async function vision(options) {
2471
2425
  const resolved = await resolveVisionStyleGuide({
2472
2426
  projectPath,
2473
2427
  styleguide: options.styleguide,
2474
- startDir: startPath ? dirname7(startPath) : projectPath
2428
+ startDir: startPath ? dirname6(startPath) : projectPath
2475
2429
  });
2476
2430
  styleGuide = resolved.styleGuide;
2477
2431
  styleguideLocation = resolved.styleguideLocation;
@@ -2481,12 +2435,12 @@ async function vision(options) {
2481
2435
  logSuccess(`Using styleguide: ${pc.dim(styleguideLocation)}`);
2482
2436
  } else if (!styleGuide && !isJsonOutput) {
2483
2437
  logWarning("No styleguide found");
2484
- note2(
2438
+ note(
2485
2439
  [
2486
2440
  `Searched in: ${options.styleguide || projectPath}`,
2487
2441
  "",
2488
2442
  "Looked for:",
2489
- ...STYLEGUIDE_PATHS2.map((p2) => ` \u2022 ${p2}`),
2443
+ ...STYLEGUIDE_PATHS2.map((p) => ` \u2022 ${p}`),
2490
2444
  "",
2491
2445
  `Create ${pc.cyan(
2492
2446
  ".uilint/styleguide.md"
@@ -2520,7 +2474,7 @@ async function vision(options) {
2520
2474
  const resolvedImagePath = imagePath || (() => {
2521
2475
  const screenshotFile = typeof sidecar?.screenshotFile === "string" ? sidecar.screenshotFile : typeof sidecar?.filename === "string" ? sidecar.filename : void 0;
2522
2476
  if (!screenshotFile) return null;
2523
- const baseDir = sidecarPath ? dirname7(sidecarPath) : projectPath;
2477
+ const baseDir = sidecarPath ? dirname6(sidecarPath) : projectPath;
2524
2478
  const abs = resolve6(baseDir, screenshotFile);
2525
2479
  return abs;
2526
2480
  })();
@@ -2699,7 +2653,7 @@ async function vision(options) {
2699
2653
  (firstAnswerNs ?? lastThinkingNs ?? analysisEndNs) - firstThinkingNs
2700
2654
  ) : null;
2701
2655
  const outputMs = firstAnswerNs && (lastAnswerNs || analysisEndNs) ? nsToMs((lastAnswerNs ?? analysisEndNs) - firstAnswerNs) : null;
2702
- note2(
2656
+ note(
2703
2657
  [
2704
2658
  `Prepare Ollama: ${formatMs(prepMs)}`,
2705
2659
  `Time to first token: ${maybeMs(ttftMs)}`,
@@ -2757,9 +2711,9 @@ async function vision(options) {
2757
2711
  }
2758
2712
 
2759
2713
  // src/index.ts
2760
- import { readFileSync as readFileSync4 } from "fs";
2761
- import { dirname as dirname8, join as join6 } from "path";
2762
- import { fileURLToPath as fileURLToPath2 } from "url";
2714
+ import { readFileSync as readFileSync3 } from "fs";
2715
+ import { dirname as dirname7, join as join5 } from "path";
2716
+ import { fileURLToPath } from "url";
2763
2717
  function assertNodeVersion(minMajor) {
2764
2718
  const ver = process.versions.node || "";
2765
2719
  const majorStr = ver.split(".")[0] || "";
@@ -2773,17 +2727,17 @@ function assertNodeVersion(minMajor) {
2773
2727
  }
2774
2728
  assertNodeVersion(20);
2775
2729
  var program = new Command();
2776
- function getCLIVersion2() {
2730
+ function getCLIVersion() {
2777
2731
  try {
2778
- const __dirname = dirname8(fileURLToPath2(import.meta.url));
2779
- const pkgPath = join6(__dirname, "..", "package.json");
2780
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
2732
+ const __dirname = dirname7(fileURLToPath(import.meta.url));
2733
+ const pkgPath = join5(__dirname, "..", "package.json");
2734
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
2781
2735
  return pkg.version || "0.0.0";
2782
2736
  } catch {
2783
2737
  return "0.0.0";
2784
2738
  }
2785
2739
  }
2786
- program.name("uilint").description("AI-powered UI consistency checker").version(getCLIVersion2());
2740
+ program.name("uilint").description("AI-powered UI consistency checker").version(getCLIVersion());
2787
2741
  program.command("analyze").description(
2788
2742
  "Analyze a source file/snippet for style issues (data-loc aware)"
2789
2743
  ).option("-f, --input-file <path>", "Path to a source file to analyze").option("--source-code <code>", "Source code to analyze (string)").option("--file-path <path>", "File path label shown in the prompt").option("--style-guide <text>", "Inline styleguide content to use").option("--styleguide-path <path>", "Path to a style guide file").option("--component-name <name>", "Component name for focused analysis").option(
@@ -2851,7 +2805,7 @@ program.command("update").description("Update existing style guide with new styl
2851
2805
  });
2852
2806
  });
2853
2807
  program.command("install").description("Install UILint integration").option("--force", "Overwrite existing configuration files").action(async (options) => {
2854
- const { installUI } = await import("./install-ui-OEFHX4FG.js");
2808
+ const { installUI } = await import("./install-ui-KI7YHOVZ.js");
2855
2809
  await installUI({ force: options.force });
2856
2810
  });
2857
2811
  program.command("serve").description("Start WebSocket server for real-time UI linting").option("-p, --port <number>", "Port to listen on", "9234").action(async (options) => {