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.
Files changed (108) hide show
  1. package/package.json +4 -4
  2. package/src/commands/build/exec/bin-meta.d.ts +49 -0
  3. package/src/commands/build/exec/bin-meta.js +68 -0
  4. package/src/commands/build/exec/bin-meta.js.map +1 -0
  5. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +195 -3
  6. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  7. package/src/commands/build/exec/cli-runtime/plugin-emitter.d.ts +160 -0
  8. package/src/commands/build/exec/cli-runtime/plugin-emitter.js +512 -0
  9. package/src/commands/build/exec/cli-runtime/plugin-emitter.js.map +1 -0
  10. package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +13 -1
  11. package/src/commands/build/exec/cli-runtime/schema-extractor.js +29 -3
  12. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  13. package/src/commands/build/exec/cli-runtime/skill-md-compose.d.ts +25 -0
  14. package/src/commands/build/exec/cli-runtime/skill-md-compose.js +63 -0
  15. package/src/commands/build/exec/cli-runtime/skill-md-compose.js.map +1 -0
  16. package/src/commands/build/exec/index.js +26 -0
  17. package/src/commands/build/exec/index.js.map +1 -1
  18. package/src/commands/dev/bridge/child-supervisor.d.ts +48 -0
  19. package/src/commands/dev/bridge/child-supervisor.js +228 -0
  20. package/src/commands/dev/bridge/child-supervisor.js.map +1 -0
  21. package/src/commands/dev/bridge/errors.d.ts +23 -0
  22. package/src/commands/dev/bridge/errors.js +34 -0
  23. package/src/commands/dev/bridge/errors.js.map +1 -0
  24. package/src/commands/dev/bridge/index.d.ts +30 -0
  25. package/src/commands/dev/bridge/index.js +220 -0
  26. package/src/commands/dev/bridge/index.js.map +1 -0
  27. package/src/commands/dev/bridge/log.d.ts +29 -0
  28. package/src/commands/dev/bridge/log.js +82 -0
  29. package/src/commands/dev/bridge/log.js.map +1 -0
  30. package/src/commands/dev/bridge/state-machine.d.ts +56 -0
  31. package/src/commands/dev/bridge/state-machine.js +245 -0
  32. package/src/commands/dev/bridge/state-machine.js.map +1 -0
  33. package/src/commands/dev/bridge/stdio-framer.d.ts +47 -0
  34. package/src/commands/dev/bridge/stdio-framer.js +128 -0
  35. package/src/commands/dev/bridge/stdio-framer.js.map +1 -0
  36. package/src/commands/dev/bridge/upstream-client.d.ts +49 -0
  37. package/src/commands/dev/bridge/upstream-client.js +159 -0
  38. package/src/commands/dev/bridge/upstream-client.js.map +1 -0
  39. package/src/commands/dev/bridge/watcher.d.ts +30 -0
  40. package/src/commands/dev/bridge/watcher.js +87 -0
  41. package/src/commands/dev/bridge/watcher.js.map +1 -0
  42. package/src/commands/dev/dev.d.ts +18 -1
  43. package/src/commands/dev/dev.js +134 -14
  44. package/src/commands/dev/dev.js.map +1 -1
  45. package/src/commands/dev/inspector.d.ts +13 -1
  46. package/src/commands/dev/inspector.js +77 -3
  47. package/src/commands/dev/inspector.js.map +1 -1
  48. package/src/commands/dev/port.d.ts +23 -0
  49. package/src/commands/dev/port.js +87 -0
  50. package/src/commands/dev/port.js.map +1 -0
  51. package/src/commands/dev/register.d.ts +1 -1
  52. package/src/commands/dev/register.js +28 -4
  53. package/src/commands/dev/register.js.map +1 -1
  54. package/src/commands/dev/test.d.ts +26 -1
  55. package/src/commands/dev/test.js +181 -64
  56. package/src/commands/dev/test.js.map +1 -1
  57. package/src/commands/eject/mcp-client.d.ts +25 -0
  58. package/src/commands/eject/mcp-client.js +74 -0
  59. package/src/commands/eject/mcp-client.js.map +1 -0
  60. package/src/commands/eject/register.d.ts +9 -0
  61. package/src/commands/eject/register.js +56 -0
  62. package/src/commands/eject/register.js.map +1 -0
  63. package/src/commands/install/install-claude-plugin.d.ts +13 -0
  64. package/src/commands/install/install-claude-plugin.js +327 -0
  65. package/src/commands/install/install-claude-plugin.js.map +1 -0
  66. package/src/commands/install/register.d.ts +16 -0
  67. package/src/commands/install/register.js +70 -0
  68. package/src/commands/install/register.js.map +1 -0
  69. package/src/commands/scaffold/create.js +44 -0
  70. package/src/commands/scaffold/create.js.map +1 -1
  71. package/src/commands/skills/from-entry.d.ts +31 -0
  72. package/src/commands/skills/from-entry.js +68 -0
  73. package/src/commands/skills/from-entry.js.map +1 -0
  74. package/src/commands/skills/install.d.ts +12 -0
  75. package/src/commands/skills/install.js +173 -8
  76. package/src/commands/skills/install.js.map +1 -1
  77. package/src/commands/skills/register.js +7 -3
  78. package/src/commands/skills/register.js.map +1 -1
  79. package/src/config/frontmcp-config.loader.d.ts +28 -0
  80. package/src/config/frontmcp-config.loader.js +146 -67
  81. package/src/config/frontmcp-config.loader.js.map +1 -1
  82. package/src/config/frontmcp-config.resolve.d.ts +67 -0
  83. package/src/config/frontmcp-config.resolve.js +118 -0
  84. package/src/config/frontmcp-config.resolve.js.map +1 -0
  85. package/src/config/frontmcp-config.schema.d.ts +207 -0
  86. package/src/config/frontmcp-config.schema.js +217 -1
  87. package/src/config/frontmcp-config.schema.js.map +1 -1
  88. package/src/config/frontmcp-config.types.d.ts +133 -0
  89. package/src/config/frontmcp-config.types.js.map +1 -1
  90. package/src/config/index.d.ts +2 -1
  91. package/src/config/index.js +3 -1
  92. package/src/config/index.js.map +1 -1
  93. package/src/core/args.d.ts +13 -0
  94. package/src/core/args.js.map +1 -1
  95. package/src/core/bridge.js +39 -0
  96. package/src/core/bridge.js.map +1 -1
  97. package/src/core/cli.d.ts +0 -6
  98. package/src/core/cli.js +23 -3
  99. package/src/core/cli.js.map +1 -1
  100. package/src/core/help.d.ts +1 -1
  101. package/src/core/help.js +27 -6
  102. package/src/core/help.js.map +1 -1
  103. package/src/core/program.d.ts +1 -1
  104. package/src/core/program.js +56 -12
  105. package/src/core/program.js.map +1 -1
  106. package/src/core/project-commands.d.ts +44 -0
  107. package/src/core/project-commands.js +216 -0
  108. package/src/core/project-commands.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/core/help.ts"],"names":[],"mappings":"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","sourcesContent":["import { Command } from 'commander';\nimport { c } from './colors';\n\n/**\n * Command groups in display order, mapped to the command names they contain.\n * The `skills` group is handled separately to show subcommands inline.\n */\nconst GROUPS: [label: string, commands: string[]][] = [\n ['Getting Started', ['create', 'init', 'doctor']],\n ['Development', ['dev', 'build', 'test', 'inspector']],\n ['Process Manager', ['start', 'stop', 'restart', 'status', 'list', 'logs', 'socket', 'service']],\n ['Package Manager', ['install', 'uninstall', 'configure']],\n];\n\nconst EXAMPLES: string[] = [\n 'npx frontmcp create my-mcp # Scaffold a new project',\n 'frontmcp dev # Start dev server with hot-reload',\n 'frontmcp build --target node # Build for Node.js deployment',\n 'frontmcp start my-app --port 3005 # Start managed server',\n 'frontmcp install @company/my-mcp # Install from npm registry',\n 'frontmcp skills search \"openapi\" # Find skills in catalog',\n];\n\n/** Format a top-level command line with cyan name and dim arg placeholders. */\nfunction formatCommandLine(sub: Command, padWidth: number): string {\n const rawName = sub.name();\n const rawArgs = sub.registeredArguments.map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const rawTerm = rawArgs ? `${rawName} ${rawArgs}` : rawName;\n\n const name = c('cyan', rawName);\n const args = sub.registeredArguments.map((a) => c('dim', a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const term = args ? `${name} ${args}` : name;\n\n const padding = ' '.repeat(Math.max(2, padWidth - rawTerm.length + 2));\n return ` ${term}${padding}${sub.description()}`;\n}\n\n/** Format a skills subcommand as \"skills <subname> <args>\". */\nfunction formatSkillsLine(sub: Command, padWidth: number): string {\n const rawPrefix = `skills ${sub.name()}`;\n const rawArgs = sub.registeredArguments.map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const rawTerm = rawArgs ? `${rawPrefix} ${rawArgs}` : rawPrefix;\n\n const prefix = c('cyan', rawPrefix);\n const args = sub.registeredArguments.map((a) => c('dim', a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const term = args ? `${prefix} ${args}` : prefix;\n\n const padding = ' '.repeat(Math.max(2, padWidth - rawTerm.length + 2));\n return ` ${term}${padding}${sub.description()}`;\n}\n\n/**\n * Apply custom help formatting to the top-level program so that\n * `frontmcp --help` groups commands under section headers, shows skills\n * subcommands inline, and appends a concise Examples block.\n */\nexport function customizeHelp(program: Command): void {\n program.configureHelp({\n formatHelp(cmd, helper) {\n let termWidth = helper.padWidth(cmd, helper);\n const lines: string[] = [];\n\n // Adjust padWidth to account for skills subcommand terms (e.g. \"skills search <query>\")\n const allCommands = helper.visibleCommands(cmd);\n const skillsCmd = allCommands.find((c) => c.name() === 'skills');\n const skillsSubs = skillsCmd ? helper.visibleCommands(skillsCmd) : [];\n if (skillsCmd) {\n for (const sub of skillsSubs) {\n const rawArgs = sub.registeredArguments\n .map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`))\n .join(' ');\n const rawTerm = rawArgs ? `skills ${sub.name()} ${rawArgs}` : `skills ${sub.name()}`;\n termWidth = Math.max(termWidth, rawTerm.length);\n }\n }\n\n // Description\n const desc = helper.commandDescription(cmd);\n if (desc) lines.push(desc, '');\n\n // Usage\n lines.push(c('bold', 'Usage'));\n lines.push(` ${helper.commandUsage(cmd)}`, '');\n\n // Grouped commands\n for (const [label, names] of GROUPS) {\n const matching = names.map((n) => allCommands.find((c) => c.name() === n)).filter(Boolean) as Command[];\n if (matching.length === 0) continue;\n\n lines.push(c('bold', label));\n for (const sub of matching) {\n lines.push(formatCommandLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Skills — show subcommands inline\n if (skillsCmd && skillsSubs.length > 0) {\n lines.push(c('bold', 'Skills'));\n for (const sub of skillsSubs) {\n lines.push(formatSkillsLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Other commands not in any group\n const renderedNames = new Set([...GROUPS.flatMap(([, names]) => names), 'skills']);\n const other = allCommands.filter((sc) => !renderedNames.has(sc.name()));\n if (other.length > 0) {\n lines.push(c('bold', 'Other'));\n for (const sub of other) {\n lines.push(formatCommandLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Global options\n const opts = helper.visibleOptions(cmd);\n if (opts.length > 0) {\n lines.push(c('bold', 'Options'));\n for (const opt of opts) {\n const term = helper.optionTerm(opt);\n const padding = ' '.repeat(Math.max(2, termWidth - term.length + 2));\n lines.push(` ${term}${padding}${helper.optionDescription(opt)}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n },\n });\n\n program.addHelpText('after', () => {\n const lines = [\n c('bold', 'Examples'),\n ...EXAMPLES.map((ex) => ` ${ex}`),\n '',\n c('dim', `Use ${c('cyan', 'frontmcp <command> --help')} for detailed usage of any command.`),\n '',\n ];\n return '\\n' + lines.join('\\n');\n });\n}\n"]}
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/core/help.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from 'commander';\n\nimport { c } from './colors';\nimport { isCommandHidden, isProjectCommand } from './project-commands';\n\ninterface CommanderArgument {\n name(): string;\n required: boolean;\n}\n\n// commander v13 exposes registered arguments on the private `_args` property.\n// Newer (v14+) versions expose `registeredArguments`; prefer that when present.\nfunction getArgs(cmd: Command): CommanderArgument[] {\n const fromNewApi = (cmd as unknown as { registeredArguments?: CommanderArgument[] }).registeredArguments;\n if (fromNewApi) return fromNewApi;\n return ((cmd as unknown as { _args?: CommanderArgument[] })._args ?? []) as CommanderArgument[];\n}\n\n/**\n * Command groups in display order, mapped to the command names they contain.\n * The `skills` group is handled separately to show subcommands inline.\n */\nconst GROUPS: [label: string, commands: string[]][] = [\n ['Getting Started', ['create', 'init', 'doctor']],\n ['Development', ['dev', 'build', 'test', 'inspector']],\n ['Process Manager', ['start', 'stop', 'restart', 'status', 'list', 'logs', 'socket', 'service']],\n ['Package Manager', ['install', 'uninstall', 'configure']],\n];\n\nconst EXAMPLES: string[] = [\n 'npx frontmcp create my-mcp # Scaffold a new project',\n 'frontmcp dev # Start dev server with hot-reload',\n 'frontmcp build --target node # Build for Node.js deployment',\n 'frontmcp start my-app --port 3005 # Start managed server',\n 'frontmcp install @company/my-mcp # Install from npm registry',\n 'frontmcp skills search \"openapi\" # Find skills in catalog',\n];\n\n/** Format a top-level command line with cyan name and dim arg placeholders. */\nfunction formatCommandLine(sub: Command, padWidth: number): string {\n const argsList = getArgs(sub);\n const rawName = sub.name();\n const rawArgs = argsList.map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const rawTerm = rawArgs ? `${rawName} ${rawArgs}` : rawName;\n\n const name = c('cyan', rawName);\n const args = argsList.map((a) => c('dim', a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const term = args ? `${name} ${args}` : name;\n\n const padding = ' '.repeat(Math.max(2, padWidth - rawTerm.length + 2));\n return ` ${term}${padding}${sub.description()}`;\n}\n\n/** Format a skills subcommand as \"skills <subname> <args>\". */\nfunction formatSkillsLine(sub: Command, padWidth: number): string {\n const argsList = getArgs(sub);\n const rawPrefix = `skills ${sub.name()}`;\n const rawArgs = argsList.map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const rawTerm = rawArgs ? `${rawPrefix} ${rawArgs}` : rawPrefix;\n\n const prefix = c('cyan', rawPrefix);\n const args = argsList.map((a) => c('dim', a.required ? `<${a.name()}>` : `[${a.name()}]`)).join(' ');\n const term = args ? `${prefix} ${args}` : prefix;\n\n const padding = ' '.repeat(Math.max(2, padWidth - rawTerm.length + 2));\n return ` ${term}${padding}${sub.description()}`;\n}\n\n/**\n * Apply custom help formatting to the top-level program so that\n * `frontmcp --help` groups commands under section headers, shows skills\n * subcommands inline, and appends a concise Examples block.\n */\nexport function customizeHelp(program: Command): void {\n program.configureHelp({\n formatHelp(cmd, helper) {\n let termWidth = helper.padWidth(cmd, helper);\n const lines: string[] = [];\n\n // Adjust padWidth to account for skills subcommand terms (e.g. \"skills search <query>\")\n const allCommands = helper.visibleCommands(cmd);\n const skillsCmd = allCommands.find((c) => c.name() === 'skills');\n const skillsSubs = skillsCmd ? helper.visibleCommands(skillsCmd) : [];\n if (skillsCmd) {\n for (const sub of skillsSubs) {\n const rawArgs = getArgs(sub)\n .map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`))\n .join(' ');\n const rawTerm = rawArgs ? `skills ${sub.name()} ${rawArgs}` : `skills ${sub.name()}`;\n termWidth = Math.max(termWidth, rawTerm.length);\n }\n }\n\n // Description\n const desc = helper.commandDescription(cmd);\n if (desc) lines.push(desc, '');\n\n // Usage\n lines.push(c('bold', 'Usage'));\n lines.push(` ${helper.commandUsage(cmd)}`, '');\n\n // Grouped commands\n for (const [label, names] of GROUPS) {\n const matching = names.map((n) => allCommands.find((c) => c.name() === n)).filter(Boolean) as Command[];\n if (matching.length === 0) continue;\n\n lines.push(c('bold', label));\n for (const sub of matching) {\n lines.push(formatCommandLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Skills — show subcommands inline\n if (skillsCmd && skillsSubs.length > 0) {\n lines.push(c('bold', 'Skills'));\n for (const sub of skillsSubs) {\n lines.push(formatSkillsLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Project commands (registered via cli.commands in frontmcp.config).\n // Hide commands marked with `hidden: true`.\n const projectCommands = allCommands.filter((sc) => isProjectCommand(sc) && !isCommandHidden(sc));\n if (projectCommands.length > 0) {\n lines.push(c('bold', 'Project Commands'));\n for (const sub of projectCommands) {\n lines.push(formatCommandLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Other commands not in any group\n const renderedNames = new Set([...GROUPS.flatMap(([, names]) => names), 'skills']);\n const other = allCommands.filter((sc) => !renderedNames.has(sc.name()) && !isProjectCommand(sc));\n if (other.length > 0) {\n lines.push(c('bold', 'Other'));\n for (const sub of other) {\n lines.push(formatCommandLine(sub, termWidth));\n }\n lines.push('');\n }\n\n // Global options\n const opts = helper.visibleOptions(cmd);\n if (opts.length > 0) {\n lines.push(c('bold', 'Options'));\n for (const opt of opts) {\n const term = helper.optionTerm(opt);\n const padding = ' '.repeat(Math.max(2, termWidth - term.length + 2));\n lines.push(` ${term}${padding}${helper.optionDescription(opt)}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n },\n });\n\n program.addHelpText('after', () => {\n const lines = [\n c('bold', 'Examples'),\n ...EXAMPLES.map((ex) => ` ${ex}`),\n '',\n c('dim', `Use ${c('cyan', 'frontmcp <command> --help')} for detailed usage of any command.`),\n '',\n ];\n return '\\n' + lines.join('\\n');\n });\n}\n"]}
@@ -1,2 +1,2 @@
1
1
  import { Command } from 'commander';
2
- export declare function createProgram(): Command;
2
+ export declare function createProgram(cwd?: string, argv?: string[]): Promise<Command>;
@@ -4,26 +4,70 @@ exports.createProgram = createProgram;
4
4
  const commander_1 = require("commander");
5
5
  const register_1 = require("../commands/build/register");
6
6
  const register_2 = require("../commands/dev/register");
7
- const register_3 = require("../commands/mcpb/register");
8
- const register_4 = require("../commands/package/register");
9
- const register_5 = require("../commands/pm/register");
10
- const register_6 = require("../commands/scaffold/register");
11
- const register_7 = require("../commands/skills/register");
7
+ const register_3 = require("../commands/eject/register");
8
+ const register_4 = require("../commands/install/register");
9
+ const register_5 = require("../commands/mcpb/register");
10
+ const register_6 = require("../commands/package/register");
11
+ const register_7 = require("../commands/pm/register");
12
+ const register_8 = require("../commands/scaffold/register");
13
+ const register_9 = require("../commands/skills/register");
12
14
  const help_1 = require("./help");
15
+ const project_commands_1 = require("./project-commands");
13
16
  const version_1 = require("./version");
14
- function createProgram() {
17
+ /**
18
+ * Skip project-command registration for invocations that only print the
19
+ * package version (`frontmcp --version` / `-V`) — those don't need the
20
+ * config-load cost.
21
+ *
22
+ * Bare `frontmcp` and `frontmcp --help` / `-h` DO need the load: both
23
+ * render the help banner, which must include project verbs registered
24
+ * via `cli.commands` (issue #409). `--list-commands` also needs the
25
+ * load — that's the whole point of the flag.
26
+ */
27
+ function shouldSkipProjectCommandLoad(argv) {
28
+ const args = argv.slice(2);
29
+ // Bare `frontmcp` shows the help banner → must include project verbs.
30
+ if (args.length === 0)
31
+ return false;
32
+ const firstVerbIdx = args.findIndex((a) => !a.startsWith('-'));
33
+ if (firstVerbIdx !== -1)
34
+ return false;
35
+ // No verb token — pure flag invocation. `--list-commands` and the
36
+ // help flags all render output that must include project verbs.
37
+ if (args.includes('--list-commands'))
38
+ return false;
39
+ if (args.includes('--help') || args.includes('-h'))
40
+ return false;
41
+ // What's left is --version / -V (or any pure metadata flag that
42
+ // doesn't render the verb listing). Skip the project-command load.
43
+ const metadataFlags = new Set(['--version', '-V']);
44
+ return args.every((a) => metadataFlags.has(a));
45
+ }
46
+ async function createProgram(cwd = process.cwd(), argv = process.argv) {
15
47
  const program = new commander_1.Command();
16
48
  program
17
49
  .name('frontmcp')
18
50
  .description('Build, test, and deploy MCP servers with FrontMCP')
19
- .version((0, version_1.getSelfVersion)(), '-V, --version');
51
+ .version((0, version_1.getSelfVersion)(), '-V, --version')
52
+ .option('--list-commands', 'List every registered verb (built-in + project) and exit')
53
+ // Issue #400 — top-level `--config <path>` option. Commands that
54
+ // consume the unified config (dev, test, inspector, eject, pm, skills) read
55
+ // `program.opts().config` (or `FRONTMCP_CONFIG` env) to locate the
56
+ // file. Override precedence:
57
+ // explicit --config > FRONTMCP_CONFIG > upward walk from cwd
58
+ .option('-c, --config <path>', 'Path to frontmcp.config file (overrides upward search and FRONTMCP_CONFIG env)');
20
59
  (0, register_2.registerDevCommands)(program);
21
60
  (0, register_1.registerBuildCommands)(program);
22
- (0, register_6.registerScaffoldCommands)(program);
23
- (0, register_5.registerPmCommands)(program);
24
- (0, register_4.registerPackageCommands)(program);
25
- (0, register_7.registerSkillsCommands)(program);
26
- (0, register_3.registerMcpbCommands)(program);
61
+ (0, register_8.registerScaffoldCommands)(program);
62
+ (0, register_7.registerPmCommands)(program);
63
+ (0, register_6.registerPackageCommands)(program);
64
+ (0, register_9.registerSkillsCommands)(program);
65
+ (0, register_5.registerMcpbCommands)(program);
66
+ (0, register_3.registerEjectCommands)(program);
67
+ (0, register_4.registerInstallCommands)(program);
68
+ if (!shouldSkipProjectCommandLoad(argv)) {
69
+ await (0, project_commands_1.registerProjectCommands)(program, cwd);
70
+ }
27
71
  (0, help_1.customizeHelp)(program);
28
72
  return program;
29
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"program.js","sourceRoot":"","sources":["../../../src/core/program.ts"],"names":[],"mappings":";;AAYA,sCAkBC;AA9BD,yCAAoC;AAEpC,yDAAmE;AACnE,uDAA+D;AAC/D,wDAAiE;AACjE,2DAAuE;AACvE,sDAA6D;AAC7D,4DAAyE;AACzE,0DAAqE;AACrE,iCAAuC;AACvC,uCAA2C;AAE3C,SAAgB,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,UAAU,CAAC;SAChB,WAAW,CAAC,mDAAmD,CAAC;SAChE,OAAO,CAAC,IAAA,wBAAc,GAAE,EAAE,eAAe,CAAC,CAAC;IAE9C,IAAA,8BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,gCAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,mCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,6BAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,iCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,oBAAa,EAAC,OAAO,CAAC,CAAC;IAEvB,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import { Command } from 'commander';\n\nimport { registerBuildCommands } from '../commands/build/register';\nimport { registerDevCommands } from '../commands/dev/register';\nimport { registerMcpbCommands } from '../commands/mcpb/register';\nimport { registerPackageCommands } from '../commands/package/register';\nimport { registerPmCommands } from '../commands/pm/register';\nimport { registerScaffoldCommands } from '../commands/scaffold/register';\nimport { registerSkillsCommands } from '../commands/skills/register';\nimport { customizeHelp } from './help';\nimport { getSelfVersion } from './version';\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name('frontmcp')\n .description('Build, test, and deploy MCP servers with FrontMCP')\n .version(getSelfVersion(), '-V, --version');\n\n registerDevCommands(program);\n registerBuildCommands(program);\n registerScaffoldCommands(program);\n registerPmCommands(program);\n registerPackageCommands(program);\n registerSkillsCommands(program);\n registerMcpbCommands(program);\n customizeHelp(program);\n\n return program;\n}\n"]}
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../../../src/core/program.ts"],"names":[],"mappings":";;AAyCA,sCAgCC;AAzED,yCAAoC;AAEpC,yDAAmE;AACnE,uDAA+D;AAC/D,yDAAmE;AACnE,2DAAuE;AACvE,wDAAiE;AACjE,2DAAuE;AACvE,sDAA6D;AAC7D,4DAAyE;AACzE,0DAAqE;AACrE,iCAAuC;AACvC,yDAA6D;AAC7D,uCAA2C;AAE3C;;;;;;;;;GASG;AACH,SAAS,4BAA4B,CAAC,IAAc;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,sEAAsE;IACtE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,IAAI,YAAY,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,kEAAkE;IAClE,gEAAgE;IAChE,IAAI,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,gEAAgE;IAChE,mEAAmE;IACnE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE,EAAE,OAAiB,OAAO,CAAC,IAAI;IAC5F,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,UAAU,CAAC;SAChB,WAAW,CAAC,mDAAmD,CAAC;SAChE,OAAO,CAAC,IAAA,wBAAc,GAAE,EAAE,eAAe,CAAC;SAC1C,MAAM,CAAC,iBAAiB,EAAE,0DAA0D,CAAC;QACtF,iEAAiE;QACjE,4EAA4E;QAC5E,mEAAmE;QACnE,6BAA6B;QAC7B,+DAA+D;SAC9D,MAAM,CAAC,qBAAqB,EAAE,gFAAgF,CAAC,CAAC;IAEnH,IAAA,8BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,gCAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,mCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,6BAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,iCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,gCAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IAEjC,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAA,0CAAuB,EAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,IAAA,oBAAa,EAAC,OAAO,CAAC,CAAC;IAEvB,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import { Command } from 'commander';\n\nimport { registerBuildCommands } from '../commands/build/register';\nimport { registerDevCommands } from '../commands/dev/register';\nimport { registerEjectCommands } from '../commands/eject/register';\nimport { registerInstallCommands } from '../commands/install/register';\nimport { registerMcpbCommands } from '../commands/mcpb/register';\nimport { registerPackageCommands } from '../commands/package/register';\nimport { registerPmCommands } from '../commands/pm/register';\nimport { registerScaffoldCommands } from '../commands/scaffold/register';\nimport { registerSkillsCommands } from '../commands/skills/register';\nimport { customizeHelp } from './help';\nimport { registerProjectCommands } from './project-commands';\nimport { getSelfVersion } from './version';\n\n/**\n * Skip project-command registration for invocations that only print the\n * package version (`frontmcp --version` / `-V`) — those don't need the\n * config-load cost.\n *\n * Bare `frontmcp` and `frontmcp --help` / `-h` DO need the load: both\n * render the help banner, which must include project verbs registered\n * via `cli.commands` (issue #409). `--list-commands` also needs the\n * load — that's the whole point of the flag.\n */\nfunction shouldSkipProjectCommandLoad(argv: string[]): boolean {\n const args = argv.slice(2);\n // Bare `frontmcp` shows the help banner → must include project verbs.\n if (args.length === 0) return false;\n const firstVerbIdx = args.findIndex((a) => !a.startsWith('-'));\n if (firstVerbIdx !== -1) return false;\n // No verb token — pure flag invocation. `--list-commands` and the\n // help flags all render output that must include project verbs.\n if (args.includes('--list-commands')) return false;\n if (args.includes('--help') || args.includes('-h')) return false;\n // What's left is --version / -V (or any pure metadata flag that\n // doesn't render the verb listing). Skip the project-command load.\n const metadataFlags = new Set(['--version', '-V']);\n return args.every((a) => metadataFlags.has(a));\n}\n\nexport async function createProgram(cwd: string = process.cwd(), argv: string[] = process.argv): Promise<Command> {\n const program = new Command();\n\n program\n .name('frontmcp')\n .description('Build, test, and deploy MCP servers with FrontMCP')\n .version(getSelfVersion(), '-V, --version')\n .option('--list-commands', 'List every registered verb (built-in + project) and exit')\n // Issue #400 — top-level `--config <path>` option. Commands that\n // consume the unified config (dev, test, inspector, eject, pm, skills) read\n // `program.opts().config` (or `FRONTMCP_CONFIG` env) to locate the\n // file. Override precedence:\n // explicit --config > FRONTMCP_CONFIG > upward walk from cwd\n .option('-c, --config <path>', 'Path to frontmcp.config file (overrides upward search and FRONTMCP_CONFIG env)');\n\n registerDevCommands(program);\n registerBuildCommands(program);\n registerScaffoldCommands(program);\n registerPmCommands(program);\n registerPackageCommands(program);\n registerSkillsCommands(program);\n registerMcpbCommands(program);\n registerEjectCommands(program);\n registerInstallCommands(program);\n\n if (!shouldSkipProjectCommandLoad(argv)) {\n await registerProjectCommands(program, cwd);\n }\n\n customizeHelp(program);\n\n return program;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Project-defined CLI commands (issue #409).
3
+ *
4
+ * Reads `cli.commands` from the project's frontmcp.config and registers
5
+ * each entry as a top-level Commander verb. The runner is spawned as a
6
+ * child process (tsx for `.ts` entries, node for `.js`/`.mjs`/`.cjs`).
7
+ *
8
+ * Failure to load the config is non-fatal: project commands are an
9
+ * optional layer on top of the built-in CLI, so a missing/broken config
10
+ * just yields zero project commands and never blocks `frontmcp --help`.
11
+ */
12
+ import type { Command } from 'commander';
13
+ import type { ProjectCommandEntry, ProjectCommandOption } from '../config/frontmcp-config.types';
14
+ interface ProjectCommandRecord {
15
+ readonly verb: string;
16
+ readonly entry: ProjectCommandEntry;
17
+ readonly cwd: string;
18
+ }
19
+ /** Thrown when a dispatched runner exits non-zero or by signal. cli.ts uses
20
+ * the exit code without printing the stack again. */
21
+ export declare class ProjectCommandFailedError extends Error {
22
+ readonly verb: string;
23
+ readonly exitCode: number;
24
+ readonly signal: NodeJS.Signals | null;
25
+ constructor(verb: string, exitCode: number, signal: NodeJS.Signals | null);
26
+ }
27
+ /**
28
+ * Load `cli.commands` from `frontmcp.config` (in `cwd`) and attach each as
29
+ * a top-level command on the program. No-op if no config is found.
30
+ */
31
+ export declare function registerProjectCommands(program: Command, cwd?: string): Promise<void>;
32
+ /**
33
+ * Spawn the project entry file as a child process and forward stdio. The
34
+ * child receives positionals as argv plus a `FRONTMCP_PROJECT_COMMAND` env
35
+ * var with the full payload (verb, positionals, options, cwd) so the runner
36
+ * can reconstruct context without re-parsing argv.
37
+ *
38
+ * Throws `ProjectCommandFailedError` (not a generic Error) on non-zero exit
39
+ * so cli.ts can exit with the child's code without re-printing the message.
40
+ */
41
+ export declare function dispatchToEntry(record: ProjectCommandRecord, actionArgs: unknown[]): Promise<void>;
42
+ export declare function isProjectCommand(cmd: Command): boolean;
43
+ export declare function isCommandHidden(cmd: Command): boolean;
44
+ export type { ProjectCommandEntry, ProjectCommandOption };
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ /**
3
+ * Project-defined CLI commands (issue #409).
4
+ *
5
+ * Reads `cli.commands` from the project's frontmcp.config and registers
6
+ * each entry as a top-level Commander verb. The runner is spawned as a
7
+ * child process (tsx for `.ts` entries, node for `.js`/`.mjs`/`.cjs`).
8
+ *
9
+ * Failure to load the config is non-fatal: project commands are an
10
+ * optional layer on top of the built-in CLI, so a missing/broken config
11
+ * just yields zero project commands and never blocks `frontmcp --help`.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.ProjectCommandFailedError = void 0;
15
+ exports.registerProjectCommands = registerProjectCommands;
16
+ exports.dispatchToEntry = dispatchToEntry;
17
+ exports.isProjectCommand = isProjectCommand;
18
+ exports.isCommandHidden = isCommandHidden;
19
+ const tslib_1 = require("tslib");
20
+ const child_process_1 = require("child_process");
21
+ const path = tslib_1.__importStar(require("path"));
22
+ const frontmcp_config_loader_1 = require("../config/frontmcp-config.loader");
23
+ const colors_1 = require("./colors");
24
+ const PROJECT_COMMAND_MARK = Symbol.for('frontmcp.project-command');
25
+ /** Thrown when a dispatched runner exits non-zero or by signal. cli.ts uses
26
+ * the exit code without printing the stack again. */
27
+ class ProjectCommandFailedError extends Error {
28
+ verb;
29
+ exitCode;
30
+ signal;
31
+ constructor(verb, exitCode, signal) {
32
+ super(signal
33
+ ? `Project command "${verb}" terminated by signal ${signal}`
34
+ : `Project command "${verb}" exited with code ${exitCode}`);
35
+ this.verb = verb;
36
+ this.exitCode = exitCode;
37
+ this.signal = signal;
38
+ this.name = 'ProjectCommandFailedError';
39
+ }
40
+ }
41
+ exports.ProjectCommandFailedError = ProjectCommandFailedError;
42
+ /**
43
+ * Load `cli.commands` from `frontmcp.config` (in `cwd`) and attach each as
44
+ * a top-level command on the program. No-op if no config is found.
45
+ */
46
+ async function registerProjectCommands(program, cwd = process.cwd()) {
47
+ const cfg = await safeLoad(cwd);
48
+ const commands = cfg?.cli?.commands;
49
+ if (!commands)
50
+ return;
51
+ for (const [verb, entry] of Object.entries(commands)) {
52
+ try {
53
+ const resolved = resolveAndCheckEntry(cwd, entry.entry);
54
+ attachCommand(program, { verb, entry: { ...entry, entry: resolved }, cwd });
55
+ }
56
+ catch (err) {
57
+ process.stderr.write((0, colors_1.c)('yellow', `⚠ Skipping project command "${verb}": ${err.message}\n`));
58
+ }
59
+ }
60
+ }
61
+ async function safeLoad(cwd) {
62
+ try {
63
+ return await (0, frontmcp_config_loader_1.tryLoadFrontMcpConfig)(cwd);
64
+ }
65
+ catch (err) {
66
+ process.stderr.write((0, colors_1.c)('yellow', `⚠ Could not load frontmcp.config for project commands: ${err.message}\n`));
67
+ return undefined;
68
+ }
69
+ }
70
+ /** Reject entry paths that escape the project cwd. */
71
+ function resolveAndCheckEntry(cwd, entry) {
72
+ if (path.isAbsolute(entry)) {
73
+ throw new Error(`entry must be a project-relative path, not absolute: "${entry}"`);
74
+ }
75
+ const resolved = path.resolve(cwd, entry);
76
+ const rel = path.relative(cwd, resolved);
77
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
78
+ throw new Error(`entry "${entry}" escapes the project directory`);
79
+ }
80
+ return resolved;
81
+ }
82
+ function attachCommand(program, record) {
83
+ const { verb, entry } = record;
84
+ const cmd = program.command(verb);
85
+ if (entry.description)
86
+ cmd.description(entry.description);
87
+ if (entry.hidden) {
88
+ cmd._hidden = true;
89
+ }
90
+ for (const arg of entry.arguments ?? []) {
91
+ cmd.argument(formatArgumentToken(arg), arg.description ?? '');
92
+ }
93
+ for (const opt of entry.options ?? []) {
94
+ if (opt.default !== undefined) {
95
+ // Commander v13 types only allow string/boolean/string[] as a default,
96
+ // but accepts arbitrary JS values at runtime. Cast to keep numbers
97
+ // (e.g. `default: 4` for a `<num>` flag) flowing through as-is.
98
+ cmd.option(opt.flags, opt.description ?? '', opt.default);
99
+ }
100
+ else {
101
+ cmd.option(opt.flags, opt.description ?? '');
102
+ }
103
+ }
104
+ cmd.action(async (...args) => {
105
+ await dispatchToEntry(record, args);
106
+ });
107
+ cmd[PROJECT_COMMAND_MARK] = true;
108
+ }
109
+ function formatArgumentToken(arg) {
110
+ const inner = arg.variadic ? `${arg.name}...` : arg.name;
111
+ return arg.required ? `<${inner}>` : `[${inner}]`;
112
+ }
113
+ /**
114
+ * Spawn the project entry file as a child process and forward stdio. The
115
+ * child receives positionals as argv plus a `FRONTMCP_PROJECT_COMMAND` env
116
+ * var with the full payload (verb, positionals, options, cwd) so the runner
117
+ * can reconstruct context without re-parsing argv.
118
+ *
119
+ * Throws `ProjectCommandFailedError` (not a generic Error) on non-zero exit
120
+ * so cli.ts can exit with the child's code without re-printing the message.
121
+ */
122
+ async function dispatchToEntry(record, actionArgs) {
123
+ // Commander invokes action with: ...positionals, options, command
124
+ const options = (actionArgs[actionArgs.length - 2] ?? {});
125
+ const positionals = actionArgs.slice(0, actionArgs.length - 2);
126
+ const entryAbs = record.entry.entry; // already resolved + safety-checked at register time
127
+ const runner = pickRunner(entryAbs);
128
+ const payload = {
129
+ verb: record.verb,
130
+ positionals,
131
+ options,
132
+ cwd: record.cwd,
133
+ };
134
+ const childArgv = [...runner.args, entryAbs, ...flattenPositionals(positionals), ...flattenOptions(options)];
135
+ const child = (0, child_process_1.spawn)(runner.cmd, childArgv, {
136
+ cwd: record.cwd,
137
+ stdio: 'inherit',
138
+ env: {
139
+ ...process.env,
140
+ FRONTMCP_PROJECT_COMMAND: JSON.stringify(payload),
141
+ },
142
+ });
143
+ await new Promise((resolve, reject) => {
144
+ child.on('error', reject);
145
+ child.on('exit', (code, signal) => {
146
+ if (signal) {
147
+ reject(new ProjectCommandFailedError(record.verb, 1, signal));
148
+ return;
149
+ }
150
+ if (code !== 0) {
151
+ reject(new ProjectCommandFailedError(record.verb, code ?? 1, null));
152
+ return;
153
+ }
154
+ resolve();
155
+ });
156
+ });
157
+ }
158
+ function pickRunner(entryAbs) {
159
+ const ext = path.extname(entryAbs).toLowerCase();
160
+ if (ext === '.ts' || ext === '.tsx' || ext === '.mts' || ext === '.cts') {
161
+ return { cmd: process.execPath, args: ['--import', 'tsx'] };
162
+ }
163
+ return { cmd: process.execPath, args: [] };
164
+ }
165
+ function flattenPositionals(positionals) {
166
+ const out = [];
167
+ for (const v of positionals) {
168
+ if (Array.isArray(v)) {
169
+ for (const item of v)
170
+ out.push(String(item));
171
+ }
172
+ else if (v !== undefined && v !== null) {
173
+ out.push(String(v));
174
+ }
175
+ }
176
+ return out;
177
+ }
178
+ /**
179
+ * Forward parsed options to the child as long-flag tokens so a runner that
180
+ * uses its own argv parser (Commander, yargs, etc.) sees the same values
181
+ * the parent did. Booleans become `--flag` (when true; omitted when false);
182
+ * other scalars become `--flag value`; arrays repeat the flag per element.
183
+ */
184
+ function flattenOptions(options) {
185
+ const out = [];
186
+ for (const [key, value] of Object.entries(options)) {
187
+ const flag = `--${toKebab(key)}`;
188
+ if (value === undefined || value === null)
189
+ continue;
190
+ if (value === true) {
191
+ out.push(flag);
192
+ }
193
+ else if (value === false) {
194
+ // omit; absence is the default false
195
+ }
196
+ else if (Array.isArray(value)) {
197
+ for (const item of value) {
198
+ out.push(flag, String(item));
199
+ }
200
+ }
201
+ else {
202
+ out.push(flag, String(value));
203
+ }
204
+ }
205
+ return out;
206
+ }
207
+ function toKebab(s) {
208
+ return s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
209
+ }
210
+ function isProjectCommand(cmd) {
211
+ return cmd[PROJECT_COMMAND_MARK] === true;
212
+ }
213
+ function isCommandHidden(cmd) {
214
+ return cmd._hidden === true;
215
+ }
216
+ //# sourceMappingURL=project-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-commands.js","sourceRoot":"","sources":["../../../src/core/project-commands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Project-defined CLI commands (issue #409).\n *\n * Reads `cli.commands` from the project's frontmcp.config and registers\n * each entry as a top-level Commander verb. The runner is spawned as a\n * child process (tsx for `.ts` entries, node for `.js`/`.mjs`/`.cjs`).\n *\n * Failure to load the config is non-fatal: project commands are an\n * optional layer on top of the built-in CLI, so a missing/broken config\n * just yields zero project commands and never blocks `frontmcp --help`.\n */\n\nimport { spawn } from 'child_process';\nimport * as path from 'path';\n\nimport type { Command } from 'commander';\n\nimport { tryLoadFrontMcpConfig } from '../config/frontmcp-config.loader';\nimport type {\n ProjectCommandArgument,\n ProjectCommandEntry,\n ProjectCommandOption,\n} from '../config/frontmcp-config.types';\nimport { c } from './colors';\n\nconst PROJECT_COMMAND_MARK: unique symbol = Symbol.for('frontmcp.project-command');\n\ninterface ProjectCommandRecord {\n readonly verb: string;\n readonly entry: ProjectCommandEntry;\n readonly cwd: string;\n}\n\n/** Thrown when a dispatched runner exits non-zero or by signal. cli.ts uses\n * the exit code without printing the stack again. */\nexport class ProjectCommandFailedError extends Error {\n constructor(\n readonly verb: string,\n readonly exitCode: number,\n readonly signal: NodeJS.Signals | null,\n ) {\n super(\n signal\n ? `Project command \"${verb}\" terminated by signal ${signal}`\n : `Project command \"${verb}\" exited with code ${exitCode}`,\n );\n this.name = 'ProjectCommandFailedError';\n }\n}\n\n/**\n * Load `cli.commands` from `frontmcp.config` (in `cwd`) and attach each as\n * a top-level command on the program. No-op if no config is found.\n */\nexport async function registerProjectCommands(program: Command, cwd: string = process.cwd()): Promise<void> {\n const cfg = await safeLoad(cwd);\n const commands = cfg?.cli?.commands;\n if (!commands) return;\n\n for (const [verb, entry] of Object.entries(commands)) {\n try {\n const resolved = resolveAndCheckEntry(cwd, entry.entry);\n attachCommand(program, { verb, entry: { ...entry, entry: resolved }, cwd });\n } catch (err) {\n process.stderr.write(c('yellow', `⚠ Skipping project command \"${verb}\": ${(err as Error).message}\\n`));\n }\n }\n}\n\nasync function safeLoad(cwd: string): Promise<Awaited<ReturnType<typeof tryLoadFrontMcpConfig>>> {\n try {\n return await tryLoadFrontMcpConfig(cwd);\n } catch (err) {\n process.stderr.write(\n c('yellow', `⚠ Could not load frontmcp.config for project commands: ${(err as Error).message}\\n`),\n );\n return undefined;\n }\n}\n\n/** Reject entry paths that escape the project cwd. */\nfunction resolveAndCheckEntry(cwd: string, entry: string): string {\n if (path.isAbsolute(entry)) {\n throw new Error(`entry must be a project-relative path, not absolute: \"${entry}\"`);\n }\n const resolved = path.resolve(cwd, entry);\n const rel = path.relative(cwd, resolved);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(`entry \"${entry}\" escapes the project directory`);\n }\n return resolved;\n}\n\nfunction attachCommand(program: Command, record: ProjectCommandRecord): void {\n const { verb, entry } = record;\n const cmd = program.command(verb);\n if (entry.description) cmd.description(entry.description);\n if (entry.hidden) {\n (cmd as unknown as { _hidden: boolean })._hidden = true;\n }\n\n for (const arg of entry.arguments ?? []) {\n cmd.argument(formatArgumentToken(arg), arg.description ?? '');\n }\n\n for (const opt of entry.options ?? []) {\n if (opt.default !== undefined) {\n // Commander v13 types only allow string/boolean/string[] as a default,\n // but accepts arbitrary JS values at runtime. Cast to keep numbers\n // (e.g. `default: 4` for a `<num>` flag) flowing through as-is.\n cmd.option(opt.flags, opt.description ?? '', opt.default as string | boolean | string[]);\n } else {\n cmd.option(opt.flags, opt.description ?? '');\n }\n }\n\n cmd.action(async (...args: unknown[]) => {\n await dispatchToEntry(record, args);\n });\n\n (cmd as unknown as Record<symbol, boolean>)[PROJECT_COMMAND_MARK] = true;\n}\n\nfunction formatArgumentToken(arg: ProjectCommandArgument): string {\n const inner = arg.variadic ? `${arg.name}...` : arg.name;\n return arg.required ? `<${inner}>` : `[${inner}]`;\n}\n\n/**\n * Spawn the project entry file as a child process and forward stdio. The\n * child receives positionals as argv plus a `FRONTMCP_PROJECT_COMMAND` env\n * var with the full payload (verb, positionals, options, cwd) so the runner\n * can reconstruct context without re-parsing argv.\n *\n * Throws `ProjectCommandFailedError` (not a generic Error) on non-zero exit\n * so cli.ts can exit with the child's code without re-printing the message.\n */\nexport async function dispatchToEntry(record: ProjectCommandRecord, actionArgs: unknown[]): Promise<void> {\n // Commander invokes action with: ...positionals, options, command\n const options = (actionArgs[actionArgs.length - 2] ?? {}) as Record<string, unknown>;\n const positionals = actionArgs.slice(0, actionArgs.length - 2);\n\n const entryAbs = record.entry.entry; // already resolved + safety-checked at register time\n const runner = pickRunner(entryAbs);\n\n const payload = {\n verb: record.verb,\n positionals,\n options,\n cwd: record.cwd,\n };\n\n const childArgv = [...runner.args, entryAbs, ...flattenPositionals(positionals), ...flattenOptions(options)];\n\n const child = spawn(runner.cmd, childArgv, {\n cwd: record.cwd,\n stdio: 'inherit',\n env: {\n ...process.env,\n FRONTMCP_PROJECT_COMMAND: JSON.stringify(payload),\n },\n });\n\n await new Promise<void>((resolve, reject) => {\n child.on('error', reject);\n child.on('exit', (code, signal) => {\n if (signal) {\n reject(new ProjectCommandFailedError(record.verb, 1, signal));\n return;\n }\n if (code !== 0) {\n reject(new ProjectCommandFailedError(record.verb, code ?? 1, null));\n return;\n }\n resolve();\n });\n });\n}\n\ninterface Runner {\n cmd: string;\n args: string[];\n}\n\nfunction pickRunner(entryAbs: string): Runner {\n const ext = path.extname(entryAbs).toLowerCase();\n if (ext === '.ts' || ext === '.tsx' || ext === '.mts' || ext === '.cts') {\n return { cmd: process.execPath, args: ['--import', 'tsx'] };\n }\n return { cmd: process.execPath, args: [] };\n}\n\nfunction flattenPositionals(positionals: unknown[]): string[] {\n const out: string[] = [];\n for (const v of positionals) {\n if (Array.isArray(v)) {\n for (const item of v) out.push(String(item));\n } else if (v !== undefined && v !== null) {\n out.push(String(v));\n }\n }\n return out;\n}\n\n/**\n * Forward parsed options to the child as long-flag tokens so a runner that\n * uses its own argv parser (Commander, yargs, etc.) sees the same values\n * the parent did. Booleans become `--flag` (when true; omitted when false);\n * other scalars become `--flag value`; arrays repeat the flag per element.\n */\nfunction flattenOptions(options: Record<string, unknown>): string[] {\n const out: string[] = [];\n for (const [key, value] of Object.entries(options)) {\n const flag = `--${toKebab(key)}`;\n if (value === undefined || value === null) continue;\n if (value === true) {\n out.push(flag);\n } else if (value === false) {\n // omit; absence is the default false\n } else if (Array.isArray(value)) {\n for (const item of value) {\n out.push(flag, String(item));\n }\n } else {\n out.push(flag, String(value));\n }\n }\n return out;\n}\n\nfunction toKebab(s: string): string {\n return s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n}\n\nexport function isProjectCommand(cmd: Command): boolean {\n return (cmd as unknown as Record<symbol, boolean>)[PROJECT_COMMAND_MARK] === true;\n}\n\nexport function isCommandHidden(cmd: Command): boolean {\n return (cmd as unknown as { _hidden?: boolean })._hidden === true;\n}\n\n// Re-export the unused-types so external consumers can build option specs.\nexport type { ProjectCommandEntry, ProjectCommandOption };\n"]}