Files
ansiblings/packages/nopy/src/nopy.history.ts
T

196 lines
4.4 KiB
TypeScript

/**
* Session history management
* @module nopy.history
*/
import fs from 'node:fs';
import path from 'node:path';
import { describeSession, type NopySession } from './nopy.session.js';
/** Default number of sessions to keep in history */
export const DEFAULT_HISTORY_SIZE = 10;
/** History file name */
export const HISTORY_FILE = '.nopy.history.json';
/**
* A session entry in history
*/
export interface HistoryEntry {
/** Unique identifier (timestamp-based) */
id: string;
/** Human-readable name (timestamp + cube names) */
name: string;
/** ISO timestamp when session was executed */
timestamp: string;
/** The full session data */
session: NopySession;
}
/**
* History file structure
*/
export interface SessionHistory {
/** Array of session entries, newest first */
entries: HistoryEntry[];
}
/**
* Gets the path to the history file
*/
export function getHistoryPath(): string {
return path.resolve(process.cwd(), HISTORY_FILE);
}
/**
* Loads the session history from disk
*
* @returns The session history or empty history if file doesn't exist
*/
export function loadHistory(): SessionHistory {
const historyPath = getHistoryPath();
if (!fs.existsSync(historyPath)) {
return { entries: [] };
}
try {
const content = fs.readFileSync(historyPath, 'utf-8');
return JSON.parse(content) as SessionHistory;
} catch {
return { entries: [] };
}
}
/**
* Saves the session history to disk
*
* @param history - The history to save
*/
export function saveHistory(history: SessionHistory): void {
const historyPath = getHistoryPath();
fs.writeFileSync(historyPath, JSON.stringify(history, null, 2), 'utf-8');
}
/**
* Generates a unique ID for a history entry
*/
function generateEntryId(): string {
return Date.now().toString(36) + Math.random().toString(36).substring(2, 7);
}
/**
* Adds a session to the history
*
* @param session - The session to add
* @param maxEntries - Maximum number of entries to keep
* @returns The created history entry
*/
export function addToHistory(
session: NopySession,
maxEntries: number = DEFAULT_HISTORY_SIZE
): HistoryEntry {
const history = loadHistory();
const timestamp = new Date().toISOString();
const entry: HistoryEntry = {
id: generateEntryId(),
name: describeSession(session, timestamp),
timestamp,
session,
};
// Add to beginning (newest first)
history.entries.unshift(entry);
// Trim to max size
if (history.entries.length > maxEntries) {
history.entries = history.entries.slice(0, maxEntries);
}
saveHistory(history);
return entry;
}
/**
* Gets the most recent session from history
*
* @returns The last session or undefined if history is empty
*/
export function getLastSession(): HistoryEntry | undefined {
const history = loadHistory();
return history.entries[0];
}
/**
* Gets a session by ID
*
* @param id - The session ID
* @returns The session entry or undefined
*/
export function getSessionById(id: string): HistoryEntry | undefined {
const history = loadHistory();
return history.entries.find((e) => e.id === id);
}
/**
* Lists all sessions in history
*
* @returns Array of history entries, newest first
*/
export function listHistory(): HistoryEntry[] {
const history = loadHistory();
return history.entries;
}
/**
* Clears all session history
*/
export function clearHistory(): void {
saveHistory({ entries: [] });
}
/**
* Removes a specific session from history
*
* @param id - The session ID to remove
* @returns true if removed, false if not found
*/
export function removeFromHistory(id: string): boolean {
const history = loadHistory();
const initialLength = history.entries.length;
history.entries = history.entries.filter((e) => e.id !== id);
if (history.entries.length < initialLength) {
saveHistory(history);
return true;
}
return false;
}
/**
* Formats history entries for display
*
* @param entries - History entries to format
* @returns Formatted string for console output
*/
export function formatHistoryList(entries: HistoryEntry[]): string {
if (entries.length === 0) {
return 'No sessions in history.';
}
const lines = ['', 'Session History:', ''];
entries.forEach((entry, index) => {
const marker = index === 0 ? '→' : ' ';
lines.push(` ${marker} [${index + 1}] ${entry.name}`);
lines.push(` ID: ${entry.id}`);
});
lines.push('');
lines.push(`Total: ${entries.length} session(s)`);
lines.push('');
return lines.join('\n');
}