frontmcp 1.1.2-beta.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/commands/build/adapters/lambda.js +13 -8
- package/src/commands/build/adapters/lambda.js.map +1 -1
- package/src/commands/build/adapters/vercel.js +24 -10
- package/src/commands/build/adapters/vercel.js.map +1 -1
- package/src/commands/build/bundler.js +10 -0
- package/src/commands/build/bundler.js.map +1 -1
- package/src/commands/build/exec/cli-runtime/schema-extractor.js +2 -1
- package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
- package/src/commands/build/exec/config.d.ts +1 -1
- package/src/commands/build/exec/config.js +30 -1
- package/src/commands/build/exec/config.js.map +1 -1
- package/src/commands/build/exec/index.d.ts +1 -0
- package/src/commands/build/exec/index.js +6 -0
- package/src/commands/build/exec/index.js.map +1 -1
- package/src/commands/build/index.js +1 -0
- package/src/commands/build/index.js.map +1 -1
- package/src/commands/skills/export.d.ts +9 -0
- package/src/commands/skills/export.js +116 -0
- package/src/commands/skills/export.js.map +1 -0
- package/src/commands/skills/exporters/copilot.d.ts +3 -0
- package/src/commands/skills/exporters/copilot.js +28 -0
- package/src/commands/skills/exporters/copilot.js.map +1 -0
- package/src/commands/skills/exporters/cursor.d.ts +19 -0
- package/src/commands/skills/exporters/cursor.js +46 -0
- package/src/commands/skills/exporters/cursor.js.map +1 -0
- package/src/commands/skills/exporters/index.d.ts +4 -0
- package/src/commands/skills/exporters/index.js +11 -0
- package/src/commands/skills/exporters/index.js.map +1 -0
- package/src/commands/skills/exporters/sanitize.d.ts +1 -0
- package/src/commands/skills/exporters/sanitize.js +0 -0
- package/src/commands/skills/exporters/sanitize.js.map +1 -0
- package/src/commands/skills/exporters/windsurf.d.ts +7 -0
- package/src/commands/skills/exporters/windsurf.js +31 -0
- package/src/commands/skills/exporters/windsurf.js.map +1 -0
- package/src/commands/skills/publish.d.ts +11 -0
- package/src/commands/skills/publish.js +91 -0
- package/src/commands/skills/publish.js.map +1 -0
- package/src/commands/skills/register.d.ts +1 -1
- package/src/commands/skills/register.js +46 -0
- package/src/commands/skills/register.js.map +1 -1
- package/src/commands/skills/targets/glama.d.ts +18 -0
- package/src/commands/skills/targets/glama.js +28 -0
- package/src/commands/skills/targets/glama.js.map +1 -0
- package/src/commands/skills/targets/index.d.ts +3 -0
- package/src/commands/skills/targets/index.js +11 -0
- package/src/commands/skills/targets/index.js.map +1 -0
- package/src/commands/skills/targets/smithery.d.ts +32 -0
- package/src/commands/skills/targets/smithery.js +29 -0
- package/src/commands/skills/targets/smithery.js.map +1 -0
- package/src/config/frontmcp-config.loader.js +31 -18
- package/src/config/frontmcp-config.loader.js.map +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/exporters/windsurf.ts
|
|
3
|
+
//
|
|
4
|
+
// Convert a catalog skill into a Windsurf `.windsurfrules` section. Windsurf
|
|
5
|
+
// uses a single rules file per project, so callers may want to merge several
|
|
6
|
+
// skill exports into one file. This exporter emits a single labeled section
|
|
7
|
+
// — the caller decides whether to overwrite or append.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.exportToWindsurf = exportToWindsurf;
|
|
10
|
+
/**
|
|
11
|
+
* Build the Windsurf section for one skill. The section is delimited by
|
|
12
|
+
* `## <name>` so multiple skills can coexist in `.windsurfrules`.
|
|
13
|
+
*/
|
|
14
|
+
function exportToWindsurf(skill) {
|
|
15
|
+
const lines = [];
|
|
16
|
+
lines.push(`## ${skill.name}`);
|
|
17
|
+
lines.push('');
|
|
18
|
+
lines.push(skill.description);
|
|
19
|
+
if (skill.tags?.length)
|
|
20
|
+
lines.push(`Tags: ${skill.tags.join(', ')}`);
|
|
21
|
+
if (skill.category)
|
|
22
|
+
lines.push(`Category: ${skill.category}`);
|
|
23
|
+
lines.push('');
|
|
24
|
+
lines.push(skill.instructions.trim());
|
|
25
|
+
lines.push('');
|
|
26
|
+
return {
|
|
27
|
+
relativePath: '.windsurfrules',
|
|
28
|
+
contents: `${lines.join('\n')}\n`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=windsurf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windsurf.js","sourceRoot":"","sources":["../../../../../src/commands/skills/exporters/windsurf.ts"],"names":[],"mappings":";AAAA,2DAA2D;AAC3D,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,uDAAuD;;AAUvD,4CAcC;AAlBD;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,KAA0B;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO;QACL,YAAY,EAAE,gBAAgB;QAC9B,QAAQ,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;KAClC,CAAC;AACJ,CAAC","sourcesContent":["// file: libs/cli/src/commands/skills/exporters/windsurf.ts\n//\n// Convert a catalog skill into a Windsurf `.windsurfrules` section. Windsurf\n// uses a single rules file per project, so callers may want to merge several\n// skill exports into one file. This exporter emits a single labeled section\n// — the caller decides whether to overwrite or append.\n\nimport type { CursorExportInput, ExporterOutput } from './cursor';\n\nexport type WindsurfExportInput = CursorExportInput;\n\n/**\n * Build the Windsurf section for one skill. The section is delimited by\n * `## <name>` so multiple skills can coexist in `.windsurfrules`.\n */\nexport function exportToWindsurf(skill: WindsurfExportInput): ExporterOutput {\n const lines: string[] = [];\n lines.push(`## ${skill.name}`);\n lines.push('');\n lines.push(skill.description);\n if (skill.tags?.length) lines.push(`Tags: ${skill.tags.join(', ')}`);\n if (skill.category) lines.push(`Category: ${skill.category}`);\n lines.push('');\n lines.push(skill.instructions.trim());\n lines.push('');\n return {\n relativePath: '.windsurfrules',\n contents: `${lines.join('\\n')}\\n`,\n };\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type PublishTarget } from './targets';
|
|
2
|
+
export interface PublishSkillsOptions {
|
|
3
|
+
target: PublishTarget;
|
|
4
|
+
/** Skill name; required when not in --dry-run-all mode. */
|
|
5
|
+
name: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
/** Optional repository URL override (for skills authored outside the monorepo). */
|
|
9
|
+
repository?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function publishSkill(options: PublishSkillsOptions): Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/publish.ts
|
|
3
|
+
//
|
|
4
|
+
// `frontmcp skills publish --target smithery|glama [--token X] [--dry-run]`
|
|
5
|
+
//
|
|
6
|
+
// Maps a catalog skill onto each registry's submission shape and POSTs.
|
|
7
|
+
// Production hosts run this from CI with the registry token set as a secret.
|
|
8
|
+
// `--dry-run` prints the URL + payload without sending so authors can inspect.
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.publishSkill = publishSkill;
|
|
11
|
+
const colors_1 = require("../../core/colors");
|
|
12
|
+
const catalog_1 = require("./catalog");
|
|
13
|
+
const targets_1 = require("./targets");
|
|
14
|
+
async function publishSkill(options) {
|
|
15
|
+
const manifest = (0, catalog_1.loadCatalog)();
|
|
16
|
+
const skill = manifest.skills.find((s) => s.name === options.name);
|
|
17
|
+
if (!skill) {
|
|
18
|
+
console.error((0, colors_1.c)('red', `Skill "${options.name}" not found in catalog.`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const publishable = {
|
|
22
|
+
name: skill.name,
|
|
23
|
+
description: skill.description,
|
|
24
|
+
category: skill.category,
|
|
25
|
+
tags: skill.tags,
|
|
26
|
+
repository: options.repository,
|
|
27
|
+
install: { type: 'npm', reference: '@frontmcp/skills', command: `frontmcp skills install ${skill.name}` },
|
|
28
|
+
};
|
|
29
|
+
const { url, payload } = renderTarget(options.target, publishable);
|
|
30
|
+
if (options.dryRun) {
|
|
31
|
+
console.log((0, colors_1.c)('bold', `\n[dry-run] POST ${url}\n`));
|
|
32
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const token = options.token ?? envTokenFor(options.target);
|
|
36
|
+
if (!token) {
|
|
37
|
+
console.error((0, colors_1.c)('red', `Missing API token for ${options.target}. Pass --token or set ${envVarFor(options.target)}.`));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
const timeoutHandle = setTimeout(() => controller.abort(), 30_000);
|
|
42
|
+
let res;
|
|
43
|
+
try {
|
|
44
|
+
res = await fetch(url, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'content-type': 'application/json',
|
|
48
|
+
authorization: `Bearer ${token}`,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(payload),
|
|
51
|
+
signal: controller.signal,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
console.error((0, colors_1.c)('red', `Publish failed: ${message}`));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
clearTimeout(timeoutHandle);
|
|
61
|
+
}
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
const body = await safeText(res);
|
|
64
|
+
console.error((0, colors_1.c)('red', `Publish failed (${res.status} ${res.statusText}): ${body}`));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
console.log((0, colors_1.c)('green', `Published "${skill.name}" to ${options.target}.`));
|
|
68
|
+
}
|
|
69
|
+
function renderTarget(target, skill) {
|
|
70
|
+
switch (target) {
|
|
71
|
+
case 'smithery':
|
|
72
|
+
return { url: targets_1.SMITHERY_ENDPOINT, payload: (0, targets_1.buildSmitheryPayload)(skill) };
|
|
73
|
+
case 'glama':
|
|
74
|
+
return { url: targets_1.GLAMA_ENDPOINT, payload: (0, targets_1.buildGlamaPayload)(skill) };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function envTokenFor(target) {
|
|
78
|
+
return process.env[envVarFor(target)];
|
|
79
|
+
}
|
|
80
|
+
function envVarFor(target) {
|
|
81
|
+
return target === 'smithery' ? 'SMITHERY_TOKEN' : 'GLAMA_TOKEN';
|
|
82
|
+
}
|
|
83
|
+
async function safeText(res) {
|
|
84
|
+
try {
|
|
85
|
+
return await res.text();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return '<no body>';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../../../src/commands/skills/publish.ts"],"names":[],"mappings":"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","sourcesContent":["// file: libs/cli/src/commands/skills/publish.ts\n//\n// `frontmcp skills publish --target smithery|glama [--token X] [--dry-run]`\n//\n// Maps a catalog skill onto each registry's submission shape and POSTs.\n// Production hosts run this from CI with the registry token set as a secret.\n// `--dry-run` prints the URL + payload without sending so authors can inspect.\n\nimport { c } from '../../core/colors';\nimport { loadCatalog } from './catalog';\nimport {\n buildGlamaPayload,\n buildSmitheryPayload,\n GLAMA_ENDPOINT,\n SMITHERY_ENDPOINT,\n type PublishableSkill,\n type PublishTarget,\n} from './targets';\n\nexport interface PublishSkillsOptions {\n target: PublishTarget;\n /** Skill name; required when not in --dry-run-all mode. */\n name: string;\n token?: string;\n dryRun?: boolean;\n /** Optional repository URL override (for skills authored outside the monorepo). */\n repository?: string;\n}\n\nexport async function publishSkill(options: PublishSkillsOptions): Promise<void> {\n const manifest = loadCatalog();\n const skill = manifest.skills.find((s) => s.name === options.name);\n if (!skill) {\n console.error(c('red', `Skill \"${options.name}\" not found in catalog.`));\n process.exit(1);\n }\n\n const publishable: PublishableSkill = {\n name: skill.name,\n description: skill.description,\n category: skill.category,\n tags: skill.tags,\n repository: options.repository,\n install: { type: 'npm', reference: '@frontmcp/skills', command: `frontmcp skills install ${skill.name}` },\n };\n\n const { url, payload } = renderTarget(options.target, publishable);\n\n if (options.dryRun) {\n console.log(c('bold', `\\n[dry-run] POST ${url}\\n`));\n console.log(JSON.stringify(payload, null, 2));\n return;\n }\n\n const token = options.token ?? envTokenFor(options.target);\n if (!token) {\n console.error(\n c('red', `Missing API token for ${options.target}. Pass --token or set ${envVarFor(options.target)}.`),\n );\n process.exit(1);\n }\n\n const controller = new AbortController();\n const timeoutHandle = setTimeout(() => controller.abort(), 30_000);\n let res: Response;\n try {\n res = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(c('red', `Publish failed: ${message}`));\n process.exit(1);\n } finally {\n clearTimeout(timeoutHandle);\n }\n if (!res.ok) {\n const body = await safeText(res);\n console.error(c('red', `Publish failed (${res.status} ${res.statusText}): ${body}`));\n process.exit(1);\n }\n console.log(c('green', `Published \"${skill.name}\" to ${options.target}.`));\n}\n\nfunction renderTarget(target: PublishTarget, skill: PublishableSkill): { url: string; payload: unknown } {\n switch (target) {\n case 'smithery':\n return { url: SMITHERY_ENDPOINT, payload: buildSmitheryPayload(skill) };\n case 'glama':\n return { url: GLAMA_ENDPOINT, payload: buildGlamaPayload(skill) };\n }\n}\n\nfunction envTokenFor(target: PublishTarget): string | undefined {\n return process.env[envVarFor(target)];\n}\n\nfunction envVarFor(target: PublishTarget): string {\n return target === 'smithery' ? 'SMITHERY_TOKEN' : 'GLAMA_TOKEN';\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '<no body>';\n }\n}\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
1
|
+
import { type Command } from 'commander';
|
|
2
2
|
export declare function registerSkillsCommands(program: Command): void;
|
|
@@ -53,6 +53,52 @@ function registerSkillsCommands(program) {
|
|
|
53
53
|
category: options.category,
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
|
+
skills
|
|
57
|
+
.command('export')
|
|
58
|
+
.description('Convert a catalog skill into a Cursor / Windsurf / Copilot rule file in the current directory')
|
|
59
|
+
.option('-t, --target <target>', 'Target IDE: cursor | windsurf | copilot', 'cursor')
|
|
60
|
+
.option('-n, --name <name>', 'Skill name to export (required unless --all is set)')
|
|
61
|
+
.option('-a, --all', 'Export every skill in the catalog')
|
|
62
|
+
.option('-d, --out <directory>', 'Output directory (default: cwd)')
|
|
63
|
+
.action(async (options) => {
|
|
64
|
+
const validTargets = ['cursor', 'windsurf', 'copilot'];
|
|
65
|
+
const t = (options.target ?? 'cursor');
|
|
66
|
+
if (!validTargets.includes(t)) {
|
|
67
|
+
console.error(`Invalid target "${options.target}". Valid targets: ${validTargets.join(', ')}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
const { exportSkills } = await import('./export.js');
|
|
71
|
+
await exportSkills({
|
|
72
|
+
target: t,
|
|
73
|
+
name: options.name,
|
|
74
|
+
all: options.all,
|
|
75
|
+
outDir: options.out,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
skills
|
|
79
|
+
.command('publish')
|
|
80
|
+
.description('Publish a skill to a public marketplace (Smithery or Glama)')
|
|
81
|
+
.argument('<name>', 'Skill name to publish')
|
|
82
|
+
.option('-t, --target <target>', 'Marketplace target: smithery | glama', 'smithery')
|
|
83
|
+
.option('--token <token>', 'API token (defaults to SMITHERY_TOKEN / GLAMA_TOKEN env)')
|
|
84
|
+
.option('--repository <url>', 'Repository URL to advertise on the marketplace')
|
|
85
|
+
.option('--dry-run', 'Print the payload + endpoint without submitting')
|
|
86
|
+
.action(async (name, options) => {
|
|
87
|
+
const validTargets = ['smithery', 'glama'];
|
|
88
|
+
const t = (options.target ?? 'smithery');
|
|
89
|
+
if (!validTargets.includes(t)) {
|
|
90
|
+
console.error(`Invalid target "${options.target}". Valid targets: ${validTargets.join(', ')}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const { publishSkill } = await import('./publish.js');
|
|
94
|
+
await publishSkill({
|
|
95
|
+
target: t,
|
|
96
|
+
name,
|
|
97
|
+
token: options.token,
|
|
98
|
+
repository: options.repository,
|
|
99
|
+
dryRun: options.dryRun,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
56
102
|
skills
|
|
57
103
|
.command('read')
|
|
58
104
|
.description('Read a skill, its references, or any file in the skill directory')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../../src/commands/skills/register.ts"],"names":[],"mappings":"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","sourcesContent":["import { Command } from 'commander';\n\nexport function registerSkillsCommands(program: Command): void {\n const skills = program.command('skills').description('Search, list, and install skills from the FrontMCP catalog');\n\n skills\n .command('search')\n .description('Search the skills catalog using semantic text matching')\n .argument('<query>', 'Search text (matches descriptions, tags, and names)')\n .option('-n, --limit <count>', 'Maximum results to return', '10')\n .option('-t, --tag <tag>', 'Filter by tag')\n .option('-c, --category <category>', 'Filter by category')\n .action(async (query: string, options: { limit?: string; tag?: string; category?: string }) => {\n const { searchSkills } = await import('./search.js');\n await searchSkills(query, {\n limit: Math.max(1, Number(options.limit) || 10),\n tag: options.tag,\n category: options.category,\n });\n });\n\n skills\n .command('list')\n .description('List all available skills in the catalog')\n .option('-c, --category <category>', 'Filter by category')\n .option('-t, --tag <tag>', 'Filter by tag')\n .option('-b, --bundle <bundle>', 'Filter by bundle (recommended, minimal, full)')\n .action(async (options: { category?: string; tag?: string; bundle?: string }) => {\n const { listSkills } = await import('./list.js');\n await listSkills(options);\n });\n\n skills\n .command('install')\n .description('Install skill(s) to a provider directory (.claude/skills or .codex/skills)')\n .argument('[name]', 'Skill name to install (optional with --all, --tag, or --category)')\n .option('-p, --provider <provider>', 'Target provider: claude, codex (default: claude)', 'claude')\n .option('-d, --dir <directory>', 'Custom install directory (overrides provider default)')\n .option('-a, --all', 'Install all skills from the catalog')\n .option('-t, --tag <tag>', 'Install all skills matching a tag')\n .option('-c, --category <category>', 'Install all skills in a category')\n .action(\n async (\n name: string | undefined,\n options: { provider?: string; dir?: string; all?: boolean; tag?: string; category?: string },\n ) => {\n const validProviders = ['claude', 'codex'] as const;\n type Provider = (typeof validProviders)[number];\n const raw = options.provider;\n if (raw && !validProviders.includes(raw as Provider)) {\n console.error(`Invalid provider \"${raw}\". Valid providers: ${validProviders.join(', ')}`);\n process.exit(1);\n }\n const { installSkill } = await import('./install.js');\n await installSkill(name, {\n provider: raw as Provider | undefined,\n dir: options.dir,\n all: options.all,\n tag: options.tag,\n category: options.category,\n });\n },\n );\n\n skills\n .command('read')\n .description('Read a skill, its references, or any file in the skill directory')\n .argument('<nameOrPath>', 'Skill name or skill:filepath (e.g., frontmcp-dev:references/create-tool.md)')\n .argument('[reference]', 'Reference name to read (e.g., create-tool)')\n .option('--refs', 'List all available references for the skill')\n .option('--examples [reference]', 'List examples for the skill, optionally filtered by reference name')\n .action(\n async (name: string, reference: string | undefined, options: { refs?: boolean; examples?: boolean | string }) => {\n const { readSkill } = await import('./read.js');\n await readSkill(name, {\n reference,\n listRefs: options.refs,\n listExamples: options.examples === true ? true : undefined,\n examplesForRef: typeof options.examples === 'string' ? options.examples : undefined,\n });\n },\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../../src/commands/skills/register.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Command } from 'commander';\n\nexport function registerSkillsCommands(program: Command): void {\n const skills = program.command('skills').description('Search, list, and install skills from the FrontMCP catalog');\n\n skills\n .command('search')\n .description('Search the skills catalog using semantic text matching')\n .argument('<query>', 'Search text (matches descriptions, tags, and names)')\n .option('-n, --limit <count>', 'Maximum results to return', '10')\n .option('-t, --tag <tag>', 'Filter by tag')\n .option('-c, --category <category>', 'Filter by category')\n .action(async (query: string, options: { limit?: string; tag?: string; category?: string }) => {\n const { searchSkills } = await import('./search.js');\n await searchSkills(query, {\n limit: Math.max(1, Number(options.limit) || 10),\n tag: options.tag,\n category: options.category,\n });\n });\n\n skills\n .command('list')\n .description('List all available skills in the catalog')\n .option('-c, --category <category>', 'Filter by category')\n .option('-t, --tag <tag>', 'Filter by tag')\n .option('-b, --bundle <bundle>', 'Filter by bundle (recommended, minimal, full)')\n .action(async (options: { category?: string; tag?: string; bundle?: string }) => {\n const { listSkills } = await import('./list.js');\n await listSkills(options);\n });\n\n skills\n .command('install')\n .description('Install skill(s) to a provider directory (.claude/skills or .codex/skills)')\n .argument('[name]', 'Skill name to install (optional with --all, --tag, or --category)')\n .option('-p, --provider <provider>', 'Target provider: claude, codex (default: claude)', 'claude')\n .option('-d, --dir <directory>', 'Custom install directory (overrides provider default)')\n .option('-a, --all', 'Install all skills from the catalog')\n .option('-t, --tag <tag>', 'Install all skills matching a tag')\n .option('-c, --category <category>', 'Install all skills in a category')\n .action(\n async (\n name: string | undefined,\n options: { provider?: string; dir?: string; all?: boolean; tag?: string; category?: string },\n ) => {\n const validProviders = ['claude', 'codex'] as const;\n type Provider = (typeof validProviders)[number];\n const raw = options.provider;\n if (raw && !validProviders.includes(raw as Provider)) {\n console.error(`Invalid provider \"${raw}\". Valid providers: ${validProviders.join(', ')}`);\n process.exit(1);\n }\n const { installSkill } = await import('./install.js');\n await installSkill(name, {\n provider: raw as Provider | undefined,\n dir: options.dir,\n all: options.all,\n tag: options.tag,\n category: options.category,\n });\n },\n );\n\n skills\n .command('export')\n .description('Convert a catalog skill into a Cursor / Windsurf / Copilot rule file in the current directory')\n .option('-t, --target <target>', 'Target IDE: cursor | windsurf | copilot', 'cursor')\n .option('-n, --name <name>', 'Skill name to export (required unless --all is set)')\n .option('-a, --all', 'Export every skill in the catalog')\n .option('-d, --out <directory>', 'Output directory (default: cwd)')\n .action(async (options: { target?: string; name?: string; all?: boolean; out?: string }) => {\n const validTargets = ['cursor', 'windsurf', 'copilot'] as const;\n type Target = (typeof validTargets)[number];\n const t = (options.target ?? 'cursor') as Target;\n if (!validTargets.includes(t)) {\n console.error(`Invalid target \"${options.target}\". Valid targets: ${validTargets.join(', ')}`);\n process.exit(1);\n }\n const { exportSkills } = await import('./export.js');\n await exportSkills({\n target: t,\n name: options.name,\n all: options.all,\n outDir: options.out,\n });\n });\n\n skills\n .command('publish')\n .description('Publish a skill to a public marketplace (Smithery or Glama)')\n .argument('<name>', 'Skill name to publish')\n .option('-t, --target <target>', 'Marketplace target: smithery | glama', 'smithery')\n .option('--token <token>', 'API token (defaults to SMITHERY_TOKEN / GLAMA_TOKEN env)')\n .option('--repository <url>', 'Repository URL to advertise on the marketplace')\n .option('--dry-run', 'Print the payload + endpoint without submitting')\n .action(\n async (name: string, options: { target?: string; token?: string; repository?: string; dryRun?: boolean }) => {\n const validTargets = ['smithery', 'glama'] as const;\n type Target = (typeof validTargets)[number];\n const t = (options.target ?? 'smithery') as Target;\n if (!validTargets.includes(t)) {\n console.error(`Invalid target \"${options.target}\". Valid targets: ${validTargets.join(', ')}`);\n process.exit(1);\n }\n const { publishSkill } = await import('./publish.js');\n await publishSkill({\n target: t,\n name,\n token: options.token,\n repository: options.repository,\n dryRun: options.dryRun,\n });\n },\n );\n\n skills\n .command('read')\n .description('Read a skill, its references, or any file in the skill directory')\n .argument('<nameOrPath>', 'Skill name or skill:filepath (e.g., frontmcp-dev:references/create-tool.md)')\n .argument('[reference]', 'Reference name to read (e.g., create-tool)')\n .option('--refs', 'List all available references for the skill')\n .option('--examples [reference]', 'List examples for the skill, optionally filtered by reference name')\n .action(\n async (name: string, reference: string | undefined, options: { refs?: boolean; examples?: boolean | string }) => {\n const { readSkill } = await import('./read.js');\n await readSkill(name, {\n reference,\n listRefs: options.refs,\n listExamples: options.examples === true ? true : undefined,\n examplesForRef: typeof options.examples === 'string' ? options.examples : undefined,\n });\n },\n );\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PublishableSkill } from './smithery';
|
|
2
|
+
export interface GlamaPayload {
|
|
3
|
+
mcpServer: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
repository?: string;
|
|
7
|
+
license?: string;
|
|
8
|
+
tags: string[];
|
|
9
|
+
rating?: number;
|
|
10
|
+
install: {
|
|
11
|
+
type: string;
|
|
12
|
+
reference?: string;
|
|
13
|
+
command?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function buildGlamaPayload(skill: PublishableSkill): GlamaPayload;
|
|
18
|
+
export declare const GLAMA_ENDPOINT = "https://glama.ai/api/mcp/v1/servers/submit";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/targets/glama.ts
|
|
3
|
+
//
|
|
4
|
+
// Glama marketplace submission payload. Glama's schema differs from Smithery:
|
|
5
|
+
// it groups everything under a `mcpServer` envelope and supports an
|
|
6
|
+
// explicit rating field.
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.GLAMA_ENDPOINT = void 0;
|
|
9
|
+
exports.buildGlamaPayload = buildGlamaPayload;
|
|
10
|
+
function buildGlamaPayload(skill) {
|
|
11
|
+
return {
|
|
12
|
+
mcpServer: {
|
|
13
|
+
name: skill.name,
|
|
14
|
+
description: skill.description,
|
|
15
|
+
...(skill.repository && { repository: skill.repository }),
|
|
16
|
+
...(skill.license && { license: skill.license }),
|
|
17
|
+
tags: skill.tags ?? [],
|
|
18
|
+
...(skill.rating !== undefined && { rating: skill.rating }),
|
|
19
|
+
install: {
|
|
20
|
+
type: skill.install?.type ?? 'npm',
|
|
21
|
+
...(skill.install?.reference && { reference: skill.install.reference }),
|
|
22
|
+
...(skill.install?.command && { command: skill.install.command }),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.GLAMA_ENDPOINT = 'https://glama.ai/api/mcp/v1/servers/submit';
|
|
28
|
+
//# sourceMappingURL=glama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glama.js","sourceRoot":"","sources":["../../../../../src/commands/skills/targets/glama.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,EAAE;AACF,8EAA8E;AAC9E,oEAAoE;AACpE,yBAAyB;;;AAgBzB,8CAgBC;AAhBD,SAAgB,iBAAiB,CAAC,KAAuB;IACvD,OAAO;QACL,SAAS,EAAE;YACT,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;YACzD,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAChD,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;YACtB,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3D,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,IAAI,KAAK;gBAClC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBACvE,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;aAClE;SACF;KACF,CAAC;AACJ,CAAC;AAEY,QAAA,cAAc,GAAG,4CAA4C,CAAC","sourcesContent":["// file: libs/cli/src/commands/skills/targets/glama.ts\n//\n// Glama marketplace submission payload. Glama's schema differs from Smithery:\n// it groups everything under a `mcpServer` envelope and supports an\n// explicit rating field.\n\nimport type { PublishableSkill } from './smithery';\n\nexport interface GlamaPayload {\n mcpServer: {\n name: string;\n description: string;\n repository?: string;\n license?: string;\n tags: string[];\n rating?: number;\n install: { type: string; reference?: string; command?: string };\n };\n}\n\nexport function buildGlamaPayload(skill: PublishableSkill): GlamaPayload {\n return {\n mcpServer: {\n name: skill.name,\n description: skill.description,\n ...(skill.repository && { repository: skill.repository }),\n ...(skill.license && { license: skill.license }),\n tags: skill.tags ?? [],\n ...(skill.rating !== undefined && { rating: skill.rating }),\n install: {\n type: skill.install?.type ?? 'npm',\n ...(skill.install?.reference && { reference: skill.install.reference }),\n ...(skill.install?.command && { command: skill.install.command }),\n },\n },\n };\n}\n\nexport const GLAMA_ENDPOINT = 'https://glama.ai/api/mcp/v1/servers/submit';\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/targets/index.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.GLAMA_ENDPOINT = exports.buildGlamaPayload = exports.SMITHERY_ENDPOINT = exports.buildSmitheryPayload = void 0;
|
|
5
|
+
var smithery_1 = require("./smithery");
|
|
6
|
+
Object.defineProperty(exports, "buildSmitheryPayload", { enumerable: true, get: function () { return smithery_1.buildSmitheryPayload; } });
|
|
7
|
+
Object.defineProperty(exports, "SMITHERY_ENDPOINT", { enumerable: true, get: function () { return smithery_1.SMITHERY_ENDPOINT; } });
|
|
8
|
+
var glama_1 = require("./glama");
|
|
9
|
+
Object.defineProperty(exports, "buildGlamaPayload", { enumerable: true, get: function () { return glama_1.buildGlamaPayload; } });
|
|
10
|
+
Object.defineProperty(exports, "GLAMA_ENDPOINT", { enumerable: true, get: function () { return glama_1.GLAMA_ENDPOINT; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/skills/targets/index.ts"],"names":[],"mappings":";AAAA,sDAAsD;;;AAEtD,uCAAkH;AAAzG,gHAAA,oBAAoB,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAChD,iCAA+E;AAAtE,0GAAA,iBAAiB,OAAA;AAAE,uGAAA,cAAc,OAAA","sourcesContent":["// file: libs/cli/src/commands/skills/targets/index.ts\n\nexport { buildSmitheryPayload, SMITHERY_ENDPOINT, type PublishableSkill, type SmitheryPayload } from './smithery';\nexport { buildGlamaPayload, GLAMA_ENDPOINT, type GlamaPayload } from './glama';\n\nexport type PublishTarget = 'smithery' | 'glama';\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface PublishableSkill {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
category?: string;
|
|
5
|
+
tags?: string[];
|
|
6
|
+
rating?: number;
|
|
7
|
+
license?: string;
|
|
8
|
+
repository?: string;
|
|
9
|
+
install?: {
|
|
10
|
+
type: 'npm' | 'git';
|
|
11
|
+
reference: string;
|
|
12
|
+
command?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface SmitheryPayload {
|
|
16
|
+
qualifiedName: string;
|
|
17
|
+
displayName: string;
|
|
18
|
+
description: string;
|
|
19
|
+
homepage?: string;
|
|
20
|
+
license?: string;
|
|
21
|
+
install: {
|
|
22
|
+
type: string;
|
|
23
|
+
command?: string;
|
|
24
|
+
reference?: string;
|
|
25
|
+
};
|
|
26
|
+
tags: string[];
|
|
27
|
+
category: string;
|
|
28
|
+
}
|
|
29
|
+
/** Build the Smithery JSON payload for one skill. */
|
|
30
|
+
export declare function buildSmitheryPayload(skill: PublishableSkill): SmitheryPayload;
|
|
31
|
+
/** Smithery submission endpoint. */
|
|
32
|
+
export declare const SMITHERY_ENDPOINT = "https://smithery.ai/api/v1/registry/submit";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/targets/smithery.ts
|
|
3
|
+
//
|
|
4
|
+
// Map a catalog skill onto Smithery's marketplace submission shape. Pure
|
|
5
|
+
// function so tests can pin the payload structure. Network submission is
|
|
6
|
+
// the responsibility of the publish command itself.
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.SMITHERY_ENDPOINT = void 0;
|
|
9
|
+
exports.buildSmitheryPayload = buildSmitheryPayload;
|
|
10
|
+
/** Build the Smithery JSON payload for one skill. */
|
|
11
|
+
function buildSmitheryPayload(skill) {
|
|
12
|
+
return {
|
|
13
|
+
qualifiedName: `frontmcp/${skill.name}`,
|
|
14
|
+
displayName: skill.name,
|
|
15
|
+
description: skill.description,
|
|
16
|
+
...(skill.repository && { homepage: skill.repository }),
|
|
17
|
+
...(skill.license && { license: skill.license }),
|
|
18
|
+
install: {
|
|
19
|
+
type: skill.install?.type ?? 'npm',
|
|
20
|
+
...(skill.install?.command && { command: skill.install.command }),
|
|
21
|
+
...(skill.install?.reference && { reference: skill.install.reference }),
|
|
22
|
+
},
|
|
23
|
+
tags: skill.tags ?? [],
|
|
24
|
+
category: skill.category ?? 'general',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Smithery submission endpoint. */
|
|
28
|
+
exports.SMITHERY_ENDPOINT = 'https://smithery.ai/api/v1/registry/submit';
|
|
29
|
+
//# sourceMappingURL=smithery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smithery.js","sourceRoot":"","sources":["../../../../../src/commands/skills/targets/smithery.ts"],"names":[],"mappings":";AAAA,yDAAyD;AACzD,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,oDAAoD;;;AAyBpD,oDAeC;AAhBD,qDAAqD;AACrD,SAAgB,oBAAoB,CAAC,KAAuB;IAC1D,OAAO;QACL,aAAa,EAAE,YAAY,KAAK,CAAC,IAAI,EAAE;QACvC,WAAW,EAAE,KAAK,CAAC,IAAI;QACvB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;QACvD,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAChD,OAAO,EAAE;YACP,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,IAAI,KAAK;YAClC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACjE,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;SACxE;QACD,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,SAAS;KACtC,CAAC;AACJ,CAAC;AAED,oCAAoC;AACvB,QAAA,iBAAiB,GAAG,4CAA4C,CAAC","sourcesContent":["// file: libs/cli/src/commands/skills/targets/smithery.ts\n//\n// Map a catalog skill onto Smithery's marketplace submission shape. Pure\n// function so tests can pin the payload structure. Network submission is\n// the responsibility of the publish command itself.\n\nexport interface PublishableSkill {\n name: string;\n description: string;\n category?: string;\n tags?: string[];\n rating?: number;\n license?: string;\n repository?: string;\n install?: { type: 'npm' | 'git'; reference: string; command?: string };\n}\n\nexport interface SmitheryPayload {\n qualifiedName: string;\n displayName: string;\n description: string;\n homepage?: string;\n license?: string;\n install: { type: string; command?: string; reference?: string };\n tags: string[];\n category: string;\n}\n\n/** Build the Smithery JSON payload for one skill. */\nexport function buildSmitheryPayload(skill: PublishableSkill): SmitheryPayload {\n return {\n qualifiedName: `frontmcp/${skill.name}`,\n displayName: skill.name,\n description: skill.description,\n ...(skill.repository && { homepage: skill.repository }),\n ...(skill.license && { license: skill.license }),\n install: {\n type: skill.install?.type ?? 'npm',\n ...(skill.install?.command && { command: skill.install.command }),\n ...(skill.install?.reference && { reference: skill.install.reference }),\n },\n tags: skill.tags ?? [],\n category: skill.category ?? 'general',\n };\n}\n\n/** Smithery submission endpoint. */\nexport const SMITHERY_ENDPOINT = 'https://smithery.ai/api/v1/registry/submit';\n"]}
|
|
@@ -102,17 +102,32 @@ async function loadRawConfig(cwd) {
|
|
|
102
102
|
return JSON.parse(content);
|
|
103
103
|
}
|
|
104
104
|
if (filename.endsWith('.ts')) {
|
|
105
|
-
// #365 —
|
|
106
|
-
//
|
|
107
|
-
// Node
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
105
|
+
// #365 — Loading `.ts` under `"type": "commonjs"` (the default) is a
|
|
106
|
+
// minefield across Node versions:
|
|
107
|
+
// - Node 20: `require()` throws on TS syntax, `await import()` errors
|
|
108
|
+
// with "Make sure to set type: module".
|
|
109
|
+
// - Node 22+: `require(esm)` may succeed but return partial data, OR
|
|
110
|
+
// emit a warning on `await import()` even when the load succeeds.
|
|
111
|
+
// - Node 24: type-stripping may swallow `import { x } from ...`
|
|
112
|
+
// statements, returning `{}` instead of the user's exports — the
|
|
113
|
+
// 1.1.2-beta.1 silent-defaults regression.
|
|
114
|
+
// Round 3: under CJS, ALWAYS transpile via esbuild. It's the only path
|
|
115
|
+
// that produces a deterministic, fully-typed result. ESM projects can
|
|
116
|
+
// still use Node's runtime loaders since they're well-behaved there.
|
|
115
117
|
const isCjsProject = await isCommonJsProject(cwd);
|
|
118
|
+
if (isCjsProject) {
|
|
119
|
+
try {
|
|
120
|
+
return await loadTsConfigViaEsbuild(configPath);
|
|
121
|
+
}
|
|
122
|
+
catch (esbuildErr) {
|
|
123
|
+
throw new Error(`Failed to load ${filename} via esbuild.\n` +
|
|
124
|
+
` ${esbuildErr.message}\n` +
|
|
125
|
+
`Hint: ensure the file exports a default config (e.g., ` +
|
|
126
|
+
`\`export default defineConfig({...})\`) and that all imports resolve.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// ESM project ("type": "module"): try Node's loaders first (faster,
|
|
130
|
+
// no transpile cost), fall back to esbuild on failure.
|
|
116
131
|
let requireErr;
|
|
117
132
|
try {
|
|
118
133
|
const mod = require(configPath);
|
|
@@ -121,14 +136,12 @@ async function loadRawConfig(cwd) {
|
|
|
121
136
|
catch (e) {
|
|
122
137
|
requireErr = e;
|
|
123
138
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// Fall through to esbuild.
|
|
131
|
-
}
|
|
139
|
+
try {
|
|
140
|
+
const mod = await import(configPath);
|
|
141
|
+
return mod.default ?? mod;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Fall through to esbuild.
|
|
132
145
|
}
|
|
133
146
|
try {
|
|
134
147
|
return await loadTsConfigViaEsbuild(configPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmcp-config.loader.js","sourceRoot":"","sources":["../../../src/config/frontmcp-config.loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FrontMCP Config Loader\n *\n * Loads `frontmcp.config.(json|js|ts|mjs|cjs)` from a directory.\n * Falls back to deriving minimal config from package.json.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nimport { readFile } from '@frontmcp/utils';\n\nimport { frontmcpConfigSchema, type FrontMcpConfigParsed } from './frontmcp-config.schema';\nimport type { DeploymentTarget, FrontMcpConfig } from './frontmcp-config.types';\n\nconst CONFIG_FILENAMES = [\n 'frontmcp.config.ts',\n 'frontmcp.config.js',\n 'frontmcp.config.json',\n 'frontmcp.config.mjs',\n 'frontmcp.config.cjs',\n];\n\n/**\n * Load and validate a frontmcp.config file from the given directory.\n *\n * Resolution order:\n * 1. frontmcp.config.ts\n * 2. frontmcp.config.js\n * 3. frontmcp.config.json\n * 4. frontmcp.config.mjs\n * 5. frontmcp.config.cjs\n * 6. Derive from package.json (minimal config with 'node' target)\n */\nexport async function loadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed> {\n const raw = await loadRawConfig(cwd);\n return validateConfig(raw);\n}\n\n/**\n * Variant that load-errors propagate (parse failures in `frontmcp.config.ts`,\n * missing dependencies, etc.) but schema-validation errors return `undefined`.\n *\n * Used by `runBuild` to support both shapes: the new top-level\n * `frontmcpConfigSchema` (with `deployments`) and the older exec-only shape\n * (top-level `cli`, `sea`, `esbuild`) that `loadExecConfig` consumes\n * directly. A user with the old shape should still get a successful build\n * — the exec-loader picks the file up from disk by itself.\n *\n * Returns `undefined` when:\n * - no config file is present (caller falls back to CLI flags), or\n * - the file loads but doesn't match the new schema (legacy shape).\n *\n * Throws when:\n * - the file exists but can't be parsed (TS syntax error, ESM/CJS mismatch\n * that even esbuild can't recover from, etc.) — i.e., #365 silent-default\n * regressions.\n */\nexport async function tryLoadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed | undefined> {\n let raw: unknown;\n try {\n raw = await loadRawConfig(cwd);\n } catch (err) {\n // Distinguish \"no config and no package.json\" from real load failures.\n // `deriveFromPackageJson` throws this exact message — treat it as \"no config\".\n if ((err as Error).message?.startsWith('No frontmcp.config found')) {\n return undefined;\n }\n throw err;\n }\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n // Distinguish two failure shapes:\n // 1. Legacy exec-only config — top-level `cli` / `sea` / `esbuild`,\n // no `deployments` key. Return undefined so `loadExecConfig` picks\n // it up directly. Pre-v1.1 fixtures live in this branch.\n // 2. Anything else — looks like the user attempted a v1.1 config and\n // got it wrong (typo'd `deployments`, invalid `target`, etc.).\n // Throw so we don't silently fall back to defaults — that was the\n // silent-corruption mode #365 was trying to eliminate.\n const obj = typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : undefined;\n const isLegacyExecOnlyShape =\n !!obj && !('deployments' in obj) && ('cli' in obj || 'sea' in obj || 'esbuild' in obj);\n if (isLegacyExecOnlyShape) return undefined;\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Load raw config without validation.\n */\nasync function loadRawConfig(cwd: string): Promise<unknown> {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = path.join(cwd, filename);\n if (!fs.existsSync(configPath)) continue;\n\n if (filename.endsWith('.json')) {\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content);\n }\n\n if (filename.endsWith('.ts')) {\n // #365 — When the project is `\"type\": \"commonjs\"` (the default),\n // `require()` can't load .ts without a hook and `await import()` emits\n // Node's `Failed to load the ES module … Make sure to set \"type\": \"module\"`\n // warning to stderr regardless of whether type-stripping eventually\n // succeeds — that warning by itself looked to retesters like a hard\n // load failure even when the build proceeded. Round 2: when the host\n // project is CJS, skip Node's `await import()` entirely and go\n // straight to esbuild. For ESM projects we still try the runtime\n // paths first (faster, no transpile cost). Either way, a failure on\n // every path throws the combined error — never silent defaults.\n const isCjsProject = await isCommonJsProject(cwd);\n let requireErr: Error | undefined;\n try {\n const mod = require(configPath);\n return mod.default ?? mod;\n } catch (e) {\n requireErr = e as Error;\n }\n if (!isCjsProject) {\n try {\n const mod = await import(configPath);\n return mod.default ?? mod;\n } catch {\n // Fall through to esbuild.\n }\n }\n try {\n return await loadTsConfigViaEsbuild(configPath);\n } catch (esbuildErr) {\n throw new Error(\n `Failed to load ${filename}.\\n` +\n ` require() error: ${requireErr?.message ?? '(skipped)'}\\n` +\n ` esbuild error: ${(esbuildErr as Error).message}\\n` +\n `Hint: ensure the file exports a default config (e.g., ` +\n `\\`export default defineConfig({...})\\`) and that all imports resolve.`,\n );\n }\n }\n\n // JS/MJS/CJS\n if (filename.endsWith('.mjs')) {\n const mod = await import(configPath);\n return mod.default ?? mod;\n }\n\n const mod = require(configPath);\n return mod.default ?? mod;\n }\n\n // Fallback: derive from package.json\n return deriveFromPackageJson(cwd);\n}\n\n/**\n * Read the host project's `package.json.type` to decide whether `await import()`\n * of a `.ts` file is worth attempting. Returns true when the project is\n * declared `\"type\": \"commonjs\"` or omits the field entirely (Node's default).\n *\n * Read errors (no package.json, malformed JSON) are treated as \"CJS\" — that's\n * the safer default for the loader because it routes us through esbuild\n * transpilation rather than relying on Node's experimental TS handling.\n *\n * Routed through `@frontmcp/utils` per repo convention so this module\n * doesn't reach into `node:fs` for an ad-hoc package.json read.\n */\nasync function isCommonJsProject(cwd: string): Promise<boolean> {\n try {\n const pkgPath = path.join(cwd, 'package.json');\n const contents = await readFile(pkgPath);\n const pkg = JSON.parse(contents) as { type?: string };\n return pkg.type !== 'module';\n } catch {\n return true;\n }\n}\n\n/**\n * Transpile a TypeScript config file with esbuild (CJS target) and eval the\n * result via Module-via-vm. Used as a last-resort path when neither `require()`\n * (no ts-node hook) nor `await import()` (project is `\"type\": \"commonjs\"`)\n * can load the file directly.\n *\n * Uses `esbuild.build({ bundle: true, packages: 'external' })` rather than\n * `transformSync` so a config that imports a sibling helper TS file\n * (`import { foo } from './helpers'`) gets the helper inlined into the\n * compiled output. Without bundling, the resulting CJS would emit\n * `require('./helpers')` which Node can't resolve under `\"type\": \"commonjs\"`.\n *\n * `packages: 'external'` keeps node_modules dependencies as runtime\n * `require()` calls so `import { defineConfig } from 'frontmcp'` still\n * resolves against the project's installed copy of the SDK.\n */\nasync function loadTsConfigViaEsbuild(configPath: string): Promise<unknown> {\n const esbuild = require('esbuild') as typeof import('esbuild');\n const built = await esbuild.build({\n entryPoints: [configPath],\n bundle: true,\n write: false,\n platform: 'node',\n format: 'cjs',\n target: 'es2022',\n packages: 'external',\n sourcemap: 'inline',\n logLevel: 'silent',\n });\n if (!built.outputFiles || built.outputFiles.length === 0) {\n throw new Error('esbuild produced no output for ' + configPath);\n }\n const code = built.outputFiles[0].text;\n\n const Module = require('module') as typeof import('module');\n const m = new Module(configPath, module);\n // Make the loaded module's `require` resolve relative to the config dir\n // so user `import { defineConfig } from 'frontmcp'` keeps working.\n m.filename = configPath;\n m.paths = (Module as unknown as { _nodeModulePaths(p: string): string[] })._nodeModulePaths(path.dirname(configPath));\n\n (m as any)._compile(code, configPath);\n\n const exported = (m as any).exports as { default?: unknown };\n return exported?.default ?? exported;\n}\n\n/**\n * Derive minimal config from package.json.\n */\nfunction deriveFromPackageJson(cwd: string): FrontMcpConfig {\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n throw new Error(\n 'No frontmcp.config found and no package.json. Create a frontmcp.config.ts to configure build targets.',\n );\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n return {\n name: pkg.name?.replace(/^@[^/]+\\//, '') || path.basename(cwd),\n version: pkg.version || '1.0.0',\n entry: pkg.main,\n deployments: [{ target: 'node' }],\n };\n}\n\n/**\n * Validate raw config against the Zod schema.\n */\nexport function validateConfig(raw: unknown): FrontMcpConfigParsed {\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Find a deployment target by type.\n */\nexport function findDeployment(config: FrontMcpConfigParsed, target: string): DeploymentTarget | undefined {\n return config.deployments.find((d) => d.target === target);\n}\n\n/**\n * Get all deployment target types from the config.\n */\nexport function getDeploymentTargets(config: FrontMcpConfigParsed): string[] {\n return config.deployments.map((d) => d.target);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"frontmcp-config.loader.js","sourceRoot":"","sources":["../../../src/config/frontmcp-config.loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FrontMCP Config Loader\n *\n * Loads `frontmcp.config.(json|js|ts|mjs|cjs)` from a directory.\n * Falls back to deriving minimal config from package.json.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nimport { readFile } from '@frontmcp/utils';\n\nimport { frontmcpConfigSchema, type FrontMcpConfigParsed } from './frontmcp-config.schema';\nimport type { DeploymentTarget, FrontMcpConfig } from './frontmcp-config.types';\n\nconst CONFIG_FILENAMES = [\n 'frontmcp.config.ts',\n 'frontmcp.config.js',\n 'frontmcp.config.json',\n 'frontmcp.config.mjs',\n 'frontmcp.config.cjs',\n];\n\n/**\n * Load and validate a frontmcp.config file from the given directory.\n *\n * Resolution order:\n * 1. frontmcp.config.ts\n * 2. frontmcp.config.js\n * 3. frontmcp.config.json\n * 4. frontmcp.config.mjs\n * 5. frontmcp.config.cjs\n * 6. Derive from package.json (minimal config with 'node' target)\n */\nexport async function loadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed> {\n const raw = await loadRawConfig(cwd);\n return validateConfig(raw);\n}\n\n/**\n * Variant that load-errors propagate (parse failures in `frontmcp.config.ts`,\n * missing dependencies, etc.) but schema-validation errors return `undefined`.\n *\n * Used by `runBuild` to support both shapes: the new top-level\n * `frontmcpConfigSchema` (with `deployments`) and the older exec-only shape\n * (top-level `cli`, `sea`, `esbuild`) that `loadExecConfig` consumes\n * directly. A user with the old shape should still get a successful build\n * — the exec-loader picks the file up from disk by itself.\n *\n * Returns `undefined` when:\n * - no config file is present (caller falls back to CLI flags), or\n * - the file loads but doesn't match the new schema (legacy shape).\n *\n * Throws when:\n * - the file exists but can't be parsed (TS syntax error, ESM/CJS mismatch\n * that even esbuild can't recover from, etc.) — i.e., #365 silent-default\n * regressions.\n */\nexport async function tryLoadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed | undefined> {\n let raw: unknown;\n try {\n raw = await loadRawConfig(cwd);\n } catch (err) {\n // Distinguish \"no config and no package.json\" from real load failures.\n // `deriveFromPackageJson` throws this exact message — treat it as \"no config\".\n if ((err as Error).message?.startsWith('No frontmcp.config found')) {\n return undefined;\n }\n throw err;\n }\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n // Distinguish two failure shapes:\n // 1. Legacy exec-only config — top-level `cli` / `sea` / `esbuild`,\n // no `deployments` key. Return undefined so `loadExecConfig` picks\n // it up directly. Pre-v1.1 fixtures live in this branch.\n // 2. Anything else — looks like the user attempted a v1.1 config and\n // got it wrong (typo'd `deployments`, invalid `target`, etc.).\n // Throw so we don't silently fall back to defaults — that was the\n // silent-corruption mode #365 was trying to eliminate.\n const obj = typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : undefined;\n const isLegacyExecOnlyShape =\n !!obj && !('deployments' in obj) && ('cli' in obj || 'sea' in obj || 'esbuild' in obj);\n if (isLegacyExecOnlyShape) return undefined;\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Load raw config without validation.\n */\nasync function loadRawConfig(cwd: string): Promise<unknown> {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = path.join(cwd, filename);\n if (!fs.existsSync(configPath)) continue;\n\n if (filename.endsWith('.json')) {\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content);\n }\n\n if (filename.endsWith('.ts')) {\n // #365 — Loading `.ts` under `\"type\": \"commonjs\"` (the default) is a\n // minefield across Node versions:\n // - Node 20: `require()` throws on TS syntax, `await import()` errors\n // with \"Make sure to set type: module\".\n // - Node 22+: `require(esm)` may succeed but return partial data, OR\n // emit a warning on `await import()` even when the load succeeds.\n // - Node 24: type-stripping may swallow `import { x } from ...`\n // statements, returning `{}` instead of the user's exports — the\n // 1.1.2-beta.1 silent-defaults regression.\n // Round 3: under CJS, ALWAYS transpile via esbuild. It's the only path\n // that produces a deterministic, fully-typed result. ESM projects can\n // still use Node's runtime loaders since they're well-behaved there.\n const isCjsProject = await isCommonJsProject(cwd);\n if (isCjsProject) {\n try {\n return await loadTsConfigViaEsbuild(configPath);\n } catch (esbuildErr) {\n throw new Error(\n `Failed to load ${filename} via esbuild.\\n` +\n ` ${(esbuildErr as Error).message}\\n` +\n `Hint: ensure the file exports a default config (e.g., ` +\n `\\`export default defineConfig({...})\\`) and that all imports resolve.`,\n );\n }\n }\n // ESM project (\"type\": \"module\"): try Node's loaders first (faster,\n // no transpile cost), fall back to esbuild on failure.\n let requireErr: Error | undefined;\n try {\n const mod = require(configPath);\n return mod.default ?? mod;\n } catch (e) {\n requireErr = e as Error;\n }\n try {\n const mod = await import(configPath);\n return mod.default ?? mod;\n } catch {\n // Fall through to esbuild.\n }\n try {\n return await loadTsConfigViaEsbuild(configPath);\n } catch (esbuildErr) {\n throw new Error(\n `Failed to load ${filename}.\\n` +\n ` require() error: ${requireErr?.message ?? '(skipped)'}\\n` +\n ` esbuild error: ${(esbuildErr as Error).message}\\n` +\n `Hint: ensure the file exports a default config (e.g., ` +\n `\\`export default defineConfig({...})\\`) and that all imports resolve.`,\n );\n }\n }\n\n // JS/MJS/CJS\n if (filename.endsWith('.mjs')) {\n const mod = await import(configPath);\n return mod.default ?? mod;\n }\n\n const mod = require(configPath);\n return mod.default ?? mod;\n }\n\n // Fallback: derive from package.json\n return deriveFromPackageJson(cwd);\n}\n\n/**\n * Read the host project's `package.json.type` to decide whether `await import()`\n * of a `.ts` file is worth attempting. Returns true when the project is\n * declared `\"type\": \"commonjs\"` or omits the field entirely (Node's default).\n *\n * Read errors (no package.json, malformed JSON) are treated as \"CJS\" — that's\n * the safer default for the loader because it routes us through esbuild\n * transpilation rather than relying on Node's experimental TS handling.\n *\n * Routed through `@frontmcp/utils` per repo convention so this module\n * doesn't reach into `node:fs` for an ad-hoc package.json read.\n */\nasync function isCommonJsProject(cwd: string): Promise<boolean> {\n try {\n const pkgPath = path.join(cwd, 'package.json');\n const contents = await readFile(pkgPath);\n const pkg = JSON.parse(contents) as { type?: string };\n return pkg.type !== 'module';\n } catch {\n return true;\n }\n}\n\n/**\n * Transpile a TypeScript config file with esbuild (CJS target) and eval the\n * result via Module-via-vm. Used as a last-resort path when neither `require()`\n * (no ts-node hook) nor `await import()` (project is `\"type\": \"commonjs\"`)\n * can load the file directly.\n *\n * Uses `esbuild.build({ bundle: true, packages: 'external' })` rather than\n * `transformSync` so a config that imports a sibling helper TS file\n * (`import { foo } from './helpers'`) gets the helper inlined into the\n * compiled output. Without bundling, the resulting CJS would emit\n * `require('./helpers')` which Node can't resolve under `\"type\": \"commonjs\"`.\n *\n * `packages: 'external'` keeps node_modules dependencies as runtime\n * `require()` calls so `import { defineConfig } from 'frontmcp'` still\n * resolves against the project's installed copy of the SDK.\n */\nasync function loadTsConfigViaEsbuild(configPath: string): Promise<unknown> {\n const esbuild = require('esbuild') as typeof import('esbuild');\n const built = await esbuild.build({\n entryPoints: [configPath],\n bundle: true,\n write: false,\n platform: 'node',\n format: 'cjs',\n target: 'es2022',\n packages: 'external',\n sourcemap: 'inline',\n logLevel: 'silent',\n });\n if (!built.outputFiles || built.outputFiles.length === 0) {\n throw new Error('esbuild produced no output for ' + configPath);\n }\n const code = built.outputFiles[0].text;\n\n const Module = require('module') as typeof import('module');\n const m = new Module(configPath, module);\n // Make the loaded module's `require` resolve relative to the config dir\n // so user `import { defineConfig } from 'frontmcp'` keeps working.\n m.filename = configPath;\n m.paths = (Module as unknown as { _nodeModulePaths(p: string): string[] })._nodeModulePaths(path.dirname(configPath));\n\n (m as any)._compile(code, configPath);\n\n const exported = (m as any).exports as { default?: unknown };\n return exported?.default ?? exported;\n}\n\n/**\n * Derive minimal config from package.json.\n */\nfunction deriveFromPackageJson(cwd: string): FrontMcpConfig {\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n throw new Error(\n 'No frontmcp.config found and no package.json. Create a frontmcp.config.ts to configure build targets.',\n );\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n return {\n name: pkg.name?.replace(/^@[^/]+\\//, '') || path.basename(cwd),\n version: pkg.version || '1.0.0',\n entry: pkg.main,\n deployments: [{ target: 'node' }],\n };\n}\n\n/**\n * Validate raw config against the Zod schema.\n */\nexport function validateConfig(raw: unknown): FrontMcpConfigParsed {\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Find a deployment target by type.\n */\nexport function findDeployment(config: FrontMcpConfigParsed, target: string): DeploymentTarget | undefined {\n return config.deployments.find((d) => d.target === target);\n}\n\n/**\n * Get all deployment target types from the config.\n */\nexport function getDeploymentTargets(config: FrontMcpConfigParsed): string[] {\n return config.deployments.map((d) => d.target);\n}\n"]}
|