uilint 0.2.72 → 0.2.73

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
@@ -25,7 +25,7 @@ import {
25
25
  import "./chunk-JPE27ROY.js";
26
26
 
27
27
  // src/index.ts
28
- import { Command as Command7 } from "commander";
28
+ import { Command as Command8 } from "commander";
29
29
 
30
30
  // src/commands/scan.ts
31
31
  import { dirname, resolve as resolve2 } from "path";
@@ -261,8 +261,8 @@ async function initializeLangfuseIfEnabled() {
261
261
  },
262
262
  { asType: "generation" }
263
263
  );
264
- await new Promise((resolve8) => {
265
- resolveTrace = resolve8;
264
+ await new Promise((resolve11) => {
265
+ resolveTrace = resolve11;
266
266
  });
267
267
  if (endData && generationRef) {
268
268
  const usageDetails = endData.usage ? Object.fromEntries(
@@ -1178,14 +1178,14 @@ import {
1178
1178
  } from "uilint-core";
1179
1179
  import { ensureOllamaReady as ensureOllamaReady3 } from "uilint-core/node";
1180
1180
  async function readStdin2() {
1181
- return new Promise((resolve8) => {
1181
+ return new Promise((resolve11) => {
1182
1182
  let data = "";
1183
1183
  const rl = createInterface({ input: process.stdin });
1184
1184
  rl.on("line", (line) => {
1185
1185
  data += line;
1186
1186
  });
1187
1187
  rl.on("close", () => {
1188
- resolve8(data);
1188
+ resolve11(data);
1189
1189
  });
1190
1190
  });
1191
1191
  }
@@ -2699,12 +2699,12 @@ async function serve(options) {
2699
2699
  `UILint WebSocket server running on ${pc.cyan(`ws://localhost:${port}`)}`
2700
2700
  );
2701
2701
  logInfo("Press Ctrl+C to stop");
2702
- await new Promise((resolve8) => {
2702
+ await new Promise((resolve11) => {
2703
2703
  process.on("SIGINT", () => {
2704
2704
  logInfo("Shutting down...");
2705
2705
  wss.close();
2706
2706
  fileWatcher?.close();
2707
- resolve8();
2707
+ resolve11();
2708
2708
  });
2709
2709
  });
2710
2710
  }
@@ -3248,7 +3248,7 @@ function parseOptionsJson(optionsStr) {
3248
3248
  }
3249
3249
  }
3250
3250
  async function sendConfigMessage(port, key, value) {
3251
- return new Promise((resolve8) => {
3251
+ return new Promise((resolve11) => {
3252
3252
  const url = `ws://localhost:${port}`;
3253
3253
  const ws = new WebSocket2(url);
3254
3254
  let resolved = false;
@@ -3256,7 +3256,7 @@ async function sendConfigMessage(port, key, value) {
3256
3256
  if (!resolved) {
3257
3257
  resolved = true;
3258
3258
  ws.close();
3259
- resolve8(false);
3259
+ resolve11(false);
3260
3260
  }
3261
3261
  }, 5e3);
3262
3262
  ws.on("open", () => {
@@ -3267,7 +3267,7 @@ async function sendConfigMessage(port, key, value) {
3267
3267
  resolved = true;
3268
3268
  clearTimeout(timeout);
3269
3269
  ws.close();
3270
- resolve8(true);
3270
+ resolve11(true);
3271
3271
  }
3272
3272
  }, 100);
3273
3273
  });
@@ -3275,13 +3275,13 @@ async function sendConfigMessage(port, key, value) {
3275
3275
  if (!resolved) {
3276
3276
  resolved = true;
3277
3277
  clearTimeout(timeout);
3278
- resolve8(false);
3278
+ resolve11(false);
3279
3279
  }
3280
3280
  });
3281
3281
  });
3282
3282
  }
3283
3283
  async function sendRuleConfigMessage(port, ruleId, severity, options) {
3284
- return new Promise((resolve8) => {
3284
+ return new Promise((resolve11) => {
3285
3285
  const url = `ws://localhost:${port}`;
3286
3286
  const ws = new WebSocket2(url);
3287
3287
  let resolved = false;
@@ -3290,7 +3290,7 @@ async function sendRuleConfigMessage(port, ruleId, severity, options) {
3290
3290
  if (!resolved) {
3291
3291
  resolved = true;
3292
3292
  ws.close();
3293
- resolve8({ success: false, error: "Request timed out" });
3293
+ resolve11({ success: false, error: "Request timed out" });
3294
3294
  }
3295
3295
  }, 1e4);
3296
3296
  ws.on("open", () => {
@@ -3311,7 +3311,7 @@ async function sendRuleConfigMessage(port, ruleId, severity, options) {
3311
3311
  resolved = true;
3312
3312
  clearTimeout(timeout);
3313
3313
  ws.close();
3314
- resolve8({
3314
+ resolve11({
3315
3315
  success: msg.success,
3316
3316
  error: msg.error
3317
3317
  });
@@ -3324,7 +3324,7 @@ async function sendRuleConfigMessage(port, ruleId, severity, options) {
3324
3324
  if (!resolved) {
3325
3325
  resolved = true;
3326
3326
  clearTimeout(timeout);
3327
- resolve8({ success: false, error: "Connection error" });
3327
+ resolve11({ success: false, error: "Connection error" });
3328
3328
  }
3329
3329
  });
3330
3330
  });
@@ -3720,13 +3720,441 @@ function createDuplicatesCommand() {
3720
3720
  }
3721
3721
 
3722
3722
  // src/index.ts
3723
- import { readFileSync as readFileSync4 } from "fs";
3724
- import { dirname as dirname7, join as join5 } from "path";
3723
+ import { readFileSync as readFileSync6 } from "fs";
3724
+ import { dirname as dirname10, join as join6 } from "path";
3725
3725
  import { fileURLToPath } from "url";
3726
3726
 
3727
- // src/commands/socket/index.ts
3727
+ // src/commands/manifest/index.ts
3728
3728
  import { Command as Command6 } from "commander";
3729
- import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
3729
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
3730
+ import { dirname as dirname9, resolve as resolve10 } from "path";
3731
+
3732
+ // src/commands/manifest/generator.ts
3733
+ import { readFileSync as readFileSync4 } from "fs";
3734
+ import { resolve as resolve9, dirname as dirname8, relative as relative6 } from "path";
3735
+ import { glob } from "glob";
3736
+ import { execSync } from "child_process";
3737
+ import { findWorkspaceRoot as findWorkspaceRoot5 } from "uilint-core/node";
3738
+ import { ruleRegistry as ruleRegistry2 } from "uilint-eslint";
3739
+
3740
+ // src/utils/eslint-utils.ts
3741
+ import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
3742
+ import { createRequire as createRequire2 } from "module";
3743
+ import { dirname as dirname7, resolve as resolve8, relative as relative5, join as join5 } from "path";
3744
+ var ESLINT_CONFIG_FILES2 = [
3745
+ // Flat config (ESLint v9+)
3746
+ "eslint.config.js",
3747
+ "eslint.config.mjs",
3748
+ "eslint.config.cjs",
3749
+ "eslint.config.ts",
3750
+ // Legacy config
3751
+ ".eslintrc",
3752
+ ".eslintrc.js",
3753
+ ".eslintrc.cjs",
3754
+ ".eslintrc.json",
3755
+ ".eslintrc.yaml",
3756
+ ".eslintrc.yml"
3757
+ ];
3758
+ function buildLineStarts2(code) {
3759
+ const starts = [0];
3760
+ for (let i = 0; i < code.length; i++) {
3761
+ if (code.charCodeAt(i) === 10) starts.push(i + 1);
3762
+ }
3763
+ return starts;
3764
+ }
3765
+ function offsetFromLineCol2(lineStarts, line1, col0, codeLength) {
3766
+ const lineIndex = Math.max(0, Math.min(lineStarts.length - 1, line1 - 1));
3767
+ const base = lineStarts[lineIndex] ?? 0;
3768
+ return Math.max(0, Math.min(codeLength, base + Math.max(0, col0)));
3769
+ }
3770
+ function buildJsxElementSpans2(code, dataLocFile) {
3771
+ const localRequire2 = createRequire2(import.meta.url);
3772
+ const { parse: parse3 } = localRequire2("@typescript-eslint/typescript-estree");
3773
+ const ast = parse3(code, {
3774
+ loc: true,
3775
+ range: true,
3776
+ jsx: true,
3777
+ comment: false,
3778
+ errorOnUnknownASTType: false
3779
+ });
3780
+ const spans = [];
3781
+ function walk(node) {
3782
+ if (!node || typeof node !== "object") return;
3783
+ if (node.type === "JSXElement") {
3784
+ const range = node.range;
3785
+ const opening = node.openingElement;
3786
+ const loc = opening?.loc?.start;
3787
+ if (range && typeof range[0] === "number" && typeof range[1] === "number" && loc && typeof loc.line === "number" && typeof loc.column === "number") {
3788
+ const dataLoc = `${dataLocFile}:${loc.line}:${loc.column}`;
3789
+ spans.push({ start: range[0], end: range[1], dataLoc });
3790
+ }
3791
+ }
3792
+ for (const key of Object.keys(node)) {
3793
+ const child = node[key];
3794
+ if (Array.isArray(child)) {
3795
+ for (const item of child) walk(item);
3796
+ } else if (child && typeof child === "object") {
3797
+ walk(child);
3798
+ }
3799
+ }
3800
+ }
3801
+ walk(ast);
3802
+ spans.sort((a, b) => a.end - a.start - (b.end - b.start));
3803
+ return spans;
3804
+ }
3805
+ function mapMessageToDataLoc2(params) {
3806
+ const col0 = typeof params.messageCol1 === "number" ? Math.max(0, params.messageCol1 - 1) : 0;
3807
+ const offset = offsetFromLineCol2(
3808
+ params.lineStarts,
3809
+ params.messageLine1,
3810
+ col0,
3811
+ params.codeLength
3812
+ );
3813
+ for (const s of params.spans) {
3814
+ if (s.start <= offset && offset < s.end) return s.dataLoc;
3815
+ }
3816
+ return void 0;
3817
+ }
3818
+ function normalizePathSlashes2(p) {
3819
+ return p.replace(/\\/g, "/");
3820
+ }
3821
+ function normalizeDataLocFilePath2(absoluteFilePath, projectCwd) {
3822
+ const abs = normalizePathSlashes2(resolve8(absoluteFilePath));
3823
+ const cwd = normalizePathSlashes2(resolve8(projectCwd));
3824
+ if (abs === cwd || abs.startsWith(cwd + "/")) {
3825
+ return normalizePathSlashes2(relative5(cwd, abs));
3826
+ }
3827
+ return abs;
3828
+ }
3829
+ function findESLintCwd2(startDir) {
3830
+ let dir = startDir;
3831
+ for (let i = 0; i < 30; i++) {
3832
+ for (const cfg of ESLINT_CONFIG_FILES2) {
3833
+ if (existsSync7(join5(dir, cfg))) return dir;
3834
+ }
3835
+ if (existsSync7(join5(dir, "package.json"))) return dir;
3836
+ const parent = dirname7(dir);
3837
+ if (parent === dir) break;
3838
+ dir = parent;
3839
+ }
3840
+ return startDir;
3841
+ }
3842
+ var eslintInstances2 = /* @__PURE__ */ new Map();
3843
+ async function getESLintForProject2(projectCwd) {
3844
+ const cached = eslintInstances2.get(projectCwd);
3845
+ if (cached) return cached;
3846
+ try {
3847
+ const req = createRequire2(join5(projectCwd, "package.json"));
3848
+ const mod = req("eslint");
3849
+ const ESLintCtor = mod?.ESLint ?? mod?.default?.ESLint ?? mod?.default ?? mod;
3850
+ if (!ESLintCtor) return null;
3851
+ const eslint = new ESLintCtor({ cwd: projectCwd });
3852
+ eslintInstances2.set(projectCwd, eslint);
3853
+ return eslint;
3854
+ } catch {
3855
+ return null;
3856
+ }
3857
+ }
3858
+ async function lintFileWithDataLoc(absolutePath, projectCwd, onProgress) {
3859
+ const progress = onProgress ?? (() => {
3860
+ });
3861
+ if (!existsSync7(absolutePath)) {
3862
+ progress(`File not found: ${absolutePath}`);
3863
+ return [];
3864
+ }
3865
+ progress(`Resolving ESLint project... ${projectCwd}`);
3866
+ const eslint = await getESLintForProject2(projectCwd);
3867
+ if (!eslint) {
3868
+ progress("ESLint not available");
3869
+ return [];
3870
+ }
3871
+ try {
3872
+ progress("Running ESLint...");
3873
+ const results = await eslint.lintFiles([absolutePath]);
3874
+ const messages = Array.isArray(results) && results.length > 0 ? results[0].messages || [] : [];
3875
+ const dataLocFile = normalizeDataLocFilePath2(absolutePath, projectCwd);
3876
+ let spans = [];
3877
+ let lineStarts = [];
3878
+ let codeLength = 0;
3879
+ try {
3880
+ progress("Building JSX map...");
3881
+ const code = readFileSync3(absolutePath, "utf-8");
3882
+ codeLength = code.length;
3883
+ lineStarts = buildLineStarts2(code);
3884
+ spans = buildJsxElementSpans2(code, dataLocFile);
3885
+ progress(`JSX map: ${spans.length} element(s)`);
3886
+ } catch (e) {
3887
+ progress("JSX map failed (falling back to unmapped issues)");
3888
+ spans = [];
3889
+ lineStarts = [];
3890
+ codeLength = 0;
3891
+ }
3892
+ const issues = messages.filter((m) => typeof m?.message === "string").map((m) => {
3893
+ const line = typeof m.line === "number" ? m.line : 1;
3894
+ const column = typeof m.column === "number" ? m.column : void 0;
3895
+ const mappedDataLoc = spans.length > 0 && lineStarts.length > 0 && codeLength > 0 ? mapMessageToDataLoc2({
3896
+ spans,
3897
+ lineStarts,
3898
+ codeLength,
3899
+ messageLine1: line,
3900
+ messageCol1: column
3901
+ }) : void 0;
3902
+ return {
3903
+ line,
3904
+ column,
3905
+ message: m.message,
3906
+ ruleId: typeof m.ruleId === "string" ? m.ruleId : void 0,
3907
+ dataLoc: mappedDataLoc
3908
+ };
3909
+ });
3910
+ const mappedCount = issues.filter((i) => Boolean(i.dataLoc)).length;
3911
+ if (issues.length > 0) {
3912
+ progress(`Mapped ${mappedCount}/${issues.length} issue(s) to JSX elements`);
3913
+ }
3914
+ return issues;
3915
+ } catch (error) {
3916
+ progress(`ESLint failed: ${error instanceof Error ? error.message : String(error)}`);
3917
+ return [];
3918
+ }
3919
+ }
3920
+ function extractSourceSnippet(code, centerLine, contextLines = 3) {
3921
+ const allLines = code.split("\n");
3922
+ const startLine = Math.max(1, centerLine - contextLines);
3923
+ const endLine = Math.min(allLines.length, centerLine + contextLines);
3924
+ return {
3925
+ lines: allLines.slice(startLine - 1, endLine),
3926
+ startLine,
3927
+ endLine
3928
+ };
3929
+ }
3930
+
3931
+ // src/commands/manifest/generator.ts
3932
+ var DEFAULT_INCLUDE = ["**/*.tsx", "**/*.jsx"];
3933
+ var DEFAULT_EXCLUDE = [
3934
+ "node_modules/**",
3935
+ "dist/**",
3936
+ ".next/**",
3937
+ "build/**",
3938
+ "coverage/**",
3939
+ ".uilint/**",
3940
+ "**/*.test.tsx",
3941
+ "**/*.test.jsx",
3942
+ "**/*.spec.tsx",
3943
+ "**/*.spec.jsx"
3944
+ ];
3945
+ function getGitInfo(cwd) {
3946
+ try {
3947
+ const commitSha = execSync("git rev-parse HEAD", {
3948
+ cwd,
3949
+ encoding: "utf-8",
3950
+ stdio: ["pipe", "pipe", "pipe"]
3951
+ }).trim();
3952
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
3953
+ cwd,
3954
+ encoding: "utf-8",
3955
+ stdio: ["pipe", "pipe", "pipe"]
3956
+ }).trim();
3957
+ return { commitSha, branch };
3958
+ } catch {
3959
+ return {};
3960
+ }
3961
+ }
3962
+ function buildRuleMetadata(appRoot) {
3963
+ const eslintConfigPath = findEslintConfigFile(appRoot);
3964
+ const currentRuleConfigs = eslintConfigPath ? readRuleConfigsFromConfig(eslintConfigPath) : /* @__PURE__ */ new Map();
3965
+ return ruleRegistry2.filter((rule) => currentRuleConfigs.has(rule.id)).map((rule) => {
3966
+ const currentConfig = currentRuleConfigs.get(rule.id);
3967
+ return {
3968
+ id: rule.id,
3969
+ name: rule.name,
3970
+ description: rule.description,
3971
+ category: rule.category,
3972
+ defaultSeverity: rule.defaultSeverity,
3973
+ currentSeverity: currentConfig?.severity,
3974
+ docs: rule.docs,
3975
+ optionSchema: rule.optionSchema
3976
+ };
3977
+ });
3978
+ }
3979
+ async function generateManifest(options = {}) {
3980
+ const cwd = resolve9(options.cwd ?? process.cwd());
3981
+ const include = options.include ?? DEFAULT_INCLUDE;
3982
+ const exclude = options.exclude ?? DEFAULT_EXCLUDE;
3983
+ const includeSnippets = options.includeSnippets ?? true;
3984
+ const snippetContextLines = options.snippetContextLines ?? 3;
3985
+ const onProgress = options.onProgress ?? (() => {
3986
+ });
3987
+ onProgress("Finding workspace root...");
3988
+ const workspaceRoot = findWorkspaceRoot5(cwd);
3989
+ const appRoot = cwd;
3990
+ onProgress("Scanning for JSX/TSX files...");
3991
+ const files = await glob(include, {
3992
+ cwd,
3993
+ ignore: exclude,
3994
+ absolute: true,
3995
+ nodir: true
3996
+ });
3997
+ onProgress(`Found ${files.length} files to scan`);
3998
+ const gitInfo = getGitInfo(cwd);
3999
+ const rules = buildRuleMetadata(appRoot);
4000
+ onProgress(`Loaded ${rules.length} rule definitions`);
4001
+ const manifestFiles = [];
4002
+ let totalIssues = 0;
4003
+ let errorCount = 0;
4004
+ let warnCount = 0;
4005
+ for (let i = 0; i < files.length; i++) {
4006
+ const absolutePath = files[i];
4007
+ const relativePath = relative6(cwd, absolutePath);
4008
+ onProgress(`Linting ${relativePath}...`, i + 1, files.length);
4009
+ const fileDir = dirname8(absolutePath);
4010
+ const projectCwd = findESLintCwd2(fileDir);
4011
+ const issues = await lintFileWithDataLoc(absolutePath, projectCwd);
4012
+ if (issues.length === 0) continue;
4013
+ const manifestIssues = issues.filter((issue) => Boolean(issue.dataLoc)).map((issue) => ({
4014
+ line: issue.line,
4015
+ column: issue.column,
4016
+ message: issue.message,
4017
+ ruleId: issue.ruleId,
4018
+ dataLoc: issue.dataLoc
4019
+ }));
4020
+ if (manifestIssues.length === 0) continue;
4021
+ let snippets;
4022
+ if (includeSnippets) {
4023
+ try {
4024
+ const code = readFileSync4(absolutePath, "utf-8");
4025
+ snippets = {};
4026
+ const issuesByDataLoc = /* @__PURE__ */ new Map();
4027
+ for (const issue of manifestIssues) {
4028
+ if (!issuesByDataLoc.has(issue.dataLoc)) {
4029
+ issuesByDataLoc.set(issue.dataLoc, issue);
4030
+ }
4031
+ }
4032
+ for (const [dataLoc, issue] of issuesByDataLoc) {
4033
+ snippets[dataLoc] = extractSourceSnippet(code, issue.line, snippetContextLines);
4034
+ }
4035
+ } catch {
4036
+ }
4037
+ }
4038
+ for (const issue of manifestIssues) {
4039
+ if (issue.ruleId) {
4040
+ const rule = rules.find((r) => `uilint/${r.id}` === issue.ruleId || r.id === issue.ruleId);
4041
+ const severity = rule?.currentSeverity ?? rule?.defaultSeverity ?? "warn";
4042
+ if (severity === "error") {
4043
+ errorCount++;
4044
+ } else {
4045
+ warnCount++;
4046
+ }
4047
+ } else {
4048
+ warnCount++;
4049
+ }
4050
+ }
4051
+ totalIssues += manifestIssues.length;
4052
+ const dataLocFilePath = normalizeDataLocFilePath2(absolutePath, projectCwd);
4053
+ manifestFiles.push({
4054
+ filePath: dataLocFilePath,
4055
+ issues: manifestIssues,
4056
+ snippets
4057
+ });
4058
+ }
4059
+ onProgress(`Scan complete: ${totalIssues} issues in ${manifestFiles.length} files`);
4060
+ return {
4061
+ version: "1.0",
4062
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4063
+ workspaceRoot,
4064
+ appRoot,
4065
+ commitSha: gitInfo.commitSha,
4066
+ branch: gitInfo.branch,
4067
+ files: manifestFiles,
4068
+ rules,
4069
+ summary: {
4070
+ filesScanned: files.length,
4071
+ filesWithIssues: manifestFiles.length,
4072
+ totalIssues,
4073
+ bySeverity: {
4074
+ error: errorCount,
4075
+ warn: warnCount
4076
+ }
4077
+ }
4078
+ };
4079
+ }
4080
+
4081
+ // src/commands/manifest/index.ts
4082
+ function createManifestCommand() {
4083
+ const cmd = new Command6("build-manifest").description("Generate static lint manifest for production deployment").option("-o, --output <path>", "Output path for manifest", ".uilint/manifest.json").option("--include <patterns...>", "File patterns to include").option("--exclude <patterns...>", "File patterns to exclude").option("--no-snippets", "Exclude source code snippets from manifest").option("--context-lines <n>", "Number of context lines for snippets", "3").option("--pretty", "Pretty-print JSON output").option("--quiet", "Suppress progress output").action(async (options) => {
4084
+ await buildManifest({
4085
+ output: options.output,
4086
+ include: options.include,
4087
+ exclude: options.exclude,
4088
+ includeSnippets: options.snippets !== false,
4089
+ contextLines: parseInt(options.contextLines, 10),
4090
+ pretty: options.pretty,
4091
+ quiet: options.quiet
4092
+ });
4093
+ });
4094
+ return cmd;
4095
+ }
4096
+ async function buildManifest(options) {
4097
+ const startTime = Date.now();
4098
+ const outputPath = resolve10(process.cwd(), options.output);
4099
+ if (!options.quiet) {
4100
+ logInfo(`Building lint manifest...`);
4101
+ logInfo(`Output: ${pc.dim(outputPath)}`);
4102
+ }
4103
+ try {
4104
+ const manifest = await generateManifest({
4105
+ cwd: process.cwd(),
4106
+ include: options.include,
4107
+ exclude: options.exclude,
4108
+ includeSnippets: options.includeSnippets,
4109
+ snippetContextLines: options.contextLines,
4110
+ onProgress: options.quiet ? void 0 : (message, current, total) => {
4111
+ if (current !== void 0 && total !== void 0) {
4112
+ logInfo(` [${current}/${total}] ${message}`);
4113
+ } else {
4114
+ logInfo(` ${message}`);
4115
+ }
4116
+ }
4117
+ });
4118
+ const outputDir = dirname9(outputPath);
4119
+ if (!existsSync9(outputDir)) {
4120
+ mkdirSync6(outputDir, { recursive: true });
4121
+ }
4122
+ const json = options.pretty ? JSON.stringify(manifest, null, 2) : JSON.stringify(manifest);
4123
+ writeFileSync6(outputPath, json, "utf-8");
4124
+ const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
4125
+ const sizeKb = Math.round(Buffer.byteLength(json) / 1024);
4126
+ if (!options.quiet) {
4127
+ logSuccess(`Manifest generated in ${elapsed}s`);
4128
+ logInfo(` Files scanned: ${pc.bold(String(manifest.summary.filesScanned))}`);
4129
+ logInfo(` Files with issues: ${pc.bold(String(manifest.summary.filesWithIssues))}`);
4130
+ logInfo(` Total issues: ${pc.bold(String(manifest.summary.totalIssues))}`);
4131
+ if (manifest.summary.bySeverity.error > 0) {
4132
+ logInfo(` Errors: ${pc.red(String(manifest.summary.bySeverity.error))}`);
4133
+ }
4134
+ if (manifest.summary.bySeverity.warn > 0) {
4135
+ logInfo(` Warnings: ${pc.yellow(String(manifest.summary.bySeverity.warn))}`);
4136
+ }
4137
+ logInfo(` Output size: ${pc.dim(`${sizeKb}kb`)}`);
4138
+ if (manifest.commitSha) {
4139
+ logInfo(` Git commit: ${pc.dim(manifest.commitSha.substring(0, 7))}`);
4140
+ }
4141
+ if (manifest.branch) {
4142
+ logInfo(` Git branch: ${pc.dim(manifest.branch)}`);
4143
+ }
4144
+ logSuccess(`Wrote ${pc.cyan(options.output)}`);
4145
+ }
4146
+ if (manifest.summary.bySeverity.error > 0) {
4147
+ process.exitCode = 1;
4148
+ }
4149
+ } catch (error) {
4150
+ logError(`Failed to generate manifest: ${error instanceof Error ? error.message : String(error)}`);
4151
+ process.exit(1);
4152
+ }
4153
+ }
4154
+
4155
+ // src/commands/socket/index.ts
4156
+ import { Command as Command7 } from "commander";
4157
+ import { readFileSync as readFileSync5, existsSync as existsSync10 } from "fs";
3730
4158
  import chalk6 from "chalk";
3731
4159
 
3732
4160
  // src/commands/socket/client.ts
@@ -3753,7 +4181,7 @@ var SocketClient = class {
3753
4181
  async connect() {
3754
4182
  if (this.connected) return;
3755
4183
  if (this.connectionPromise) return this.connectionPromise;
3756
- this.connectionPromise = new Promise((resolve8, reject) => {
4184
+ this.connectionPromise = new Promise((resolve11, reject) => {
3757
4185
  const timeout = setTimeout(() => {
3758
4186
  reject(new Error(`Connection timeout to ${this.url}`));
3759
4187
  }, this.connectTimeout);
@@ -3762,7 +4190,7 @@ var SocketClient = class {
3762
4190
  clearTimeout(timeout);
3763
4191
  this.connected = true;
3764
4192
  this.log("Connected to", this.url);
3765
- resolve8();
4193
+ resolve11();
3766
4194
  });
3767
4195
  this.ws.on("message", (data) => {
3768
4196
  try {
@@ -3838,7 +4266,7 @@ var SocketClient = class {
3838
4266
  this.messageQueue = this.messageQueue.filter((m) => m !== existing);
3839
4267
  return Promise.resolve(existing);
3840
4268
  }
3841
- return new Promise((resolve8, reject) => {
4269
+ return new Promise((resolve11, reject) => {
3842
4270
  const timeoutId = setTimeout(() => {
3843
4271
  this.pendingRequests = this.pendingRequests.filter(
3844
4272
  (r) => r.timeout !== timeoutId
@@ -3847,7 +4275,7 @@ var SocketClient = class {
3847
4275
  }, timeout);
3848
4276
  this.pendingRequests.push({
3849
4277
  predicate,
3850
- resolve: resolve8,
4278
+ resolve: resolve11,
3851
4279
  reject,
3852
4280
  timeout: timeoutId
3853
4281
  });
@@ -4188,14 +4616,14 @@ Available commands:
4188
4616
  console.log(chalk6.red("Usage: vision:analyze <route> <manifestFile> [screenshotFile]"));
4189
4617
  } else {
4190
4618
  const manifestPath = args[1];
4191
- if (!existsSync7(manifestPath)) {
4619
+ if (!existsSync10(manifestPath)) {
4192
4620
  console.log(chalk6.red(`Manifest file not found: ${manifestPath}`));
4193
4621
  } else {
4194
- const manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
4622
+ const manifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
4195
4623
  const params = { route: args[0], manifest };
4196
4624
  if (args[2]) {
4197
- if (existsSync7(args[2])) {
4198
- const imageBuffer = readFileSync3(args[2]);
4625
+ if (existsSync10(args[2])) {
4626
+ const imageBuffer = readFileSync5(args[2]);
4199
4627
  params.screenshot = imageBuffer.toString("base64");
4200
4628
  } else {
4201
4629
  console.log(chalk6.red(`Screenshot file not found: ${args[2]}`));
@@ -4309,7 +4737,7 @@ async function runListen(client, options, filter) {
4309
4737
  });
4310
4738
  }
4311
4739
  function createSocketCommand() {
4312
- const cmd = new Command6("socket").description("Interact with the UILint socket server").option("-p, --port <number>", "Socket server port", "9234").option("-d, --debug", "Enable debug logging", false).option("-j, --json", "Output JSON format", false).option("-t, --timeout <ms>", "Request timeout in milliseconds", "30000");
4740
+ const cmd = new Command7("socket").description("Interact with the UILint socket server").option("-p, --port <number>", "Socket server port", "9234").option("-d, --debug", "Enable debug logging", false).option("-j, --json", "Output JSON format", false).option("-t, --timeout <ms>", "Request timeout in milliseconds", "30000");
4313
4741
  cmd.action(async (cmdOptions) => {
4314
4742
  const options = {
4315
4743
  port: parseInt(cmdOptions.port, 10),
@@ -4456,12 +4884,12 @@ function assertNodeVersion(minMajor, minMinor) {
4456
4884
  }
4457
4885
  }
4458
4886
  assertNodeVersion(20, 19);
4459
- var program = new Command7();
4887
+ var program = new Command8();
4460
4888
  function getCLIVersion() {
4461
4889
  try {
4462
- const __dirname = dirname7(fileURLToPath(import.meta.url));
4463
- const pkgPath = join5(__dirname, "..", "package.json");
4464
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
4890
+ const __dirname = dirname10(fileURLToPath(import.meta.url));
4891
+ const pkgPath = join6(__dirname, "..", "package.json");
4892
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
4465
4893
  return pkg.version || "0.0.0";
4466
4894
  } catch {
4467
4895
  return "0.0.0";
@@ -4593,6 +5021,7 @@ program.command("config").description("Get or set UILint configuration options")
4593
5021
  });
4594
5022
  });
4595
5023
  program.addCommand(createDuplicatesCommand());
5024
+ program.addCommand(createManifestCommand());
4596
5025
  program.addCommand(createSocketCommand());
4597
5026
  program.command("upgrade").description("Update installed ESLint rules to latest versions").option("--check", "Show available updates without applying").option("-y, --yes", "Auto-confirm all updates").option("--dry-run", "Show what would change without modifying files").option("--rule <id>", "Upgrade only a specific rule").action(async (options) => {
4598
5027
  const { upgrade } = await import("./upgrade-GUYNMGSY.js");