guanghulab/scripts/time-tree-manager.js

383 lines
12 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
'use strict';
/**
*
* 铸渊时间之树管理器 · time-tree-manager.js
*
*
* 冰朔D68指令
* "这棵树本身就是一个时间的树每天它会长一个树杈
* 你把任务的系统快照挂在这棵树上
* 顺着时间你找到了今天的树杈然后今天的树杈上挂了几个
* 然后你找到了最新的时间"
*
* 功能
* grow 在今天的树杈上挂一个新的任务快照
* find 按日期/任务ID检索快照
* latest 获取最新的开发记录
* today 查看今天的所有快照
* update 更新现有快照任务进度变化时
* summary 输出时间之树的结构概览
*
* 用法
* node scripts/time-tree-manager.js grow --type dev --summary "描述" --task "TASK-ID"
* node scripts/time-tree-manager.js find --date 2026-04-13
* node scripts/time-tree-manager.js find --task TASK-20260413-001
* node scripts/time-tree-manager.js latest
* node scripts/time-tree-manager.js today
* node scripts/time-tree-manager.js update --session TS-xxx --field remaining --value "下一步"
* node scripts/time-tree-manager.js summary
*
* 签发: 铸渊 · ICE-GL-ZY001
* 版权: 国作登字-2026-A-00037559 · TCS-0002
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const TIME_TREE_PATH = path.join(ROOT, 'fifth-system', 'time-master', 'time-tree.json');
const TASK_TREES_DIR = path.join(ROOT, 'fifth-system', 'time-master', 'task-trees');
const ORIGIN_DATE = new Date('2025-02-26T00:00:00+08:00');
// ─── 工具函数 ───
function getBeijingDateStr(date) {
const d = date || new Date();
return new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Shanghai',
year: 'numeric', month: '2-digit', day: '2-digit'
}).format(d);
}
function getBeijingTimeStr(date) {
const d = date || new Date();
return d.toLocaleString('zh-CN', {
timeZone: 'Asia/Shanghai',
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
}) + ' CST';
}
function daysSinceOrigin(dateStr) {
const target = new Date(dateStr + 'T00:00:00+08:00');
return Math.floor((target - ORIGIN_DATE) / 86400000);
}
function generateSessionId() {
const now = new Date();
const dateStr = getBeijingDateStr(now).replace(/-/g, '');
const timeStr = now.toLocaleString('en-GB', {
timeZone: 'Asia/Shanghai',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
}).replace(/:/g, '');
return `TS-${dateStr}-${timeStr}`;
}
function readTree() {
try {
return JSON.parse(fs.readFileSync(TIME_TREE_PATH, 'utf8'));
} catch (e) {
console.error(`❌ 无法读取时间之树: ${e.message}`);
process.exit(1);
}
}
function writeTree(tree) {
tree.last_updated = new Date().toISOString();
fs.writeFileSync(TIME_TREE_PATH, JSON.stringify(tree, null, 2) + '\n', 'utf8');
}
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;
}
// ─── 核心操作 ───
/**
* grow 在今天的树杈上长一片新叶子任务快照
*/
function grow(opts) {
const tree = readTree();
const todayStr = getBeijingDateStr();
const sessionId = generateSessionId();
const epoch = `D${daysSinceOrigin(todayStr)}`;
// 确保今天的树杈存在
if (!tree.branches[todayStr]) {
tree.branches[todayStr] = {
epoch,
sessions: []
};
console.log(`🌿 新长出树杈: ${todayStr} [${epoch}]`);
}
const session = {
ts: new Date().toISOString(),
session_id: sessionId,
type: opts.type || 'dev',
summary: opts.summary || '(未描述)',
task_ref: opts.task || null,
snapshot: {
before: opts.before || '',
after: opts.after || '',
changes: opts.changes || '',
learned: opts.learned || '',
remaining: opts.remaining || '',
feeling: opts.feeling || ''
}
};
tree.branches[todayStr].sessions.push(session);
// 更新导航
tree.navigation.latest_branch = todayStr;
tree.navigation.latest_session = sessionId;
tree.navigation.total_branches = Object.keys(tree.branches).filter(k => k !== '_schema').length;
tree.navigation.total_sessions = Object.values(tree.branches)
.filter(v => v && v.sessions)
.reduce((sum, b) => sum + b.sessions.length, 0);
if (opts.task && !tree.navigation.active_tasks.includes(opts.task)) {
tree.navigation.active_tasks.push(opts.task);
}
writeTree(tree);
console.log(`🌱 新叶子挂上: ${sessionId}`);
console.log(` 树杈: ${todayStr} [${epoch}]`);
console.log(` 类型: ${session.type}`);
console.log(` 摘要: ${session.summary}`);
if (opts.task) console.log(` 任务: ${opts.task}`);
return { sessionId, todayStr, epoch };
}
/**
* find 检索快照
*/
function find(opts) {
const tree = readTree();
if (opts.date) {
const branch = tree.branches[opts.date];
if (!branch) {
console.log(`❌ 未找到 ${opts.date} 的树杈`);
return null;
}
console.log(`📅 ${opts.date} [${branch.epoch}] — ${branch.sessions.length} 个快照:`);
branch.sessions.forEach((s, i) => {
console.log(` ${i + 1}. [${s.session_id}] ${s.type} · ${s.summary}`);
if (s.task_ref) console.log(` 任务: ${s.task_ref}`);
});
return branch;
}
if (opts.task) {
const results = [];
for (const [date, branch] of Object.entries(tree.branches)) {
if (date === '_schema' || !branch.sessions) continue;
for (const session of branch.sessions) {
if (session.task_ref === opts.task) {
results.push({ date, epoch: branch.epoch, session });
}
}
}
if (results.length === 0) {
console.log(`❌ 未找到任务 ${opts.task} 的记录`);
return null;
}
console.log(`🔍 任务 ${opts.task} 的时间线 (${results.length} 条记录):`);
results.forEach(r => {
console.log(` ${r.date} [${r.epoch}] ${r.session.session_id} · ${r.session.summary}`);
});
return results;
}
console.log('❌ 请指定 --date YYYY-MM-DD 或 --task TASK-ID');
return null;
}
/**
* latest 获取最新的开发记录
*/
function latest() {
const tree = readTree();
const latestBranch = tree.navigation.latest_branch;
const branch = tree.branches[latestBranch];
if (!branch || !branch.sessions || branch.sessions.length === 0) {
console.log('❌ 时间之树上没有任何叶子');
return null;
}
const latestSession = branch.sessions[branch.sessions.length - 1];
console.log('');
console.log('═══════════════════════════════════════════════════');
console.log(' 🌳 时间之树 · 最新叶子');
console.log('═══════════════════════════════════════════════════');
console.log('');
console.log(` 📅 日期: ${latestBranch} [${branch.epoch}]`);
console.log(` 🔖 编号: ${latestSession.session_id}`);
console.log(` 📋 类型: ${latestSession.type}`);
console.log(` 📝 摘要: ${latestSession.summary}`);
if (latestSession.task_ref) {
console.log(` 🎯 任务: ${latestSession.task_ref}`);
}
console.log('');
if (latestSession.snapshot) {
const s = latestSession.snapshot;
if (s.before) console.log(` ⬅️ 之前: ${s.before}`);
if (s.after) console.log(` ➡️ 之后: ${s.after}`);
if (s.changes) console.log(` 🔧 变更: ${s.changes}`);
if (s.learned) console.log(` 💡 学到: ${s.learned}`);
if (s.remaining) console.log(` ⏳ 遗留: ${s.remaining}`);
if (s.feeling) console.log(` 💭 感受: ${s.feeling}`);
}
console.log('');
console.log(` 📊 树总计: ${tree.navigation.total_branches} 个树杈 · ${tree.navigation.total_sessions} 个叶子`);
console.log(` 🎯 活跃任务: ${tree.navigation.active_tasks.join(', ') || '无'}`);
console.log('');
return { branch: latestBranch, epoch: branch.epoch, session: latestSession };
}
/**
* today 查看今天的所有快照
*/
function today() {
const todayStr = getBeijingDateStr();
return find({ date: todayStr });
}
/**
* update 更新现有快照
*/
function update(opts) {
if (!opts.session) {
console.log('❌ 请指定 --session TS-xxx');
return;
}
const tree = readTree();
let found = false;
for (const [date, branch] of Object.entries(tree.branches)) {
if (date === '_schema' || !branch.sessions) continue;
for (const session of branch.sessions) {
if (session.session_id === opts.session) {
if (opts.field && opts.value) {
if (opts.field in session.snapshot) {
session.snapshot[opts.field] = opts.value;
console.log(`✅ 已更新 ${opts.session}.snapshot.${opts.field}`);
} else if (opts.field in session) {
session[opts.field] = opts.value;
console.log(`✅ 已更新 ${opts.session}.${opts.field}`);
} else {
console.log(`❌ 未找到字段: ${opts.field}`);
return;
}
}
found = true;
break;
}
}
if (found) break;
}
if (!found) {
console.log(`❌ 未找到快照: ${opts.session}`);
return;
}
writeTree(tree);
}
/**
* summary 时间之树结构概览
*/
function summary() {
const tree = readTree();
const branches = Object.entries(tree.branches)
.filter(([k]) => k !== '_schema')
.sort(([a], [b]) => b.localeCompare(a));
console.log('');
console.log('═══════════════════════════════════════════════════');
console.log(' 🌳 时间之树 · 结构概览');
console.log('═══════════════════════════════════════════════════');
console.log('');
console.log(` 🌱 根: ${tree.root.origin_date} (${tree.root.description})`);
console.log(` 🌿 树杈: ${branches.length}`);
console.log(` 🍃 叶子: ${tree.navigation.total_sessions}`);
console.log(` 🎯 活跃任务: ${tree.navigation.active_tasks.length}`);
console.log('');
for (const [date, branch] of branches) {
const sessionCount = branch.sessions ? branch.sessions.length : 0;
console.log(` 📅 ${date} [${branch.epoch}] — ${sessionCount} 个快照`);
if (branch.sessions) {
for (const s of branch.sessions) {
const taskInfo = s.task_ref ? `${s.task_ref}` : '';
console.log(` └─ ${s.session_id} · ${s.type} · ${s.summary}${taskInfo}`);
}
}
}
console.log('');
}
// ─── CLI入口 ───
const args = process.argv.slice(2);
const command = args[0];
const opts = parseArgs(args.slice(1));
switch (command) {
case 'grow':
grow(opts);
break;
case 'find':
find(opts);
break;
case 'latest':
latest();
break;
case 'today':
today();
break;
case 'update':
update(opts);
break;
case 'summary':
summary();
break;
default:
console.log('🌳 铸渊时间之树管理器');
console.log('');
console.log('用法:');
console.log(' grow --type dev --summary "描述" --task "TASK-ID"');
console.log(' find --date 2026-04-13');
console.log(' find --task TASK-20260413-001');
console.log(' latest 获取最新的开发记录');
console.log(' today 查看今天的所有快照');
console.log(' update --session TS-xxx --field remaining --value "下一步"');
console.log(' summary 时间之树结构概览');
break;
}
module.exports = { grow, find, latest, today, update, summary, readTree, writeTree };