frontmcp 1.2.1 → 1.4.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.
Files changed (114) hide show
  1. package/README.md +38 -29
  2. package/package.json +4 -4
  3. package/src/commands/build/exec/bin-meta.d.ts +49 -0
  4. package/src/commands/build/exec/bin-meta.js +68 -0
  5. package/src/commands/build/exec/bin-meta.js.map +1 -0
  6. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +195 -3
  7. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  8. package/src/commands/build/exec/cli-runtime/plugin-emitter.d.ts +160 -0
  9. package/src/commands/build/exec/cli-runtime/plugin-emitter.js +512 -0
  10. package/src/commands/build/exec/cli-runtime/plugin-emitter.js.map +1 -0
  11. package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +13 -1
  12. package/src/commands/build/exec/cli-runtime/schema-extractor.js +29 -3
  13. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  14. package/src/commands/build/exec/cli-runtime/skill-md-compose.d.ts +25 -0
  15. package/src/commands/build/exec/cli-runtime/skill-md-compose.js +63 -0
  16. package/src/commands/build/exec/cli-runtime/skill-md-compose.js.map +1 -0
  17. package/src/commands/build/exec/index.js +26 -0
  18. package/src/commands/build/exec/index.js.map +1 -1
  19. package/src/commands/build/exec/runner-script.js +16 -4
  20. package/src/commands/build/exec/runner-script.js.map +1 -1
  21. package/src/commands/dev/bridge/child-supervisor.d.ts +48 -0
  22. package/src/commands/dev/bridge/child-supervisor.js +228 -0
  23. package/src/commands/dev/bridge/child-supervisor.js.map +1 -0
  24. package/src/commands/dev/bridge/errors.d.ts +23 -0
  25. package/src/commands/dev/bridge/errors.js +34 -0
  26. package/src/commands/dev/bridge/errors.js.map +1 -0
  27. package/src/commands/dev/bridge/index.d.ts +30 -0
  28. package/src/commands/dev/bridge/index.js +220 -0
  29. package/src/commands/dev/bridge/index.js.map +1 -0
  30. package/src/commands/dev/bridge/log.d.ts +29 -0
  31. package/src/commands/dev/bridge/log.js +82 -0
  32. package/src/commands/dev/bridge/log.js.map +1 -0
  33. package/src/commands/dev/bridge/state-machine.d.ts +56 -0
  34. package/src/commands/dev/bridge/state-machine.js +245 -0
  35. package/src/commands/dev/bridge/state-machine.js.map +1 -0
  36. package/src/commands/dev/bridge/stdio-framer.d.ts +47 -0
  37. package/src/commands/dev/bridge/stdio-framer.js +128 -0
  38. package/src/commands/dev/bridge/stdio-framer.js.map +1 -0
  39. package/src/commands/dev/bridge/upstream-client.d.ts +49 -0
  40. package/src/commands/dev/bridge/upstream-client.js +159 -0
  41. package/src/commands/dev/bridge/upstream-client.js.map +1 -0
  42. package/src/commands/dev/bridge/watcher.d.ts +30 -0
  43. package/src/commands/dev/bridge/watcher.js +87 -0
  44. package/src/commands/dev/bridge/watcher.js.map +1 -0
  45. package/src/commands/dev/dev.d.ts +34 -1
  46. package/src/commands/dev/dev.js +168 -14
  47. package/src/commands/dev/dev.js.map +1 -1
  48. package/src/commands/dev/inspector.d.ts +13 -1
  49. package/src/commands/dev/inspector.js +77 -3
  50. package/src/commands/dev/inspector.js.map +1 -1
  51. package/src/commands/dev/port.d.ts +23 -0
  52. package/src/commands/dev/port.js +87 -0
  53. package/src/commands/dev/port.js.map +1 -0
  54. package/src/commands/dev/register.d.ts +1 -1
  55. package/src/commands/dev/register.js +28 -4
  56. package/src/commands/dev/register.js.map +1 -1
  57. package/src/commands/dev/test.d.ts +26 -1
  58. package/src/commands/dev/test.js +181 -64
  59. package/src/commands/dev/test.js.map +1 -1
  60. package/src/commands/eject/mcp-client.d.ts +25 -0
  61. package/src/commands/eject/mcp-client.js +74 -0
  62. package/src/commands/eject/mcp-client.js.map +1 -0
  63. package/src/commands/eject/register.d.ts +9 -0
  64. package/src/commands/eject/register.js +56 -0
  65. package/src/commands/eject/register.js.map +1 -0
  66. package/src/commands/install/install-claude-plugin.d.ts +13 -0
  67. package/src/commands/install/install-claude-plugin.js +327 -0
  68. package/src/commands/install/install-claude-plugin.js.map +1 -0
  69. package/src/commands/install/register.d.ts +16 -0
  70. package/src/commands/install/register.js +70 -0
  71. package/src/commands/install/register.js.map +1 -0
  72. package/src/commands/scaffold/create.js +52 -8
  73. package/src/commands/scaffold/create.js.map +1 -1
  74. package/src/commands/skills/from-entry.d.ts +31 -0
  75. package/src/commands/skills/from-entry.js +68 -0
  76. package/src/commands/skills/from-entry.js.map +1 -0
  77. package/src/commands/skills/install.d.ts +12 -0
  78. package/src/commands/skills/install.js +173 -8
  79. package/src/commands/skills/install.js.map +1 -1
  80. package/src/commands/skills/register.js +7 -3
  81. package/src/commands/skills/register.js.map +1 -1
  82. package/src/config/frontmcp-config.loader.d.ts +28 -0
  83. package/src/config/frontmcp-config.loader.js +146 -67
  84. package/src/config/frontmcp-config.loader.js.map +1 -1
  85. package/src/config/frontmcp-config.resolve.d.ts +67 -0
  86. package/src/config/frontmcp-config.resolve.js +118 -0
  87. package/src/config/frontmcp-config.resolve.js.map +1 -0
  88. package/src/config/frontmcp-config.schema.d.ts +207 -0
  89. package/src/config/frontmcp-config.schema.js +217 -1
  90. package/src/config/frontmcp-config.schema.js.map +1 -1
  91. package/src/config/frontmcp-config.types.d.ts +133 -0
  92. package/src/config/frontmcp-config.types.js.map +1 -1
  93. package/src/config/index.d.ts +2 -1
  94. package/src/config/index.js +3 -1
  95. package/src/config/index.js.map +1 -1
  96. package/src/core/args.d.ts +13 -0
  97. package/src/core/args.js.map +1 -1
  98. package/src/core/bridge.js +39 -0
  99. package/src/core/bridge.js.map +1 -1
  100. package/src/core/cli.d.ts +0 -6
  101. package/src/core/cli.js +23 -3
  102. package/src/core/cli.js.map +1 -1
  103. package/src/core/help.d.ts +1 -1
  104. package/src/core/help.js +27 -6
  105. package/src/core/help.js.map +1 -1
  106. package/src/core/program.d.ts +1 -1
  107. package/src/core/program.js +56 -12
  108. package/src/core/program.js.map +1 -1
  109. package/src/core/project-commands.d.ts +44 -0
  110. package/src/core/project-commands.js +216 -0
  111. package/src/core/project-commands.js.map +1 -0
  112. package/src/core/tsconfig.d.ts +20 -0
  113. package/src/core/tsconfig.js +41 -2
  114. package/src/core/tsconfig.js.map +1 -1
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ /**
3
+ * Implementation of `frontmcp install --claude-plugin [--codex]` (issue #411).
4
+ *
5
+ * Flow:
6
+ * 1. Load frontmcp.config + package.json to determine name/version/description.
7
+ * 2. Spin up a transient direct-mode SDK instance to enumerate skills and
8
+ * prompts (the same surfaces a built bin would expose).
9
+ * 3. Delegate to the shared `plugin-emitter.ts` helper.
10
+ *
11
+ * Status / dry-run paths are short-circuited before the SDK is loaded.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.runInstallCurrentProject = runInstallCurrentProject;
15
+ exports.runUninstallCurrentProject = runUninstallCurrentProject;
16
+ const tslib_1 = require("tslib");
17
+ const os = tslib_1.__importStar(require("os"));
18
+ const path = tslib_1.__importStar(require("path"));
19
+ const frontmcp_config_loader_1 = require("../../config/frontmcp-config.loader");
20
+ const colors_1 = require("../../core/colors");
21
+ const plugin_emitter_1 = require("../build/exec/cli-runtime/plugin-emitter");
22
+ async function runInstallCurrentProject(rawOpts) {
23
+ const opts = normalizeOptions(rawOpts);
24
+ if (!opts.claudePlugin && !opts.codex) {
25
+ process.stderr.write((0, colors_1.c)('red', 'Specify --claude and/or --codex.\n'));
26
+ process.exit(1);
27
+ }
28
+ const projectMeta = await loadProjectMeta();
29
+ if (!projectMeta) {
30
+ process.stderr.write((0, colors_1.c)('red', 'No frontmcp.config found in this directory. Run `frontmcp install` from a FrontMCP project root.\n'));
31
+ process.exit(1);
32
+ }
33
+ if (opts.status) {
34
+ await printStatus(projectMeta, opts);
35
+ return;
36
+ }
37
+ const cliVersion = await getCliVersion();
38
+ const skills = opts.skills === false || opts.onlyMcp ? [] : await collectSkillsFromProject();
39
+ const commands = opts.commands === false || opts.onlyMcp ? [] : await collectPromptsFromProject();
40
+ if (opts.claudePlugin) {
41
+ await runClaudeInstall({ projectMeta, opts, cliVersion, skills, commands });
42
+ }
43
+ if (opts.codex) {
44
+ await runCodexInstall({ projectMeta, opts, cliVersion });
45
+ }
46
+ }
47
+ async function loadProjectMeta() {
48
+ const cwd = process.cwd();
49
+ const cfg = await (0, frontmcp_config_loader_1.tryLoadFrontMcpConfig)(cwd).catch(() => undefined);
50
+ if (!cfg)
51
+ return undefined;
52
+ const { readJSON, fileExists } = await import('@frontmcp/utils');
53
+ const pkgPath = path.join(cwd, 'package.json');
54
+ const pkg = (await fileExists(pkgPath))
55
+ ? (await readJSON(pkgPath))
56
+ : {};
57
+ return {
58
+ name: cfg.name ?? pkg.name ?? path.basename(cwd),
59
+ version: cfg.version ?? pkg.version ?? '0.0.0',
60
+ description: pkg.description ?? `${cfg.name ?? pkg.name ?? 'FrontMCP server'} (FrontMCP plugin)`,
61
+ };
62
+ }
63
+ /**
64
+ * Best-effort: bundle the project's entry to a temp file, hand the bundle
65
+ * to the schema-extractor (the same routine the per-bin path uses at build
66
+ * time), and return the enumerated skills / prompts. Failures are logged
67
+ * and the empty-array fallback is returned so a partial setup never blocks
68
+ * an install — the per-bin path remains the authoritative source.
69
+ *
70
+ * Wrapped in a single bundle+extract pass so both collectors share work:
71
+ * spawning two extractor passes would double the SDK boot cost on every
72
+ * dev-tool install.
73
+ */
74
+ // Keyed by absolute `cwd` so two installs invoked back-to-back from
75
+ // different project roots (e.g. an integration test, or a script that
76
+ // `cd`s between projects) don't reuse the first project's skill/prompt
77
+ // extraction. Each project gets its own cached promise — and within a
78
+ // single project both `collectSkillsFromProject` and
79
+ // `collectPromptsFromProject` share the one bundle+extract pass.
80
+ const cachedExtractionByCwd = new Map();
81
+ async function getProjectExtraction(cwd) {
82
+ const cached = cachedExtractionByCwd.get(cwd);
83
+ if (cached)
84
+ return cached;
85
+ const extractionPromise = (async () => {
86
+ try {
87
+ const { resolveEntry } = await import('../../shared/fs.js');
88
+ const { mkdtemp, rm } = await import('@frontmcp/utils');
89
+ const entry = await resolveEntry(cwd);
90
+ const tempDir = await mkdtemp(path.join(os.tmpdir(), 'frontmcp-install-'));
91
+ const bundlePath = path.join(tempDir, 'entry.cjs');
92
+ try {
93
+ const esbuild = require('esbuild');
94
+ await esbuild.build({
95
+ entryPoints: [entry],
96
+ bundle: true,
97
+ write: true,
98
+ outfile: bundlePath,
99
+ platform: 'node',
100
+ format: 'cjs',
101
+ target: 'es2022',
102
+ packages: 'external',
103
+ sourcemap: false,
104
+ logLevel: 'silent',
105
+ absWorkingDir: cwd,
106
+ });
107
+ const { extractSchemas } = await import('../build/exec/cli-runtime/schema-extractor.js');
108
+ const schema = await extractSchemas(bundlePath);
109
+ const skills = (schema.skillAssets ?? []).map((entry) => ({
110
+ name: entry.skillName,
111
+ description: entry.description ?? `${entry.skillName} skill`,
112
+ tags: entry.tags,
113
+ license: entry.license,
114
+ instructionFile: entry.instructionFile,
115
+ resourceDirs: entry.resourceDirs,
116
+ }));
117
+ const commands = (schema.prompts ?? []).map((p) => ({
118
+ name: p.name,
119
+ description: p.description,
120
+ arguments: p.arguments,
121
+ }));
122
+ return { skills, commands };
123
+ }
124
+ finally {
125
+ await rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
126
+ }
127
+ }
128
+ catch (err) {
129
+ // Drop the cache entry so the NEXT install from this cwd retries
130
+ // extraction — without this, a transient esbuild/SDK-boot failure
131
+ // would freeze the project at `{ skills: [], commands: [] }` for
132
+ // the rest of the process even after the user fixes the entry.
133
+ // The current caller still gets the empty fallback so the install
134
+ // itself doesn't crash.
135
+ cachedExtractionByCwd.delete(cwd);
136
+ process.stderr.write((0, colors_1.c)('yellow', `[install] could not enumerate skills/prompts from project (${err.message}); ` +
137
+ `emitting plugin without them. Use the per-bin install (\`<bin> install -p claude\`) ` +
138
+ `after \`frontmcp build:exec\` if you need the full set.\n`));
139
+ return { skills: [], commands: [] };
140
+ }
141
+ })();
142
+ // Set BEFORE awaiting so concurrent callers within the same install
143
+ // (collectSkillsFromProject + collectPromptsFromProject) share the
144
+ // single bundle+extract pass. The catch above un-sets on failure.
145
+ cachedExtractionByCwd.set(cwd, extractionPromise);
146
+ return extractionPromise;
147
+ }
148
+ async function collectSkillsFromProject() {
149
+ return (await getProjectExtraction(process.cwd())).skills;
150
+ }
151
+ async function collectPromptsFromProject() {
152
+ return (await getProjectExtraction(process.cwd())).commands;
153
+ }
154
+ async function getCliVersion() {
155
+ try {
156
+ const { readJSON } = await import('@frontmcp/utils');
157
+ const pkg = (await readJSON(path.join(__dirname, '..', '..', '..', 'package.json')));
158
+ return pkg.version ?? '0.0.0';
159
+ }
160
+ catch {
161
+ return '0.0.0';
162
+ }
163
+ }
164
+ async function runClaudeInstall(args) {
165
+ const destRoot = resolveDestRoot(args.opts);
166
+ const emitOpts = {
167
+ destRoot,
168
+ name: args.projectMeta.name,
169
+ version: args.projectMeta.version,
170
+ description: args.projectMeta.description,
171
+ mcpCommand: args.opts.command ?? args.projectMeta.name,
172
+ mcpArgs: ['serve', '--stdio'],
173
+ envHints: args.opts.env ?? [],
174
+ skills: args.skills,
175
+ commands: args.commands,
176
+ cliVersion: args.cliVersion,
177
+ dryRun: args.opts.dryRun,
178
+ };
179
+ const result = await (0, plugin_emitter_1.emitClaudePlugin)(emitOpts);
180
+ if (args.opts.dryRun) {
181
+ process.stdout.write(`${(0, colors_1.c)('cyan', '[install:claude] dry-run plan')}\n`);
182
+ process.stdout.write(` pluginDir: ${result.pluginDir}\n`);
183
+ process.stdout.write(` manifest: ${JSON.stringify(result.manifest, null, 2)}\n`);
184
+ process.stdout.write(` filesWritten (planned):\n`);
185
+ for (const f of result.filesWritten)
186
+ process.stdout.write(` + ${f}\n`);
187
+ return;
188
+ }
189
+ process.stdout.write((0, colors_1.c)('green', `✓ Wrote ${result.pluginDir}/ (${args.skills.length} skills, ${args.commands.length} commands, 1 MCP server)\n`));
190
+ if (result.filesRemoved.length > 0) {
191
+ process.stdout.write(` Cleaned up ${result.filesRemoved.length} stale file(s) from previous install.\n`);
192
+ }
193
+ // Surface the dev-tool limitation explicitly: today this command only
194
+ // emits the MCP server entry; per-project @Skill / @Prompt enumeration
195
+ // is wired through the per-bin path (`<bin> install -p claude`). Don't
196
+ // let users assume zero counts mean "your project has no skills".
197
+ if (args.skills.length === 0 &&
198
+ args.commands.length === 0 &&
199
+ args.opts.skills !== false &&
200
+ args.opts.commands !== false &&
201
+ !args.opts.onlyMcp) {
202
+ process.stdout.write((0, colors_1.c)('yellow', ' Note: the dev-tool path does not yet enumerate @Skill or @Prompt from project source.\n' +
203
+ ' Build with `frontmcp build --target cli` and run `<bin> install -p claude` for full plugin coverage.\n'));
204
+ }
205
+ process.stdout.write((0, colors_1.c)('dim', ' Restart Claude Code (or run `/plugins reload`) to pick up the plugin.\n'));
206
+ }
207
+ async function runCodexInstall(args) {
208
+ void args.cliVersion;
209
+ const configPath = path.join(os.homedir(), '.codex', 'config.toml');
210
+ const env = {};
211
+ for (const name of args.opts.env ?? [])
212
+ env[name] = `\${${name}}`;
213
+ const result = await (0, plugin_emitter_1.emitCodexEntry)({
214
+ configPath,
215
+ name: args.projectMeta.name,
216
+ command: args.opts.command ?? args.projectMeta.name,
217
+ args: ['serve', '--stdio'],
218
+ env,
219
+ dryRun: args.opts.dryRun,
220
+ });
221
+ if (args.opts.dryRun) {
222
+ process.stdout.write(`${(0, colors_1.c)('cyan', '[install:codex] dry-run plan')}\n`);
223
+ process.stdout.write(` configPath: ${configPath}\n`);
224
+ process.stdout.write(` configContent:\n${indentBlock(result.configContent, 4)}\n`);
225
+ return;
226
+ }
227
+ process.stdout.write((0, colors_1.c)('green', `✓ Updated ${configPath} with [[mcp_servers]] entry for ${args.projectMeta.name}\n`));
228
+ }
229
+ async function printStatus(projectMeta, opts) {
230
+ process.stdout.write(`${(0, colors_1.c)('bold', `${projectMeta.name} install --status`)}\n`);
231
+ if (opts.claudePlugin || !opts.codex) {
232
+ const destRoot = resolveDestRoot(opts);
233
+ const pluginDir = path.join(destRoot, projectMeta.name);
234
+ const installed = await (0, plugin_emitter_1.readInstalledPluginVersion)(pluginDir);
235
+ if (installed) {
236
+ const tag = installed === projectMeta.version ? 'installed' : 'outdated';
237
+ process.stdout.write(` claude: ${tag} v${installed}${tag === 'outdated' ? ` (project at v${projectMeta.version})` : ''} at ${pluginDir}\n`);
238
+ }
239
+ else {
240
+ process.stdout.write(` claude: not installed at ${pluginDir}\n`);
241
+ }
242
+ }
243
+ if (opts.codex) {
244
+ const configPath = path.join(os.homedir(), '.codex', 'config.toml');
245
+ const { fileExists, readFile } = await import('@frontmcp/utils');
246
+ if (!(await fileExists(configPath))) {
247
+ process.stdout.write(` codex: not installed (${configPath} does not exist)\n`);
248
+ }
249
+ else {
250
+ const content = await readFile(configPath);
251
+ const present = content.includes(`# frontmcp:codex-start:${projectMeta.name}`);
252
+ process.stdout.write(present
253
+ ? ` codex: installed entry for ${projectMeta.name} in ${configPath}\n`
254
+ : ` codex: not installed in ${configPath}\n`);
255
+ }
256
+ }
257
+ }
258
+ async function runUninstallCurrentProject(rawOpts) {
259
+ const opts = normalizeOptions(rawOpts);
260
+ if (!opts.claudePlugin && !opts.codex) {
261
+ process.stderr.write((0, colors_1.c)('red', 'Specify --claude and/or --codex.\n'));
262
+ process.exit(1);
263
+ }
264
+ const projectMeta = await loadProjectMeta();
265
+ if (!projectMeta) {
266
+ process.stderr.write((0, colors_1.c)('red', 'No frontmcp.config found in this directory.\n'));
267
+ process.exit(1);
268
+ }
269
+ if (opts.claudePlugin) {
270
+ const destRoot = resolveDestRoot(opts);
271
+ const result = await (0, plugin_emitter_1.removeClaudePlugin)({ destRoot, name: projectMeta.name });
272
+ if (result.removed.length === 0) {
273
+ process.stdout.write((0, colors_1.c)('dim', ` claude: nothing to remove at ${result.pluginDir}\n`));
274
+ }
275
+ else {
276
+ process.stdout.write((0, colors_1.c)('green', `✓ Removed ${result.removed.length} file(s) from ${result.pluginDir}\n`));
277
+ }
278
+ }
279
+ if (opts.codex) {
280
+ const configPath = path.join(os.homedir(), '.codex', 'config.toml');
281
+ const result = await (0, plugin_emitter_1.removeCodexEntry)({ configPath, name: projectMeta.name });
282
+ if (result.removed) {
283
+ process.stdout.write((0, colors_1.c)('green', `✓ Removed [[mcp_servers]] entry for ${projectMeta.name} from ${configPath}\n`));
284
+ }
285
+ else {
286
+ process.stdout.write((0, colors_1.c)('dim', ` codex: no entry for ${projectMeta.name} in ${configPath}\n`));
287
+ }
288
+ }
289
+ }
290
+ function resolveDestRoot(opts) {
291
+ if (opts.dir)
292
+ return path.resolve(opts.dir);
293
+ if (opts.scope === 'user')
294
+ return path.join(os.homedir(), '.claude', 'plugins');
295
+ return path.join(process.cwd(), '.claude', 'plugins');
296
+ }
297
+ function normalizeOptions(raw) {
298
+ // Fail fast on bogus `--scope` values rather than silently coercing them to
299
+ // 'project' — `--scope usr` would otherwise write files to the wrong root
300
+ // (cwd's `.claude/plugins/` instead of `~/.claude/plugins/`) with no signal.
301
+ const rawScope = raw['scope'];
302
+ if (rawScope !== undefined && rawScope !== 'project' && rawScope !== 'user') {
303
+ throw new Error(`Invalid --scope value: ${String(rawScope)}. Expected "project" or "user".`);
304
+ }
305
+ const scope = rawScope === 'user' ? 'user' : 'project';
306
+ return {
307
+ claudePlugin: raw['claudePlugin'] === true,
308
+ codex: raw['codex'] === true,
309
+ scope,
310
+ skills: raw['skills'] !== false,
311
+ commands: raw['commands'] !== false,
312
+ onlyMcp: raw['onlyMcp'] === true,
313
+ command: typeof raw['command'] === 'string' ? raw['command'] : undefined,
314
+ env: Array.isArray(raw['env']) ? raw['env'] : [],
315
+ dir: typeof raw['dir'] === 'string' ? raw['dir'] : undefined,
316
+ dryRun: raw['dryRun'] === true,
317
+ status: raw['status'] === true,
318
+ };
319
+ }
320
+ function indentBlock(text, n) {
321
+ const pad = ' '.repeat(n);
322
+ return text
323
+ .split('\n')
324
+ .map((line) => pad + line)
325
+ .join('\n');
326
+ }
327
+ //# sourceMappingURL=install-claude-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-claude-plugin.js","sourceRoot":"","sources":["../../../../src/commands/install/install-claude-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Implementation of `frontmcp install --claude-plugin [--codex]` (issue #411).\n *\n * Flow:\n * 1. Load frontmcp.config + package.json to determine name/version/description.\n * 2. Spin up a transient direct-mode SDK instance to enumerate skills and\n * prompts (the same surfaces a built bin would expose).\n * 3. Delegate to the shared `plugin-emitter.ts` helper.\n *\n * Status / dry-run paths are short-circuited before the SDK is loaded.\n */\n\nimport * as os from 'os';\nimport * as path from 'path';\n\nimport { tryLoadFrontMcpConfig } from '../../config/frontmcp-config.loader';\nimport { c } from '../../core/colors';\nimport {\n emitClaudePlugin,\n emitCodexEntry,\n readInstalledPluginVersion,\n removeClaudePlugin,\n removeCodexEntry,\n type EmitClaudePluginOptions,\n type PluginEmitterCommandInput,\n type PluginEmitterSkillInput,\n} from '../build/exec/cli-runtime/plugin-emitter';\n\ninterface InstallOptions {\n claudePlugin?: boolean;\n codex?: boolean;\n scope?: 'project' | 'user';\n skills?: boolean;\n commands?: boolean;\n onlyMcp?: boolean;\n command?: string;\n env?: string[];\n dir?: string;\n dryRun?: boolean;\n status?: boolean;\n}\n\nexport async function runInstallCurrentProject(rawOpts: Record<string, unknown>): Promise<void> {\n const opts = normalizeOptions(rawOpts);\n\n if (!opts.claudePlugin && !opts.codex) {\n process.stderr.write(c('red', 'Specify --claude and/or --codex.\\n'));\n process.exit(1);\n }\n\n const projectMeta = await loadProjectMeta();\n if (!projectMeta) {\n process.stderr.write(\n c('red', 'No frontmcp.config found in this directory. Run `frontmcp install` from a FrontMCP project root.\\n'),\n );\n process.exit(1);\n }\n\n if (opts.status) {\n await printStatus(projectMeta, opts);\n return;\n }\n\n const cliVersion = await getCliVersion();\n const skills = opts.skills === false || opts.onlyMcp ? [] : await collectSkillsFromProject();\n const commands = opts.commands === false || opts.onlyMcp ? [] : await collectPromptsFromProject();\n\n if (opts.claudePlugin) {\n await runClaudeInstall({ projectMeta, opts, cliVersion, skills, commands });\n }\n if (opts.codex) {\n await runCodexInstall({ projectMeta, opts, cliVersion });\n }\n}\n\ninterface ProjectMeta {\n name: string;\n version: string;\n description: string;\n}\n\nasync function loadProjectMeta(): Promise<ProjectMeta | undefined> {\n const cwd = process.cwd();\n const cfg = await tryLoadFrontMcpConfig(cwd).catch(() => undefined);\n if (!cfg) return undefined;\n const { readJSON, fileExists } = await import('@frontmcp/utils');\n const pkgPath = path.join(cwd, 'package.json');\n const pkg: { name?: string; version?: string; description?: string } = (await fileExists(pkgPath))\n ? ((await readJSON(pkgPath)) as { name?: string; version?: string; description?: string })\n : {};\n return {\n name: cfg.name ?? pkg.name ?? path.basename(cwd),\n version: cfg.version ?? pkg.version ?? '0.0.0',\n description: pkg.description ?? `${cfg.name ?? pkg.name ?? 'FrontMCP server'} (FrontMCP plugin)`,\n };\n}\n\n/**\n * Best-effort: bundle the project's entry to a temp file, hand the bundle\n * to the schema-extractor (the same routine the per-bin path uses at build\n * time), and return the enumerated skills / prompts. Failures are logged\n * and the empty-array fallback is returned so a partial setup never blocks\n * an install — the per-bin path remains the authoritative source.\n *\n * Wrapped in a single bundle+extract pass so both collectors share work:\n * spawning two extractor passes would double the SDK boot cost on every\n * dev-tool install.\n */\n// Keyed by absolute `cwd` so two installs invoked back-to-back from\n// different project roots (e.g. an integration test, or a script that\n// `cd`s between projects) don't reuse the first project's skill/prompt\n// extraction. Each project gets its own cached promise — and within a\n// single project both `collectSkillsFromProject` and\n// `collectPromptsFromProject` share the one bundle+extract pass.\nconst cachedExtractionByCwd = new Map<\n string,\n Promise<{ skills: PluginEmitterSkillInput[]; commands: PluginEmitterCommandInput[] }>\n>();\n\nasync function getProjectExtraction(cwd: string): Promise<{\n skills: PluginEmitterSkillInput[];\n commands: PluginEmitterCommandInput[];\n}> {\n const cached = cachedExtractionByCwd.get(cwd);\n if (cached) return cached;\n const extractionPromise = (async () => {\n try {\n const { resolveEntry } = await import('../../shared/fs.js');\n const { mkdtemp, rm } = await import('@frontmcp/utils');\n const entry = await resolveEntry(cwd);\n const tempDir = await mkdtemp(path.join(os.tmpdir(), 'frontmcp-install-'));\n const bundlePath = path.join(tempDir, 'entry.cjs');\n try {\n const esbuild = require('esbuild') as typeof import('esbuild');\n await esbuild.build({\n entryPoints: [entry],\n bundle: true,\n write: true,\n outfile: bundlePath,\n platform: 'node',\n format: 'cjs',\n target: 'es2022',\n packages: 'external',\n sourcemap: false,\n logLevel: 'silent',\n absWorkingDir: cwd,\n });\n const { extractSchemas } = await import('../build/exec/cli-runtime/schema-extractor.js');\n const schema = await extractSchemas(bundlePath);\n const skills: PluginEmitterSkillInput[] = (schema.skillAssets ?? []).map((entry) => ({\n name: entry.skillName,\n description: entry.description ?? `${entry.skillName} skill`,\n tags: entry.tags,\n license: entry.license,\n instructionFile: entry.instructionFile,\n resourceDirs: entry.resourceDirs,\n }));\n const commands: PluginEmitterCommandInput[] = (schema.prompts ?? []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments,\n }));\n return { skills, commands };\n } finally {\n await rm(tempDir, { recursive: true, force: true }).catch(() => undefined);\n }\n } catch (err) {\n // Drop the cache entry so the NEXT install from this cwd retries\n // extraction — without this, a transient esbuild/SDK-boot failure\n // would freeze the project at `{ skills: [], commands: [] }` for\n // the rest of the process even after the user fixes the entry.\n // The current caller still gets the empty fallback so the install\n // itself doesn't crash.\n cachedExtractionByCwd.delete(cwd);\n process.stderr.write(\n c(\n 'yellow',\n `[install] could not enumerate skills/prompts from project (${(err as Error).message}); ` +\n `emitting plugin without them. Use the per-bin install (\\`<bin> install -p claude\\`) ` +\n `after \\`frontmcp build:exec\\` if you need the full set.\\n`,\n ),\n );\n return { skills: [], commands: [] };\n }\n })();\n // Set BEFORE awaiting so concurrent callers within the same install\n // (collectSkillsFromProject + collectPromptsFromProject) share the\n // single bundle+extract pass. The catch above un-sets on failure.\n cachedExtractionByCwd.set(cwd, extractionPromise);\n return extractionPromise;\n}\n\nasync function collectSkillsFromProject(): Promise<PluginEmitterSkillInput[]> {\n return (await getProjectExtraction(process.cwd())).skills;\n}\n\nasync function collectPromptsFromProject(): Promise<PluginEmitterCommandInput[]> {\n return (await getProjectExtraction(process.cwd())).commands;\n}\n\nasync function getCliVersion(): Promise<string> {\n try {\n const { readJSON } = await import('@frontmcp/utils');\n const pkg = (await readJSON(path.join(__dirname, '..', '..', '..', 'package.json'))) as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nasync function runClaudeInstall(args: {\n projectMeta: ProjectMeta;\n opts: InstallOptions;\n cliVersion: string;\n skills: PluginEmitterSkillInput[];\n commands: PluginEmitterCommandInput[];\n}): Promise<void> {\n const destRoot = resolveDestRoot(args.opts);\n const emitOpts: EmitClaudePluginOptions = {\n destRoot,\n name: args.projectMeta.name,\n version: args.projectMeta.version,\n description: args.projectMeta.description,\n mcpCommand: args.opts.command ?? args.projectMeta.name,\n mcpArgs: ['serve', '--stdio'],\n envHints: args.opts.env ?? [],\n skills: args.skills,\n commands: args.commands,\n cliVersion: args.cliVersion,\n dryRun: args.opts.dryRun,\n };\n\n const result = await emitClaudePlugin(emitOpts);\n\n if (args.opts.dryRun) {\n process.stdout.write(`${c('cyan', '[install:claude] dry-run plan')}\\n`);\n process.stdout.write(` pluginDir: ${result.pluginDir}\\n`);\n process.stdout.write(` manifest: ${JSON.stringify(result.manifest, null, 2)}\\n`);\n process.stdout.write(` filesWritten (planned):\\n`);\n for (const f of result.filesWritten) process.stdout.write(` + ${f}\\n`);\n return;\n }\n\n process.stdout.write(\n c(\n 'green',\n `✓ Wrote ${result.pluginDir}/ (${args.skills.length} skills, ${args.commands.length} commands, 1 MCP server)\\n`,\n ),\n );\n if (result.filesRemoved.length > 0) {\n process.stdout.write(` Cleaned up ${result.filesRemoved.length} stale file(s) from previous install.\\n`);\n }\n // Surface the dev-tool limitation explicitly: today this command only\n // emits the MCP server entry; per-project @Skill / @Prompt enumeration\n // is wired through the per-bin path (`<bin> install -p claude`). Don't\n // let users assume zero counts mean \"your project has no skills\".\n if (\n args.skills.length === 0 &&\n args.commands.length === 0 &&\n args.opts.skills !== false &&\n args.opts.commands !== false &&\n !args.opts.onlyMcp\n ) {\n process.stdout.write(\n c(\n 'yellow',\n ' Note: the dev-tool path does not yet enumerate @Skill or @Prompt from project source.\\n' +\n ' Build with `frontmcp build --target cli` and run `<bin> install -p claude` for full plugin coverage.\\n',\n ),\n );\n }\n process.stdout.write(c('dim', ' Restart Claude Code (or run `/plugins reload`) to pick up the plugin.\\n'));\n}\n\nasync function runCodexInstall(args: {\n projectMeta: ProjectMeta;\n opts: InstallOptions;\n cliVersion: string;\n}): Promise<void> {\n void args.cliVersion;\n const configPath = path.join(os.homedir(), '.codex', 'config.toml');\n const env: Record<string, string> = {};\n for (const name of args.opts.env ?? []) env[name] = `\\${${name}}`;\n const result = await emitCodexEntry({\n configPath,\n name: args.projectMeta.name,\n command: args.opts.command ?? args.projectMeta.name,\n args: ['serve', '--stdio'],\n env,\n dryRun: args.opts.dryRun,\n });\n\n if (args.opts.dryRun) {\n process.stdout.write(`${c('cyan', '[install:codex] dry-run plan')}\\n`);\n process.stdout.write(` configPath: ${configPath}\\n`);\n process.stdout.write(` configContent:\\n${indentBlock(result.configContent, 4)}\\n`);\n return;\n }\n\n process.stdout.write(c('green', `✓ Updated ${configPath} with [[mcp_servers]] entry for ${args.projectMeta.name}\\n`));\n}\n\nasync function printStatus(projectMeta: ProjectMeta, opts: InstallOptions): Promise<void> {\n process.stdout.write(`${c('bold', `${projectMeta.name} install --status`)}\\n`);\n if (opts.claudePlugin || !opts.codex) {\n const destRoot = resolveDestRoot(opts);\n const pluginDir = path.join(destRoot, projectMeta.name);\n const installed = await readInstalledPluginVersion(pluginDir);\n if (installed) {\n const tag = installed === projectMeta.version ? 'installed' : 'outdated';\n process.stdout.write(\n ` claude: ${tag} v${installed}${tag === 'outdated' ? ` (project at v${projectMeta.version})` : ''} at ${pluginDir}\\n`,\n );\n } else {\n process.stdout.write(` claude: not installed at ${pluginDir}\\n`);\n }\n }\n if (opts.codex) {\n const configPath = path.join(os.homedir(), '.codex', 'config.toml');\n const { fileExists, readFile } = await import('@frontmcp/utils');\n if (!(await fileExists(configPath))) {\n process.stdout.write(` codex: not installed (${configPath} does not exist)\\n`);\n } else {\n const content = await readFile(configPath);\n const present = content.includes(`# frontmcp:codex-start:${projectMeta.name}`);\n process.stdout.write(\n present\n ? ` codex: installed entry for ${projectMeta.name} in ${configPath}\\n`\n : ` codex: not installed in ${configPath}\\n`,\n );\n }\n }\n}\n\nexport async function runUninstallCurrentProject(rawOpts: Record<string, unknown>): Promise<void> {\n const opts = normalizeOptions(rawOpts);\n\n if (!opts.claudePlugin && !opts.codex) {\n process.stderr.write(c('red', 'Specify --claude and/or --codex.\\n'));\n process.exit(1);\n }\n\n const projectMeta = await loadProjectMeta();\n if (!projectMeta) {\n process.stderr.write(c('red', 'No frontmcp.config found in this directory.\\n'));\n process.exit(1);\n }\n\n if (opts.claudePlugin) {\n const destRoot = resolveDestRoot(opts);\n const result = await removeClaudePlugin({ destRoot, name: projectMeta.name });\n if (result.removed.length === 0) {\n process.stdout.write(c('dim', ` claude: nothing to remove at ${result.pluginDir}\\n`));\n } else {\n process.stdout.write(c('green', `✓ Removed ${result.removed.length} file(s) from ${result.pluginDir}\\n`));\n }\n }\n if (opts.codex) {\n const configPath = path.join(os.homedir(), '.codex', 'config.toml');\n const result = await removeCodexEntry({ configPath, name: projectMeta.name });\n if (result.removed) {\n process.stdout.write(c('green', `✓ Removed [[mcp_servers]] entry for ${projectMeta.name} from ${configPath}\\n`));\n } else {\n process.stdout.write(c('dim', ` codex: no entry for ${projectMeta.name} in ${configPath}\\n`));\n }\n }\n}\n\nfunction resolveDestRoot(opts: InstallOptions): string {\n if (opts.dir) return path.resolve(opts.dir);\n if (opts.scope === 'user') return path.join(os.homedir(), '.claude', 'plugins');\n return path.join(process.cwd(), '.claude', 'plugins');\n}\n\nfunction normalizeOptions(raw: Record<string, unknown>): InstallOptions {\n // Fail fast on bogus `--scope` values rather than silently coercing them to\n // 'project' — `--scope usr` would otherwise write files to the wrong root\n // (cwd's `.claude/plugins/` instead of `~/.claude/plugins/`) with no signal.\n const rawScope = raw['scope'];\n if (rawScope !== undefined && rawScope !== 'project' && rawScope !== 'user') {\n throw new Error(`Invalid --scope value: ${String(rawScope)}. Expected \"project\" or \"user\".`);\n }\n const scope: InstallOptions['scope'] = rawScope === 'user' ? 'user' : 'project';\n return {\n claudePlugin: raw['claudePlugin'] === true,\n codex: raw['codex'] === true,\n scope,\n skills: raw['skills'] !== false,\n commands: raw['commands'] !== false,\n onlyMcp: raw['onlyMcp'] === true,\n command: typeof raw['command'] === 'string' ? (raw['command'] as string) : undefined,\n env: Array.isArray(raw['env']) ? (raw['env'] as string[]) : [],\n dir: typeof raw['dir'] === 'string' ? (raw['dir'] as string) : undefined,\n dryRun: raw['dryRun'] === true,\n status: raw['status'] === true,\n };\n}\n\nfunction indentBlock(text: string, n: number): string {\n const pad = ' '.repeat(n);\n return text\n .split('\\n')\n .map((line) => pad + line)\n .join('\\n');\n}\n"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Issue #411 — registers the dev-tool side `frontmcp plugin` command tree
3
+ * (uses `plugin` as the noun because `install` is already taken by the
4
+ * npm-package install command at `package/register.ts`).
5
+ *
6
+ * Subcommands:
7
+ * frontmcp plugin install — emit a Claude Code plugin folder + Codex entry
8
+ * frontmcp plugin uninstall — remove what `install` wrote
9
+ * frontmcp plugin status — report install state per provider
10
+ *
11
+ * The per-bin equivalent (auto-included in every built CLI) extends the
12
+ * existing `<bin> install` with `-p claude|codex` and shares the same
13
+ * emitter under `cli-runtime/plugin-emitter.ts`.
14
+ */
15
+ import { type Command } from 'commander';
16
+ export declare function registerInstallCommands(program: Command): void;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * Issue #411 — registers the dev-tool side `frontmcp plugin` command tree
4
+ * (uses `plugin` as the noun because `install` is already taken by the
5
+ * npm-package install command at `package/register.ts`).
6
+ *
7
+ * Subcommands:
8
+ * frontmcp plugin install — emit a Claude Code plugin folder + Codex entry
9
+ * frontmcp plugin uninstall — remove what `install` wrote
10
+ * frontmcp plugin status — report install state per provider
11
+ *
12
+ * The per-bin equivalent (auto-included in every built CLI) extends the
13
+ * existing `<bin> install` with `-p claude|codex` and shares the same
14
+ * emitter under `cli-runtime/plugin-emitter.ts`.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.registerInstallCommands = registerInstallCommands;
18
+ /**
19
+ * Attach the shared plugin-flag surface to a subcommand. `install`,
20
+ * `uninstall`, and `status` all accept the same flag set so docs +
21
+ * tests can rely on a uniform invocation contract (issue #411). The
22
+ * dry-run / skip-tree / mcp-override flags are no-ops on `status` but
23
+ * keeping the surface uniform avoids `Unknown option` errors when the
24
+ * caller scripts the three subcommands together.
25
+ */
26
+ function withSharedPluginFlags(cmd, scopeDescription) {
27
+ return cmd
28
+ .option('--scope <scope>', scopeDescription, 'project')
29
+ .option('--no-skills', 'Skip the skills/ subtree')
30
+ .option('--no-commands', 'Skip the commands/ subtree')
31
+ .option('--only-mcp', 'Skip the plugin folder; just register the MCP server')
32
+ .option('--command <cmd>', 'Override the MCP server invocation in the plugin manifest')
33
+ .option('--env <name>', 'Env-var placeholder to surface on the plugin (repeatable)', (v, acc) => [...acc, v], [])
34
+ .option('--dir <dir>', 'Override the plugin destination root')
35
+ .option('--dry-run', 'Print the planned tree and exit without writing');
36
+ }
37
+ function registerInstallCommands(program) {
38
+ const plugin = program
39
+ .command('plugin')
40
+ .description('Install the current FrontMCP server as a plugin for an AI tool');
41
+ withSharedPluginFlags(plugin
42
+ .command('install')
43
+ .description('Emit a Claude Code plugin folder and/or Codex mcp_servers entry from the current project')
44
+ .option('--claude', 'Emit a Claude Code plugin into <scope>/.claude/plugins/<name>/')
45
+ .option('--codex', 'Emit a Codex mcp_servers entry into ~/.codex/config.toml'), 'project | user (default: project)').action(async (opts) => {
46
+ // Map flag name to the legacy internal option name expected by the runner.
47
+ const mappedOpts = { ...opts, claudePlugin: opts['claude'] === true };
48
+ const { runInstallCurrentProject } = await import('./install-claude-plugin.js');
49
+ await runInstallCurrentProject(mappedOpts);
50
+ });
51
+ withSharedPluginFlags(plugin
52
+ .command('uninstall')
53
+ .description('Remove what `frontmcp plugin install` previously wrote')
54
+ .option('--claude', 'Remove the Claude Code plugin folder')
55
+ .option('--codex', 'Remove the Codex mcp_servers entry'), 'project | user (default: project)').action(async (opts) => {
56
+ const mappedOpts = { ...opts, claudePlugin: opts['claude'] === true };
57
+ const { runUninstallCurrentProject } = await import('./install-claude-plugin.js');
58
+ await runUninstallCurrentProject(mappedOpts);
59
+ });
60
+ withSharedPluginFlags(plugin
61
+ .command('status')
62
+ .description('Report install state per provider')
63
+ .option('--claude', 'Check Claude Code plugin state')
64
+ .option('--codex', 'Check Codex mcp_servers entry'), 'project | user (default: project)').action(async (opts) => {
65
+ const mappedOpts = { ...opts, claudePlugin: opts['claude'] === true, status: true };
66
+ const { runInstallCurrentProject } = await import('./install-claude-plugin.js');
67
+ await runInstallCurrentProject(mappedOpts);
68
+ });
69
+ }
70
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../../../src/commands/install/register.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;AA6BH,0DA4CC;AArED;;;;;;;GAOG;AACH,SAAS,qBAAqB,CAAC,GAAY,EAAE,gBAAwB;IACnE,OAAO,GAAG;SACP,MAAM,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,CAAC;SACtD,MAAM,CAAC,aAAa,EAAE,0BAA0B,CAAC;SACjD,MAAM,CAAC,eAAe,EAAE,4BAA4B,CAAC;SACrD,MAAM,CAAC,YAAY,EAAE,sDAAsD,CAAC;SAC5E,MAAM,CAAC,iBAAiB,EAAE,2DAA2D,CAAC;SACtF,MAAM,CACL,cAAc,EACd,2DAA2D,EAC3D,CAAC,CAAS,EAAE,GAAa,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,EACzC,EAAc,CACf;SACA,MAAM,CAAC,aAAa,EAAE,sCAAsC,CAAC;SAC7D,MAAM,CAAC,WAAW,EAAE,iDAAiD,CAAC,CAAC;AAC5E,CAAC;AAED,SAAgB,uBAAuB,CAAC,OAAgB;IACtD,MAAM,MAAM,GAAG,OAAO;SACnB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,gEAAgE,CAAC,CAAC;IAEjF,qBAAqB,CACnB,MAAM;SACH,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,0FAA0F,CAAC;SACvG,MAAM,CAAC,UAAU,EAAE,gEAAgE,CAAC;SACpF,MAAM,CAAC,SAAS,EAAE,0DAA0D,CAAC,EAChF,mCAAmC,CACpC,CAAC,MAAM,CAAC,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,2EAA2E;QAC3E,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;QACtE,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAChF,MAAM,wBAAwB,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,qBAAqB,CACnB,MAAM;SACH,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,wDAAwD,CAAC;SACrE,MAAM,CAAC,UAAU,EAAE,sCAAsC,CAAC;SAC1D,MAAM,CAAC,SAAS,EAAE,oCAAoC,CAAC,EAC1D,mCAAmC,CACpC,CAAC,MAAM,CAAC,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;QACtE,MAAM,EAAE,0BAA0B,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClF,MAAM,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,qBAAqB,CACnB,MAAM;SACH,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,mCAAmC,CAAC;SAChD,MAAM,CAAC,UAAU,EAAE,gCAAgC,CAAC;SACpD,MAAM,CAAC,SAAS,EAAE,+BAA+B,CAAC,EACrD,mCAAmC,CACpC,CAAC,MAAM,CAAC,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACpF,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAChF,MAAM,wBAAwB,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Issue #411 — registers the dev-tool side `frontmcp plugin` command tree\n * (uses `plugin` as the noun because `install` is already taken by the\n * npm-package install command at `package/register.ts`).\n *\n * Subcommands:\n * frontmcp plugin install — emit a Claude Code plugin folder + Codex entry\n * frontmcp plugin uninstall — remove what `install` wrote\n * frontmcp plugin status — report install state per provider\n *\n * The per-bin equivalent (auto-included in every built CLI) extends the\n * existing `<bin> install` with `-p claude|codex` and shares the same\n * emitter under `cli-runtime/plugin-emitter.ts`.\n */\n\nimport { type Command } from 'commander';\n\n/**\n * Attach the shared plugin-flag surface to a subcommand. `install`,\n * `uninstall`, and `status` all accept the same flag set so docs +\n * tests can rely on a uniform invocation contract (issue #411). The\n * dry-run / skip-tree / mcp-override flags are no-ops on `status` but\n * keeping the surface uniform avoids `Unknown option` errors when the\n * caller scripts the three subcommands together.\n */\nfunction withSharedPluginFlags(cmd: Command, scopeDescription: string): Command {\n return cmd\n .option('--scope <scope>', scopeDescription, 'project')\n .option('--no-skills', 'Skip the skills/ subtree')\n .option('--no-commands', 'Skip the commands/ subtree')\n .option('--only-mcp', 'Skip the plugin folder; just register the MCP server')\n .option('--command <cmd>', 'Override the MCP server invocation in the plugin manifest')\n .option(\n '--env <name>',\n 'Env-var placeholder to surface on the plugin (repeatable)',\n (v: string, acc: string[]) => [...acc, v],\n [] as string[],\n )\n .option('--dir <dir>', 'Override the plugin destination root')\n .option('--dry-run', 'Print the planned tree and exit without writing');\n}\n\nexport function registerInstallCommands(program: Command): void {\n const plugin = program\n .command('plugin')\n .description('Install the current FrontMCP server as a plugin for an AI tool');\n\n withSharedPluginFlags(\n plugin\n .command('install')\n .description('Emit a Claude Code plugin folder and/or Codex mcp_servers entry from the current project')\n .option('--claude', 'Emit a Claude Code plugin into <scope>/.claude/plugins/<name>/')\n .option('--codex', 'Emit a Codex mcp_servers entry into ~/.codex/config.toml'),\n 'project | user (default: project)',\n ).action(async (opts: Record<string, unknown>) => {\n // Map flag name to the legacy internal option name expected by the runner.\n const mappedOpts = { ...opts, claudePlugin: opts['claude'] === true };\n const { runInstallCurrentProject } = await import('./install-claude-plugin.js');\n await runInstallCurrentProject(mappedOpts);\n });\n\n withSharedPluginFlags(\n plugin\n .command('uninstall')\n .description('Remove what `frontmcp plugin install` previously wrote')\n .option('--claude', 'Remove the Claude Code plugin folder')\n .option('--codex', 'Remove the Codex mcp_servers entry'),\n 'project | user (default: project)',\n ).action(async (opts: Record<string, unknown>) => {\n const mappedOpts = { ...opts, claudePlugin: opts['claude'] === true };\n const { runUninstallCurrentProject } = await import('./install-claude-plugin.js');\n await runUninstallCurrentProject(mappedOpts);\n });\n\n withSharedPluginFlags(\n plugin\n .command('status')\n .description('Report install state per provider')\n .option('--claude', 'Check Claude Code plugin state')\n .option('--codex', 'Check Codex mcp_servers entry'),\n 'project | user (default: project)',\n ).action(async (opts: Record<string, unknown>) => {\n const mappedOpts = { ...opts, claudePlugin: opts['claude'] === true, status: true };\n const { runInstallCurrentProject } = await import('./install-claude-plugin.js');\n await runInstallCurrentProject(mappedOpts);\n });\n}\n"]}
@@ -77,6 +77,47 @@ async function scaffoldFileIfMissing(baseDir, p, content) {
77
77
  await (0, utils_1.writeFile)(p, content.replace(/^\n/, ''));
78
78
  console.log((0, colors_1.c)('green', `✓ created ${path.relative(baseDir, p)}`));
79
79
  }
