frontmcp 1.2.0 → 1.3.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/package.json +4 -4
- package/src/commands/build/exec/bin-meta.d.ts +49 -0
- package/src/commands/build/exec/bin-meta.js +68 -0
- package/src/commands/build/exec/bin-meta.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +195 -3
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
- package/src/commands/build/exec/cli-runtime/plugin-emitter.d.ts +160 -0
- package/src/commands/build/exec/cli-runtime/plugin-emitter.js +512 -0
- package/src/commands/build/exec/cli-runtime/plugin-emitter.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +13 -1
- package/src/commands/build/exec/cli-runtime/schema-extractor.js +29 -3
- package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
- package/src/commands/build/exec/cli-runtime/skill-md-compose.d.ts +25 -0
- package/src/commands/build/exec/cli-runtime/skill-md-compose.js +63 -0
- package/src/commands/build/exec/cli-runtime/skill-md-compose.js.map +1 -0
- package/src/commands/build/exec/index.js +26 -0
- package/src/commands/build/exec/index.js.map +1 -1
- package/src/commands/dev/bridge/child-supervisor.d.ts +48 -0
- package/src/commands/dev/bridge/child-supervisor.js +228 -0
- package/src/commands/dev/bridge/child-supervisor.js.map +1 -0
- package/src/commands/dev/bridge/errors.d.ts +23 -0
- package/src/commands/dev/bridge/errors.js +34 -0
- package/src/commands/dev/bridge/errors.js.map +1 -0
- package/src/commands/dev/bridge/index.d.ts +30 -0
- package/src/commands/dev/bridge/index.js +220 -0
- package/src/commands/dev/bridge/index.js.map +1 -0
- package/src/commands/dev/bridge/log.d.ts +29 -0
- package/src/commands/dev/bridge/log.js +82 -0
- package/src/commands/dev/bridge/log.js.map +1 -0
- package/src/commands/dev/bridge/state-machine.d.ts +56 -0
- package/src/commands/dev/bridge/state-machine.js +245 -0
- package/src/commands/dev/bridge/state-machine.js.map +1 -0
- package/src/commands/dev/bridge/stdio-framer.d.ts +47 -0
- package/src/commands/dev/bridge/stdio-framer.js +128 -0
- package/src/commands/dev/bridge/stdio-framer.js.map +1 -0
- package/src/commands/dev/bridge/upstream-client.d.ts +49 -0
- package/src/commands/dev/bridge/upstream-client.js +159 -0
- package/src/commands/dev/bridge/upstream-client.js.map +1 -0
- package/src/commands/dev/bridge/watcher.d.ts +30 -0
- package/src/commands/dev/bridge/watcher.js +87 -0
- package/src/commands/dev/bridge/watcher.js.map +1 -0
- package/src/commands/dev/dev.d.ts +18 -1
- package/src/commands/dev/dev.js +134 -14
- package/src/commands/dev/dev.js.map +1 -1
- package/src/commands/dev/inspector.d.ts +13 -1
- package/src/commands/dev/inspector.js +77 -3
- package/src/commands/dev/inspector.js.map +1 -1
- package/src/commands/dev/port.d.ts +23 -0
- package/src/commands/dev/port.js +87 -0
- package/src/commands/dev/port.js.map +1 -0
- package/src/commands/dev/register.d.ts +1 -1
- package/src/commands/dev/register.js +28 -4
- package/src/commands/dev/register.js.map +1 -1
- package/src/commands/dev/test.d.ts +26 -1
- package/src/commands/dev/test.js +181 -64
- package/src/commands/dev/test.js.map +1 -1
- package/src/commands/eject/mcp-client.d.ts +25 -0
- package/src/commands/eject/mcp-client.js +74 -0
- package/src/commands/eject/mcp-client.js.map +1 -0
- package/src/commands/eject/register.d.ts +9 -0
- package/src/commands/eject/register.js +56 -0
- package/src/commands/eject/register.js.map +1 -0
- package/src/commands/install/install-claude-plugin.d.ts +13 -0
- package/src/commands/install/install-claude-plugin.js +327 -0
- package/src/commands/install/install-claude-plugin.js.map +1 -0
- package/src/commands/install/register.d.ts +16 -0
- package/src/commands/install/register.js +70 -0
- package/src/commands/install/register.js.map +1 -0
- package/src/commands/scaffold/create.js +44 -0
- package/src/commands/scaffold/create.js.map +1 -1
- package/src/commands/skills/from-entry.d.ts +31 -0
- package/src/commands/skills/from-entry.js +68 -0
- package/src/commands/skills/from-entry.js.map +1 -0
- package/src/commands/skills/install.d.ts +12 -0
- package/src/commands/skills/install.js +173 -8
- package/src/commands/skills/install.js.map +1 -1
- package/src/commands/skills/register.js +7 -3
- package/src/commands/skills/register.js.map +1 -1
- package/src/config/frontmcp-config.loader.d.ts +28 -0
- package/src/config/frontmcp-config.loader.js +146 -67
- package/src/config/frontmcp-config.loader.js.map +1 -1
- package/src/config/frontmcp-config.resolve.d.ts +67 -0
- package/src/config/frontmcp-config.resolve.js +118 -0
- package/src/config/frontmcp-config.resolve.js.map +1 -0
- package/src/config/frontmcp-config.schema.d.ts +207 -0
- package/src/config/frontmcp-config.schema.js +217 -1
- package/src/config/frontmcp-config.schema.js.map +1 -1
- package/src/config/frontmcp-config.types.d.ts +133 -0
- package/src/config/frontmcp-config.types.js.map +1 -1
- package/src/config/index.d.ts +2 -1
- package/src/config/index.js +3 -1
- package/src/config/index.js.map +1 -1
- package/src/core/args.d.ts +13 -0
- package/src/core/args.js.map +1 -1
- package/src/core/bridge.js +39 -0
- package/src/core/bridge.js.map +1 -1
- package/src/core/cli.d.ts +0 -6
- package/src/core/cli.js +23 -3
- package/src/core/cli.js.map +1 -1
- package/src/core/help.d.ts +1 -1
- package/src/core/help.js +27 -6
- package/src/core/help.js.map +1 -1
- package/src/core/program.d.ts +1 -1
- package/src/core/program.js +56 -12
- package/src/core/program.js.map +1 -1
- package/src/core/project-commands.d.ts +44 -0
- package/src/core/project-commands.js +216 -0
- package/src/core/project-commands.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontmcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "FrontMCP command line interface",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@clack/prompts": "^0.10.0",
|
|
34
|
-
"@frontmcp/lazy-zod": "1.
|
|
35
|
-
"@frontmcp/utils": "1.
|
|
36
|
-
"@frontmcp/skills": "1.
|
|
34
|
+
"@frontmcp/lazy-zod": "1.3.0",
|
|
35
|
+
"@frontmcp/utils": "1.3.0",
|
|
36
|
+
"@frontmcp/skills": "1.3.0",
|
|
37
37
|
"commander": "^13.0.0",
|
|
38
38
|
"tslib": "^2.3.0",
|
|
39
39
|
"vectoriadb": "^2.2.0",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue #411 — write `bin-meta.json` next to the CLI bundle. The per-bin
|
|
3
|
+
* `<bin> install -p claude|codex` reads this sidecar at runtime to drive
|
|
4
|
+
* the shared plugin-emitter without re-running schema extraction.
|
|
5
|
+
*
|
|
6
|
+
* Shape:
|
|
7
|
+
* {
|
|
8
|
+
* "name": "<bin>",
|
|
9
|
+
* "version": "<bin-version>",
|
|
10
|
+
* "description": "<from cliConfig or package.json>",
|
|
11
|
+
* "mcpDefault": { "command": "<bin>", "args": ["serve", "--stdio"] },
|
|
12
|
+
* "prompts": [{ "name", "description", "arguments": [...] }],
|
|
13
|
+
* "skills": [{ "name", "description", "instructionFile" (path under _skills/), "resourceDirs": { references?, examples?, scripts?, assets? } }]
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
import type { FrontmcpExecConfig } from './config';
|
|
17
|
+
import type { ExtractedSchema } from './cli-runtime/schema-extractor';
|
|
18
|
+
export interface BinMeta {
|
|
19
|
+
name: string;
|
|
20
|
+
version: string;
|
|
21
|
+
description: string;
|
|
22
|
+
mcpDefault: {
|
|
23
|
+
command: string;
|
|
24
|
+
args: string[];
|
|
25
|
+
};
|
|
26
|
+
prompts: Array<{
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
arguments?: Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
required?: boolean;
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|
|
35
|
+
skills: Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
tags?: string[];
|
|
39
|
+
license?: string;
|
|
40
|
+
instructionFile?: string;
|
|
41
|
+
resourceDirs?: {
|
|
42
|
+
references?: string;
|
|
43
|
+
examples?: string;
|
|
44
|
+
scripts?: string;
|
|
45
|
+
assets?: string;
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export declare function writeBinMeta(outDir: string, config: FrontmcpExecConfig, schema: ExtractedSchema): Promise<void>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Issue #411 — write `bin-meta.json` next to the CLI bundle. The per-bin
|
|
4
|
+
* `<bin> install -p claude|codex` reads this sidecar at runtime to drive
|
|
5
|
+
* the shared plugin-emitter without re-running schema extraction.
|
|
6
|
+
*
|
|
7
|
+
* Shape:
|
|
8
|
+
* {
|
|
9
|
+
* "name": "<bin>",
|
|
10
|
+
* "version": "<bin-version>",
|
|
11
|
+
* "description": "<from cliConfig or package.json>",
|
|
12
|
+
* "mcpDefault": { "command": "<bin>", "args": ["serve", "--stdio"] },
|
|
13
|
+
* "prompts": [{ "name", "description", "arguments": [...] }],
|
|
14
|
+
* "skills": [{ "name", "description", "instructionFile" (path under _skills/), "resourceDirs": { references?, examples?, scripts?, assets? } }]
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.writeBinMeta = writeBinMeta;
|
|
19
|
+
const tslib_1 = require("tslib");
|
|
20
|
+
const path = tslib_1.__importStar(require("path"));
|
|
21
|
+
const utils_1 = require("@frontmcp/utils");
|
|
22
|
+
async function writeBinMeta(outDir, config, schema) {
|
|
23
|
+
const meta = {
|
|
24
|
+
name: config.name,
|
|
25
|
+
version: config.version ?? '0.0.0',
|
|
26
|
+
description: config.cli?.description ?? `${config.name} (FrontMCP server)`,
|
|
27
|
+
mcpDefault: { command: config.name, args: ['serve', '--stdio'] },
|
|
28
|
+
prompts: schema.prompts.map((p) => ({
|
|
29
|
+
name: p.name,
|
|
30
|
+
description: p.description,
|
|
31
|
+
arguments: p.arguments,
|
|
32
|
+
})),
|
|
33
|
+
skills: schema.skillAssets.map((asset) => {
|
|
34
|
+
// The bin runtime expects RELATIVE paths under `_skills/` so the bundle
|
|
35
|
+
// is portable across install destinations.
|
|
36
|
+
const instructionFile = asset.instructionFile
|
|
37
|
+
? path.join('_skills', `${asset.skillName}--${path.basename(asset.instructionFile)}`)
|
|
38
|
+
: undefined;
|
|
39
|
+
const resourceDirs = {};
|
|
40
|
+
for (const kind of ['references', 'examples', 'scripts', 'assets']) {
|
|
41
|
+
if (asset.resourceDirs?.[kind]) {
|
|
42
|
+
resourceDirs[kind] = path.join('_skills', `${asset.skillName}--${kind}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
name: asset.skillName,
|
|
47
|
+
description: asset.description,
|
|
48
|
+
tags: asset.tags && asset.tags.length > 0 ? asset.tags : undefined,
|
|
49
|
+
license: asset.license,
|
|
50
|
+
instructionFile,
|
|
51
|
+
resourceDirs: Object.keys(resourceDirs).length > 0 ? resourceDirs : undefined,
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
// Drop undefined fields so the JSON payload matches the TS interface (no
|
|
56
|
+
// explicit "description": undefined keys land in stringified output).
|
|
57
|
+
const cleanSkills = meta.skills.map((s) => omitUndefined(s));
|
|
58
|
+
await (0, utils_1.writeJSON)(path.join(outDir, 'bin-meta.json'), { ...meta, skills: cleanSkills });
|
|
59
|
+
}
|
|
60
|
+
function omitUndefined(obj) {
|
|
61
|
+
const out = {};
|
|
62
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
63
|
+
if (v !== undefined)
|
|
64
|
+
out[k] = v;
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=bin-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin-meta.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/bin-meta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Issue #411 — write `bin-meta.json` next to the CLI bundle. The per-bin\n * `<bin> install -p claude|codex` reads this sidecar at runtime to drive\n * the shared plugin-emitter without re-running schema extraction.\n *\n * Shape:\n * {\n * \"name\": \"<bin>\",\n * \"version\": \"<bin-version>\",\n * \"description\": \"<from cliConfig or package.json>\",\n * \"mcpDefault\": { \"command\": \"<bin>\", \"args\": [\"serve\", \"--stdio\"] },\n * \"prompts\": [{ \"name\", \"description\", \"arguments\": [...] }],\n * \"skills\": [{ \"name\", \"description\", \"instructionFile\" (path under _skills/), \"resourceDirs\": { references?, examples?, scripts?, assets? } }]\n * }\n */\n\nimport * as path from 'path';\n\nimport { writeJSON } from '@frontmcp/utils';\n\nimport type { FrontmcpExecConfig } from './config';\nimport type { ExtractedSchema } from './cli-runtime/schema-extractor';\n\nexport interface BinMeta {\n name: string;\n version: string;\n description: string;\n mcpDefault: { command: string; args: string[] };\n prompts: Array<{\n name: string;\n description?: string;\n arguments?: Array<{ name: string; description?: string; required?: boolean }>;\n }>;\n skills: Array<{\n name: string;\n description?: string;\n tags?: string[];\n license?: string;\n instructionFile?: string;\n resourceDirs?: { references?: string; examples?: string; scripts?: string; assets?: string };\n }>;\n}\n\nexport async function writeBinMeta(\n outDir: string,\n config: FrontmcpExecConfig,\n schema: ExtractedSchema,\n): Promise<void> {\n const meta: BinMeta = {\n name: config.name,\n version: config.version ?? '0.0.0',\n description: config.cli?.description ?? `${config.name} (FrontMCP server)`,\n mcpDefault: { command: config.name, args: ['serve', '--stdio'] },\n prompts: schema.prompts.map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments,\n })),\n skills: schema.skillAssets.map((asset) => {\n // The bin runtime expects RELATIVE paths under `_skills/` so the bundle\n // is portable across install destinations.\n const instructionFile = asset.instructionFile\n ? path.join('_skills', `${asset.skillName}--${path.basename(asset.instructionFile)}`)\n : undefined;\n const resourceDirs: BinMeta['skills'][number]['resourceDirs'] = {};\n for (const kind of ['references', 'examples', 'scripts', 'assets'] as const) {\n if (asset.resourceDirs?.[kind]) {\n resourceDirs[kind] = path.join('_skills', `${asset.skillName}--${kind}`);\n }\n }\n return {\n name: asset.skillName,\n description: asset.description,\n tags: asset.tags && asset.tags.length > 0 ? asset.tags : undefined,\n license: asset.license,\n instructionFile,\n resourceDirs: Object.keys(resourceDirs).length > 0 ? resourceDirs : undefined,\n };\n }),\n };\n\n // Drop undefined fields so the JSON payload matches the TS interface (no\n // explicit \"description\": undefined keys land in stringified output).\n const cleanSkills = meta.skills.map((s) => omitUndefined(s));\n await writeJSON(path.join(outDir, 'bin-meta.json'), { ...meta, skills: cleanSkills });\n}\n\nfunction omitUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Partial<T> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k as keyof T] = v as T[keyof T];\n }\n return out;\n}\n"]}
|
|
@@ -1362,16 +1362,158 @@ function generateInstallCommand(appName, nativeDeps, selfContained) {
|
|
|
1362
1362
|
depEntries.push(` { name: ${JSON.stringify(pkg)}, type: 'npm', install: 'npm install ${pkg}', check: 'npm ls ${pkg}' }`);
|
|
1363
1363
|
}
|
|
1364
1364
|
}
|
|
1365
|
-
return `
|
|
1365
|
+
return `function _frontmcpCollectArg(value, acc) { return Array.isArray(acc) ? acc.concat(value) : [value]; }
|
|
1366
|
+
|
|
1367
|
+
program
|
|
1366
1368
|
.command('install')
|
|
1367
|
-
.description('Install to ~/.frontmcp/ and set up dependencies')
|
|
1369
|
+
.description('Install to ~/.frontmcp/ and set up dependencies, OR emit an IDE plugin (use -p)')
|
|
1368
1370
|
.option('--prefix <path>', 'Installation prefix directory')
|
|
1369
1371
|
.option('--bin-dir <path>', 'Directory for symlink (default: ~/.local/bin or /usr/local/bin)')
|
|
1372
|
+
.option('-p, --provider <provider>', 'Emit plugin for provider: claude | codex (repeatable)', _frontmcpCollectArg, [])
|
|
1373
|
+
.option('--scope <scope>', 'Plugin scope when -p is set: project | user', 'project')
|
|
1374
|
+
.option('--no-skills', 'Skip the skills/ subtree (when -p claude)')
|
|
1375
|
+
.option('--no-commands', 'Skip the commands/ subtree (when -p claude)')
|
|
1376
|
+
.option('--only-mcp', 'Skip plugin folder; just register the MCP server')
|
|
1377
|
+
.option('--command <cmd>', 'Override MCP server invocation in the plugin manifest')
|
|
1378
|
+
.option('--env <name>', 'Add env-var placeholder to plugin manifest (repeatable)', _frontmcpCollectArg, [])
|
|
1379
|
+
.option('--dir <dir>', 'Override plugin destination root')
|
|
1380
|
+
.option('--dry-run', 'Print plan; do not write')
|
|
1381
|
+
.option('--status', 'Print install status per provider; exit 0')
|
|
1370
1382
|
.action(async function(opts) {
|
|
1371
1383
|
var fs = require('fs');
|
|
1372
1384
|
var pathMod = require('path');
|
|
1373
1385
|
var os = require('os');
|
|
1374
1386
|
var exec = require('child_process').execSync;
|
|
1387
|
+
|
|
1388
|
+
// Issue #411 — when -p is set OR --status is set, run the plugin-install
|
|
1389
|
+
// path instead of the legacy bundle-copy + symlink behavior.
|
|
1390
|
+
var providers = Array.isArray(opts.provider) ? opts.provider : [];
|
|
1391
|
+
if (opts.status || providers.length > 0) {
|
|
1392
|
+
var emitter = require('./plugin-emitter');
|
|
1393
|
+
var binMetaPath = pathMod.join(SCRIPT_DIR, 'bin-meta.json');
|
|
1394
|
+
var meta;
|
|
1395
|
+
try { meta = JSON.parse(fs.readFileSync(binMetaPath, 'utf8')); }
|
|
1396
|
+
catch (e) {
|
|
1397
|
+
console.error('Could not read bin-meta.json at ' + binMetaPath + '. Was the bin built with a recent frontmcp?');
|
|
1398
|
+
process.exit(1);
|
|
1399
|
+
}
|
|
1400
|
+
var pkgJsonPath = pathMod.join(__dirname, '..', '..', 'package.json');
|
|
1401
|
+
var cliVersion = '0.0.0';
|
|
1402
|
+
try { cliVersion = (require(pkgJsonPath) || {}).version || '0.0.0'; } catch (e) { /* ok */ }
|
|
1403
|
+
|
|
1404
|
+
function resolveDestRoot() {
|
|
1405
|
+
if (opts.dir) return pathMod.resolve(opts.dir);
|
|
1406
|
+
if (opts.scope === 'user') return pathMod.join(os.homedir(), '.claude', 'plugins');
|
|
1407
|
+
return pathMod.join(process.cwd(), '.claude', 'plugins');
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
if (opts.status) {
|
|
1411
|
+
console.log(meta.name + ' install --status');
|
|
1412
|
+
var destRootSt = resolveDestRoot();
|
|
1413
|
+
var pluginDirSt = pathMod.join(destRootSt, meta.name);
|
|
1414
|
+
var installed = await emitter.readInstalledPluginVersion(pluginDirSt);
|
|
1415
|
+
if (installed) {
|
|
1416
|
+
var tag = installed === meta.version ? 'installed' : 'outdated';
|
|
1417
|
+
console.log(' claude: ' + tag + ' v' + installed + (tag === 'outdated' ? ' (bin at v' + meta.version + ')' : '') + ' at ' + pluginDirSt);
|
|
1418
|
+
} else {
|
|
1419
|
+
console.log(' claude: not installed at ' + pluginDirSt);
|
|
1420
|
+
}
|
|
1421
|
+
var codexConfigSt = pathMod.join(os.homedir(), '.codex', 'config.toml');
|
|
1422
|
+
if (fs.existsSync(codexConfigSt) && fs.readFileSync(codexConfigSt, 'utf8').indexOf('# frontmcp:codex-start:' + meta.name) !== -1) {
|
|
1423
|
+
console.log(' codex: installed entry for ' + meta.name + ' in ' + codexConfigSt);
|
|
1424
|
+
} else {
|
|
1425
|
+
console.log(' codex: not installed in ' + codexConfigSt);
|
|
1426
|
+
}
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function buildSkills() {
|
|
1431
|
+
if (opts.skills === false || opts.onlyMcp) return [];
|
|
1432
|
+
var out = [];
|
|
1433
|
+
for (var i = 0; i < (meta.skills || []).length; i++) {
|
|
1434
|
+
var s = meta.skills[i];
|
|
1435
|
+
var resourceDirs = {};
|
|
1436
|
+
if (s.resourceDirs) {
|
|
1437
|
+
for (var k in s.resourceDirs) {
|
|
1438
|
+
if (Object.prototype.hasOwnProperty.call(s.resourceDirs, k)) {
|
|
1439
|
+
resourceDirs[k] = pathMod.join(SCRIPT_DIR, s.resourceDirs[k]);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
out.push({
|
|
1444
|
+
name: s.name,
|
|
1445
|
+
description: s.description || (s.name + ' skill from ' + meta.name),
|
|
1446
|
+
tags: Array.isArray(s.tags) && s.tags.length > 0 ? s.tags : undefined,
|
|
1447
|
+
license: s.license || undefined,
|
|
1448
|
+
instructionFile: s.instructionFile ? pathMod.join(SCRIPT_DIR, s.instructionFile) : undefined,
|
|
1449
|
+
resourceDirs: Object.keys(resourceDirs).length > 0 ? resourceDirs : undefined,
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
return out;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
function buildCommands() {
|
|
1456
|
+
if (opts.commands === false || opts.onlyMcp) return [];
|
|
1457
|
+
return (meta.prompts || []).map(function(p) {
|
|
1458
|
+
return { name: p.name, description: p.description, arguments: p.arguments };
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
for (var pi = 0; pi < providers.length; pi++) {
|
|
1463
|
+
var provider = providers[pi];
|
|
1464
|
+
if (provider === 'claude') {
|
|
1465
|
+
var destRoot = resolveDestRoot();
|
|
1466
|
+
var result = await emitter.emitClaudePlugin({
|
|
1467
|
+
destRoot: destRoot,
|
|
1468
|
+
name: meta.name,
|
|
1469
|
+
version: meta.version,
|
|
1470
|
+
description: meta.description,
|
|
1471
|
+
mcpCommand: opts.command || meta.mcpDefault.command,
|
|
1472
|
+
mcpArgs: meta.mcpDefault.args,
|
|
1473
|
+
envHints: Array.isArray(opts.env) ? opts.env : [],
|
|
1474
|
+
skills: buildSkills(),
|
|
1475
|
+
commands: buildCommands(),
|
|
1476
|
+
cliVersion: cliVersion,
|
|
1477
|
+
dryRun: opts.dryRun,
|
|
1478
|
+
});
|
|
1479
|
+
if (opts.dryRun) {
|
|
1480
|
+
console.log('[install:claude] dry-run plan');
|
|
1481
|
+
console.log(' pluginDir: ' + result.pluginDir);
|
|
1482
|
+
console.log(' filesWritten (planned):');
|
|
1483
|
+
for (var fwi = 0; fwi < result.filesWritten.length; fwi++) console.log(' + ' + result.filesWritten[fwi]);
|
|
1484
|
+
} else {
|
|
1485
|
+
console.log('✓ Wrote ' + result.pluginDir + '/ (' + (result.manifest.skills || []).length + ' skills, ' + ((result.manifest.commands || []).length) + ' commands, 1 MCP server)');
|
|
1486
|
+
console.log(' Restart Claude Code (or run /plugins reload) to pick up the plugin.');
|
|
1487
|
+
}
|
|
1488
|
+
} else if (provider === 'codex') {
|
|
1489
|
+
var codexConfig = pathMod.join(os.homedir(), '.codex', 'config.toml');
|
|
1490
|
+
var env = {};
|
|
1491
|
+
var envList = Array.isArray(opts.env) ? opts.env : [];
|
|
1492
|
+
for (var ei = 0; ei < envList.length; ei++) env[envList[ei]] = '${'$'}{' + envList[ei] + '}';
|
|
1493
|
+
var codexResult = await emitter.emitCodexEntry({
|
|
1494
|
+
configPath: codexConfig,
|
|
1495
|
+
name: meta.name,
|
|
1496
|
+
command: opts.command || meta.mcpDefault.command,
|
|
1497
|
+
args: meta.mcpDefault.args,
|
|
1498
|
+
env: env,
|
|
1499
|
+
dryRun: opts.dryRun,
|
|
1500
|
+
});
|
|
1501
|
+
if (opts.dryRun) {
|
|
1502
|
+
console.log('[install:codex] dry-run plan');
|
|
1503
|
+
console.log(' configPath: ' + codexConfig);
|
|
1504
|
+
console.log(codexResult.configContent);
|
|
1505
|
+
} else {
|
|
1506
|
+
console.log('✓ Updated ' + codexConfig + ' with [[mcp_servers]] entry for ' + meta.name);
|
|
1507
|
+
}
|
|
1508
|
+
} else {
|
|
1509
|
+
console.error('Unknown provider: ' + provider);
|
|
1510
|
+
process.exitCode = 1;
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1375
1517
|
var installBase = opts.prefix || FRONTMCP_HOME;
|
|
1376
1518
|
var appDir = pathMod.join(installBase, 'apps', ${JSON.stringify(appName)});
|
|
1377
1519
|
var dirs = ['', '/data', '/sessions', '/credentials'].map(function(s) { return appDir + s; });
|
|
@@ -1432,13 +1574,63 @@ ${depEntries.join(',\n')}
|
|
|
1432
1574
|
|
|
1433
1575
|
program
|
|
1434
1576
|
.command('uninstall')
|
|
1435
|
-
.description('Remove from ~/.frontmcp
|
|
1577
|
+
.description('Remove from ~/.frontmcp/, OR remove an IDE plugin (use -p)')
|
|
1436
1578
|
.option('--prefix <path>', 'Installation prefix directory')
|
|
1437
1579
|
.option('--bin-dir <path>', 'Directory where symlink was created')
|
|
1580
|
+
.option('-p, --provider <provider>', 'Remove plugin for provider: claude | codex (repeatable)', _frontmcpCollectArg, [])
|
|
1581
|
+
.option('--scope <scope>', 'Plugin scope when -p is set: project | user', 'project')
|
|
1582
|
+
.option('--dir <dir>', 'Override plugin destination root')
|
|
1438
1583
|
.action(async function(opts) {
|
|
1439
1584
|
var fs = require('fs');
|
|
1440
1585
|
var pathMod = require('path');
|
|
1441
1586
|
var os = require('os');
|
|
1587
|
+
|
|
1588
|
+
// Issue #411 — when -p is set, route through the shared plugin-emitter
|
|
1589
|
+
// to remove the IDE plugin instead of the legacy ~/.frontmcp uninstall.
|
|
1590
|
+
var providers = Array.isArray(opts.provider) ? opts.provider : [];
|
|
1591
|
+
if (providers.length > 0) {
|
|
1592
|
+
var emitter = require('./plugin-emitter');
|
|
1593
|
+
var binMetaPath = pathMod.join(SCRIPT_DIR, 'bin-meta.json');
|
|
1594
|
+
var meta;
|
|
1595
|
+
try { meta = JSON.parse(fs.readFileSync(binMetaPath, 'utf8')); }
|
|
1596
|
+
catch (e) {
|
|
1597
|
+
console.error('Could not read bin-meta.json at ' + binMetaPath + '. Was the bin built with a recent frontmcp?');
|
|
1598
|
+
process.exit(1);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
function resolveDestRoot() {
|
|
1602
|
+
if (opts.dir) return pathMod.resolve(opts.dir);
|
|
1603
|
+
if (opts.scope === 'user') return pathMod.join(os.homedir(), '.claude', 'plugins');
|
|
1604
|
+
return pathMod.join(process.cwd(), '.claude', 'plugins');
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
for (var pi = 0; pi < providers.length; pi++) {
|
|
1608
|
+
var provider = providers[pi];
|
|
1609
|
+
if (provider === 'claude') {
|
|
1610
|
+
var destRoot = resolveDestRoot();
|
|
1611
|
+
var result = await emitter.removeClaudePlugin({ destRoot: destRoot, name: meta.name });
|
|
1612
|
+
if (result.removed.length === 0) {
|
|
1613
|
+
console.log(' claude: nothing to remove at ' + result.pluginDir);
|
|
1614
|
+
} else {
|
|
1615
|
+
console.log('✓ Removed ' + result.removed.length + ' file(s) from ' + result.pluginDir);
|
|
1616
|
+
}
|
|
1617
|
+
} else if (provider === 'codex') {
|
|
1618
|
+
var codexConfig = pathMod.join(os.homedir(), '.codex', 'config.toml');
|
|
1619
|
+
var codexResult = await emitter.removeCodexEntry({ configPath: codexConfig, name: meta.name });
|
|
1620
|
+
if (codexResult.removed) {
|
|
1621
|
+
console.log('✓ Removed [[mcp_servers]] entry for ' + meta.name + ' from ' + codexConfig);
|
|
1622
|
+
} else {
|
|
1623
|
+
console.log(' codex: no entry for ' + meta.name + ' in ' + codexConfig);
|
|
1624
|
+
}
|
|
1625
|
+
} else {
|
|
1626
|
+
console.error('Unknown provider: ' + provider);
|
|
1627
|
+
process.exitCode = 1;
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1442
1634
|
var uninstallBase = opts.prefix || FRONTMCP_HOME;
|
|
1443
1635
|
var appDir = pathMod.join(uninstallBase, 'apps', ${JSON.stringify(appName)});
|
|
1444
1636
|
|