uloop-cli 0.66.1 → 0.67.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 +57 -20
- package/dist/cli.bundle.cjs.map +2 -2
- package/package.json +3 -3
- package/src/__tests__/port-resolver.test.ts +7 -23
- package/src/__tests__/project-root.test.ts +97 -0
- package/src/cli.ts +17 -1
- package/src/default-tools.json +1 -1
- package/src/direct-unity-client.ts +1 -1
- package/src/port-resolver.ts +0 -10
- package/src/project-root.ts +12 -2
- package/src/skills/skills-command.ts +27 -3
- package/src/skills/target-config.ts +20 -2
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uloop-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.0",
|
|
4
4
|
"//version": "x-release-please-version",
|
|
5
5
|
"description": "CLI tool for Unity Editor communication via uLoopMCP",
|
|
6
6
|
"main": "dist/cli.bundle.cjs",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@types/node": "25.3.0",
|
|
52
52
|
"@types/semver": "7.7.1",
|
|
53
53
|
"esbuild": "0.27.3",
|
|
54
|
-
"eslint": "10.0.
|
|
54
|
+
"eslint": "10.0.2",
|
|
55
55
|
"eslint-config-prettier": "10.1.8",
|
|
56
56
|
"eslint-plugin-prettier": "5.5.5",
|
|
57
57
|
"eslint-plugin-security": "4.0.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"ts-jest": "29.4.6",
|
|
61
61
|
"tsx": "4.21.0",
|
|
62
62
|
"typescript": "5.9.3",
|
|
63
|
-
"typescript-eslint": "8.56.
|
|
63
|
+
"typescript-eslint": "8.56.1"
|
|
64
64
|
},
|
|
65
65
|
"overrides": {
|
|
66
66
|
"minimatch": "10.2.2"
|
|
@@ -6,51 +6,36 @@ import {
|
|
|
6
6
|
} from '../port-resolver.js';
|
|
7
7
|
|
|
8
8
|
describe('resolvePortFromUnitySettings', () => {
|
|
9
|
-
it('returns
|
|
9
|
+
it('returns customPort when valid', () => {
|
|
10
10
|
const port = resolvePortFromUnitySettings({
|
|
11
11
|
isServerRunning: true,
|
|
12
|
-
serverPort: 8711,
|
|
13
|
-
customPort: 8700,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
expect(port).toBe(8711);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('returns customPort when server is not running', () => {
|
|
20
|
-
const port = resolvePortFromUnitySettings({
|
|
21
|
-
isServerRunning: false,
|
|
22
|
-
serverPort: 8711,
|
|
23
12
|
customPort: 8700,
|
|
24
13
|
});
|
|
25
14
|
|
|
26
15
|
expect(port).toBe(8700);
|
|
27
16
|
});
|
|
28
17
|
|
|
29
|
-
it('returns customPort
|
|
18
|
+
it('returns customPort regardless of isServerRunning flag', () => {
|
|
30
19
|
const port = resolvePortFromUnitySettings({
|
|
31
20
|
isServerRunning: false,
|
|
32
|
-
serverPort: 0,
|
|
33
21
|
customPort: 8711,
|
|
34
22
|
});
|
|
35
23
|
|
|
36
24
|
expect(port).toBe(8711);
|
|
37
25
|
});
|
|
38
26
|
|
|
39
|
-
it('
|
|
27
|
+
it('returns null when customPort is invalid', () => {
|
|
40
28
|
const port = resolvePortFromUnitySettings({
|
|
41
|
-
isServerRunning:
|
|
42
|
-
serverPort: 8711,
|
|
29
|
+
isServerRunning: true,
|
|
43
30
|
customPort: 0,
|
|
44
31
|
});
|
|
45
32
|
|
|
46
|
-
expect(port).
|
|
33
|
+
expect(port).toBeNull();
|
|
47
34
|
});
|
|
48
35
|
|
|
49
|
-
it('returns null when
|
|
36
|
+
it('returns null when customPort is missing', () => {
|
|
50
37
|
const port = resolvePortFromUnitySettings({
|
|
51
|
-
isServerRunning:
|
|
52
|
-
serverPort: 0,
|
|
53
|
-
customPort: 0,
|
|
38
|
+
isServerRunning: true,
|
|
54
39
|
});
|
|
55
40
|
|
|
56
41
|
expect(port).toBeNull();
|
|
@@ -59,7 +44,6 @@ describe('resolvePortFromUnitySettings', () => {
|
|
|
59
44
|
it('returns null when port is not an integer', () => {
|
|
60
45
|
const port = resolvePortFromUnitySettings({
|
|
61
46
|
isServerRunning: true,
|
|
62
|
-
serverPort: 8711.5,
|
|
63
47
|
customPort: 8700.1,
|
|
64
48
|
});
|
|
65
49
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Test helpers use dynamic paths for temp directories and console assertions
|
|
2
|
+
/* eslint-disable security/detect-non-literal-fs-filename, no-console */
|
|
3
|
+
|
|
4
|
+
import { mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { tmpdir } from 'os';
|
|
7
|
+
import { findUnityProjectRoot, resetMultipleProjectsWarning } from '../project-root.js';
|
|
8
|
+
|
|
9
|
+
function createUnityProject(basePath: string, name: string): string {
|
|
10
|
+
const projectPath = join(basePath, name);
|
|
11
|
+
mkdirSync(join(projectPath, 'Assets'), { recursive: true });
|
|
12
|
+
mkdirSync(join(projectPath, 'ProjectSettings'), { recursive: true });
|
|
13
|
+
mkdirSync(join(projectPath, 'UserSettings'), { recursive: true });
|
|
14
|
+
writeFileSync(join(projectPath, 'UserSettings/UnityMcpSettings.json'), '{}');
|
|
15
|
+
return projectPath;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('findUnityProjectRoot', () => {
|
|
19
|
+
let testDir: string;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
resetMultipleProjectsWarning();
|
|
23
|
+
testDir = join(tmpdir(), `uloop-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
24
|
+
mkdirSync(testDir, { recursive: true });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns null when no Unity project is found', () => {
|
|
32
|
+
const result = findUnityProjectRoot(testDir);
|
|
33
|
+
|
|
34
|
+
expect(result).toBeNull();
|
|
35
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('returns project path without warning when single project found', () => {
|
|
39
|
+
const projectPath = createUnityProject(testDir, 'MyProject');
|
|
40
|
+
|
|
41
|
+
const result = findUnityProjectRoot(testDir);
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(projectPath);
|
|
44
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('warns once when multiple projects found', () => {
|
|
48
|
+
createUnityProject(testDir, 'ProjectA');
|
|
49
|
+
createUnityProject(testDir, 'ProjectB');
|
|
50
|
+
|
|
51
|
+
findUnityProjectRoot(testDir);
|
|
52
|
+
|
|
53
|
+
expect(console.error).toHaveBeenCalled();
|
|
54
|
+
const calls = (console.error as jest.Mock).mock.calls.map((c: unknown[]) => c[0] as string);
|
|
55
|
+
const warningLine = calls.find((msg: string) => msg.includes('Multiple Unity projects'));
|
|
56
|
+
expect(warningLine).toBeDefined();
|
|
57
|
+
const actionLine = calls.find((msg: string) => msg.includes('--project-path'));
|
|
58
|
+
expect(actionLine).toBeDefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('does not warn on second call (once-per-process)', () => {
|
|
62
|
+
createUnityProject(testDir, 'ProjectA');
|
|
63
|
+
createUnityProject(testDir, 'ProjectB');
|
|
64
|
+
|
|
65
|
+
findUnityProjectRoot(testDir);
|
|
66
|
+
(console.error as jest.Mock).mockClear();
|
|
67
|
+
|
|
68
|
+
findUnityProjectRoot(testDir);
|
|
69
|
+
|
|
70
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('warns again after resetMultipleProjectsWarning()', () => {
|
|
74
|
+
createUnityProject(testDir, 'ProjectA');
|
|
75
|
+
createUnityProject(testDir, 'ProjectB');
|
|
76
|
+
|
|
77
|
+
findUnityProjectRoot(testDir);
|
|
78
|
+
(console.error as jest.Mock).mockClear();
|
|
79
|
+
|
|
80
|
+
resetMultipleProjectsWarning();
|
|
81
|
+
findUnityProjectRoot(testDir);
|
|
82
|
+
|
|
83
|
+
expect(console.error).toHaveBeenCalled();
|
|
84
|
+
const calls = (console.error as jest.Mock).mock.calls.map((c: unknown[]) => c[0] as string);
|
|
85
|
+
const warningLine = calls.find((msg: string) => msg.includes('Multiple Unity projects'));
|
|
86
|
+
expect(warningLine).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('returns first project alphabetically when multiple found', () => {
|
|
90
|
+
createUnityProject(testDir, 'Zebra');
|
|
91
|
+
createUnityProject(testDir, 'Alpha');
|
|
92
|
+
|
|
93
|
+
const result = findUnityProjectRoot(testDir);
|
|
94
|
+
|
|
95
|
+
expect(result).toBe(join(testDir, 'Alpha'));
|
|
96
|
+
});
|
|
97
|
+
});
|
package/src/cli.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
loadToolsCache,
|
|
25
25
|
hasCacheFile,
|
|
26
|
+
getDefaultTools,
|
|
26
27
|
ToolDefinition,
|
|
27
28
|
ToolProperty,
|
|
28
29
|
getCachedServerVersion,
|
|
@@ -61,7 +62,8 @@ const program = new Command();
|
|
|
61
62
|
program
|
|
62
63
|
.name('uloop')
|
|
63
64
|
.description('Unity MCP CLI - Direct communication with Unity Editor')
|
|
64
|
-
.version(VERSION, '-v, --version', 'Output the version number')
|
|
65
|
+
.version(VERSION, '-v, --version', 'Output the version number')
|
|
66
|
+
.showHelpAfterError('(run with -h for available options)');
|
|
65
67
|
|
|
66
68
|
// --list-commands: Output command names for shell completion
|
|
67
69
|
program.option('--list-commands', 'List all command names (for shell completion)');
|
|
@@ -813,6 +815,20 @@ async function main(): Promise<void> {
|
|
|
813
815
|
const cmdName = args.find((arg) => !arg.startsWith('-'));
|
|
814
816
|
const syncGlobalOptions = extractSyncGlobalOptions(args);
|
|
815
817
|
|
|
818
|
+
// No command name = no Unity operation; skip project detection
|
|
819
|
+
const NO_PROJECT_COMMANDS = [UPDATE_COMMAND, 'completion'] as const;
|
|
820
|
+
const skipProjectDetection =
|
|
821
|
+
cmdName === undefined || (NO_PROJECT_COMMANDS as readonly string[]).includes(cmdName);
|
|
822
|
+
|
|
823
|
+
if (skipProjectDetection) {
|
|
824
|
+
const defaultTools = getDefaultTools();
|
|
825
|
+
for (const tool of defaultTools.tools) {
|
|
826
|
+
registerToolCommand(tool);
|
|
827
|
+
}
|
|
828
|
+
program.parse();
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
816
832
|
if (!shouldSkipAutoSync(cmdName, args)) {
|
|
817
833
|
// Check if cache version is outdated and auto-sync if needed
|
|
818
834
|
const cachedVersion = loadToolsCache().version;
|
package/src/default-tools.json
CHANGED
|
@@ -84,7 +84,7 @@ export class DirectUnityClient {
|
|
|
84
84
|
cleanup();
|
|
85
85
|
reject(
|
|
86
86
|
new Error(
|
|
87
|
-
`Request timed out after ${NETWORK_TIMEOUT_MS}ms. Unity may be frozen or busy. [For AI]
|
|
87
|
+
`Request timed out after ${NETWORK_TIMEOUT_MS}ms. Unity may be frozen or busy. [For AI] Report this to the user and ask how to proceed.`,
|
|
88
88
|
),
|
|
89
89
|
);
|
|
90
90
|
}, NETWORK_TIMEOUT_MS);
|
package/src/port-resolver.ts
CHANGED
|
@@ -15,7 +15,6 @@ const DEFAULT_PORT = 8700;
|
|
|
15
15
|
|
|
16
16
|
interface UnityMcpSettings {
|
|
17
17
|
isServerRunning?: boolean;
|
|
18
|
-
serverPort?: number;
|
|
19
18
|
customPort?: number;
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -36,21 +35,12 @@ function normalizePort(port: unknown): number | null {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
export function resolvePortFromUnitySettings(settings: UnityMcpSettings): number | null {
|
|
39
|
-
const serverPort = normalizePort(settings.serverPort);
|
|
40
38
|
const customPort = normalizePort(settings.customPort);
|
|
41
39
|
|
|
42
|
-
if (settings.isServerRunning === true && serverPort !== null) {
|
|
43
|
-
return serverPort;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
40
|
if (customPort !== null) {
|
|
47
41
|
return customPort;
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
if (serverPort !== null) {
|
|
51
|
-
return serverPort;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
44
|
return null;
|
|
55
45
|
}
|
|
56
46
|
|
package/src/project-root.ts
CHANGED
|
@@ -113,17 +113,27 @@ function findUnityProjectInParents(startPath: string): string | null {
|
|
|
113
113
|
*
|
|
114
114
|
* Returns null if no Unity project is found.
|
|
115
115
|
*/
|
|
116
|
+
let hasWarnedMultipleProjects = false;
|
|
117
|
+
|
|
118
|
+
/** @internal Reset warning state for testing */
|
|
119
|
+
export function resetMultipleProjectsWarning(): void {
|
|
120
|
+
hasWarnedMultipleProjects = false;
|
|
121
|
+
}
|
|
122
|
+
|
|
116
123
|
export function findUnityProjectRoot(startPath: string = process.cwd()): string | null {
|
|
117
124
|
const childProjects = findUnityProjectsInChildren(startPath, CHILD_SEARCH_MAX_DEPTH);
|
|
118
125
|
|
|
119
126
|
if (childProjects.length > 0) {
|
|
120
|
-
if (childProjects.length > 1) {
|
|
127
|
+
if (childProjects.length > 1 && !hasWarnedMultipleProjects) {
|
|
128
|
+
hasWarnedMultipleProjects = true;
|
|
121
129
|
/* eslint-disable no-console -- CLI user-facing warning output */
|
|
122
130
|
console.error('\x1b[33mWarning: Multiple Unity projects found in child directories:\x1b[0m');
|
|
123
131
|
for (const project of childProjects) {
|
|
124
132
|
console.error(` - ${project}`);
|
|
125
133
|
}
|
|
126
|
-
console.error(
|
|
134
|
+
console.error(
|
|
135
|
+
'\x1b[33mRun from a Unity project root or use --project-path to specify one.\x1b[0m',
|
|
136
|
+
);
|
|
127
137
|
console.error('');
|
|
128
138
|
/* eslint-enable no-console */
|
|
129
139
|
}
|
|
@@ -19,6 +19,9 @@ interface SkillsOptions {
|
|
|
19
19
|
global?: boolean;
|
|
20
20
|
claude?: boolean;
|
|
21
21
|
codex?: boolean;
|
|
22
|
+
cursor?: boolean;
|
|
23
|
+
gemini?: boolean;
|
|
24
|
+
windsurf?: boolean;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export function registerSkillsCommand(program: Command): void {
|
|
@@ -32,6 +35,9 @@ export function registerSkillsCommand(program: Command): void {
|
|
|
32
35
|
.option('-g, --global', 'Check global installation')
|
|
33
36
|
.option('--claude', 'Check Claude Code installation')
|
|
34
37
|
.option('--codex', 'Check Codex CLI installation')
|
|
38
|
+
.option('--cursor', 'Check Cursor installation')
|
|
39
|
+
.option('--gemini', 'Check Gemini CLI installation')
|
|
40
|
+
.option('--windsurf', 'Check Windsurf installation')
|
|
35
41
|
.action((options: SkillsOptions) => {
|
|
36
42
|
const targets = resolveTargets(options);
|
|
37
43
|
const global = options.global ?? false;
|
|
@@ -44,6 +50,9 @@ export function registerSkillsCommand(program: Command): void {
|
|
|
44
50
|
.option('-g, --global', 'Install to global location')
|
|
45
51
|
.option('--claude', 'Install to Claude Code')
|
|
46
52
|
.option('--codex', 'Install to Codex CLI')
|
|
53
|
+
.option('--cursor', 'Install to Cursor')
|
|
54
|
+
.option('--gemini', 'Install to Gemini CLI')
|
|
55
|
+
.option('--windsurf', 'Install to Windsurf')
|
|
47
56
|
.action((options: SkillsOptions) => {
|
|
48
57
|
const targets = resolveTargets(options);
|
|
49
58
|
if (targets.length === 0) {
|
|
@@ -59,6 +68,9 @@ export function registerSkillsCommand(program: Command): void {
|
|
|
59
68
|
.option('-g, --global', 'Uninstall from global location')
|
|
60
69
|
.option('--claude', 'Uninstall from Claude Code')
|
|
61
70
|
.option('--codex', 'Uninstall from Codex CLI')
|
|
71
|
+
.option('--cursor', 'Uninstall from Cursor')
|
|
72
|
+
.option('--gemini', 'Uninstall from Gemini CLI')
|
|
73
|
+
.option('--windsurf', 'Uninstall from Windsurf')
|
|
62
74
|
.action((options: SkillsOptions) => {
|
|
63
75
|
const targets = resolveTargets(options);
|
|
64
76
|
if (targets.length === 0) {
|
|
@@ -77,6 +89,15 @@ function resolveTargets(options: SkillsOptions): TargetConfig[] {
|
|
|
77
89
|
if (options.codex) {
|
|
78
90
|
targets.push(getTargetConfig('codex'));
|
|
79
91
|
}
|
|
92
|
+
if (options.cursor) {
|
|
93
|
+
targets.push(getTargetConfig('cursor'));
|
|
94
|
+
}
|
|
95
|
+
if (options.gemini) {
|
|
96
|
+
targets.push(getTargetConfig('gemini'));
|
|
97
|
+
}
|
|
98
|
+
if (options.windsurf) {
|
|
99
|
+
targets.push(getTargetConfig('windsurf'));
|
|
100
|
+
}
|
|
80
101
|
return targets;
|
|
81
102
|
}
|
|
82
103
|
|
|
@@ -86,14 +107,17 @@ function showTargetGuidance(command: string): void {
|
|
|
86
107
|
console.log('Available targets:');
|
|
87
108
|
console.log(' --claude Claude Code (.claude/skills/)');
|
|
88
109
|
console.log(' --codex Codex CLI (.codex/skills/)');
|
|
110
|
+
console.log(' --cursor Cursor (.cursor/skills/)');
|
|
111
|
+
console.log(' --gemini Gemini CLI (.gemini/skills/)');
|
|
112
|
+
console.log(' --windsurf Windsurf (.windsurf/skills/)');
|
|
89
113
|
console.log('');
|
|
90
114
|
console.log('Options:');
|
|
91
|
-
console.log(' -g, --global Use global location
|
|
115
|
+
console.log(' -g, --global Use global location');
|
|
92
116
|
console.log('');
|
|
93
117
|
console.log('Examples:');
|
|
94
118
|
console.log(` uloop skills ${command} --claude`);
|
|
95
|
-
console.log(` uloop skills ${command} --
|
|
96
|
-
console.log(` uloop skills ${command} --claude --codex`);
|
|
119
|
+
console.log(` uloop skills ${command} --cursor --global`);
|
|
120
|
+
console.log(` uloop skills ${command} --claude --codex --cursor --gemini`);
|
|
97
121
|
}
|
|
98
122
|
|
|
99
123
|
function listSkills(targets: TargetConfig[], global: boolean): void {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Supports Claude Code and Codex CLI, with extensibility for future targets.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export type TargetId = 'claude' | 'codex';
|
|
6
|
+
export type TargetId = 'claude' | 'codex' | 'cursor' | 'gemini' | 'windsurf';
|
|
7
7
|
|
|
8
8
|
export interface TargetConfig {
|
|
9
9
|
id: TargetId;
|
|
@@ -25,9 +25,27 @@ export const TARGET_CONFIGS: Record<TargetId, TargetConfig> = {
|
|
|
25
25
|
projectDir: '.codex',
|
|
26
26
|
skillFileName: 'SKILL.md',
|
|
27
27
|
},
|
|
28
|
+
cursor: {
|
|
29
|
+
id: 'cursor',
|
|
30
|
+
displayName: 'Cursor',
|
|
31
|
+
projectDir: '.cursor',
|
|
32
|
+
skillFileName: 'SKILL.md',
|
|
33
|
+
},
|
|
34
|
+
gemini: {
|
|
35
|
+
id: 'gemini',
|
|
36
|
+
displayName: 'Gemini CLI',
|
|
37
|
+
projectDir: '.gemini',
|
|
38
|
+
skillFileName: 'SKILL.md',
|
|
39
|
+
},
|
|
40
|
+
windsurf: {
|
|
41
|
+
id: 'windsurf',
|
|
42
|
+
displayName: 'Windsurf',
|
|
43
|
+
projectDir: '.windsurf',
|
|
44
|
+
skillFileName: 'SKILL.md',
|
|
45
|
+
},
|
|
28
46
|
};
|
|
29
47
|
|
|
30
|
-
export const ALL_TARGET_IDS: TargetId[] = ['claude', 'codex'];
|
|
48
|
+
export const ALL_TARGET_IDS: TargetId[] = ['claude', 'codex', 'cursor', 'gemini', 'windsurf'];
|
|
31
49
|
|
|
32
50
|
export function getTargetConfig(id: TargetId): TargetConfig {
|
|
33
51
|
// eslint-disable-next-line security/detect-object-injection -- id is type-constrained to TargetId union type
|
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.67.0'; // x-release-please-version
|