-
Notifications
You must be signed in to change notification settings - Fork 11
Open
Description
Summary
Implement automatic git-based checkpointing that creates restore points before file modifications, allowing users to rewind to any previous state with a simple Esc Esc gesture or /checkpoint restore command.
Current State
Amplifier CLI stores session transcripts as JSONL but has no:
- Automatic checkpointing of file system state
- Git-based snapshots before tool execution
- Rewind/restore functionality
- Diff viewing between checkpoints
Proposed Implementation
1. Checkpoint Architecture
.amplifier/
└── checkpoints/
└── {session_id}/
├── metadata.json # Session info
├── 001_initial.json # First checkpoint
├── 002_before_write.json # Before file write
├── 003_before_bash.json # Before bash command
└── ...
Each checkpoint stores:
{
"id": "cp_001",
"timestamp": "2024-01-15T10:30:00Z",
"trigger": "before_tool",
"tool": "Write",
"args": {"path": "src/main.py", "content": "..."},
"git_ref": "amplifier/checkpoint/cp_001",
"conversation_index": 5,
"description": "Before writing to src/main.py"
}2. Git Integration
Checkpoints use git refs (not commits on main branch):
# Create checkpoint
git stash create "amplifier checkpoint cp_001"
git update-ref refs/amplifier/checkpoints/cp_001 <stash-sha>
# Restore checkpoint
git stash apply refs/amplifier/checkpoints/cp_001
# Clean up old checkpoints
git update-ref -d refs/amplifier/checkpoints/cp_001This approach:
- Doesn't pollute git history
- Works even with uncommitted changes
- Can be restored at any time
- Cleans up automatically when session ends
3. Automatic Checkpoint Triggers
checkpoints:
enabled: true
triggers:
- before_write # Before any file write
- before_edit # Before any file edit
- before_bash # Before bash commands (configurable)
- on_request # Manual /checkpoint command
# Only checkpoint bash commands matching these patterns
bash_patterns:
- "rm *"
- "mv *"
- "git *"
- "npm *"
# Auto-cleanup
max_checkpoints: 50
retention_hours: 244. Rewind Gesture (Esc Esc)
In interactive mode, pressing Esc twice quickly:
- Shows last checkpoint info
- Prompts for confirmation
- Restores file system state
- Optionally rolls back conversation
amplifier> [Esc Esc pressed]
Last checkpoint: cp_003 (2 minutes ago)
Trigger: Before writing to src/main.py
Files changed since: src/main.py, tests/test_main.py
Restore options:
[1] Restore files only (keep conversation)
[2] Restore files and rewind conversation
[3] View diff since checkpoint
[c] Cancel
Choice:
5. Slash Commands
/checkpoint # List recent checkpoints
/checkpoint list # Same as above
/checkpoint show <id> # Show checkpoint details
/checkpoint diff <id> # Show diff since checkpoint
/checkpoint restore <id> # Restore to checkpoint
/checkpoint create [message] # Create manual checkpoint
6. New Module Structure
src/amplifier_app_cli/checkpoints/
├── __init__.py
├── manager.py # Main checkpoint management
├── git.py # Git operations (ref-based)
├── storage.py # Metadata storage
├── triggers.py # Automatic trigger detection
├── restore.py # Restore operations
└── cleanup.py # Old checkpoint cleanup
7. Core Interfaces
from dataclasses import dataclass
from pathlib import Path
from datetime import datetime
@dataclass
class Checkpoint:
id: str
session_id: str
timestamp: datetime
trigger: Literal["before_write", "before_edit", "before_bash", "manual"]
tool: str | None
tool_args: dict | None
git_ref: str
conversation_index: int
description: str
files_changed: list[Path] # Files modified since this checkpoint
class CheckpointManager:
def __init__(
self,
session_id: str,
working_dir: Path,
config: CheckpointConfig
): ...
def create(
self,
trigger: str,
tool: str | None = None,
tool_args: dict | None = None,
description: str | None = None
) -> Checkpoint:
"""Create a new checkpoint."""
...
def list(self, limit: int = 10) -> list[Checkpoint]:
"""List recent checkpoints."""
...
def get(self, checkpoint_id: str) -> Checkpoint | None:
"""Get checkpoint by ID."""
...
def restore(
self,
checkpoint_id: str,
restore_conversation: bool = False
) -> RestoreResult:
"""Restore to a checkpoint."""
...
def diff(self, checkpoint_id: str) -> str:
"""Get diff since checkpoint."""
...
def cleanup(self, max_age_hours: int = 24) -> int:
"""Remove old checkpoints, return count removed."""
...
@dataclass
class RestoreResult:
success: bool
checkpoint: Checkpoint
files_restored: list[Path]
conversation_rewound: bool
error: str | None = None8. Integration Points
- PreToolUse hook: Create checkpoint before file/bash operations
- Interactive mode: Handle
Esc Escgesture - Session end: Clean up old checkpoints
- Slash commands: Register checkpoint commands
9. Non-Git Fallback
For directories without git:
class FileBackupCheckpoint:
"""Fallback checkpoint using file copies."""
def create(self, files: list[Path]) -> str:
"""Copy files to .amplifier/checkpoints/backup/"""
...
def restore(self, checkpoint_id: str) -> None:
"""Restore from backup copies."""
...10. CLI Flags
# Disable checkpointing
amplifier run -p "task" --no-checkpoints
# Restore before starting
amplifier run --restore-checkpoint cp_003
# List checkpoints for a session
amplifier checkpoints list --session abc123
amplifier checkpoints restore cp_003Acceptance Criteria
- Automatic checkpoint before file writes
- Automatic checkpoint before file edits
- Configurable checkpoint before bash commands
- Git ref-based storage (doesn't pollute history)
- Fallback for non-git directories
-
Esc Escgesture triggers restore prompt -
/checkpointslash commands work - Diff viewing between checkpoints
- Conversation rewind option
- Automatic cleanup of old checkpoints
- Checkpoints survive session restart
- Unit tests for checkpoint operations
- Integration tests for restore flow
Related
- Depends on: None
- Enhances: User safety, confidence in AI operations
Estimated Effort
High - 2-3 weeks
Files to Create/Modify
src/amplifier_app_cli/checkpoints/(new module)src/amplifier_app_cli/ui/interactive.py(Esc Esc handling)src/amplifier_app_cli/commands/slash.py(/checkpoint commands)src/amplifier_app_cli/ui/approval.py(checkpoint before tools)
Metadata
Metadata
Assignees
Labels
No labels