80
+ /**
81
+ * Render a starter `frontmcp.config.ts` for the chosen deployment target
82
+ * (issue #400). The emitted file is consumed by `dev`, `test`, `inspector`,
83
+ * `pm start/socket`, and `skills install/export` — see the matching
84
+ * `deployment/frontmcp-config` docs page for the full surface.
85
+ */
86
+ function renderFrontmcpConfigTemplate(projectName, deploymentTarget) {
87
+ const safeName = sanitizeForFolder(projectName);
88
+ // Every value of `DeploymentTarget` ('node' | 'vercel' | 'lambda' |
89
+ // 'cloudflare') ships HTTP, so the scaffold only ever emits the HTTP
90
+ // client + transport blocks. The stdio variant lived here briefly but
91
+ // was unreachable — reintroduce it (and add a 'desktop'/'cli' target to
92
+ // `DeploymentTarget`) if scaffold should support stdio in the future.
93
+ const port = 3000;
94
+ const clientBlock = ` clients: {
95
+ 'claude-code': {
96
+ name: '${safeName}',
97
+ transport: 'http',
98
+ url: 'http://127.0.0.1:${port}/mcp',
99
+ },
100
+ },`;
101
+ const transportBlock = ` transport: { default: 'http', http: { port: ${port}, path: '/mcp' } },`;
102
+ return `import { defineConfig } from 'frontmcp';
103
+
104
+ // Single source of truth for every \`frontmcp\` CLI command (dev / test /
105
+ // inspector / pm start / socket / skills install / export). Override any
106
+ // field with an explicit CLI flag or the matching \`FRONTMCP_<NAME>\` env
107
+ // var — precedence: CLI > env > config > built-in default.
108
+ export default defineConfig({
109
+ name: '${safeName}',
110
+ entry: './src/main.ts',
111
+ deployments: [{ target: '${deploymentTarget}' }],
112
+ ${transportBlock}
113
+ env: {
114
+ shared: {},
115
+ dev: { NODE_ENV: 'development' },
116
+ },
117
+ ${clientBlock}
118
+ });
119
+ `;
120
+ }
80
121
  const TEMPLATE_MAIN_TS = `
81
122
  import 'reflect-metadata';
82
123
  import { FrontMcp } from '@frontmcp/sdk';
@@ -524,7 +565,7 @@ function generatePmSetupSteps(pm) {
524
565
  uses: pnpm/action-setup@v4
525
566
 
526
567
  - name: Setup Node.js
527
- uses: actions/setup-node@v4
568
+ uses: actions/setup-node@v6
528
569
  with:
529
570
  node-version: '24'
530
571
  cache: '${cfg.ghCache}'
@@ -534,7 +575,7 @@ function generatePmSetupSteps(pm) {
534
575
  }
535
576
  return `
