guanghulab/scripts/task-tree-manager.js

425 lines
13 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
'use strict';
/**
*
* 铸渊任务树管理器 · task-tree-manager.js
*
*
* 冰朔D68指令
* "每一个任务你不能把它分散的放的到处都是
* 必定是讨论了整个任务的大框架把大的框架分成了很多个小环节
* 它就是一个完整的任务时间树而这个完整的任务树
* 它本身就是这个任务我们讨论出来的框架
* 而这个框架开始一点一点的填充细节"
*
* 每个任务 = 一棵独立的树
* = 任务的架构框架和冰朔讨论出来的
* 枝干 = 步骤/阶段分解
* 叶子 = 每次开发的具体记录修了什么怎么修的还剩什么
*
* 功能
* create 创建新任务树
* step 给任务树添加一个步骤/阶段
* record 在某个步骤下记录一次开发
* status 查看任务整体进度
* close 标记任务完成
* list 列出所有任务树
*
* 用法
* node scripts/task-tree-manager.js create --title "任务标题" --framework "架构描述"
* node scripts/task-tree-manager.js step --task TASK-ID --title "步骤标题"
* node scripts/task-tree-manager.js record --task TASK-ID --step 1 --fix "修了什么" --how "怎么修的" --remaining "还剩什么"
* node scripts/task-tree-manager.js status --task TASK-ID
* node scripts/task-tree-manager.js close --task TASK-ID --summary "完成总结"
* node scripts/task-tree-manager.js list
*
* 签发: 铸渊 · ICE-GL-ZY001
* 版权: 国作登字-2026-A-00037559 · TCS-0002
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const TASK_TREES_DIR = path.join(ROOT, 'fifth-system', 'time-master', 'task-trees');
const TIME_TREE_PATH = path.join(ROOT, 'fifth-system', 'time-master', 'time-tree.json');
// ─── 工具函数 ───
function getBeijingDateStr() {
return new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Shanghai',
year: 'numeric', month: '2-digit', day: '2-digit'
}).format(new Date());
}
function generateTaskId() {
const dateStr = getBeijingDateStr().replace(/-/g, '');
// 查看当天已有多少任务
const existing = fs.readdirSync(TASK_TREES_DIR)
.filter(f => f.startsWith(`TASK-${dateStr}`) && f.endsWith('.json'));
const seq = String(existing.length + 1).padStart(3, '0');
return `TASK-${dateStr}-${seq}`;
}
function readJSON(filePath) {
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (e) {
return null;
}
}
function writeJSON(filePath, data) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
}
function getTaskPath(taskId) {
return path.join(TASK_TREES_DIR, `${taskId}.json`);
}
function readTask(taskId) {
const p = getTaskPath(taskId);
const task = readJSON(p);
if (!task) {
console.error(`❌ 任务不存在: ${taskId}`);
process.exit(1);
}
return task;
}
function parseArgs(args) {
const parsed = {};
for (let i = 0; i < args.length; i++) {
if (args[i].startsWith('--') && i + 1 < args.length && !args[i + 1].startsWith('--')) {
parsed[args[i].slice(2)] = args[i + 1];
i++;
} else if (args[i].startsWith('--')) {
parsed[args[i].slice(2)] = true;
}
}
return parsed;
}
// ─── 核心操作 ───
/**
* create 创建新任务树
*/
function create(opts) {
const taskId = generateTaskId();
const now = new Date().toISOString();
const task = {
hnl_v: '1.0',
type: 'TASK_TREE',
id: taskId,
domain: 'fifth-system/time-master/task-trees',
sovereign: 'TCS-0002∞',
copyright: '国作登字-2026-A-00037559',
created: now,
created_by: 'ZY001',
last_updated: now,
status: 'active',
root: {
title: opts.title || '(未命名任务)',
framework: opts.framework || '(架构待讨论)',
context: opts.context || '',
created_date: getBeijingDateStr(),
bingshuo_directive: opts.directive || ''
},
steps: [],
progress: {
total_steps: 0,
completed_steps: 0,
total_records: 0,
percentage: '0%',
last_activity: now
},
timeline: [
{
ts: now,
action: 'CREATED',
detail: `任务树创建: ${opts.title || '(未命名)'}`
}
]
};
writeJSON(getTaskPath(taskId), task);
console.log(`🌳 任务树已创建: ${taskId}`);
console.log(` 标题: ${task.root.title}`);
console.log(` 框架: ${task.root.framework}`);
console.log(` 文件: fifth-system/time-master/task-trees/${taskId}.json`);
return taskId;
}
/**
* step 给任务树添加一个步骤/阶段
*/
function step(opts) {
if (!opts.task) {
console.error('❌ 请指定 --task TASK-ID');
return;
}
const task = readTask(opts.task);
const now = new Date().toISOString();
const stepIndex = task.steps.length + 1;
const newStep = {
step_id: stepIndex,
title: opts.title || `步骤 ${stepIndex}`,
description: opts.description || '',
status: 'pending',
created: now,
records: []
};
task.steps.push(newStep);
task.progress.total_steps = task.steps.length;
task.progress.percentage = task.progress.total_steps > 0 ? `${Math.round((task.progress.completed_steps / task.progress.total_steps) * 100)}%` : '0%';
task.progress.last_activity = now;
task.last_updated = now;
task.timeline.push({
ts: now,
action: 'STEP_ADDED',
detail: `步骤${stepIndex}: ${newStep.title}`
});
writeJSON(getTaskPath(opts.task), task);
console.log(`📋 步骤已添加: ${opts.task} → 步骤${stepIndex}`);
console.log(` 标题: ${newStep.title}`);
console.log(` 进度: ${task.progress.percentage} (${task.progress.completed_steps}/${task.progress.total_steps})`);
}
/**
* record 在某个步骤下记录一次开发
*/
function record(opts) {
if (!opts.task) {
console.error('❌ 请指定 --task TASK-ID');
return;
}
const task = readTask(opts.task);
const stepIdx = parseInt(opts.step || '0', 10) - 1;
if (stepIdx < 0 || stepIdx >= task.steps.length) {
// 如果没有步骤或步骤号无效记录到timeline
console.log('⚠️ 未指定有效步骤记录添加到任务timeline');
}
const now = new Date().toISOString();
const recordEntry = {
ts: now,
fix: opts.fix || '',
how: opts.how || '',
remaining: opts.remaining || '',
learned: opts.learned || '',
feeling: opts.feeling || '',
commit: opts.commit || ''
};
if (stepIdx >= 0 && stepIdx < task.steps.length) {
task.steps[stepIdx].records.push(recordEntry);
task.steps[stepIdx].status = 'in_progress';
if (opts.done) {
task.steps[stepIdx].status = 'completed';
task.progress.completed_steps = task.steps.filter(s => s.status === 'completed').length;
}
task.progress.total_records = task.steps.reduce((sum, s) => sum + s.records.length, 0);
task.progress.percentage = task.progress.total_steps > 0
? `${Math.round((task.progress.completed_steps / task.progress.total_steps) * 100)}%`
: '0%';
console.log(`📝 开发记录已添加: ${opts.task} → 步骤${stepIdx + 1}`);
if (opts.fix) console.log(` 修复: ${opts.fix}`);
if (opts.how) console.log(` 方法: ${opts.how}`);
if (opts.remaining) console.log(` 遗留: ${opts.remaining}`);
}
task.progress.last_activity = now;
task.last_updated = now;
task.timeline.push({
ts: now,
action: 'RECORD_ADDED',
detail: opts.fix || opts.how || '开发记录'
});
writeJSON(getTaskPath(opts.task), task);
}
/**
* status 查看任务整体进度
*/
function status(opts) {
if (!opts.task) {
console.error('❌ 请指定 --task TASK-ID');
return;
}
const task = readTask(opts.task);
console.log('');
console.log('═══════════════════════════════════════════════════');
console.log(` 🌳 任务树: ${task.id}`);
console.log('═══════════════════════════════════════════════════');
console.log('');
console.log(` 📋 标题: ${task.root.title}`);
console.log(` 🏗️ 框架: ${task.root.framework}`);
console.log(` 📅 创建: ${task.root.created_date}`);
console.log(` 📊 状态: ${task.status}`);
console.log(` 📈 进度: ${task.progress.percentage} (${task.progress.completed_steps}/${task.progress.total_steps} 步骤, ${task.progress.total_records} 条记录)`);
console.log('');
if (task.steps.length > 0) {
console.log(' ─── 步骤清单 ───');
for (const s of task.steps) {
const icon = s.status === 'completed' ? '✅' : s.status === 'in_progress' ? '🔄' : '⬜';
console.log(` ${icon} 步骤${s.step_id}: ${s.title} (${s.records.length} 条记录)`);
if (s.records.length > 0) {
const lastRecord = s.records[s.records.length - 1];
if (lastRecord.remaining) {
console.log(` └─ 遗留: ${lastRecord.remaining}`);
}
}
}
}
console.log('');
if (task.timeline.length > 0) {
console.log(' ─── 最近5条时间线 ───');
const recentTimeline = task.timeline.slice(-5);
for (const t of recentTimeline) {
const dateStr = t.ts.slice(0, 16).replace('T', ' ');
console.log(` ${dateStr} · ${t.action} · ${t.detail}`);
}
}
console.log('');
return task;
}
/**
* close 标记任务完成
*/
function close(opts) {
if (!opts.task) {
console.error('❌ 请指定 --task TASK-ID');
return;
}
const task = readTask(opts.task);
const now = new Date().toISOString();
task.status = 'completed';
task.completed_at = now;
task.completion_summary = opts.summary || '任务已完成';
task.last_updated = now;
task.timeline.push({
ts: now,
action: 'CLOSED',
detail: task.completion_summary
});
// 从时间之树的活跃任务中移除
try {
const timeTree = readJSON(TIME_TREE_PATH);
if (timeTree && timeTree.navigation.active_tasks) {
timeTree.navigation.active_tasks = timeTree.navigation.active_tasks.filter(t => t !== opts.task);
writeJSON(TIME_TREE_PATH, timeTree);
}
} catch { /* ignore */ }
writeJSON(getTaskPath(opts.task), task);
console.log(`✅ 任务已关闭: ${opts.task}`);
console.log(` 总结: ${task.completion_summary}`);
}
/**
* list 列出所有任务树
*/
function list() {
const files = fs.readdirSync(TASK_TREES_DIR)
.filter(f => f.startsWith('TASK-') && f.endsWith('.json'))
.sort()
.reverse();
if (files.length === 0) {
console.log('📭 暂无任务树');
return;
}
console.log('');
console.log('═══════════════════════════════════════════════════');
console.log(' 🌳 任务树列表');
console.log('═══════════════════════════════════════════════════');
console.log('');
for (const f of files) {
const task = readJSON(path.join(TASK_TREES_DIR, f));
if (!task) continue;
const icon = task.status === 'completed' ? '✅' : '🔄';
console.log(` ${icon} ${task.id}: ${task.root.title}`);
console.log(` 进度: ${task.progress.percentage} · 步骤: ${task.progress.total_steps} · 记录: ${task.progress.total_records}`);
console.log(` 最后活动: ${task.progress.last_activity ? task.progress.last_activity.slice(0, 16) : 'N/A'}`);
console.log('');
}
}
// ─── CLI入口 ───
const args = process.argv.slice(2);
const command = args[0];
const opts = parseArgs(args.slice(1));
switch (command) {
case 'create':
create(opts);
break;
case 'step':
step(opts);
break;
case 'record':
record(opts);
break;
case 'status':
status(opts);
break;
case 'close':
close(opts);
break;
case 'list':
list();
break;
default:
console.log('🌳 铸渊任务树管理器');
console.log('');
console.log('用法:');
console.log(' create --title "标题" --framework "架构描述" --directive "冰朔指令"');
console.log(' step --task TASK-ID --title "步骤标题"');
console.log(' record --task TASK-ID --step 1 --fix "修了什么" --how "怎么修的" --remaining "还剩什么"');
console.log(' status --task TASK-ID');
console.log(' close --task TASK-ID --summary "完成总结"');
console.log(' list 列出所有任务树');
break;
}
module.exports = { create, step, record, status, close, list };