uloop-cli 0.44.1

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.
Files changed (39) hide show
  1. package/.prettierrc.json +28 -0
  2. package/CLAUDE.md +61 -0
  3. package/dist/cli.bundle.cjs +4761 -0
  4. package/dist/cli.bundle.cjs.map +7 -0
  5. package/eslint.config.mjs +72 -0
  6. package/jest.config.cjs +19 -0
  7. package/package.json +61 -0
  8. package/src/__tests__/cli-e2e.test.ts +349 -0
  9. package/src/__tests__/setup.ts +24 -0
  10. package/src/arg-parser.ts +128 -0
  11. package/src/cli.ts +489 -0
  12. package/src/default-tools.json +355 -0
  13. package/src/direct-unity-client.ts +125 -0
  14. package/src/execute-tool.ts +155 -0
  15. package/src/port-resolver.ts +60 -0
  16. package/src/project-root.ts +31 -0
  17. package/src/simple-framer.ts +97 -0
  18. package/src/skills/bundled-skills.ts +64 -0
  19. package/src/skills/markdown.d.ts +4 -0
  20. package/src/skills/skill-definitions/uloop-capture-gameview/SKILL.md +39 -0
  21. package/src/skills/skill-definitions/uloop-clear-console/SKILL.md +34 -0
  22. package/src/skills/skill-definitions/uloop-compile/SKILL.md +37 -0
  23. package/src/skills/skill-definitions/uloop-execute-dynamic-code/SKILL.md +79 -0
  24. package/src/skills/skill-definitions/uloop-execute-menu-item/SKILL.md +43 -0
  25. package/src/skills/skill-definitions/uloop-find-game-objects/SKILL.md +46 -0
  26. package/src/skills/skill-definitions/uloop-focus-window/SKILL.md +34 -0
  27. package/src/skills/skill-definitions/uloop-get-hierarchy/SKILL.md +44 -0
  28. package/src/skills/skill-definitions/uloop-get-logs/SKILL.md +45 -0
  29. package/src/skills/skill-definitions/uloop-get-menu-items/SKILL.md +44 -0
  30. package/src/skills/skill-definitions/uloop-get-project-info/SKILL.md +34 -0
  31. package/src/skills/skill-definitions/uloop-get-provider-details/SKILL.md +45 -0
  32. package/src/skills/skill-definitions/uloop-get-version/SKILL.md +31 -0
  33. package/src/skills/skill-definitions/uloop-run-tests/SKILL.md +43 -0
  34. package/src/skills/skill-definitions/uloop-unity-search/SKILL.md +44 -0
  35. package/src/skills/skills-command.ts +118 -0
  36. package/src/skills/skills-manager.ts +135 -0
  37. package/src/tool-cache.ts +104 -0
  38. package/src/version.ts +7 -0
  39. package/tsconfig.json +26 -0
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: uloop-get-provider-details
3
+ description: Get Unity Search provider details via uloop CLI. Use when you need to: (1) Discover available search providers, (2) Understand search capabilities and filters, (3) Configure searches with specific provider options.
4
+ ---
5
+
6
+ # uloop get-provider-details
7
+
8
+ Get detailed information about Unity Search providers.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ uloop get-provider-details [options]
14
+ ```
15
+
16
+ ## Parameters
17
+
18
+ | Parameter | Type | Default | Description |
19
+ |-----------|------|---------|-------------|
20
+ | `--provider-id` | string | - | Specific provider ID to query |
21
+ | `--active-only` | boolean | `false` | Only show active providers |
22
+ | `--include-descriptions` | boolean | `true` | Include descriptions |
23
+ | `--sort-by-priority` | boolean | `true` | Sort by priority |
24
+
25
+ ## Examples
26
+
27
+ ```bash
28
+ # List all providers
29
+ uloop get-provider-details
30
+
31
+ # Get specific provider
32
+ uloop get-provider-details --provider-id asset
33
+
34
+ # Active providers only
35
+ uloop get-provider-details --active-only
36
+ ```
37
+
38
+ ## Output
39
+
40
+ Returns JSON:
41
+ - `Providers`: array of provider info (ID, name, description, priority)
42
+
43
+ ## Notes
44
+
45
+ Use provider IDs with `uloop unity-search --providers` option.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: uloop-get-version
3
+ description: Get uLoopMCP version information via uloop CLI. Use when you need to: (1) Check uLoopMCP package version, (2) Verify installation is working correctly, (3) Troubleshoot version compatibility issues.
4
+ ---
5
+
6
+ # uloop get-version
7
+
8
+ Get uLoopMCP version information.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ uloop get-version
14
+ ```
15
+
16
+ ## Parameters
17
+
18
+ None.
19
+
20
+ ## Examples
21
+
22
+ ```bash
23
+ # Get version info
24
+ uloop get-version
25
+ ```
26
+
27
+ ## Output
28
+
29
+ Returns JSON with version information:
30
+ - uLoopMCP package version
31
+ - CLI version
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: uloop-run-tests
3
+ description: Execute Unity Test Runner via uloop CLI. Use when you need to: (1) Run unit tests (EditMode tests), (2) Run integration tests (PlayMode tests), (3) Verify code changes don't break existing functionality.
4
+ ---
5
+
6
+ # uloop run-tests
7
+
8
+ Execute Unity Test Runner.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ uloop run-tests [options]
14
+ ```
15
+
16
+ ## Parameters
17
+
18
+ | Parameter | Type | Default | Description |
19
+ |-----------|------|---------|-------------|
20
+ | `--test-mode` | string | `EditMode` | Test mode: `EditMode`, `PlayMode` |
21
+ | `--filter-type` | string | `all` | Filter type: `all`, `exact`, `regex`, `assembly` |
22
+ | `--filter-value` | string | - | Filter value (test name, pattern, or assembly) |
23
+ | `--save-xml` | boolean | `false` | Save test results as XML |
24
+
25
+ ## Examples
26
+
27
+ ```bash
28
+ # Run all EditMode tests
29
+ uloop run-tests
30
+
31
+ # Run PlayMode tests
32
+ uloop run-tests --test-mode PlayMode
33
+
34
+ # Run specific test
35
+ uloop run-tests --filter-type exact --filter-value "MyTest.TestMethod"
36
+
37
+ # Run tests matching pattern
38
+ uloop run-tests --filter-type regex --filter-value ".*Integration.*"
39
+ ```
40
+
41
+ ## Output
42
+
43
+ Returns JSON with test results including pass/fail counts and details.
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: uloop-unity-search
3
+ description: Search Unity project via uloop CLI. Use when you need to: (1) Find assets by name or type (scenes, prefabs, scripts, materials), (2) Search for project resources using Unity's search system, (3) Locate files within the Unity project.
4
+ ---
5
+
6
+ # uloop unity-search
7
+
8
+ Search Unity project using Unity Search.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ uloop unity-search [options]
14
+ ```
15
+
16
+ ## Parameters
17
+
18
+ | Parameter | Type | Default | Description |
19
+ |-----------|------|---------|-------------|
20
+ | `--search-query` | string | - | Search query |
21
+ | `--providers` | array | - | Search providers (e.g., `asset`, `scene`, `find`) |
22
+ | `--max-results` | integer | `50` | Maximum number of results |
23
+ | `--save-to-file` | boolean | `false` | Save results to file |
24
+
25
+ ## Examples
26
+
27
+ ```bash
28
+ # Search for assets
29
+ uloop unity-search --search-query "Player"
30
+
31
+ # Search with specific provider
32
+ uloop unity-search --search-query "t:Prefab" --providers asset
33
+
34
+ # Limit results
35
+ uloop unity-search --search-query "*.cs" --max-results 20
36
+ ```
37
+
38
+ ## Output
39
+
40
+ Returns JSON array of search results with paths and metadata.
41
+
42
+ ## Notes
43
+
44
+ Use `uloop get-provider-details` to discover available search providers.
@@ -0,0 +1,118 @@
1
+ /**
2
+ * CLI command definitions for skills management.
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import {
7
+ getAllSkillStatuses,
8
+ installAllSkills,
9
+ uninstallAllSkills,
10
+ getInstallDir,
11
+ getTotalSkillCount,
12
+ } from './skills-manager.js';
13
+
14
+ export function registerSkillsCommand(program: Command): void {
15
+ const skillsCmd = program.command('skills').description('Manage uloop skills for Claude Code');
16
+
17
+ skillsCmd
18
+ .command('list')
19
+ .description('List all uloop skills and their installation status')
20
+ .option('-g, --global', 'Check global installation (~/.claude/skills/)')
21
+ .action((options: { global?: boolean }) => {
22
+ listSkills(options.global ?? false);
23
+ });
24
+
25
+ skillsCmd
26
+ .command('install')
27
+ .description('Install all uloop skills')
28
+ .option('-g, --global', 'Install to global location (~/.claude/skills/)')
29
+ .action((options: { global?: boolean }) => {
30
+ installSkills(options.global ?? false);
31
+ });
32
+
33
+ skillsCmd
34
+ .command('uninstall')
35
+ .description('Uninstall all uloop skills')
36
+ .option('-g, --global', 'Uninstall from global location (~/.claude/skills/)')
37
+ .action((options: { global?: boolean }) => {
38
+ uninstallSkills(options.global ?? false);
39
+ });
40
+ }
41
+
42
+ function listSkills(global: boolean): void {
43
+ const location = global ? 'Global' : 'Project';
44
+ const dir = getInstallDir(global);
45
+
46
+ console.log(`\nuloop Skills Status (${location}):`);
47
+ console.log(`Location: ${dir}`);
48
+ console.log('='.repeat(50));
49
+ console.log('');
50
+
51
+ const statuses = getAllSkillStatuses(global);
52
+
53
+ for (const skill of statuses) {
54
+ const icon = getStatusIcon(skill.status);
55
+ const statusText = getStatusText(skill.status);
56
+ console.log(` ${icon} ${skill.name} (${statusText})`);
57
+ }
58
+
59
+ console.log('');
60
+ console.log(`Total: ${getTotalSkillCount()} bundled skills`);
61
+ }
62
+
63
+ function getStatusIcon(status: string): string {
64
+ switch (status) {
65
+ case 'installed':
66
+ return '\x1b[32m✓\x1b[0m';
67
+ case 'outdated':
68
+ return '\x1b[33m↑\x1b[0m';
69
+ case 'not_installed':
70
+ return '\x1b[31m✗\x1b[0m';
71
+ default:
72
+ return '?';
73
+ }
74
+ }
75
+
76
+ function getStatusText(status: string): string {
77
+ switch (status) {
78
+ case 'installed':
79
+ return 'installed';
80
+ case 'outdated':
81
+ return 'outdated';
82
+ case 'not_installed':
83
+ return 'not installed';
84
+ default:
85
+ return 'unknown';
86
+ }
87
+ }
88
+
89
+ function installSkills(global: boolean): void {
90
+ const location = global ? 'global' : 'project';
91
+ const dir = getInstallDir(global);
92
+
93
+ console.log(`\nInstalling uloop skills (${location})...`);
94
+ console.log('');
95
+
96
+ const result = installAllSkills(global);
97
+
98
+ console.log(`\x1b[32m✓\x1b[0m Installed: ${result.installed}`);
99
+ console.log(`\x1b[33m↑\x1b[0m Updated: ${result.updated}`);
100
+ console.log(`\x1b[90m-\x1b[0m Skipped (up-to-date): ${result.skipped}`);
101
+ console.log('');
102
+ console.log(`Skills installed to ${dir}`);
103
+ }
104
+
105
+ function uninstallSkills(global: boolean): void {
106
+ const location = global ? 'global' : 'project';
107
+ const dir = getInstallDir(global);
108
+
109
+ console.log(`\nUninstalling uloop skills (${location})...`);
110
+ console.log('');
111
+
112
+ const result = uninstallAllSkills(global);
113
+
114
+ console.log(`\x1b[31m✗\x1b[0m Removed: ${result.removed}`);
115
+ console.log(`\x1b[90m-\x1b[0m Not found: ${result.notFound}`);
116
+ console.log('');
117
+ console.log(`Skills removed from ${dir}`);
118
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Skills manager for installing/uninstalling/listing uloop skills.
3
+ */
4
+
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
8
+ import { BUNDLED_SKILLS, BundledSkill } from './bundled-skills.js';
9
+
10
+ export type SkillStatus = 'installed' | 'not_installed' | 'outdated';
11
+
12
+ export interface SkillInfo {
13
+ name: string;
14
+ status: SkillStatus;
15
+ path?: string;
16
+ }
17
+
18
+ function getGlobalSkillsDir(): string {
19
+ return join(homedir(), '.claude', 'skills');
20
+ }
21
+
22
+ function getProjectSkillsDir(): string {
23
+ return join(process.cwd(), '.claude', 'skills');
24
+ }
25
+
26
+ function getSkillPath(skillDirName: string, global: boolean): string {
27
+ const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
28
+ return join(baseDir, skillDirName, 'SKILL.md');
29
+ }
30
+
31
+ function isSkillInstalled(skill: BundledSkill, global: boolean): boolean {
32
+ const skillPath = getSkillPath(skill.dirName, global);
33
+ return existsSync(skillPath);
34
+ }
35
+
36
+ function isSkillOutdated(skill: BundledSkill, global: boolean): boolean {
37
+ const skillPath = getSkillPath(skill.dirName, global);
38
+ if (!existsSync(skillPath)) {
39
+ return false;
40
+ }
41
+ const installedContent = readFileSync(skillPath, 'utf-8');
42
+ return installedContent !== skill.content;
43
+ }
44
+
45
+ export function getSkillStatus(skill: BundledSkill, global: boolean): SkillStatus {
46
+ if (!isSkillInstalled(skill, global)) {
47
+ return 'not_installed';
48
+ }
49
+ if (isSkillOutdated(skill, global)) {
50
+ return 'outdated';
51
+ }
52
+ return 'installed';
53
+ }
54
+
55
+ export function getAllSkillStatuses(global: boolean): SkillInfo[] {
56
+ return BUNDLED_SKILLS.map((skill) => ({
57
+ name: skill.name,
58
+ status: getSkillStatus(skill, global),
59
+ path: isSkillInstalled(skill, global) ? getSkillPath(skill.dirName, global) : undefined,
60
+ }));
61
+ }
62
+
63
+ export function installSkill(skill: BundledSkill, global: boolean): void {
64
+ const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
65
+ const skillDir = join(baseDir, skill.dirName);
66
+ const skillPath = join(skillDir, 'SKILL.md');
67
+
68
+ mkdirSync(skillDir, { recursive: true });
69
+ writeFileSync(skillPath, skill.content, 'utf-8');
70
+ }
71
+
72
+ export function uninstallSkill(skill: BundledSkill, global: boolean): boolean {
73
+ const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
74
+ const skillDir = join(baseDir, skill.dirName);
75
+
76
+ if (!existsSync(skillDir)) {
77
+ return false;
78
+ }
79
+
80
+ rmSync(skillDir, { recursive: true, force: true });
81
+ return true;
82
+ }
83
+
84
+ export interface InstallResult {
85
+ installed: number;
86
+ updated: number;
87
+ skipped: number;
88
+ }
89
+
90
+ export function installAllSkills(global: boolean): InstallResult {
91
+ const result: InstallResult = { installed: 0, updated: 0, skipped: 0 };
92
+
93
+ for (const skill of BUNDLED_SKILLS) {
94
+ const status = getSkillStatus(skill, global);
95
+
96
+ if (status === 'not_installed') {
97
+ installSkill(skill, global);
98
+ result.installed++;
99
+ } else if (status === 'outdated') {
100
+ installSkill(skill, global);
101
+ result.updated++;
102
+ } else {
103
+ result.skipped++;
104
+ }
105
+ }
106
+
107
+ return result;
108
+ }
109
+
110
+ export interface UninstallResult {
111
+ removed: number;
112
+ notFound: number;
113
+ }
114
+
115
+ export function uninstallAllSkills(global: boolean): UninstallResult {
116
+ const result: UninstallResult = { removed: 0, notFound: 0 };
117
+
118
+ for (const skill of BUNDLED_SKILLS) {
119
+ if (uninstallSkill(skill, global)) {
120
+ result.removed++;
121
+ } else {
122
+ result.notFound++;
123
+ }
124
+ }
125
+
126
+ return result;
127
+ }
128
+
129
+ export function getInstallDir(global: boolean): string {
130
+ return global ? getGlobalSkillsDir() : getProjectSkillsDir();
131
+ }
132
+
133
+ export function getTotalSkillCount(): number {
134
+ return BUNDLED_SKILLS.length;
135
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Tool cache management for CLI.
3
+ * Handles loading/saving tool definitions from .uloop/tools.json cache.
4
+ */
5
+
6
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
7
+ import { join } from 'path';
8
+ import defaultToolsData from './default-tools.json';
9
+ import { findUnityProjectRoot } from './project-root.js';
10
+
11
+ export interface ToolProperty {
12
+ type: string;
13
+ description?: string;
14
+ default?: unknown;
15
+ enum?: string[];
16
+ items?: { type: string };
17
+ }
18
+
19
+ export interface ToolInputSchema {
20
+ type: string;
21
+ properties: Record<string, ToolProperty>;
22
+ required?: string[];
23
+ }
24
+
25
+ export interface ToolDefinition {
26
+ name: string;
27
+ description: string;
28
+ inputSchema: ToolInputSchema;
29
+ }
30
+
31
+ export interface ToolsCache {
32
+ version: string;
33
+ updatedAt?: string;
34
+ tools: ToolDefinition[];
35
+ }
36
+
37
+ const CACHE_DIR = '.uloop';
38
+ const CACHE_FILE = 'tools.json';
39
+
40
+ function getCacheDir(): string {
41
+ const projectRoot = findUnityProjectRoot();
42
+ if (projectRoot === null) {
43
+ return join(process.cwd(), CACHE_DIR);
44
+ }
45
+ return join(projectRoot, CACHE_DIR);
46
+ }
47
+
48
+ function getCachePath(): string {
49
+ return join(getCacheDir(), CACHE_FILE);
50
+ }
51
+
52
+ /**
53
+ * Load default tools bundled with npm package.
54
+ */
55
+ export function getDefaultTools(): ToolsCache {
56
+ return defaultToolsData as ToolsCache;
57
+ }
58
+
59
+ /**
60
+ * Load tools from cache file, falling back to default tools if cache doesn't exist.
61
+ */
62
+ export function loadToolsCache(): ToolsCache {
63
+ const cachePath = getCachePath();
64
+
65
+ if (existsSync(cachePath)) {
66
+ try {
67
+ const content = readFileSync(cachePath, 'utf-8');
68
+ return JSON.parse(content) as ToolsCache;
69
+ } catch {
70
+ return getDefaultTools();
71
+ }
72
+ }
73
+
74
+ return getDefaultTools();
75
+ }
76
+
77
+ /**
78
+ * Save tools to cache file.
79
+ */
80
+ export function saveToolsCache(cache: ToolsCache): void {
81
+ const cacheDir = getCacheDir();
82
+ const cachePath = getCachePath();
83
+
84
+ if (!existsSync(cacheDir)) {
85
+ mkdirSync(cacheDir, { recursive: true });
86
+ }
87
+
88
+ const content = JSON.stringify(cache, null, 2);
89
+ writeFileSync(cachePath, content, 'utf-8');
90
+ }
91
+
92
+ /**
93
+ * Check if cache file exists.
94
+ */
95
+ export function hasCacheFile(): boolean {
96
+ return existsSync(getCachePath());
97
+ }
98
+
99
+ /**
100
+ * Get the cache file path for display purposes.
101
+ */
102
+ export function getCacheFilePath(): string {
103
+ return getCachePath();
104
+ }
package/src/version.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CLI version
3
+ *
4
+ * This file exists to avoid bundling the entire package.json into the CLI bundle.
5
+ * This version is automatically updated by release-please.
6
+ */
7
+ export const VERSION = '0.44.1'; // x-release-please-version
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "resolveJsonModule": true,
16
+ "allowSyntheticDefaultImports": true,
17
+ "types": ["node", "jest"]
18
+ },
19
+ "include": [
20
+ "src/**/*"
21
+ ],
22
+ "exclude": [
23
+ "node_modules",
24
+ "dist"
25
+ ]
26
+ }