176 lines
4.5 KiB
JavaScript
176 lines
4.5 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* bootstrap.js - MetaCraft 自举循环入口
|
|||
|
|
*
|
|||
|
|
* 1. 读取待办任务列表
|
|||
|
|
* 2. 显示可选任务
|
|||
|
|
* 3. 执行选中的任务(调用对应prompt)
|
|||
|
|
* 4. 验证结果并更新任务状态
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
const readline = require('readline');
|
|||
|
|
|
|||
|
|
const TODO_FILE = path.join(__dirname, '../meta/todo.md');
|
|||
|
|
const PROMPTS_DIR = path.join(__dirname, '../prompts');
|
|||
|
|
|
|||
|
|
// 解析待办任务文件
|
|||
|
|
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,
|
|||
|
|
raw: line
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tasks;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新待办任务文件
|
|||
|
|
function updateTodo(tasks, updatedTask) {
|
|||
|
|
// 简化的实现:找到对应行并替换
|
|||
|
|
const content = fs.readFileSync(TODO_FILE, 'utf-8');
|
|||
|
|
const lines = content.split('\n');
|
|||
|
|
for (let i = 0; i < lines.length; i++) {
|
|||
|
|
if (lines[i].includes(updatedTask.description)) {
|
|||
|
|
const newLine = lines[i].replace('- [ ]', '- [x]');
|
|||
|
|
lines[i] = newLine;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
fs.writeFileSync(TODO_FILE, lines.join('\n'), 'utf-8');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找对应prompt文件
|
|||
|
|
function findPromptForTask(task) {
|
|||
|
|
// 简单映射:将任务描述转换为文件名
|
|||
|
|
const name = task.description
|
|||
|
|
.toLowerCase()
|
|||
|
|
.replace(/[^\w\s]/g, '')
|
|||
|
|
.replace(/\s+/g, '-');
|
|||
|
|
const possiblePaths = [
|
|||
|
|
path.join(PROMPTS_DIR, `${name}.md`),
|
|||
|
|
path.join(PROMPTS_DIR, `implement-${name}.md`),
|
|||
|
|
path.join(PROMPTS_DIR, `task-${name}.md`)
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
for (const p of possiblePaths) {
|
|||
|
|
if (fs.existsSync(p)) {
|
|||
|
|
return p;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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 pendingTasks = tasks.filter(t => !t.checked);
|
|||
|
|
|
|||
|
|
if (pendingTasks.length === 0) {
|
|||
|
|
console.log('🎉 所有任务已完成!');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('📋 待办任务列表:');
|
|||
|
|
pendingTasks.forEach((task, index) => {
|
|||
|
|
console.log(` ${index + 1}. [${task.category}] ${task.description}`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const rl = readline.createInterface({
|
|||
|
|
input: process.stdin,
|
|||
|
|
output: process.stdout
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function askQuestion(query) {
|
|||
|
|
return new Promise(resolve => {
|
|||
|
|
rl.question(query, answer => {
|
|||
|
|
resolve(answer);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const answer = await askQuestion('\n请输入要执行的任务编号(或按回车跳过):');
|
|||
|
|
rl.close();
|
|||
|
|
|
|||
|
|
if (!answer.trim()) {
|
|||
|
|
console.log('⏭️ 跳过任务选择');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const taskIndex = parseInt(answer) - 1;
|
|||
|
|
if (isNaN(taskIndex) || taskIndex < 0 || taskIndex >= pendingTasks.length) {
|
|||
|
|
console.error('❌ 无效的选择');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const selectedTask = pendingTasks[taskIndex];
|
|||
|
|
console.log(`\n🎯 选中任务:${selectedTask.description}`);
|
|||
|
|
|
|||
|
|
const promptPath = findPromptForTask(selectedTask);
|
|||
|
|
if (!promptPath) {
|
|||
|
|
console.error(`❌ 未找到对应prompt文件,请在 ${PROMPTS_DIR} 中创建。`);
|
|||
|
|
console.log('提示:prompt文件名应与任务描述相关,如 "implement-prompt-engine.md"');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`📄 找到prompt文件:${promptPath}`);
|
|||
|
|
|
|||
|
|
// 执行prompt
|
|||
|
|
const { main: runPrompt } = require('./run-prompt');
|
|||
|
|
await runPrompt([promptPath]);
|
|||
|
|
|
|||
|
|
// 询问是否标记为完成
|
|||
|
|
const confirm = await new Promise(resolve => {
|
|||
|
|
const confirmRl = readline.createInterface({
|
|||
|
|
input: process.stdin,
|
|||
|
|
output: process.stdout
|
|||
|
|
});
|
|||
|
|
confirmRl.question('\n✅ 任务是否已完成?(y/n) ', answer => {
|
|||
|
|
confirmRl.close();
|
|||
|
|
resolve(answer.toLowerCase() === 'y');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (confirm) {
|
|||
|
|
updateTodo(tasks, selectedTask);
|
|||
|
|
console.log('📝 已更新待办任务状态');
|
|||
|
|
} else {
|
|||
|
|
console.log('⚠️ 任务状态未更新,请手动处理');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (require.main === module) {
|
|||
|
|
main().catch(err => {
|
|||
|
|
console.error('自举循环错误:', err);
|
|||
|
|
process.exit(1);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = { parseTodo, findPromptForTask };
|