uloop-cli 0.44.2 → 0.45.2
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 +412 -151
- package/dist/cli.bundle.cjs.map +4 -4
- package/package.json +1 -1
- package/src/arg-parser.ts +3 -0
- package/src/cli.ts +70 -23
- package/src/default-tools.json +1 -1
- package/src/direct-unity-client.ts +3 -0
- package/src/execute-tool.ts +212 -50
- package/src/port-resolver.ts +3 -0
- package/src/project-root.ts +3 -0
- package/src/skills/skill-definitions/uloop-compile/SKILL.md +10 -0
- package/src/skills/skills-command.ts +106 -36
- package/src/skills/skills-manager.ts +44 -30
- package/src/skills/target-config.ts +34 -0
- package/src/spinner.ts +49 -0
- package/src/tool-cache.ts +3 -0
- package/src/version.ts +1 -1
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* CLI command definitions for skills management.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// CLI commands output to console by design
|
|
6
|
+
/* eslint-disable no-console */
|
|
7
|
+
|
|
5
8
|
import { Command } from 'commander';
|
|
6
9
|
import {
|
|
7
10
|
getAllSkillStatuses,
|
|
@@ -10,53 +13,114 @@ import {
|
|
|
10
13
|
getInstallDir,
|
|
11
14
|
getTotalSkillCount,
|
|
12
15
|
} from './skills-manager.js';
|
|
16
|
+
import { TargetConfig, ALL_TARGET_IDS, getTargetConfig } from './target-config.js';
|
|
17
|
+
|
|
18
|
+
interface SkillsOptions {
|
|
19
|
+
global?: boolean;
|
|
20
|
+
claude?: boolean;
|
|
21
|
+
codex?: boolean;
|
|
22
|
+
}
|
|
13
23
|
|
|
14
24
|
export function registerSkillsCommand(program: Command): void {
|
|
15
|
-
const skillsCmd = program
|
|
25
|
+
const skillsCmd = program
|
|
26
|
+
.command('skills')
|
|
27
|
+
.description('Manage uloop skills for AI coding tools');
|
|
16
28
|
|
|
17
29
|
skillsCmd
|
|
18
30
|
.command('list')
|
|
19
31
|
.description('List all uloop skills and their installation status')
|
|
20
|
-
.option('-g, --global', 'Check global installation
|
|
21
|
-
.
|
|
22
|
-
|
|
32
|
+
.option('-g, --global', 'Check global installation')
|
|
33
|
+
.option('--claude', 'Check Claude Code installation')
|
|
34
|
+
.option('--codex', 'Check Codex CLI installation')
|
|
35
|
+
.action((options: SkillsOptions) => {
|
|
36
|
+
const targets = resolveTargets(options);
|
|
37
|
+
const global = options.global ?? false;
|
|
38
|
+
listSkills(targets, global);
|
|
23
39
|
});
|
|
24
40
|
|
|
25
41
|
skillsCmd
|
|
26
42
|
.command('install')
|
|
27
43
|
.description('Install all uloop skills')
|
|
28
|
-
.option('-g, --global', 'Install to global location
|
|
29
|
-
.
|
|
30
|
-
|
|
44
|
+
.option('-g, --global', 'Install to global location')
|
|
45
|
+
.option('--claude', 'Install to Claude Code')
|
|
46
|
+
.option('--codex', 'Install to Codex CLI')
|
|
47
|
+
.action((options: SkillsOptions) => {
|
|
48
|
+
const targets = resolveTargets(options);
|
|
49
|
+
if (targets.length === 0) {
|
|
50
|
+
showTargetGuidance('install');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
installSkills(targets, options.global ?? false);
|
|
31
54
|
});
|
|
32
55
|
|
|
33
56
|
skillsCmd
|
|
34
57
|
.command('uninstall')
|
|
35
58
|
.description('Uninstall all uloop skills')
|
|
36
|
-
.option('-g, --global', 'Uninstall from global location
|
|
37
|
-
.
|
|
38
|
-
|
|
59
|
+
.option('-g, --global', 'Uninstall from global location')
|
|
60
|
+
.option('--claude', 'Uninstall from Claude Code')
|
|
61
|
+
.option('--codex', 'Uninstall from Codex CLI')
|
|
62
|
+
.action((options: SkillsOptions) => {
|
|
63
|
+
const targets = resolveTargets(options);
|
|
64
|
+
if (targets.length === 0) {
|
|
65
|
+
showTargetGuidance('uninstall');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
uninstallSkills(targets, options.global ?? false);
|
|
39
69
|
});
|
|
40
70
|
}
|
|
41
71
|
|
|
42
|
-
function
|
|
72
|
+
function resolveTargets(options: SkillsOptions): TargetConfig[] {
|
|
73
|
+
const targets: TargetConfig[] = [];
|
|
74
|
+
if (options.claude) {
|
|
75
|
+
targets.push(getTargetConfig('claude'));
|
|
76
|
+
}
|
|
77
|
+
if (options.codex) {
|
|
78
|
+
targets.push(getTargetConfig('codex'));
|
|
79
|
+
}
|
|
80
|
+
return targets;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function showTargetGuidance(command: string): void {
|
|
84
|
+
console.log(`\nPlease specify at least one target for '${command}':`);
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log('Available targets:');
|
|
87
|
+
console.log(' --claude Claude Code (.claude/skills/)');
|
|
88
|
+
console.log(' --codex Codex CLI (.codex/skills/)');
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log('Options:');
|
|
91
|
+
console.log(' -g, --global Use global location (~/.claude/ or ~/.codex/)');
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log('Examples:');
|
|
94
|
+
console.log(` uloop skills ${command} --claude`);
|
|
95
|
+
console.log(` uloop skills ${command} --codex --global`);
|
|
96
|
+
console.log(` uloop skills ${command} --claude --codex`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function listSkills(targets: TargetConfig[], global: boolean): void {
|
|
43
100
|
const location = global ? 'Global' : 'Project';
|
|
44
|
-
const
|
|
101
|
+
const targetConfigs = targets.length > 0 ? targets : ALL_TARGET_IDS.map(getTargetConfig);
|
|
45
102
|
|
|
46
|
-
console.log(`\nuloop Skills Status
|
|
47
|
-
console.log(`Location: ${dir}`);
|
|
48
|
-
console.log('='.repeat(50));
|
|
103
|
+
console.log(`\nuloop Skills Status:`);
|
|
49
104
|
console.log('');
|
|
50
105
|
|
|
51
|
-
const
|
|
106
|
+
for (const target of targetConfigs) {
|
|
107
|
+
const dir = getInstallDir(target, global);
|
|
108
|
+
|
|
109
|
+
console.log(`${target.displayName} (${location}):`);
|
|
110
|
+
console.log(`Location: ${dir}`);
|
|
111
|
+
console.log('='.repeat(50));
|
|
112
|
+
|
|
113
|
+
const statuses = getAllSkillStatuses(target, global);
|
|
114
|
+
|
|
115
|
+
for (const skill of statuses) {
|
|
116
|
+
const icon = getStatusIcon(skill.status);
|
|
117
|
+
const statusText = getStatusText(skill.status);
|
|
118
|
+
console.log(` ${icon} ${skill.name} (${statusText})`);
|
|
119
|
+
}
|
|
52
120
|
|
|
53
|
-
|
|
54
|
-
const icon = getStatusIcon(skill.status);
|
|
55
|
-
const statusText = getStatusText(skill.status);
|
|
56
|
-
console.log(` ${icon} ${skill.name} (${statusText})`);
|
|
121
|
+
console.log('');
|
|
57
122
|
}
|
|
58
123
|
|
|
59
|
-
console.log('');
|
|
60
124
|
console.log(`Total: ${getTotalSkillCount()} bundled skills`);
|
|
61
125
|
}
|
|
62
126
|
|
|
@@ -86,33 +150,39 @@ function getStatusText(status: string): string {
|
|
|
86
150
|
}
|
|
87
151
|
}
|
|
88
152
|
|
|
89
|
-
function installSkills(global: boolean): void {
|
|
153
|
+
function installSkills(targets: TargetConfig[], global: boolean): void {
|
|
90
154
|
const location = global ? 'global' : 'project';
|
|
91
|
-
const dir = getInstallDir(global);
|
|
92
155
|
|
|
93
156
|
console.log(`\nInstalling uloop skills (${location})...`);
|
|
94
157
|
console.log('');
|
|
95
158
|
|
|
96
|
-
const
|
|
159
|
+
for (const target of targets) {
|
|
160
|
+
const dir = getInstallDir(target, global);
|
|
161
|
+
const result = installAllSkills(target, global);
|
|
97
162
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
163
|
+
console.log(`${target.displayName}:`);
|
|
164
|
+
console.log(` \x1b[32m✓\x1b[0m Installed: ${result.installed}`);
|
|
165
|
+
console.log(` \x1b[33m↑\x1b[0m Updated: ${result.updated}`);
|
|
166
|
+
console.log(` \x1b[90m-\x1b[0m Skipped (up-to-date): ${result.skipped}`);
|
|
167
|
+
console.log(` Location: ${dir}`);
|
|
168
|
+
console.log('');
|
|
169
|
+
}
|
|
103
170
|
}
|
|
104
171
|
|
|
105
|
-
function uninstallSkills(global: boolean): void {
|
|
172
|
+
function uninstallSkills(targets: TargetConfig[], global: boolean): void {
|
|
106
173
|
const location = global ? 'global' : 'project';
|
|
107
|
-
const dir = getInstallDir(global);
|
|
108
174
|
|
|
109
175
|
console.log(`\nUninstalling uloop skills (${location})...`);
|
|
110
176
|
console.log('');
|
|
111
177
|
|
|
112
|
-
const
|
|
178
|
+
for (const target of targets) {
|
|
179
|
+
const dir = getInstallDir(target, global);
|
|
180
|
+
const result = uninstallAllSkills(target, global);
|
|
113
181
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
182
|
+
console.log(`${target.displayName}:`);
|
|
183
|
+
console.log(` \x1b[31m✗\x1b[0m Removed: ${result.removed}`);
|
|
184
|
+
console.log(` \x1b[90m-\x1b[0m Not found: ${result.notFound}`);
|
|
185
|
+
console.log(` Location: ${dir}`);
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
118
188
|
}
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
* Skills manager for installing/uninstalling/listing uloop skills.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// File paths are constructed from home directory and skill names, not from untrusted user input
|
|
6
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
7
|
+
|
|
5
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
|
|
6
9
|
import { join } from 'path';
|
|
7
10
|
import { homedir } from 'os';
|
|
8
11
|
import { BUNDLED_SKILLS, BundledSkill } from './bundled-skills.js';
|
|
12
|
+
import { TargetConfig } from './target-config.js';
|
|
9
13
|
|
|
10
14
|
export type SkillStatus = 'installed' | 'not_installed' | 'outdated';
|
|
11
15
|
|
|
@@ -15,26 +19,26 @@ export interface SkillInfo {
|
|
|
15
19
|
path?: string;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
function getGlobalSkillsDir(): string {
|
|
19
|
-
return join(homedir(),
|
|
22
|
+
function getGlobalSkillsDir(target: TargetConfig): string {
|
|
23
|
+
return join(homedir(), target.projectDir, 'skills');
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
function getProjectSkillsDir(): string {
|
|
23
|
-
return join(process.cwd(),
|
|
26
|
+
function getProjectSkillsDir(target: TargetConfig): string {
|
|
27
|
+
return join(process.cwd(), target.projectDir, 'skills');
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
function getSkillPath(skillDirName: string, global: boolean): string {
|
|
27
|
-
const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
|
|
28
|
-
return join(baseDir, skillDirName,
|
|
30
|
+
function getSkillPath(skillDirName: string, target: TargetConfig, global: boolean): string {
|
|
31
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
32
|
+
return join(baseDir, skillDirName, target.skillFileName);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
function isSkillInstalled(skill: BundledSkill, global: boolean): boolean {
|
|
32
|
-
const skillPath = getSkillPath(skill.dirName, global);
|
|
35
|
+
function isSkillInstalled(skill: BundledSkill, target: TargetConfig, global: boolean): boolean {
|
|
36
|
+
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
33
37
|
return existsSync(skillPath);
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
function isSkillOutdated(skill: BundledSkill, global: boolean): boolean {
|
|
37
|
-
const skillPath = getSkillPath(skill.dirName, global);
|
|
40
|
+
function isSkillOutdated(skill: BundledSkill, target: TargetConfig, global: boolean): boolean {
|
|
41
|
+
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
38
42
|
if (!existsSync(skillPath)) {
|
|
39
43
|
return false;
|
|
40
44
|
}
|
|
@@ -42,35 +46,45 @@ function isSkillOutdated(skill: BundledSkill, global: boolean): boolean {
|
|
|
42
46
|
return installedContent !== skill.content;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
export function getSkillStatus(
|
|
46
|
-
|
|
49
|
+
export function getSkillStatus(
|
|
50
|
+
skill: BundledSkill,
|
|
51
|
+
target: TargetConfig,
|
|
52
|
+
global: boolean,
|
|
53
|
+
): SkillStatus {
|
|
54
|
+
if (!isSkillInstalled(skill, target, global)) {
|
|
47
55
|
return 'not_installed';
|
|
48
56
|
}
|
|
49
|
-
if (isSkillOutdated(skill, global)) {
|
|
57
|
+
if (isSkillOutdated(skill, target, global)) {
|
|
50
58
|
return 'outdated';
|
|
51
59
|
}
|
|
52
60
|
return 'installed';
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
export function getAllSkillStatuses(global: boolean): SkillInfo[] {
|
|
63
|
+
export function getAllSkillStatuses(target: TargetConfig, global: boolean): SkillInfo[] {
|
|
56
64
|
return BUNDLED_SKILLS.map((skill) => ({
|
|
57
65
|
name: skill.name,
|
|
58
|
-
status: getSkillStatus(skill, global),
|
|
59
|
-
path: isSkillInstalled(skill,
|
|
66
|
+
status: getSkillStatus(skill, target, global),
|
|
67
|
+
path: isSkillInstalled(skill, target, global)
|
|
68
|
+
? getSkillPath(skill.dirName, target, global)
|
|
69
|
+
: undefined,
|
|
60
70
|
}));
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
export function installSkill(skill: BundledSkill, global: boolean): void {
|
|
64
|
-
const baseDir = global ? getGlobalSkillsDir() : getProjectSkillsDir();
|
|
73
|
+
export function installSkill(skill: BundledSkill, target: TargetConfig, global: boolean): void {
|
|
74
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
65
75
|
const skillDir = join(baseDir, skill.dirName);
|
|
66
|
-
const skillPath = join(skillDir,
|
|
76
|
+
const skillPath = join(skillDir, target.skillFileName);
|
|
67
77
|
|
|
68
78
|
mkdirSync(skillDir, { recursive: true });
|
|
69
79
|
writeFileSync(skillPath, skill.content, 'utf-8');
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
export function uninstallSkill(
|
|
73
|
-
|
|
82
|
+
export function uninstallSkill(
|
|
83
|
+
skill: BundledSkill,
|
|
84
|
+
target: TargetConfig,
|
|
85
|
+
global: boolean,
|
|
86
|
+
): boolean {
|
|
87
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
74
88
|
const skillDir = join(baseDir, skill.dirName);
|
|
75
89
|
|
|
76
90
|
if (!existsSync(skillDir)) {
|
|
@@ -87,17 +101,17 @@ export interface InstallResult {
|
|
|
87
101
|
skipped: number;
|
|
88
102
|
}
|
|
89
103
|
|
|
90
|
-
export function installAllSkills(global: boolean): InstallResult {
|
|
104
|
+
export function installAllSkills(target: TargetConfig, global: boolean): InstallResult {
|
|
91
105
|
const result: InstallResult = { installed: 0, updated: 0, skipped: 0 };
|
|
92
106
|
|
|
93
107
|
for (const skill of BUNDLED_SKILLS) {
|
|
94
|
-
const status = getSkillStatus(skill, global);
|
|
108
|
+
const status = getSkillStatus(skill, target, global);
|
|
95
109
|
|
|
96
110
|
if (status === 'not_installed') {
|
|
97
|
-
installSkill(skill, global);
|
|
111
|
+
installSkill(skill, target, global);
|
|
98
112
|
result.installed++;
|
|
99
113
|
} else if (status === 'outdated') {
|
|
100
|
-
installSkill(skill, global);
|
|
114
|
+
installSkill(skill, target, global);
|
|
101
115
|
result.updated++;
|
|
102
116
|
} else {
|
|
103
117
|
result.skipped++;
|
|
@@ -112,11 +126,11 @@ export interface UninstallResult {
|
|
|
112
126
|
notFound: number;
|
|
113
127
|
}
|
|
114
128
|
|
|
115
|
-
export function uninstallAllSkills(global: boolean): UninstallResult {
|
|
129
|
+
export function uninstallAllSkills(target: TargetConfig, global: boolean): UninstallResult {
|
|
116
130
|
const result: UninstallResult = { removed: 0, notFound: 0 };
|
|
117
131
|
|
|
118
132
|
for (const skill of BUNDLED_SKILLS) {
|
|
119
|
-
if (uninstallSkill(skill, global)) {
|
|
133
|
+
if (uninstallSkill(skill, target, global)) {
|
|
120
134
|
result.removed++;
|
|
121
135
|
} else {
|
|
122
136
|
result.notFound++;
|
|
@@ -126,8 +140,8 @@ export function uninstallAllSkills(global: boolean): UninstallResult {
|
|
|
126
140
|
return result;
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
export function getInstallDir(global: boolean): string {
|
|
130
|
-
return global ? getGlobalSkillsDir() : getProjectSkillsDir();
|
|
143
|
+
export function getInstallDir(target: TargetConfig, global: boolean): string {
|
|
144
|
+
return global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
export function getTotalSkillCount(): number {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Target configuration for multi-AI tool support.
|
|
3
|
+
* Supports Claude Code and Codex CLI, with extensibility for future targets.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type TargetId = 'claude' | 'codex';
|
|
7
|
+
|
|
8
|
+
export interface TargetConfig {
|
|
9
|
+
id: TargetId;
|
|
10
|
+
displayName: string;
|
|
11
|
+
projectDir: string;
|
|
12
|
+
skillFileName: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const TARGET_CONFIGS: Record<TargetId, TargetConfig> = {
|
|
16
|
+
claude: {
|
|
17
|
+
id: 'claude',
|
|
18
|
+
displayName: 'Claude Code',
|
|
19
|
+
projectDir: '.claude',
|
|
20
|
+
skillFileName: 'SKILL.md',
|
|
21
|
+
},
|
|
22
|
+
codex: {
|
|
23
|
+
id: 'codex',
|
|
24
|
+
displayName: 'Codex CLI',
|
|
25
|
+
projectDir: '.codex',
|
|
26
|
+
skillFileName: 'SKILL.md',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const ALL_TARGET_IDS: TargetId[] = ['claude', 'codex'];
|
|
31
|
+
|
|
32
|
+
export function getTargetConfig(id: TargetId): TargetConfig {
|
|
33
|
+
return TARGET_CONFIGS[id];
|
|
34
|
+
}
|
package/src/spinner.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal spinner for showing loading state during async operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Array index access is safe here (modulo operation keeps it in bounds)
|
|
6
|
+
/* eslint-disable security/detect-object-injection */
|
|
7
|
+
|
|
8
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] as const;
|
|
9
|
+
const FRAME_INTERVAL_MS = 80;
|
|
10
|
+
|
|
11
|
+
export interface Spinner {
|
|
12
|
+
update(message: string): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a terminal spinner that displays a rotating animation with a message.
|
|
18
|
+
* Returns a Spinner object with update() and stop() methods.
|
|
19
|
+
*/
|
|
20
|
+
export function createSpinner(initialMessage: string): Spinner {
|
|
21
|
+
if (!process.stderr.isTTY) {
|
|
22
|
+
return {
|
|
23
|
+
update: (): void => {},
|
|
24
|
+
stop: (): void => {},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let frameIndex = 0;
|
|
29
|
+
let currentMessage = initialMessage;
|
|
30
|
+
|
|
31
|
+
const render = (): void => {
|
|
32
|
+
const frame = SPINNER_FRAMES[frameIndex];
|
|
33
|
+
process.stderr.write(`\r\x1b[K${frame} ${currentMessage}`);
|
|
34
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
render();
|
|
38
|
+
const intervalId = setInterval(render, FRAME_INTERVAL_MS);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
update(message: string): void {
|
|
42
|
+
currentMessage = message;
|
|
43
|
+
},
|
|
44
|
+
stop(): void {
|
|
45
|
+
clearInterval(intervalId);
|
|
46
|
+
process.stderr.write('\r\x1b[K');
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
package/src/tool-cache.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* Handles loading/saving tool definitions from .uloop/tools.json cache.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// File paths are constructed from Unity project root, not from untrusted user input
|
|
7
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
8
|
+
|
|
6
9
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
7
10
|
import { join } from 'path';
|
|
8
11
|
import defaultToolsData from './default-tools.json';
|
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.45.2'; // x-release-please-version
|