UNPKG

flashbacker

Version:

Claude Code state management with session continuity and AI personas

194 lines (190 loc) 7.82 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TodoChainManager = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); /** * TodoChainManager handles persistent TODO_CHAIN.md operations * Based on Cline's FocusChainManager pattern adapted for Flashbacker memory system */ class TodoChainManager { constructor(projectDir) { this.projectDir = projectDir; this.todoChainPath = path_1.default.join(projectDir, '.claude', 'flashback', 'memory', 'TODO_CHAIN.md'); } /** * Get current TODO_CHAIN.md content * Returns null if file doesn't exist */ async getCurrentTodoChain() { try { if (await fs_extra_1.default.pathExists(this.todoChainPath)) { const content = await fs_extra_1.default.readFile(this.todoChainPath, 'utf8'); return content.trim(); } return null; } catch (error) { console.error('Error reading TODO_CHAIN.md:', error); return null; } } /** * Update TODO_CHAIN.md with new content * Creates file if it doesn't exist */ async updateTodoChain(content) { try { // Ensure memory directory exists const memoryDir = path_1.default.dirname(this.todoChainPath); await fs_extra_1.default.ensureDir(memoryDir); // Write TODO_CHAIN.md with proper header const todoChainContent = `# TODO Chain - Persistent Development Tasks > **Context-resistant todo list that survives compaction** > This file maintains development continuity across context resets ## Current Tasks ${content.trim()} --- *Last updated: ${new Date().toLocaleString()}* *Use \`flashback todo-chain --update\` to modify this list* `; await fs_extra_1.default.writeFile(this.todoChainPath, todoChainContent, 'utf8'); } catch (error) { console.error('Error writing TODO_CHAIN.md:', error); throw error; } } /** * Parse progress from todo chain content * Counts completed vs total tasks using markdown checklist format */ parseProgress(todoContent) { try { const lines = todoContent.split('\n'); let total = 0; let completed = 0; for (const line of lines) { const trimmed = line.trim(); // Match standard markdown checklist format: - [ ] or - [x] or - [X] if (trimmed.match(/^-\s*\[[\sxX]\]/)) { total++; // Check if completed (x or X in brackets) if (trimmed.match(/^-\s*\[[xX]\]/)) { completed++; } } } const percentage = total > 0 ? Math.round((completed / total) * 100) : 0; return { completed, total, percentage, }; } catch (error) { console.error('Error parsing todo progress:', error); return { completed: 0, total: 0, percentage: 0, }; } } /** * Generate context summary from memory files and git commits * Combines REMEMBER.md, WORKING_PLAN.md, and recent commits */ async generateContextSummary() { try { const memoryDir = path_1.default.join(this.projectDir, '.claude', 'flashback', 'memory'); let summary = '## Context Summary\n\n'; // Add REMEMBER.md content (first 500 chars) const rememberPath = path_1.default.join(memoryDir, 'REMEMBER.md'); if (await fs_extra_1.default.pathExists(rememberPath)) { const rememberContent = await fs_extra_1.default.readFile(rememberPath, 'utf8'); summary += `### Project Knowledge (REMEMBER.md):\n${rememberContent.substring(0, 500)}...\n\n`; } // Add WORKING_PLAN.md current status (first 300 chars) const workingPlanPath = path_1.default.join(memoryDir, 'WORKING_PLAN.md'); if (await fs_extra_1.default.pathExists(workingPlanPath)) { const workingPlanContent = await fs_extra_1.default.readFile(workingPlanPath, 'utf8'); summary += `### Current Priorities (WORKING_PLAN.md):\n${workingPlanContent.substring(0, 300)}...\n\n`; } // Add recent git commits try { const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process'))); const commits = execSync('git log --oneline -5', { cwd: this.projectDir, encoding: 'utf8', }); summary += `### Recent Commits:\n\`\`\`\n${commits.trim()}\n\`\`\`\n\n`; } catch (error) { summary += '### Recent Commits:\nNo git history available\n\n'; } // Add current TODO_CHAIN.md if it exists const currentTodo = await this.getCurrentTodoChain(); if (currentTodo) { const progress = this.parseProgress(currentTodo); summary += `### Current TODO Chain (${progress.completed}/${progress.total} - ${progress.percentage}%):\n${currentTodo}\n\n`; } else { summary += '### Current TODO Chain:\nNo TODO_CHAIN.md exists yet\n\n'; } return summary; } catch (error) { console.error('Error generating context summary:', error); return 'Error generating context summary'; } } /** * Check if reminder should be injected * Based on message count and todo update frequency */ shouldInjectReminder(messageCount, lastUpdateMessage = 0) { // Follow Cline's pattern: remind every 6 messages const reminderInterval = 6; const messagesSinceUpdate = messageCount - lastUpdateMessage; return messagesSinceUpdate >= reminderInterval && messageCount > 0; } } exports.TodoChainManager = TodoChainManager; //# sourceMappingURL=todo-chain-manager.js.map