uilint 0.2.65 → 0.2.67

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 Command6 } from "commander";
28
+ import { Command as Command7 } from "commander";
29
29
 
30
30
  // src/commands/scan.ts
31
31
  import { dirname, resolve as resolve2 } from "path";
@@ -3720,9 +3720,728 @@ function createDuplicatesCommand() {
3720
3720
  }
3721
3721
 
3722
3722
  // src/index.ts
3723
- import { readFileSync as readFileSync3 } from "fs";
3723
+ import { readFileSync as readFileSync4 } from "fs";
3724
3724
  import { dirname as dirname7, join as join5 } from "path";
3725
3725
  import { fileURLToPath } from "url";
3726
+
3727
+ // src/commands/socket/index.ts
3728
+ import { Command as Command6 } from "commander";
3729
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
3730
+ import chalk6 from "chalk";
3731
+
3732
+ // src/commands/socket/client.ts
3733
+ import WebSocket3 from "ws";
3734
+ var SocketClient = class {
3735
+ ws = null;
3736
+ url;
3737
+ debug;
3738
+ connectTimeout;
3739
+ connected = false;
3740
+ connectionPromise = null;
3741
+ pendingRequests = [];
3742
+ messageQueue = [];
3743
+ messageHandlers = [];
3744
+ constructor(options = {}) {
3745
+ const port = options.port || 9234;
3746
+ this.url = options.url || `ws://localhost:${port}`;
3747
+ this.debug = options.debug || false;
3748
+ this.connectTimeout = options.connectTimeout || 1e4;
3749
+ }
3750
+ /**
3751
+ * Connect to the socket server
3752
+ */
3753
+ async connect() {
3754
+ if (this.connected) return;
3755
+ if (this.connectionPromise) return this.connectionPromise;
3756
+ this.connectionPromise = new Promise((resolve8, reject) => {
3757
+ const timeout = setTimeout(() => {
3758
+ reject(new Error(`Connection timeout to ${this.url}`));
3759
+ }, this.connectTimeout);
3760
+ this.ws = new WebSocket3(this.url);
3761
+ this.ws.on("open", () => {
3762
+ clearTimeout(timeout);
3763
+ this.connected = true;
3764
+ this.log("Connected to", this.url);
3765
+ resolve8();
3766
+ });
3767
+ this.ws.on("message", (data) => {
3768
+ try {
3769
+ const message = JSON.parse(data.toString());
3770
+ this.log("Received:", message.type);
3771
+ this.handleMessage(message);
3772
+ } catch (e) {
3773
+ console.error("Failed to parse message:", e);
3774
+ }
3775
+ });
3776
+ this.ws.on("error", (err) => {
3777
+ clearTimeout(timeout);
3778
+ if (!this.connected) {
3779
+ reject(err);
3780
+ } else {
3781
+ console.error("WebSocket error:", err);
3782
+ }
3783
+ });
3784
+ this.ws.on("close", () => {
3785
+ this.connected = false;
3786
+ this.log("Disconnected");
3787
+ for (const req of this.pendingRequests) {
3788
+ clearTimeout(req.timeout);
3789
+ req.reject(new Error("Disconnected"));
3790
+ }
3791
+ this.pendingRequests = [];
3792
+ });
3793
+ });
3794
+ return this.connectionPromise;
3795
+ }
3796
+ /**
3797
+ * Disconnect from the server
3798
+ */
3799
+ disconnect() {
3800
+ if (this.ws) {
3801
+ this.ws.close();
3802
+ this.ws = null;
3803
+ this.connected = false;
3804
+ this.connectionPromise = null;
3805
+ }
3806
+ }
3807
+ /**
3808
+ * Check if connected
3809
+ */
3810
+ isConnected() {
3811
+ return this.connected;
3812
+ }
3813
+ /**
3814
+ * Add a handler for all incoming messages
3815
+ */
3816
+ onMessage(handler) {
3817
+ this.messageHandlers.push(handler);
3818
+ return () => {
3819
+ this.messageHandlers = this.messageHandlers.filter((h) => h !== handler);
3820
+ };
3821
+ }
3822
+ /**
3823
+ * Send a raw message
3824
+ */
3825
+ send(message) {
3826
+ if (!this.ws || !this.connected) {
3827
+ throw new Error("Not connected");
3828
+ }
3829
+ this.log("Sending:", message.type);
3830
+ this.ws.send(JSON.stringify(message));
3831
+ }
3832
+ /**
3833
+ * Wait for a message matching the predicate
3834
+ */
3835
+ waitFor(predicate, timeout = 3e4) {
3836
+ const existing = this.messageQueue.find(predicate);
3837
+ if (existing) {
3838
+ this.messageQueue = this.messageQueue.filter((m) => m !== existing);
3839
+ return Promise.resolve(existing);
3840
+ }
3841
+ return new Promise((resolve8, reject) => {
3842
+ const timeoutId = setTimeout(() => {
3843
+ this.pendingRequests = this.pendingRequests.filter(
3844
+ (r) => r.timeout !== timeoutId
3845
+ );
3846
+ reject(new Error(`Timeout waiting for message (${timeout}ms)`));
3847
+ }, timeout);
3848
+ this.pendingRequests.push({
3849
+ predicate,
3850
+ resolve: resolve8,
3851
+ reject,
3852
+ timeout: timeoutId
3853
+ });
3854
+ });
3855
+ }
3856
+ /**
3857
+ * Generate a unique request ID
3858
+ */
3859
+ generateRequestId() {
3860
+ return `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
3861
+ }
3862
+ // ==========================================================================
3863
+ // High-Level API Methods
3864
+ // ==========================================================================
3865
+ /**
3866
+ * Wait for workspace info (sent on connect)
3867
+ */
3868
+ async waitForWorkspaceInfo(timeout = 5e3) {
3869
+ const existing = this.messageQueue.find((m) => m.type === "workspace:info");
3870
+ if (existing) {
3871
+ return existing;
3872
+ }
3873
+ return await this.waitFor(
3874
+ (msg) => msg.type === "workspace:info",
3875
+ timeout
3876
+ );
3877
+ }
3878
+ /**
3879
+ * Wait for rules metadata (sent on connect)
3880
+ */
3881
+ async waitForRulesMetadata(timeout = 5e3) {
3882
+ const existing = this.messageQueue.find((m) => m.type === "rules:metadata");
3883
+ if (existing) {
3884
+ return existing;
3885
+ }
3886
+ return await this.waitFor(
3887
+ (msg) => msg.type === "rules:metadata",
3888
+ timeout
3889
+ );
3890
+ }
3891
+ /**
3892
+ * Lint a file
3893
+ */
3894
+ async lintFile(filePath, timeout = 3e4) {
3895
+ const requestId = this.generateRequestId();
3896
+ this.send({ type: "lint:file", filePath, requestId });
3897
+ return await this.waitFor(
3898
+ (msg) => msg.type === "lint:result" && msg.requestId === requestId,
3899
+ timeout
3900
+ );
3901
+ }
3902
+ /**
3903
+ * Lint a specific element
3904
+ */
3905
+ async lintElement(filePath, dataLoc, timeout = 3e4) {
3906
+ const requestId = this.generateRequestId();
3907
+ this.send({ type: "lint:element", filePath, dataLoc, requestId });
3908
+ return await this.waitFor(
3909
+ (msg) => msg.type === "lint:result" && msg.requestId === requestId,
3910
+ timeout
3911
+ );
3912
+ }
3913
+ /**
3914
+ * Check if vision analysis is available
3915
+ */
3916
+ async visionCheck(timeout = 1e4) {
3917
+ const requestId = this.generateRequestId();
3918
+ this.send({ type: "vision:check", requestId });
3919
+ return await this.waitFor(
3920
+ (msg) => msg.type === "vision:status" && msg.requestId === requestId,
3921
+ timeout
3922
+ );
3923
+ }
3924
+ /**
3925
+ * Run vision analysis
3926
+ */
3927
+ async visionAnalyze(params, timeout = 12e4) {
3928
+ const requestId = this.generateRequestId();
3929
+ this.send({
3930
+ type: "vision:analyze",
3931
+ route: params.route,
3932
+ timestamp: Date.now(),
3933
+ screenshot: params.screenshot,
3934
+ screenshotFile: params.screenshotFile,
3935
+ manifest: params.manifest,
3936
+ requestId
3937
+ });
3938
+ return await this.waitFor(
3939
+ (msg) => msg.type === "vision:result" && msg.requestId === requestId,
3940
+ timeout
3941
+ );
3942
+ }
3943
+ /**
3944
+ * Fetch source code
3945
+ */
3946
+ async fetchSource(filePath, timeout = 1e4) {
3947
+ const requestId = this.generateRequestId();
3948
+ this.send({ type: "source:fetch", filePath, requestId });
3949
+ return await this.waitFor(
3950
+ (msg) => (msg.type === "source:result" || msg.type === "source:error") && (msg.requestId === requestId || msg.requestId === requestId),
3951
+ timeout
3952
+ );
3953
+ }
3954
+ /**
3955
+ * Request coverage data
3956
+ */
3957
+ async requestCoverage(timeout = 3e4) {
3958
+ const requestId = this.generateRequestId();
3959
+ this.send({ type: "coverage:request", requestId });
3960
+ return await this.waitFor(
3961
+ (msg) => (msg.type === "coverage:result" || msg.type === "coverage:error") && (msg.requestId === requestId || msg.requestId === requestId),
3962
+ timeout
3963
+ );
3964
+ }
3965
+ /**
3966
+ * Set rule configuration
3967
+ */
3968
+ async setRuleConfig(ruleId, severity, options, timeout = 1e4) {
3969
+ const requestId = this.generateRequestId();
3970
+ this.send({
3971
+ type: "rule:config:set",
3972
+ ruleId,
3973
+ severity,
3974
+ options,
3975
+ requestId
3976
+ });
3977
+ return await this.waitFor(
3978
+ (msg) => msg.type === "rule:config:result" && msg.requestId === requestId,
3979
+ timeout
3980
+ );
3981
+ }
3982
+ /**
3983
+ * Set a configuration value
3984
+ */
3985
+ setConfig(key, value) {
3986
+ this.send({ type: "config:set", key, value });
3987
+ }
3988
+ /**
3989
+ * Subscribe to file changes
3990
+ */
3991
+ subscribeFile(filePath) {
3992
+ this.send({ type: "subscribe:file", filePath });
3993
+ }
3994
+ /**
3995
+ * Invalidate cache
3996
+ */
3997
+ invalidateCache(filePath) {
3998
+ this.send({ type: "cache:invalidate", filePath });
3999
+ }
4000
+ /**
4001
+ * Get all queued messages
4002
+ */
4003
+ getMessages() {
4004
+ return [...this.messageQueue];
4005
+ }
4006
+ /**
4007
+ * Clear message queue
4008
+ */
4009
+ clearMessages() {
4010
+ this.messageQueue = [];
4011
+ }
4012
+ handleMessage(message) {
4013
+ for (const handler of this.messageHandlers) {
4014
+ handler(message);
4015
+ }
4016
+ for (let i = 0; i < this.pendingRequests.length; i++) {
4017
+ const req = this.pendingRequests[i];
4018
+ if (req.predicate(message)) {
4019
+ clearTimeout(req.timeout);
4020
+ this.pendingRequests.splice(i, 1);
4021
+ req.resolve(message);
4022
+ return;
4023
+ }
4024
+ }
4025
+ this.messageQueue.push(message);
4026
+ }
4027
+ log(...args) {
4028
+ if (this.debug) {
4029
+ console.log("[SocketClient]", ...args);
4030
+ }
4031
+ }
4032
+ };
4033
+ async function createSocketClient(options = {}) {
4034
+ const client = new SocketClient(options);
4035
+ await client.connect();
4036
+ return client;
4037
+ }
4038
+
4039
+ // src/commands/socket/index.ts
4040
+ function formatMessage(msg, json) {
4041
+ if (json) {
4042
+ return JSON.stringify(msg, null, 2);
4043
+ }
4044
+ switch (msg.type) {
4045
+ case "lint:result": {
4046
+ const lines = [`${chalk6.cyan("lint:result")} ${msg.filePath}`];
4047
+ if (msg.issues.length === 0) {
4048
+ lines.push(chalk6.green(" No issues found"));
4049
+ } else {
4050
+ for (const issue of msg.issues) {
4051
+ const loc = issue.column ? `${issue.line}:${issue.column}` : `${issue.line}`;
4052
+ const rule = issue.ruleId ? chalk6.dim(` [${issue.ruleId}]`) : "";
4053
+ lines.push(` ${chalk6.yellow(loc)} ${issue.message}${rule}`);
4054
+ }
4055
+ }
4056
+ return lines.join("\n");
4057
+ }
4058
+ case "vision:status":
4059
+ if (msg.available) {
4060
+ return `${chalk6.cyan("vision:status")} ${chalk6.green("available")} (${msg.model})`;
4061
+ }
4062
+ return `${chalk6.cyan("vision:status")} ${chalk6.red("not available")}`;
4063
+ case "vision:result": {
4064
+ const lines = [
4065
+ `${chalk6.cyan("vision:result")} ${msg.route} (${msg.analysisTime}ms)`
4066
+ ];
4067
+ if (msg.error) {
4068
+ lines.push(chalk6.red(` Error: ${msg.error}`));
4069
+ } else if (msg.issues.length === 0) {
4070
+ lines.push(chalk6.green(" No issues found"));
4071
+ } else {
4072
+ for (const issue of msg.issues) {
4073
+ const severity = issue.severity === "error" ? chalk6.red("error") : issue.severity === "warning" ? chalk6.yellow("warn") : chalk6.blue("info");
4074
+ lines.push(` ${severity} [${issue.category}] ${issue.message}`);
4075
+ if (issue.dataLoc) {
4076
+ lines.push(chalk6.dim(` at ${issue.dataLoc}`));
4077
+ }
4078
+ }
4079
+ }
4080
+ return lines.join("\n");
4081
+ }
4082
+ case "workspace:info":
4083
+ return `${chalk6.cyan("workspace:info")}
4084
+ appRoot: ${msg.appRoot}
4085
+ workspaceRoot: ${msg.workspaceRoot}`;
4086
+ case "rules:metadata":
4087
+ return `${chalk6.cyan("rules:metadata")} ${msg.rules.length} rules available`;
4088
+ case "source:result":
4089
+ return `${chalk6.cyan("source:result")} ${msg.filePath} (${msg.totalLines} lines)`;
4090
+ case "source:error":
4091
+ return `${chalk6.cyan("source:error")} ${msg.filePath}: ${chalk6.red(msg.error)}`;
4092
+ case "coverage:result":
4093
+ return `${chalk6.cyan("coverage:result")} Coverage data received`;
4094
+ case "coverage:error":
4095
+ return `${chalk6.cyan("coverage:error")} ${chalk6.red(msg.error)}`;
4096
+ case "rule:config:result":
4097
+ if (msg.success) {
4098
+ return `${chalk6.cyan("rule:config:result")} ${msg.ruleId} -> ${msg.severity}`;
4099
+ }
4100
+ return `${chalk6.cyan("rule:config:result")} ${msg.ruleId} ${chalk6.red("failed")}: ${msg.error}`;
4101
+ case "duplicates:indexing:start":
4102
+ return `${chalk6.cyan("duplicates:indexing:start")}`;
4103
+ case "duplicates:indexing:progress":
4104
+ return `${chalk6.cyan("duplicates:indexing:progress")} ${msg.message}`;
4105
+ case "duplicates:indexing:complete":
4106
+ return `${chalk6.cyan("duplicates:indexing:complete")} ${msg.totalChunks} chunks in ${msg.duration}ms`;
4107
+ case "duplicates:indexing:error":
4108
+ return `${chalk6.cyan("duplicates:indexing:error")} ${chalk6.red(msg.error)}`;
4109
+ case "file:changed":
4110
+ return `${chalk6.cyan("file:changed")} ${msg.filePath}`;
4111
+ case "config:update":
4112
+ return `${chalk6.cyan("config:update")} ${msg.key} = ${JSON.stringify(msg.value)}`;
4113
+ default:
4114
+ return `${chalk6.cyan(msg.type)} ${JSON.stringify(msg)}`;
4115
+ }
4116
+ }
4117
+ async function runRepl(client, options) {
4118
+ const readline = await import("readline");
4119
+ const rl = readline.createInterface({
4120
+ input: process.stdin,
4121
+ output: process.stdout,
4122
+ prompt: chalk6.green("uilint> ")
4123
+ });
4124
+ console.log(chalk6.bold("\nUILint Socket REPL"));
4125
+ console.log("Type 'help' for available commands, 'exit' to quit.\n");
4126
+ client.onMessage((msg) => {
4127
+ console.log("\n" + formatMessage(msg, options.json));
4128
+ rl.prompt();
4129
+ });
4130
+ try {
4131
+ await client.waitForWorkspaceInfo(2e3);
4132
+ } catch {
4133
+ }
4134
+ rl.prompt();
4135
+ rl.on("line", async (line) => {
4136
+ const trimmed = line.trim();
4137
+ if (!trimmed) {
4138
+ rl.prompt();
4139
+ return;
4140
+ }
4141
+ const [cmd, ...args] = trimmed.split(/\s+/);
4142
+ try {
4143
+ switch (cmd) {
4144
+ case "help":
4145
+ console.log(`
4146
+ Available commands:
4147
+ lint:file <path> Lint a file
4148
+ lint:element <path> <dataLoc> Lint a specific element
4149
+ vision:check Check if vision is available
4150
+ vision:analyze <route> <manifestFile> [screenshotFile]
4151
+ Run vision analysis
4152
+ source:fetch <path> Fetch source code
4153
+ coverage:request Request coverage data
4154
+ rule:config <ruleId> <severity> [optionsJson]
4155
+ Set rule configuration
4156
+ config:set <key> <value> Set a config value
4157
+ subscribe <path> Subscribe to file changes
4158
+ invalidate [path] Invalidate cache
4159
+ messages Show queued messages
4160
+ clear Clear message queue
4161
+ json Toggle JSON output
4162
+ exit Exit REPL
4163
+ `);
4164
+ break;
4165
+ case "lint:file":
4166
+ if (!args[0]) {
4167
+ console.log(chalk6.red("Usage: lint:file <path>"));
4168
+ } else {
4169
+ const result = await client.lintFile(args[0], options.timeout);
4170
+ console.log(formatMessage(result, options.json));
4171
+ }
4172
+ break;
4173
+ case "lint:element":
4174
+ if (!args[0] || !args[1]) {
4175
+ console.log(chalk6.red("Usage: lint:element <path> <dataLoc>"));
4176
+ } else {
4177
+ const result = await client.lintElement(args[0], args[1], options.timeout);
4178
+ console.log(formatMessage(result, options.json));
4179
+ }
4180
+ break;
4181
+ case "vision:check": {
4182
+ const result = await client.visionCheck(options.timeout);
4183
+ console.log(formatMessage(result, options.json));
4184
+ break;
4185
+ }
4186
+ case "vision:analyze":
4187
+ if (!args[0] || !args[1]) {
4188
+ console.log(chalk6.red("Usage: vision:analyze <route> <manifestFile> [screenshotFile]"));
4189
+ } else {
4190
+ const manifestPath = args[1];
4191
+ if (!existsSync7(manifestPath)) {
4192
+ console.log(chalk6.red(`Manifest file not found: ${manifestPath}`));
4193
+ } else {
4194
+ const manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
4195
+ const params = { route: args[0], manifest };
4196
+ if (args[2]) {
4197
+ if (existsSync7(args[2])) {
4198
+ const imageBuffer = readFileSync3(args[2]);
4199
+ params.screenshot = imageBuffer.toString("base64");
4200
+ } else {
4201
+ console.log(chalk6.red(`Screenshot file not found: ${args[2]}`));
4202
+ break;
4203
+ }
4204
+ }
4205
+ const result = await client.visionAnalyze(params, options.timeout);
4206
+ console.log(formatMessage(result, options.json));
4207
+ }
4208
+ }
4209
+ break;
4210
+ case "source:fetch":
4211
+ if (!args[0]) {
4212
+ console.log(chalk6.red("Usage: source:fetch <path>"));
4213
+ } else {
4214
+ const result = await client.fetchSource(args[0], options.timeout);
4215
+ console.log(formatMessage(result, options.json));
4216
+ }
4217
+ break;
4218
+ case "coverage:request": {
4219
+ const result = await client.requestCoverage(options.timeout);
4220
+ console.log(formatMessage(result, options.json));
4221
+ break;
4222
+ }
4223
+ case "rule:config":
4224
+ if (!args[0] || !args[1]) {
4225
+ console.log(chalk6.red("Usage: rule:config <ruleId> <severity> [optionsJson]"));
4226
+ } else {
4227
+ const severity = args[1];
4228
+ const ruleOptions = args[2] ? JSON.parse(args[2]) : void 0;
4229
+ const result = await client.setRuleConfig(args[0], severity, ruleOptions, options.timeout);
4230
+ console.log(formatMessage(result, options.json));
4231
+ }
4232
+ break;
4233
+ case "config:set":
4234
+ if (!args[0] || !args[1]) {
4235
+ console.log(chalk6.red("Usage: config:set <key> <value>"));
4236
+ } else {
4237
+ let value = args[1];
4238
+ try {
4239
+ value = JSON.parse(args[1]);
4240
+ } catch {
4241
+ }
4242
+ client.setConfig(args[0], value);
4243
+ console.log(chalk6.green(`Set ${args[0]} = ${JSON.stringify(value)}`));
4244
+ }
4245
+ break;
4246
+ case "subscribe":
4247
+ if (!args[0]) {
4248
+ console.log(chalk6.red("Usage: subscribe <path>"));
4249
+ } else {
4250
+ client.subscribeFile(args[0]);
4251
+ console.log(chalk6.green(`Subscribed to ${args[0]}`));
4252
+ }
4253
+ break;
4254
+ case "invalidate":
4255
+ client.invalidateCache(args[0]);
4256
+ console.log(chalk6.green(args[0] ? `Invalidated ${args[0]}` : "Invalidated all cache"));
4257
+ break;
4258
+ case "messages": {
4259
+ const msgs = client.getMessages();
4260
+ if (msgs.length === 0) {
4261
+ console.log(chalk6.dim("No queued messages"));
4262
+ } else {
4263
+ for (const msg of msgs) {
4264
+ console.log(formatMessage(msg, options.json));
4265
+ }
4266
+ }
4267
+ break;
4268
+ }
4269
+ case "clear":
4270
+ client.clearMessages();
4271
+ console.log(chalk6.green("Message queue cleared"));
4272
+ break;
4273
+ case "json":
4274
+ options.json = !options.json;
4275
+ console.log(chalk6.green(`JSON output ${options.json ? "enabled" : "disabled"}`));
4276
+ break;
4277
+ case "exit":
4278
+ case "quit":
4279
+ client.disconnect();
4280
+ rl.close();
4281
+ process.exit(0);
4282
+ break;
4283
+ default:
4284
+ console.log(chalk6.red(`Unknown command: ${cmd}. Type 'help' for available commands.`));
4285
+ }
4286
+ } catch (err) {
4287
+ console.log(chalk6.red(`Error: ${err.message}`));
4288
+ }
4289
+ rl.prompt();
4290
+ });
4291
+ rl.on("close", () => {
4292
+ client.disconnect();
4293
+ process.exit(0);
4294
+ });
4295
+ }
4296
+ async function runListen(client, options, filter) {
4297
+ console.log(chalk6.bold("\nListening for messages..."));
4298
+ if (filter) {
4299
+ console.log(chalk6.dim(`Filter: ${filter}`));
4300
+ }
4301
+ console.log(chalk6.dim("Press Ctrl+C to stop.\n"));
4302
+ client.onMessage((msg) => {
4303
+ if (filter && !msg.type.includes(filter.replace("*", ""))) {
4304
+ return;
4305
+ }
4306
+ console.log(formatMessage(msg, options.json));
4307
+ });
4308
+ await new Promise(() => {
4309
+ });
4310
+ }
4311
+ 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");
4313
+ cmd.action(async (cmdOptions) => {
4314
+ const options = {
4315
+ port: parseInt(cmdOptions.port, 10),
4316
+ debug: cmdOptions.debug,
4317
+ json: cmdOptions.json,
4318
+ timeout: parseInt(cmdOptions.timeout, 10)
4319
+ };
4320
+ try {
4321
+ const client = await createSocketClient({ port: options.port, debug: options.debug });
4322
+ await runRepl(client, options);
4323
+ } catch (err) {
4324
+ console.error(chalk6.red(`Failed to connect: ${err.message}`));
4325
+ console.error(chalk6.dim(`Make sure the server is running: uilint serve -p ${options.port}`));
4326
+ process.exit(1);
4327
+ }
4328
+ });
4329
+ cmd.command("listen").description("Listen for all messages from the server").option("-f, --filter <pattern>", "Filter messages by type pattern (e.g., lint:*, vision:*)").action(async (subOptions, command) => {
4330
+ const parentOptions = command.parent?.opts() || {};
4331
+ const options = {
4332
+ port: parseInt(parentOptions.port || "9234", 10),
4333
+ debug: parentOptions.debug || false,
4334
+ json: parentOptions.json || false,
4335
+ timeout: parseInt(parentOptions.timeout || "30000", 10)
4336
+ };
4337
+ try {
4338
+ const client = await createSocketClient({ port: options.port, debug: options.debug });
4339
+ await runListen(client, options, subOptions.filter);
4340
+ } catch (err) {
4341
+ console.error(chalk6.red(`Failed to connect: ${err.message}`));
4342
+ process.exit(1);
4343
+ }
4344
+ });
4345
+ cmd.command("lint:file <path>").description("Lint a file and output results").action(async (filePath, _subOptions, command) => {
4346
+ const parentOptions = command.parent?.opts() || {};
4347
+ const options = {
4348
+ port: parseInt(parentOptions.port || "9234", 10),
4349
+ debug: parentOptions.debug || false,
4350
+ json: parentOptions.json || false,
4351
+ timeout: parseInt(parentOptions.timeout || "30000", 10)
4352
+ };
4353
+ try {
4354
+ const client = await createSocketClient({ port: options.port, debug: options.debug });
4355
+ await client.waitForWorkspaceInfo(2e3).catch(() => {
4356
+ });
4357
+ const result = await client.lintFile(filePath, options.timeout);
4358
+ console.log(formatMessage(result, options.json));
4359
+ client.disconnect();
4360
+ process.exit(result.issues.length > 0 ? 1 : 0);
4361
+ } catch (err) {
4362
+ console.error(chalk6.red(`Error: ${err.message}`));
4363
+ process.exit(1);
4364
+ }
4365
+ });
4366
+ cmd.command("vision:check").description("Check if vision analysis is available").action(async (_subOptions, command) => {
4367
+ const parentOptions = command.parent?.opts() || {};
4368
+ const options = {
4369
+ port: parseInt(parentOptions.port || "9234", 10),
4370
+ debug: parentOptions.debug || false,
4371
+ json: parentOptions.json || false,
4372
+ timeout: parseInt(parentOptions.timeout || "10000", 10)
4373
+ };
4374
+ try {
4375
+ const client = await createSocketClient({ port: options.port, debug: options.debug });
4376
+ const result = await client.visionCheck(options.timeout);
4377
+ console.log(formatMessage(result, options.json));
4378
+ client.disconnect();
4379
+ process.exit(result.available ? 0 : 1);
4380
+ } catch (err) {
4381
+ console.error(chalk6.red(`Error: ${err.message}`));
4382
+ process.exit(1);
4383
+ }
4384
+ });
4385
+ cmd.command("source:fetch <path>").description("Fetch source code for a file").action(async (filePath, _subOptions, command) => {
4386
+ const parentOptions = command.parent?.opts() || {};
4387
+ const options = {
4388
+ port: parseInt(parentOptions.port || "9234", 10),
4389
+ debug: parentOptions.debug || false,
4390
+ json: parentOptions.json || false,
4391
+ timeout: parseInt(parentOptions.timeout || "10000", 10)
4392
+ };
4393
+ try {
4394
+ const client = await createSocketClient({ port: options.port, debug: options.debug });
4395
+ await client.waitForWorkspaceInfo(2e3).catch(() => {
4396
+ });
4397
+ const result = await client.fetchSource(filePath, options.timeout);
4398
+ if (options.json) {
4399
+ console.log(JSON.stringify(result, null, 2));
4400
+ } else if (result.type === "source:result") {
4401
+ console.log(result.content);
4402
+ } else {
4403
+ console.error(chalk6.red(`Error: ${result.error}`));
4404
+ }
4405
+ client.disconnect();
4406
+ process.exit(result.type === "source:result" ? 0 : 1);
4407
+ } catch (err) {
4408
+ console.error(chalk6.red(`Error: ${err.message}`));
4409
+ process.exit(1);
4410
+ }
4411
+ });
4412
+ cmd.command("rules").description("List available rules").action(async (_subOptions, command) => {
4413
+ const parentOptions = command.parent?.opts() || {};
4414
+ const options = {
4415
+ port: parseInt(parentOptions.port || "9234", 10),
4416
+ debug: parentOptions.debug || false,
4417
+ json: parentOptions.json || false,
4418
+ timeout: parseInt(parentOptions.timeout || "10000", 10)
4419
+ };
4420
+ try {
4421
+ const client = await createSocketClient({ port: options.port, debug: options.debug });
4422
+ const metadata = await client.waitForRulesMetadata(options.timeout);
4423
+ if (options.json) {
4424
+ console.log(JSON.stringify(metadata.rules, null, 2));
4425
+ } else {
4426
+ console.log(chalk6.bold(`
4427
+ ${metadata.rules.length} rules available:
4428
+ `));
4429
+ for (const rule of metadata.rules) {
4430
+ const severity = rule.currentSeverity === "error" ? chalk6.red("error") : rule.currentSeverity === "warn" ? chalk6.yellow("warn") : chalk6.dim("off");
4431
+ console.log(` ${chalk6.cyan(rule.id)} [${severity}]`);
4432
+ console.log(chalk6.dim(` ${rule.description}`));
4433
+ }
4434
+ }
4435
+ client.disconnect();
4436
+ } catch (err) {
4437
+ console.error(chalk6.red(`Error: ${err.message}`));
4438
+ process.exit(1);
4439
+ }
4440
+ });
4441
+ return cmd;
4442
+ }
4443
+
4444
+ // src/index.ts
3726
4445
  function assertNodeVersion(minMajor, minMinor) {
3727
4446
  const ver = process.versions.node || "";
3728
4447
  const parts = ver.split(".");
@@ -3737,12 +4456,12 @@ function assertNodeVersion(minMajor, minMinor) {
3737
4456
  }
3738
4457
  }
3739
4458
  assertNodeVersion(20, 19);
3740
- var program = new Command6();
4459
+ var program = new Command7();
3741
4460
  function getCLIVersion() {
3742
4461
  try {
3743
4462
  const __dirname = dirname7(fileURLToPath(import.meta.url));
3744
4463
  const pkgPath = join5(__dirname, "..", "package.json");
3745
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
4464
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
3746
4465
  return pkg.version || "0.0.0";
3747
4466
  } catch {
3748
4467
  return "0.0.0";
@@ -3868,6 +4587,7 @@ program.command("config").description("Get or set UILint configuration options")
3868
4587
  });
3869
4588
  });
3870
4589
  program.addCommand(createDuplicatesCommand());
4590
+ program.addCommand(createSocketCommand());
3871
4591
  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) => {
3872
4592
  const { upgrade } = await import("./upgrade-TGYLZ4QX.js");
3873
4593
  await upgrade({