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/dist/cli.bundle.cjs +62 -13
- package/dist/cli.bundle.cjs.map +2 -2
- package/package.json +1 -1
- package/src/__tests__/cli-e2e.test.ts +64 -1
- package/src/cli.ts +72 -16
- package/src/default-tools.json +2 -2
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -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
|
|
108
|
-
if (defaultValue !== undefined) {
|
|
109
|
-
|
|
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
|
-
|
|
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
|
|
package/src/default-tools.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
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":
|
|
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.
|
|
7
|
+
export const VERSION = '0.55.0'; // x-release-please-version
|