UNPKG

article-writer-cn

Version:

AI 驱动的智能写作系统 - 专注公众号/自媒体文章创作

230 lines 8.25 kB
/** * 工作区检测和管理工具 */ import * as fs from 'fs-extra'; import * as path from 'path'; /** * 加载工作区配置 */ export async function loadWorkspaceConfig() { const configPath = path.join(__dirname, '../../templates/workspace-config.json'); const config = await fs.readJSON(configPath); return config.workspaces; } /** * 自动检测当前项目的工作区类型 */ export async function detectWorkspace(projectPath) { const configs = await loadWorkspaceConfig(); // 规则1: 检查目录结构 const workspaceFromDir = await detectFromDirectory(projectPath); if (workspaceFromDir) { return { workspace: workspaceFromDir, confidence: 'high', reason: `检测到项目位于 workspaces/${workspaceFromDir}/ 目录`, config: configs[workspaceFromDir] }; } // 规则2: 检查brief文件 const workspaceFromBrief = await detectFromBrief(projectPath); if (workspaceFromBrief) { return { workspace: workspaceFromBrief, confidence: 'high', reason: 'Brief文件中明确指定了平台类型', config: configs[workspaceFromBrief] }; } // 规则3: 检查specification文件 const workspaceFromSpec = await detectFromSpecification(projectPath); if (workspaceFromSpec) { return { workspace: workspaceFromSpec, confidence: 'medium', reason: 'Specification文件中包含特定工作区的特征标记', config: configs[workspaceFromSpec] }; } // 默认: general return { workspace: 'general', confidence: 'low', reason: '未检测到明确的工作区特征,使用通用工作区', config: configs.general }; } /** * 从目录结构检测 */ async function detectFromDirectory(projectPath) { // 检查项目路径是否包含 workspaces/<type>/ if (projectPath.includes('workspaces/wechat')) { return 'wechat'; } if (projectPath.includes('workspaces/video')) { return 'video'; } if (projectPath.includes('workspaces/general')) { return 'general'; } return null; } /** * 从Brief文件检测 */ async function detectFromBrief(projectPath) { try { // 查找_briefs目录 const briefsDir = path.join(projectPath, '_briefs'); if (!await fs.pathExists(briefsDir)) { // 尝试在父目录查找 const parentBriefsDir = path.join(projectPath, '..', '..', '_briefs'); if (!await fs.pathExists(parentBriefsDir)) { return null; } } // 读取最新的brief文件 (简化实现,实际应该读取对应项目的brief) const briefFiles = await fs.readdir(briefsDir || path.join(projectPath, '..', '..', '_briefs')); if (briefFiles.length === 0) { return null; } // 读取第一个brief文件 const briefPath = path.join(briefsDir || path.join(projectPath, '..', '..', '_briefs'), briefFiles[0]); const briefContent = await fs.readFile(briefPath, 'utf-8'); // 检测平台关键词 const platformMapping = { '公众号': 'wechat', '微信': 'wechat', 'wechat': 'wechat', '视频': 'video', 'YouTube': 'video', 'B站': 'video', 'bilibili': 'video', '抖音': 'video' }; for (const [keyword, workspace] of Object.entries(platformMapping)) { if (briefContent.includes(keyword)) { return workspace; } } return null; } catch (error) { return null; } } /** * 从Specification文件检测 */ async function detectFromSpecification(projectPath) { try { const specPath = path.join(projectPath, 'specification.md'); if (!await fs.pathExists(specPath)) { return null; } const specContent = await fs.readFile(specPath, 'utf-8'); // 检测视频脚本特征 if (specContent.includes('[画面]') || specContent.includes('[镜头]') || specContent.includes('分镜')) { return 'video'; } // 检测公众号特征 if (specContent.includes('封面图') || specContent.includes('公众号')) { return 'wechat'; } return null; } catch (error) { return null; } } /** * 获取工作区的推荐配置 */ export async function getWorkspaceRecommendations(workspace) { const configs = await loadWorkspaceConfig(); const config = configs[workspace]; const recommendations = { wechat: { briefTips: [ '字数建议: 800-3000字(根据主题深度调整)', '必须准备封面图(900x500px或2:1比例)', '注意敏感词限制,尤其是政治、宗教、医疗相关', '考虑是否需要"阅读原文"链接' ], writingTips: [ '段落控制在150字以内,提升手机阅读体验', '开头3句话决定打开率,务必吸引人', '适度使用emoji增强表现力(但不要过度)', '结尾引导互动(点赞/在看/转发)' ], auditTips: [ '检查是否有微信敏感词(可能导致不推荐)', '确保AI味 < 30%,否则可能被判定为低质内容', '检查外链是否可访问(微信对外链有限制)', '测试在手机端的阅读体验' ] }, video: { briefTips: [ '明确视频时长(1分钟约150-180字口播)', '确定目标平台(YouTube/B站/抖音风格差异大)', '考虑是否需要字幕(影响脚本格式)', '预留分镜和画面提示的空间' ], writingTips: [ '极度口语化,写完后大声读一遍看是否顺口', '句子要短(< 30字),适合口播节奏', '标注画面提示 [画面: XX]、重点强调 [重点]', '前3秒Hook要抓人,否则观众会划走' ], auditTips: [ 'AI味要求更低(< 20%),因为口语化要求高', '检查是否有难读的词语或绕口的句子', '确认分镜标注清晰,方便拍摄团队理解', '测试口播节奏(可以自己录音试听)' ] }, general: { briefTips: [ '明确目标平台(知乎/博客/Medium风格不同)', '字数灵活,根据主题深度决定(500-5000字)', '考虑SEO需求(如果是个人博客)', '确定是否需要配图' ], writingTips: [ '风格根据主题调整(技术文偏正式,生活类可轻松)', '使用Markdown标准格式,方便多平台发布', '长文章建议添加目录', '注意段落间的逻辑衔接' ], auditTips: [ 'AI味目标 < 30%', '检查格式在不同平台的兼容性', '确认链接和引用格式正确', '根据目标平台调整风格' ] } }; return recommendations[workspace]; } /** * 验证当前项目是否符合工作区要求 */ export async function validateWorkspace(projectPath, expectedWorkspace) { const detection = await detectWorkspace(projectPath); const warnings = []; if (detection.workspace !== expectedWorkspace) { warnings.push(`⚠️ 检测到的工作区(${detection.workspace})与预期(${expectedWorkspace})不符`); warnings.push(` 原因: ${detection.reason}`); warnings.push(` 建议: 检查项目路径或brief文件中的平台设置`); } return { valid: detection.workspace === expectedWorkspace, warnings }; } //# sourceMappingURL=workspace.js.map