uloop-cli 0.48.3 → 0.49.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 +211 -22
- package/dist/cli.bundle.cjs.map +3 -3
- package/jest.config.cjs +2 -1
- package/md-transformer.cjs +11 -0
- package/package.json +1 -1
- package/scripts/generate-bundled-skills.ts +87 -31
- package/src/__tests__/cli-e2e.test.ts +76 -10
- package/src/__tests__/skills-manager.test.ts +158 -0
- package/src/cli.ts +44 -2
- package/src/default-tools.json +1 -1
- package/src/skills/bundled-skills.ts +19 -16
- package/src/skills/skill-definitions/cli-only/uloop-get-version/SKILL.md +37 -0
- package/src/skills/skills-manager.ts +232 -9
- package/src/version.ts +1 -1
- package/CLAUDE.md +0 -61
- package/src/skills/skill-definitions/uloop-capture-unity-window/SKILL.md +0 -81
- package/src/skills/skill-definitions/uloop-clear-console/SKILL.md +0 -34
- package/src/skills/skill-definitions/uloop-compile/SKILL.md +0 -47
- package/src/skills/skill-definitions/uloop-control-play-mode/SKILL.md +0 -48
- package/src/skills/skill-definitions/uloop-execute-dynamic-code/SKILL.md +0 -79
- package/src/skills/skill-definitions/uloop-execute-menu-item/SKILL.md +0 -43
- package/src/skills/skill-definitions/uloop-find-game-objects/SKILL.md +0 -46
- package/src/skills/skill-definitions/uloop-focus-window/SKILL.md +0 -34
- package/src/skills/skill-definitions/uloop-get-hierarchy/SKILL.md +0 -44
- package/src/skills/skill-definitions/uloop-get-logs/SKILL.md +0 -45
- package/src/skills/skill-definitions/uloop-get-menu-items/SKILL.md +0 -44
- package/src/skills/skill-definitions/uloop-get-provider-details/SKILL.md +0 -45
- package/src/skills/skill-definitions/uloop-get-version/SKILL.md +0 -32
- package/src/skills/skill-definitions/uloop-run-tests/SKILL.md +0 -43
- package/src/skills/skill-definitions/uloop-unity-search/SKILL.md +0 -44
- /package/src/skills/skill-definitions/{uloop-get-project-info → cli-only/uloop-get-project-info}/SKILL.md +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Skills manager for installing/uninstalling/listing uloop skills.
|
|
3
|
+
* Supports both bundled skills and project-local skills.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
// File paths are constructed from home directory and skill names, not from untrusted user input
|
|
6
7
|
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
7
8
|
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, readdirSync } from 'fs';
|
|
9
10
|
import { join } from 'path';
|
|
10
11
|
import { homedir } from 'os';
|
|
11
12
|
import { BUNDLED_SKILLS, BundledSkill } from './bundled-skills.js';
|
|
@@ -17,8 +18,18 @@ export interface SkillInfo {
|
|
|
17
18
|
name: string;
|
|
18
19
|
status: SkillStatus;
|
|
19
20
|
path?: string;
|
|
21
|
+
source?: 'bundled' | 'project';
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
export interface ProjectSkill {
|
|
25
|
+
name: string;
|
|
26
|
+
dirName: string;
|
|
27
|
+
content: string;
|
|
28
|
+
sourcePath: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const EXCLUDED_DIRS = new Set(['node_modules', '.git', 'Temp', 'obj', 'Build', 'Builds', 'Logs']);
|
|
32
|
+
|
|
22
33
|
function getGlobalSkillsDir(target: TargetConfig): string {
|
|
23
34
|
return join(homedir(), target.projectDir, 'skills');
|
|
24
35
|
}
|
|
@@ -32,12 +43,20 @@ function getSkillPath(skillDirName: string, target: TargetConfig, global: boolea
|
|
|
32
43
|
return join(baseDir, skillDirName, target.skillFileName);
|
|
33
44
|
}
|
|
34
45
|
|
|
35
|
-
function isSkillInstalled(
|
|
46
|
+
function isSkillInstalled(
|
|
47
|
+
skill: BundledSkill | ProjectSkill,
|
|
48
|
+
target: TargetConfig,
|
|
49
|
+
global: boolean,
|
|
50
|
+
): boolean {
|
|
36
51
|
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
37
52
|
return existsSync(skillPath);
|
|
38
53
|
}
|
|
39
54
|
|
|
40
|
-
function isSkillOutdated(
|
|
55
|
+
function isSkillOutdated(
|
|
56
|
+
skill: BundledSkill | ProjectSkill,
|
|
57
|
+
target: TargetConfig,
|
|
58
|
+
global: boolean,
|
|
59
|
+
): boolean {
|
|
41
60
|
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
42
61
|
if (!existsSync(skillPath)) {
|
|
43
62
|
return false;
|
|
@@ -47,7 +66,7 @@ function isSkillOutdated(skill: BundledSkill, target: TargetConfig, global: bool
|
|
|
47
66
|
}
|
|
48
67
|
|
|
49
68
|
export function getSkillStatus(
|
|
50
|
-
skill: BundledSkill,
|
|
69
|
+
skill: BundledSkill | ProjectSkill,
|
|
51
70
|
target: TargetConfig,
|
|
52
71
|
global: boolean,
|
|
53
72
|
): SkillStatus {
|
|
@@ -60,17 +79,170 @@ export function getSkillStatus(
|
|
|
60
79
|
return 'installed';
|
|
61
80
|
}
|
|
62
81
|
|
|
82
|
+
export function parseFrontmatter(content: string): Record<string, string | boolean> {
|
|
83
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
84
|
+
if (!frontmatterMatch) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const frontmatter: Record<string, string | boolean> = {};
|
|
89
|
+
const lines = frontmatterMatch[1].split('\n');
|
|
90
|
+
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const colonIndex = line.indexOf(':');
|
|
93
|
+
if (colonIndex === -1) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const key = line.slice(0, colonIndex).trim();
|
|
98
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
99
|
+
|
|
100
|
+
if (value === 'true') {
|
|
101
|
+
frontmatter[key] = true;
|
|
102
|
+
} else if (value === 'false') {
|
|
103
|
+
frontmatter[key] = false;
|
|
104
|
+
} else {
|
|
105
|
+
frontmatter[key] = value;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return frontmatter;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function scanEditorFolderForSkills(editorPath: string, skills: ProjectSkill[]): void {
|
|
113
|
+
if (!existsSync(editorPath)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const entries = readdirSync(editorPath, { withFileTypes: true });
|
|
118
|
+
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
if (EXCLUDED_DIRS.has(entry.name)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const fullPath = join(editorPath, entry.name);
|
|
125
|
+
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
const skillMdPath = join(fullPath, 'SKILL.md');
|
|
128
|
+
if (existsSync(skillMdPath)) {
|
|
129
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
130
|
+
const frontmatter = parseFrontmatter(content);
|
|
131
|
+
|
|
132
|
+
if (frontmatter.internal === true) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const name = typeof frontmatter.name === 'string' ? frontmatter.name : entry.name;
|
|
137
|
+
|
|
138
|
+
skills.push({
|
|
139
|
+
name,
|
|
140
|
+
dirName: name,
|
|
141
|
+
content,
|
|
142
|
+
sourcePath: skillMdPath,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
scanEditorFolderForSkills(fullPath, skills);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function findEditorFolders(basePath: string, maxDepth: number = 2): string[] {
|
|
152
|
+
const editorFolders: string[] = [];
|
|
153
|
+
|
|
154
|
+
function scan(currentPath: string, depth: number): void {
|
|
155
|
+
if (depth > maxDepth || !existsSync(currentPath)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
160
|
+
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
if (!entry.isDirectory() || EXCLUDED_DIRS.has(entry.name)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const fullPath = join(currentPath, entry.name);
|
|
167
|
+
|
|
168
|
+
if (entry.name === 'Editor') {
|
|
169
|
+
editorFolders.push(fullPath);
|
|
170
|
+
} else {
|
|
171
|
+
scan(fullPath, depth + 1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
scan(basePath, 0);
|
|
177
|
+
return editorFolders;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function collectProjectSkills(): ProjectSkill[] {
|
|
181
|
+
const projectRoot = process.cwd();
|
|
182
|
+
const skills: ProjectSkill[] = [];
|
|
183
|
+
const seenNames = new Set<string>();
|
|
184
|
+
|
|
185
|
+
const searchPaths = [
|
|
186
|
+
join(projectRoot, 'Assets'),
|
|
187
|
+
join(projectRoot, 'Packages'),
|
|
188
|
+
join(projectRoot, 'Library', 'PackageCache'),
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
for (const searchPath of searchPaths) {
|
|
192
|
+
if (!existsSync(searchPath)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const editorFolders = findEditorFolders(searchPath, 3);
|
|
197
|
+
|
|
198
|
+
for (const editorFolder of editorFolders) {
|
|
199
|
+
scanEditorFolderForSkills(editorFolder, skills);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const uniqueSkills: ProjectSkill[] = [];
|
|
204
|
+
for (const skill of skills) {
|
|
205
|
+
if (!seenNames.has(skill.name)) {
|
|
206
|
+
seenNames.add(skill.name);
|
|
207
|
+
uniqueSkills.push(skill);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return uniqueSkills;
|
|
212
|
+
}
|
|
213
|
+
|
|
63
214
|
export function getAllSkillStatuses(target: TargetConfig, global: boolean): SkillInfo[] {
|
|
64
|
-
|
|
215
|
+
const bundledStatuses: SkillInfo[] = BUNDLED_SKILLS.map((skill) => ({
|
|
65
216
|
name: skill.name,
|
|
66
217
|
status: getSkillStatus(skill, target, global),
|
|
67
218
|
path: isSkillInstalled(skill, target, global)
|
|
68
219
|
? getSkillPath(skill.dirName, target, global)
|
|
69
220
|
: undefined,
|
|
221
|
+
source: 'bundled' as const,
|
|
70
222
|
}));
|
|
223
|
+
|
|
224
|
+
const projectSkills = collectProjectSkills();
|
|
225
|
+
const bundledNames = new Set(BUNDLED_SKILLS.map((s) => s.name));
|
|
226
|
+
|
|
227
|
+
const projectStatuses: SkillInfo[] = projectSkills
|
|
228
|
+
.filter((skill) => !bundledNames.has(skill.name))
|
|
229
|
+
.map((skill) => ({
|
|
230
|
+
name: skill.name,
|
|
231
|
+
status: getSkillStatus(skill, target, global),
|
|
232
|
+
path: isSkillInstalled(skill, target, global)
|
|
233
|
+
? getSkillPath(skill.dirName, target, global)
|
|
234
|
+
: undefined,
|
|
235
|
+
source: 'project' as const,
|
|
236
|
+
}));
|
|
237
|
+
|
|
238
|
+
return [...bundledStatuses, ...projectStatuses];
|
|
71
239
|
}
|
|
72
240
|
|
|
73
|
-
export function installSkill(
|
|
241
|
+
export function installSkill(
|
|
242
|
+
skill: BundledSkill | ProjectSkill,
|
|
243
|
+
target: TargetConfig,
|
|
244
|
+
global: boolean,
|
|
245
|
+
): void {
|
|
74
246
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
75
247
|
const skillDir = join(baseDir, skill.dirName);
|
|
76
248
|
const skillPath = join(skillDir, target.skillFileName);
|
|
@@ -80,7 +252,7 @@ export function installSkill(skill: BundledSkill, target: TargetConfig, global:
|
|
|
80
252
|
}
|
|
81
253
|
|
|
82
254
|
export function uninstallSkill(
|
|
83
|
-
skill: BundledSkill,
|
|
255
|
+
skill: BundledSkill | ProjectSkill,
|
|
84
256
|
target: TargetConfig,
|
|
85
257
|
global: boolean,
|
|
86
258
|
): boolean {
|
|
@@ -99,10 +271,18 @@ export interface InstallResult {
|
|
|
99
271
|
installed: number;
|
|
100
272
|
updated: number;
|
|
101
273
|
skipped: number;
|
|
274
|
+
bundledCount: number;
|
|
275
|
+
projectCount: number;
|
|
102
276
|
}
|
|
103
277
|
|
|
104
278
|
export function installAllSkills(target: TargetConfig, global: boolean): InstallResult {
|
|
105
|
-
const result: InstallResult = {
|
|
279
|
+
const result: InstallResult = {
|
|
280
|
+
installed: 0,
|
|
281
|
+
updated: 0,
|
|
282
|
+
skipped: 0,
|
|
283
|
+
bundledCount: 0,
|
|
284
|
+
projectCount: 0,
|
|
285
|
+
};
|
|
106
286
|
|
|
107
287
|
for (const skill of BUNDLED_SKILLS) {
|
|
108
288
|
const status = getSkillStatus(skill, target, global);
|
|
@@ -117,6 +297,31 @@ export function installAllSkills(target: TargetConfig, global: boolean): Install
|
|
|
117
297
|
result.skipped++;
|
|
118
298
|
}
|
|
119
299
|
}
|
|
300
|
+
result.bundledCount = BUNDLED_SKILLS.length;
|
|
301
|
+
|
|
302
|
+
const projectSkills = collectProjectSkills();
|
|
303
|
+
const bundledNames = new Set(BUNDLED_SKILLS.map((s) => s.name));
|
|
304
|
+
|
|
305
|
+
for (const skill of projectSkills) {
|
|
306
|
+
if (bundledNames.has(skill.name)) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const status = getSkillStatus(skill, target, global);
|
|
311
|
+
|
|
312
|
+
if (status === 'not_installed') {
|
|
313
|
+
installSkill(skill, target, global);
|
|
314
|
+
result.installed++;
|
|
315
|
+
result.projectCount++;
|
|
316
|
+
} else if (status === 'outdated') {
|
|
317
|
+
installSkill(skill, target, global);
|
|
318
|
+
result.updated++;
|
|
319
|
+
result.projectCount++;
|
|
320
|
+
} else {
|
|
321
|
+
result.skipped++;
|
|
322
|
+
result.projectCount++;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
120
325
|
|
|
121
326
|
return result;
|
|
122
327
|
}
|
|
@@ -137,6 +342,21 @@ export function uninstallAllSkills(target: TargetConfig, global: boolean): Unins
|
|
|
137
342
|
}
|
|
138
343
|
}
|
|
139
344
|
|
|
345
|
+
const projectSkills = collectProjectSkills();
|
|
346
|
+
const bundledNames = new Set(BUNDLED_SKILLS.map((s) => s.name));
|
|
347
|
+
|
|
348
|
+
for (const skill of projectSkills) {
|
|
349
|
+
if (bundledNames.has(skill.name)) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (uninstallSkill(skill, target, global)) {
|
|
354
|
+
result.removed++;
|
|
355
|
+
} else {
|
|
356
|
+
result.notFound++;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
140
360
|
return result;
|
|
141
361
|
}
|
|
142
362
|
|
|
@@ -145,5 +365,8 @@ export function getInstallDir(target: TargetConfig, global: boolean): string {
|
|
|
145
365
|
}
|
|
146
366
|
|
|
147
367
|
export function getTotalSkillCount(): number {
|
|
148
|
-
|
|
368
|
+
const projectSkills = collectProjectSkills();
|
|
369
|
+
const bundledNames = new Set(BUNDLED_SKILLS.map((s) => s.name));
|
|
370
|
+
const uniqueProjectCount = projectSkills.filter((s) => !bundledNames.has(s.name)).length;
|
|
371
|
+
return BUNDLED_SKILLS.length + uniqueProjectCount;
|
|
149
372
|
}
|
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.49.0'; // x-release-please-version
|
package/CLAUDE.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# uloop CLI
|
|
2
|
-
|
|
3
|
-
A CLI tool for communicating with Unity Editor. Completely independent from the MCP server (TypeScriptServer~).
|
|
4
|
-
|
|
5
|
-
## Architecture
|
|
6
|
-
|
|
7
|
-
- **Zero dependency on TypeScriptServer~**
|
|
8
|
-
- Communicates with Unity directly via TCP connection in `direct-unity-client.ts`
|
|
9
|
-
- Interacts directly with Unity TCP server without going through MCP server
|
|
10
|
-
|
|
11
|
-
## Directory Structure
|
|
12
|
-
|
|
13
|
-
```text
|
|
14
|
-
src/
|
|
15
|
-
├── cli.ts # Entry point (commander.js)
|
|
16
|
-
├── version.ts # Version management (auto-updated by release-please)
|
|
17
|
-
├── execute-tool.ts # Tool execution logic
|
|
18
|
-
├── direct-unity-client.ts # Direct Unity TCP communication
|
|
19
|
-
├── simple-framer.ts # TCP framing
|
|
20
|
-
├── port-resolver.ts # Port detection
|
|
21
|
-
├── tool-cache.ts # Tool cache (.uloop/tools.json)
|
|
22
|
-
├── arg-parser.ts # Argument parsing
|
|
23
|
-
├── default-tools.json # Default tool definitions
|
|
24
|
-
├── skills/ # Claude Code skills feature
|
|
25
|
-
│ ├── skills-command.ts
|
|
26
|
-
│ ├── skills-manager.ts
|
|
27
|
-
│ ├── bundled-skills.ts
|
|
28
|
-
│ └── skill-definitions/ # 13 skill definitions (.md)
|
|
29
|
-
└── __tests__/
|
|
30
|
-
└── cli-e2e.test.ts # E2E tests
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Build
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm run build # Generates dist/cli.bundle.cjs
|
|
37
|
-
npm run lint # Run ESLint
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## E2E Tests
|
|
41
|
-
|
|
42
|
-
E2E tests communicate with actual Unity Editor, so the following prerequisites are required:
|
|
43
|
-
|
|
44
|
-
1. Unity Editor must be running
|
|
45
|
-
2. uLoopMCP package must be installed
|
|
46
|
-
3. CLI must be built (`npm run build`)
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npm run test:cli # Run E2E tests (Unity must be running)
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## npm Publishing
|
|
53
|
-
|
|
54
|
-
This directory is published to npm as the `uloop-cli` package.
|
|
55
|
-
Version is synchronized with `Packages/src/package.json` (managed by release-please).
|
|
56
|
-
|
|
57
|
-
## Notes
|
|
58
|
-
|
|
59
|
-
- `version.ts` is a separate file from TypeScriptServer~ (not a copy)
|
|
60
|
-
- Build artifact `dist/cli.bundle.cjs` is excluded via `.gitignore`
|
|
61
|
-
- `node_modules/` is also excluded via `.gitignore`
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uloop-capture-unity-window
|
|
3
|
-
description: Capture Unity EditorWindow and save as PNG image. Use when you need to: (1) Take a screenshot of Game View, Scene View, Console, Inspector, etc., (2) Capture visual state for debugging or verification, (3) Save editor output as an image file.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# uloop capture-unity-window
|
|
7
|
-
|
|
8
|
-
Capture any Unity EditorWindow by name and save as PNG image.
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
uloop capture-unity-window [--window-name <name>] [--resolution-scale <scale>]
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Parameters
|
|
17
|
-
|
|
18
|
-
| Parameter | Type | Default | Description |
|
|
19
|
-
|-----------|------|---------|-------------|
|
|
20
|
-
| `--window-name` | string | `Game` | Window name to capture (e.g., "Game", "Scene", "Console", "Inspector", "Project", "Hierarchy", or any EditorWindow title) |
|
|
21
|
-
| `--resolution-scale` | number | `1.0` | Resolution scale (0.1 to 1.0) |
|
|
22
|
-
|
|
23
|
-
## Window Name
|
|
24
|
-
|
|
25
|
-
The window name is the text displayed in the window's title bar (tab). The user (human) will tell you which window to capture. Common window names include:
|
|
26
|
-
|
|
27
|
-
- **Game**: Game View window
|
|
28
|
-
- **Scene**: Scene View window
|
|
29
|
-
- **Console**: Console window
|
|
30
|
-
- **Inspector**: Inspector window
|
|
31
|
-
- **Project**: Project browser window
|
|
32
|
-
- **Hierarchy**: Hierarchy window
|
|
33
|
-
- **Animation**: Animation window
|
|
34
|
-
- **Animator**: Animator window
|
|
35
|
-
- **Profiler**: Profiler window
|
|
36
|
-
- **Audio Mixer**: Audio Mixer window
|
|
37
|
-
|
|
38
|
-
You can also specify custom EditorWindow titles (e.g., "EditorWindow Capture Test").
|
|
39
|
-
|
|
40
|
-
## Examples
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
# Capture Game View at full resolution
|
|
44
|
-
uloop capture-unity-window
|
|
45
|
-
|
|
46
|
-
# Capture Game View at half resolution
|
|
47
|
-
uloop capture-unity-window --window-name Game --resolution-scale 0.5
|
|
48
|
-
|
|
49
|
-
# Capture Scene View
|
|
50
|
-
uloop capture-unity-window --window-name Scene
|
|
51
|
-
|
|
52
|
-
# Capture Console window
|
|
53
|
-
uloop capture-unity-window --window-name Console
|
|
54
|
-
|
|
55
|
-
# Capture Inspector window
|
|
56
|
-
uloop capture-unity-window --window-name Inspector
|
|
57
|
-
|
|
58
|
-
# Capture Project browser
|
|
59
|
-
uloop capture-unity-window --window-name Project
|
|
60
|
-
|
|
61
|
-
# Capture custom EditorWindow by title
|
|
62
|
-
uloop capture-unity-window --window-name "My Custom Window"
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Output
|
|
66
|
-
|
|
67
|
-
Returns JSON with:
|
|
68
|
-
- `CapturedCount`: Number of windows captured
|
|
69
|
-
- `CapturedWindows`: Array of captured window info, each containing:
|
|
70
|
-
- `ImagePath`: Absolute path to the saved PNG image
|
|
71
|
-
- `FileSizeBytes`: Size of the saved file in bytes
|
|
72
|
-
- `Width`: Captured image width in pixels
|
|
73
|
-
- `Height`: Captured image height in pixels
|
|
74
|
-
|
|
75
|
-
When multiple windows of the same type are open (e.g., multiple Inspector windows), all matching windows are captured with numbered filenames (e.g., `Inspector_1_*.png`, `Inspector_2_*.png`).
|
|
76
|
-
|
|
77
|
-
## Notes
|
|
78
|
-
|
|
79
|
-
- Use `uloop focus-window` first if needed
|
|
80
|
-
- Target window must be open in Unity Editor
|
|
81
|
-
- Window name matching is case-insensitive
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uloop-clear-console
|
|
3
|
-
description: Clear Unity console logs via uloop CLI. Use when you need to: (1) Clear the console before running tests, (2) Start a fresh debugging session, (3) Clean up log output for better readability.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# uloop clear-console
|
|
7
|
-
|
|
8
|
-
Clear Unity console logs.
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
uloop clear-console [--add-confirmation-message]
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Parameters
|
|
17
|
-
|
|
18
|
-
| Parameter | Type | Default | Description |
|
|
19
|
-
|-----------|------|---------|-------------|
|
|
20
|
-
| `--add-confirmation-message` | boolean | `false` | Add confirmation message after clearing |
|
|
21
|
-
|
|
22
|
-
## Examples
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Clear console
|
|
26
|
-
uloop clear-console
|
|
27
|
-
|
|
28
|
-
# Clear with confirmation
|
|
29
|
-
uloop clear-console --add-confirmation-message
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Output
|
|
33
|
-
|
|
34
|
-
Returns JSON confirming the console was cleared.
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uloop-compile
|
|
3
|
-
description: 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.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# uloop compile
|
|
7
|
-
|
|
8
|
-
Execute Unity project compilation.
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
uloop compile [--force-recompile]
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Parameters
|
|
17
|
-
|
|
18
|
-
| Parameter | Type | Description |
|
|
19
|
-
|-----------|------|-------------|
|
|
20
|
-
| `--force-recompile` | boolean | Force full recompilation (triggers Domain Reload) |
|
|
21
|
-
|
|
22
|
-
## Examples
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Check compilation
|
|
26
|
-
uloop compile
|
|
27
|
-
|
|
28
|
-
# Force full recompilation
|
|
29
|
-
uloop compile --force-recompile
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Output
|
|
33
|
-
|
|
34
|
-
Returns JSON:
|
|
35
|
-
- `Success`: boolean
|
|
36
|
-
- `ErrorCount`: number
|
|
37
|
-
- `WarningCount`: number
|
|
38
|
-
|
|
39
|
-
## Troubleshooting
|
|
40
|
-
|
|
41
|
-
If CLI hangs or shows "Unity is busy" errors after compilation, stale lock files may be preventing connection. Run the following to clean them up:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
uloop fix
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
This removes any leftover lock files (`compiling.lock`, `domainreload.lock`, `serverstarting.lock`) from the Unity project's Temp directory.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uloop-control-play-mode
|
|
3
|
-
description: Control Unity Editor play mode via uloop CLI. Use when you need to: (1) Start play mode for testing, (2) Stop play mode after testing, (3) Pause play mode for debugging.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# uloop control-play-mode
|
|
7
|
-
|
|
8
|
-
Control Unity Editor play mode (play/stop/pause).
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
uloop control-play-mode [options]
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Parameters
|
|
17
|
-
|
|
18
|
-
| Parameter | Type | Default | Description |
|
|
19
|
-
|-----------|------|---------|-------------|
|
|
20
|
-
| `--action` | string | `Play` | Action to perform: `Play`, `Stop`, `Pause` |
|
|
21
|
-
|
|
22
|
-
## Examples
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Start play mode
|
|
26
|
-
uloop control-play-mode --action Play
|
|
27
|
-
|
|
28
|
-
# Stop play mode
|
|
29
|
-
uloop control-play-mode --action Stop
|
|
30
|
-
|
|
31
|
-
# Pause play mode
|
|
32
|
-
uloop control-play-mode --action Pause
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Output
|
|
36
|
-
|
|
37
|
-
Returns JSON with the current play mode state:
|
|
38
|
-
- `IsPlaying`: Whether Unity is currently in play mode
|
|
39
|
-
- `IsPaused`: Whether play mode is paused
|
|
40
|
-
- `Message`: Description of the action performed
|
|
41
|
-
|
|
42
|
-
## Notes
|
|
43
|
-
|
|
44
|
-
- Play action starts the game in the Unity Editor (also resumes from pause)
|
|
45
|
-
- Stop action exits play mode and returns to edit mode
|
|
46
|
-
- Pause action pauses the game while remaining in play mode
|
|
47
|
-
- Useful for automated testing workflows
|
|
48
|
-
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uloop-execute-dynamic-code
|
|
3
|
-
description: Execute C# code dynamically in Unity Editor via uloop CLI. Use for editor automation: (1) Prefab/material wiring and AddComponent operations, (2) Reference wiring with SerializedObject, (3) Scene/hierarchy edits and batch operations. NOT for file I/O or script authoring.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# uloop execute-dynamic-code
|
|
7
|
-
|
|
8
|
-
Execute C# code dynamically in Unity Editor.
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
uloop execute-dynamic-code --code '<c# code>'
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Parameters
|
|
17
|
-
|
|
18
|
-
| Parameter | Type | Description |
|
|
19
|
-
|-----------|------|-------------|
|
|
20
|
-
| `--code` | string | C# code to execute (direct statements, no class wrapper) |
|
|
21
|
-
| `--compile-only` | boolean | Compile without execution |
|
|
22
|
-
| `--auto-qualify-unity-types-once` | boolean | Auto-qualify Unity types |
|
|
23
|
-
|
|
24
|
-
## Code Format
|
|
25
|
-
|
|
26
|
-
Write direct statements only (no classes/namespaces/methods). Return is optional.
|
|
27
|
-
|
|
28
|
-
```csharp
|
|
29
|
-
// Using directives at top are hoisted
|
|
30
|
-
using UnityEngine;
|
|
31
|
-
var x = Mathf.PI;
|
|
32
|
-
return x;
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## String Literals (Shell-specific)
|
|
36
|
-
|
|
37
|
-
| Shell | Method |
|
|
38
|
-
|-------|--------|
|
|
39
|
-
| bash/zsh/MINGW64/Git Bash | `'Debug.Log("Hello!");'` |
|
|
40
|
-
| PowerShell | `'Debug.Log(""Hello!"");'` |
|
|
41
|
-
|
|
42
|
-
## Allowed Operations
|
|
43
|
-
|
|
44
|
-
- Prefab/material wiring (PrefabUtility)
|
|
45
|
-
- AddComponent + reference wiring (SerializedObject)
|
|
46
|
-
- Scene/hierarchy edits
|
|
47
|
-
- Inspector modifications
|
|
48
|
-
|
|
49
|
-
## Forbidden Operations
|
|
50
|
-
|
|
51
|
-
- System.IO.* (File/Directory/Path)
|
|
52
|
-
- AssetDatabase.CreateFolder / file writes
|
|
53
|
-
- Create/edit .cs/.asmdef files
|
|
54
|
-
|
|
55
|
-
## Examples
|
|
56
|
-
|
|
57
|
-
### bash / zsh / MINGW64 / Git Bash
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
uloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'
|
|
61
|
-
uloop execute-dynamic-code --code 'new GameObject("MyObject");'
|
|
62
|
-
uloop execute-dynamic-code --code 'UnityEngine.Debug.Log("Hello from CLI!");'
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### PowerShell
|
|
66
|
-
|
|
67
|
-
```powershell
|
|
68
|
-
uloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'
|
|
69
|
-
uloop execute-dynamic-code --code 'new GameObject(""MyObject"");'
|
|
70
|
-
uloop execute-dynamic-code --code 'UnityEngine.Debug.Log(""Hello from CLI!"");'
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Output
|
|
74
|
-
|
|
75
|
-
Returns JSON with execution result or compile errors.
|
|
76
|
-
|
|
77
|
-
## Notes
|
|
78
|
-
|
|
79
|
-
For file/directory operations, use terminal commands instead.
|