godot-cli 0.13.3 → 0.14.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/CHANGELOG.md +12 -0
- package/README.md +31 -1
- package/dist/commands/install-extension.d.ts +2 -0
- package/dist/commands/install-extension.js +63 -0
- package/dist/commands/install-extension.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/install-extension.d.ts +20 -0
- package/dist/lib/install-extension.js +183 -0
- package/dist/lib/install-extension.js.map +1 -0
- package/dist/lib/types.d.ts +55 -0
- package/dist/lib.d.ts +4 -1
- package/dist/lib.js +4 -0
- package/dist/lib.js.map +1 -1
- package/dist/utils/extension-install.d.ts +41 -0
- package/dist/utils/extension-install.js +223 -0
- package/dist/utils/extension-install.js.map +1 -0
- package/dist/utils/extensions-catalog.d.ts +35 -0
- package/dist/utils/extensions-catalog.js +46 -0
- package/dist/utils/extensions-catalog.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `godot-cli` are documented in this file.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
- `install-extension <id> [path]` — install a Godot-MCP **extension** (an optional AI-tool-family package)
|
|
8
|
+
into a Godot C# project: resolve `<id>` from the shared catalog, add/update its `<PackageReference>` in
|
|
9
|
+
the project `.csproj` (added when absent, version-bumped only when newer, no-op when up to date), then
|
|
10
|
+
ask the user to rebuild. Idempotent and behaviorally identical to the in-editor Extensions dock.
|
|
11
|
+
- Added `installExtension` to the library API, plus the shared `EXTENSIONS_CATALOG` + `findExtension`
|
|
12
|
+
exports so the app can render/install the same list the dock + CLI use.
|
|
13
|
+
- The extension catalog (`addons/godot_mcp/extensions.catalog.json`) is now the single source of truth
|
|
14
|
+
consumed by all three channels: the dock parses it via an embedded resource, the CLI mirrors it
|
|
15
|
+
(`extensions-catalog.ts`, parity-tested), and the app imports it from the `godot-cli` library.
|
|
16
|
+
|
|
5
17
|
## 0.1.0
|
|
6
18
|
|
|
7
19
|
Initial release. A cross-platform CLI for Godot-MCP, mirroring `unity-mcp-cli`'s feature set and structure,
|
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ Requires Node `^20.19.0 || >=22.12.0`.
|
|
|
36
36
|
| `configure [path]` | List / enable / disable tools, prompts, and resources in the project-local `.godot-mcp/features.json`. |
|
|
37
37
|
| `close [path]` | Gracefully stop the Godot editor running for a project (`--force` to hard-kill). |
|
|
38
38
|
| `install-plugin [path]` | Install the `godot_mcp` addon end-to-end: materialize `res://addons/godot_mcp/` (download the matching GitHub release, or `--source <path>` a local copy), add the required NuGet `PackageReference`s to the project `.csproj`, and enable the plugin. Idempotent. |
|
|
39
|
+
| `install-extension <id> [path]` | Install a Godot-MCP **extension** (an optional AI-tool-family package) into the project: resolve `<id>` from the shared catalog, add/update its `<PackageReference>` in the project `.csproj`, then rebuild to restore. Idempotent — behaviorally identical to the in-editor dock. |
|
|
39
40
|
| `remove-plugin [path]` | Disable the `godot_mcp` addon in `project.godot` `[editor_plugins]` (does not delete the addon files). |
|
|
40
41
|
| `update` | Check npm for a newer `godot-cli` and install it. |
|
|
41
42
|
|
|
@@ -141,12 +142,41 @@ It performs three steps:
|
|
|
141
142
|
It is library-safe (returns a `{ kind: 'success' | 'failure' }` union; never throws past the public
|
|
142
143
|
boundary) and idempotent — re-running on an already-installed project reports no change.
|
|
143
144
|
|
|
145
|
+
## `install-extension`
|
|
146
|
+
|
|
147
|
+
`godot-cli install-extension <id> [path]` installs an optional Godot-MCP **extension** (a package that
|
|
148
|
+
adds more AI tool families) into a Godot C# project — the terminal/library channel for the same install
|
|
149
|
+
the in-editor **Extensions** dock performs:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
godot-cli install-extension com.IvanMurzak.Godot.MCP.ProBuilder ./MyGodotProject # add/update the PackageReference
|
|
153
|
+
godot-cli install-extension "ProBuilder Tools" # resolve by name; default cwd
|
|
154
|
+
godot-cli install-extension com.IvanMurzak.Godot.MCP.ProBuilder --version 1.3.0 # override the catalog pin
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- Resolves `<id>` against the **shared extension catalog** (`addons/godot_mcp/extensions.catalog.json` —
|
|
158
|
+
the single source of truth the dock, the CLI, and the app all consume), matching by package id (then
|
|
159
|
+
name), case-insensitive.
|
|
160
|
+
- Read-modify-writes a `<PackageReference Include="<packageId>" Version="<version>" />` into the project's
|
|
161
|
+
root `.csproj`: **added** when absent, version **bumped** only when the catalog (or `--version`) pins a
|
|
162
|
+
newer version, and a **no-op** when already up to date — then asks you to rebuild (`godot-cli build`) so
|
|
163
|
+
Godot restores + compiles the new package.
|
|
164
|
+
- Behaviorally identical to the dock's `ExtensionInstaller` (the same add / update / no-op + numeric
|
|
165
|
+
version-compare rules; verified by a shared scenario set on both sides).
|
|
166
|
+
|
|
167
|
+
> The catalog ships **empty** until the first Godot-MCP extension package is published, so every `<id>` is
|
|
168
|
+
> currently reported as an unknown extension — there is nothing to install yet.
|
|
169
|
+
|
|
144
170
|
## Library API
|
|
145
171
|
|
|
146
172
|
The package also exports a side-effect-free library (the `.` entry):
|
|
147
173
|
|
|
148
174
|
```ts
|
|
149
|
-
import { openProject, runTool, setupMcp, installPlugin } from 'godot-cli';
|
|
175
|
+
import { openProject, runTool, setupMcp, installPlugin, installExtension } from 'godot-cli';
|
|
176
|
+
|
|
177
|
+
// The shared extension catalog + lookup helpers are exported too, so a GUI (the app)
|
|
178
|
+
// can render the same list the dock + CLI install from:
|
|
179
|
+
import { EXTENSIONS_CATALOG, findExtension } from 'godot-cli';
|
|
150
180
|
```
|
|
151
181
|
|
|
152
182
|
Every function returns a discriminated union (`{ kind: 'success', ... }` / `{ kind: 'failure', error }`)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as ui from '../utils/ui.js';
|
|
4
|
+
import { verbose } from '../utils/ui.js';
|
|
5
|
+
import { installExtension } from '../lib/install-extension.js';
|
|
6
|
+
export const installExtensionCommand = new Command('install-extension')
|
|
7
|
+
.description('Install a Godot-MCP extension into a Godot C# project: resolve the extension <id> from the shared catalog, add (or update) its <PackageReference> in the project .csproj, then rebuild to restore it. Idempotent. The project path defaults to the current directory (like install-plugin).')
|
|
8
|
+
.argument('<id>', 'Extension id to install (the package id, or the extension name)')
|
|
9
|
+
.argument('[path]', 'Path to the Godot project (defaults to the current directory)')
|
|
10
|
+
.option('--path <path>', 'Path to the Godot project')
|
|
11
|
+
.option('--version <x.y.z>', 'Override the catalog-pinned version to install')
|
|
12
|
+
.action(async (id, positionalPath, options) => {
|
|
13
|
+
const resolvedPath = positionalPath ?? options.path ?? process.cwd();
|
|
14
|
+
const projectPath = path.resolve(resolvedPath);
|
|
15
|
+
ui.heading('Installing Godot-MCP extension');
|
|
16
|
+
verbose(`Extension id: ${id}`);
|
|
17
|
+
verbose(`Project path: ${projectPath}`);
|
|
18
|
+
if (options.version)
|
|
19
|
+
verbose(`--version: ${options.version}`);
|
|
20
|
+
const spinner = ui.startSpinner('Installing extension...');
|
|
21
|
+
const result = await installExtension({
|
|
22
|
+
godotProjectPath: projectPath,
|
|
23
|
+
extensionId: id,
|
|
24
|
+
version: options.version,
|
|
25
|
+
});
|
|
26
|
+
if (result.kind === 'failure') {
|
|
27
|
+
spinner.error('Failed to install extension');
|
|
28
|
+
ui.error(result.error.message);
|
|
29
|
+
for (const warning of result.warnings) {
|
|
30
|
+
console.log('');
|
|
31
|
+
ui.warn(warning);
|
|
32
|
+
}
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
switch (result.outcome) {
|
|
36
|
+
case 'added':
|
|
37
|
+
spinner.success(`Installed ${result.packageId}`);
|
|
38
|
+
break;
|
|
39
|
+
case 'updated':
|
|
40
|
+
spinner.success(`Updated ${result.packageId} to ${result.toVersion}`);
|
|
41
|
+
break;
|
|
42
|
+
case 'already-up-to-date':
|
|
43
|
+
spinner.success(`${result.packageId} is already up to date`);
|
|
44
|
+
break;
|
|
45
|
+
case 'no-project':
|
|
46
|
+
// A no-project result is a kind:'success' outcome (exit 0) — use the success
|
|
47
|
+
// indicator so the spinner doesn't show a red error contradicting the exit code.
|
|
48
|
+
// The explanation is surfaced once below via ui.label('Status', result.message).
|
|
49
|
+
spinner.success('No project .csproj found — nothing to install');
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
ui.label('Status', result.message);
|
|
53
|
+
if (result.csprojPath)
|
|
54
|
+
ui.label('Project .csproj', result.csprojPath);
|
|
55
|
+
if (result.rebuildRequired) {
|
|
56
|
+
ui.label('Next step', 'Rebuild solutions (e.g. `godot-cli build`) to restore + compile the extension.');
|
|
57
|
+
}
|
|
58
|
+
for (const warning of result.warnings) {
|
|
59
|
+
console.log('');
|
|
60
|
+
ui.warn(warning);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=install-extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-extension.js","sourceRoot":"","sources":["../../src/commands/install-extension.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createProjectCommand } from './commands/create-project.js';
|
|
|
13
13
|
import { closeCommand } from './commands/close.js';
|
|
14
14
|
import { createUpdateCommand } from './commands/update.js';
|
|
15
15
|
import { installPluginCommand } from './commands/install-plugin.js';
|
|
16
|
+
import { installExtensionCommand } from './commands/install-extension.js';
|
|
16
17
|
import { removePluginCommand } from './commands/remove-plugin.js';
|
|
17
18
|
import { configureStyledHelp, error as uiError, setVerbose } from './utils/ui.js';
|
|
18
19
|
import { checkForUpdate, isRunningViaNpx, printUpdateNotification } from './utils/update-check.js';
|
|
@@ -31,6 +32,7 @@ const subcommands = [
|
|
|
31
32
|
configureCommand,
|
|
32
33
|
createProjectCommand,
|
|
33
34
|
installPluginCommand,
|
|
35
|
+
installExtensionCommand,
|
|
34
36
|
openCommand,
|
|
35
37
|
removePluginCommand,
|
|
36
38
|
runToolCommand,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { InstallExtensionOptions, InstallExtensionResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Install (or update) a Godot-MCP extension into a consumer Godot C# project, as a
|
|
4
|
+
* REAL installer that is behaviorally identical to the in-editor dock's
|
|
5
|
+
* `ExtensionInstaller`:
|
|
6
|
+
*
|
|
7
|
+
* 1. Resolve `extensionId` to a descriptor in the SHARED catalog
|
|
8
|
+
* (`addons/godot_mcp/extensions.catalog.json`, mirrored by `EXTENSIONS_CATALOG`).
|
|
9
|
+
* 2. Locate the consumer `.csproj` at the project root.
|
|
10
|
+
* 3. Read-modify-write a `<PackageReference Include="<packageId>" Version="<version>" />`
|
|
11
|
+
* into it — added when absent, version-bumped only when the catalog (or `--version`)
|
|
12
|
+
* pins a NEWER version, and a no-op when already up to date.
|
|
13
|
+
* 4. Tell the caller a rebuild is required (Godot has no programmatic restore).
|
|
14
|
+
*
|
|
15
|
+
* Library-safe: no stdout noise, no `process.exit`, no throws past the public boundary;
|
|
16
|
+
* returns a `{ kind: 'success' | 'failure' }` union. Idempotent: a re-run that finds the
|
|
17
|
+
* reference present at an equal/newer version reports `already-up-to-date` and makes no
|
|
18
|
+
* write. Mirrors `installPlugin`'s shape exactly so the app can adopt it the same way.
|
|
19
|
+
*/
|
|
20
|
+
export declare function installExtension(opts: InstallExtensionOptions): Promise<InstallExtensionResult>;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { EXTENSIONS_CATALOG, findExtension, hasVersion, } from '../utils/extensions-catalog.js';
|
|
4
|
+
import { planExtensionInstall, CsprojParseError, } from '../utils/extension-install.js';
|
|
5
|
+
import { emitProgress } from './progress.js';
|
|
6
|
+
import { requireGodotProject } from './validation.js';
|
|
7
|
+
/**
|
|
8
|
+
* Install (or update) a Godot-MCP extension into a consumer Godot C# project, as a
|
|
9
|
+
* REAL installer that is behaviorally identical to the in-editor dock's
|
|
10
|
+
* `ExtensionInstaller`:
|
|
11
|
+
*
|
|
12
|
+
* 1. Resolve `extensionId` to a descriptor in the SHARED catalog
|
|
13
|
+
* (`addons/godot_mcp/extensions.catalog.json`, mirrored by `EXTENSIONS_CATALOG`).
|
|
14
|
+
* 2. Locate the consumer `.csproj` at the project root.
|
|
15
|
+
* 3. Read-modify-write a `<PackageReference Include="<packageId>" Version="<version>" />`
|
|
16
|
+
* into it — added when absent, version-bumped only when the catalog (or `--version`)
|
|
17
|
+
* pins a NEWER version, and a no-op when already up to date.
|
|
18
|
+
* 4. Tell the caller a rebuild is required (Godot has no programmatic restore).
|
|
19
|
+
*
|
|
20
|
+
* Library-safe: no stdout noise, no `process.exit`, no throws past the public boundary;
|
|
21
|
+
* returns a `{ kind: 'success' | 'failure' }` union. Idempotent: a re-run that finds the
|
|
22
|
+
* reference present at an equal/newer version reports `already-up-to-date` and makes no
|
|
23
|
+
* write. Mirrors `installPlugin`'s shape exactly so the app can adopt it the same way.
|
|
24
|
+
*/
|
|
25
|
+
export async function installExtension(opts) {
|
|
26
|
+
const warnings = [];
|
|
27
|
+
try {
|
|
28
|
+
const validated = requireGodotProject(opts?.godotProjectPath);
|
|
29
|
+
if (!validated.ok) {
|
|
30
|
+
return {
|
|
31
|
+
kind: 'failure',
|
|
32
|
+
success: false,
|
|
33
|
+
projectGodotPath: validated.projectGodotPath,
|
|
34
|
+
warnings,
|
|
35
|
+
error: validated.error,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const { projectPath } = validated;
|
|
39
|
+
const catalog = opts.catalog ?? EXTENSIONS_CATALOG;
|
|
40
|
+
const resolved = findExtension(opts.extensionId, catalog);
|
|
41
|
+
if (resolved === null) {
|
|
42
|
+
return {
|
|
43
|
+
kind: 'failure',
|
|
44
|
+
success: false,
|
|
45
|
+
warnings,
|
|
46
|
+
error: new Error(unknownExtensionMessage(opts.extensionId, catalog)),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Apply the optional --version override (drives both the pin written and the bump decision).
|
|
50
|
+
const descriptor = applyVersionOverride(resolved, opts.version, warnings);
|
|
51
|
+
emitProgress(opts.onProgress, {
|
|
52
|
+
phase: 'start',
|
|
53
|
+
message: `Installing extension ${descriptor.packageId}${hasVersion(descriptor) ? ` ${descriptor.version}` : ''} into ${projectPath}`,
|
|
54
|
+
});
|
|
55
|
+
const csprojPath = findConsumerCsproj(projectPath, warnings);
|
|
56
|
+
if (csprojPath === null) {
|
|
57
|
+
const message = 'No project .csproj found at the project root — open this addon inside a Godot C# project to install extensions. ' +
|
|
58
|
+
`Create one (e.g. \`godot-cli create-project --dotnet\`) and add the ${descriptor.packageId} PackageReference.`;
|
|
59
|
+
emitProgress(opts.onProgress, { phase: 'done', message: 'No project .csproj — nothing installed.' });
|
|
60
|
+
return {
|
|
61
|
+
kind: 'success',
|
|
62
|
+
success: true,
|
|
63
|
+
outcome: 'no-project',
|
|
64
|
+
changed: false,
|
|
65
|
+
rebuildRequired: false,
|
|
66
|
+
extensionId: opts.extensionId,
|
|
67
|
+
packageId: descriptor.packageId,
|
|
68
|
+
fromVersion: null,
|
|
69
|
+
toVersion: hasVersion(descriptor) ? descriptor.version : null,
|
|
70
|
+
message,
|
|
71
|
+
warnings,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const before = fs.readFileSync(csprojPath, 'utf-8');
|
|
75
|
+
let plan;
|
|
76
|
+
try {
|
|
77
|
+
plan = planExtensionInstall(descriptor, before);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (err instanceof CsprojParseError) {
|
|
81
|
+
return {
|
|
82
|
+
kind: 'failure',
|
|
83
|
+
success: false,
|
|
84
|
+
warnings,
|
|
85
|
+
error: new Error(`Could not edit the project .csproj: ${err.message}`),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
if (plan.action === 'noop') {
|
|
91
|
+
emitProgress(opts.onProgress, { phase: 'done', message: `${descriptor.packageId} is already up to date.` });
|
|
92
|
+
return {
|
|
93
|
+
kind: 'success',
|
|
94
|
+
success: true,
|
|
95
|
+
outcome: 'already-up-to-date',
|
|
96
|
+
changed: false,
|
|
97
|
+
rebuildRequired: false,
|
|
98
|
+
extensionId: opts.extensionId,
|
|
99
|
+
packageId: descriptor.packageId,
|
|
100
|
+
fromVersion: plan.fromVersion,
|
|
101
|
+
toVersion: plan.toVersion,
|
|
102
|
+
csprojPath,
|
|
103
|
+
message: `${descriptor.name} is already installed and up to date.`,
|
|
104
|
+
warnings,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
fs.writeFileSync(csprojPath, plan.resultingCsproj);
|
|
108
|
+
emitProgress(opts.onProgress, {
|
|
109
|
+
phase: 'csproj-patched',
|
|
110
|
+
message: `${plan.action === 'add' ? 'Added' : 'Updated'} ${descriptor.packageId} in ${csprojPath}`,
|
|
111
|
+
csprojPath,
|
|
112
|
+
});
|
|
113
|
+
emitProgress(opts.onProgress, { phase: 'done', message: 'Extension installed — rebuild solutions to restore.' });
|
|
114
|
+
const message = plan.action === 'add'
|
|
115
|
+
? `Added ${descriptor.packageId}${hasVersion(descriptor) ? ` ${descriptor.version}` : ''}. Rebuild solutions to restore and compile the extension.`
|
|
116
|
+
: `Updated ${descriptor.packageId} to ${plan.toVersion}. Rebuild solutions to restore the new version.`;
|
|
117
|
+
return {
|
|
118
|
+
kind: 'success',
|
|
119
|
+
success: true,
|
|
120
|
+
outcome: plan.action === 'add' ? 'added' : 'updated',
|
|
121
|
+
changed: true,
|
|
122
|
+
rebuildRequired: true,
|
|
123
|
+
extensionId: opts.extensionId,
|
|
124
|
+
packageId: descriptor.packageId,
|
|
125
|
+
fromVersion: plan.fromVersion,
|
|
126
|
+
toVersion: plan.toVersion,
|
|
127
|
+
csprojPath,
|
|
128
|
+
message,
|
|
129
|
+
warnings,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
return {
|
|
134
|
+
kind: 'failure',
|
|
135
|
+
success: false,
|
|
136
|
+
warnings,
|
|
137
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Return a descriptor with the `--version` override applied (empty/whitespace override is ignored). */
|
|
142
|
+
function applyVersionOverride(descriptor, version, warnings) {
|
|
143
|
+
const v = (version ?? '').trim();
|
|
144
|
+
if (v === '')
|
|
145
|
+
return descriptor;
|
|
146
|
+
if (descriptor.version !== null && descriptor.version !== v) {
|
|
147
|
+
warnings.push(`--version ${v} overrides the catalog pin ${descriptor.packageId}@${descriptor.version} for this install.`);
|
|
148
|
+
}
|
|
149
|
+
return { ...descriptor, version: v };
|
|
150
|
+
}
|
|
151
|
+
/** A clear "unknown extension" error that lists what IS installable (or says the catalog is empty). */
|
|
152
|
+
function unknownExtensionMessage(id, catalog) {
|
|
153
|
+
if (catalog.length === 0) {
|
|
154
|
+
return (`Unknown extension "${id}". The Godot-MCP extension catalog is currently empty ` +
|
|
155
|
+
'(no extension packages have been published yet), so there is nothing to install.');
|
|
156
|
+
}
|
|
157
|
+
const available = catalog.map((d) => d.packageId).join(', ');
|
|
158
|
+
return `Unknown extension "${id}". Available extensions: ${available}.`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Find the consumer's `.csproj` (the one Godot compiles every `.cs` into) — the single
|
|
162
|
+
* `*.csproj` at the project root. When several exist, pick the first alphabetically and
|
|
163
|
+
* warn (they all compile into the same Godot assembly). Returns null when none exist
|
|
164
|
+
* (a GDScript-only project). Mirrors `install-plugin`'s `findConsumerCsproj`.
|
|
165
|
+
*/
|
|
166
|
+
function findConsumerCsproj(projectPath, warnings) {
|
|
167
|
+
let names;
|
|
168
|
+
try {
|
|
169
|
+
names = fs.readdirSync(projectPath).filter((n) => n.toLowerCase().endsWith('.csproj'));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
if (names.length === 0)
|
|
175
|
+
return null;
|
|
176
|
+
names.sort();
|
|
177
|
+
if (names.length > 1) {
|
|
178
|
+
warnings.push(`Multiple .csproj files found at the project root (${names.join(', ')}); patched ${names[0]}. ` +
|
|
179
|
+
'If a different one is the Godot project, add the extension PackageReference to it as well.');
|
|
180
|
+
}
|
|
181
|
+
return path.join(projectPath, names[0]);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=install-extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-extension.js","sourceRoot":"","sources":["../../src/lib/install-extension.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ExtensionDescriptor } from '../utils/extensions-catalog.js';
|
|
1
2
|
/**
|
|
2
3
|
* Discriminated union describing every progress event the library can emit to
|
|
3
4
|
* the optional `onProgress` callback. Consumers narrow on `event.phase`.
|
|
@@ -415,3 +416,57 @@ export interface RemovePluginFailure {
|
|
|
415
416
|
error: Error;
|
|
416
417
|
}
|
|
417
418
|
export type RemovePluginResult = RemovePluginSuccess | RemovePluginFailure;
|
|
419
|
+
/**
|
|
420
|
+
* What `installExtension` did to the consumer `.csproj` — the CLI mirror of the dock's
|
|
421
|
+
* `ExtensionInstallOutcome`:
|
|
422
|
+
* - `added` — the `<PackageReference>` was newly added (rebuild required).
|
|
423
|
+
* - `updated` — its version was bumped up (rebuild required).
|
|
424
|
+
* - `already-up-to-date`— present at an equal/newer version, or the descriptor has no pin (no write).
|
|
425
|
+
* - `no-project` — no consumer `.csproj` was found to install into (nothing written).
|
|
426
|
+
*/
|
|
427
|
+
export type ExtensionInstallOutcome = 'added' | 'updated' | 'already-up-to-date' | 'no-project';
|
|
428
|
+
export interface InstallExtensionOptions {
|
|
429
|
+
/** Absolute or relative path to the Godot project root (holds `project.godot` + the consumer `.csproj`). */
|
|
430
|
+
godotProjectPath: string;
|
|
431
|
+
/** The extension `<id>` to install — matched against the catalog by `packageId` (then `name`), case-insensitive. */
|
|
432
|
+
extensionId: string;
|
|
433
|
+
/** Override the catalog's pinned version for this install (the `--version` flag). Ignored when empty. */
|
|
434
|
+
version?: string;
|
|
435
|
+
/**
|
|
436
|
+
* Catalog to resolve `extensionId` against. Defaults to the bundled `EXTENSIONS_CATALOG`
|
|
437
|
+
* (single-sourced from `addons/godot_mcp/extensions.catalog.json`). Injectable for tests.
|
|
438
|
+
*/
|
|
439
|
+
catalog?: readonly ExtensionDescriptor[];
|
|
440
|
+
onProgress?: ProgressCallback;
|
|
441
|
+
}
|
|
442
|
+
export interface InstallExtensionSuccess {
|
|
443
|
+
kind: 'success';
|
|
444
|
+
success: true;
|
|
445
|
+
/** The classified outcome (added / updated / already-up-to-date / no-project). */
|
|
446
|
+
outcome: ExtensionInstallOutcome;
|
|
447
|
+
/** True when the `.csproj` was written (added or updated). */
|
|
448
|
+
changed: boolean;
|
|
449
|
+
/** True when a write happened, so the consumer must rebuild solutions (Godot has no programmatic restore). */
|
|
450
|
+
rebuildRequired: boolean;
|
|
451
|
+
/** The user-supplied id that resolved to the descriptor. */
|
|
452
|
+
extensionId: string;
|
|
453
|
+
/** The resolved descriptor's package id (the `<PackageReference Include>`). */
|
|
454
|
+
packageId: string;
|
|
455
|
+
/** Version currently referenced before this install: `null` when not installed, `''` when referenced unversioned. */
|
|
456
|
+
fromVersion: string | null;
|
|
457
|
+
/** The version this install targeted (catalog pin or `--version` override), or `null` when unpinned. */
|
|
458
|
+
toVersion: string | null;
|
|
459
|
+
/** Absolute path to the consumer `.csproj`, when one was located. */
|
|
460
|
+
csprojPath?: string;
|
|
461
|
+
/** A short human-readable status line, safe to print. */
|
|
462
|
+
message: string;
|
|
463
|
+
warnings: string[];
|
|
464
|
+
}
|
|
465
|
+
export interface InstallExtensionFailure {
|
|
466
|
+
kind: 'failure';
|
|
467
|
+
success: false;
|
|
468
|
+
projectGodotPath?: string;
|
|
469
|
+
warnings: string[];
|
|
470
|
+
error: Error;
|
|
471
|
+
}
|
|
472
|
+
export type InstallExtensionResult = InstallExtensionSuccess | InstallExtensionFailure;
|
package/dist/lib.d.ts
CHANGED
|
@@ -5,4 +5,7 @@ export { runTool, runSystemTool } from './lib/run-tool.js';
|
|
|
5
5
|
export { setupMcp, listAgentIds } from './lib/setup-mcp.js';
|
|
6
6
|
export { setupSkills } from './lib/setup-skills.js';
|
|
7
7
|
export { installPlugin, removePlugin } from './lib/install-plugin.js';
|
|
8
|
-
export
|
|
8
|
+
export { installExtension } from './lib/install-extension.js';
|
|
9
|
+
export { EXTENSIONS_CATALOG, findExtension, hasVersion } from './utils/extensions-catalog.js';
|
|
10
|
+
export type { ExtensionDescriptor, ExtensionTool } from './utils/extensions-catalog.js';
|
|
11
|
+
export type { ProgressEvent, ProgressCallback, ResultKind, OpenProjectOptions, OpenProjectResult, OpenProjectSuccess, OpenProjectFailure, OpenProjectAuthOption, OpenProjectConnectionMode, BuildProjectOptions, BuildProjectResult, BuildProjectSuccess, BuildProjectFailure, BuildSkipReason, CreateProjectOptions, CreateProjectResult, CreateProjectSuccess, CreateProjectFailure, RunToolOptions, RunToolResult, RunToolSuccess, RunToolFailure, RunToolFailureReason, RunSystemToolOptions, RunSystemToolResult, RunSystemToolSuccess, RunSystemToolFailure, SetupMcpOptions, SetupMcpResult, SetupMcpSuccess, SetupMcpFailure, SetupSkillsOptions, SetupSkillsResult, SetupSkillsSuccess, SetupSkillsFailure, InstallPluginOptions, InstallPluginResult, InstallPluginSuccess, InstallPluginFailure, AddonMaterializeOutcome, CsprojPatchOutcome, RemovePluginOptions, RemovePluginResult, RemovePluginSuccess, RemovePluginFailure, InstallExtensionOptions, InstallExtensionResult, InstallExtensionSuccess, InstallExtensionFailure, ExtensionInstallOutcome, } from './lib/types.js';
|
package/dist/lib.js
CHANGED
|
@@ -19,4 +19,8 @@ export { runTool, runSystemTool } from './lib/run-tool.js';
|
|
|
19
19
|
export { setupMcp, listAgentIds } from './lib/setup-mcp.js';
|
|
20
20
|
export { setupSkills } from './lib/setup-skills.js';
|
|
21
21
|
export { installPlugin, removePlugin } from './lib/install-plugin.js';
|
|
22
|
+
export { installExtension } from './lib/install-extension.js';
|
|
23
|
+
// Shared extension catalog (single-sourced from addons/godot_mcp/extensions.catalog.json)
|
|
24
|
+
// + its lookup helpers, so the app can render the same list the dock + CLI install from.
|
|
25
|
+
export { EXTENSIONS_CATALOG, findExtension, hasVersion } from './utils/extensions-catalog.js';
|
|
22
26
|
//# sourceMappingURL=lib.js.map
|
package/dist/lib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,eAAe;AACf,+EAA+E;AAC/E,wDAAwD;AACxD,oDAAoD;AACpD,yEAAyE;AACzE,4DAA4D;AAC5D,+EAA+E;AAC/E,gEAAgE;AAChE,6EAA6E;AAC7E,EAAE;AACF,0EAA0E;AAC1E,4CAA4C;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,eAAe;AACf,+EAA+E;AAC/E,wDAAwD;AACxD,oDAAoD;AACpD,yEAAyE;AACzE,4DAA4D;AAC5D,+EAA+E;AAC/E,gEAAgE;AAChE,6EAA6E;AAC7E,EAAE;AACF,0EAA0E;AAC1E,4CAA4C;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE9D,0FAA0F;AAC1F,yFAAyF;AACzF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ExtensionDescriptor } from './extensions-catalog.js';
|
|
2
|
+
export type ExtensionInstallAction = 'add' | 'update' | 'noop';
|
|
3
|
+
export interface ExtensionInstallPlan {
|
|
4
|
+
/** The add / update / no-op decision. */
|
|
5
|
+
action: ExtensionInstallAction;
|
|
6
|
+
/** The full `.csproj` text after applying the action (=== input for a no-op). */
|
|
7
|
+
resultingCsproj: string;
|
|
8
|
+
/** The package id the plan targets. */
|
|
9
|
+
packageId: string;
|
|
10
|
+
/** Version currently referenced: `null` when not installed; `''` when referenced without a version. */
|
|
11
|
+
fromVersion: string | null;
|
|
12
|
+
/** The descriptor's target version, or `null` when the descriptor has no pin. */
|
|
13
|
+
toVersion: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** Thrown by {@link planExtensionInstall} on a genuinely unparseable `.csproj` (the lib turns it into a structured failure). */
|
|
16
|
+
export declare class CsprojParseError extends Error {
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compare two dotted numeric version strings component-by-component as integers
|
|
20
|
+
* (so `1.10.0` > `1.2.0`, unlike an ordinal string compare). A trailing pre-release
|
|
21
|
+
* / build suffix on a component (`1.0.0-rc1`) is tolerated — only the leading integer
|
|
22
|
+
* of each component is read; a non-numeric component reads as 0. Missing trailing
|
|
23
|
+
* components are treated as 0 (`1.0` === `1.0.0`). Returns >0 when `a` is newer, <0
|
|
24
|
+
* when older, 0 when equal. Exact port of C# `InstalledStateDetector.CompareVersions`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
27
|
+
/**
|
|
28
|
+
* Parse every `<PackageReference>` in the `.csproj` into a `{ packageId → version }`
|
|
29
|
+
* map (ids lower-cased — NuGet ids are case-insensitive; value is the `Version`
|
|
30
|
+
* attribute, else a child `<Version>` element, else `''`). Returns an EMPTY map for
|
|
31
|
+
* empty/whitespace input. Port of C# `InstalledStateDetector.ParsePackageReferences`
|
|
32
|
+
* (the CLI never needs the bad-XML tolerance branch — the planner validates first).
|
|
33
|
+
*/
|
|
34
|
+
export declare function parsePackageReferences(csprojText: string | null | undefined): Map<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Compute the install plan for `descriptor` against `csprojText`. Port of
|
|
37
|
+
* `ExtensionInstallPlanner.Plan`. Throws {@link CsprojParseError} only on a genuinely
|
|
38
|
+
* malformed `.csproj` (no recognizable `<Project ...>` root) — the caller maps that to
|
|
39
|
+
* a structured failure rather than corrupting the file.
|
|
40
|
+
*/
|
|
41
|
+
export declare function planExtensionInstall(descriptor: ExtensionDescriptor, csprojText: string): ExtensionInstallPlan;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// Pure, behaviorally-identical TS port of the dock's extension-install logic
|
|
2
|
+
// (`addons/godot_mcp/Runtime/Extensions/ExtensionInstallPlanner.cs` +
|
|
3
|
+
// `InstalledStateDetector.CompareVersions`). It computes the add / update / no-op
|
|
4
|
+
// plan AND the resulting `.csproj` text from a descriptor + the current `.csproj`,
|
|
5
|
+
// preserving every other `<PackageReference>` and unrelated XML.
|
|
6
|
+
//
|
|
7
|
+
// Parity contract (verified by `cli/tests/extension-install.test.ts` against the
|
|
8
|
+
// SAME scenario set as `Godot-MCP.Tests/ExtensionInstallTests.cs`):
|
|
9
|
+
// - absent reference → ADD (append; version attribute only when pinned)
|
|
10
|
+
// - present, descriptor newer → UPDATE (bump version, honoring attr vs child form)
|
|
11
|
+
// - present, no version, pinned → UPDATE (set the descriptor's pin)
|
|
12
|
+
// - present, equal/newer → NO-OP
|
|
13
|
+
// - descriptor unpinned, present→ NO-OP
|
|
14
|
+
// - version compare is numeric + tolerant (1.10.0 > 1.2.0; trailing 0; suffix-tolerant)
|
|
15
|
+
//
|
|
16
|
+
// The C# planner uses System.Xml.Linq; this uses scoped text edits (like the CLI's
|
|
17
|
+
// existing `csproj-deps.ts`) so the consumer's formatting is preserved verbatim. The
|
|
18
|
+
// add/update/no-op DECISION and the resulting parsed PackageReferences are identical.
|
|
19
|
+
//
|
|
20
|
+
// No top-level side effects; pure string transforms only.
|
|
21
|
+
import { hasVersion } from './extensions-catalog.js';
|
|
22
|
+
/** Thrown by {@link planExtensionInstall} on a genuinely unparseable `.csproj` (the lib turns it into a structured failure). */
|
|
23
|
+
export class CsprojParseError extends Error {
|
|
24
|
+
}
|
|
25
|
+
/** Escape a string for use inside a RegExp source. */
|
|
26
|
+
function escapeRegExp(value) {
|
|
27
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Compare two dotted numeric version strings component-by-component as integers
|
|
31
|
+
* (so `1.10.0` > `1.2.0`, unlike an ordinal string compare). A trailing pre-release
|
|
32
|
+
* / build suffix on a component (`1.0.0-rc1`) is tolerated — only the leading integer
|
|
33
|
+
* of each component is read; a non-numeric component reads as 0. Missing trailing
|
|
34
|
+
* components are treated as 0 (`1.0` === `1.0.0`). Returns >0 when `a` is newer, <0
|
|
35
|
+
* when older, 0 when equal. Exact port of C# `InstalledStateDetector.CompareVersions`.
|
|
36
|
+
*/
|
|
37
|
+
export function compareVersions(a, b) {
|
|
38
|
+
const pa = splitComponents(a);
|
|
39
|
+
const pb = splitComponents(b);
|
|
40
|
+
const n = Math.max(pa.length, pb.length);
|
|
41
|
+
for (let i = 0; i < n; i++) {
|
|
42
|
+
const ca = i < pa.length ? pa[i] : 0;
|
|
43
|
+
const cb = i < pb.length ? pb[i] : 0;
|
|
44
|
+
if (ca !== cb)
|
|
45
|
+
return ca < cb ? -1 : 1;
|
|
46
|
+
}
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
function splitComponents(version) {
|
|
50
|
+
if (version === null || version === undefined || version.trim() === '')
|
|
51
|
+
return [];
|
|
52
|
+
return version
|
|
53
|
+
.trim()
|
|
54
|
+
.split('.')
|
|
55
|
+
.map((part) => leadingInt(part));
|
|
56
|
+
}
|
|
57
|
+
function leadingInt(component) {
|
|
58
|
+
let end = 0;
|
|
59
|
+
while (end < component.length && component[end] >= '0' && component[end] <= '9')
|
|
60
|
+
end++;
|
|
61
|
+
if (end === 0)
|
|
62
|
+
return 0;
|
|
63
|
+
const value = Number.parseInt(component.slice(0, end), 10);
|
|
64
|
+
return Number.isNaN(value) ? 0 : value;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse every `<PackageReference>` in the `.csproj` into a `{ packageId → version }`
|
|
68
|
+
* map (ids lower-cased — NuGet ids are case-insensitive; value is the `Version`
|
|
69
|
+
* attribute, else a child `<Version>` element, else `''`). Returns an EMPTY map for
|
|
70
|
+
* empty/whitespace input. Port of C# `InstalledStateDetector.ParsePackageReferences`
|
|
71
|
+
* (the CLI never needs the bad-XML tolerance branch — the planner validates first).
|
|
72
|
+
*/
|
|
73
|
+
export function parsePackageReferences(csprojText) {
|
|
74
|
+
const map = new Map();
|
|
75
|
+
if (csprojText === null || csprojText === undefined || csprojText.trim() === '')
|
|
76
|
+
return map;
|
|
77
|
+
const elementRe = /<PackageReference\b[^>]*?(?:\/>|>[\s\S]*?<\/PackageReference>)/gi;
|
|
78
|
+
let m;
|
|
79
|
+
while ((m = elementRe.exec(csprojText)) !== null) {
|
|
80
|
+
const element = m[0];
|
|
81
|
+
const include = /\bInclude\s*=\s*"([^"]*)"/i.exec(element);
|
|
82
|
+
if (!include || include[1].trim() === '')
|
|
83
|
+
continue;
|
|
84
|
+
map.set(include[1].trim().toLowerCase(), readElementVersion(element));
|
|
85
|
+
}
|
|
86
|
+
return map;
|
|
87
|
+
}
|
|
88
|
+
/** Read the version off a single matched `<PackageReference>` element (attribute form, else child element; `''` when unversioned). */
|
|
89
|
+
function readElementVersion(element) {
|
|
90
|
+
const attr = /\bVersion\s*=\s*"([^"]*)"/i.exec(element);
|
|
91
|
+
if (attr && attr[1].trim() !== '')
|
|
92
|
+
return attr[1].trim();
|
|
93
|
+
const child = /<Version>\s*([^<]*?)\s*<\/Version>/i.exec(element);
|
|
94
|
+
if (child)
|
|
95
|
+
return child[1].trim();
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
/** Match the whole `<PackageReference>` element (either attribute order, self-close or open/close) for one id. */
|
|
99
|
+
function packageRefElementRegex(id) {
|
|
100
|
+
const escId = escapeRegExp(id);
|
|
101
|
+
return new RegExp(`<PackageReference\\b[^>]*?\\bInclude\\s*=\\s*"${escId}"[^>]*?(?:/>|>[\\s\\S]*?</PackageReference>)`, 'i');
|
|
102
|
+
}
|
|
103
|
+
function detectIndent(text) {
|
|
104
|
+
const m = text.match(/\n([ \t]*)<PackageReference\b/);
|
|
105
|
+
return m ? m[1] : ' ';
|
|
106
|
+
}
|
|
107
|
+
function detectEol(text) {
|
|
108
|
+
const crlf = (text.match(/\r\n/g) ?? []).length;
|
|
109
|
+
const lf = (text.match(/\n/g) ?? []).length - crlf;
|
|
110
|
+
return crlf > lf ? '\r\n' : '\n';
|
|
111
|
+
}
|
|
112
|
+
function renderRef(descriptor) {
|
|
113
|
+
return hasVersion(descriptor)
|
|
114
|
+
? `<PackageReference Include="${descriptor.packageId}" Version="${descriptor.version}" />`
|
|
115
|
+
: `<PackageReference Include="${descriptor.packageId}" />`;
|
|
116
|
+
}
|
|
117
|
+
/** Index of the `</ItemGroup>` closing the FIRST `<ItemGroup>` holding a `<PackageReference>`, or -1. */
|
|
118
|
+
function findPackageReferenceItemGroupClose(text) {
|
|
119
|
+
const itemGroupRe = /<ItemGroup\b[^>]*>([\s\S]*?)<\/ItemGroup>/gi;
|
|
120
|
+
let m;
|
|
121
|
+
while ((m = itemGroupRe.exec(text)) !== null) {
|
|
122
|
+
if (/<PackageReference\b/i.test(m[1])) {
|
|
123
|
+
const closeTag = '</ItemGroup>';
|
|
124
|
+
return m.index + m[0].length - closeTag.length;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return -1;
|
|
128
|
+
}
|
|
129
|
+
/** Append a new `<PackageReference>` for the descriptor, joining the first package ItemGroup or a fresh one. */
|
|
130
|
+
function appendReference(text, descriptor) {
|
|
131
|
+
const indent = detectIndent(text);
|
|
132
|
+
const eol = detectEol(text);
|
|
133
|
+
const element = `${indent}${renderRef(descriptor)}`;
|
|
134
|
+
const pkgItemGroupClose = findPackageReferenceItemGroupClose(text);
|
|
135
|
+
if (pkgItemGroupClose !== -1) {
|
|
136
|
+
// Insert before the START of the `</ItemGroup>` line, not at the `<` of `</ItemGroup>`
|
|
137
|
+
// itself: slicing at `pkgItemGroupClose` would absorb the closing tag's indent into the
|
|
138
|
+
// prefix, so the appended element would inherit that indent on top of its own and
|
|
139
|
+
// `</ItemGroup>` would lose its indent. Splitting at the line start keeps both aligned.
|
|
140
|
+
const lineStart = text.lastIndexOf('\n', pkgItemGroupClose) + 1;
|
|
141
|
+
return `${text.slice(0, lineStart)}${element}${eol}${text.slice(lineStart)}`;
|
|
142
|
+
}
|
|
143
|
+
const groupIndent = indent.length >= 2 ? indent.slice(0, Math.floor(indent.length / 2)) : ' ';
|
|
144
|
+
const newGroup = `${groupIndent}<ItemGroup>${eol}${element}${eol}${groupIndent}</ItemGroup>${eol}`;
|
|
145
|
+
const closeIdx = text.lastIndexOf('</Project>');
|
|
146
|
+
if (closeIdx === -1)
|
|
147
|
+
return `${text}${eol}${newGroup}`;
|
|
148
|
+
return `${text.slice(0, closeIdx)}${newGroup}${text.slice(closeIdx)}`;
|
|
149
|
+
}
|
|
150
|
+
/** Set the version on an existing element span, honoring whichever form it already uses (attribute, child element, or unversioned). */
|
|
151
|
+
function setElementVersion(element, version) {
|
|
152
|
+
// Existing Version="x" attribute → replace its value.
|
|
153
|
+
if (/\bVersion\s*=\s*"[^"]*"/i.test(element)) {
|
|
154
|
+
return element.replace(/\bVersion\s*=\s*"[^"]*"/i, `Version="${version}"`);
|
|
155
|
+
}
|
|
156
|
+
// Existing <Version>x</Version> child element → replace inner text (keep the child form).
|
|
157
|
+
if (/<Version>[\s\S]*?<\/Version>/i.test(element)) {
|
|
158
|
+
return element.replace(/<Version>[\s\S]*?<\/Version>/i, `<Version>${version}</Version>`);
|
|
159
|
+
}
|
|
160
|
+
// No version present → add a Version attribute to the open tag.
|
|
161
|
+
if (/\/>\s*$/.test(element)) {
|
|
162
|
+
// self-close: `<PackageReference Include="id" />` → `... Version="x" />`
|
|
163
|
+
return element.replace(/\s*\/>\s*$/, ` Version="${version}" />`);
|
|
164
|
+
}
|
|
165
|
+
// open/close pair with no child version: add the attribute to the open tag's first `>`.
|
|
166
|
+
return element.replace(/>/, ` Version="${version}">`);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Compute the install plan for `descriptor` against `csprojText`. Port of
|
|
170
|
+
* `ExtensionInstallPlanner.Plan`. Throws {@link CsprojParseError} only on a genuinely
|
|
171
|
+
* malformed `.csproj` (no recognizable `<Project ...>` root) — the caller maps that to
|
|
172
|
+
* a structured failure rather than corrupting the file.
|
|
173
|
+
*/
|
|
174
|
+
export function planExtensionInstall(descriptor, csprojText) {
|
|
175
|
+
if (!/<Project\b[\s\S]*?>/i.test(csprojText) || !/<\/Project>/i.test(csprojText)) {
|
|
176
|
+
throw new CsprojParseError('The consumer .csproj is not valid XML; refusing to edit it.');
|
|
177
|
+
}
|
|
178
|
+
const elementRe = packageRefElementRegex(descriptor.packageId);
|
|
179
|
+
const match = csprojText.match(elementRe);
|
|
180
|
+
// --- No existing reference → ADD ---
|
|
181
|
+
if (!match) {
|
|
182
|
+
return {
|
|
183
|
+
action: 'add',
|
|
184
|
+
resultingCsproj: appendReference(csprojText, descriptor),
|
|
185
|
+
packageId: descriptor.packageId,
|
|
186
|
+
fromVersion: null,
|
|
187
|
+
toVersion: hasVersion(descriptor) ? descriptor.version : null,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const element = match[0];
|
|
191
|
+
const currentVersion = readElementVersion(element);
|
|
192
|
+
// Descriptor pins no version → leave the existing reference untouched.
|
|
193
|
+
if (!hasVersion(descriptor)) {
|
|
194
|
+
return {
|
|
195
|
+
action: 'noop',
|
|
196
|
+
resultingCsproj: csprojText,
|
|
197
|
+
packageId: descriptor.packageId,
|
|
198
|
+
fromVersion: currentVersion,
|
|
199
|
+
toVersion: null,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const target = descriptor.version;
|
|
203
|
+
const needsBump = currentVersion === '' || compareVersions(target, currentVersion) > 0;
|
|
204
|
+
if (!needsBump) {
|
|
205
|
+
return {
|
|
206
|
+
action: 'noop',
|
|
207
|
+
resultingCsproj: csprojText,
|
|
208
|
+
packageId: descriptor.packageId,
|
|
209
|
+
fromVersion: currentVersion,
|
|
210
|
+
toVersion: target,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const updatedElement = setElementVersion(element, target);
|
|
214
|
+
return {
|
|
215
|
+
action: 'update',
|
|
216
|
+
// Function replacement so any `$` in the version is never treated as a replacement pattern.
|
|
217
|
+
resultingCsproj: csprojText.replace(elementRe, () => updatedElement),
|
|
218
|
+
packageId: descriptor.packageId,
|
|
219
|
+
fromVersion: currentVersion,
|
|
220
|
+
toVersion: target,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=extension-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-install.js","sourceRoot":"","sources":["../../src/utils/extension-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** One tool a catalog extension contributes — mirrors the JSON `tools[]` entry. */
|
|
2
|
+
export interface ExtensionTool {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly description: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* One installable extension — the CLI analog of the C# `GodotExtensionDescriptor`.
|
|
8
|
+
* `packageId` is the INSTALL IDENTITY (the `<PackageReference Include="...">`).
|
|
9
|
+
* `version` is `null` for a floating (unpinned) reference.
|
|
10
|
+
*/
|
|
11
|
+
export interface ExtensionDescriptor {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly description: string;
|
|
14
|
+
readonly packageId: string;
|
|
15
|
+
readonly version: string | null;
|
|
16
|
+
readonly gitUrl: string | null;
|
|
17
|
+
readonly tools: readonly ExtensionTool[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The extension catalog, single-sourced from `addons/godot_mcp/extensions.catalog.json`.
|
|
21
|
+
* Ships EMPTY until the first Godot-MCP extension package is published on nuget.org —
|
|
22
|
+
* `install-extension <id>` then reports "unknown extension" for every id, which is the
|
|
23
|
+
* correct behavior (there is nothing to install yet). The parity test keeps this in
|
|
24
|
+
* lockstep with the JSON so adding an entry there forces an update here.
|
|
25
|
+
*/
|
|
26
|
+
export declare const EXTENSIONS_CATALOG: readonly ExtensionDescriptor[];
|
|
27
|
+
/** True when a descriptor carries a concrete version pin (drives the up-to-date / update decision). */
|
|
28
|
+
export declare function hasVersion(descriptor: ExtensionDescriptor): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a user-supplied `<id>` to a catalog descriptor. Matches by `packageId`
|
|
31
|
+
* first (ordinal-ignore-case, like NuGet's case-insensitive ids — the install
|
|
32
|
+
* identity), then falls back to an exact case-insensitive `name` match for
|
|
33
|
+
* convenience. Returns `null` when absent or `id` is empty.
|
|
34
|
+
*/
|
|
35
|
+
export declare function findExtension(id: string | undefined | null, catalog?: readonly ExtensionDescriptor[]): ExtensionDescriptor | null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The CLI's typed mirror of the SHARED extension catalog — the single source of
|
|
2
|
+
// truth `addons/godot_mcp/extensions.catalog.json` (see that file's sibling
|
|
3
|
+
// `extensions.catalog.md`). It is the CLI half of the "one catalog consumed by the
|
|
4
|
+
// dock, the CLI, and the app" contract: the dock parses the JSON via an embedded
|
|
5
|
+
// resource (C# `GodotExtensionCatalog`); the CLI mirrors it here so the published
|
|
6
|
+
// npm package stays self-contained (no runtime `../addons` dependency).
|
|
7
|
+
//
|
|
8
|
+
// SINGLE SOURCE OF TRUTH: this constant MUST stay byte-equivalent to the JSON. The
|
|
9
|
+
// parity test `cli/tests/extensions-catalog-parity.test.ts` reads the addon JSON and
|
|
10
|
+
// FAILS the build if this mirror drifts — exactly the discipline `addon-deps.ts` /
|
|
11
|
+
// `addon-deps-parity.test.ts` use for the NuGet pins. Adding an extension =
|
|
12
|
+
// appending an entry to BOTH the JSON and this array (the test enforces it).
|
|
13
|
+
//
|
|
14
|
+
// No top-level side effects; pure data + pure lookups only.
|
|
15
|
+
/**
|
|
16
|
+
* The extension catalog, single-sourced from `addons/godot_mcp/extensions.catalog.json`.
|
|
17
|
+
* Ships EMPTY until the first Godot-MCP extension package is published on nuget.org —
|
|
18
|
+
* `install-extension <id>` then reports "unknown extension" for every id, which is the
|
|
19
|
+
* correct behavior (there is nothing to install yet). The parity test keeps this in
|
|
20
|
+
* lockstep with the JSON so adding an entry there forces an update here.
|
|
21
|
+
*/
|
|
22
|
+
export const EXTENSIONS_CATALOG = [
|
|
23
|
+
// (none yet — see addons/godot_mcp/extensions.catalog.json / extensions.catalog.md)
|
|
24
|
+
];
|
|
25
|
+
/** True when a descriptor carries a concrete version pin (drives the up-to-date / update decision). */
|
|
26
|
+
export function hasVersion(descriptor) {
|
|
27
|
+
return descriptor.version !== null && descriptor.version.trim() !== '';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a user-supplied `<id>` to a catalog descriptor. Matches by `packageId`
|
|
31
|
+
* first (ordinal-ignore-case, like NuGet's case-insensitive ids — the install
|
|
32
|
+
* identity), then falls back to an exact case-insensitive `name` match for
|
|
33
|
+
* convenience. Returns `null` when absent or `id` is empty.
|
|
34
|
+
*/
|
|
35
|
+
export function findExtension(id, catalog = EXTENSIONS_CATALOG) {
|
|
36
|
+
if (id === undefined || id === null)
|
|
37
|
+
return null;
|
|
38
|
+
const needle = id.trim();
|
|
39
|
+
if (needle === '')
|
|
40
|
+
return null;
|
|
41
|
+
const byPackageId = catalog.find((d) => d.packageId.toLowerCase() === needle.toLowerCase());
|
|
42
|
+
if (byPackageId)
|
|
43
|
+
return byPackageId;
|
|
44
|
+
return catalog.find((d) => d.name.toLowerCase() === needle.toLowerCase()) ?? null;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=extensions-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions-catalog.js","sourceRoot":"","sources":["../../src/utils/extensions-catalog.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4EAA4E;AAC5E,mFAAmF;AACnF,iFAAiF;AACjF,kFAAkF;AAClF,wEAAwE;AACxE,EAAE;AACF,mFAAmF;AACnF,qFAAqF;AACrF,mFAAmF;AACnF,4EAA4E;AAC5E,6EAA6E;AAC7E,EAAE;AACF,4DAA4D;AAsB5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAmC;AAChE,oFAAoF;CAC5E,CAAC;AAEX,uGAAuG;AACvG,MAAM,UAAU,UAAU,CAAC,UAA+B;IACxD,OAAO,UAAU,CAAC,OAAO,KAAK,IAAI,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,EAA6B,EAC7B,UAA0C,kBAAkB;IAE5D,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAC1D,CAAC;IACF,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AACpF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godot-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Cross-platform CLI tool for Godot-MCP (Skills & MCP). Resolves and launches the Godot editor with MCP connection env vars, runs MCP/system tools over HTTP, probes server health, configures AI agents (Claude Code, Cursor, VS Code, …), and enables/disables the godot_mcp addon. Works with Claude Code, Cursor, Copilot, and any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/lib.js",
|