frontmcp 1.1.2 → 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/exec/cli-runtime/schema-extractor.js +2 -1
- package/src/commands/build/exec/cli-runtime/schema-extractor.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontmcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "FrontMCP command line interface",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@clack/prompts": "^0.10.0",
|
|
34
|
-
"@frontmcp/lazy-zod": "1.
|
|
35
|
-
"@frontmcp/utils": "1.
|
|
36
|
-
"@frontmcp/skills": "1.
|
|
34
|
+
"@frontmcp/lazy-zod": "1.2.0",
|
|
35
|
+
"@frontmcp/utils": "1.2.0",
|
|
36
|
+
"@frontmcp/skills": "1.2.0",
|
|
37
37
|
"commander": "^13.0.0",
|
|
38
38
|
"tslib": "^2.3.0",
|
|
39
39
|
"vectoriadb": "^2.2.0",
|
|
@@ -97,7 +97,8 @@ async function extractSchemas(bundlePath) {
|
|
|
97
97
|
arguments: p.arguments,
|
|
98
98
|
}));
|
|
99
99
|
const toolNameSet = new Set(tools.map((t) => t.name));
|
|
100
|
-
|
|
100
|
+
// SEP-2640: skills are exposed under the singular `skill://` URI scheme.
|
|
101
|
+
const hasSkillsResources = resourceTemplates.some((rt) => rt.uriTemplate.startsWith('skill://'));
|
|
101
102
|
const capabilities = {
|
|
102
103
|
skills: hasSkillsResources,
|
|
103
104
|
jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-extractor.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/schema-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build-time schema extraction.\n * After the server bundle is produced, boots a DirectClient via connect(),\n * extracts tool/resource/prompt schemas, and serializes them for CLI code generation.\n */\n\nexport interface ExtractedTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ExtractedResource {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n}\n\nexport interface ExtractedResourceTemplate {\n uriTemplate: string;\n name: string;\n description?: string;\n}\n\nexport interface ExtractedPrompt {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\nexport interface ExtractedJob {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n tags?: string[];\n}\n\nexport interface ExtractedCapabilities {\n skills: boolean;\n jobs: boolean;\n workflows: boolean;\n}\n\nexport interface ExtractedSkillAsset {\n skillName: string;\n baseDir?: string;\n instructionFile?: string;\n resourceDirs?: {\n references?: string;\n examples?: string;\n scripts?: string;\n assets?: string;\n };\n}\n\nexport interface ExtractedSchema {\n tools: ExtractedTool[];\n resources: ExtractedResource[];\n resourceTemplates: ExtractedResourceTemplate[];\n prompts: ExtractedPrompt[];\n jobs: ExtractedJob[];\n capabilities: ExtractedCapabilities;\n skillAssets: ExtractedSkillAsset[];\n /**\n * Server bind config extracted from `@FrontMcp({ http })` (or its\n * decorator-attached `__frontmcp:config` metadata). Used by the manifest\n * writer so the published manifest reflects the port the server will\n * actually listen on, not a hard-coded SDK default.\n */\n httpPort?: number;\n}\n\n/** Known system tool names injected by SDK features (jobs, workflows). */\nexport const SYSTEM_TOOL_NAMES = new Set([\n 'list-jobs',\n 'execute-job',\n 'get-job-status',\n 'register-job',\n 'remove-job',\n 'list-workflows',\n 'execute-workflow',\n 'get-workflow-status',\n 'register-workflow',\n 'remove-workflow',\n]);\n\n/**\n * Extract schemas from a compiled server bundle.\n * Requires the bundle to export a FrontMcp-decorated class as default export\n * or a config object usable by connect().\n */\nexport async function extractSchemas(bundlePath: string): Promise<ExtractedSchema> {\n // Suppress @FrontMcp() decorator bootstrap — we only need metadata, not a running server\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n\n let mod: Record<string, unknown>;\n try {\n mod = require(bundlePath);\n } finally {\n if (prev === undefined) {\n delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n } else {\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n }\n\n const configOrClass = mod.default || mod;\n\n // Use @frontmcp/sdk connect() to boot in-memory client\n let connect: (config: unknown, options?: { mode?: string }) => Promise<unknown>;\n try {\n const sdk = require('@frontmcp/sdk');\n connect = sdk.connect || sdk.direct?.connect;\n if (!connect) {\n throw new Error('connect() not found in @frontmcp/sdk');\n }\n } catch {\n throw new Error(\n '@frontmcp/sdk is required for CLI schema extraction. Ensure it is installed.',\n );\n }\n\n const client = await connect(configOrClass, { mode: 'cli' }) as {\n listTools(): Promise<unknown>;\n listResources(): Promise<{ resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }> }>;\n listResourceTemplates?(): Promise<{ resourceTemplates: Array<{ uriTemplate: string; name?: string; description?: string }> }>;\n listPrompts(): Promise<{ prompts: Array<{ name: string; description?: string; arguments?: unknown[] }> }>;\n listJobs?(): Promise<{ jobs: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown>; tags?: string[] }>; count: number }>;\n collectSkillAssets?(): Promise<{ entries: Array<{ skillName: string; baseDir?: string; instructionFile?: string; resources?: Record<string, string | undefined> }> }>;\n close(): Promise<void>;\n };\n\n try {\n const [toolsRaw, resourcesResult, promptsResult] = await Promise.all([\n client.listTools().catch(() => []),\n client.listResources().catch(() => ({ resources: [] })),\n client.listPrompts().catch(() => ({ prompts: [] })),\n ]);\n\n let resourceTemplates: ExtractedResourceTemplate[] = [];\n if (client.listResourceTemplates) {\n try {\n const templatesResult = await client.listResourceTemplates();\n resourceTemplates = (templatesResult.resourceTemplates || []).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.name || t.uriTemplate,\n description: t.description,\n }));\n } catch {\n // Resource templates not supported\n }\n }\n\n // DirectClient.listTools() returns FormattedTools (array) directly,\n // not { tools: [...] } like the raw MCP client\n const toolsList = Array.isArray(toolsRaw)\n ? toolsRaw as Array<{ name: string; description?: string; inputSchema?: unknown }>\n : ((toolsRaw as { tools?: unknown[] })?.tools || []) as Array<{ name: string; description?: string; inputSchema?: unknown }>;\n\n const tools: ExtractedTool[] = toolsList.map((t) => ({\n name: t.name,\n description: t.description || '',\n inputSchema: (t.inputSchema as Record<string, unknown>) || { type: 'object', properties: {} },\n }));\n\n const resources: ExtractedResource[] = (resourcesResult.resources || []).map((r) => ({\n uri: r.uri,\n name: r.name || r.uri,\n description: r.description,\n mimeType: r.mimeType,\n }));\n\n const prompts: ExtractedPrompt[] = (promptsResult.prompts || []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments as ExtractedPrompt['arguments'],\n }));\n\n const toolNameSet = new Set(tools.map((t) => t.name));\n const hasSkillsResources = resourceTemplates.some(\n (rt) => rt.uriTemplate.startsWith('skills://'),\n );\n const capabilities: ExtractedCapabilities = {\n skills: hasSkillsResources,\n jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),\n workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),\n };\n\n // Extract job schemas if jobs capability is available\n let jobs: ExtractedJob[] = [];\n if (capabilities.jobs && client.listJobs) {\n try {\n const jobsResult = await client.listJobs();\n jobs = (jobsResult.jobs || []).map((j) => ({\n name: j.name,\n description: j.description,\n inputSchema: j.inputSchema,\n tags: j.tags,\n }));\n } catch {\n // Jobs listing not available at build time\n }\n }\n\n // Collect skill file assets for copying to dist\n let skillAssets: ExtractedSkillAsset[] = [];\n if (capabilities.skills && client.collectSkillAssets) {\n try {\n const manifest = await client.collectSkillAssets();\n skillAssets = manifest.entries.map((e) => ({\n skillName: e.skillName,\n baseDir: e.baseDir,\n instructionFile: e.instructionFile,\n resourceDirs: e.resources as ExtractedSkillAsset['resourceDirs'],\n }));\n } catch {\n // Skill asset collection not available\n }\n }\n\n // Extract http.port from the @FrontMcp() decorator metadata. Returns\n // undefined when the entry is a plain config object (no decorator) or\n // when no port was declared — caller falls back to its own default.\n let httpPort: number | undefined;\n try {\n const sdk = require('@frontmcp/sdk') as { getDecoratorConfig?: (t: unknown) => Record<string, unknown> | undefined };\n const cfg = typeof sdk.getDecoratorConfig === 'function'\n ? sdk.getDecoratorConfig(configOrClass)\n : (configOrClass as Record<string, unknown> | undefined);\n const http = cfg?.['http'] as { port?: number } | undefined;\n if (http && typeof http.port === 'number') httpPort = http.port;\n } catch {\n // best-effort — manifest writer will fall back to its own default\n }\n\n return { tools, resources, resourceTemplates, prompts, jobs, capabilities, skillAssets, httpPort };\n } finally {\n await client.close().catch(() => {});\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"schema-extractor.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/schema-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build-time schema extraction.\n * After the server bundle is produced, boots a DirectClient via connect(),\n * extracts tool/resource/prompt schemas, and serializes them for CLI code generation.\n */\n\nexport interface ExtractedTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ExtractedResource {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n}\n\nexport interface ExtractedResourceTemplate {\n uriTemplate: string;\n name: string;\n description?: string;\n}\n\nexport interface ExtractedPrompt {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\nexport interface ExtractedJob {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n tags?: string[];\n}\n\nexport interface ExtractedCapabilities {\n skills: boolean;\n jobs: boolean;\n workflows: boolean;\n}\n\nexport interface ExtractedSkillAsset {\n skillName: string;\n baseDir?: string;\n instructionFile?: string;\n resourceDirs?: {\n references?: string;\n examples?: string;\n scripts?: string;\n assets?: string;\n };\n}\n\nexport interface ExtractedSchema {\n tools: ExtractedTool[];\n resources: ExtractedResource[];\n resourceTemplates: ExtractedResourceTemplate[];\n prompts: ExtractedPrompt[];\n jobs: ExtractedJob[];\n capabilities: ExtractedCapabilities;\n skillAssets: ExtractedSkillAsset[];\n /**\n * Server bind config extracted from `@FrontMcp({ http })` (or its\n * decorator-attached `__frontmcp:config` metadata). Used by the manifest\n * writer so the published manifest reflects the port the server will\n * actually listen on, not a hard-coded SDK default.\n */\n httpPort?: number;\n}\n\n/** Known system tool names injected by SDK features (jobs, workflows). */\nexport const SYSTEM_TOOL_NAMES = new Set([\n 'list-jobs',\n 'execute-job',\n 'get-job-status',\n 'register-job',\n 'remove-job',\n 'list-workflows',\n 'execute-workflow',\n 'get-workflow-status',\n 'register-workflow',\n 'remove-workflow',\n]);\n\n/**\n * Extract schemas from a compiled server bundle.\n * Requires the bundle to export a FrontMcp-decorated class as default export\n * or a config object usable by connect().\n */\nexport async function extractSchemas(bundlePath: string): Promise<ExtractedSchema> {\n // Suppress @FrontMcp() decorator bootstrap — we only need metadata, not a running server\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n\n let mod: Record<string, unknown>;\n try {\n mod = require(bundlePath);\n } finally {\n if (prev === undefined) {\n delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n } else {\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n }\n\n const configOrClass = mod.default || mod;\n\n // Use @frontmcp/sdk connect() to boot in-memory client\n let connect: (config: unknown, options?: { mode?: string }) => Promise<unknown>;\n try {\n const sdk = require('@frontmcp/sdk');\n connect = sdk.connect || sdk.direct?.connect;\n if (!connect) {\n throw new Error('connect() not found in @frontmcp/sdk');\n }\n } catch {\n throw new Error(\n '@frontmcp/sdk is required for CLI schema extraction. Ensure it is installed.',\n );\n }\n\n const client = await connect(configOrClass, { mode: 'cli' }) as {\n listTools(): Promise<unknown>;\n listResources(): Promise<{ resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }> }>;\n listResourceTemplates?(): Promise<{ resourceTemplates: Array<{ uriTemplate: string; name?: string; description?: string }> }>;\n listPrompts(): Promise<{ prompts: Array<{ name: string; description?: string; arguments?: unknown[] }> }>;\n listJobs?(): Promise<{ jobs: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown>; tags?: string[] }>; count: number }>;\n collectSkillAssets?(): Promise<{ entries: Array<{ skillName: string; baseDir?: string; instructionFile?: string; resources?: Record<string, string | undefined> }> }>;\n close(): Promise<void>;\n };\n\n try {\n const [toolsRaw, resourcesResult, promptsResult] = await Promise.all([\n client.listTools().catch(() => []),\n client.listResources().catch(() => ({ resources: [] })),\n client.listPrompts().catch(() => ({ prompts: [] })),\n ]);\n\n let resourceTemplates: ExtractedResourceTemplate[] = [];\n if (client.listResourceTemplates) {\n try {\n const templatesResult = await client.listResourceTemplates();\n resourceTemplates = (templatesResult.resourceTemplates || []).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.name || t.uriTemplate,\n description: t.description,\n }));\n } catch {\n // Resource templates not supported\n }\n }\n\n // DirectClient.listTools() returns FormattedTools (array) directly,\n // not { tools: [...] } like the raw MCP client\n const toolsList = Array.isArray(toolsRaw)\n ? toolsRaw as Array<{ name: string; description?: string; inputSchema?: unknown }>\n : ((toolsRaw as { tools?: unknown[] })?.tools || []) as Array<{ name: string; description?: string; inputSchema?: unknown }>;\n\n const tools: ExtractedTool[] = toolsList.map((t) => ({\n name: t.name,\n description: t.description || '',\n inputSchema: (t.inputSchema as Record<string, unknown>) || { type: 'object', properties: {} },\n }));\n\n const resources: ExtractedResource[] = (resourcesResult.resources || []).map((r) => ({\n uri: r.uri,\n name: r.name || r.uri,\n description: r.description,\n mimeType: r.mimeType,\n }));\n\n const prompts: ExtractedPrompt[] = (promptsResult.prompts || []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments as ExtractedPrompt['arguments'],\n }));\n\n const toolNameSet = new Set(tools.map((t) => t.name));\n // SEP-2640: skills are exposed under the singular `skill://` URI scheme.\n const hasSkillsResources = resourceTemplates.some(\n (rt) => rt.uriTemplate.startsWith('skill://'),\n );\n const capabilities: ExtractedCapabilities = {\n skills: hasSkillsResources,\n jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),\n workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),\n };\n\n // Extract job schemas if jobs capability is available\n let jobs: ExtractedJob[] = [];\n if (capabilities.jobs && client.listJobs) {\n try {\n const jobsResult = await client.listJobs();\n jobs = (jobsResult.jobs || []).map((j) => ({\n name: j.name,\n description: j.description,\n inputSchema: j.inputSchema,\n tags: j.tags,\n }));\n } catch {\n // Jobs listing not available at build time\n }\n }\n\n // Collect skill file assets for copying to dist\n let skillAssets: ExtractedSkillAsset[] = [];\n if (capabilities.skills && client.collectSkillAssets) {\n try {\n const manifest = await client.collectSkillAssets();\n skillAssets = manifest.entries.map((e) => ({\n skillName: e.skillName,\n baseDir: e.baseDir,\n instructionFile: e.instructionFile,\n resourceDirs: e.resources as ExtractedSkillAsset['resourceDirs'],\n }));\n } catch {\n // Skill asset collection not available\n }\n }\n\n // Extract http.port from the @FrontMcp() decorator metadata. Returns\n // undefined when the entry is a plain config object (no decorator) or\n // when no port was declared — caller falls back to its own default.\n let httpPort: number | undefined;\n try {\n const sdk = require('@frontmcp/sdk') as { getDecoratorConfig?: (t: unknown) => Record<string, unknown> | undefined };\n const cfg = typeof sdk.getDecoratorConfig === 'function'\n ? sdk.getDecoratorConfig(configOrClass)\n : (configOrClass as Record<string, unknown> | undefined);\n const http = cfg?.['http'] as { port?: number } | undefined;\n if (http && typeof http.port === 'number') httpPort = http.port;\n } catch {\n // best-effort — manifest writer will fall back to its own default\n }\n\n return { tools, resources, resourceTemplates, prompts, jobs, capabilities, skillAssets, httpPort };\n } finally {\n await client.close().catch(() => {});\n }\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ExportTarget } from './exporters';
|
|
2
|
+
export interface ExportSkillsOptions {
|
|
3
|
+
target: ExportTarget;
|
|
4
|
+
/** Specific skill name. When omitted, --all must be set. */
|
|
5
|
+
name?: string;
|
|
6
|
+
all?: boolean;
|
|
7
|
+
outDir?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function exportSkills(options: ExportSkillsOptions): Promise<void>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/export.ts
|
|
3
|
+
//
|
|
4
|
+
// `frontmcp skills export --target cursor|windsurf|copilot` — convert a
|
|
5
|
+
// catalog skill into the IDE-rule format the chosen target expects, and
|
|
6
|
+
// write the file under the current working directory.
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.exportSkills = exportSkills;
|
|
9
|
+
const tslib_1 = require("tslib");
|
|
10
|
+
const path = tslib_1.__importStar(require("path"));
|
|
11
|
+
const utils_1 = require("@frontmcp/utils");
|
|
12
|
+
const colors_1 = require("../../core/colors");
|
|
13
|
+
const catalog_1 = require("./catalog");
|
|
14
|
+
const exporters_1 = require("./exporters");
|
|
15
|
+
async function exportSkills(options) {
|
|
16
|
+
const manifest = (0, catalog_1.loadCatalog)();
|
|
17
|
+
const outDir = options.outDir ?? process.cwd();
|
|
18
|
+
let skills = manifest.skills;
|
|
19
|
+
if (options.name) {
|
|
20
|
+
const found = skills.find((s) => s.name === options.name);
|
|
21
|
+
if (!found) {
|
|
22
|
+
console.error((0, colors_1.c)('red', `Skill "${options.name}" not found in catalog.`));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
skills = [found];
|
|
26
|
+
}
|
|
27
|
+
else if (!options.all) {
|
|
28
|
+
console.error((0, colors_1.c)('red', 'Specify a skill name or pass --all to export every skill.'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const catalogDir = (0, catalog_1.getCatalogDir)();
|
|
32
|
+
let written = 0;
|
|
33
|
+
for (const skill of skills) {
|
|
34
|
+
const skillDir = path.join(catalogDir, skill.path);
|
|
35
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
36
|
+
let instructions = '';
|
|
37
|
+
try {
|
|
38
|
+
const raw = await (0, utils_1.readFile)(skillMdPath, 'utf8');
|
|
39
|
+
instructions = stripFrontmatter(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
console.warn((0, colors_1.c)('yellow', `Skipping ${skill.name}: SKILL.md not readable.`));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const input = {
|
|
46
|
+
name: skill.name,
|
|
47
|
+
description: skill.description,
|
|
48
|
+
instructions,
|
|
49
|
+
category: skill.category,
|
|
50
|
+
tags: skill.tags,
|
|
51
|
+
};
|
|
52
|
+
const out = renderForTarget(options.target, input);
|
|
53
|
+
const absPath = path.join(outDir, out.relativePath);
|
|
54
|
+
await (0, utils_1.ensureDir)(path.dirname(absPath));
|
|
55
|
+
if (options.target === 'windsurf') {
|
|
56
|
+
// Windsurf rules live in a single file — append rather than overwrite.
|
|
57
|
+
let existing = '';
|
|
58
|
+
try {
|
|
59
|
+
existing = await (0, utils_1.readFile)(absPath, 'utf8');
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err.code !== 'ENOENT')
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
await (0, utils_1.writeFile)(absPath, mergeWindsurf(existing, skill.name, out.contents));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await (0, utils_1.writeFile)(absPath, out.contents);
|
|
69
|
+
}
|
|
70
|
+
console.log((0, colors_1.c)('green', `wrote ${out.relativePath}`));
|
|
71
|
+
written++;
|
|
72
|
+
}
|
|
73
|
+
console.log((0, colors_1.c)('bold', `\n${written} skill(s) exported to target=${options.target}.\n`));
|
|
74
|
+
}
|
|
75
|
+
function renderForTarget(target, input) {
|
|
76
|
+
switch (target) {
|
|
77
|
+
case 'cursor':
|
|
78
|
+
return (0, exporters_1.exportToCursor)(input);
|
|
79
|
+
case 'windsurf':
|
|
80
|
+
return (0, exporters_1.exportToWindsurf)(input);
|
|
81
|
+
case 'copilot':
|
|
82
|
+
return (0, exporters_1.exportToCopilot)(input);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function stripFrontmatter(raw) {
|
|
86
|
+
if (!raw.startsWith('---'))
|
|
87
|
+
return raw.trim();
|
|
88
|
+
const closeIdx = raw.indexOf('\n---', 3);
|
|
89
|
+
if (closeIdx === -1)
|
|
90
|
+
return raw.trim();
|
|
91
|
+
return raw.slice(closeIdx + 4).trim();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Replace or append a `## <name>` block in `.windsurfrules`. Multiple
|
|
95
|
+
* exports into the same file are stable: re-exporting overwrites just the
|
|
96
|
+
* skill's section, leaving siblings untouched.
|
|
97
|
+
*/
|
|
98
|
+
function mergeWindsurf(existing, name, sectionContents) {
|
|
99
|
+
if (!existing)
|
|
100
|
+
return sectionContents;
|
|
101
|
+
// Anchor the heading match to the start of a line so a skill name appearing
|
|
102
|
+
// inside prose or a code block can't be mistaken for the section header.
|
|
103
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
104
|
+
const headingRe = new RegExp(`^##\\s+${escaped}\\b`, 'm');
|
|
105
|
+
const match = headingRe.exec(existing);
|
|
106
|
+
if (!match) {
|
|
107
|
+
return existing.endsWith('\n') ? `${existing}\n${sectionContents}` : `${existing}\n\n${sectionContents}`;
|
|
108
|
+
}
|
|
109
|
+
const idx = match.index;
|
|
110
|
+
const after = idx + match[0].length;
|
|
111
|
+
// Replace from this heading to the next `## ` line (or EOF).
|
|
112
|
+
const nextHeadingIdx = existing.indexOf('\n## ', after);
|
|
113
|
+
const tail = nextHeadingIdx === -1 ? '' : existing.slice(nextHeadingIdx);
|
|
114
|
+
return `${existing.slice(0, idx)}${sectionContents.trim()}\n${tail}`;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../../../src/commands/skills/export.ts"],"names":[],"mappings":"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","sourcesContent":["// file: libs/cli/src/commands/skills/export.ts\n//\n// `frontmcp skills export --target cursor|windsurf|copilot` — convert a\n// catalog skill into the IDE-rule format the chosen target expects, and\n// write the file under the current working directory.\n\nimport * as path from 'path';\n\nimport { ensureDir, readFile, writeFile } from '@frontmcp/utils';\n\nimport { c } from '../../core/colors';\nimport { getCatalogDir, loadCatalog } from './catalog';\nimport { exportToCopilot, exportToCursor, exportToWindsurf, type ExporterOutput, type ExportTarget } from './exporters';\n\nexport interface ExportSkillsOptions {\n target: ExportTarget;\n /** Specific skill name. When omitted, --all must be set. */\n name?: string;\n all?: boolean;\n outDir?: string;\n}\n\nexport async function exportSkills(options: ExportSkillsOptions): Promise<void> {\n const manifest = loadCatalog();\n const outDir = options.outDir ?? process.cwd();\n\n let skills = manifest.skills;\n if (options.name) {\n const found = skills.find((s) => s.name === options.name);\n if (!found) {\n console.error(c('red', `Skill \"${options.name}\" not found in catalog.`));\n process.exit(1);\n }\n skills = [found];\n } else if (!options.all) {\n console.error(c('red', 'Specify a skill name or pass --all to export every skill.'));\n process.exit(1);\n }\n\n const catalogDir = getCatalogDir();\n let written = 0;\n for (const skill of skills) {\n const skillDir = path.join(catalogDir, skill.path);\n const skillMdPath = path.join(skillDir, 'SKILL.md');\n let instructions = '';\n try {\n const raw = await readFile(skillMdPath, 'utf8');\n instructions = stripFrontmatter(raw);\n } catch {\n console.warn(c('yellow', `Skipping ${skill.name}: SKILL.md not readable.`));\n continue;\n }\n\n const input = {\n name: skill.name,\n description: skill.description,\n instructions,\n category: skill.category,\n tags: skill.tags,\n };\n const out = renderForTarget(options.target, input);\n\n const absPath = path.join(outDir, out.relativePath);\n await ensureDir(path.dirname(absPath));\n if (options.target === 'windsurf') {\n // Windsurf rules live in a single file — append rather than overwrite.\n let existing = '';\n try {\n existing = await readFile(absPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n await writeFile(absPath, mergeWindsurf(existing, skill.name, out.contents));\n } else {\n await writeFile(absPath, out.contents);\n }\n console.log(c('green', `wrote ${out.relativePath}`));\n written++;\n }\n\n console.log(c('bold', `\\n${written} skill(s) exported to target=${options.target}.\\n`));\n}\n\nfunction renderForTarget(\n target: ExportTarget,\n input: { name: string; description: string; instructions: string; category?: string; tags?: string[] },\n): ExporterOutput {\n switch (target) {\n case 'cursor':\n return exportToCursor(input);\n case 'windsurf':\n return exportToWindsurf(input);\n case 'copilot':\n return exportToCopilot(input);\n }\n}\n\nfunction stripFrontmatter(raw: string): string {\n if (!raw.startsWith('---')) return raw.trim();\n const closeIdx = raw.indexOf('\\n---', 3);\n if (closeIdx === -1) return raw.trim();\n return raw.slice(closeIdx + 4).trim();\n}\n\n/**\n * Replace or append a `## <name>` block in `.windsurfrules`. Multiple\n * exports into the same file are stable: re-exporting overwrites just the\n * skill's section, leaving siblings untouched.\n */\nfunction mergeWindsurf(existing: string, name: string, sectionContents: string): string {\n if (!existing) return sectionContents;\n // Anchor the heading match to the start of a line so a skill name appearing\n // inside prose or a code block can't be mistaken for the section header.\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const headingRe = new RegExp(`^##\\\\s+${escaped}\\\\b`, 'm');\n const match = headingRe.exec(existing);\n if (!match) {\n return existing.endsWith('\\n') ? `${existing}\\n${sectionContents}` : `${existing}\\n\\n${sectionContents}`;\n }\n const idx = match.index;\n const after = idx + match[0].length;\n // Replace from this heading to the next `## ` line (or EOF).\n const nextHeadingIdx = existing.indexOf('\\n## ', after);\n const tail = nextHeadingIdx === -1 ? '' : existing.slice(nextHeadingIdx);\n return `${existing.slice(0, idx)}${sectionContents.trim()}\\n${tail}`;\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/exporters/copilot.ts
|
|
3
|
+
//
|
|
4
|
+
// Convert a catalog skill into a GitHub Copilot instructions file. Copilot
|
|
5
|
+
// reads `.github/copilot-instructions.md` (workspace) plus per-skill files
|
|
6
|
+
// under `.github/instructions/`. We emit one file per skill so users can
|
|
7
|
+
// pick which ones to commit.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.exportToCopilot = exportToCopilot;
|
|
10
|
+
const sanitize_1 = require("./sanitize");
|
|
11
|
+
function exportToCopilot(skill) {
|
|
12
|
+
const header = [];
|
|
13
|
+
header.push(`# ${skill.name}`);
|
|
14
|
+
header.push('');
|
|
15
|
+
header.push(`> ${skill.description}`);
|
|
16
|
+
if (skill.category)
|
|
17
|
+
header.push(`>`);
|
|
18
|
+
if (skill.category)
|
|
19
|
+
header.push(`> Category: ${skill.category}`);
|
|
20
|
+
if (skill.tags?.length)
|
|
21
|
+
header.push(`> Tags: ${skill.tags.join(', ')}`);
|
|
22
|
+
header.push('');
|
|
23
|
+
return {
|
|
24
|
+
relativePath: `.github/instructions/${(0, sanitize_1.safeSkillSlug)(skill.name)}.md`,
|
|
25
|
+
contents: `${header.join('\n')}${skill.instructions.trim()}\n`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=copilot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.js","sourceRoot":"","sources":["../../../../../src/commands/skills/exporters/copilot.ts"],"names":[],"mappings":";AAAA,0DAA0D;AAC1D,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,yEAAyE;AACzE,6BAA6B;;AAO7B,0CAaC;AAjBD,yCAA2C;AAI3C,SAAgB,eAAe,CAAC,KAAyB;IACvD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChB,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,QAAQ;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,QAAQ;QAAE,MAAM,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjE,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO;QACL,YAAY,EAAE,wBAAwB,IAAA,wBAAa,EAAC,KAAK,CAAC,IAAI,CAAC,KAAK;QACpE,QAAQ,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI;KAC/D,CAAC;AACJ,CAAC","sourcesContent":["// file: libs/cli/src/commands/skills/exporters/copilot.ts\n//\n// Convert a catalog skill into a GitHub Copilot instructions file. Copilot\n// reads `.github/copilot-instructions.md` (workspace) plus per-skill files\n// under `.github/instructions/`. We emit one file per skill so users can\n// pick which ones to commit.\n\nimport type { CursorExportInput, ExporterOutput } from './cursor';\nimport { safeSkillSlug } from './sanitize';\n\nexport type CopilotExportInput = CursorExportInput;\n\nexport function exportToCopilot(skill: CopilotExportInput): ExporterOutput {\n const header: string[] = [];\n header.push(`# ${skill.name}`);\n header.push('');\n header.push(`> ${skill.description}`);\n if (skill.category) header.push(`>`);\n if (skill.category) header.push(`> Category: ${skill.category}`);\n if (skill.tags?.length) header.push(`> Tags: ${skill.tags.join(', ')}`);\n header.push('');\n return {\n relativePath: `.github/instructions/${safeSkillSlug(skill.name)}.md`,\n contents: `${header.join('\\n')}${skill.instructions.trim()}\\n`,\n };\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CursorExportInput {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
instructions: string;
|
|
5
|
+
category?: string;
|
|
6
|
+
tags?: string[];
|
|
7
|
+
globs?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ExporterOutput {
|
|
10
|
+
/** Relative path the file should be written to (`.cursor/rules/<name>.mdc`). */
|
|
11
|
+
relativePath: string;
|
|
12
|
+
/** Full file contents. */
|
|
13
|
+
contents: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Cursor rules use frontmatter with `description` + optional `globs`. The
|
|
17
|
+
* skill's category is appended as a hash-tag in description so users can grep.
|
|
18
|
+
*/
|
|
19
|
+
export declare function exportToCursor(skill: CursorExportInput): ExporterOutput;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/exporters/cursor.ts
|
|
3
|
+
//
|
|
4
|
+
// Convert a catalog skill into a Cursor `.cursor/rules/<name>.mdc` rule file.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.exportToCursor = exportToCursor;
|
|
7
|
+
const sanitize_1 = require("./sanitize");
|
|
8
|
+
/**
|
|
9
|
+
* Cursor rules use frontmatter with `description` + optional `globs`. The
|
|
10
|
+
* skill's category is appended as a hash-tag in description so users can grep.
|
|
11
|
+
*/
|
|
12
|
+
function exportToCursor(skill) {
|
|
13
|
+
const fmLines = ['---'];
|
|
14
|
+
const desc = skill.category ? `${skill.description} #${skill.category}` : skill.description;
|
|
15
|
+
fmLines.push(`description: ${stringifyOneLine(desc)}`);
|
|
16
|
+
if (skill.globs && skill.globs.length > 0) {
|
|
17
|
+
fmLines.push(`globs: ${JSON.stringify(skill.globs)}`);
|
|
18
|
+
}
|
|
19
|
+
if (skill.tags && skill.tags.length > 0) {
|
|
20
|
+
fmLines.push(`tags: ${JSON.stringify(skill.tags)}`);
|
|
21
|
+
}
|
|
22
|
+
fmLines.push('alwaysApply: false');
|
|
23
|
+
fmLines.push('---');
|
|
24
|
+
const body = `# ${skill.name}\n\n${skill.instructions.trim()}\n`;
|
|
25
|
+
return {
|
|
26
|
+
relativePath: `.cursor/rules/${(0, sanitize_1.safeSkillSlug)(skill.name)}.mdc`,
|
|
27
|
+
contents: `${fmLines.join('\n')}\n\n${body}`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function stringifyOneLine(text) {
|
|
31
|
+
// Cursor frontmatter is YAML-ish; one-line plain scalars break on indicator
|
|
32
|
+
// chars, on leading/trailing whitespace, and on values that look like
|
|
33
|
+
// booleans/null. JSON.stringify gives a safe quoted form for any of those.
|
|
34
|
+
if (text.length === 0)
|
|
35
|
+
return '""';
|
|
36
|
+
if (/^\s|\s$/.test(text))
|
|
37
|
+
return JSON.stringify(text);
|
|
38
|
+
if (/[:#"'&*!|>%@`,[\]{}]/.test(text))
|
|
39
|
+
return JSON.stringify(text);
|
|
40
|
+
if (/^[-?]/.test(text))
|
|
41
|
+
return JSON.stringify(text);
|
|
42
|
+
if (/^(true|false|null|yes|no|on|off|~)$/i.test(text))
|
|
43
|
+
return JSON.stringify(text);
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../../../src/commands/skills/exporters/cursor.ts"],"names":[],"mappings":";AAAA,yDAAyD;AACzD,EAAE;AACF,8EAA8E;;AAwB9E,wCAiBC;AAvCD,yCAA2C;AAkB3C;;;GAGG;AACH,SAAgB,cAAc,CAAC,KAAwB;IACrD,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;IAC5F,OAAO,CAAC,IAAI,CAAC,gBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvD,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC;IACjE,OAAO;QACL,YAAY,EAAE,iBAAiB,IAAA,wBAAa,EAAC,KAAK,CAAC,IAAI,CAAC,MAAM;QAC9D,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE;KAC7C,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,4EAA4E;IAC5E,sEAAsE;IACtE,2EAA2E;IAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACnF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["// file: libs/cli/src/commands/skills/exporters/cursor.ts\n//\n// Convert a catalog skill into a Cursor `.cursor/rules/<name>.mdc` rule file.\n\nimport { safeSkillSlug } from './sanitize';\n\nexport interface CursorExportInput {\n name: string;\n description: string;\n instructions: string;\n category?: string;\n tags?: string[];\n globs?: string[];\n}\n\nexport interface ExporterOutput {\n /** Relative path the file should be written to (`.cursor/rules/<name>.mdc`). */\n relativePath: string;\n /** Full file contents. */\n contents: string;\n}\n\n/**\n * Cursor rules use frontmatter with `description` + optional `globs`. The\n * skill's category is appended as a hash-tag in description so users can grep.\n */\nexport function exportToCursor(skill: CursorExportInput): ExporterOutput {\n const fmLines = ['---'];\n const desc = skill.category ? `${skill.description} #${skill.category}` : skill.description;\n fmLines.push(`description: ${stringifyOneLine(desc)}`);\n if (skill.globs && skill.globs.length > 0) {\n fmLines.push(`globs: ${JSON.stringify(skill.globs)}`);\n }\n if (skill.tags && skill.tags.length > 0) {\n fmLines.push(`tags: ${JSON.stringify(skill.tags)}`);\n }\n fmLines.push('alwaysApply: false');\n fmLines.push('---');\n const body = `# ${skill.name}\\n\\n${skill.instructions.trim()}\\n`;\n return {\n relativePath: `.cursor/rules/${safeSkillSlug(skill.name)}.mdc`,\n contents: `${fmLines.join('\\n')}\\n\\n${body}`,\n };\n}\n\nfunction stringifyOneLine(text: string): string {\n // Cursor frontmatter is YAML-ish; one-line plain scalars break on indicator\n // chars, on leading/trailing whitespace, and on values that look like\n // booleans/null. JSON.stringify gives a safe quoted form for any of those.\n if (text.length === 0) return '\"\"';\n if (/^\\s|\\s$/.test(text)) return JSON.stringify(text);\n if (/[:#\"'&*!|>%@`,[\\]{}]/.test(text)) return JSON.stringify(text);\n if (/^[-?]/.test(text)) return JSON.stringify(text);\n if (/^(true|false|null|yes|no|on|off|~)$/i.test(text)) return JSON.stringify(text);\n return text;\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { exportToCursor, type CursorExportInput, type ExporterOutput } from './cursor';
|
|
2
|
+
export { exportToWindsurf, type WindsurfExportInput } from './windsurf';
|
|
3
|
+
export { exportToCopilot, type CopilotExportInput } from './copilot';
|
|
4
|
+
export type ExportTarget = 'cursor' | 'windsurf' | 'copilot';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// file: libs/cli/src/commands/skills/exporters/index.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.exportToCopilot = exports.exportToWindsurf = exports.exportToCursor = void 0;
|
|
5
|
+
var cursor_1 = require("./cursor");
|
|
6
|
+
Object.defineProperty(exports, "exportToCursor", { enumerable: true, get: function () { return cursor_1.exportToCursor; } });
|
|
7
|
+
var windsurf_1 = require("./windsurf");
|
|
8
|
+
Object.defineProperty(exports, "exportToWindsurf", { enumerable: true, get: function () { return windsurf_1.exportToWindsurf; } });
|
|
9
|
+
var copilot_1 = require("./copilot");
|
|
10
|
+
Object.defineProperty(exports, "exportToCopilot", { enumerable: true, get: function () { return copilot_1.exportToCopilot; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/skills/exporters/index.ts"],"names":[],"mappings":";AAAA,wDAAwD;;;AAExD,mCAAuF;AAA9E,wGAAA,cAAc,OAAA;AACvB,uCAAwE;AAA/D,4GAAA,gBAAgB,OAAA;AACzB,qCAAqE;AAA5D,0GAAA,eAAe,OAAA","sourcesContent":["// file: libs/cli/src/commands/skills/exporters/index.ts\n\nexport { exportToCursor, type CursorExportInput, type ExporterOutput } from './cursor';\nexport { exportToWindsurf, type WindsurfExportInput } from './windsurf';\nexport { exportToCopilot, type CopilotExportInput } from './copilot';\n\nexport type ExportTarget = 'cursor' | 'windsurf' | 'copilot';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function safeSkillSlug(name: string): string;
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../../../../src/commands/skills/exporters/sanitize.ts"],"names":[],"mappings":";AAAA,2DAA2D;AAC3D,EAAE;AACF,6EAA6E;AAC7E,yEAAyE;AACzE,+BAA+B;;AAiB/B,sCASC;AAxBD,MAAM,eAAe,GAAG,QAAQ,CAAC;AACjC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAEzC,SAAS,iBAAiB,CAAC,CAAS;IAClC,qEAAqE;IACrE,kEAAkE;IAClE,yEAAyE;IACzE,sCAAsC;IACtC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;YAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAgB,aAAa,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;SACnD,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;SACnB,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC;SAC1B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;SAC7B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;AACxD,CAAC","sourcesContent":["// file: libs/cli/src/commands/skills/exporters/sanitize.ts\n//\n// Filesystem-safe slug derivation for skill names. Exporters use this before\n// building output paths so a hostile or malformed `skill.name` can never\n// escape the target directory.\n\nconst PATH_SEPARATORS = /[\\\\/]/g;\nconst UNSAFE_CHARS = /[^A-Za-z0-9._-]+/g;\n\nfunction stripControlChars(s: string): string {\n // Drop C0 control chars (U+0000–U+001F) up front. UNSAFE_CHARS would\n // collapse them anyway, but doing it here means inputs like \"a\u0000b\"\n // become \"ab\" instead of \"a-b\" and stay distinguishable from inputs that\n // legitimately contained a separator.\n let out = '';\n for (let i = 0; i < s.length; i++) {\n if (s.charCodeAt(i) > 0x1f) out += s[i];\n }\n return out;\n}\n\nexport function safeSkillSlug(name: string): string {\n const slug = stripControlChars(name.normalize('NFKD'))\n .replace(PATH_SEPARATORS, '-')\n .replace(/^\\.+/, '')\n .replace(UNSAFE_CHARS, '-')\n .replace(/-+/g, '-')\n .replace(/^[-.]+|[-.]+$/g, '')\n .slice(0, 80);\n return slug.length > 0 ? slug.toLowerCase() : 'skill';\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CursorExportInput, ExporterOutput } from './cursor';
|
|
2
|
+
export type WindsurfExportInput = CursorExportInput;
|
|
3
|
+
/**
|
|
4
|
+
* Build the Windsurf section for one skill. The section is delimited by
|
|
5
|
+
* `## <name>` so multiple skills can coexist in `.windsurfrules`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function exportToWindsurf(skill: WindsurfExportInput): ExporterOutput;
|
|
@@ -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"]}
|