@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
274 lines (268 loc) • 11.4 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import React from 'react';
import { CheckpointListDisplay } from '../components/checkpoint-display.js';
import { ErrorMessage, InfoMessage, SuccessMessage, WarningMessage, } from '../components/message-box.js';
import { CheckpointManager } from '../services/checkpoint-manager.js';
import { addToMessageQueue } from '../utils/message-queue.js';
// Default checkpoint manager instance (lazy-initialized)
let defaultCheckpointManager = null;
/**
* Get or create the default checkpoint manager.
* For testing, use createCheckpointCommand() with a custom manager.
*/
function getDefaultCheckpointManager() {
if (!defaultCheckpointManager) {
defaultCheckpointManager = new CheckpointManager();
}
return defaultCheckpointManager;
}
/**
* Show checkpoint command help
*/
function CheckpointHelp() {
return (_jsx(InfoMessage, { message: `Checkpoint Commands:
/checkpoint create [name] - Create a new checkpoint
• Creates a snapshot of current conversation and modified files
• Auto-generates timestamped name if not provided
• Example: /checkpoint create feature-auth-v1
/checkpoint list - List all available checkpoints
• Shows checkpoint name, creation time, message count, and files changed
/checkpoint load - Interactive checkpoint selection and restore
• Choose from available checkpoints
• Shows confirmation before restoring
• Optionally creates backup of current session
/checkpoint delete <name> - Delete a specific checkpoint
• Permanently removes checkpoint and all its data
• Shows confirmation before deletion
/checkpoint help - Show this help message
Note: Checkpoints are stored in your nanocoder config directory.`, hideBox: false }));
}
/**
* Create checkpoint subcommand
*/
async function createCheckpoint(args, messages, metadata) {
try {
const manager = getDefaultCheckpointManager();
const name = args.length > 0 ? args.join(' ') : undefined;
if (messages.length === 0) {
return React.createElement(WarningMessage, {
key: `warning-${Date.now()}`,
message: 'No messages to checkpoint. Start a conversation first.',
hideBox: true,
});
}
const checkpointMetadata = await manager.saveCheckpoint(name, messages, metadata.provider, metadata.model);
return React.createElement(SuccessMessage, {
key: `success-${Date.now()}`,
message: `Checkpoint '${checkpointMetadata.name}' created successfully
└─ ${checkpointMetadata.messageCount} messages saved
└─ ${checkpointMetadata.filesChanged.length} files captured: ${checkpointMetadata.filesChanged.slice(0, 3).join(', ')}${checkpointMetadata.filesChanged.length > 3 ? '...' : ''}
└─ Provider: ${checkpointMetadata.provider.name} (${checkpointMetadata.provider.model})`,
hideBox: true,
});
}
catch (error) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Failed to create checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`,
hideBox: true,
});
}
}
/**
* List checkpoints subcommand
*/
async function listCheckpoints() {
try {
const manager = getDefaultCheckpointManager();
const checkpoints = await manager.listCheckpoints();
return React.createElement(CheckpointListDisplay, {
key: `list-${Date.now()}`,
checkpoints,
});
}
catch (error) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Failed to list checkpoints: ${error instanceof Error ? error.message : 'Unknown error'}`,
hideBox: true,
});
}
}
/**
* Load checkpoint subcommand
*/
async function loadCheckpoint(args, messages, metadata) {
try {
const manager = getDefaultCheckpointManager();
const checkpointName = args.join(' ');
if (checkpointName) {
if (!manager.checkpointExists(checkpointName)) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Checkpoint '${checkpointName}' does not exist. Use /checkpoint list to see available checkpoints.`,
hideBox: true,
});
}
const checkpointData = await manager.loadCheckpoint(checkpointName, {
validateIntegrity: true,
});
await manager.restoreFiles(checkpointData);
return React.createElement(React.Fragment, { key: `load-success-${Date.now()}` }, React.createElement(SuccessMessage, {
key: 'success',
message: `✓ Checkpoint '${checkpointName}' files restored successfully`,
hideBox: true,
}), React.createElement(InfoMessage, {
key: 'details',
message: `Restored checkpoint:
• ${checkpointData.fileSnapshots.size} file(s) restored to workspace
• Provider: ${checkpointData.metadata.provider.name} (${checkpointData.metadata.provider.model})
• Created: ${new Date(checkpointData.metadata.timestamp).toLocaleString()}`,
hideBox: true,
}));
}
const checkpoints = await manager.listCheckpoints();
if (checkpoints.length === 0) {
return React.createElement(InfoMessage, {
key: `info-${Date.now()}`,
message: 'No checkpoints available. Create one with /checkpoint create [name]',
hideBox: true,
});
}
const CheckpointSelector = (await import('../components/checkpoint-selector.js')).default;
const handleError = (error) => {
addToMessageQueue(React.createElement(ErrorMessage, {
key: `restore-error-${Date.now()}`,
message: `Failed to restore checkpoint: ${error.message}`,
hideBox: true,
}));
};
return React.createElement(CheckpointSelector, {
key: `selector-${Date.now()}`,
checkpoints,
currentMessageCount: messages.length,
onSelect: (selectedName, createBackup) => {
void (async () => {
try {
if (createBackup) {
try {
await manager.saveCheckpoint(`backup-${new Date().toISOString().replace(/[:.]/g, '-')}`, messages, metadata.provider, metadata.model);
}
catch (error) {
// Show backup error but continue with restore
addToMessageQueue(React.createElement(WarningMessage, {
key: `backup-warning-${Date.now()}`,
message: `Warning: Failed to create backup: ${error instanceof Error ? error.message : 'Unknown error'}`,
hideBox: true,
}));
}
}
const checkpointData = await manager.loadCheckpoint(selectedName, {
validateIntegrity: true,
});
await manager.restoreFiles(checkpointData);
addToMessageQueue(React.createElement(SuccessMessage, {
key: `restore-success-${Date.now()}`,
message: `✓ Checkpoint '${selectedName}' restored successfully`,
hideBox: true,
}));
}
catch (error) {
handleError(error instanceof Error ? error : new Error('Unknown error'));
}
})();
},
onCancel: () => {
// Nothing to do, component will unmount
},
onError: handleError,
});
}
catch (error) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Failed to load checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`,
hideBox: true,
});
}
}
/**
* Delete checkpoint subcommand
*/
async function deleteCheckpoint(args) {
try {
if (args.length === 0) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: 'Please specify a checkpoint name to delete. Usage: /checkpoint delete <name>',
hideBox: true,
});
}
const manager = getDefaultCheckpointManager();
const checkpointName = args.join(' ');
if (!manager.checkpointExists(checkpointName)) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Checkpoint '${checkpointName}' does not exist. Use /checkpoint list to see available checkpoints.`,
hideBox: true,
});
}
// Actually delete the checkpoint
await manager.deleteCheckpoint(checkpointName);
// Show success with what was deleted
return React.createElement(SuccessMessage, {
key: `delete-success-${Date.now()}`,
message: `✓ Checkpoint '${checkpointName}' deleted successfully`,
hideBox: true,
});
}
catch (error) {
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Failed to delete checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`,
hideBox: true,
});
}
}
/**
* Main checkpoint command handler
*/
export const checkpointCommand = {
name: 'checkpoint',
description: 'Manage conversation checkpoints - save and restore session snapshots',
handler: async (args, messages, metadata) => {
if (args.length === 0) {
return checkpointCommand.handler(['help'], messages, metadata);
}
const subcommand = args[0].toLowerCase();
const subArgs = args.slice(1);
switch (subcommand) {
case 'create':
case 'save':
return await createCheckpoint(subArgs, messages, metadata);
case 'list':
case 'ls':
return await listCheckpoints();
case 'load':
case 'restore':
return await loadCheckpoint(subArgs, messages, metadata);
case 'delete':
case 'remove':
case 'rm':
return await deleteCheckpoint(subArgs);
case 'help':
case '--help':
case '-h':
return React.createElement(CheckpointHelp, {
key: `help-${Date.now()}`,
});
default:
return React.createElement(ErrorMessage, {
key: `error-${Date.now()}`,
message: `Unknown checkpoint subcommand: ${subcommand}. Use /checkpoint help for available commands.`,
hideBox: true,
});
}
},
};
//# sourceMappingURL=checkpoint.js.map