uilint 0.2.145 → 0.2.147
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/{chunk-ZUOFUPGT.js → chunk-5EDE3J6O.js} +2 -1
- package/dist/chunk-5QUW7BNW.js +80 -0
- package/dist/chunk-5QUW7BNW.js.map +1 -0
- package/dist/{chunk-ZOLQHFVQ.js → chunk-JEYSQ5KF.js} +20 -1
- package/dist/chunk-JEYSQ5KF.js.map +1 -0
- package/dist/index.js +475 -248
- package/dist/index.js.map +1 -1
- package/dist/{init-ui-KJYYI5DH.js → init-ui-QQXIZSKI.js} +123 -22
- package/dist/init-ui-QQXIZSKI.js.map +1 -0
- package/dist/{plugin-loader-O6PNFN6D.js → plugin-loader-LUIV7MLR.js} +2 -2
- package/dist/{remove-ui-ZHW4GUFL.js → remove-ui-GZRFA2AC.js} +2 -2
- package/dist/{render-43OMCORR.js → render-2P4YWHXV.js} +112 -97
- package/dist/render-2P4YWHXV.js.map +1 -0
- package/dist/{upgrade-TPZ62BT2.js → upgrade-2UKW3SIQ.js} +2 -2
- package/package.json +8 -8
- package/dist/chunk-WG2WZTB2.js +0 -56
- package/dist/chunk-WG2WZTB2.js.map +0 -1
- package/dist/chunk-ZOLQHFVQ.js.map +0 -1
- package/dist/init-ui-KJYYI5DH.js.map +0 -1
- package/dist/render-43OMCORR.js.map +0 -1
- /package/dist/{chunk-ZUOFUPGT.js.map → chunk-5EDE3J6O.js.map} +0 -0
- /package/dist/{plugin-loader-O6PNFN6D.js.map → plugin-loader-LUIV7MLR.js.map} +0 -0
- /package/dist/{remove-ui-ZHW4GUFL.js.map → remove-ui-GZRFA2AC.js.map} +0 -0
- /package/dist/{upgrade-TPZ62BT2.js.map → upgrade-2UKW3SIQ.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getDashboardStore
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JEYSQ5KF.js";
|
|
5
5
|
import {
|
|
6
6
|
detectCoverageSetup,
|
|
7
7
|
detectNextAppRouter,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
discoverPlugins,
|
|
18
18
|
loadPluginESLintRules
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-5QUW7BNW.js";
|
|
20
20
|
import {
|
|
21
21
|
createSpinner,
|
|
22
22
|
intro,
|
|
@@ -1508,6 +1508,19 @@ function logVisionDone(route, issueCount, elapsedMs) {
|
|
|
1508
1508
|
function logVisionCheck(requestId) {
|
|
1509
1509
|
logActivity("vision:check", requestId ? `(req ${requestId})` : "");
|
|
1510
1510
|
}
|
|
1511
|
+
function logSemanticAnalyze(filePath, requestId) {
|
|
1512
|
+
const msg = filePath + (requestId ? ` (req ${requestId})` : "");
|
|
1513
|
+
logActivity("semantic:analyze", msg);
|
|
1514
|
+
}
|
|
1515
|
+
function logSemanticDone(filePath, issueCount, elapsedMs) {
|
|
1516
|
+
logActivity(
|
|
1517
|
+
"semantic:done",
|
|
1518
|
+
`${filePath} \u2192 ${issueCount} issue(s) (${elapsedMs}ms)`
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
function logSemanticSkipped(filePath, reason) {
|
|
1522
|
+
logActivity("semantic:skip", `${filePath} \u2014 ${reason}`);
|
|
1523
|
+
}
|
|
1511
1524
|
function logConfigSet(key, value) {
|
|
1512
1525
|
logActivity("config:set", `${key} = ${JSON.stringify(value)}`);
|
|
1513
1526
|
}
|
|
@@ -1635,6 +1648,40 @@ function completeBackgroundTask(id, successMessage, error) {
|
|
|
1635
1648
|
function logRuleInternalError(ruleId, filePath, detail) {
|
|
1636
1649
|
logActivity("error", `Rule error [${ruleId}] ${filePath}`, detail, true);
|
|
1637
1650
|
}
|
|
1651
|
+
function registerPlugin(id, name, model) {
|
|
1652
|
+
if (useDashboard) {
|
|
1653
|
+
const store = getDashboardStore();
|
|
1654
|
+
store.registerPlugin(id, name, model);
|
|
1655
|
+
} else {
|
|
1656
|
+
consoleInfo(`Plugin registered: ${name} (${model})`);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
function setPluginStatus(id, status, message) {
|
|
1660
|
+
if (useDashboard) {
|
|
1661
|
+
const store = getDashboardStore();
|
|
1662
|
+
store.setPluginState(id, {
|
|
1663
|
+
status,
|
|
1664
|
+
message,
|
|
1665
|
+
error: status === "error" ? message : void 0,
|
|
1666
|
+
// Clear progress on terminal states
|
|
1667
|
+
...status === "idle" || status === "complete" || status === "error" ? { progress: void 0, current: void 0, total: void 0 } : {}
|
|
1668
|
+
});
|
|
1669
|
+
} else if (status === "error") {
|
|
1670
|
+
consoleError(`Plugin ${id}: ${message}`);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
function updatePluginProgress(id, progress, current, total, message) {
|
|
1674
|
+
if (useDashboard) {
|
|
1675
|
+
const store = getDashboardStore();
|
|
1676
|
+
store.setPluginState(id, { progress, current, total, message });
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function setPluginModel(id, model) {
|
|
1680
|
+
if (useDashboard) {
|
|
1681
|
+
const store = getDashboardStore();
|
|
1682
|
+
store.setPluginState(id, { model });
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1638
1685
|
|
|
1639
1686
|
// src/commands/serve.ts
|
|
1640
1687
|
import { ruleRegistry } from "uilint-eslint";
|
|
@@ -2302,13 +2349,58 @@ async function getVisionAnalyzerInstance() {
|
|
|
2302
2349
|
return visionAnalyzer;
|
|
2303
2350
|
}
|
|
2304
2351
|
var ollamaMutexPromise = Promise.resolve();
|
|
2305
|
-
|
|
2352
|
+
var ollamaMutexHolder = null;
|
|
2353
|
+
var ollamaMutexQueue = [];
|
|
2354
|
+
function acquireOllamaMutex(pluginId) {
|
|
2306
2355
|
let release;
|
|
2307
2356
|
const prev = ollamaMutexPromise;
|
|
2308
2357
|
ollamaMutexPromise = new Promise((resolve11) => {
|
|
2309
2358
|
release = resolve11;
|
|
2310
2359
|
});
|
|
2311
|
-
|
|
2360
|
+
ollamaMutexQueue.push(pluginId);
|
|
2361
|
+
setPluginStatus(pluginId, "waiting-for-ollama", "Queued for Ollama...");
|
|
2362
|
+
return prev.then(() => {
|
|
2363
|
+
const idx = ollamaMutexQueue.indexOf(pluginId);
|
|
2364
|
+
if (idx !== -1) ollamaMutexQueue.splice(idx, 1);
|
|
2365
|
+
ollamaMutexHolder = pluginId;
|
|
2366
|
+
setPluginStatus(pluginId, "using-ollama", "Using Ollama...");
|
|
2367
|
+
return () => {
|
|
2368
|
+
if (ollamaMutexHolder === pluginId) {
|
|
2369
|
+
ollamaMutexHolder = null;
|
|
2370
|
+
}
|
|
2371
|
+
release();
|
|
2372
|
+
};
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2375
|
+
var semanticFilesRequested = 0;
|
|
2376
|
+
var semanticFilesCompleted = 0;
|
|
2377
|
+
var semanticIdleResetTimer = null;
|
|
2378
|
+
function updateSemanticBatchProgress(message) {
|
|
2379
|
+
const progress = semanticFilesRequested > 0 ? Math.round(semanticFilesCompleted / semanticFilesRequested * 100) : 0;
|
|
2380
|
+
updatePluginProgress(
|
|
2381
|
+
"semantic",
|
|
2382
|
+
progress,
|
|
2383
|
+
semanticFilesCompleted,
|
|
2384
|
+
semanticFilesRequested,
|
|
2385
|
+
message
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
function completeSemanticFile(terminalStatus, message) {
|
|
2389
|
+
semanticFilesCompleted++;
|
|
2390
|
+
if (semanticFilesCompleted >= semanticFilesRequested) {
|
|
2391
|
+
const summary = terminalStatus === "complete" ? `Done: ${semanticFilesCompleted} file(s) analyzed` : message;
|
|
2392
|
+
setPluginStatus("semantic", terminalStatus, summary);
|
|
2393
|
+
semanticIdleResetTimer = setTimeout(() => {
|
|
2394
|
+
setPluginStatus("semantic", "idle");
|
|
2395
|
+
semanticFilesRequested = 0;
|
|
2396
|
+
semanticFilesCompleted = 0;
|
|
2397
|
+
semanticIdleResetTimer = null;
|
|
2398
|
+
}, 3e3);
|
|
2399
|
+
} else {
|
|
2400
|
+
updateSemanticBatchProgress(
|
|
2401
|
+
`${semanticFilesCompleted}/${semanticFilesRequested} files analyzed`
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2312
2404
|
}
|
|
2313
2405
|
var serverAppRootForVision = process.cwd();
|
|
2314
2406
|
function isValidScreenshotFilename(filename) {
|
|
@@ -2454,23 +2546,6 @@ function resolveRequestedFilePath(filePath) {
|
|
|
2454
2546
|
resolvedPathCache.set(filePath, fromCwd);
|
|
2455
2547
|
return fromCwd;
|
|
2456
2548
|
}
|
|
2457
|
-
async function getESLintForProject2(projectCwd) {
|
|
2458
|
-
const cached = eslintInstances2.get(projectCwd);
|
|
2459
|
-
if (cached) return cached;
|
|
2460
|
-
try {
|
|
2461
|
-
const req = createRequire3(join3(projectCwd, "package.json"));
|
|
2462
|
-
const mod = req("eslint");
|
|
2463
|
-
const modDefault = mod?.default;
|
|
2464
|
-
const ESLintCtor = mod?.ESLint ?? modDefault?.ESLint ?? mod?.default ?? mod;
|
|
2465
|
-
if (!ESLintCtor) return null;
|
|
2466
|
-
const Ctor = ESLintCtor;
|
|
2467
|
-
const eslint = new Ctor({ cwd: projectCwd });
|
|
2468
|
-
eslintInstances2.set(projectCwd, eslint);
|
|
2469
|
-
return eslint;
|
|
2470
|
-
} catch {
|
|
2471
|
-
return null;
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
2549
|
async function getESLintWithOverrides(projectCwd, overrideRules, instanceCache) {
|
|
2475
2550
|
const cacheKey = `${projectCwd}::${JSON.stringify(overrideRules)}`;
|
|
2476
2551
|
const cached = instanceCache.get(cacheKey);
|
|
@@ -2597,39 +2672,329 @@ async function lintFileFast(filePath, onProgress) {
|
|
|
2597
2672
|
return [];
|
|
2598
2673
|
}
|
|
2599
2674
|
}
|
|
2600
|
-
async function
|
|
2675
|
+
async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
2676
|
+
const startTime = Date.now();
|
|
2677
|
+
logSemanticAnalyze(filePath, requestId);
|
|
2601
2678
|
const absolutePath = resolveRequestedFilePath(filePath);
|
|
2602
|
-
if (!existsSync5(absolutePath))
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
return statSync2(absolutePath).mtimeMs;
|
|
2606
|
-
} catch {
|
|
2607
|
-
return 0;
|
|
2608
|
-
}
|
|
2609
|
-
})();
|
|
2610
|
-
const cached = semanticCache.get(absolutePath);
|
|
2611
|
-
if (cached && cached.mtimeMs === mtimeMs) {
|
|
2612
|
-
onProgress("Semantic cache hit (unchanged)");
|
|
2613
|
-
return cached.issues;
|
|
2679
|
+
if (!existsSync5(absolutePath)) {
|
|
2680
|
+
logSemanticSkipped(filePath, "file not found");
|
|
2681
|
+
return;
|
|
2614
2682
|
}
|
|
2683
|
+
const eslintConfigPath = findEslintConfigFile(serverAppRootForVision);
|
|
2684
|
+
const ruleConfigs = eslintConfigPath ? readRuleConfigsFromConfig(eslintConfigPath) : /* @__PURE__ */ new Map();
|
|
2685
|
+
const semanticConfig = ruleConfigs.get("semantic");
|
|
2686
|
+
const model = semanticConfig?.options?.model || "qwen3-vl:8b-instruct";
|
|
2687
|
+
const styleguidePath = semanticConfig?.options?.styleguidePath || void 0;
|
|
2688
|
+
setPluginModel("semantic", model);
|
|
2689
|
+
const { getStyleguide, hashContentSync, setCacheEntry, getCacheEntry } = await import("uilint-eslint");
|
|
2615
2690
|
const fileDir = dirname5(absolutePath);
|
|
2616
|
-
const
|
|
2617
|
-
|
|
2618
|
-
|
|
2691
|
+
const { content: styleguide } = getStyleguide(fileDir, styleguidePath);
|
|
2692
|
+
if (!styleguide) {
|
|
2693
|
+
logSemanticSkipped(filePath, "no styleguide found");
|
|
2694
|
+
return;
|
|
2695
|
+
}
|
|
2696
|
+
let fileContent;
|
|
2619
2697
|
try {
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2698
|
+
fileContent = readFileSync2(absolutePath, "utf-8");
|
|
2699
|
+
} catch {
|
|
2700
|
+
logSemanticSkipped(filePath, "file read error");
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
const fileHash = hashContentSync(fileContent);
|
|
2704
|
+
const styleguideHash = hashContentSync(styleguide);
|
|
2705
|
+
const projectRoot = findWorkspaceRoot4(fileDir) || fileDir;
|
|
2706
|
+
const relativeFilePath = relative2(projectRoot, absolutePath);
|
|
2707
|
+
const cached = getCacheEntry(projectRoot, relativeFilePath, fileHash, styleguideHash);
|
|
2708
|
+
if (cached) {
|
|
2709
|
+
logSemanticSkipped(filePath, "cache fresh");
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
if (semanticIdleResetTimer) {
|
|
2713
|
+
clearTimeout(semanticIdleResetTimer);
|
|
2714
|
+
semanticIdleResetTimer = null;
|
|
2715
|
+
}
|
|
2716
|
+
semanticFilesRequested++;
|
|
2717
|
+
updateSemanticBatchProgress(
|
|
2718
|
+
`Queued ${relative2(serverAppRootForVision, absolutePath)}...`
|
|
2719
|
+
);
|
|
2720
|
+
sendMessage(ws, {
|
|
2721
|
+
type: "lint:progress",
|
|
2722
|
+
filePath,
|
|
2723
|
+
requestId,
|
|
2724
|
+
phase: "Running semantic analysis (async)..."
|
|
2725
|
+
});
|
|
2726
|
+
startBackgroundTask("semantic-analysis", "Semantic Analysis", `Analyzing ${filePath}...`);
|
|
2727
|
+
broadcast({
|
|
2728
|
+
type: "plugin:operation:start",
|
|
2729
|
+
pluginId: "semantic",
|
|
2730
|
+
operationName: "analysis",
|
|
2731
|
+
message: `Analyzing ${relative2(serverAppRootForVision, absolutePath)}...`
|
|
2732
|
+
});
|
|
2733
|
+
const release = await acquireOllamaMutex("semantic");
|
|
2734
|
+
try {
|
|
2735
|
+
const { OllamaClient: OllamaClient2, buildSourceScanPrompt: buildSourceScanPrompt2 } = await import("uilint-core/node");
|
|
2736
|
+
const client = new OllamaClient2({ model });
|
|
2737
|
+
const ok = await client.isAvailable();
|
|
2738
|
+
if (!ok) {
|
|
2739
|
+
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");
|
|
2742
|
+
broadcast({
|
|
2743
|
+
type: "plugin:operation:error",
|
|
2744
|
+
pluginId: "semantic",
|
|
2745
|
+
operationName: "analysis",
|
|
2746
|
+
error: "Ollama not available"
|
|
2747
|
+
});
|
|
2748
|
+
completeSemanticFile("error", "Ollama not available");
|
|
2749
|
+
return;
|
|
2750
|
+
}
|
|
2751
|
+
updateBackgroundTaskProgress("semantic-analysis", 50, 0, 0, "Waiting for LLM response...");
|
|
2752
|
+
updateSemanticBatchProgress(
|
|
2753
|
+
`Analyzing ${relative2(serverAppRootForVision, absolutePath)}...`
|
|
2625
2754
|
);
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2755
|
+
broadcast({
|
|
2756
|
+
type: "plugin:operation:progress",
|
|
2757
|
+
pluginId: "semantic",
|
|
2758
|
+
operationName: "analysis",
|
|
2759
|
+
message: "Waiting for LLM response..."
|
|
2760
|
+
});
|
|
2761
|
+
const prompt = buildSourceScanPrompt2(fileContent, styleguide, {
|
|
2762
|
+
filePath: relative2(serverAppRootForVision, absolutePath)
|
|
2763
|
+
});
|
|
2764
|
+
const responseText = await client.complete(prompt, { json: true });
|
|
2765
|
+
const parsed = JSON.parse(responseText);
|
|
2766
|
+
const issues = (parsed.issues || []).map((issue) => ({
|
|
2767
|
+
line: issue.line || 1,
|
|
2768
|
+
column: issue.column,
|
|
2769
|
+
message: issue.message || "Semantic issue detected",
|
|
2770
|
+
ruleId: "uilint/semantic",
|
|
2771
|
+
severity: 1
|
|
2772
|
+
}));
|
|
2773
|
+
setCacheEntry(projectRoot, relativeFilePath, {
|
|
2774
|
+
fileHash,
|
|
2775
|
+
styleguideHash,
|
|
2776
|
+
issues,
|
|
2777
|
+
timestamp: Date.now()
|
|
2778
|
+
});
|
|
2779
|
+
const fileDir2 = dirname5(absolutePath);
|
|
2780
|
+
const projectCwd = findESLintCwd2(fileDir2);
|
|
2781
|
+
const dataLocFile = normalizeDataLocFilePath2(absolutePath, projectCwd);
|
|
2782
|
+
const lintIssues = issues.map((issue) => ({
|
|
2783
|
+
ruleId: "uilint/semantic",
|
|
2784
|
+
severity: issue.severity,
|
|
2785
|
+
message: issue.message,
|
|
2786
|
+
line: issue.line,
|
|
2787
|
+
column: issue.column || 0,
|
|
2788
|
+
nodeType: null,
|
|
2789
|
+
source: null,
|
|
2790
|
+
dataLoc: `${dataLocFile}:${issue.line}:${issue.column || 0}`
|
|
2791
|
+
}));
|
|
2792
|
+
sendMessage(ws, {
|
|
2793
|
+
type: "lint:result",
|
|
2794
|
+
filePath,
|
|
2795
|
+
requestId,
|
|
2796
|
+
issues: lintIssues
|
|
2797
|
+
});
|
|
2798
|
+
const elapsed = Date.now() - startTime;
|
|
2799
|
+
const msg = `${issues.length} issue(s) found`;
|
|
2800
|
+
logSemanticDone(filePath, issues.length, elapsed);
|
|
2801
|
+
completeBackgroundTask("semantic-analysis", msg);
|
|
2802
|
+
completeSemanticFile("complete", msg);
|
|
2803
|
+
broadcast({
|
|
2804
|
+
type: "plugin:operation:complete",
|
|
2805
|
+
pluginId: "semantic",
|
|
2806
|
+
operationName: "analysis",
|
|
2807
|
+
message: msg
|
|
2808
|
+
});
|
|
2809
|
+
sendMessage(ws, {
|
|
2810
|
+
type: "lint:progress",
|
|
2811
|
+
filePath,
|
|
2812
|
+
requestId,
|
|
2813
|
+
phase: `Done (semantic: ${issues.length} issues)`
|
|
2814
|
+
});
|
|
2630
2815
|
} catch (error) {
|
|
2631
|
-
|
|
2632
|
-
|
|
2816
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2817
|
+
logServerError("Async semantic analysis failed", errorMessage);
|
|
2818
|
+
completeBackgroundTask("semantic-analysis", void 0, errorMessage);
|
|
2819
|
+
completeSemanticFile("error", errorMessage);
|
|
2820
|
+
broadcast({
|
|
2821
|
+
type: "plugin:operation:error",
|
|
2822
|
+
pluginId: "semantic",
|
|
2823
|
+
operationName: "analysis",
|
|
2824
|
+
error: errorMessage
|
|
2825
|
+
});
|
|
2826
|
+
} finally {
|
|
2827
|
+
release();
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
async function runVisionAnalysisInBackground(ws, message) {
|
|
2831
|
+
const { route, timestamp, screenshot, screenshotFile, manifest, requestId } = message;
|
|
2832
|
+
setPluginStatus("vision", "processing", `Analyzing ${route}...`);
|
|
2833
|
+
startBackgroundTask("vision-analysis", "Vision Analysis", `Analyzing ${route}...`);
|
|
2834
|
+
broadcast({
|
|
2835
|
+
type: "plugin:operation:start",
|
|
2836
|
+
pluginId: "vision",
|
|
2837
|
+
operationName: "analysis",
|
|
2838
|
+
message: `Analyzing ${route}...`
|
|
2839
|
+
});
|
|
2840
|
+
const visionMod = await getVisionModule();
|
|
2841
|
+
if (!visionMod) {
|
|
2842
|
+
sendMessage(ws, {
|
|
2843
|
+
type: "vision:result",
|
|
2844
|
+
route,
|
|
2845
|
+
issues: [],
|
|
2846
|
+
analysisTime: 0,
|
|
2847
|
+
error: "uilint-vision is not installed",
|
|
2848
|
+
requestId
|
|
2849
|
+
});
|
|
2850
|
+
completeBackgroundTask("vision-analysis", void 0, "uilint-vision not installed");
|
|
2851
|
+
setPluginStatus("vision", "error", "uilint-vision not installed");
|
|
2852
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2853
|
+
broadcast({
|
|
2854
|
+
type: "plugin:operation:error",
|
|
2855
|
+
pluginId: "vision",
|
|
2856
|
+
operationName: "analysis",
|
|
2857
|
+
error: "uilint-vision not installed"
|
|
2858
|
+
});
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
sendMessage(ws, {
|
|
2862
|
+
type: "vision:progress",
|
|
2863
|
+
route,
|
|
2864
|
+
requestId,
|
|
2865
|
+
phase: "Starting vision analysis..."
|
|
2866
|
+
});
|
|
2867
|
+
const startedAt = Date.now();
|
|
2868
|
+
const analyzer = await getVisionAnalyzerInstance();
|
|
2869
|
+
updateBackgroundTaskProgress("vision-analysis", 10, 0, 0, "Waiting for Ollama...");
|
|
2870
|
+
const releaseOllama = await acquireOllamaMutex("vision");
|
|
2871
|
+
try {
|
|
2872
|
+
const analyzerObj = analyzer;
|
|
2873
|
+
const analyzerModel = typeof analyzerObj?.getModel === "function" ? analyzerObj.getModel() : void 0;
|
|
2874
|
+
const analyzerBaseUrl = typeof analyzerObj?.getBaseUrl === "function" ? analyzerObj.getBaseUrl() : void 0;
|
|
2875
|
+
if (analyzerModel) {
|
|
2876
|
+
setPluginModel("vision", analyzerModel);
|
|
2877
|
+
}
|
|
2878
|
+
if (!screenshot) {
|
|
2879
|
+
sendMessage(ws, {
|
|
2880
|
+
type: "vision:result",
|
|
2881
|
+
route,
|
|
2882
|
+
issues: [],
|
|
2883
|
+
analysisTime: Date.now() - startedAt,
|
|
2884
|
+
error: "No screenshot provided for vision analysis",
|
|
2885
|
+
requestId
|
|
2886
|
+
});
|
|
2887
|
+
completeBackgroundTask("vision-analysis", void 0, "No screenshot provided");
|
|
2888
|
+
setPluginStatus("vision", "error", "No screenshot provided");
|
|
2889
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2890
|
+
broadcast({
|
|
2891
|
+
type: "plugin:operation:error",
|
|
2892
|
+
pluginId: "vision",
|
|
2893
|
+
operationName: "analysis",
|
|
2894
|
+
error: "No screenshot provided"
|
|
2895
|
+
});
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
updateBackgroundTaskProgress("vision-analysis", 30, 0, 0, "Running vision analysis...");
|
|
2899
|
+
broadcast({
|
|
2900
|
+
type: "plugin:operation:progress",
|
|
2901
|
+
pluginId: "vision",
|
|
2902
|
+
operationName: "analysis",
|
|
2903
|
+
message: "Running vision analysis..."
|
|
2904
|
+
});
|
|
2905
|
+
const result = await visionMod.runVisionAnalysis({
|
|
2906
|
+
imageBase64: screenshot,
|
|
2907
|
+
manifest,
|
|
2908
|
+
projectPath: serverAppRootForVision,
|
|
2909
|
+
baseUrl: analyzerBaseUrl,
|
|
2910
|
+
model: analyzerModel,
|
|
2911
|
+
analyzer,
|
|
2912
|
+
onPhase: (phase) => {
|
|
2913
|
+
sendMessage(ws, {
|
|
2914
|
+
type: "vision:progress",
|
|
2915
|
+
route,
|
|
2916
|
+
requestId,
|
|
2917
|
+
phase
|
|
2918
|
+
});
|
|
2919
|
+
updateBackgroundTaskProgress("vision-analysis", 50, 0, 0, phase);
|
|
2920
|
+
},
|
|
2921
|
+
pathResolver: resolvePathSpecifier
|
|
2922
|
+
});
|
|
2923
|
+
if (typeof screenshotFile === "string" && screenshotFile.length > 0) {
|
|
2924
|
+
if (isValidScreenshotFilename(screenshotFile)) {
|
|
2925
|
+
const screenshotsDir = join3(serverAppRootForVision, ".uilint", "screenshots");
|
|
2926
|
+
const imagePath = join3(screenshotsDir, screenshotFile);
|
|
2927
|
+
try {
|
|
2928
|
+
if (existsSync5(imagePath)) {
|
|
2929
|
+
const report = visionMod.writeVisionMarkdownReport({
|
|
2930
|
+
imagePath,
|
|
2931
|
+
route,
|
|
2932
|
+
timestamp,
|
|
2933
|
+
visionModel: result.visionModel,
|
|
2934
|
+
baseUrl: result.baseUrl,
|
|
2935
|
+
analysisTimeMs: result.analysisTime,
|
|
2936
|
+
prompt: result.prompt ?? null,
|
|
2937
|
+
rawResponse: result.rawResponse ?? null,
|
|
2938
|
+
metadata: {
|
|
2939
|
+
screenshotFile: parse(imagePath).base,
|
|
2940
|
+
appRoot: serverAppRootForVision,
|
|
2941
|
+
manifestElements: manifest.length,
|
|
2942
|
+
requestId: requestId ?? null
|
|
2943
|
+
}
|
|
2944
|
+
});
|
|
2945
|
+
logServerInfo(`Wrote vision report`, report.outPath);
|
|
2946
|
+
}
|
|
2947
|
+
} catch (e) {
|
|
2948
|
+
logServerWarning(
|
|
2949
|
+
`Failed to write vision report for ${screenshotFile}`,
|
|
2950
|
+
e instanceof Error ? e.message : String(e)
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
const elapsed = Date.now() - startedAt;
|
|
2956
|
+
const resultIssues = result.issues;
|
|
2957
|
+
logVisionDone(route, resultIssues.length, elapsed);
|
|
2958
|
+
sendMessage(ws, {
|
|
2959
|
+
type: "vision:result",
|
|
2960
|
+
route,
|
|
2961
|
+
issues: resultIssues,
|
|
2962
|
+
analysisTime: result.analysisTime,
|
|
2963
|
+
requestId
|
|
2964
|
+
});
|
|
2965
|
+
const msg = `${resultIssues.length} issue(s) in ${(elapsed / 1e3).toFixed(1)}s`;
|
|
2966
|
+
completeBackgroundTask("vision-analysis", msg);
|
|
2967
|
+
setPluginStatus("vision", "complete", msg);
|
|
2968
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2969
|
+
broadcast({
|
|
2970
|
+
type: "plugin:operation:complete",
|
|
2971
|
+
pluginId: "vision",
|
|
2972
|
+
operationName: "analysis",
|
|
2973
|
+
message: msg
|
|
2974
|
+
});
|
|
2975
|
+
} catch (error) {
|
|
2976
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2977
|
+
const elapsed = Date.now() - startedAt;
|
|
2978
|
+
logServerError(`Vision analysis failed for ${route}`, errorMessage);
|
|
2979
|
+
sendMessage(ws, {
|
|
2980
|
+
type: "vision:result",
|
|
2981
|
+
route,
|
|
2982
|
+
issues: [],
|
|
2983
|
+
analysisTime: elapsed,
|
|
2984
|
+
error: errorMessage,
|
|
2985
|
+
requestId
|
|
2986
|
+
});
|
|
2987
|
+
completeBackgroundTask("vision-analysis", void 0, errorMessage);
|
|
2988
|
+
setPluginStatus("vision", "error", errorMessage);
|
|
2989
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2990
|
+
broadcast({
|
|
2991
|
+
type: "plugin:operation:error",
|
|
2992
|
+
pluginId: "vision",
|
|
2993
|
+
operationName: "analysis",
|
|
2994
|
+
error: errorMessage
|
|
2995
|
+
});
|
|
2996
|
+
} finally {
|
|
2997
|
+
releaseOllama();
|
|
2633
2998
|
}
|
|
2634
2999
|
}
|
|
2635
3000
|
function sendMessage(ws, message) {
|
|
@@ -2690,52 +3055,18 @@ async function handleMessage(ws, data) {
|
|
|
2690
3055
|
logLintDone(filePath, fastClientIssues.length, fastElapsed);
|
|
2691
3056
|
updateCacheCount(fastCache.size + semanticCache.size);
|
|
2692
3057
|
sendMessage(ws, { type: "lint:result", filePath, requestId, issues: fastClientIssues });
|
|
3058
|
+
sendMessage(ws, {
|
|
3059
|
+
type: "lint:progress",
|
|
3060
|
+
filePath,
|
|
3061
|
+
requestId,
|
|
3062
|
+
phase: `Done (${fastClientIssues.length} issues, ${fastElapsed}ms)`
|
|
3063
|
+
});
|
|
2693
3064
|
if (isSemanticRuleEnabled()) {
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
filePath,
|
|
2697
|
-
requestId,
|
|
2698
|
-
phase: `Fast lint done (${fastClientIssues.length} issues, ${fastElapsed}ms). Running semantic...`
|
|
2699
|
-
});
|
|
2700
|
-
setImmediate(async () => {
|
|
2701
|
-
try {
|
|
2702
|
-
const _semanticStart = Date.now();
|
|
2703
|
-
const semanticIssues = await lintFileSemantic(filePath, (phase) => {
|
|
2704
|
-
sendMessage(ws, { type: "lint:progress", filePath, requestId, phase });
|
|
2705
|
-
});
|
|
2706
|
-
const semanticSentinels = semanticIssues.filter(isSentinelIssue);
|
|
2707
|
-
for (const se of semanticSentinels) {
|
|
2708
|
-
logRuleInternalError(se.ruleId ?? "unknown", filePath, se.message);
|
|
2709
|
-
}
|
|
2710
|
-
const semanticClientIssues = semanticIssues.filter((i) => !isSentinelIssue(i));
|
|
2711
|
-
const totalElapsed = Date.now() - startedAt;
|
|
2712
|
-
const totalIssues = fastClientIssues.length + semanticClientIssues.length;
|
|
2713
|
-
if (semanticClientIssues.length > 0) {
|
|
2714
|
-
sendMessage(ws, { type: "lint:result", filePath, requestId, issues: semanticClientIssues });
|
|
2715
|
-
}
|
|
2716
|
-
sendMessage(ws, {
|
|
2717
|
-
type: "lint:progress",
|
|
2718
|
-
filePath,
|
|
2719
|
-
requestId,
|
|
2720
|
-
phase: `Done (${totalIssues} issues, ${totalElapsed}ms)`
|
|
2721
|
-
});
|
|
2722
|
-
} catch (error) {
|
|
2723
|
-
logServerError("Semantic pass failed", error instanceof Error ? error.message : String(error));
|
|
2724
|
-
sendMessage(ws, {
|
|
2725
|
-
type: "lint:progress",
|
|
2726
|
-
filePath,
|
|
2727
|
-
requestId,
|
|
2728
|
-
phase: `Done (${fastClientIssues.length} issues, ${Date.now() - startedAt}ms)`
|
|
2729
|
-
});
|
|
2730
|
-
}
|
|
3065
|
+
runSemanticAnalysisAsync(filePath, ws, requestId).catch((err) => {
|
|
3066
|
+
logServerError("Async semantic analysis failed", err instanceof Error ? err.message : String(err));
|
|
2731
3067
|
});
|
|
2732
3068
|
} else {
|
|
2733
|
-
|
|
2734
|
-
type: "lint:progress",
|
|
2735
|
-
filePath,
|
|
2736
|
-
requestId,
|
|
2737
|
-
phase: `Done (${fastClientIssues.length} issues, ${fastElapsed}ms)`
|
|
2738
|
-
});
|
|
3069
|
+
logSemanticSkipped(filePath, "rule not enabled");
|
|
2739
3070
|
}
|
|
2740
3071
|
break;
|
|
2741
3072
|
}
|
|
@@ -2757,39 +3088,7 @@ async function handleMessage(ws, data) {
|
|
|
2757
3088
|
}
|
|
2758
3089
|
const fastFiltered = fastIssues.filter((i) => !isSentinelIssue(i)).filter((issue) => issue.dataLoc === dataLoc);
|
|
2759
3090
|
sendMessage(ws, { type: "lint:result", filePath, requestId, issues: fastFiltered });
|
|
2760
|
-
|
|
2761
|
-
setImmediate(async () => {
|
|
2762
|
-
try {
|
|
2763
|
-
const semanticIssues = await lintFileSemantic(filePath, (phase) => {
|
|
2764
|
-
sendMessage(ws, { type: "lint:progress", filePath, requestId, phase });
|
|
2765
|
-
});
|
|
2766
|
-
const semanticSentinels = semanticIssues.filter(isSentinelIssue);
|
|
2767
|
-
for (const se of semanticSentinels) {
|
|
2768
|
-
logRuleInternalError(se.ruleId ?? "unknown", filePath, se.message);
|
|
2769
|
-
}
|
|
2770
|
-
const semanticFiltered = semanticIssues.filter((i) => !isSentinelIssue(i)).filter((issue) => issue.dataLoc === dataLoc);
|
|
2771
|
-
const totalFiltered = fastFiltered.length + semanticFiltered.length;
|
|
2772
|
-
const elapsed = Date.now() - startedAt;
|
|
2773
|
-
if (semanticFiltered.length > 0) {
|
|
2774
|
-
sendMessage(ws, { type: "lint:result", filePath, requestId, issues: semanticFiltered });
|
|
2775
|
-
}
|
|
2776
|
-
sendMessage(ws, {
|
|
2777
|
-
type: "lint:progress",
|
|
2778
|
-
filePath,
|
|
2779
|
-
requestId,
|
|
2780
|
-
phase: `Done (${totalFiltered} issues, ${elapsed}ms)`
|
|
2781
|
-
});
|
|
2782
|
-
} catch (error) {
|
|
2783
|
-
logServerError("Semantic pass failed", error instanceof Error ? error.message : String(error));
|
|
2784
|
-
sendMessage(ws, {
|
|
2785
|
-
type: "lint:progress",
|
|
2786
|
-
filePath,
|
|
2787
|
-
requestId,
|
|
2788
|
-
phase: `Done (${fastFiltered.length} issues, ${Date.now() - startedAt}ms)`
|
|
2789
|
-
});
|
|
2790
|
-
}
|
|
2791
|
-
});
|
|
2792
|
-
} else {
|
|
3091
|
+
{
|
|
2793
3092
|
const elapsed = Date.now() - startedAt;
|
|
2794
3093
|
sendMessage(ws, {
|
|
2795
3094
|
type: "lint:progress",
|
|
@@ -2798,6 +3097,13 @@ async function handleMessage(ws, data) {
|
|
|
2798
3097
|
phase: `Done (${fastFiltered.length} issues, ${elapsed}ms)`
|
|
2799
3098
|
});
|
|
2800
3099
|
}
|
|
3100
|
+
if (isSemanticRuleEnabled()) {
|
|
3101
|
+
runSemanticAnalysisAsync(filePath, ws, requestId).catch((err) => {
|
|
3102
|
+
logServerError("Async semantic analysis failed", err instanceof Error ? err.message : String(err));
|
|
3103
|
+
});
|
|
3104
|
+
} else {
|
|
3105
|
+
logSemanticSkipped(filePath, "rule not enabled");
|
|
3106
|
+
}
|
|
2801
3107
|
break;
|
|
2802
3108
|
}
|
|
2803
3109
|
case "subscribe:file": {
|
|
@@ -2831,125 +3137,11 @@ async function handleMessage(ws, data) {
|
|
|
2831
3137
|
break;
|
|
2832
3138
|
}
|
|
2833
3139
|
case "vision:analyze": {
|
|
2834
|
-
const
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
screenshotFile,
|
|
2839
|
-
manifest,
|
|
2840
|
-
requestId
|
|
2841
|
-
} = message;
|
|
2842
|
-
logVisionAnalyze(route, requestId);
|
|
2843
|
-
const visionMod = await getVisionModule();
|
|
2844
|
-
if (!visionMod) {
|
|
2845
|
-
sendMessage(ws, {
|
|
2846
|
-
type: "vision:result",
|
|
2847
|
-
route,
|
|
2848
|
-
issues: [],
|
|
2849
|
-
analysisTime: 0,
|
|
2850
|
-
error: "uilint-vision is not installed",
|
|
2851
|
-
requestId
|
|
2852
|
-
});
|
|
2853
|
-
break;
|
|
2854
|
-
}
|
|
2855
|
-
sendMessage(ws, {
|
|
2856
|
-
type: "vision:progress",
|
|
2857
|
-
route,
|
|
2858
|
-
requestId,
|
|
2859
|
-
phase: "Starting vision analysis..."
|
|
3140
|
+
const visionMsg = message;
|
|
3141
|
+
logVisionAnalyze(visionMsg.route, visionMsg.requestId);
|
|
3142
|
+
runVisionAnalysisInBackground(ws, visionMsg).catch((err) => {
|
|
3143
|
+
logServerError("Vision analysis failed", err instanceof Error ? err.message : String(err));
|
|
2860
3144
|
});
|
|
2861
|
-
const startedAt = Date.now();
|
|
2862
|
-
const analyzer = await getVisionAnalyzerInstance();
|
|
2863
|
-
const releaseOllama = await acquireOllamaMutex();
|
|
2864
|
-
try {
|
|
2865
|
-
const analyzerObj = analyzer;
|
|
2866
|
-
const analyzerModel = typeof analyzerObj?.getModel === "function" ? analyzerObj.getModel() : void 0;
|
|
2867
|
-
const analyzerBaseUrl = typeof analyzerObj?.getBaseUrl === "function" ? analyzerObj.getBaseUrl() : void 0;
|
|
2868
|
-
if (!screenshot) {
|
|
2869
|
-
sendMessage(ws, {
|
|
2870
|
-
type: "vision:result",
|
|
2871
|
-
route,
|
|
2872
|
-
issues: [],
|
|
2873
|
-
analysisTime: Date.now() - startedAt,
|
|
2874
|
-
error: "No screenshot provided for vision analysis",
|
|
2875
|
-
requestId
|
|
2876
|
-
});
|
|
2877
|
-
break;
|
|
2878
|
-
}
|
|
2879
|
-
const result = await visionMod.runVisionAnalysis({
|
|
2880
|
-
imageBase64: screenshot,
|
|
2881
|
-
manifest,
|
|
2882
|
-
projectPath: serverAppRootForVision,
|
|
2883
|
-
baseUrl: analyzerBaseUrl,
|
|
2884
|
-
model: analyzerModel,
|
|
2885
|
-
analyzer,
|
|
2886
|
-
onPhase: (phase) => {
|
|
2887
|
-
sendMessage(ws, {
|
|
2888
|
-
type: "vision:progress",
|
|
2889
|
-
route,
|
|
2890
|
-
requestId,
|
|
2891
|
-
phase
|
|
2892
|
-
});
|
|
2893
|
-
},
|
|
2894
|
-
pathResolver: resolvePathSpecifier
|
|
2895
|
-
});
|
|
2896
|
-
if (typeof screenshotFile === "string" && screenshotFile.length > 0) {
|
|
2897
|
-
if (isValidScreenshotFilename(screenshotFile)) {
|
|
2898
|
-
const screenshotsDir = join3(serverAppRootForVision, ".uilint", "screenshots");
|
|
2899
|
-
const imagePath = join3(screenshotsDir, screenshotFile);
|
|
2900
|
-
try {
|
|
2901
|
-
if (existsSync5(imagePath)) {
|
|
2902
|
-
const report = visionMod.writeVisionMarkdownReport({
|
|
2903
|
-
imagePath,
|
|
2904
|
-
route,
|
|
2905
|
-
timestamp,
|
|
2906
|
-
visionModel: result.visionModel,
|
|
2907
|
-
baseUrl: result.baseUrl,
|
|
2908
|
-
analysisTimeMs: result.analysisTime,
|
|
2909
|
-
prompt: result.prompt ?? null,
|
|
2910
|
-
rawResponse: result.rawResponse ?? null,
|
|
2911
|
-
metadata: {
|
|
2912
|
-
screenshotFile: parse(imagePath).base,
|
|
2913
|
-
appRoot: serverAppRootForVision,
|
|
2914
|
-
manifestElements: manifest.length,
|
|
2915
|
-
requestId: requestId ?? null
|
|
2916
|
-
}
|
|
2917
|
-
});
|
|
2918
|
-
logServerInfo(`Wrote vision report`, report.outPath);
|
|
2919
|
-
}
|
|
2920
|
-
} catch (e) {
|
|
2921
|
-
logServerWarning(
|
|
2922
|
-
`Failed to write vision report for ${screenshotFile}`,
|
|
2923
|
-
e instanceof Error ? e.message : String(e)
|
|
2924
|
-
);
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
const elapsed = Date.now() - startedAt;
|
|
2929
|
-
const resultIssues = result.issues;
|
|
2930
|
-
logVisionDone(route, resultIssues.length, elapsed);
|
|
2931
|
-
sendMessage(ws, {
|
|
2932
|
-
type: "vision:result",
|
|
2933
|
-
route,
|
|
2934
|
-
issues: resultIssues,
|
|
2935
|
-
analysisTime: result.analysisTime,
|
|
2936
|
-
requestId
|
|
2937
|
-
});
|
|
2938
|
-
} catch (error) {
|
|
2939
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2940
|
-
const elapsed = Date.now() - startedAt;
|
|
2941
|
-
logServerError(`Vision analysis failed for ${route}`, errorMessage);
|
|
2942
|
-
sendMessage(ws, {
|
|
2943
|
-
type: "vision:result",
|
|
2944
|
-
route,
|
|
2945
|
-
issues: [],
|
|
2946
|
-
analysisTime: elapsed,
|
|
2947
|
-
error: errorMessage,
|
|
2948
|
-
requestId
|
|
2949
|
-
});
|
|
2950
|
-
} finally {
|
|
2951
|
-
releaseOllama();
|
|
2952
|
-
}
|
|
2953
3145
|
break;
|
|
2954
3146
|
}
|
|
2955
3147
|
case "vision:check": {
|
|
@@ -3204,13 +3396,20 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3204
3396
|
return;
|
|
3205
3397
|
}
|
|
3206
3398
|
isIndexing = true;
|
|
3399
|
+
setPluginStatus("duplicates", "processing", "Preparing index...");
|
|
3207
3400
|
startBackgroundTask(
|
|
3208
3401
|
"duplicates-index",
|
|
3209
3402
|
"Duplicates Index",
|
|
3210
3403
|
"Waiting for Ollama..."
|
|
3211
3404
|
);
|
|
3212
|
-
const release = await acquireOllamaMutex();
|
|
3405
|
+
const release = await acquireOllamaMutex("duplicates");
|
|
3213
3406
|
broadcast({ type: "duplicates:indexing:start" });
|
|
3407
|
+
broadcast({
|
|
3408
|
+
type: "plugin:operation:start",
|
|
3409
|
+
pluginId: "duplicates",
|
|
3410
|
+
operationName: "indexing",
|
|
3411
|
+
message: "Building duplicates index..."
|
|
3412
|
+
});
|
|
3214
3413
|
try {
|
|
3215
3414
|
const { indexDirectory } = await import("uilint-duplicates");
|
|
3216
3415
|
const result = await indexDirectory(appRoot, {
|
|
@@ -3223,16 +3422,27 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3223
3422
|
total,
|
|
3224
3423
|
message
|
|
3225
3424
|
);
|
|
3425
|
+
updatePluginProgress("duplicates", progress, current, total, message);
|
|
3226
3426
|
broadcast({
|
|
3227
3427
|
type: "duplicates:indexing:progress",
|
|
3228
3428
|
message,
|
|
3229
3429
|
current,
|
|
3230
3430
|
total
|
|
3231
3431
|
});
|
|
3432
|
+
broadcast({
|
|
3433
|
+
type: "plugin:operation:progress",
|
|
3434
|
+
pluginId: "duplicates",
|
|
3435
|
+
operationName: "indexing",
|
|
3436
|
+
current,
|
|
3437
|
+
total,
|
|
3438
|
+
message
|
|
3439
|
+
});
|
|
3232
3440
|
}
|
|
3233
3441
|
});
|
|
3234
3442
|
const successMsg = `${result.totalChunks} chunks (${result.added} added, ${result.modified} modified, ${result.deleted} deleted) in ${(result.duration / 1e3).toFixed(1)}s`;
|
|
3235
3443
|
completeBackgroundTask("duplicates-index", `Index complete: ${successMsg}`);
|
|
3444
|
+
setPluginStatus("duplicates", "complete", successMsg);
|
|
3445
|
+
setTimeout(() => setPluginStatus("duplicates", "idle"), 3e3);
|
|
3236
3446
|
broadcast({
|
|
3237
3447
|
type: "duplicates:indexing:complete",
|
|
3238
3448
|
added: result.added,
|
|
@@ -3241,10 +3451,24 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3241
3451
|
totalChunks: result.totalChunks,
|
|
3242
3452
|
duration: result.duration
|
|
3243
3453
|
});
|
|
3454
|
+
broadcast({
|
|
3455
|
+
type: "plugin:operation:complete",
|
|
3456
|
+
pluginId: "duplicates",
|
|
3457
|
+
operationName: "indexing",
|
|
3458
|
+
message: successMsg
|
|
3459
|
+
});
|
|
3244
3460
|
} catch (error) {
|
|
3245
3461
|
const msg = error instanceof Error ? error.message : String(error);
|
|
3246
3462
|
completeBackgroundTask("duplicates-index", void 0, msg);
|
|
3463
|
+
setPluginStatus("duplicates", "error", msg);
|
|
3464
|
+
setTimeout(() => setPluginStatus("duplicates", "idle"), 3e3);
|
|
3247
3465
|
broadcast({ type: "duplicates:indexing:error", error: msg });
|
|
3466
|
+
broadcast({
|
|
3467
|
+
type: "plugin:operation:error",
|
|
3468
|
+
pluginId: "duplicates",
|
|
3469
|
+
operationName: "indexing",
|
|
3470
|
+
error: msg
|
|
3471
|
+
});
|
|
3248
3472
|
} finally {
|
|
3249
3473
|
release();
|
|
3250
3474
|
isIndexing = false;
|
|
@@ -3392,6 +3616,9 @@ async function serve(options) {
|
|
|
3392
3616
|
const useDashboardUI = process.stdout.isTTY && !options.noDashboard;
|
|
3393
3617
|
if (useDashboardUI) {
|
|
3394
3618
|
enableDashboard();
|
|
3619
|
+
registerPlugin("semantic", "Semantic", "qwen3-vl:8b-instruct");
|
|
3620
|
+
registerPlugin("vision", "Vision", "gemma3:4b");
|
|
3621
|
+
registerPlugin("duplicates", "Duplicates", "nomic-embed-text");
|
|
3395
3622
|
} else {
|
|
3396
3623
|
disableDashboard();
|
|
3397
3624
|
}
|
|
@@ -3561,7 +3788,7 @@ async function serve(options) {
|
|
|
3561
3788
|
});
|
|
3562
3789
|
setServerRunning(port);
|
|
3563
3790
|
if (useDashboardUI) {
|
|
3564
|
-
const { renderDashboard } = await import("./render-
|
|
3791
|
+
const { renderDashboard } = await import("./render-2P4YWHXV.js");
|
|
3565
3792
|
const { waitUntilExit } = renderDashboard({
|
|
3566
3793
|
onQuit: () => {
|
|
3567
3794
|
clearInterval(pingInterval);
|
|
@@ -5704,7 +5931,7 @@ program.command("update").description("Update existing style guide with new styl
|
|
|
5704
5931
|
});
|
|
5705
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)");
|
|
5706
5933
|
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) => {
|
|
5707
|
-
const { removeUI } = await import("./remove-ui-
|
|
5934
|
+
const { removeUI } = await import("./remove-ui-GZRFA2AC.js");
|
|
5708
5935
|
await removeUI({ dryRun: options.dryRun, yes: options.yes });
|
|
5709
5936
|
});
|
|
5710
5937
|
program.command("serve").description("Start WebSocket server for real-time UI linting").option("-p, --port <number>", "Port to listen on", "9234").option("--no-dashboard", "Disable dashboard UI (use simple logging)").action(async (options) => {
|
|
@@ -5756,7 +5983,7 @@ program.addCommand(createDuplicatesCommand());
|
|
|
5756
5983
|
program.addCommand(createManifestCommand());
|
|
5757
5984
|
program.addCommand(createSocketCommand());
|
|
5758
5985
|
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) => {
|
|
5759
|
-
const { upgrade } = await import("./upgrade-
|
|
5986
|
+
const { upgrade } = await import("./upgrade-2UKW3SIQ.js");
|
|
5760
5987
|
await upgrade({
|
|
5761
5988
|
check: options.check,
|
|
5762
5989
|
yes: options.yes,
|
|
@@ -5765,14 +5992,14 @@ program.command("upgrade").description("Update installed ESLint rules to latest
|
|
|
5765
5992
|
});
|
|
5766
5993
|
});
|
|
5767
5994
|
async function main() {
|
|
5768
|
-
const { discoverPlugins: discoverPlugins2 } = await import("./plugin-loader-
|
|
5995
|
+
const { discoverPlugins: discoverPlugins2 } = await import("./plugin-loader-LUIV7MLR.js");
|
|
5769
5996
|
const pluginManifests = await discoverPlugins2();
|
|
5770
5997
|
for (const manifest of pluginManifests) {
|
|
5771
5998
|
initCommand.option(`--${manifest.cliFlag}`, manifest.cliDescription);
|
|
5772
5999
|
}
|
|
5773
6000
|
initCommand.action(async (options) => {
|
|
5774
6001
|
const plugins = pluginManifests.filter((m) => options[m.cliFlag]).map((m) => m.cliFlag);
|
|
5775
|
-
const { initUI } = await import("./init-ui-
|
|
6002
|
+
const { initUI } = await import("./init-ui-QQXIZSKI.js");
|
|
5776
6003
|
await initUI({
|
|
5777
6004
|
force: options.force,
|
|
5778
6005
|
react: options.react,
|