uloop-cli 0.54.6 → 0.55.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uloop-cli",
3
- "version": "0.54.6",
3
+ "version": "0.55.0",
4
4
  "//version": "x-release-please-version",
5
5
  "description": "CLI tool for Unity Editor communication via uLoopMCP",
6
6
  "main": "dist/cli.bundle.cjs",
@@ -212,6 +212,24 @@ describe('CLI E2E Tests (requires running Unity)', () => {
212
212
  expect(typeof result.hierarchyFilePath).toBe('string');
213
213
  expect(result.hierarchyFilePath).toContain('hierarchy_');
214
214
  });
215
+
216
+ it('should support --include-components false to disable components', () => {
217
+ const result = runCliJson<{ hierarchyFilePath: string }>(
218
+ 'get-hierarchy --max-depth 1 --include-components false',
219
+ );
220
+
221
+ expect(typeof result.hierarchyFilePath).toBe('string');
222
+ expect(result.hierarchyFilePath).toContain('hierarchy_');
223
+ });
224
+
225
+ it('should support --include-inactive false to exclude inactive objects', () => {
226
+ const result = runCliJson<{ hierarchyFilePath: string }>(
227
+ 'get-hierarchy --max-depth 1 --include-inactive false',
228
+ );
229
+
230
+ expect(typeof result.hierarchyFilePath).toBe('string');
231
+ expect(result.hierarchyFilePath).toContain('hierarchy_');
232
+ });
215
233
  });
216
234
 
217
235
  describe('get-menu-items', () => {
@@ -257,6 +275,14 @@ describe('CLI E2E Tests (requires running Unity)', () => {
257
275
  const messages = logs.Logs.map((log) => log.Message);
258
276
  expect(messages.some((m) => m.includes('LogGetter test complete'))).toBe(true);
259
277
  });
278
+
279
+ it('should support --use-reflection-fallback false option', () => {
280
+ const result = runCliJson<{ Success: boolean }>(
281
+ `execute-menu-item --menu-item-path "${TEST_LOG_MENU_PATH}" --use-reflection-fallback false`,
282
+ );
283
+
284
+ expect(result.Success).toBe(true);
285
+ });
260
286
  });
261
287
 
