This commit is contained in:
2026-03-18 12:08:28 +08:00
parent eecf429a20
commit 62aff91a86
8 changed files with 1241 additions and 118 deletions
@@ -0,0 +1,127 @@
import { readFileSync, existsSync } from 'fs';
import yaml from 'js-yaml';
/**
* 读取 CSV frontmatter 的参数
*/
export interface ReadFrontmatterParams {
/**
* CSV 文件路径(相对路径相对于 MCP 服务器工作目录)
*/
csv_file: string;
}
/**
* Frontmatter 数据结构
*/
export interface DeckFrontmatter {
/**
* 字段定义
*/
fields?: CardField[];
/**
* Deck 配置
*/
deck?: DeckConfig;
/**
* 其他自定义属性
*/
[key: string]: unknown;
}
/**
* 卡牌字段定义
*/
export interface CardField {
name: string;
description?: string;
examples?: string[];
[key: string]: unknown;
}
/**
* Deck 配置
*/
export interface DeckConfig {
size?: string;
grid?: string;
bleed?: number;
padding?: number;
shape?: 'rectangle' | 'circle' | 'hex' | 'diamond';
layers?: string;
back_layers?: string;
[key: string]: unknown;
}
/**
* 读取 CSV frontmatter 的结果
*/
export interface ReadFrontmatterResult {
success: boolean;
frontmatter?: DeckFrontmatter;
message: string;
}
/**
* 解析 CSV 文件的 frontmatter
*/
function parseFrontMatter(content: string): { frontmatter?: DeckFrontmatter; csvContent: string } {
const parts = content.trim().split(/(?:^|\n)---\s*\n/g);
// 至少需要三个部分:空字符串、front matter、CSV 内容
if (parts.length !== 3 || parts[0] !== '') {
return { csvContent: content };
}
try {
const frontmatterStr = parts[1].trim();
const frontmatter = yaml.load(frontmatterStr) as DeckFrontmatter | undefined;
const csvContent = parts.slice(2).join('---\n').trimStart();
return { frontmatter, csvContent };
} catch (error) {
console.warn('Failed to parse front matter:', error);
return { csvContent: content };
}
}
/**
* 读取 CSV 文件的 frontmatter
*/
export function readFrontmatter(params: ReadFrontmatterParams): ReadFrontmatterResult {
const { csv_file } = params;
// 检查文件是否存在
if (!existsSync(csv_file)) {
return {
success: false,
message: `文件不存在:${csv_file}`
};
}
try {
// 读取文件内容
const content = readFileSync(csv_file, 'utf-8');
// 解析 frontmatter
const { frontmatter } = parseFrontMatter(content);
if (!frontmatter) {
return {
success: true,
frontmatter: {},
message: `文件 ${csv_file} 没有 frontmatter,返回空对象`
};
}
return {
success: true,
frontmatter,
message: `成功读取 ${csv_file} 的 frontmatter`
};
} catch (error) {
return {
success: false,
message: `读取失败:${error instanceof Error ? error.message : '未知错误'}`
};
}
}
@@ -0,0 +1,131 @@
import { readFileSync, writeFileSync, existsSync } from 'fs';
import yaml from 'js-yaml';
import type { DeckFrontmatter } from './read-frontmatter.js';
/**
* 写入 CSV frontmatter 的参数
*/
export interface WriteFrontmatterParams {
/**
* CSV 文件路径(相对路径相对于 MCP 服务器工作目录)
*/
csv_file: string;
/**
* 要写入的 frontmatter 数据
*/
frontmatter: DeckFrontmatter;
/**
* 是否合并现有 frontmatter(默认 true
*/
merge?: boolean;
}
/**
* 写入 CSV frontmatter 的结果
*/
export interface WriteFrontmatterResult {
success: boolean;
message: string;
csv_file?: string;
}
/**
* 解析 CSV 文件的 frontmatter
*/
function parseFrontMatter(content: string): { frontmatter?: DeckFrontmatter; csvContent: string } {
const parts = content.trim().split(/(?:^|\n)---\s*\n/g);
// 至少需要三个部分:空字符串、front matter、CSV 内容
if (parts.length !== 3 || parts[0] !== '') {
return { csvContent: content };
}
try {
const frontmatterStr = parts[1].trim();
const frontmatter = yaml.load(frontmatterStr) as DeckFrontmatter | undefined;
const csvContent = parts.slice(2).join('---\n').trimStart();
return { frontmatter, csvContent };
} catch (error) {
console.warn('Failed to parse front matter:', error);
return { csvContent: content };
}
}
/**
* 序列化 frontmatter 为 YAML 字符串
*/
function serializeFrontMatter(frontmatter: DeckFrontmatter): string {
const yamlStr = yaml.dump(frontmatter, {
indent: 2,
lineWidth: -1, // 不限制行宽
noRefs: true, // 不使用引用
quotingType: '"',
forceQuotes: false
});
return `---\n${yamlStr}---\n`;
}
/**
* 写入/更新 CSV 文件的 frontmatter
*/
export function writeFrontmatter(params: WriteFrontmatterParams): WriteFrontmatterResult {
const { csv_file, frontmatter, merge = true } = params;
let csvContent = '';
let existingFrontmatter: DeckFrontmatter | undefined;
// 如果文件存在且需要合并,先读取现有内容
if (merge && existsSync(csv_file)) {
try {
const content = readFileSync(csv_file, 'utf-8');
const result = parseFrontMatter(content);
existingFrontmatter = result.frontmatter;
csvContent = result.csvContent;
} catch (error) {
return {
success: false,
message: `读取现有文件失败:${error instanceof Error ? error.message : '未知错误'}`
};
}
}
// 合并或替换 frontmatter
const finalFrontmatter = merge && existingFrontmatter
? { ...existingFrontmatter, ...frontmatter }
: frontmatter;
// 序列化 frontmatter
const frontmatterStr = serializeFrontMatter(finalFrontmatter);
// 如果文件不存在或没有 CSV 内容,创建一个空的 CSV 内容
if (!csvContent.trim()) {
// 从 frontmatter 推断 CSV 表头
const headers = ['label'];
if (finalFrontmatter.fields && Array.isArray(finalFrontmatter.fields)) {
for (const field of finalFrontmatter.fields) {
if (field.name && typeof field.name === 'string') {
headers.push(field.name);
}
}
}
headers.push('body');
csvContent = headers.join(',') + '\n';
}
// 写入文件
try {
const fullContent = frontmatterStr + csvContent;
writeFileSync(csv_file, fullContent, 'utf-8');
return {
success: true,
message: `成功写入 frontmatter 到 ${csv_file}`,
csv_file
};
} catch (error) {
return {
success: false,
message: `写入失败:${error instanceof Error ? error.message : '未知错误'}`
};
}
}