uloop-cli 0.68.2 → 0.69.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/README.md +87 -18
- package/README_ja.md +87 -18
- package/dist/cli.bundle.cjs +333 -137
- package/dist/cli.bundle.cjs.map +4 -4
- package/knip.json +4 -0
- package/package.json +4 -3
- package/src/__tests__/cli-e2e.test.ts +46 -4
- package/src/__tests__/execute-tool.test.ts +10 -0
- package/src/__tests__/port-resolver.test.ts +68 -0
- package/src/__tests__/project-validator.test.ts +107 -0
- package/src/arg-parser.ts +0 -118
- package/src/cli.ts +121 -14
- package/src/commands/focus-window.ts +54 -49
- package/src/compile-helpers.ts +5 -5
- package/src/default-tools.json +29 -1
- package/src/direct-unity-client.ts +2 -2
- package/src/execute-tool.ts +26 -0
- package/src/port-resolver.ts +39 -18
- package/src/project-root.ts +1 -1
- package/src/project-validator.ts +70 -0
- package/src/simple-framer.ts +2 -2
- package/src/skills/deprecated-skills.ts +1 -1
- package/src/skills/skills-manager.ts +9 -13
- package/src/skills/target-config.ts +1 -1
- package/src/spinner.ts +1 -1
- package/src/tool-cache.ts +18 -5
- package/src/version.ts +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project validation for CLI.
|
|
3
|
+
* Verifies that the connected Unity instance belongs to the expected project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Paths come from trusted Unity responses and validated project root, console.error for user warnings
|
|
7
|
+
/* eslint-disable security/detect-non-literal-fs-filename, no-console */
|
|
8
|
+
|
|
9
|
+
import assert from 'node:assert';
|
|
10
|
+
import { realpath } from 'fs/promises';
|
|
11
|
+
import { dirname } from 'path';
|
|
12
|
+
import { DirectUnityClient } from './direct-unity-client.js';
|
|
13
|
+
|
|
14
|
+
export class ProjectMismatchError extends Error {
|
|
15
|
+
constructor(
|
|
16
|
+
public readonly expectedProjectRoot: string,
|
|
17
|
+
public readonly connectedProjectRoot: string,
|
|
18
|
+
) {
|
|
19
|
+
super('PROJECT_MISMATCH');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface GetVersionResponse {
|
|
24
|
+
DataPath: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const JSON_RPC_METHOD_NOT_FOUND = -32601;
|
|
28
|
+
|
|
29
|
+
async function normalizePath(path: string): Promise<string> {
|
|
30
|
+
const resolved = await realpath(path);
|
|
31
|
+
return resolved.replace(/\/+$/, '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function validateConnectedProject(
|
|
35
|
+
client: DirectUnityClient,
|
|
36
|
+
expectedProjectRoot: string,
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
assert(client.isConnected(), 'client must be connected before validation');
|
|
39
|
+
|
|
40
|
+
let response: GetVersionResponse;
|
|
41
|
+
try {
|
|
42
|
+
response = await client.sendRequest<GetVersionResponse>('get-version', {});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// Method not found: old uLoopMCP version without get-version tool
|
|
45
|
+
if (
|
|
46
|
+
error instanceof Error &&
|
|
47
|
+
(error.message.includes(`${JSON_RPC_METHOD_NOT_FOUND}`) ||
|
|
48
|
+
/method not found/i.test(error.message))
|
|
49
|
+
) {
|
|
50
|
+
console.error(
|
|
51
|
+
'Warning: Could not verify project identity (get-version not available). Consider updating uLoopMCP package.',
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof response?.DataPath !== 'string' || response.DataPath.length === 0) {
|
|
59
|
+
console.error('Warning: Could not verify project identity (invalid get-version response).');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const connectedProjectRoot = dirname(response.DataPath);
|
|
64
|
+
const normalizedExpected = await normalizePath(expectedProjectRoot);
|
|
65
|
+
const normalizedConnected = await normalizePath(connectedProjectRoot);
|
|
66
|
+
|
|
67
|
+
if (normalizedExpected !== normalizedConnected) {
|
|
68
|
+
throw new ProjectMismatchError(normalizedExpected, normalizedConnected);
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/simple-framer.ts
CHANGED
|
@@ -11,7 +11,7 @@ export function createFrame(jsonContent: string): string {
|
|
|
11
11
|
return `${CONTENT_LENGTH_HEADER} ${contentLength}${HEADER_SEPARATOR}${jsonContent}`;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
interface FrameParseResult {
|
|
15
15
|
contentLength: number;
|
|
16
16
|
headerLength: number;
|
|
17
17
|
isComplete: boolean;
|
|
@@ -43,7 +43,7 @@ export function parseFrameFromBuffer(data: Buffer): FrameParseResult {
|
|
|
43
43
|
return { contentLength, headerLength, isComplete };
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
interface FrameExtractionResult {
|
|
47
47
|
jsonContent: string | null;
|
|
48
48
|
remainingData: Buffer;
|
|
49
49
|
}
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const DEPRECATED_SKILLS: string[] = [
|
|
8
8
|
'uloop-capture-window', // renamed to uloop-screenshot in v0.54.0
|
|
9
|
-
'uloop-get-provider-details', //
|
|
9
|
+
'uloop-get-provider-details', // renamed to uloop-get-unity-search-providers
|
|
10
10
|
];
|
|
@@ -15,16 +15,16 @@ import { findUnityProjectRoot, getUnityProjectStatus } from '../project-root.js'
|
|
|
15
15
|
import { DEPRECATED_SKILLS } from './deprecated-skills.js';
|
|
16
16
|
import { loadDisabledTools } from '../tool-settings-loader.js';
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
type SkillStatus = 'installed' | 'not_installed' | 'outdated';
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
interface SkillInfo {
|
|
21
21
|
name: string;
|
|
22
22
|
status: SkillStatus;
|
|
23
23
|
path?: string;
|
|
24
24
|
source?: 'bundled' | 'project';
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
interface SkillDefinition {
|
|
28
28
|
name: string;
|
|
29
29
|
toolName?: string;
|
|
30
30
|
dirName: string;
|
|
@@ -132,7 +132,7 @@ function isSkillOutdated(skill: SkillDefinition, target: TargetConfig, global: b
|
|
|
132
132
|
return false;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
function getSkillStatus(
|
|
136
136
|
skill: SkillDefinition,
|
|
137
137
|
target: TargetConfig,
|
|
138
138
|
global: boolean,
|
|
@@ -290,7 +290,7 @@ function findEditorFolders(basePath: string, maxDepth: number = 2): string[] {
|
|
|
290
290
|
return editorFolders;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
|
|
293
|
+
function collectProjectSkills(excludedRoots: string[] = []): SkillDefinition[] {
|
|
294
294
|
const projectRoot = findUnityProjectRoot();
|
|
295
295
|
if (!projectRoot) {
|
|
296
296
|
return [];
|
|
@@ -342,7 +342,7 @@ export function getAllSkillStatuses(target: TargetConfig, global: boolean): Skil
|
|
|
342
342
|
}));
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
|
|
345
|
+
function installSkill(skill: SkillDefinition, target: TargetConfig, global: boolean): void {
|
|
346
346
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
347
347
|
const skillDir = join(baseDir, skill.dirName);
|
|
348
348
|
const skillPath = join(skillDir, target.skillFileName);
|
|
@@ -360,11 +360,7 @@ export function installSkill(skill: SkillDefinition, target: TargetConfig, globa
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
skill: SkillDefinition,
|
|
365
|
-
target: TargetConfig,
|
|
366
|
-
global: boolean,
|
|
367
|
-
): boolean {
|
|
363
|
+
function uninstallSkill(skill: SkillDefinition, target: TargetConfig, global: boolean): boolean {
|
|
368
364
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
369
365
|
const skillDir = join(baseDir, skill.dirName);
|
|
370
366
|
|
|
@@ -376,7 +372,7 @@ export function uninstallSkill(
|
|
|
376
372
|
return true;
|
|
377
373
|
}
|
|
378
374
|
|
|
379
|
-
|
|
375
|
+
interface InstallResult {
|
|
380
376
|
installed: number;
|
|
381
377
|
updated: number;
|
|
382
378
|
skipped: number;
|
|
@@ -448,7 +444,7 @@ function isSkillDisabledByToolSettings(skill: SkillDefinition, disabledTools: st
|
|
|
448
444
|
return disabledTools.includes(toolName);
|
|
449
445
|
}
|
|
450
446
|
|
|
451
|
-
|
|
447
|
+
interface UninstallResult {
|
|
452
448
|
removed: number;
|
|
453
449
|
notFound: number;
|
|
454
450
|
}
|
package/src/spinner.ts
CHANGED
package/src/tool-cache.ts
CHANGED
|
@@ -41,7 +41,10 @@ export interface ToolsCache {
|
|
|
41
41
|
const CACHE_DIR = '.uloop';
|
|
42
42
|
const CACHE_FILE = 'tools.json';
|
|
43
43
|
|
|
44
|
-
function getCacheDir(): string {
|
|
44
|
+
function getCacheDir(projectPath?: string): string {
|
|
45
|
+
if (projectPath !== undefined) {
|
|
46
|
+
return join(projectPath, CACHE_DIR);
|
|
47
|
+
}
|
|
45
48
|
const projectRoot = findUnityProjectRoot();
|
|
46
49
|
if (projectRoot === null) {
|
|
47
50
|
return join(process.cwd(), CACHE_DIR);
|
|
@@ -49,8 +52,8 @@ function getCacheDir(): string {
|
|
|
49
52
|
return join(projectRoot, CACHE_DIR);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
function getCachePath(): string {
|
|
53
|
-
return join(getCacheDir(), CACHE_FILE);
|
|
55
|
+
function getCachePath(projectPath?: string): string {
|
|
56
|
+
return join(getCacheDir(projectPath), CACHE_FILE);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/**
|
|
@@ -62,9 +65,10 @@ export function getDefaultTools(): ToolsCache {
|
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
67
|
* Load tools from cache file, falling back to default tools if cache doesn't exist.
|
|
68
|
+
* When projectPath is specified, reads cache from that project directory.
|
|
65
69
|
*/
|
|
66
|
-
export function loadToolsCache(): ToolsCache {
|
|
67
|
-
const cachePath = getCachePath();
|
|
70
|
+
export function loadToolsCache(projectPath?: string): ToolsCache {
|
|
71
|
+
const cachePath = getCachePath(projectPath);
|
|
68
72
|
|
|
69
73
|
if (existsSync(cachePath)) {
|
|
70
74
|
try {
|
|
@@ -107,6 +111,15 @@ export function getCacheFilePath(): string {
|
|
|
107
111
|
return getCachePath();
|
|
108
112
|
}
|
|
109
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Get the set of default tool names bundled with npm package.
|
|
116
|
+
* Used to distinguish built-in tools from third-party tools.
|
|
117
|
+
*/
|
|
118
|
+
export function getDefaultToolNames(): ReadonlySet<string> {
|
|
119
|
+
const defaultTools: ToolsCache = getDefaultTools();
|
|
120
|
+
return new Set(defaultTools.tools.map((tool: ToolDefinition) => tool.name));
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
/**
|
|
111
124
|
* Get the Unity server version from cache file.
|
|
112
125
|
* Returns undefined if cache doesn't exist, is corrupted, or serverVersion is missing.
|
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.69.0'; // x-release-please-version
|