uilint 0.2.146 → 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-ZOLQHFVQ.js → chunk-JEYSQ5KF.js} +20 -1
- package/dist/chunk-JEYSQ5KF.js.map +1 -0
- package/dist/index.js +163 -18
- package/dist/index.js.map +1 -1
- package/dist/{render-43OMCORR.js → render-2P4YWHXV.js} +112 -97
- package/dist/render-2P4YWHXV.js.map +1 -0
- package/package.json +8 -8
- package/dist/chunk-ZOLQHFVQ.js.map +0 -1
- package/dist/render-43OMCORR.js.map +0 -1
|
@@ -17,6 +17,7 @@ function createInitialState() {
|
|
|
17
17
|
status: "checking"
|
|
18
18
|
},
|
|
19
19
|
backgroundTasks: /* @__PURE__ */ new Map(),
|
|
20
|
+
pluginStates: /* @__PURE__ */ new Map(),
|
|
20
21
|
activities: [],
|
|
21
22
|
maxActivities: 50,
|
|
22
23
|
verbose: false,
|
|
@@ -60,6 +61,9 @@ function createDashboardStore() {
|
|
|
60
61
|
get ollamaStatus() {
|
|
61
62
|
return state.ollamaStatus;
|
|
62
63
|
},
|
|
64
|
+
get pluginStates() {
|
|
65
|
+
return state.pluginStates;
|
|
66
|
+
},
|
|
63
67
|
get activeFilter() {
|
|
64
68
|
return state.activeFilter;
|
|
65
69
|
},
|
|
@@ -199,6 +203,21 @@ function createDashboardStore() {
|
|
|
199
203
|
state = { ...state, activeFilter: order[nextIndex] };
|
|
200
204
|
notify();
|
|
201
205
|
},
|
|
206
|
+
// Plugin states
|
|
207
|
+
registerPlugin(id, name, model) {
|
|
208
|
+
const newMap = new Map(state.pluginStates);
|
|
209
|
+
newMap.set(id, { id, name, model, status: "idle" });
|
|
210
|
+
state = { ...state, pluginStates: newMap };
|
|
211
|
+
notify();
|
|
212
|
+
},
|
|
213
|
+
setPluginState(id, update) {
|
|
214
|
+
const existing = state.pluginStates.get(id);
|
|
215
|
+
if (!existing) return;
|
|
216
|
+
const newMap = new Map(state.pluginStates);
|
|
217
|
+
newMap.set(id, { ...existing, ...update });
|
|
218
|
+
state = { ...state, pluginStates: newMap };
|
|
219
|
+
notify();
|
|
220
|
+
},
|
|
202
221
|
// Ollama status
|
|
203
222
|
setOllamaStatus(status) {
|
|
204
223
|
state = { ...state, ollamaStatus: status };
|
|
@@ -226,4 +245,4 @@ function getDashboardStore() {
|
|
|
226
245
|
export {
|
|
227
246
|
getDashboardStore
|
|
228
247
|
};
|
|
229
|
-
//# sourceMappingURL=chunk-
|
|
248
|
+
//# sourceMappingURL=chunk-JEYSQ5KF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/serve/dashboard/store.ts"],"sourcesContent":["/**\n * Dashboard state store - simple event-based state management\n */\n\nimport type {\n DashboardState,\n DashboardStore,\n ActivityEntry,\n ActivityCategory,\n BackgroundTask,\n WorkspaceInfo,\n ServerStats,\n OllamaStatus,\n PluginId,\n PluginState,\n} from \"./types.js\";\n\ntype Listener = () => void;\n\nlet activityIdCounter = 0;\n\nfunction createInitialState(): DashboardState {\n return {\n isRunning: false,\n port: 9234,\n workspace: null,\n stats: {\n connectedClients: 0,\n subscriptions: 0,\n cacheEntries: 0,\n startTime: new Date(),\n },\n ollamaStatus: {\n status: \"checking\",\n },\n backgroundTasks: new Map(),\n pluginStates: new Map(),\n activities: [],\n maxActivities: 50,\n verbose: false,\n activeFilter: \"all\",\n };\n}\n\n/**\n * Create a dashboard store instance\n */\nexport function createDashboardStore(): DashboardStore & {\n subscribe: (listener: Listener) => () => void;\n getState: () => DashboardState;\n} {\n let state = createInitialState();\n const listeners = new Set<Listener>();\n\n function notify() {\n for (const listener of listeners) {\n listener();\n }\n }\n\n const store: DashboardStore & {\n subscribe: (listener: Listener) => () => void;\n getState: () => DashboardState;\n } = {\n // State getters\n get isRunning() {\n return state.isRunning;\n },\n get port() {\n return state.port;\n },\n get workspace() {\n return state.workspace;\n },\n get stats() {\n return state.stats;\n },\n get backgroundTasks() {\n return state.backgroundTasks;\n },\n get activities() {\n return state.activities;\n },\n get maxActivities() {\n return state.maxActivities;\n },\n get verbose() {\n return state.verbose;\n },\n get ollamaStatus() {\n return state.ollamaStatus;\n },\n get pluginStates() {\n return state.pluginStates;\n },\n get activeFilter() {\n return state.activeFilter;\n },\n\n // Server lifecycle\n setRunning(running: boolean) {\n state = { ...state, isRunning: running };\n if (running) {\n state.stats = { ...state.stats, startTime: new Date() };\n }\n notify();\n },\n\n setPort(port: number) {\n state = { ...state, port };\n notify();\n },\n\n setWorkspace(info: WorkspaceInfo) {\n state = { ...state, workspace: info };\n notify();\n },\n\n // Stats updates\n updateStats(partial: Partial<ServerStats>) {\n state = { ...state, stats: { ...state.stats, ...partial } };\n notify();\n },\n\n incrementClients() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n connectedClients: state.stats.connectedClients + 1,\n },\n };\n notify();\n },\n\n decrementClients() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n connectedClients: Math.max(0, state.stats.connectedClients - 1),\n },\n };\n notify();\n },\n\n incrementSubscriptions() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n subscriptions: state.stats.subscriptions + 1,\n },\n };\n notify();\n },\n\n decrementSubscriptions() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n subscriptions: Math.max(0, state.stats.subscriptions - 1),\n },\n };\n notify();\n },\n\n setCacheEntries(count: number) {\n state = {\n ...state,\n stats: { ...state.stats, cacheEntries: count },\n };\n notify();\n },\n\n // Background tasks\n setBackgroundTask(task: BackgroundTask) {\n const newTasks = new Map(state.backgroundTasks);\n newTasks.set(task.id, task);\n state = { ...state, backgroundTasks: newTasks };\n notify();\n },\n\n updateBackgroundTaskProgress(\n id: string,\n progress: number,\n current?: number,\n total?: number,\n message?: string\n ) {\n const task = state.backgroundTasks.get(id);\n if (task) {\n const newTasks = new Map(state.backgroundTasks);\n newTasks.set(id, {\n ...task,\n progress,\n current,\n total,\n message,\n status: \"running\",\n });\n state = { ...state, backgroundTasks: newTasks };\n notify();\n }\n },\n\n completeBackgroundTask(id: string, error?: string) {\n const task = state.backgroundTasks.get(id);\n if (task) {\n const newTasks = new Map(state.backgroundTasks);\n newTasks.set(id, {\n ...task,\n status: error ? \"error\" : \"complete\",\n progress: error ? task.progress : 100,\n error,\n });\n state = { ...state, backgroundTasks: newTasks };\n notify();\n }\n },\n\n // Activity logging\n addActivity(entry: Omit<ActivityEntry, \"id\" | \"timestamp\">) {\n const newEntry: ActivityEntry = {\n ...entry,\n id: `activity-${++activityIdCounter}`,\n timestamp: new Date(),\n };\n\n // Prepend new entry, trim to max\n const activities = [newEntry, ...state.activities].slice(\n 0,\n state.maxActivities\n );\n state = { ...state, activities };\n notify();\n },\n\n clearActivities() {\n state = { ...state, activities: [] };\n notify();\n },\n\n // Display options\n toggleVerbose() {\n state = { ...state, verbose: !state.verbose };\n notify();\n },\n\n // Activity filter\n cycleFilter() {\n const order: ActivityCategory[] = [\"all\", \"errors\", \"vision\", \"semantic\", \"lint\"];\n const currentIndex = order.indexOf(state.activeFilter);\n const nextIndex = (currentIndex + 1) % order.length;\n state = { ...state, activeFilter: order[nextIndex]! };\n notify();\n },\n\n // Plugin states\n registerPlugin(id: PluginId, name: string, model: string) {\n const newMap = new Map(state.pluginStates);\n newMap.set(id, { id, name, model, status: \"idle\" });\n state = { ...state, pluginStates: newMap };\n notify();\n },\n\n setPluginState(id: PluginId, update: Partial<PluginState>) {\n const existing = state.pluginStates.get(id);\n if (!existing) return;\n const newMap = new Map(state.pluginStates);\n newMap.set(id, { ...existing, ...update });\n state = { ...state, pluginStates: newMap };\n notify();\n },\n\n // Ollama status\n setOllamaStatus(status: OllamaStatus) {\n state = { ...state, ollamaStatus: status };\n notify();\n },\n\n // Subscription\n subscribe(listener: Listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n getState() {\n return state;\n },\n };\n\n return store;\n}\n\n// Singleton store instance for use across the application\nlet globalStore: ReturnType<typeof createDashboardStore> | null = null;\n\nexport function getDashboardStore(): ReturnType<typeof createDashboardStore> {\n if (!globalStore) {\n globalStore = createDashboardStore();\n }\n return globalStore;\n}\n\n// Reset store (useful for testing)\nexport function resetDashboardStore(): void {\n globalStore = null;\n}\n"],"mappings":"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","names":[]}
|
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,
|
|
@@ -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) {
|
|
@@ -2581,21 +2673,31 @@ async function lintFileFast(filePath, onProgress) {
|
|
|
2581
2673
|
}
|
|
2582
2674
|
}
|
|
2583
2675
|
async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
2676
|
+
const startTime = Date.now();
|
|
2677
|
+
logSemanticAnalyze(filePath, requestId);
|
|
2584
2678
|
const absolutePath = resolveRequestedFilePath(filePath);
|
|
2585
|
-
if (!existsSync5(absolutePath))
|
|
2679
|
+
if (!existsSync5(absolutePath)) {
|
|
2680
|
+
logSemanticSkipped(filePath, "file not found");
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2586
2683
|
const eslintConfigPath = findEslintConfigFile(serverAppRootForVision);
|
|
2587
2684
|
const ruleConfigs = eslintConfigPath ? readRuleConfigsFromConfig(eslintConfigPath) : /* @__PURE__ */ new Map();
|
|
2588
2685
|
const semanticConfig = ruleConfigs.get("semantic");
|
|
2589
2686
|
const model = semanticConfig?.options?.model || "qwen3-vl:8b-instruct";
|
|
2590
2687
|
const styleguidePath = semanticConfig?.options?.styleguidePath || void 0;
|
|
2688
|
+
setPluginModel("semantic", model);
|
|
2591
2689
|
const { getStyleguide, hashContentSync, setCacheEntry, getCacheEntry } = await import("uilint-eslint");
|
|
2592
2690
|
const fileDir = dirname5(absolutePath);
|
|
2593
2691
|
const { content: styleguide } = getStyleguide(fileDir, styleguidePath);
|
|
2594
|
-
if (!styleguide)
|
|
2692
|
+
if (!styleguide) {
|
|
2693
|
+
logSemanticSkipped(filePath, "no styleguide found");
|
|
2694
|
+
return;
|
|
2695
|
+
}
|
|
2595
2696
|
let fileContent;
|
|
2596
2697
|
try {
|
|
2597
2698
|
fileContent = readFileSync2(absolutePath, "utf-8");
|
|
2598
2699
|
} catch {
|
|
2700
|
+
logSemanticSkipped(filePath, "file read error");
|
|
2599
2701
|
return;
|
|
2600
2702
|
}
|
|
2601
2703
|
const fileHash = hashContentSync(fileContent);
|
|
@@ -2603,7 +2705,18 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
|
2603
2705
|
const projectRoot = findWorkspaceRoot4(fileDir) || fileDir;
|
|
2604
2706
|
const relativeFilePath = relative2(projectRoot, absolutePath);
|
|
2605
2707
|
const cached = getCacheEntry(projectRoot, relativeFilePath, fileHash, styleguideHash);
|
|
2606
|
-
if (cached)
|
|
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
|
+
);
|
|
2607
2720
|
sendMessage(ws, {
|
|
2608
2721
|
type: "lint:progress",
|
|
2609
2722
|
filePath,
|
|
@@ -2617,12 +2730,13 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
|
2617
2730
|
operationName: "analysis",
|
|
2618
2731
|
message: `Analyzing ${relative2(serverAppRootForVision, absolutePath)}...`
|
|
2619
2732
|
});
|
|
2620
|
-
const release = await acquireOllamaMutex();
|
|
2733
|
+
const release = await acquireOllamaMutex("semantic");
|
|
2621
2734
|
try {
|
|
2622
2735
|
const { OllamaClient: OllamaClient2, buildSourceScanPrompt: buildSourceScanPrompt2 } = await import("uilint-core/node");
|
|
2623
2736
|
const client = new OllamaClient2({ model });
|
|
2624
2737
|
const ok = await client.isAvailable();
|
|
2625
2738
|
if (!ok) {
|
|
2739
|
+
logServerWarning("Semantic analysis: Ollama not available");
|
|
2626
2740
|
updateBackgroundTaskProgress("semantic-analysis", 0, 0, 0, "Ollama not available");
|
|
2627
2741
|
completeBackgroundTask("semantic-analysis", void 0, "Ollama not available");
|
|
2628
2742
|
broadcast({
|
|
@@ -2631,9 +2745,13 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
|
2631
2745
|
operationName: "analysis",
|
|
2632
2746
|
error: "Ollama not available"
|
|
2633
2747
|
});
|
|
2748
|
+
completeSemanticFile("error", "Ollama not available");
|
|
2634
2749
|
return;
|
|
2635
2750
|
}
|
|
2636
2751
|
updateBackgroundTaskProgress("semantic-analysis", 50, 0, 0, "Waiting for LLM response...");
|
|
2752
|
+
updateSemanticBatchProgress(
|
|
2753
|
+
`Analyzing ${relative2(serverAppRootForVision, absolutePath)}...`
|
|
2754
|
+
);
|
|
2637
2755
|
broadcast({
|
|
2638
2756
|
type: "plugin:operation:progress",
|
|
2639
2757
|
pluginId: "semantic",
|
|
@@ -2671,16 +2789,17 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
|
2671
2789
|
source: null,
|
|
2672
2790
|
dataLoc: `${dataLocFile}:${issue.line}:${issue.column || 0}`
|
|
2673
2791
|
}));
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
}
|
|
2792
|
+
sendMessage(ws, {
|
|
2793
|
+
type: "lint:result",
|
|
2794
|
+
filePath,
|
|
2795
|
+
requestId,
|
|
2796
|
+
issues: lintIssues
|
|
2797
|
+
});
|
|
2798
|
+
const elapsed = Date.now() - startTime;
|
|
2682
2799
|
const msg = `${issues.length} issue(s) found`;
|
|
2800
|
+
logSemanticDone(filePath, issues.length, elapsed);
|
|
2683
2801
|
completeBackgroundTask("semantic-analysis", msg);
|
|
2802
|
+
completeSemanticFile("complete", msg);
|
|
2684
2803
|
broadcast({
|
|
2685
2804
|
type: "plugin:operation:complete",
|
|
2686
2805
|
pluginId: "semantic",
|
|
@@ -2697,6 +2816,7 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
|
2697
2816
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2698
2817
|
logServerError("Async semantic analysis failed", errorMessage);
|
|
2699
2818
|
completeBackgroundTask("semantic-analysis", void 0, errorMessage);
|
|
2819
|
+
completeSemanticFile("error", errorMessage);
|
|
2700
2820
|
broadcast({
|
|
2701
2821
|
type: "plugin:operation:error",
|
|
2702
2822
|
pluginId: "semantic",
|
|
@@ -2709,6 +2829,7 @@ async function runSemanticAnalysisAsync(filePath, ws, requestId) {
|
|
|
2709
2829
|
}
|
|
2710
2830
|
async function runVisionAnalysisInBackground(ws, message) {
|
|
2711
2831
|
const { route, timestamp, screenshot, screenshotFile, manifest, requestId } = message;
|
|
2832
|
+
setPluginStatus("vision", "processing", `Analyzing ${route}...`);
|
|
2712
2833
|
startBackgroundTask("vision-analysis", "Vision Analysis", `Analyzing ${route}...`);
|
|
2713
2834
|
broadcast({
|
|
2714
2835
|
type: "plugin:operation:start",
|
|
@@ -2727,6 +2848,8 @@ async function runVisionAnalysisInBackground(ws, message) {
|
|
|
2727
2848
|
requestId
|
|
2728
2849
|
});
|
|
2729
2850
|
completeBackgroundTask("vision-analysis", void 0, "uilint-vision not installed");
|
|
2851
|
+
setPluginStatus("vision", "error", "uilint-vision not installed");
|
|
2852
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2730
2853
|
broadcast({
|
|
2731
2854
|
type: "plugin:operation:error",
|
|
2732
2855
|
pluginId: "vision",
|
|
@@ -2744,11 +2867,14 @@ async function runVisionAnalysisInBackground(ws, message) {
|
|
|
2744
2867
|
const startedAt = Date.now();
|
|
2745
2868
|
const analyzer = await getVisionAnalyzerInstance();
|
|
2746
2869
|
updateBackgroundTaskProgress("vision-analysis", 10, 0, 0, "Waiting for Ollama...");
|
|
2747
|
-
const releaseOllama = await acquireOllamaMutex();
|
|
2870
|
+
const releaseOllama = await acquireOllamaMutex("vision");
|
|
2748
2871
|
try {
|
|
2749
2872
|
const analyzerObj = analyzer;
|
|
2750
2873
|
const analyzerModel = typeof analyzerObj?.getModel === "function" ? analyzerObj.getModel() : void 0;
|
|
2751
2874
|
const analyzerBaseUrl = typeof analyzerObj?.getBaseUrl === "function" ? analyzerObj.getBaseUrl() : void 0;
|
|
2875
|
+
if (analyzerModel) {
|
|
2876
|
+
setPluginModel("vision", analyzerModel);
|
|
2877
|
+
}
|
|
2752
2878
|
if (!screenshot) {
|
|
2753
2879
|
sendMessage(ws, {
|
|
2754
2880
|
type: "vision:result",
|
|
@@ -2759,6 +2885,8 @@ async function runVisionAnalysisInBackground(ws, message) {
|
|
|
2759
2885
|
requestId
|
|
2760
2886
|
});
|
|
2761
2887
|
completeBackgroundTask("vision-analysis", void 0, "No screenshot provided");
|
|
2888
|
+
setPluginStatus("vision", "error", "No screenshot provided");
|
|
2889
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2762
2890
|
broadcast({
|
|
2763
2891
|
type: "plugin:operation:error",
|
|
2764
2892
|
pluginId: "vision",
|
|
@@ -2836,6 +2964,8 @@ async function runVisionAnalysisInBackground(ws, message) {
|
|
|
2836
2964
|
});
|
|
2837
2965
|
const msg = `${resultIssues.length} issue(s) in ${(elapsed / 1e3).toFixed(1)}s`;
|
|
2838
2966
|
completeBackgroundTask("vision-analysis", msg);
|
|
2967
|
+
setPluginStatus("vision", "complete", msg);
|
|
2968
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2839
2969
|
broadcast({
|
|
2840
2970
|
type: "plugin:operation:complete",
|
|
2841
2971
|
pluginId: "vision",
|
|
@@ -2855,6 +2985,8 @@ async function runVisionAnalysisInBackground(ws, message) {
|
|
|
2855
2985
|
requestId
|
|
2856
2986
|
});
|
|
2857
2987
|
completeBackgroundTask("vision-analysis", void 0, errorMessage);
|
|
2988
|
+
setPluginStatus("vision", "error", errorMessage);
|
|
2989
|
+
setTimeout(() => setPluginStatus("vision", "idle"), 3e3);
|
|
2858
2990
|
broadcast({
|
|
2859
2991
|
type: "plugin:operation:error",
|
|
2860
2992
|
pluginId: "vision",
|
|
@@ -2933,6 +3065,8 @@ async function handleMessage(ws, data) {
|
|
|
2933
3065
|
runSemanticAnalysisAsync(filePath, ws, requestId).catch((err) => {
|
|
2934
3066
|
logServerError("Async semantic analysis failed", err instanceof Error ? err.message : String(err));
|
|
2935
3067
|
});
|
|
3068
|
+
} else {
|
|
3069
|
+
logSemanticSkipped(filePath, "rule not enabled");
|
|
2936
3070
|
}
|
|
2937
3071
|
break;
|
|
2938
3072
|
}
|
|
@@ -2967,6 +3101,8 @@ async function handleMessage(ws, data) {
|
|
|
2967
3101
|
runSemanticAnalysisAsync(filePath, ws, requestId).catch((err) => {
|
|
2968
3102
|
logServerError("Async semantic analysis failed", err instanceof Error ? err.message : String(err));
|
|
2969
3103
|
});
|
|
3104
|
+
} else {
|
|
3105
|
+
logSemanticSkipped(filePath, "rule not enabled");
|
|
2970
3106
|
}
|
|
2971
3107
|
break;
|
|
2972
3108
|
}
|
|
@@ -3260,12 +3396,13 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3260
3396
|
return;
|
|
3261
3397
|
}
|
|
3262
3398
|
isIndexing = true;
|
|
3399
|
+
setPluginStatus("duplicates", "processing", "Preparing index...");
|
|
3263
3400
|
startBackgroundTask(
|
|
3264
3401
|
"duplicates-index",
|
|
3265
3402
|
"Duplicates Index",
|
|
3266
3403
|
"Waiting for Ollama..."
|
|
3267
3404
|
);
|
|
3268
|
-
const release = await acquireOllamaMutex();
|
|
3405
|
+
const release = await acquireOllamaMutex("duplicates");
|
|
3269
3406
|
broadcast({ type: "duplicates:indexing:start" });
|
|
3270
3407
|
broadcast({
|
|
3271
3408
|
type: "plugin:operation:start",
|
|
@@ -3285,6 +3422,7 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3285
3422
|
total,
|
|
3286
3423
|
message
|
|
3287
3424
|
);
|
|
3425
|
+
updatePluginProgress("duplicates", progress, current, total, message);
|
|
3288
3426
|
broadcast({
|
|
3289
3427
|
type: "duplicates:indexing:progress",
|
|
3290
3428
|
message,
|
|
@@ -3303,6 +3441,8 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3303
3441
|
});
|
|
3304
3442
|
const successMsg = `${result.totalChunks} chunks (${result.added} added, ${result.modified} modified, ${result.deleted} deleted) in ${(result.duration / 1e3).toFixed(1)}s`;
|
|
3305
3443
|
completeBackgroundTask("duplicates-index", `Index complete: ${successMsg}`);
|
|
3444
|
+
setPluginStatus("duplicates", "complete", successMsg);
|
|
3445
|
+
setTimeout(() => setPluginStatus("duplicates", "idle"), 3e3);
|
|
3306
3446
|
broadcast({
|
|
3307
3447
|
type: "duplicates:indexing:complete",
|
|
3308
3448
|
added: result.added,
|
|
@@ -3320,6 +3460,8 @@ async function buildDuplicatesIndex(appRoot) {
|
|
|
3320
3460
|
} catch (error) {
|
|
3321
3461
|
const msg = error instanceof Error ? error.message : String(error);
|
|
3322
3462
|
completeBackgroundTask("duplicates-index", void 0, msg);
|
|
3463
|
+
setPluginStatus("duplicates", "error", msg);
|
|
3464
|
+
setTimeout(() => setPluginStatus("duplicates", "idle"), 3e3);
|
|
3323
3465
|
broadcast({ type: "duplicates:indexing:error", error: msg });
|
|
3324
3466
|
broadcast({
|
|
3325
3467
|
type: "plugin:operation:error",
|
|
@@ -3474,6 +3616,9 @@ async function serve(options) {
|
|
|
3474
3616
|
const useDashboardUI = process.stdout.isTTY && !options.noDashboard;
|
|
3475
3617
|
if (useDashboardUI) {
|
|
3476
3618
|
enableDashboard();
|
|
3619
|
+
registerPlugin("semantic", "Semantic", "qwen3-vl:8b-instruct");
|
|
3620
|
+
registerPlugin("vision", "Vision", "gemma3:4b");
|
|
3621
|
+
registerPlugin("duplicates", "Duplicates", "nomic-embed-text");
|
|
3477
3622
|
} else {
|
|
3478
3623
|
disableDashboard();
|
|
3479
3624
|
}
|
|
@@ -3643,7 +3788,7 @@ async function serve(options) {
|
|
|
3643
3788
|
});
|
|
3644
3789
|
setServerRunning(port);
|
|
3645
3790
|
if (useDashboardUI) {
|
|
3646
|
-
const { renderDashboard } = await import("./render-
|
|
3791
|
+
const { renderDashboard } = await import("./render-2P4YWHXV.js");
|
|
3647
3792
|
const { waitUntilExit } = renderDashboard({
|
|
3648
3793
|
onQuit: () => {
|
|
3649
3794
|
clearInterval(pingInterval);
|