536
577
  - name: Setup Node.js
537
- uses: actions/setup-node@v4
578
+ uses: actions/setup-node@v6
538
579
  with:
539
580
  node-version: '24'
540
581
  cache: '${cfg.ghCache}'
@@ -558,7 +599,7 @@ jobs:
558
599
  runs-on: ubuntu-latest
559
600
 
560
601
  steps:
561
- - uses: actions/checkout@v4
602
+ - uses: actions/checkout@v6
562
603
  ${generatePmSetupSteps(pm)}
563
604
 
564
605
  - name: Type check
@@ -584,7 +625,7 @@ jobs:
584
625
  runs-on: ubuntu-latest
585
626
 
586
627
  steps:
587
- - uses: actions/checkout@v4
628
+ - uses: actions/checkout@v6
588
629
  ${generatePmSetupSteps(pm)}
589
630
 
590
631
  - name: Build
@@ -614,7 +655,7 @@ jobs:
614
655
  packages: write
615
656
 
616
657
  steps:
617
- - uses: actions/checkout@v4
658
+ - uses: actions/checkout@v6
618
659
 
619
660
  - name: Log in to Container Registry
620
661
  uses: docker/login-action@v3
@@ -652,7 +693,7 @@ jobs:
652
693
  runs-on: ubuntu-latest