262
288
  describe('unity-search', () => {
@@ -270,12 +296,23 @@ describe('CLI E2E Tests (requires running Unity)', () => {
270
296
  describe('find-game-objects', () => {
271
297
  it('should find game objects with name pattern', () => {
272
298
  const result = runCliJson<{ results: unknown[]; totalFound: number }>(
273
- 'find-game-objects --name-pattern "*" --include-inactive',
299
+ 'find-game-objects --name-pattern "*" --include-inactive true',
274
300
  );
275
301
 
276
302
  expect(Array.isArray(result.results)).toBe(true);
277
303
  expect(typeof result.totalFound).toBe('number');
278
304
  });
305
+
306
+ it('should find Cube game object with default array parameter', () => {
307
+ // This test verifies that default array values (e.g., RequiredComponents: [])
308
+ // are correctly handled and don't cause search to fail
309
+ const result = runCliJson<{ results: Array<{ name: string }>; totalFound: number }>(
310
+ 'find-game-objects --name-pattern "Cube"',
311
+ );
312
+
313
+ expect(result.totalFound).toBeGreaterThan(0);
314
+ expect(result.results.some((r) => r.name === 'Cube')).toBe(true);
315
+ });
279
316
  });
280
317
 
281
318
  describe('get-provider-details', () => {
@@ -284,6 +321,22 @@ describe('CLI E2E Tests (requires running Unity)', () => {
284
321
 
285
322
  expect(Array.isArray(result.Providers)).toBe(true);
286
323
  });
324
+
325
+ it('should support --include-descriptions false to exclude descriptions', () => {
326
+ const result = runCliJson<{ Providers: unknown[] }>(
327
+ 'get-provider-details --include-descriptions false',
328
+ );
329
+
330
+ expect(Array.isArray(result.Providers)).toBe(true);
331
+ });
332
+
333
+ it('should support --sort-by-priority false to disable priority sorting', () => {
334
+ const result = runCliJson<{ Providers: unknown[] }>(
335
+ 'get-provider-details --sort-by-priority false',
336
+ );
337
+
338
+ expect(Array.isArray(result.Providers)).toBe(true);
339
+ });
287
340
  });
288
341
 
289
342
  describe('--help', () => {
@@ -302,6 +355,16 @@ describe('CLI E2E Tests (requires running Unity)', () => {
302
355
  expect(exitCode).toBe(0);
303
356
  expect(stdout).toContain('--force-recompile');
304
357
  });
358
+
359
+ it('should display boolean options with value format in get-hierarchy help', () => {
360
+ const { stdout, exitCode } = runCli('get-hierarchy --help');
361
+
362
+ expect(exitCode).toBe(0);
363
+ // Boolean options should show <value> format
364
+ expect(stdout).toContain('--include-components <value>');
365
+ expect(stdout).toContain('--include-inactive <value>');
366
+ expect(stdout).toContain('(default: "true")');
367
+ });
305
368
  });
306
369
 
307
370
  describe('--version', () => {
package/src/cli.ts CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  GlobalOptions,
20
20
  syncTools,
21
21
  } from './execute-tool.js';
22
- import { loadToolsCache, ToolDefinition, ToolProperty } from './tool-cache.js';
22
+ import { loadToolsCache, hasCacheFile, ToolDefinition, ToolProperty } from './tool-cache.js';
23
23
  import { pascalToKebabCase } from './arg-parser.js';
24
24
  import { registerSkillsCommand } from './skills/skills-command.js';
25
25
  import { VERSION } from './version.js';
@@ -87,12 +87,6 @@ program
87
87
  // Register skills subcommand
88
88
  registerSkillsCommand(program);
89
89
 
90
- // Load tools from cache and register commands dynamically
91
- const toolsCache = loadToolsCache();
92
- for (const tool of toolsCache.tools) {
93
- registerToolCommand(tool);
94
- }
95
-
96
90
  /**
97
91
  * Register a tool as a CLI command dynamically.
98
92
  */
@@ -104,9 +98,11 @@ function registerToolCommand(tool: ToolDefinition): void {
104
98
  for (const [propName, propInfo] of Object.entries(properties)) {
105
99
  const optionStr = generateOptionString(propName, propInfo);
106
100
  const description = buildOptionDescription(propInfo);
107
- const defaultValue = propInfo.default as string | boolean | undefined;
108
- if (defaultValue !== undefined) {
109
- cmd.option(optionStr, description, defaultValue);
101
+ const defaultValue = propInfo.default;
102
+ if (defaultValue !== undefined && defaultValue !== null) {
103
+ // Convert default values to strings for consistent CLI handling
104
+ const defaultStr = convertDefaultToString(defaultValue);
105
+ cmd.option(optionStr, description, defaultStr);
110
106
  } else {
111
107
  cmd.option(optionStr, description);
112
108
  }
@@ -131,17 +127,26 @@ function registerToolCommand(tool: ToolDefinition): void {
131
127
  });
132
128
  }
133
129
 
130
+ /**
131
+ * Convert default value to string for CLI option registration.
132
+ */
133
+ function convertDefaultToString(value: unknown): string {
134
+ if (typeof value === 'string') {
135
+ return value;
136
+ }
137
+ if (typeof value === 'boolean' || typeof value === 'number') {
138
+ return String(value);
139
+ }
140
+ return JSON.stringify(value);
141
+ }
142
+
134
143
  /**
135
144
  * Generate commander.js option string from property info.
145
+ * All types use value format (--option <value>) for consistency with MCP.
136
146
  */
137
147
  function generateOptionString(propName: string, propInfo: ToolProperty): string {
138
148
  const kebabName = pascalToKebabCase(propName);
139
- const lowerType = propInfo.type.toLowerCase();
140
-
141
- if (lowerType === 'boolean') {
142
- return `--${kebabName}`;
143
- }
144
-
149
+ void propInfo; // All types now use value format
145
150
  return `--${kebabName} <value>`;
146
151
  }
147
152
 
@@ -184,7 +189,30 @@ function buildParams(
184
189
  function convertValue(value: unknown, propInfo: ToolProperty): unknown {
185
190
  const lowerType = propInfo.type.toLowerCase();
186
191
 
192
+ if (lowerType === 'boolean' && typeof value === 'string') {
193
+ const lower = value.toLowerCase();
194
+ if (lower === 'true') {
195
+ return true;
196
+ }
197
+ if (lower === 'false') {
198
+ return false;
199
+ }
200
+ throw new Error(`Invalid boolean value: ${value}. Use 'true' or 'false'.`);
201
+ }
202
+
187
203
  if (lowerType === 'array' && typeof value === 'string') {
204
+ // Handle JSON array format (e.g., "[]" or "[\"item1\",\"item2\"]")
205
+ if (value.startsWith('[') && value.endsWith(']')) {
206
+ try {
207
+ const parsed: unknown = JSON.parse(value);
208
+ if (Array.isArray(parsed)) {
209
+ return parsed;
210
+ }
211
+ } catch {
212
+ // Fall through to comma-separated handling
213
+ }
214
+ }
215
+ // Handle comma-separated format (e.g., "item1,item2")
188
216
  return value.split(',').map((s) => s.trim());
189
217
  }
190
218
 
@@ -624,6 +652,34 @@ async function main(): Promise<void> {
624
652
  return;
625
653
  }
626
654
 
655
+ // Check if cache version is outdated and auto-sync if needed
656
+ const cachedVersion = loadToolsCache().version;
657
+ if (hasCacheFile() && cachedVersion !== VERSION) {
658
+ console.log(
659
+ `\x1b[33mCache outdated (${cachedVersion} → ${VERSION}). Syncing tools from Unity...\x1b[0m`,
660
+ );
661
+ try {
662
+ await syncTools({});
663
+ console.log('\x1b[32m✓ Tools synced successfully.\x1b[0m\n');
664
+ } catch (error) {
665
+ const message = error instanceof Error ? error.message : String(error);
666
+ if (isConnectionError(message)) {
667
+ console.error('\x1b[33mWarning: Failed to sync tools. Using cached definitions.\x1b[0m');
668
+ console.error("\x1b[33mRun 'uloop sync' manually when Unity is available.\x1b[0m\n");
669
+ } else {
670
+ console.error('\x1b[33mWarning: Failed to sync tools. Using cached definitions.\x1b[0m');
671
+ console.error(`\x1b[33mError: ${message}\x1b[0m`);
672
+ console.error("\x1b[33mRun 'uloop sync' manually when Unity is available.\x1b[0m\n");
673
+ }
674
+ }
675
+ }
676
+
677
+ // Register tool commands from cache (after potential auto-sync)
678
+ const toolsCache = loadToolsCache();
679
+ for (const tool of toolsCache.tools) {
680
+ registerToolCommand(tool);
681
+ }
682
+
627
683
  const args = process.argv.slice(2);
628
684
  const cmdName = args.find((arg) => !arg.startsWith('-'));
629
685
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.54.6",
2
+ "version": "0.55.0",
3
3
  "tools": [
4
4
  {
5
5
  "name": "compile",
@@ -43,7 +43,7 @@
43
43
  "IncludeStackTrace": {
44
44
  "type": "boolean",
45
45
  "description": "Include stack trace in output",
46
- "default": true
46
+ "default": false
47
47
  },
48
48
  "UseRegex": {
49
49
  "type": "boolean",
package/src/version.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * This file exists to avoid bundling the entire package.json into the CLI bundle.
5
5
  * This version is automatically updated by release-please.
6
6
  */
7
- export const VERSION = '0.54.6'; // x-release-please-version
7
+ export const VERSION = '0.55.0'; // x-release-please-version