393 lines
11 KiB
JavaScript
393 lines
11 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* plan-next.js - 规划下一步功能
|
|||
|
|
*
|
|||
|
|
* 1. 分析当前项目状态(待办任务、已完成任务)
|
|||
|
|
* 2. 根据优先级推荐下一个要完成的任务
|
|||
|
|
* 3. 生成对应的prompt文件或下一步文档
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const TODO_FILE = path.join(__dirname, '../meta/todo.md');
|
|||
|
|
const PROMPTS_DIR = path.join(__dirname, '../prompts');
|
|||
|
|
const OUTPUT_DIR = path.join(__dirname, '../docs/next-steps');
|
|||
|
|
|
|||
|
|
// 任务优先级映射
|
|||
|
|
const PRIORITY_MAP = {
|
|||
|
|
'核心功能开发': 'high',
|
|||
|
|
'基础设施': 'medium',
|
|||
|
|
'文档': 'medium',
|
|||
|
|
'测试与质量': 'medium',
|
|||
|
|
'未来功能': 'low'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 解析待办任务文件
|
|||
|
|
function parseTodo(content) {
|
|||
|
|
const lines = content.split('\n');
|
|||
|
|
const tasks = [];
|
|||
|
|
let currentCategory = '';
|
|||
|
|
|
|||
|
|
for (const line of lines) {
|
|||
|
|
const categoryMatch = line.match(/^##\s+(.+)/);
|
|||
|
|
if (categoryMatch) {
|
|||
|
|
currentCategory = categoryMatch[1].trim();
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const taskMatch = line.match(/^- \[( |x)\]\s+(.+)/);
|
|||
|
|
if (taskMatch) {
|
|||
|
|
const checked = taskMatch[1] === 'x';
|
|||
|
|
const description = taskMatch[2].trim();
|
|||
|
|
tasks.push({
|
|||
|
|
category: currentCategory,
|
|||
|
|
description,
|
|||
|
|
checked,
|
|||
|
|
priority: PRIORITY_MAP[currentCategory] || 'medium',
|
|||
|
|
raw: line
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tasks;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 分析项目状态并推荐下一个任务
|
|||
|
|
function recommendNextTask(tasks) {
|
|||
|
|
// 只考虑未完成的任务
|
|||
|
|
const pendingTasks = tasks.filter(t => !t.checked);
|
|||
|
|
|
|||
|
|
if (pendingTasks.length === 0) {
|
|||
|
|
return {
|
|||
|
|
recommended: null,
|
|||
|
|
message: '🎉 所有任务已完成!没有待推荐的任务。'
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按优先级排序:high > medium > low
|
|||
|
|
const priorityOrder = { high: 1, medium: 2, low: 3 };
|
|||
|
|
pendingTasks.sort((a, b) => {
|
|||
|
|
if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
|
|||
|
|
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|||
|
|
}
|
|||
|
|
// 同优先级按类别排序
|
|||
|
|
const categoryOrder = ['核心功能开发', '基础设施', '文档', '测试与质量', '未来功能'];
|
|||
|
|
return categoryOrder.indexOf(a.category) - categoryOrder.indexOf(b.category);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const recommendedTask = pendingTasks[0];
|
|||
|
|
|
|||
|
|
// 计算统计数据
|
|||
|
|
const totalTasks = tasks.length;
|
|||
|
|
const completedTasks = tasks.filter(t => t.checked).length;
|
|||
|
|
const pendingByCategory = {};
|
|||
|
|
pendingTasks.forEach(task => {
|
|||
|
|
pendingByCategory[task.category] = (pendingByCategory[task.category] || 0) + 1;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
recommended: recommendedTask,
|
|||
|
|
stats: {
|
|||
|
|
totalTasks,
|
|||
|
|
completedTasks,
|
|||
|
|
pendingTasks: pendingTasks.length,
|
|||
|
|
completionRate: totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0,
|
|||
|
|
pendingByCategory
|
|||
|
|
},
|
|||
|
|
message: `🎯 推荐下一个任务:${recommendedTask.description} (优先级: ${recommendedTask.priority}, 类别: ${recommendedTask.category})`
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 为推荐的任务生成prompt文件
|
|||
|
|
function generatePromptForTask(task) {
|
|||
|
|
if (!task) return null;
|
|||
|
|
|
|||
|
|
// 生成文件名:将任务描述转换为kebab-case
|
|||
|
|
// 保留中文字符、英文字母、数字,将其他字符替换为空格,然后转换为连字符
|
|||
|
|
const fileName = task.description
|
|||
|
|
.toLowerCase()
|
|||
|
|
// 保留中文字符 (\u4e00-\u9fff)、英文字母、数字、空格
|
|||
|
|
.replace(/[^\u4e00-\u9fffa-z0-9\s]/g, ' ')
|
|||
|
|
// 将连续的多个空格替换为单个空格
|
|||
|
|
.replace(/\s+/g, ' ')
|
|||
|
|
.trim()
|
|||
|
|
// 将空格替换为连字符
|
|||
|
|
.replace(/\s/g, '-')
|
|||
|
|
.replace(/^implement-/, '') // 如果以implement-开头,去掉它
|
|||
|
|
+ '.md';
|
|||
|
|
|
|||
|
|
const filePath = path.join(PROMPTS_DIR, fileName);
|
|||
|
|
|
|||
|
|
// 生成prompt内容
|
|||
|
|
const promptContent = `# ${task.description}
|
|||
|
|
|
|||
|
|
<!-- target: ${task.description.toLowerCase().replace(/[^\u4e00-\u9fffa-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim().replace(/\s/g, '-').replace(/^implement-/, '')} -->
|
|||
|
|
<!-- priority: ${task.priority} -->
|
|||
|
|
<!-- category: ${task.category} -->
|
|||
|
|
|
|||
|
|
## 描述
|
|||
|
|
详细描述${task.description}的功能需求和实现目标。
|
|||
|
|
|
|||
|
|
## 要求
|
|||
|
|
1. 明确的功能边界和交付物
|
|||
|
|
2. 与现有系统集成的考虑
|
|||
|
|
3. 代码质量标准和最佳实践
|
|||
|
|
4. 必要的测试覆盖
|
|||
|
|
|
|||
|
|
## 上下文
|
|||
|
|
当前项目状态:
|
|||
|
|
- 项目:MetaCraft (AI驱动的元开发工具)
|
|||
|
|
- 阶段:早期开发
|
|||
|
|
- 已完成任务:${task.checked ? '是' : '否'}
|
|||
|
|
- 优先级:${task.priority}
|
|||
|
|
|
|||
|
|
相关依赖和前置条件:
|
|||
|
|
(根据任务分析添加具体依赖)
|
|||
|
|
|
|||
|
|
## 输出
|
|||
|
|
请提供:
|
|||
|
|
1. 完整的设计方案
|
|||
|
|
2. 具体的实现代码
|
|||
|
|
3. 必要的测试用例
|
|||
|
|
4. 文档更新建议
|
|||
|
|
|
|||
|
|
## 约束
|
|||
|
|
- 保持与现有代码风格一致
|
|||
|
|
- 考虑可扩展性和维护性
|
|||
|
|
- 遵循项目架构设计原则`;
|
|||
|
|
|
|||
|
|
// 确保prompts目录存在
|
|||
|
|
if (!fs.existsSync(PROMPTS_DIR)) {
|
|||
|
|
fs.mkdirSync(PROMPTS_DIR, { recursive: true });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 写入文件
|
|||
|
|
fs.writeFileSync(filePath, promptContent, 'utf-8');
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
filePath,
|
|||
|
|
fileName,
|
|||
|
|
promptContent
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成下一步文档
|
|||
|
|
function generateNextStepDocument(task, stats) {
|
|||
|
|
if (!task) return null;
|
|||
|
|
|
|||
|
|
// 确保输出目录存在
|
|||
|
|
if (!fs.existsSync(OUTPUT_DIR)) {
|
|||
|
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const timestamp = new Date().toISOString().split('T')[0];
|
|||
|
|
const docFileName = `next-step-${timestamp}.md`;
|
|||
|
|
const docFilePath = path.join(OUTPUT_DIR, docFileName);
|
|||
|
|
|
|||
|
|
const docContent = `# 下一步规划:${task.description}
|
|||
|
|
|
|||
|
|
生成时间:${new Date().toLocaleString()}
|
|||
|
|
|
|||
|
|
## 项目状态概览
|
|||
|
|
- 总任务数:${stats.totalTasks}
|
|||
|
|
- 已完成:${stats.completedTasks}
|
|||
|
|
- 待完成:${stats.pendingTasks}
|
|||
|
|
- 完成率:${stats.completionRate}%
|
|||
|
|
|
|||
|
|
### 按类别待完成任务:
|
|||
|
|
${Object.entries(stats.pendingByCategory).map(([cat, count]) => `- ${cat}: ${count}个`).join('\n')}
|
|||
|
|
|
|||
|
|
## 推荐的下一个任务
|
|||
|
|
**任务描述**:${task.description}
|
|||
|
|
|
|||
|
|
**优先级**:${task.priority}
|
|||
|
|
|
|||
|
|
**类别**:${task.category}
|
|||
|
|
|
|||
|
|
## 任务分析
|
|||
|
|
|
|||
|
|
### 为什么选择这个任务?
|
|||
|
|
1. **优先级最高**:属于"${task.category}"类别,在当前开发阶段最为关键
|
|||
|
|
2. **依赖关系**:可能是其他任务的前置条件
|
|||
|
|
3. **价值最大**:完成此任务将为后续开发奠定基础
|
|||
|
|
|
|||
|
|
### 预期影响
|
|||
|
|
- ✅ 推动核心功能开发进度
|
|||
|
|
- ✅ 为后续任务扫清障碍
|
|||
|
|
- ✅ 验证工具链的有效性
|
|||
|
|
|
|||
|
|
## 详细需求
|
|||
|
|
|
|||
|
|
### 功能要求
|
|||
|
|
1. 明确的功能定义和边界
|
|||
|
|
2. 与现有系统的集成点
|
|||
|
|
3. 输入/输出接口规范
|
|||
|
|
4. 错误处理和边界情况
|
|||
|
|
|
|||
|
|
### 技术实现考虑
|
|||
|
|
1. 代码结构和模块设计
|
|||
|
|
2. 性能和安全要求
|
|||
|
|
3. 测试策略和覆盖目标
|
|||
|
|
4. 文档和示例代码
|
|||
|
|
|
|||
|
|
## 验收标准
|
|||
|
|
- [ ] 功能实现完整且符合需求
|
|||
|
|
- [ ] 代码质量符合项目标准
|
|||
|
|
- [ ] 测试用例覆盖主要场景
|
|||
|
|
- [ ] 文档更新及时准确
|
|||
|
|
|
|||
|
|
## 时间规划建议
|
|||
|
|
**阶段1:设计与规划** (1-2天)
|
|||
|
|
- 详细需求分析
|
|||
|
|
- 技术方案设计
|
|||
|
|
- 接口定义和原型验证
|
|||
|
|
|
|||
|
|
**阶段2:实现与测试** (2-3天)
|
|||
|
|
- 核心功能实现
|
|||
|
|
- 单元测试和集成测试
|
|||
|
|
- 代码审查和优化
|
|||
|
|
|
|||
|
|
**阶段3:集成与文档** (1天)
|
|||
|
|
- 系统集成和验证
|
|||
|
|
- 文档编写和示例
|
|||
|
|
- 部署和发布准备
|
|||
|
|
|
|||
|
|
## 风险与缓解
|
|||
|
|
### 技术风险
|
|||
|
|
- 与现有系统兼容性问题
|
|||
|
|
- 性能瓶颈或扩展性限制
|
|||
|
|
|
|||
|
|
### 缓解措施
|
|||
|
|
- 提前进行技术验证和原型测试
|
|||
|
|
- 分阶段实现,及时反馈和调整
|
|||
|
|
|
|||
|
|
## 后续任务建议
|
|||
|
|
完成此任务后,建议考虑:
|
|||
|
|
1. **${task.category}**中的其他高优先级任务
|
|||
|
|
2. 依赖此任务的下游功能开发
|
|||
|
|
3. 相关文档和测试完善
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*本文档由MetaCraft的"规划下一步"功能自动生成*`;
|
|||
|
|
|
|||
|
|
fs.writeFileSync(docFilePath, docContent, 'utf-8');
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
docFilePath,
|
|||
|
|
docFileName,
|
|||
|
|
docContent
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
console.log('🚀 MetaCraft 规划下一步功能启动\n');
|
|||
|
|
|
|||
|
|
// 读取待办任务文件
|
|||
|
|
if (!fs.existsSync(TODO_FILE)) {
|
|||
|
|
console.error('❌ 待办任务文件不存在:', TODO_FILE);
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const todoContent = fs.readFileSync(TODO_FILE, 'utf-8');
|
|||
|
|
const tasks = parseTodo(todoContent);
|
|||
|
|
|
|||
|
|
// 推荐下一个任务
|
|||
|
|
const recommendation = recommendNextTask(tasks);
|
|||
|
|
console.log(recommendation.message);
|
|||
|
|
|
|||
|
|
if (recommendation.recommended) {
|
|||
|
|
// 显示统计信息
|
|||
|
|
console.log('\n📊 项目状态统计:');
|
|||
|
|
console.log(` 总任务数:${recommendation.stats.totalTasks}`);
|
|||
|
|
console.log(` 已完成:${recommendation.stats.completedTasks}`);
|
|||
|
|
console.log(` 待完成:${recommendation.stats.pendingTasks}`);
|
|||
|
|
console.log(` 完成率:${recommendation.stats.completionRate}%`);
|
|||
|
|
|
|||
|
|
console.log('\n📈 按类别待完成任务:');
|
|||
|
|
Object.entries(recommendation.stats.pendingByCategory).forEach(([cat, count]) => {
|
|||
|
|
console.log(` ${cat}: ${count}个`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 询问用户要做什么
|
|||
|
|
const readline = require('readline');
|
|||
|
|
const rl = readline.createInterface({
|
|||
|
|
input: process.stdin,
|
|||
|
|
output: process.stdout
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function askQuestion(query) {
|
|||
|
|
return new Promise(resolve => {
|
|||
|
|
rl.question(query, answer => {
|
|||
|
|
resolve(answer.trim());
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('\n📝 请选择操作:');
|
|||
|
|
console.log(' 1. 为推荐任务生成prompt文件');
|
|||
|
|
console.log(' 2. 生成下一步规划文档');
|
|||
|
|
console.log(' 3. 两者都生成');
|
|||
|
|
console.log(' 4. 只显示推荐,不生成文件');
|
|||
|
|
|
|||
|
|
const choice = await askQuestion('\n请输入选择 (1-4,默认3): ') || '3';
|
|||
|
|
|
|||
|
|
let promptResult = null;
|
|||
|
|
let docResult = null;
|
|||
|
|
|
|||
|
|
switch(choice) {
|
|||
|
|
case '1':
|
|||
|
|
promptResult = generatePromptForTask(recommendation.recommended);
|
|||
|
|
break;
|
|||
|
|
case '2':
|
|||
|
|
docResult = generateNextStepDocument(recommendation.recommended, recommendation.stats);
|
|||
|
|
break;
|
|||
|
|
case '3':
|
|||
|
|
promptResult = generatePromptForTask(recommendation.recommended);
|
|||
|
|
docResult = generateNextStepDocument(recommendation.recommended, recommendation.stats);
|
|||
|
|
break;
|
|||
|
|
case '4':
|
|||
|
|
console.log('\n👌 已跳过文件生成。');
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
console.log(`\n❌ 无效的选择:"${choice}",跳过文件生成。`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 输出生成结果
|
|||
|
|
if (promptResult) {
|
|||
|
|
console.log(`\n✅ 已生成prompt文件:${promptResult.filePath}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (docResult) {
|
|||
|
|
console.log(`\n📄 已生成下一步规划文档:${docResult.docFilePath}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
rl.close();
|
|||
|
|
|
|||
|
|
// 给出后续建议
|
|||
|
|
if (promptResult || docResult) {
|
|||
|
|
console.log('\n💡 后续建议:');
|
|||
|
|
console.log(' 1. 查看生成的文件,根据需要调整内容');
|
|||
|
|
console.log(' 2. 运行 bootstrap.js 执行该任务');
|
|||
|
|
console.log(' 3. 或直接运行 prompt:node scripts/run-prompt.js prompts/...');
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.log(recommendation.message);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (require.main === module) {
|
|||
|
|
main().catch(err => {
|
|||
|
|
console.error('规划下一步功能错误:', err);
|
|||
|
|
process.exit(1);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
parseTodo,
|
|||
|
|
recommendNextTask,
|
|||
|
|
generatePromptForTask,
|
|||
|
|
generateNextStepDocument
|
|||
|
|
};
|