653
694
 
654
695
  steps:
655
- - uses: actions/checkout@v4
696
+ - uses: actions/checkout@v6
656
697
  ${generatePmSetupSteps(pm)}
657
698
 
658
699
  - name: Build
@@ -681,7 +722,7 @@ jobs:
681
722
  runs-on: ubuntu-latest
682
723
 
683
724
  steps:
684
- - uses: actions/checkout@v4
725
+ - uses: actions/checkout@v6
685
726
  ${generatePmSetupSteps(pm)}
686
727
 
687
728
  - name: Build
@@ -718,7 +759,7 @@ jobs:
718
759
  runs-on: ubuntu-latest
719
760
 
720
761
  steps:
721
- - uses: actions/checkout@v4
762
+ - uses: actions/checkout@v6
722
763
  ${generatePmSetupSteps(pm)}
723
764
 
724
765
  - name: Build
@@ -1444,6 +1485,9 @@ async function scaffoldProject(options) {
1444
1485
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'main.ts'), TEMPLATE_MAIN_TS);
1445
1486
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'calc.app.ts'), TEMPLATE_CALC_APP_TS);
1446
1487
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'tools', 'add.tool.ts'), TEMPLATE_ADD_TOOL_TS);
1488
+ // Issue #400 — emit a starter frontmcp.config.ts so subsequent CLI runs
1489
+ // pick up entry / port / env / client snippets without re-typing them.
1490
+ await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'frontmcp.config.ts'), renderFrontmcpConfigTemplate(projectName, deploymentTarget));
1447
1491
  // E2E scaffolding (jest config auto-generated by `frontmcp test`)
1448
1492
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'e2e', 'server.e2e.spec.ts'), TEMPLATE_E2E_TEST_TS);
1449
1493
  // Skills scaffolding