uilint 0.2.147 → 0.2.149

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
@@ -2590,7 +2590,10 @@ function processLintResults(absolutePath, projectCwd, messages, onProgress) {
2590
2590
  onProgress(`JSX map: ${spans.length} element(s)`);
2591
2591
  } catch (e) {
2592
2592
  onProgress("JSX map failed (falling back to unmapped issues)");
2593
- logServerError("JSX map failed", e instanceof Error ? e.message : String(e));
2593
+ logServerError(
2594
+ "JSX map failed",
2595
+ e instanceof Error ? e.message : String(e)
2596
+ );
2594
2597
  spans = [];
2595
2598
  lineStarts = [];
2596
2599
  codeLength = 0;
@@ -2617,13 +2620,17 @@ function processLintResults(absolutePath, projectCwd, messages, onProgress) {
2617
2620
  });
2618
2621
  const mappedCount = issues.filter((i) => Boolean(i.dataLoc)).length;
2619
2622
  if (issues.length > 0) {
2620
- onProgress(`Mapped ${mappedCount}/${issues.length} issue(s) to JSX elements`);
2623
+ onProgress(
2624
+ `Mapped ${mappedCount}/${issues.length} issue(s) to JSX elements`
2625
+ );
2621
2626
  }
2622
2627
  if (fileCode && issues.length > 0) {
2623
2628
  onProgress("Extracting scope info...");
2624
2629
  issues = enrichIssuesWithScopeInfo(issues, fileCode);
2625
2630
  const scopeCount = issues.filter((i) => Boolean(i.scopeInfo)).length;
2626
- onProgress(`Enriched ${scopeCount}/${issues.length} issue(s) with scope info`);
2631
+ onProgress(
2632
+ `Enriched ${scopeCount}/${issues.length} issue(s) with scope info`
2633
+ );
2627
2634
  }
2628
2635
  return issues;
2629
2636
  }
@@ -2655,7 +2662,9 @@ async function lintFileFast(filePath, onProgress) {
2655
2662
  );
2656
2663
  if (!eslint) {
2657
2664
  logWarning(
2658
- `ESLint not found in project. Install it in ${pc.dim(projectCwd)} to enable server-side linting.`
2665
+ `ESLint not found in project. Install it in ${pc.dim(
2666
+ projectCwd
2667
+ )} to enable server-side linting.`
2659
2668
  );
2660
2669
  onProgress("ESLint not available (install eslint in this project)");
2661
2670
  return [];
@@ -2664,11 +2673,19 @@ async function lintFileFast(filePath, onProgress) {
2664
2673
  onProgress("Running ESLint...");
2665
2674
  const results = await eslint.lintFiles([absolutePath]);
2666
2675
  const messages = Array.isArray(results) && results.length > 0 ? results[0].messages || [] : [];
2667
- const issues = processLintResults(absolutePath, projectCwd, messages, onProgress);
2676
+ const issues = processLintResults(
2677
+ absolutePath,
2678
+ projectCwd,
2679
+ messages,
2680
+ onProgress
2681
+ );
2668
2682
  fastCache.set(absolutePath, { issues, mtimeMs, timestamp: Date.now() });
2669
2683
  return issues;
2670
2684
  } catch (error) {
2671
- logServerError("ESLint fast pass failed", error instanceof Error ? error.message : String(error));
2685
+ logServerError(
2686
+ "ESLint fast pass failed",
2687
+ error instanceof Error ? error.message : String(error)
2688
+ );
2672
2689
  return [];
2673
2690
  }
2674
2691
  }
@@ -2704,7 +2721,12 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
2704
2721
  const styleguideHash = hashContentSync(styleguide);
2705
2722
  const projectRoot = findWorkspaceRoot4(fileDir) || fileDir;
2706
2723
  const relativeFilePath = relative2(projectRoot, absolutePath);
