uloop-cli 0.44.2 → 0.45.2

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.
@@ -3453,8 +3453,8 @@ var require_commander = __commonJS({
3453
3453
  });
3454
3454
 
3455
3455
  // src/cli.ts
3456
- var import_fs4 = require("fs");
3457
- var import_path5 = require("path");
3456
+ var import_fs5 = require("fs");
3457
+ var import_path6 = require("path");
3458
3458
  var import_os2 = require("os");
3459
3459
  var import_child_process = require("child_process");
3460
3460
 
@@ -3475,6 +3475,11 @@ var {
3475
3475
  Help
3476
3476
  } = import_index.default;
3477
3477
 
3478
+ // src/execute-tool.ts
3479
+ var readline = __toESM(require("readline"), 1);
3480
+ var import_fs3 = require("fs");
3481
+ var import_path4 = require("path");
3482
+
3478
3483
  // src/direct-unity-client.ts
3479
3484
  var net = __toESM(require("net"), 1);
3480
3485
 
@@ -3685,7 +3690,7 @@ var import_path3 = require("path");
3685
3690
 
3686
3691
  // src/default-tools.json
3687
3692
  var default_tools_default = {
3688
- version: "0.44.2",
3693
+ version: "0.45.2",
3689
3694
  tools: [
3690
3695
  {
3691
3696
  name: "compile",
@@ -4082,9 +4087,96 @@ function getCacheFilePath() {
4082
4087
  }
4083
4088
 
4084
4089
  // src/version.ts
4085
- var VERSION = "0.44.2";
4090
+ var VERSION = "0.45.2";
4091
+
4092
+ // src/spinner.ts
4093
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4094
+ var FRAME_INTERVAL_MS = 80;
4095
+ function createSpinner(initialMessage) {
4096
+ if (!process.stderr.isTTY) {
4097
+ return {
4098
+ update: () => {
4099
+ },
4100
+ stop: () => {
4101
+ }
4102
+ };
4103
+ }
4104
+ let frameIndex = 0;
4105
+ let currentMessage = initialMessage;
4106
+ const render = () => {
4107
+ const frame = SPINNER_FRAMES[frameIndex];
4108
+ process.stderr.write(`\r\x1B[K${frame} ${currentMessage}`);
4109
+ frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
4110
+ };
4111
+ render();
4112
+ const intervalId = setInterval(render, FRAME_INTERVAL_MS);
4113
+ return {
4114
+ update(message) {
4115
+ currentMessage = message;
4116
+ },
4117
+ stop() {
4118
+ clearInterval(intervalId);
4119
+ process.stderr.write("\r\x1B[K");
4120
+ }
4121
+ };
4122
+ }
4086
4123
 
4087
4124
  // src/execute-tool.ts
4125
+ function suppressStdinEcho() {
4126
+ if (!process.stdin.isTTY) {
4127
+ return () => {
4128
+ };
4129
+ }
4130
+ const rl = readline.createInterface({
4131
+ input: process.stdin,
4132
+ output: process.stdout,
4133
+ terminal: false
4134
+ });
4135
+ process.stdin.setRawMode(true);
4136
+ process.stdin.resume();
4137
+ const onData = (data) => {
4138
+ if (data[0] === 3) {
4139
+ process.exit(130);
4140
+ }
4141
+ };
4142
+ process.stdin.on("data", onData);
4143
+ return () => {
4144
+ process.stdin.off("data", onData);
4145
+ process.stdin.setRawMode(false);
4146
+ process.stdin.pause();
4147
+ rl.close();
4148
+ };
4149
+ }
4150
+ var RETRY_DELAY_MS = 500;
4151
+ var MAX_RETRIES = 3;
4152
+ function sleep(ms) {
4153
+ return new Promise((resolve) => setTimeout(resolve, ms));
4154
+ }
4155
+ function isRetryableError(error) {
4156
+ if (!(error instanceof Error)) {
4157
+ return false;
4158
+ }
4159
+ const message = error.message;
4160
+ return message.includes("ECONNREFUSED") || message === "UNITY_NO_RESPONSE";
4161
+ }
4162
+ function checkUnityBusyState() {
4163
+ const projectRoot = findUnityProjectRoot();
4164
+ if (projectRoot === null) {
4165
+ return;
4166
+ }
4167
+ const compilingLock = (0, import_path4.join)(projectRoot, "Temp", "compiling.lock");
4168
+ if ((0, import_fs3.existsSync)(compilingLock)) {
4169
+ throw new Error("UNITY_COMPILING");
4170
+ }
4171
+ const domainReloadLock = (0, import_path4.join)(projectRoot, "Temp", "domainreload.lock");
4172
+ if ((0, import_fs3.existsSync)(domainReloadLock)) {
4173
+ throw new Error("UNITY_DOMAIN_RELOAD");
4174
+ }
4175
+ const serverStartingLock = (0, import_path4.join)(projectRoot, "Temp", "serverstarting.lock");
4176
+ if ((0, import_fs3.existsSync)(serverStartingLock)) {
4177
+ throw new Error("UNITY_SERVER_STARTING");
4178
+ }
4179
+ }
4088
4180
  async function executeToolCommand(toolName, params, globalOptions) {
4089
4181
  let portNumber;
4090
4182
  if (globalOptions.port) {
@@ -4095,14 +4187,38 @@ async function executeToolCommand(toolName, params, globalOptions) {
4095
4187
  portNumber = parsed;
4096
4188
  }
4097
4189
  const port = await resolveUnityPort(portNumber);
4098
- const client = new DirectUnityClient(port);
4099
- try {
4100
- await client.connect();
4101
- const result = await client.sendRequest(toolName, params);
4102
- console.log(JSON.stringify(result, null, 2));
4103
- } finally {
4104
- client.disconnect();
4190
+ const restoreStdin = suppressStdinEcho();
4191
+ const spinner = createSpinner("Connecting to Unity...");
4192
+ let lastError;
4193
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
4194
+ checkUnityBusyState();
4195
+ const client = new DirectUnityClient(port);
4196
+ try {
4197
+ await client.connect();
4198
+ spinner.update(`Executing ${toolName}...`);
4199
+ const result = await client.sendRequest(toolName, params);
4200
+ if (result === void 0 || result === null) {
4201
+ throw new Error("UNITY_NO_RESPONSE");
4202
+ }
4203
+ spinner.stop();
4204
+ restoreStdin();
4205
+ console.log(JSON.stringify(result, null, 2));
4206
+ return;
4207
+ } catch (error) {
4208
+ lastError = error;
4209
+ client.disconnect();
4210
+ if (!isRetryableError(error) || attempt >= MAX_RETRIES) {
4211
+ break;
4212
+ }
4213
+ spinner.update("Retrying connection...");
4214
+ await sleep(RETRY_DELAY_MS);
4215
+ } finally {
4216
+ client.disconnect();
4217
+ }
4105
4218
  }
4219
+ spinner.stop();
4220
+ restoreStdin();
4221
+ throw lastError;
4106
4222
  }
4107
4223
  async function listAvailableTools(globalOptions) {
4108
4224
  let portNumber;
@@ -4114,19 +4230,40 @@ async function listAvailableTools(globalOptions) {
4114
4230
  portNumber = parsed;
4115
4231
  }
4116
4232
  const port = await resolveUnityPort(portNumber);
4117
- const client = new DirectUnityClient(port);
4118
- try {
4119
- await client.connect();
4120
- const result = await client.sendRequest("get-tool-details", { IncludeDevelopmentOnly: false });
4121
- if (!result.Tools || !Array.isArray(result.Tools)) {
4122
- throw new Error("Unexpected response from Unity: missing Tools array");
4123
- }
4124
- for (const tool of result.Tools) {
4125
- console.log(` - ${tool.name}`);
4233
+ const restoreStdin = suppressStdinEcho();
4234
+ const spinner = createSpinner("Connecting to Unity...");
4235
+ let lastError;
4236
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
4237
+ checkUnityBusyState();
4238
+ const client = new DirectUnityClient(port);
4239
+ try {
4240
+ await client.connect();
4241
+ spinner.update("Fetching tool list...");
4242
+ const result = await client.sendRequest("get-tool-details", { IncludeDevelopmentOnly: false });
4243
+ if (!result.Tools || !Array.isArray(result.Tools)) {
4244
+ throw new Error("Unexpected response from Unity: missing Tools array");
4245
+ }
4246
+ spinner.stop();
4247
+ restoreStdin();
4248
+ for (const tool of result.Tools) {
4249
+ console.log(` - ${tool.name}`);
4250
+ }
4251
+ return;
4252
+ } catch (error) {
4253
+ lastError = error;
4254
+ client.disconnect();
4255
+ if (!isRetryableError(error) || attempt >= MAX_RETRIES) {
4256
+ break;
4257
+ }
4258
+ spinner.update("Retrying connection...");
4259
+ await sleep(RETRY_DELAY_MS);
4260
+ } finally {
4261
+ client.disconnect();
4126
4262
  }
4127
- } finally {
4128
- client.disconnect();
4129
4263
  }
4264
+ spinner.stop();
4265
+ restoreStdin();
4266
+ throw lastError;
4130
4267
  }
4131
4268
  function convertProperties(unityProps) {
4132
4269
  const result = {};
@@ -4150,35 +4287,57 @@ async function syncTools(globalOptions) {
4150
4287
  portNumber = parsed;
4151
4288
  }
4152
4289
  const port = await resolveUnityPort(portNumber);
4153
- const client = new DirectUnityClient(port);
4154
- try {
4155
- await client.connect();
4156
- const result = await client.sendRequest("get-tool-details", { IncludeDevelopmentOnly: false });
4157
- if (!result.Tools || !Array.isArray(result.Tools)) {
4158
- throw new Error("Unexpected response from Unity: missing Tools array");
4159
- }
4160
- const cache = {
4161
- version: VERSION,
4162
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4163
- tools: result.Tools.map((tool) => ({
4164
- name: tool.name,
4165
- description: tool.description,
4166
- inputSchema: {
4167
- type: "object",
4168
- properties: convertProperties(tool.parameterSchema.Properties),
4169
- required: tool.parameterSchema.Required
4170
- }
4171
- }))
4172
- };
4173
- saveToolsCache(cache);
4174
- console.log(`Synced ${cache.tools.length} tools to ${getCacheFilePath()}`);
4175
- console.log("\nTools:");
4176
- for (const tool of cache.tools) {
4177
- console.log(` - ${tool.name}`);
4290
+ const restoreStdin = suppressStdinEcho();
4291
+ const spinner = createSpinner("Connecting to Unity...");
4292
+ let lastError;
4293
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
4294
+ checkUnityBusyState();
4295
+ const client = new DirectUnityClient(port);
4296
+ try {
4297
+ await client.connect();
4298
+ spinner.update("Syncing tools...");
4299
+ const result = await client.sendRequest("get-tool-details", { IncludeDevelopmentOnly: false });
4300
+ spinner.stop();
4301
+ if (!result.Tools || !Array.isArray(result.Tools)) {
4302
+ restoreStdin();
4303
+ throw new Error("Unexpected response from Unity: missing Tools array");
4304
+ }
4305
+ const cache = {
4306
+ version: VERSION,
4307
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4308
+ tools: result.Tools.map((tool) => ({
4309
+ name: tool.name,
4310
+ description: tool.description,
4311
+ inputSchema: {
4312
+ type: "object",
4313
+ properties: convertProperties(tool.parameterSchema.Properties),
4314
+ required: tool.parameterSchema.Required
4315
+ }
4316
+ }))
4317
+ };
4318
+ saveToolsCache(cache);
4319
+ console.log(`Synced ${cache.tools.length} tools to ${getCacheFilePath()}`);
4320
+ console.log("\nTools:");
4321
+ for (const tool of cache.tools) {
4322
+ console.log(` - ${tool.name}`);
4323
+ }
4324
+ restoreStdin();
4325
+ return;
4326
+ } catch (error) {
4327
+ lastError = error;
4328
+ client.disconnect();
4329
+ if (!isRetryableError(error) || attempt >= MAX_RETRIES) {
4330
+ break;
4331
+ }
4332
+ spinner.update("Retrying connection...");
4333
+ await sleep(RETRY_DELAY_MS);
4334
+ } finally {
4335
+ client.disconnect();
4178
4336
  }
4179
- } finally {
4180
- client.disconnect();
4181
4337
  }
4338
+ spinner.stop();
4339
+ restoreStdin();
4340
+ throw lastError;
4182
4341
  }
4183
4342
 
4184
4343
  // src/arg-parser.ts
@@ -4188,12 +4347,12 @@ function pascalToKebabCase(pascal) {
4188
4347
  }
4189
4348
 
4190
4349
  // src/skills/skills-manager.ts
4191
- var import_fs3 = require("fs");
4192
- var import_path4 = require("path");
4350
+ var import_fs4 = require("fs");
4351
+ var import_path5 = require("path");
4193
4352
  var import_os = require("os");
4194
4353
 
4195
4354
  // src/skills/skill-definitions/uloop-compile/SKILL.md
4196
- var SKILL_default = "---\nname: uloop-compile\ndescription: Compile Unity project via uloop CLI. Use when you need to: (1) Verify C# code compiles successfully after editing scripts, (2) Check for compile errors or warnings, (3) Validate script changes before running tests.\n---\n\n# uloop compile\n\nExecute Unity project compilation.\n\n## Usage\n\n```bash\nuloop compile [--force-recompile]\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--force-recompile` | boolean | Force full recompilation (triggers Domain Reload) |\n\n## Examples\n\n```bash\n# Check compilation\nuloop compile\n\n# Force full recompilation\nuloop compile --force-recompile\n```\n\n## Output\n\nReturns JSON:\n- `Success`: boolean\n- `ErrorCount`: number\n- `WarningCount`: number\n";
4355
+ var SKILL_default = '---\nname: uloop-compile\ndescription: Compile Unity project via uloop CLI. Use when you need to: (1) Verify C# code compiles successfully after editing scripts, (2) Check for compile errors or warnings, (3) Validate script changes before running tests.\n---\n\n# uloop compile\n\nExecute Unity project compilation.\n\n## Usage\n\n```bash\nuloop compile [--force-recompile]\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--force-recompile` | boolean | Force full recompilation (triggers Domain Reload) |\n\n## Examples\n\n```bash\n# Check compilation\nuloop compile\n\n# Force full recompilation\nuloop compile --force-recompile\n```\n\n## Output\n\nReturns JSON:\n- `Success`: boolean\n- `ErrorCount`: number\n- `WarningCount`: number\n\n## Troubleshooting\n\nIf CLI hangs or shows "Unity is busy" errors after compilation, stale lock files may be preventing connection. Run the following to clean them up:\n\n```bash\nuloop fix\n```\n\nThis removes any leftover lock files (`compiling.lock`, `domainreload.lock`, `serverstarting.lock`) from the Unity project\'s Temp directory.\n';
4197
4356
 
4198
4357
  // src/skills/skill-definitions/uloop-get-logs/SKILL.md
4199
4358
  var SKILL_default2 = '---\nname: uloop-get-logs\ndescription: Retrieve Unity Console logs via uloop CLI. Use when you need to: (1) Check for errors or warnings after operations, (2) Debug runtime issues in Unity Editor, (3) Investigate unexpected behavior or exceptions.\n---\n\n# uloop get-logs\n\nRetrieve logs from Unity Console.\n\n## Usage\n\n```bash\nuloop get-logs [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--log-type` | string | `All` | Log type filter: `Error`, `Warning`, `Log`, `All` |\n| `--max-count` | integer | `100` | Maximum number of logs to retrieve |\n| `--search-text` | string | - | Text to search within logs |\n| `--include-stack-trace` | boolean | `true` | Include stack trace in output |\n| `--use-regex` | boolean | `false` | Use regex for search |\n| `--search-in-stack-trace` | boolean | `false` | Search within stack trace |\n\n## Examples\n\n```bash\n# Get all logs\nuloop get-logs\n\n# Get only errors\nuloop get-logs --log-type Error\n\n# Search for specific text\nuloop get-logs --search-text "NullReference"\n\n# Regex search\nuloop get-logs --search-text "Missing.*Component" --use-regex\n```\n\n## Output\n\nReturns JSON array of log entries with message, type, and optional stack trace.\n';
@@ -4269,69 +4428,69 @@ var BUNDLED_SKILLS = [
4269
4428
  ];
4270
4429
 
4271
4430
  // src/skills/skills-manager.ts
4272
- function getGlobalSkillsDir() {
4273
- return (0, import_path4.join)((0, import_os.homedir)(), ".claude", "skills");
4431
+ function getGlobalSkillsDir(target) {
4432
+ return (0, import_path5.join)((0, import_os.homedir)(), target.projectDir, "skills");
4274
4433
  }
4275
- function getProjectSkillsDir() {
4276
- return (0, import_path4.join)(process.cwd(), ".claude", "skills");
4434
+ function getProjectSkillsDir(target) {
4435
+ return (0, import_path5.join)(process.cwd(), target.projectDir, "skills");
4277
4436
  }
4278
- function getSkillPath(skillDirName, global) {
4279
- const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
4280
- return (0, import_path4.join)(baseDir, skillDirName, "SKILL.md");
4437
+ function getSkillPath(skillDirName, target, global) {
4438
+ const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
4439
+ return (0, import_path5.join)(baseDir, skillDirName, target.skillFileName);
4281
4440
  }
4282
- function isSkillInstalled(skill, global) {
4283
- const skillPath = getSkillPath(skill.dirName, global);
4284
- return (0, import_fs3.existsSync)(skillPath);
4441
+ function isSkillInstalled(skill, target, global) {
4442
+ const skillPath = getSkillPath(skill.dirName, target, global);
4443
+ return (0, import_fs4.existsSync)(skillPath);
4285
4444
  }
4286
- function isSkillOutdated(skill, global) {
4287
- const skillPath = getSkillPath(skill.dirName, global);
4288
- if (!(0, import_fs3.existsSync)(skillPath)) {
4445
+ function isSkillOutdated(skill, target, global) {
4446
+ const skillPath = getSkillPath(skill.dirName, target, global);
4447
+ if (!(0, import_fs4.existsSync)(skillPath)) {
4289
4448
  return false;
4290
4449
  }
4291
- const installedContent = (0, import_fs3.readFileSync)(skillPath, "utf-8");
4450
+ const installedContent = (0, import_fs4.readFileSync)(skillPath, "utf-8");
4292
4451
  return installedContent !== skill.content;
4293
4452
  }
4294
- function getSkillStatus(skill, global) {
4295
- if (!isSkillInstalled(skill, global)) {
4453
+ function getSkillStatus(skill, target, global) {
4454
+ if (!isSkillInstalled(skill, target, global)) {
4296
4455
  return "not_installed";
4297
4456
  }
4298
- if (isSkillOutdated(skill, global)) {
4457
+ if (isSkillOutdated(skill, target, global)) {
4299
4458
  return "outdated";
4300
4459
  }
4301
4460
  return "installed";
4302
4461
  }
4303
- function getAllSkillStatuses(global) {
4462
+ function getAllSkillStatuses(target, global) {
4304
4463
  return BUNDLED_SKILLS.map((skill) => ({
4305
4464
  name: skill.name,
4306
- status: getSkillStatus(skill, global),
4307
- path: isSkillInstalled(skill, global) ? getSkillPath(skill.dirName, global) : void 0
4465
+ status: getSkillStatus(skill, target, global),
4466
+ path: isSkillInstalled(skill, target, global) ? getSkillPath(skill.dirName, target, global) : void 0
4308
4467
  }));
4309
4468
  }
4310
- function installSkill(skill, global) {
4311
- const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
4312
- const skillDir = (0, import_path4.join)(baseDir, skill.dirName);
4313
- const skillPath = (0, import_path4.join)(skillDir, "SKILL.md");
4314
- (0, import_fs3.mkdirSync)(skillDir, { recursive: true });
4315
- (0, import_fs3.writeFileSync)(skillPath, skill.content, "utf-8");
4469
+ function installSkill(skill, target, global) {
4470
+ const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
4471
+ const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
4472
+ const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
4473
+ (0, import_fs4.mkdirSync)(skillDir, { recursive: true });
4474
+ (0, import_fs4.writeFileSync)(skillPath, skill.content, "utf-8");
4316
4475
  }
4317
- function uninstallSkill(skill, global) {
4318
- const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
4319
- const skillDir = (0, import_path4.join)(baseDir, skill.dirName);
4320
- if (!(0, import_fs3.existsSync)(skillDir)) {
4476
+ function uninstallSkill(skill, target, global) {
4477
+ const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
4478
+ const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
4479
+ if (!(0, import_fs4.existsSync)(skillDir)) {
4321
4480
  return false;
4322
4481
  }
4323
- (0, import_fs3.rmSync)(skillDir, { recursive: true, force: true });
4482
+ (0, import_fs4.rmSync)(skillDir, { recursive: true, force: true });
4324
4483
  return true;
4325
4484
  }
4326
- function installAllSkills(global) {
4485
+ function installAllSkills(target, global) {
4327
4486
  const result = { installed: 0, updated: 0, skipped: 0 };
4328
4487
  for (const skill of BUNDLED_SKILLS) {
4329
- const status = getSkillStatus(skill, global);
4488
+ const status = getSkillStatus(skill, target, global);
4330
4489
  if (status === "not_installed") {
4331
- installSkill(skill, global);
4490
+ installSkill(skill, target, global);
4332
4491
  result.installed++;
4333
4492
  } else if (status === "outdated") {
4334
- installSkill(skill, global);
4493
+ installSkill(skill, target, global);
4335
4494
  result.updated++;
4336
4495
  } else {
4337
4496
  result.skipped++;
@@ -4339,10 +4498,10 @@ function installAllSkills(global) {
4339
4498
  }
4340
4499
  return result;
4341
4500
  }
4342
- function uninstallAllSkills(global) {
4501
+ function uninstallAllSkills(target, global) {
4343
4502
  const result = { removed: 0, notFound: 0 };
4344
4503
  for (const skill of BUNDLED_SKILLS) {
4345
- if (uninstallSkill(skill, global)) {
4504
+ if (uninstallSkill(skill, target, global)) {
4346
4505
  result.removed++;
4347
4506
  } else {
4348
4507
  result.notFound++;
@@ -4350,41 +4509,103 @@ function uninstallAllSkills(global) {
4350
4509
  }
4351
4510
  return result;
4352
4511
  }
4353
- function getInstallDir(global) {
4354
- return global ? getGlobalSkillsDir() : getProjectSkillsDir();
4512
+ function getInstallDir(target, global) {
4513
+ return global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
4355
4514
  }
4356
4515
  function getTotalSkillCount() {
4357
4516
  return BUNDLED_SKILLS.length;
4358
4517
  }
4359
4518
 
4519
+ // src/skills/target-config.ts
4520
+ var TARGET_CONFIGS = {
4521
+ claude: {
4522
+ id: "claude",
4523
+ displayName: "Claude Code",
4524
+ projectDir: ".claude",
4525
+ skillFileName: "SKILL.md"
4526
+ },
4527
+ codex: {
4528
+ id: "codex",
4529
+ displayName: "Codex CLI",
4530
+ projectDir: ".codex",
4531
+ skillFileName: "SKILL.md"
4532
+ }
4533
+ };
4534
+ var ALL_TARGET_IDS = ["claude", "codex"];
4535
+ function getTargetConfig(id) {
4536
+ return TARGET_CONFIGS[id];
4537
+ }
4538
+
4360
4539
  // src/skills/skills-command.ts
4361
4540
  function registerSkillsCommand(program3) {
4362
- const skillsCmd = program3.command("skills").description("Manage uloop skills for Claude Code");
4363
- skillsCmd.command("list").description("List all uloop skills and their installation status").option("-g, --global", "Check global installation (~/.claude/skills/)").action((options) => {
4364
- listSkills(options.global ?? false);
4541
+ const skillsCmd = program3.command("skills").description("Manage uloop skills for AI coding tools");
4542
+ skillsCmd.command("list").description("List all uloop skills and their installation status").option("-g, --global", "Check global installation").option("--claude", "Check Claude Code installation").option("--codex", "Check Codex CLI installation").action((options) => {
4543
+ const targets = resolveTargets(options);
4544
+ const global = options.global ?? false;
4545
+ listSkills(targets, global);
4365
4546
  });
4366
- skillsCmd.command("install").description("Install all uloop skills").option("-g, --global", "Install to global location (~/.claude/skills/)").action((options) => {
4367
- installSkills(options.global ?? false);
4547
+ skillsCmd.command("install").description("Install all uloop skills").option("-g, --global", "Install to global location").option("--claude", "Install to Claude Code").option("--codex", "Install to Codex CLI").action((options) => {
4548
+ const targets = resolveTargets(options);
4549
+ if (targets.length === 0) {
4550
+ showTargetGuidance("install");
4551
+ return;
4552
+ }
4553
+ installSkills(targets, options.global ?? false);
4368
4554
  });
4369
- skillsCmd.command("uninstall").description("Uninstall all uloop skills").option("-g, --global", "Uninstall from global location (~/.claude/skills/)").action((options) => {
4370
- uninstallSkills(options.global ?? false);
4555
+ skillsCmd.command("uninstall").description("Uninstall all uloop skills").option("-g, --global", "Uninstall from global location").option("--claude", "Uninstall from Claude Code").option("--codex", "Uninstall from Codex CLI").action((options) => {
4556
+ const targets = resolveTargets(options);
4557
+ if (targets.length === 0) {
4558
+ showTargetGuidance("uninstall");
4559
+ return;
4560
+ }
4561
+ uninstallSkills(targets, options.global ?? false);
4371
4562
  });
4372
4563
  }
4373
- function listSkills(global) {
4564
+ function resolveTargets(options) {
4565
+ const targets = [];
4566
+ if (options.claude) {
4567
+ targets.push(getTargetConfig("claude"));
4568
+ }
4569
+ if (options.codex) {
4570
+ targets.push(getTargetConfig("codex"));
4571
+ }
4572
+ return targets;
4573
+ }
4574
+ function showTargetGuidance(command) {
4575
+ console.log(`
4576
+ Please specify at least one target for '${command}':`);
4577
+ console.log("");
4578
+ console.log("Available targets:");
4579
+ console.log(" --claude Claude Code (.claude/skills/)");
4580
+ console.log(" --codex Codex CLI (.codex/skills/)");
4581
+ console.log("");
4582
+ console.log("Options:");
4583
+ console.log(" -g, --global Use global location (~/.claude/ or ~/.codex/)");
4584
+ console.log("");
4585
+ console.log("Examples:");
4586
+ console.log(` uloop skills ${command} --claude`);
4587
+ console.log(` uloop skills ${command} --codex --global`);
4588
+ console.log(` uloop skills ${command} --claude --codex`);
4589
+ }
4590
+ function listSkills(targets, global) {
4374
4591
  const location = global ? "Global" : "Project";
4375
- const dir = getInstallDir(global);
4592
+ const targetConfigs = targets.length > 0 ? targets : ALL_TARGET_IDS.map(getTargetConfig);
4376
4593
  console.log(`
4377
- uloop Skills Status (${location}):`);
4378
- console.log(`Location: ${dir}`);
4379
- console.log("=".repeat(50));
4594
+ uloop Skills Status:`);
4380
4595
  console.log("");
4381
- const statuses = getAllSkillStatuses(global);
4382
- for (const skill of statuses) {
4383
- const icon = getStatusIcon(skill.status);
4384
- const statusText = getStatusText(skill.status);
4385
- console.log(` ${icon} ${skill.name} (${statusText})`);
4596
+ for (const target of targetConfigs) {
4597
+ const dir = getInstallDir(target, global);
4598
+ console.log(`${target.displayName} (${location}):`);
4599
+ console.log(`Location: ${dir}`);
4600
+ console.log("=".repeat(50));
4601
+ const statuses = getAllSkillStatuses(target, global);
4602
+ for (const skill of statuses) {
4603
+ const icon = getStatusIcon(skill.status);
4604
+ const statusText = getStatusText(skill.status);
4605
+ console.log(` ${icon} ${skill.name} (${statusText})`);
4606
+ }
4607
+ console.log("");
4386
4608
  }
4387
- console.log("");
4388
4609
  console.log(`Total: ${getTotalSkillCount()} bundled skills`);
4389
4610
  }
4390
4611
  function getStatusIcon(status) {
@@ -4411,34 +4632,40 @@ function getStatusText(status) {
4411
4632
  return "unknown";
4412
4633
  }
4413
4634
  }
4414
- function installSkills(global) {
4635
+ function installSkills(targets, global) {
4415
4636
  const location = global ? "global" : "project";
4416
- const dir = getInstallDir(global);
4417
4637
  console.log(`
4418
4638
  Installing uloop skills (${location})...`);
4419
4639
  console.log("");
4420
- const result = installAllSkills(global);
4421
- console.log(`\x1B[32m\u2713\x1B[0m Installed: ${result.installed}`);
4422
- console.log(`\x1B[33m\u2191\x1B[0m Updated: ${result.updated}`);
4423
- console.log(`\x1B[90m-\x1B[0m Skipped (up-to-date): ${result.skipped}`);
4424
- console.log("");
4425
- console.log(`Skills installed to ${dir}`);
4640
+ for (const target of targets) {
4641
+ const dir = getInstallDir(target, global);
4642
+ const result = installAllSkills(target, global);
4643
+ console.log(`${target.displayName}:`);
4644
+ console.log(` \x1B[32m\u2713\x1B[0m Installed: ${result.installed}`);
4645
+ console.log(` \x1B[33m\u2191\x1B[0m Updated: ${result.updated}`);
4646
+ console.log(` \x1B[90m-\x1B[0m Skipped (up-to-date): ${result.skipped}`);
4647
+ console.log(` Location: ${dir}`);
4648
+ console.log("");
4649
+ }
4426
4650
  }
4427
- function uninstallSkills(global) {
4651
+ function uninstallSkills(targets, global) {
4428
4652
  const location = global ? "global" : "project";
4429
- const dir = getInstallDir(global);
4430
4653
  console.log(`
4431
4654
  Uninstalling uloop skills (${location})...`);
4432
4655
  console.log("");
4433
- const result = uninstallAllSkills(global);
4434
- console.log(`\x1B[31m\u2717\x1B[0m Removed: ${result.removed}`);
4435
- console.log(`\x1B[90m-\x1B[0m Not found: ${result.notFound}`);
4436
- console.log("");
4437
- console.log(`Skills removed from ${dir}`);
4656
+ for (const target of targets) {
4657
+ const dir = getInstallDir(target, global);
4658
+ const result = uninstallAllSkills(target, global);
4659
+ console.log(`${target.displayName}:`);
4660
+ console.log(` \x1B[31m\u2717\x1B[0m Removed: ${result.removed}`);
4661
+ console.log(` \x1B[90m-\x1B[0m Not found: ${result.notFound}`);
4662
+ console.log(` Location: ${dir}`);
4663
+ console.log("");
4664
+ }
4438
4665
  }
4439
4666
 
4440
4667
  // src/cli.ts
4441
- var BUILTIN_COMMANDS = ["list", "sync", "completion", "update", "skills"];
4668
+ var BUILTIN_COMMANDS = ["list", "sync", "completion", "update", "fix", "skills"];
4442
4669
  var program2 = new Command();
4443
4670
  program2.name("uloop").description("Unity MCP CLI - Direct communication with Unity Editor").version(VERSION, "-v, --version", "Output the version number");
4444
4671
  program2.option("--list-commands", "List all command names (for shell completion)");
@@ -4455,6 +4682,9 @@ program2.command("completion").description("Setup shell completion").option("--i
4455
4682
  program2.command("update").description("Update uloop CLI to the latest version").action(() => {
4456
4683
  updateCli();
4457
4684
  });
4685
+ program2.command("fix").description("Clean up stale lock files that may prevent CLI from connecting").action(() => {
4686
+ cleanupLockFiles();
4687
+ });
4458
4688
  registerSkillsCommand(program2);
4459
4689
  var toolsCache = loadToolsCache();
4460
4690
  for (const tool of toolsCache.tools) {
@@ -4538,27 +4768,34 @@ function extractGlobalOptions(options) {
4538
4768
  port: options["port"]
4539
4769
  };
4540
4770
  }
4541
- function isDomainReloadLockFilePresent() {
4542
- const projectRoot = findUnityProjectRoot();
4543
- if (projectRoot === null) {
4544
- return false;
4545
- }
4546
- const lockPath = (0, import_path5.join)(projectRoot, "Temp", "domainreload.lock");
4547
- return (0, import_fs4.existsSync)(lockPath);
4548
- }
4549
4771
  async function runWithErrorHandling(fn) {
4550
4772
  try {
4551
4773
  await fn();
4552
4774
  } catch (error) {
4553
4775
  const message = error instanceof Error ? error.message : String(error);
4776
+ if (message === "UNITY_COMPILING") {
4777
+ console.error("\x1B[33m\u23F3 Unity is compiling scripts.\x1B[0m");
4778
+ console.error("Please wait for compilation to finish and try again.");
4779
+ process.exit(1);
4780
+ }
4781
+ if (message === "UNITY_DOMAIN_RELOAD") {
4782
+ console.error("\x1B[33m\u23F3 Unity is reloading (Domain Reload in progress).\x1B[0m");
4783
+ console.error("Please wait a moment and try again.");
4784
+ process.exit(1);
4785
+ }
4786
+ if (message === "UNITY_SERVER_STARTING") {
4787
+ console.error("\x1B[33m\u23F3 Unity server is starting.\x1B[0m");
4788
+ console.error("Please wait a moment and try again.");
4789
+ process.exit(1);
4790
+ }
4791
+ if (message === "UNITY_NO_RESPONSE") {
4792
+ console.error("\x1B[33m\u23F3 Unity is busy (no response received).\x1B[0m");
4793
+ console.error("Unity may be compiling, reloading, or starting. Please wait and try again.");
4794
+ process.exit(1);
4795
+ }
4554
4796
  if (message.includes("ECONNREFUSED")) {
4555
- if (isDomainReloadLockFilePresent()) {
4556
- console.error("\x1B[33m\u23F3 Unity is reloading (Domain Reload in progress).\x1B[0m");
4557
- console.error("Please wait a moment and try again.");
4558
- } else {
4559
- console.error("\x1B[31mError: Cannot connect to Unity.\x1B[0m");
4560
- console.error("Make sure Unity is running with uLoopMCP installed.");
4561
- }
4797
+ console.error("\x1B[31mError: Cannot connect to Unity.\x1B[0m");
4798
+ console.error("Make sure Unity is running with uLoopMCP installed.");
4562
4799
  process.exit(1);
4563
4800
  }
4564
4801
  console.error(`\x1B[31mError: ${message}\x1B[0m`);
@@ -4567,7 +4804,7 @@ async function runWithErrorHandling(fn) {
4567
4804
  }
4568
4805
  function detectShell() {
4569
4806
  const shell = process.env["SHELL"] || "";
4570
- const shellName = (0, import_path5.basename)(shell).replace(/\.exe$/i, "");
4807
+ const shellName = (0, import_path6.basename)(shell).replace(/\.exe$/i, "");
4571
4808
  if (shellName === "zsh") {
4572
4809
  return "zsh";
4573
4810
  }
@@ -4582,12 +4819,12 @@ function detectShell() {
4582
4819
  function getShellConfigPath(shell) {
4583
4820
  const home = (0, import_os2.homedir)();
4584
4821
  if (shell === "zsh") {
4585
- return (0, import_path5.join)(home, ".zshrc");
4822
+ return (0, import_path6.join)(home, ".zshrc");
4586
4823
  }
4587
4824
  if (shell === "powershell") {
4588
- return (0, import_path5.join)(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
4825
+ return (0, import_path6.join)(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
4589
4826
  }
4590
- return (0, import_path5.join)(home, ".bashrc");
4827
+ return (0, import_path6.join)(home, ".bashrc");
4591
4828
  }
4592
4829
  function getCompletionScript(shell) {
4593
4830
  if (shell === "bash") {
@@ -4663,6 +4900,30 @@ function updateCli() {
4663
4900
  process.exit(1);
4664
4901
  });
4665
4902
  }
4903
+ var LOCK_FILES = ["compiling.lock", "domainreload.lock", "serverstarting.lock"];
4904
+ function cleanupLockFiles() {
4905
+ const projectRoot = findUnityProjectRoot();
4906
+ if (projectRoot === null) {
4907
+ console.error("Could not find Unity project root.");
4908
+ process.exit(1);
4909
+ }
4910
+ const tempDir = (0, import_path6.join)(projectRoot, "Temp");
4911
+ let cleaned = 0;
4912
+ for (const lockFile of LOCK_FILES) {
4913
+ const lockPath = (0, import_path6.join)(tempDir, lockFile);
4914
+ if ((0, import_fs5.existsSync)(lockPath)) {
4915
+ (0, import_fs5.unlinkSync)(lockPath);
4916
+ console.log(`Removed: ${lockFile}`);
4917
+ cleaned++;
4918
+ }
4919
+ }
4920
+ if (cleaned === 0) {
4921
+ console.log("No lock files found.");
4922
+ } else {
4923
+ console.log(`
4924
+ \u2705 Cleaned up ${cleaned} lock file(s).`);
4925
+ }
4926
+ }
4666
4927
  function handleCompletion(install, shellOverride) {
4667
4928
  let shell;
4668
4929
  if (shellOverride) {
@@ -4686,13 +4947,13 @@ function handleCompletion(install, shellOverride) {
4686
4947
  return;
4687
4948
  }
4688
4949
  const configPath = getShellConfigPath(shell);
4689
- const configDir = (0, import_path5.dirname)(configPath);
4690
- if (!(0, import_fs4.existsSync)(configDir)) {
4691
- (0, import_fs4.mkdirSync)(configDir, { recursive: true });
4950
+ const configDir = (0, import_path6.dirname)(configPath);
4951
+ if (!(0, import_fs5.existsSync)(configDir)) {
4952
+ (0, import_fs5.mkdirSync)(configDir, { recursive: true });
4692
4953
  }
4693
4954
  let content = "";
4694
- if ((0, import_fs4.existsSync)(configPath)) {
4695
- content = (0, import_fs4.readFileSync)(configPath, "utf-8");
4955
+ if ((0, import_fs5.existsSync)(configPath)) {
4956
+ content = (0, import_fs5.readFileSync)(configPath, "utf-8");
4696
4957
  content = content.replace(
4697
4958
  /\n?# >>> uloop completion >>>[\s\S]*?# <<< uloop completion <<<\n?/g,
4698
4959
  ""
@@ -4706,7 +4967,7 @@ ${startMarker}
4706
4967
  ${script}
4707
4968
  ${endMarker}
4708
4969
  `;
4709
- (0, import_fs4.writeFileSync)(configPath, content + lineToAdd, "utf-8");
4970
+ (0, import_fs5.writeFileSync)(configPath, content + lineToAdd, "utf-8");
4710
4971
  } else {
4711
4972
  const evalLine = `eval "$(uloop completion --shell ${shell})"`;
4712
4973
  const lineToAdd = `
@@ -4714,7 +4975,7 @@ ${startMarker}
4714
4975
  ${evalLine}
4715
4976
  ${endMarker}
4716
4977
  `;
4717
- (0, import_fs4.writeFileSync)(configPath, content + lineToAdd, "utf-8");
4978
+ (0, import_fs5.writeFileSync)(configPath, content + lineToAdd, "utf-8");
4718
4979
  }
4719
4980
  console.log(`Completion installed to ${configPath}`);
4720
4981
  if (shell === "powershell") {