agy-plugins-cli 1.1.14
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/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/config.js +90 -0
- package/dist/fetcher.js +265 -0
- package/dist/hooks-merger.js +89 -0
- package/dist/index.js +388 -0
- package/dist/manager.js +151 -0
- package/dist/mcp-merger.js +88 -0
- package/dist/state.js +89 -0
- package/i18n/ja/README.md +94 -0
- package/i18n/ko/README.md +94 -0
- package/i18n/zh-CN/README.md +94 -0
- package/i18n/zh-TW/README.md +94 -0
- package/package.json +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const config_1 = require("./config");
|
|
45
|
+
const fetcher_1 = require("./fetcher");
|
|
46
|
+
const state_1 = require("./state");
|
|
47
|
+
const manager_1 = require("./manager");
|
|
48
|
+
const ora_1 = __importDefault(require("ora"));
|
|
49
|
+
const packageJson = require('../package.json');
|
|
50
|
+
const program = new commander_1.Command();
|
|
51
|
+
program
|
|
52
|
+
.name('agy-plugin')
|
|
53
|
+
.description('Antigravity CLI Package Manager')
|
|
54
|
+
.version(packageJson.version);
|
|
55
|
+
const getTargetDir = (isLocal) => {
|
|
56
|
+
return isLocal ? path.join(process.cwd(), '.agents') : path.join(os.homedir(), '.gemini', 'config');
|
|
57
|
+
};
|
|
58
|
+
const marketplaceCmd = program
|
|
59
|
+
.command('marketplace')
|
|
60
|
+
.description('Manage plugin marketplaces');
|
|
61
|
+
marketplaceCmd
|
|
62
|
+
.command('add <repo>')
|
|
63
|
+
.description('Add a new marketplace repository (e.g. ZaunEkko/agy-plugins)')
|
|
64
|
+
.action((repo) => {
|
|
65
|
+
console.log(chalk_1.default.blue(`Adding marketplace: ${repo}`));
|
|
66
|
+
(0, config_1.addMarketplace)(repo);
|
|
67
|
+
});
|
|
68
|
+
async function runCheckCommand(namespaceArg) {
|
|
69
|
+
let namespace = namespaceArg.replace('@', '').toLowerCase();
|
|
70
|
+
const config = (0, config_1.loadConfig)();
|
|
71
|
+
const targetRepo = config.marketplaces.find(repo => repo.toLowerCase().includes(namespace));
|
|
72
|
+
if (!targetRepo) {
|
|
73
|
+
console.error(chalk_1.default.red(`Error: Namespace '${namespace}' not found in your marketplaces.`));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const s = (0, ora_1.default)(`Checking namespace @${namespace}...`).start();
|
|
77
|
+
let plugins = [];
|
|
78
|
+
try {
|
|
79
|
+
plugins = await (0, fetcher_1.listPluginsInRepo)(targetRepo);
|
|
80
|
+
s.stop();
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
s.fail(chalk_1.default.red(`Failed to check namespace @${namespace}`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const { select, isCancel, multiselect } = await Promise.resolve().then(() => __importStar(require('@clack/prompts')));
|
|
87
|
+
while (true) {
|
|
88
|
+
const state = (0, state_1.loadState)();
|
|
89
|
+
const installedPlugins = Object.entries(state.plugins).filter(([k, v]) => v.repo === targetRepo).map(([k, v]) => k);
|
|
90
|
+
console.clear();
|
|
91
|
+
console.log(chalk_1.default.white.bold(namespace));
|
|
92
|
+
console.log(chalk_1.default.gray(targetRepo) + '\n');
|
|
93
|
+
console.log(chalk_1.default.white(`${plugins.length} available plugins\n`));
|
|
94
|
+
if (installedPlugins.length > 0) {
|
|
95
|
+
console.log(chalk_1.default.white.bold(`Installed plugins (${installedPlugins.length}):`));
|
|
96
|
+
installedPlugins.forEach(p => {
|
|
97
|
+
console.log(chalk_1.default.gray(`● `) + chalk_1.default.white(p.split('@')[0]));
|
|
98
|
+
});
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
const action = await select({
|
|
102
|
+
message: 'Select an action (Enter to select • Esc to go back)',
|
|
103
|
+
options: [
|
|
104
|
+
{ value: 'browse', label: `> Browse plugins (${plugins.length})` },
|
|
105
|
+
{ value: 'update', label: ` Update marketplace` },
|
|
106
|
+
{ value: 'remove', label: ` Remove marketplace` },
|
|
107
|
+
{ value: 'back', label: ` < Go back` }
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
if (isCancel(action) || action === 'back') {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (action === 'browse') {
|
|
114
|
+
const selectedPlugins = await multiselect({
|
|
115
|
+
message: 'Select plugins to install (Space to select, Enter to confirm, Esc to go back)',
|
|
116
|
+
options: plugins.map(p => {
|
|
117
|
+
const dateStr = p.date ? chalk_1.default.gray(` (updated ${new Date(p.date).toISOString().split('T')[0]})`) : '';
|
|
118
|
+
let statusStr = '';
|
|
119
|
+
const installedPlugin = state.plugins[p.name] || state.plugins[`${p.name}@${namespace}`];
|
|
120
|
+
let colorize = chalk_1.default.white;
|
|
121
|
+
if (installedPlugin) {
|
|
122
|
+
if (p.sha && p.sha !== installedPlugin.sha) {
|
|
123
|
+
statusStr = chalk_1.default.yellow(' (★ Update available)');
|
|
124
|
+
colorize = chalk_1.default.yellow;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
statusStr = chalk_1.default.green(' (✔ Installed)');
|
|
128
|
+
colorize = chalk_1.default.green;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
value: p.name,
|
|
133
|
+
label: `${colorize(p.name)}${statusStr}${dateStr}`
|
|
134
|
+
};
|
|
135
|
+
}),
|
|
136
|
+
initialValues: plugins.filter(p => installedPlugins.includes(p.name) || installedPlugins.includes(`${p.name}@${namespace}`)).map(p => p.name),
|
|
137
|
+
required: false,
|
|
138
|
+
});
|
|
139
|
+
if (isCancel(selectedPlugins)) {
|
|
140
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const toInstall = selectedPlugins;
|
|
144
|
+
if (toInstall.length > 0) {
|
|
145
|
+
for (const p of toInstall) {
|
|
146
|
+
// Skip if already installed
|
|
147
|
+
if (!installedPlugins.includes(p) && !installedPlugins.includes(`${p}@${namespace}`)) {
|
|
148
|
+
console.log(chalk_1.default.blue(`\nInstalling ${p}...`));
|
|
149
|
+
const targetDir = getTargetDir(false);
|
|
150
|
+
try {
|
|
151
|
+
const result = await (0, fetcher_1.downloadPlugin)(targetRepo, p, targetDir);
|
|
152
|
+
const sha = await (0, fetcher_1.getLatestCommitSha)(targetRepo, p);
|
|
153
|
+
if (sha) {
|
|
154
|
+
(0, state_1.recordPluginInstall)(`${p}@${namespace}`, targetRepo, sha, result.files, result.hooks);
|
|
155
|
+
}
|
|
156
|
+
console.log(chalk_1.default.green(`✓ Successfully installed ${p}`));
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
console.log(chalk_1.default.red(`Failed to install ${p}`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Yield to event loop before recreating prompt to prevent terminal hangs
|
|
165
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
166
|
+
}
|
|
167
|
+
else if (action === 'update') {
|
|
168
|
+
console.log(chalk_1.default.yellow('Update marketplace feature coming soon!'));
|
|
169
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
170
|
+
}
|
|
171
|
+
else if (action === 'remove') {
|
|
172
|
+
console.log(chalk_1.default.yellow('Remove marketplace feature coming soon!'));
|
|
173
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
marketplaceCmd
|
|
178
|
+
.command('list')
|
|
179
|
+
.description('List all registered marketplace namespaces')
|
|
180
|
+
.action(async () => {
|
|
181
|
+
const { select, isCancel, text } = await Promise.resolve().then(() => __importStar(require('@clack/prompts')));
|
|
182
|
+
// Fetch GitHub stats ONCE to avoid rate limits when looping back
|
|
183
|
+
const initialConfig = (0, config_1.loadConfig)();
|
|
184
|
+
let stats = {};
|
|
185
|
+
if (initialConfig.marketplaces.length > 0) {
|
|
186
|
+
const s = (0, ora_1.default)('Loading marketplaces...').start();
|
|
187
|
+
for (const repo of initialConfig.marketplaces) {
|
|
188
|
+
let availableCount = 0;
|
|
189
|
+
let lastUpdated = 'unknown';
|
|
190
|
+
try {
|
|
191
|
+
const plugins = await (0, fetcher_1.listPluginsInRepo)(repo);
|
|
192
|
+
availableCount = plugins.length;
|
|
193
|
+
if (plugins.length > 0) {
|
|
194
|
+
const latestDateStr = plugins.reduce((max, p) => p.date && (!max || new Date(p.date) > new Date(max)) ? p.date : max, null);
|
|
195
|
+
if (latestDateStr) {
|
|
196
|
+
const d = new Date(latestDateStr);
|
|
197
|
+
lastUpdated = `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
// Ignore
|
|
203
|
+
}
|
|
204
|
+
stats[repo] = { availableCount, lastUpdated };
|
|
205
|
+
}
|
|
206
|
+
s.stop();
|
|
207
|
+
}
|
|
208
|
+
while (true) {
|
|
209
|
+
// Reload config/state dynamically to reflect any new additions or installs
|
|
210
|
+
const config = (0, config_1.loadConfig)();
|
|
211
|
+
const state = (0, state_1.loadState)();
|
|
212
|
+
let options = [];
|
|
213
|
+
options.push({ value: 'add', label: chalk_1.default.blue('> + Add Marketplace') });
|
|
214
|
+
for (const repo of config.marketplaces) {
|
|
215
|
+
const namespace = repo.split('/')[0].toLowerCase();
|
|
216
|
+
const stat = stats[repo] || { availableCount: 0, lastUpdated: 'unknown' };
|
|
217
|
+
const installedCount = Object.values(state.plugins).filter(p => p.repo === repo).length;
|
|
218
|
+
options.push({
|
|
219
|
+
value: namespace,
|
|
220
|
+
label: chalk_1.default.white(`● `) + chalk_1.default.white.bold(namespace) + chalk_1.default.gray(` (${repo})`),
|
|
221
|
+
hint: `${stat.availableCount} available • ${installedCount} installed • Updated ${stat.lastUpdated}`
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
console.clear();
|
|
225
|
+
const action = await select({
|
|
226
|
+
message: 'Manage marketplaces',
|
|
227
|
+
options,
|
|
228
|
+
});
|
|
229
|
+
if (isCancel(action)) {
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
232
|
+
if (action === 'add') {
|
|
233
|
+
const repo = await text({
|
|
234
|
+
message: 'Enter the GitHub repository path (e.g., ZaunEkko/agy-plugins):',
|
|
235
|
+
placeholder: 'username/repo',
|
|
236
|
+
validate(value) {
|
|
237
|
+
if (!value)
|
|
238
|
+
return 'Repository path is required!';
|
|
239
|
+
if (!value.includes('/'))
|
|
240
|
+
return 'Must be in username/repo format!';
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
if (isCancel(repo)) {
|
|
244
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
(0, config_1.addMarketplace)(repo);
|
|
248
|
+
console.log(chalk_1.default.green(`\n✓ Successfully added marketplace: ${repo}`));
|
|
249
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
250
|
+
// Note: For simplicity, new marketplaces added in the same session won't have pre-fetched stats until restart
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
await runCheckCommand(action);
|
|
254
|
+
// Yield to event loop when returning from inner menu to prevent terminal hangs
|
|
255
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
marketplaceCmd
|
|
260
|
+
.command('check <namespace>')
|
|
261
|
+
.description('Check and list all available plugins in a specific namespace (e.g. @zaunekko)')
|
|
262
|
+
.action(runCheckCommand);
|
|
263
|
+
program
|
|
264
|
+
.command('add <plugin>')
|
|
265
|
+
.description('Install a plugin (e.g. commit-commands@zaunekko)')
|
|
266
|
+
.option('-l, --local', 'Install locally to the current project instead of globally')
|
|
267
|
+
.action(async (pluginArg, options) => {
|
|
268
|
+
if (!pluginArg.includes('@')) {
|
|
269
|
+
console.error(chalk_1.default.red(`Error: Please specify the marketplace namespace (e.g., ${pluginArg}@zaunekko)`));
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
const parts = pluginArg.split('@');
|
|
273
|
+
const pluginName = parts[0];
|
|
274
|
+
const namespace = parts[1];
|
|
275
|
+
const config = (0, config_1.loadConfig)();
|
|
276
|
+
const targetRepo = config.marketplaces.find(repo => repo.toLowerCase().includes(namespace.toLowerCase()));
|
|
277
|
+
if (!targetRepo) {
|
|
278
|
+
console.error(chalk_1.default.red(`Error: Namespace '${namespace}' not found in your marketplaces.`));
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
console.log(chalk_1.default.green(`Installing plugin: ${pluginName} from repository: ${targetRepo}`));
|
|
282
|
+
const spinner = (0, ora_1.default)(`Downloading ${pluginName}...`).start();
|
|
283
|
+
try {
|
|
284
|
+
const targetDir = getTargetDir(options.local);
|
|
285
|
+
const result = await (0, fetcher_1.downloadPlugin)(targetRepo, pluginName, targetDir);
|
|
286
|
+
const sha = await (0, fetcher_1.getLatestCommitSha)(targetRepo, pluginName);
|
|
287
|
+
if (sha) {
|
|
288
|
+
(0, state_1.recordPluginInstall)(pluginArg, targetRepo, sha, result.files, result.hooks);
|
|
289
|
+
}
|
|
290
|
+
spinner.succeed(chalk_1.default.green(`Successfully installed ${pluginName} to ${targetDir}`));
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
spinner.fail(chalk_1.default.red(`Failed to install ${pluginName}.`));
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
program
|
|
298
|
+
.command('update [target]')
|
|
299
|
+
.description('Update all plugins, a specific plugin (e.g. plugin@namespace), or a whole namespace (e.g. @namespace)')
|
|
300
|
+
.option('-l, --local', 'Update locally to the current project instead of globally')
|
|
301
|
+
.action(async (targetArg, options) => {
|
|
302
|
+
const state = (0, state_1.loadState)();
|
|
303
|
+
let pluginsToUpdate = [];
|
|
304
|
+
if (!targetArg) {
|
|
305
|
+
// Update ALL installed plugins
|
|
306
|
+
pluginsToUpdate = Object.keys(state.plugins);
|
|
307
|
+
}
|
|
308
|
+
else if (targetArg.startsWith('@')) {
|
|
309
|
+
// Update ALL plugins in a specific namespace
|
|
310
|
+
const namespace = targetArg.replace('@', '').toLowerCase();
|
|
311
|
+
const config = (0, config_1.loadConfig)();
|
|
312
|
+
const targetRepo = config.marketplaces.find(repo => repo.toLowerCase().includes(namespace));
|
|
313
|
+
if (!targetRepo) {
|
|
314
|
+
console.error(chalk_1.default.red(`Error: Namespace '${namespace}' not found in your marketplaces.`));
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
pluginsToUpdate = Object.entries(state.plugins)
|
|
318
|
+
.filter(([k, v]) => v.repo === targetRepo)
|
|
319
|
+
.map(([k, v]) => k);
|
|
320
|
+
if (pluginsToUpdate.length === 0) {
|
|
321
|
+
console.log(chalk_1.default.yellow(`No plugins installed from namespace @${namespace}.`));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// Update a specific plugin
|
|
327
|
+
pluginsToUpdate = [targetArg];
|
|
328
|
+
}
|
|
329
|
+
if (pluginsToUpdate.length === 0) {
|
|
330
|
+
console.log(chalk_1.default.yellow(`No plugins installed yet.`));
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
for (const name of pluginsToUpdate) {
|
|
334
|
+
const installed = state.plugins[name];
|
|
335
|
+
if (!installed) {
|
|
336
|
+
console.log(chalk_1.default.red(`Plugin ${name} is not installed.`));
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
let actualPluginName = name;
|
|
340
|
+
if (name.includes('@')) {
|
|
341
|
+
actualPluginName = name.split('@')[0];
|
|
342
|
+
}
|
|
343
|
+
console.log(chalk_1.default.blue(`\nChecking for updates: ${name}...`));
|
|
344
|
+
const latestSha = await (0, fetcher_1.getLatestCommitSha)(installed.repo, actualPluginName);
|
|
345
|
+
if (!latestSha) {
|
|
346
|
+
console.log(chalk_1.default.yellow(`Could not fetch version info for ${name}. Skipping.`));
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (latestSha === installed.sha) {
|
|
350
|
+
console.log(chalk_1.default.green(`✓ ${name} is already up to date.`));
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
console.log(chalk_1.default.cyan(`Update found for ${name}. Downloading new version...`));
|
|
354
|
+
const spinner = (0, ora_1.default)(`Updating ${name}...`).start();
|
|
355
|
+
try {
|
|
356
|
+
const targetDir = getTargetDir(options.local);
|
|
357
|
+
const result = await (0, fetcher_1.downloadPlugin)(installed.repo, actualPluginName, targetDir, true);
|
|
358
|
+
(0, state_1.recordPluginInstall)(name, installed.repo, latestSha, result.files, result.hooks);
|
|
359
|
+
spinner.succeed(chalk_1.default.green(`Successfully updated ${name}.`));
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
spinner.fail(chalk_1.default.red(`Failed to update ${name}.`));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
program
|
|
368
|
+
.command('disable <plugin>')
|
|
369
|
+
.description('Disable an installed plugin')
|
|
370
|
+
.option('-l, --local', 'Disable from the local project instead of globally')
|
|
371
|
+
.action((pluginName, options) => {
|
|
372
|
+
(0, manager_1.disablePlugin)(pluginName, getTargetDir(options.local));
|
|
373
|
+
});
|
|
374
|
+
program
|
|
375
|
+
.command('enable <plugin>')
|
|
376
|
+
.description('Enable a disabled plugin')
|
|
377
|
+
.option('-l, --local', 'Enable from the local project instead of globally')
|
|
378
|
+
.action((pluginName, options) => {
|
|
379
|
+
(0, manager_1.enablePlugin)(pluginName, getTargetDir(options.local));
|
|
380
|
+
});
|
|
381
|
+
program
|
|
382
|
+
.command('remove <plugin>')
|
|
383
|
+
.description('Uninstall a plugin and remove its files')
|
|
384
|
+
.option('-l, --local', 'Remove from the local project instead of globally')
|
|
385
|
+
.action((pluginName, options) => {
|
|
386
|
+
(0, manager_1.removePlugin)(pluginName, getTargetDir(options.local));
|
|
387
|
+
});
|
|
388
|
+
program.parse(process.argv);
|
package/dist/manager.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.setPluginHooksState = setPluginHooksState;
|
|
40
|
+
exports.removePluginHooks = removePluginHooks;
|
|
41
|
+
exports.disablePlugin = disablePlugin;
|
|
42
|
+
exports.enablePlugin = enablePlugin;
|
|
43
|
+
exports.removePlugin = removePlugin;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
+
const state_1 = require("./state");
|
|
48
|
+
function getHooksPath(targetDir) {
|
|
49
|
+
return path.join(targetDir, 'hooks.json');
|
|
50
|
+
}
|
|
51
|
+
function setPluginHooksState(targetDir, hooks, enabled) {
|
|
52
|
+
const hooksPath = getHooksPath(targetDir);
|
|
53
|
+
if (!fs.existsSync(hooksPath))
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
const data = JSON.parse(fs.readFileSync(hooksPath, 'utf-8'));
|
|
57
|
+
let changed = false;
|
|
58
|
+
for (const hookName of hooks) {
|
|
59
|
+
if (data[hookName]) {
|
|
60
|
+
data[hookName].enabled = enabled;
|
|
61
|
+
changed = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (changed) {
|
|
65
|
+
fs.writeFileSync(hooksPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
console.warn(chalk_1.default.yellow(`Could not modify hooks at ${hooksPath}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function removePluginHooks(targetDir, hooks) {
|
|
73
|
+
const hooksPath = getHooksPath(targetDir);
|
|
74
|
+
if (!fs.existsSync(hooksPath))
|
|
75
|
+
return;
|
|
76
|
+
try {
|
|
77
|
+
const data = JSON.parse(fs.readFileSync(hooksPath, 'utf-8'));
|
|
78
|
+
let changed = false;
|
|
79
|
+
for (const hookName of hooks) {
|
|
80
|
+
if (data[hookName]) {
|
|
81
|
+
delete data[hookName];
|
|
82
|
+
changed = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (changed) {
|
|
86
|
+
fs.writeFileSync(hooksPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
console.warn(chalk_1.default.yellow(`Could not remove hooks at ${hooksPath}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function disablePlugin(pluginName, targetDir) {
|
|
94
|
+
const plugin = (0, state_1.getInstalledPlugin)(pluginName);
|
|
95
|
+
if (!plugin) {
|
|
96
|
+
console.log(chalk_1.default.red(`Plugin ${pluginName} is not installed.`));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (plugin.files) {
|
|
100
|
+
for (const file of plugin.files) {
|
|
101
|
+
if (fs.existsSync(file) && !file.endsWith('.disabled')) {
|
|
102
|
+
fs.renameSync(file, file + '.disabled');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (plugin.hooks) {
|
|
107
|
+
setPluginHooksState(targetDir, plugin.hooks, false);
|
|
108
|
+
}
|
|
109
|
+
console.log(chalk_1.default.green(`✓ Plugin ${pluginName} has been disabled.`));
|
|
110
|
+
}
|
|
111
|
+
function enablePlugin(pluginName, targetDir) {
|
|
112
|
+
const plugin = (0, state_1.getInstalledPlugin)(pluginName);
|
|
113
|
+
if (!plugin) {
|
|
114
|
+
console.log(chalk_1.default.red(`Plugin ${pluginName} is not installed.`));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (plugin.files) {
|
|
118
|
+
for (const file of plugin.files) {
|
|
119
|
+
const disabledFile = file + '.disabled';
|
|
120
|
+
if (fs.existsSync(disabledFile)) {
|
|
121
|
+
fs.renameSync(disabledFile, file);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (plugin.hooks) {
|
|
126
|
+
setPluginHooksState(targetDir, plugin.hooks, true);
|
|
127
|
+
}
|
|
128
|
+
console.log(chalk_1.default.green(`✓ Plugin ${pluginName} has been enabled.`));
|
|
129
|
+
}
|
|
130
|
+
function removePlugin(pluginName, targetDir) {
|
|
131
|
+
const plugin = (0, state_1.getInstalledPlugin)(pluginName);
|
|
132
|
+
if (!plugin) {
|
|
133
|
+
console.log(chalk_1.default.red(`Plugin ${pluginName} is not installed.`));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (plugin.files) {
|
|
137
|
+
for (const file of plugin.files) {
|
|
138
|
+
if (fs.existsSync(file)) {
|
|
139
|
+
fs.unlinkSync(file);
|
|
140
|
+
}
|
|
141
|
+
else if (fs.existsSync(file + '.disabled')) {
|
|
142
|
+
fs.unlinkSync(file + '.disabled');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (plugin.hooks) {
|
|
147
|
+
removePluginHooks(targetDir, plugin.hooks);
|
|
148
|
+
}
|
|
149
|
+
(0, state_1.removePluginState)(pluginName);
|
|
150
|
+
console.log(chalk_1.default.green(`✓ Plugin ${pluginName} has been removed.`));
|
|
151
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.mergeMcpConfig = mergeMcpConfig;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
function mergeMcpConfig(localMcpPath, remoteMcpContent) {
|
|
44
|
+
let localMcp = { mcpServers: {} };
|
|
45
|
+
// Read existing local mcp.json if it exists
|
|
46
|
+
if (fs.existsSync(localMcpPath)) {
|
|
47
|
+
try {
|
|
48
|
+
const data = fs.readFileSync(localMcpPath, 'utf-8');
|
|
49
|
+
localMcp = JSON.parse(data);
|
|
50
|
+
if (!localMcp.mcpServers) {
|
|
51
|
+
localMcp.mcpServers = {};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
console.warn(chalk_1.default.yellow(`Warning: Could not parse existing local mcp.json at ${localMcpPath}. It will be overwritten.`));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Parse remote mcp.json
|
|
59
|
+
let remoteMcp = {};
|
|
60
|
+
try {
|
|
61
|
+
remoteMcp = JSON.parse(remoteMcpContent);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
console.error(chalk_1.default.red(`Error: Could not parse remote mcp.json. Skipping MCP merge.`));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (remoteMcp.mcpServers) {
|
|
68
|
+
let addedCount = 0;
|
|
69
|
+
for (const [serverName, serverConfig] of Object.entries(remoteMcp.mcpServers)) {
|
|
70
|
+
if (localMcp.mcpServers[serverName]) {
|
|
71
|
+
console.log(chalk_1.default.yellow(`Warning: MCP server '${serverName}' already exists locally. Overwriting...`));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
addedCount++;
|
|
75
|
+
}
|
|
76
|
+
localMcp.mcpServers[serverName] = serverConfig;
|
|
77
|
+
}
|
|
78
|
+
// Save merged config
|
|
79
|
+
fs.mkdirSync(path.dirname(localMcpPath), { recursive: true });
|
|
80
|
+
fs.writeFileSync(localMcpPath, JSON.stringify(localMcp, null, 2), 'utf-8');
|
|
81
|
+
if (addedCount > 0) {
|
|
82
|
+
console.log(chalk_1.default.green(`Successfully merged ${addedCount} MCP server(s) into ${localMcpPath}`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(chalk_1.default.gray(`Remote mcp.json contains no mcpServers. Skipping.`));
|
|
87
|
+
}
|
|
88
|
+
}
|