2707
- const cached = getCacheEntry(projectRoot, relativeFilePath, fileHash, styleguideHash);
2724
+ const cached = getCacheEntry(
2725
+ projectRoot,
2726
+ relativeFilePath,
2727
+ fileHash,
2728
+ styleguideHash
2729
+ );
2708
2730
  if (cached) {
2709
2731
  logSemanticSkipped(filePath, "cache fresh");
2710
2732
  return;
@@ -2723,7 +2745,11 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
2723
2745
  requestId,
2724
2746
  phase: "Running semantic analysis (async)..."
2725
2747
  });
2726
- startBackgroundTask("semantic-analysis", "Semantic Analysis", `Analyzing ${filePath}...`);
2748
+ startBackgroundTask(
2749
+ "semantic-analysis",
2750
+ "Semantic Analysis",
2751
+ `Analyzing ${filePath}...`
2752
+ );
2727
2753
  broadcast({
2728
2754
  type: "plugin:operation:start",
2729
2755
  pluginId: "semantic",
@@ -2737,8 +2763,18 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
2737
2763
  const ok = await client.isAvailable();
2738
2764
  if (!ok) {
2739
2765
  logServerWarning("Semantic analysis: Ollama not available");
2740
- updateBackgroundTaskProgress("semantic-analysis", 0, 0, 0, "Ollama not available");
2741
- completeBackgroundTask("semantic-analysis", void 0, "Ollama not available");
2766
+ updateBackgroundTaskProgress(
2767
+ "semantic-analysis",
2768
+ 0,
2769
+ 0,
2770
+ 0,
2771
+ "Ollama not available"
2772
+ );
2773
+ completeBackgroundTask(
2774
+ "semantic-analysis",
2775
+ void 0,
2776
+ "Ollama not available"
2777
+ );
2742
2778
  broadcast({
2743
2779
  type: "plugin:operation:error",
2744
2780
  pluginId: "semantic",
@@ -2748,7 +2784,13 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
2748
2784
  completeSemanticFile("error", "Ollama not available");
2749
2785
  return;
2750
2786
  }
2751
- updateBackgroundTaskProgress("semantic-analysis", 50, 0, 0, "Waiting for LLM response...");
2787
+ updateBackgroundTaskProgress(
2788
+ "semantic-analysis",
2789
+ 50,
2790
+ 0,
2791
+ 0,
2792
+ "Waiting for LLM response..."
2793
+ );
2752
2794
  updateSemanticBatchProgress(
2753
2795
  `Analyzing ${relative2(serverAppRootForVision, absolutePath)}...`
2754
2796
  );
@@ -2830,7 +2872,11 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
2830
2872
  async function runVisionAnalysisInBackground(ws, message) {
2831
2873
  const { route, timestamp, screenshot, screenshotFile, manifest, requestId } = message;
2832
2874
  setPluginStatus("vision", "processing", `Analyzing ${route}...`);
2833
- startBackgroundTask("vision-analysis", "Vision Analysis", `Analyzing ${route}...`);
2875
+ startBackgroundTask(
2876
+ "vision-analysis",
2877
+ "Vision Analysis",
2878
+ `Analyzing ${route}...`
2879
+ );
2834
2880
  broadcast({
2835
2881
  type: "plugin:operation:start",
2836
2882
  pluginId: "vision",
@@ -2847,7 +2893,11 @@ async function runVisionAnalysisInBackground(ws, message) {
2847
2893
  error: "uilint-vision is not installed",
2848
2894
  requestId
2849
2895
  });
2850
- completeBackgroundTask("vision-analysis", void 0, "uilint-vision not installed");
2896
+ completeBackgroundTask(
2897
+ "vision-analysis",
2898
+ void 0,
2899
+ "uilint-vision not installed"
2900
+ );
2851
2901
  setPluginStatus("vision", "error", "uilint-vision not installed");
2852
2902
  setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
2853
2903
  broadcast({
@@ -2866,7 +2916,13 @@ async function runVisionAnalysisInBackground(ws, message) {
2866
2916
  });
2867
2917
  const startedAt = Date.now();
2868
2918
  const analyzer = await getVisionAnalyzerInstance();
2869
- updateBackgroundTaskProgress("vision-analysis", 10, 0, 0, "Waiting for Ollama...");
2919
+ updateBackgroundTaskProgress(
2920
+ "vision-analysis",
2921
+ 10,
2922
+ 0,
2923
+ 0,
2924
+ "Waiting for Ollama..."
2925
+ );
2870
2926
  const releaseOllama = await acquireOllamaMutex("vision");
2871
2927
  try {
2872
2928
  const analyzerObj = analyzer;
@@ -2884,7 +2940,11 @@ async function runVisionAnalysisInBackground(ws, message) {
2884
2940
  error: "No screenshot provided for vision analysis",
2885
2941
  requestId
2886
2942
  });
2887
- completeBackgroundTask("vision-analysis", void 0, "No screenshot provided");
2943
+ completeBackgroundTask(
2944
+ "vision-analysis",
2945
+ void 0,
2946
+ "No screenshot provided"
2947
+ );
2888
2948
  setPluginStatus("vision", "error", "No screenshot provided");
2889
2949
  setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
2890
2950
  broadcast({
@@ -2895,7 +2955,13 @@ async function runVisionAnalysisInBackground(ws, message) {
2895
2955
  });
2896
2956
  return;
2897
2957
  }
2898
- updateBackgroundTaskProgress("vision-analysis", 30, 0, 0, "Running vision analysis...");
2958
+ updateBackgroundTaskProgress(
2959
+ "vision-analysis",
2960
+ 30,
2961
+ 0,
2962
+ 0,
2963
+ "Running vision analysis..."
2964
+ );
2899
2965
  broadcast({
2900
2966
  type: "plugin:operation:progress",
2901
2967
  pluginId: "vision",
@@ -2922,7 +2988,11 @@ async function runVisionAnalysisInBackground(ws, message) {
2922
2988
  });
2923
2989
  if (typeof screenshotFile === "string" && screenshotFile.length > 0) {
2924
2990
  if (isValidScreenshotFilename(screenshotFile)) {
2925
- const screenshotsDir = join3(serverAppRootForVision, ".uilint", "screenshots");
2991
+ const screenshotsDir = join3(
2992
+ serverAppRootForVision,
2993
+ ".uilint",
2994
+ "screenshots"
2995
+ );
2926
2996
  const imagePath = join3(screenshotsDir, screenshotFile);
2927
2997
  try {
2928
2998
  if (existsSync5(imagePath)) {
@@ -2962,7 +3032,9 @@ async function runVisionAnalysisInBackground(ws, message) {
2962
3032
  analysisTime: result.analysisTime,
2963
3033
  requestId
2964
3034
  });
2965
- const msg = `${resultIssues.length} issue(s) in ${(elapsed / 1e3).toFixed(1)}s`;
3035
+ const msg = `${resultIssues.length} issue(s) in ${(elapsed / 1e3).toFixed(
3036
+ 1
3037
+ )}s`;
2966
3038
  completeBackgroundTask("vision-analysis", msg);
2967
3039
  setPluginStatus("vision", "complete", msg);
2968
3040
  setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
@@ -3054,7 +3126,12 @@ async function handleMessage(ws, data) {
3054
3126
  const fastClientIssues = fastIssues.filter((i) => !isSentinelIssue(i));
3055
3127
  logLintDone(filePath, fastClientIssues.length, fastElapsed);
3056
3128
  updateCacheCount(fastCache.size + semanticCache.size);
3057
- sendMessage(ws, { type: "lint:result", filePath, requestId, issues: fastClientIssues });
3129
+ sendMessage(ws, {
3130
+ type: "lint:result",
3131
+ filePath,
3132
+ requestId,
3133
+ issues: fastClientIssues
3134
+ });
3058
3135
  sendMessage(ws, {
3059
3136
  type: "lint:progress",
3060
3137
  filePath,
@@ -3063,7 +3140,10 @@ async function handleMessage(ws, data) {
3063
3140
  });
3064
3141
  if (isSemanticRuleEnabled()) {
3065
3142
  runSemanticAnalysisAsync(filePath, ws, requestId).catch((err) => {
3066
- logServerError("Async semantic analysis failed", err instanceof Error ? err.message : String(err));
3143
+ logServerError(
3144
+ "Async semantic analysis failed",
3145
+ err instanceof Error ? err.message : String(err)
3146
+ );
3067
3147
  });
3068
3148
  } else {
3069
3149
  logSemanticSkipped(filePath, "rule not enabled");
@@ -3087,7 +3167,12 @@ async function handleMessage(ws, data) {
3087
3167
  logRuleInternalError(se.ruleId ?? "unknown", filePath, se.message);
3088
3168
  }
3089
3169
  const fastFiltered = fastIssues.filter((i) => !isSentinelIssue(i)).filter((issue) => issue.dataLoc === dataLoc);
3090
- sendMessage(ws, { type: "lint:result", filePath, requestId, issues: fastFiltered });
3170
+ sendMessage(ws, {
3171
+ type: "lint:result",
3172
+ filePath,
3173
+ requestId,
3174
+ issues: fastFiltered
3175
+ });
3091
3176
  {
3092
3177
  const elapsed = Date.now() - startedAt;
3093
3178
  sendMessage(ws, {
@@ -3099,7 +3184,10 @@ async function handleMessage(ws, data) {
3099
3184
  }
3100
3185
  if (isSemanticRuleEnabled()) {
3101
3186
  runSemanticAnalysisAsync(filePath, ws, requestId).catch((err) => {
3102
- logServerError("Async semantic analysis failed", err instanceof Error ? err.message : String(err));
3187
+ logServerError(
3188
+ "Async semantic analysis failed",
3189
+ err instanceof Error ? err.message : String(err)
3190
+ );
3103
3191
  });
3104
3192
  } else {
3105
3193
  logSemanticSkipped(filePath, "rule not enabled");
@@ -3140,7 +3228,10 @@ async function handleMessage(ws, data) {
3140
3228
  const visionMsg = message;
3141
3229
  logVisionAnalyze(visionMsg.route, visionMsg.requestId);
3142
3230
  runVisionAnalysisInBackground(ws, visionMsg).catch((err) => {
3143
- logServerError("Vision analysis failed", err instanceof Error ? err.message : String(err));
3231
+ logServerError(
3232
+ "Vision analysis failed",
3233
+ err instanceof Error ? err.message : String(err)
3234
+ );
3144
3235
  });
3145
3236
  break;
3146
3237
  }
@@ -3150,12 +3241,21 @@ async function handleMessage(ws, data) {
3150
3241
  try {
3151
3242
  const analyzer = await getVisionAnalyzerInstance();
3152
3243
  if (!analyzer) {
3153
- sendMessage(ws, { type: "vision:status", available: false, requestId });
3244
+ sendMessage(ws, {
3245
+ type: "vision:status",
3246
+ available: false,
3247
+ requestId
3248
+ });
3154
3249
  break;
3155
3250
  }
3156
3251
  const analyzerObj = analyzer;
3157
3252
  const model = typeof analyzerObj.getModel === "function" ? analyzerObj.getModel() : void 0;
3158
- sendMessage(ws, { type: "vision:status", available: true, model, requestId });
3253
+ sendMessage(ws, {
3254
+ type: "vision:status",
3255
+ available: true,
3256
+ model,
3257
+ requestId
3258
+ });
3159
3259
  } catch {
3160
3260
  sendMessage(ws, { type: "vision:status", available: false, requestId });
3161
3261
  }
@@ -3617,7 +3717,7 @@ async function serve(options) {
3617
3717
  if (useDashboardUI) {
3618
3718
  enableDashboard();
3619
3719
  registerPlugin("semantic", "Semantic", "qwen3-vl:8b-instruct");
3620
- registerPlugin("vision", "Vision", "gemma3:4b");
3720
+ registerPlugin("vision", "Vision", "qwen3-vl:8b-instruct");
3621
3721
  registerPlugin("duplicates", "Duplicates", "nomic-embed-text");
3622
3722
  } else {
3623
3723
  disableDashboard();
@@ -3651,43 +3751,45 @@ async function serve(options) {
3651
3751
  if (port !== preferredPort) {
3652
3752
  logServerInfo(`Port ${preferredPort} in use, using ${port}`);
3653
3753
  }
3654
- const httpServer = createServer((req, res) => {
3655
- if (req.method === "OPTIONS") {
3656
- res.writeHead(204, {
3657
- "Access-Control-Allow-Origin": "*",
3658
- "Access-Control-Allow-Methods": "GET, OPTIONS",
3659
- "Access-Control-Allow-Headers": "Content-Type"
3660
- });
3661
- res.end();
3662
- return;
3663
- }
3664
- const parsedUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
3665
- if (parsedUrl.pathname === "/_uilint/info") {
3666
- const probePath = parsedUrl.searchParams.get("probe");
3667
- let probeExists;
3668
- if (probePath) {
3669
- const absolute = join3(appRoot, probePath);
3670
- probeExists = existsSync5(absolute);
3754
+ const httpServer = createServer(
3755
+ (req, res) => {
3756
+ if (req.method === "OPTIONS") {
3757
+ res.writeHead(204, {
3758
+ "Access-Control-Allow-Origin": "*",
3759
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
3760
+ "Access-Control-Allow-Headers": "Content-Type"
3761
+ });
3762
+ res.end();
3763
+ return;
3671
3764
  }
3672
- res.writeHead(200, {
3673
- "Content-Type": "application/json",
3674
- "Access-Control-Allow-Origin": "*"
3675
- });
3676
- res.end(
3677
- JSON.stringify({
3678
- appRoot,
3679
- workspaceRoot: wsRoot,
3680
- serverCwd: cwd,
3681
- port,
3682
- pid: process.pid,
3683
- ...probeExists !== void 0 && { probeExists }
3684
- })
3685
- );
3686
- return;
3765
+ const parsedUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
3766
+ if (parsedUrl.pathname === "/_uilint/info") {
3767
+ const probePath = parsedUrl.searchParams.get("probe");
3768
+ let probeExists;
3769
+ if (probePath) {
3770
+ const absolute = join3(appRoot, probePath);
3771
+ probeExists = existsSync5(absolute);
3772
+ }
3773
+ res.writeHead(200, {
3774
+ "Content-Type": "application/json",
3775
+ "Access-Control-Allow-Origin": "*"
3776
+ });
3777
+ res.end(
3778
+ JSON.stringify({
3779
+ appRoot,
3780
+ workspaceRoot: wsRoot,
3781
+ serverCwd: cwd,
3782
+ port,
3783
+ pid: process.pid,
3784
+ ...probeExists !== void 0 && { probeExists }
3785
+ })
3786
+ );
3787
+ return;
3788
+ }
3789
+ res.writeHead(404);
3790
+ res.end();
3687
3791
  }
3688
- res.writeHead(404);
3689
- res.end();
3690
- });
3792
+ );
3691
3793
  const wss = new WebSocketServer({ noServer: true });
3692
3794
  httpServer.on("upgrade", (request, socket, head) => {
3693
3795
  wss.handleUpgrade(request, socket, head, (ws) => {
@@ -5929,7 +6031,7 @@ program.command("update").description("Update existing style guide with new styl
5929
6031
  llm: options.llm
5930
6032
  });
5931
6033
  });
5932
- var initCommand = program.command("init").description("Initialize UILint integration").option("--force", "Overwrite existing configuration files").option("--react", "Install React DevTool (non-interactive)").option("--eslint", "Install ESLint rules (non-interactive)").option("--genstyleguide", "Generate styleguide (non-interactive)").option("--skill", "Install Claude skill (non-interactive)");
6034
+ var initCommand = program.command("init").description("Initialize UILint integration").option("--force", "Overwrite existing configuration files").option("--react", "Install React DevTool (non-interactive)").option("--eslint", "Install ESLint rules (non-interactive)").option("--genstyleguide", "Generate styleguide (non-interactive)").option("--skill", "Install Claude skill (non-interactive)").option("--vision", "Install Vision Analysis plugin").option("--semantic", "Install Semantic Analysis plugin").option("--duplicates", "Install Duplicates Detection plugin");
5933
6035
  program.command("remove").description("Remove UILint components from your project").option("--dry-run", "Preview changes without removing anything").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
5934
6036
  const { removeUI } = await import("./remove-ui-GZRFA2AC.js");
5935
6037
  await removeUI({ dryRun: options.dryRun, yes: options.yes });
@@ -5940,7 +6042,9 @@ program.command("serve").description("Start WebSocket server for real-time UI li
5940
6042
  noDashboard: !options.dashboard
5941
6043
  });
5942
6044
  });
5943
- program.command("vision").description("Analyze a screenshot with Ollama vision models (requires a manifest)").option("--list", "List available .uilint/screenshots sidecars and exit").option(
6045
+ program.command("vision").description(
6046
+ "Analyze a screenshot with Ollama vision models (requires a manifest)"
6047
+ ).option("--list", "List available .uilint/screenshots sidecars and exit").option(
5944
6048
  "--screenshots-dir <path>",
5945
6049
  "Screenshots directory for --list (default: nearest .uilint/screenshots)"
5946
6050
  ).option("--image <path>", "Path to a screenshot image (png/jpg)").option(
@@ -5949,7 +6053,10 @@ program.command("vision").description("Analyze a screenshot with Ollama vision m
5949
6053
  ).option("--manifest-file <path>", "Path to a manifest JSON file (array)").option("--manifest-json <json>", "Inline manifest JSON (array)").option("--route <route>", "Optional route label (e.g., /todos)").option(
5950
6054
  "-s, --styleguide <path>",
5951
6055
  "Path to style guide file OR project directory (falls back to upward search)"
5952
- ).option("-o, --output <format>", "Output format: text or json", "text").option("--model <name>", "Ollama vision model override", void 0).option("--base-url <url>", "Ollama base URL (default: http://localhost:11434)").option("--stream", "Stream model output/progress to stderr (text mode only)").option("--debug", "Enable debug logging (stderr)").option(
6056
+ ).option("-o, --output <format>", "Output format: text or json", "text").option("--model <name>", "Ollama vision model override", void 0).option(
6057
+ "--base-url <url>",
6058
+ "Ollama base URL (default: http://localhost:11434)"
6059
+ ).option("--stream", "Stream model output/progress to stderr (text mode only)").option("--debug", "Enable debug logging (stderr)").option(
5953
6060
  "--debug-full",
5954
6061
  "Print full prompt/styleguide and include base64 in dumps (can be very large)"
5955
6062
  ).option(
@@ -5994,11 +6101,18 @@ program.command("upgrade").description("Update installed ESLint rules to latest
5994
6101
  async function main() {
5995
6102
  const { discoverPlugins: discoverPlugins2 } = await import("./plugin-loader-LUIV7MLR.js");
5996
6103
  const pluginManifests = await discoverPlugins2();
6104
+ const KNOWN_PLUGIN_FLAGS = ["vision", "semantic", "duplicates"];
5997
6105
  for (const manifest of pluginManifests) {
5998
- initCommand.option(`--${manifest.cliFlag}`, manifest.cliDescription);
6106
+ if (!KNOWN_PLUGIN_FLAGS.includes(manifest.cliFlag)) {
6107
+ initCommand.option(`--${manifest.cliFlag}`, manifest.cliDescription);
6108
+ }
5999
6109
  }
6000
6110
  initCommand.action(async (options) => {
6001
- const plugins = pluginManifests.filter((m) => options[m.cliFlag]).map((m) => m.cliFlag);
6111
+ const allFlags = /* @__PURE__ */ new Set([
6112
+ ...KNOWN_PLUGIN_FLAGS,
6113
+ ...pluginManifests.map((m) => m.cliFlag)
6114
+ ]);
6115
+ const plugins = [...allFlags].filter((flag) => options[flag]);
6002
6116
  const { initUI } = await import("./init-ui-QQXIZSKI.js");
6003
6117
  await initUI({
6004
6118
  force: options.force,