#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const DUMP_PATH = path.join(__dirname, '..', 'memory', 'brain-dump.json'); const NUDGE_INTERVALS = { now: 24 * 60 * 60 * 1000, // 1 day soon: 3 * 24 * 60 * 60 * 1000, // 3 days someday: 7 * 24 * 60 * 60 * 1000 // 7 days }; function load() { try { return JSON.parse(fs.readFileSync(DUMP_PATH, 'utf8')); } catch { return { description: "Task tracking", recurring: [], tasks: [] }; } } function save(data) { fs.writeFileSync(DUMP_PATH, JSON.stringify(data, null, 2) + '\n'); } function shortId() { return crypto.randomBytes(4).toString('hex'); } function isDue(task) { const interval = NUDGE_INTERVALS[task.priority] || NUDGE_INTERVALS.someday; const last = task.lastNudged ? new Date(task.lastNudged).getTime() : 0; return Date.now() - last >= interval; } function formatTask(t) { let line = `${t.id}\t${t.priority}\t${t.text}`; if (t.context) line += `\t${t.context}`; if (t.lastNudged) line += `\tnudged:${t.lastNudged}`; return line; } // --- Commands --- function cmdList(args) { const data = load(); let tasks = data.tasks || []; // Filter by priority const prioArg = getFlag(args, '--priority'); if (prioArg) { const prios = prioArg.split(',').map(s => s.trim()); tasks = tasks.filter(t => prios.includes(t.priority)); } // Filter by due if (args.includes('--due')) { tasks = tasks.filter(isDue); } // Sort: now > soon > someday const prioOrder = { now: 0, soon: 1, someday: 2 }; tasks.sort((a, b) => (prioOrder[a.priority] ?? 3) - (prioOrder[b.priority] ?? 3)); // Limit const limitArg = getFlag(args, '--limit'); if (limitArg) { tasks = tasks.slice(0, parseInt(limitArg, 10)); } if (tasks.length === 0) { console.log('No tasks found.'); return; } for (const t of tasks) { console.log(formatTask(t)); } } function cmdAdd(args) { const text = getFlag(args, '--text'); if (!text) { console.error('--text required'); process.exit(1); } const priority = getFlag(args, '--priority') || 'soon'; const context = getFlag(args, '--context') || undefined; const data = load(); const task = { id: shortId(), added: new Date().toISOString().slice(0, 10), text, priority, }; if (context) task.context = context; data.tasks = data.tasks || []; data.tasks.push(task); save(data); console.log(`Added: ${task.id}\t${priority}\t${text}`); } function cmdEdit(args) { const id = args[0]; if (!id) { console.error('Usage: brain-dump edit [--text ...] [--priority ...] [--context ...]'); process.exit(1); } const data = load(); const task = (data.tasks || []).find(t => t.id === id); if (!task) { console.error(`Task ${id} not found.`); process.exit(1); } const text = getFlag(args, '--text'); const priority = getFlag(args, '--priority'); const context = getFlag(args, '--context'); if (text) task.text = text; if (priority) task.priority = priority; if (context !== null && context !== undefined) task.context = context; save(data); console.log(`Updated: ${formatTask(task)}`); } function cmdDone(args) { const id = args[0]; if (!id) { console.error('Usage: brain-dump done '); process.exit(1); } const data = load(); const idx = (data.tasks || []).findIndex(t => t.id === id); if (idx === -1) { console.error(`Task ${id} not found.`); process.exit(1); } const removed = data.tasks.splice(idx, 1)[0]; save(data); console.log(`Done: ${removed.text}`); } function cmdShow(args) { const id = args[0]; if (!id) { console.error('Usage: brain-dump show '); process.exit(1); } const data = load(); const task = (data.tasks || []).find(t => t.id === id); if (!task) { console.error(`Task ${id} not found.`); process.exit(1); } console.log(JSON.stringify(task, null, 2)); } function cmdNudged(args) { const ids = (args[0] || '').split(',').map(s => s.trim()).filter(Boolean); if (ids.length === 0) { console.error('Usage: brain-dump nudged ,,...'); process.exit(1); } const data = load(); const now = new Date().toISOString(); let count = 0; for (const id of ids) { const task = (data.tasks || []).find(t => t.id === id); if (task) { task.lastNudged = now; count++; } } save(data); console.log(`Marked ${count} task(s) as nudged.`); } function cmdRecurring(args) { const data = load(); const when = getFlag(args, '--when'); let items = data.recurring || []; if (when) { items = items.filter(r => r.when === when); } if (items.length === 0) { console.log('No recurring items.'); return; } for (const r of items) { console.log(`${r.id}\t${r.frequency}\t${r.when || '-'}\t${r.text}`); } } // --- Helpers --- function getFlag(args, flag) { const idx = args.indexOf(flag); if (idx === -1) return null; return args[idx + 1] || null; } // --- Main --- const [cmd, ...args] = process.argv.slice(2); switch (cmd) { case 'list': cmdList(args); break; case 'add': cmdAdd(args); break; case 'edit': cmdEdit(args); break; case 'done': cmdDone(args); break; case 'show': cmdShow(args); break; case 'nudged': cmdNudged(args); break; case 'recurring': cmdRecurring(args); break; default: console.log(`Usage: brain-dump Commands: list [--priority now,soon] [--due] [--limit N] add --text "..." --priority soon [--context "..."] edit [--text "..."] [--priority ...] [--context "..."] done show nudged ,,... recurring [--when evening]`); }