2026-03-17 20:01:04 +08:00
|
|
|
|
import { Command } from 'commander';
|
|
|
|
|
|
import { generateCardDeck, type GenerateCardDeckParams, type CardField } from '../tools/generate-card-deck.js';
|
2026-03-18 12:08:28 +08:00
|
|
|
|
import { readFrontmatter, type ReadFrontmatterParams, type DeckFrontmatter } from '../tools/frontmatter/read-frontmatter.js';
|
|
|
|
|
|
import { writeFrontmatter, type WriteFrontmatterParams } from '../tools/frontmatter/write-frontmatter.js';
|
|
|
|
|
|
import { cardCrud, type CardCrudParams, type CardData } from '../tools/card/card-crud.js';
|
|
|
|
|
|
import { ensureDeckPreview, type EnsureDeckPreviewParams } from '../tools/ensure-deck-preview.js';
|
2026-03-18 12:12:43 +08:00
|
|
|
|
import {
|
|
|
|
|
|
designCardGame,
|
|
|
|
|
|
getDesignCardGamePrompt,
|
|
|
|
|
|
type DesignCardGameOptions
|
|
|
|
|
|
} from '../prompts/design-card-game.js';
|
|
|
|
|
|
import {
|
|
|
|
|
|
populateDeck,
|
|
|
|
|
|
getPopulateDeckPrompt,
|
|
|
|
|
|
type PopulateDeckOptions
|
|
|
|
|
|
} from '../prompts/populate-deck.js';
|
|
|
|
|
|
import {
|
|
|
|
|
|
setupDeckDisplay,
|
|
|
|
|
|
getSetupDeckDisplayPrompt,
|
|
|
|
|
|
type SetupDeckDisplayOptions
|
|
|
|
|
|
} from '../prompts/setup-deck-display.js';
|
2026-03-17 20:01:04 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* MCP 服务器命令
|
2026-03-18 12:08:28 +08:00
|
|
|
|
*
|
2026-03-17 20:01:04 +08:00
|
|
|
|
* 提供 MCP (Model Context Protocol) 服务器功能,用于与 AI 助手集成
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
export interface MCPOptions {
|
|
|
|
|
|
port?: string;
|
2026-03-17 22:49:47 +08:00
|
|
|
|
cwd?: string;
|
2026-03-17 20:01:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const mcpCommand = new Command('mcp')
|
|
|
|
|
|
.description('MCP 服务器 - 用于 AI 助手集成的工具协议')
|
|
|
|
|
|
.addCommand(
|
|
|
|
|
|
new Command('serve')
|
|
|
|
|
|
.description('启动 MCP 服务器')
|
|
|
|
|
|
.argument('[host]', '服务器地址', 'stdio')
|
|
|
|
|
|
.option('-p, --port <port>', 'HTTP 端口(仅 HTTP 传输)', '3001')
|
2026-03-18 11:31:13 +08:00
|
|
|
|
.option('--cwd <dir>', '工作目录(工具调用的相对路径基准)', process.env.TTRPG_MCP_CWD || process.cwd())
|
2026-03-17 20:01:04 +08:00
|
|
|
|
.action(mcpServeAction)
|
|
|
|
|
|
)
|
|
|
|
|
|
.addCommand(
|
|
|
|
|
|
new Command('generate-card-deck')
|
|
|
|
|
|
.description('生成卡牌组(快速命令)')
|
|
|
|
|
|
.requiredOption('--name <name>', '卡牌组名称')
|
|
|
|
|
|
.requiredOption('--output <dir>', '输出目录')
|
|
|
|
|
|
.option('-c, --count <number>', '卡牌数量', '10')
|
|
|
|
|
|
.option('--fields <fields>', '字段列表(逗号分隔)', 'name,type,cost,description')
|
|
|
|
|
|
.option('--description <desc>', '卡牌组描述')
|
|
|
|
|
|
.option('--size <size>', '卡牌尺寸', '54x86')
|
|
|
|
|
|
.option('--grid <grid>', '网格布局', '5x8')
|
|
|
|
|
|
.action(generateCardDeckAction)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* MCP 服务器启动处理函数
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function mcpServeAction(host: string, options: MCPOptions) {
|
2026-03-17 22:49:47 +08:00
|
|
|
|
// 切换到指定的工作目录
|
|
|
|
|
|
const cwd = options.cwd || process.cwd();
|
|
|
|
|
|
process.chdir(cwd);
|
|
|
|
|
|
console.error(`MCP 服务器工作目录:${cwd}`);
|
|
|
|
|
|
|
2026-03-17 20:01:04 +08:00
|
|
|
|
// 动态导入 MCP SDK
|
|
|
|
|
|
const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
|
|
|
|
|
|
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
|
|
|
|
const {
|
|
|
|
|
|
CallToolRequestSchema,
|
|
|
|
|
|
ListToolsRequestSchema,
|
2026-03-18 12:12:43 +08:00
|
|
|
|
ListPromptsRequestSchema,
|
|
|
|
|
|
GetPromptRequestSchema,
|
2026-03-17 20:01:04 +08:00
|
|
|
|
} = await import('@modelcontextprotocol/sdk/types.js');
|
|
|
|
|
|
|
|
|
|
|
|
const server = new Server(
|
|
|
|
|
|
{
|
|
|
|
|
|
name: 'ttrpg-card-generator',
|
|
|
|
|
|
version: '0.1.0',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities: {
|
|
|
|
|
|
tools: {},
|
2026-03-18 12:12:43 +08:00
|
|
|
|
prompts: {},
|
2026-03-17 20:01:04 +08:00
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理工具列表请求
|
|
|
|
|
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
tools: [
|
2026-03-18 12:08:28 +08:00
|
|
|
|
{
|
|
|
|
|
|
name: 'deck_frontmatter_read',
|
|
|
|
|
|
description: '读取 CSV 文件的 frontmatter(包含模板定义和 deck 配置)',
|
|
|
|
|
|
inputSchema: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
csv_file: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: 'CSV 文件路径(相对路径相对于 MCP 服务器工作目录)',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
required: ['csv_file'],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: 'deck_frontmatter_write',
|
|
|
|
|
|
description: '写入/更新 CSV 文件的 frontmatter(模板定义和 deck 配置)',
|
|
|
|
|
|
inputSchema: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
csv_file: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: 'CSV 文件路径',
|
|
|
|
|
|
},
|
|
|
|
|
|
frontmatter: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
description: '要写入的 frontmatter 数据',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
fields: {
|
|
|
|
|
|
type: 'array',
|
|
|
|
|
|
description: '字段定义列表',
|
|
|
|
|
|
items: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
name: { type: 'string', description: '字段名称' },
|
|
|
|
|
|
description: { type: 'string', description: '字段描述' },
|
|
|
|
|
|
examples: { type: 'array', items: { type: 'string' }, description: '示例值列表' },
|
|
|
|
|
|
},
|
|
|
|
|
|
required: ['name'],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
deck: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
description: 'Deck 配置',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
size: { type: 'string', description: '卡牌尺寸,格式 "宽 x 高"' },
|
|
|
|
|
|
grid: { type: 'string', description: '网格布局,格式 "列 x 行"' },
|
|
|
|
|
|
bleed: { type: 'number', description: '出血边距(mm)' },
|
|
|
|
|
|
padding: { type: 'number', description: '内边距(mm)' },
|
|
|
|
|
|
shape: { type: 'string', enum: ['rectangle', 'circle', 'hex', 'diamond'] },
|
|
|
|
|
|
layers: { type: 'string', description: '正面图层配置' },
|
|
|
|
|
|
back_layers: { type: 'string', description: '背面图层配置' },
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
merge: {
|
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
|
description: '是否合并现有 frontmatter(默认 true)',
|
|
|
|
|
|
default: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
required: ['csv_file', 'frontmatter'],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: 'deck_card_crud',
|
|
|
|
|
|
description: '卡牌 CRUD 操作(创建/读取/更新/删除),支持批量操作',
|
|
|
|
|
|
inputSchema: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
csv_file: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: 'CSV 文件路径',
|
|
|
|
|
|
},
|
|
|
|
|
|
action: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: '操作类型',
|
|
|
|
|
|
enum: ['create', 'read', 'update', 'delete'],
|
|
|
|
|
|
},
|
|
|
|
|
|
cards: {
|
|
|
|
|
|
type: ['array', 'object'],
|
|
|
|
|
|
description: '卡牌数据(单张或数组)',
|
|
|
|
|
|
items: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
additionalProperties: { type: 'string' },
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
type: ['string', 'array'],
|
|
|
|
|
|
description: '要操作的卡牌 label(用于 read/update/delete)',
|
|
|
|
|
|
items: { type: 'string' },
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
required: ['csv_file', 'action'],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: 'deck_ensure_preview',
|
|
|
|
|
|
description: '确保 CSV 对应的 Markdown 预览文件存在',
|
|
|
|
|
|
inputSchema: {
|
|
|
|
|
|
type: 'object',
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
csv_file: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: 'CSV 文件路径',
|
|
|
|
|
|
},
|
|
|
|
|
|
md_file: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: 'Markdown 文件路径(可选,默认与 CSV 同名)',
|
|
|
|
|
|
},
|
|
|
|
|
|
title: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: '标题(可选,默认从 CSV 文件名推断)',
|
|
|
|
|
|
},
|
|
|
|
|
|
description: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
description: '描述(可选)',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
required: ['csv_file'],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-03-17 20:01:04 +08:00
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-18 12:12:43 +08:00
|
|
|
|
// 处理 Prompts 列表请求
|
|
|
|
|
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
prompts: [
|
|
|
|
|
|
getDesignCardGamePrompt(),
|
|
|
|
|
|
getPopulateDeckPrompt(),
|
|
|
|
|
|
getSetupDeckDisplayPrompt(),
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-17 20:01:04 +08:00
|
|
|
|
// 处理工具调用请求
|
|
|
|
|
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
|
|
|
const { name, arguments: args } = request.params;
|
|
|
|
|
|
|
2026-03-18 12:08:28 +08:00
|
|
|
|
try {
|
|
|
|
|
|
switch (name) {
|
|
|
|
|
|
case 'generate_card_deck': {
|
|
|
|
|
|
const params = args as unknown as GenerateCardDeckParams;
|
|
|
|
|
|
|
|
|
|
|
|
// 验证必需参数
|
|
|
|
|
|
if (!params.deck_name || !params.output_dir) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'text',
|
|
|
|
|
|
text: '错误:缺少必需参数 deck_name 或 output_dir',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成卡牌组(使用当前工作目录)
|
|
|
|
|
|
const result = generateCardDeck(params);
|
2026-03-17 20:01:04 +08:00
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'text',
|
2026-03-18 12:08:28 +08:00
|
|
|
|
text: result.message,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'text',
|
|
|
|
|
|
text: `\n## 生成的组件代码\n\n\`\`\`markdown\n${result.deckComponent}\n\`\`\``,
|
2026-03-17 20:01:04 +08:00
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 12:08:28 +08:00
|
|
|
|
case 'deck_frontmatter_read': {
|
|
|
|
|
|
const params = args as unknown as ReadFrontmatterParams;
|
2026-03-17 20:01:04 +08:00
|
|
|
|
|
2026-03-18 12:08:28 +08:00
|
|
|
|
if (!params.csv_file) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: '错误:缺少必需参数 csv_file' }],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = readFrontmatter(params);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'text',
|
|
|
|
|
|
text: result.message,
|
|
|
|
|
|
},
|
|
|
|
|
|
...(result.frontmatter ? [{
|
|
|
|
|
|
type: 'text',
|
|
|
|
|
|
text: `\n## Frontmatter\n\n\`\`\`json\n${JSON.stringify(result.frontmatter, null, 2)}\n\`\`\``,
|
|
|
|
|
|
}] : []),
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'deck_frontmatter_write': {
|
|
|
|
|
|
const params = args as unknown as WriteFrontmatterParams;
|
|
|
|
|
|
|
|
|
|
|
|
if (!params.csv_file || !params.frontmatter) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: '错误:缺少必需参数 csv_file 或 frontmatter' }],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = writeFrontmatter(params);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }],
|
|
|
|
|
|
isError: !result.success,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'deck_card_crud': {
|
|
|
|
|
|
const params = args as unknown as CardCrudParams;
|
|
|
|
|
|
|
|
|
|
|
|
if (!params.csv_file || !params.action) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: '错误:缺少必需参数 csv_file 或 action' }],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = cardCrud(params);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'text',
|
|
|
|
|
|
text: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,
|
|
|
|
|
|
},
|
|
|
|
|
|
...(result.cards && result.cards.length > 0 ? [{
|
|
|
|
|
|
type: 'text',
|
|
|
|
|
|
text: `\n## 卡牌数据\n\n\`\`\`json\n${JSON.stringify(result.cards, null, 2)}\n\`\`\``,
|
|
|
|
|
|
}] : []),
|
|
|
|
|
|
],
|
|
|
|
|
|
isError: !result.success,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'deck_ensure_preview': {
|
|
|
|
|
|
const params = args as unknown as EnsureDeckPreviewParams;
|
|
|
|
|
|
|
|
|
|
|
|
if (!params.csv_file) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: '错误:缺少必需参数 csv_file' }],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = ensureDeckPreview(params);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }],
|
|
|
|
|
|
isError: !result.success,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: `未知工具:${name}` }],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
2026-03-17 20:01:04 +08:00
|
|
|
|
}
|
2026-03-18 12:08:28 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
content: [{ type: 'text', text: `工具调用失败:${error instanceof Error ? error.message : '未知错误'}` }],
|
|
|
|
|
|
isError: true,
|
|
|
|
|
|
};
|
2026-03-17 20:01:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-18 12:12:43 +08:00
|
|
|
|
// 处理 Prompts 获取请求
|
|
|
|
|
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
|
|
|
const { name, arguments: args } = request.params;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
switch (name) {
|
|
|
|
|
|
case 'design-card-game': {
|
|
|
|
|
|
const options = args as unknown as DesignCardGameOptions;
|
|
|
|
|
|
const result = designCardGame(options);
|
|
|
|
|
|
return {
|
|
|
|
|
|
description: '引导用户设计新的卡牌游戏系统,定义卡牌模板和字段结构',
|
|
|
|
|
|
messages: result.messages,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'populate-deck': {
|
|
|
|
|
|
const options = args as unknown as PopulateDeckOptions;
|
|
|
|
|
|
const result = populateDeck(options);
|
|
|
|
|
|
return {
|
|
|
|
|
|
description: '为已有的卡牌组生成和填充卡牌内容',
|
|
|
|
|
|
messages: result.messages,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 'setup-deck-display': {
|
|
|
|
|
|
const options = args as unknown as SetupDeckDisplayOptions;
|
|
|
|
|
|
const result = setupDeckDisplay(options);
|
|
|
|
|
|
return {
|
|
|
|
|
|
description: '引导用户配置卡牌的显示参数(尺寸、布局、样式等)',
|
|
|
|
|
|
messages: result.messages,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new Error(`未知 prompt:${name}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-17 20:01:04 +08:00
|
|
|
|
// 启动服务器
|
|
|
|
|
|
if (host === 'stdio') {
|
|
|
|
|
|
const transport = new StdioServerTransport();
|
|
|
|
|
|
await server.connect(transport);
|
|
|
|
|
|
console.error('TTRPG 卡牌生成 MCP 服务器已启动(stdio)');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// TODO: 支持 HTTP 传输
|
|
|
|
|
|
console.error('HTTP 传输模式尚未实现,请使用 stdio 模式');
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 快速生成卡牌组命令处理函数
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function generateCardDeckAction(options: {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
output: string;
|
|
|
|
|
|
count: string;
|
|
|
|
|
|
fields: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
size: string;
|
|
|
|
|
|
grid: string;
|
|
|
|
|
|
}) {
|
|
|
|
|
|
const fieldNames = options.fields.split(',').map(f => f.trim());
|
|
|
|
|
|
|
|
|
|
|
|
const template = {
|
|
|
|
|
|
fields: fieldNames.map((name): CardField => ({
|
|
|
|
|
|
name,
|
|
|
|
|
|
examples: ['示例 1', '示例 2']
|
|
|
|
|
|
}))
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const params: GenerateCardDeckParams = {
|
|
|
|
|
|
deck_name: options.name,
|
|
|
|
|
|
output_dir: options.output,
|
|
|
|
|
|
card_count: parseInt(options.count, 10),
|
|
|
|
|
|
card_template: template,
|
|
|
|
|
|
deck_config: {
|
|
|
|
|
|
size: options.size,
|
|
|
|
|
|
grid: options.grid
|
|
|
|
|
|
},
|
|
|
|
|
|
description: options.description
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const result = generateCardDeck(params);
|
|
|
|
|
|
|
|
|
|
|
|
console.log('\n✅ 卡牌组生成成功!\n');
|
|
|
|
|
|
console.log(result.message);
|
|
|
|
|
|
console.log('\n📦 组件代码:');
|
|
|
|
|
|
console.log(` ${result.deckComponent}\n`);
|
|
|
|
|
|
}
|