guanghulab/scripts/task-tree-manager.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

425 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 };