Snapshot Manager Api

Complete API documentation for the Snapshot Manager module.

Snapshot Manager API Reference

Prompt-first procedure: Describe the outcome you want in your agent conversation. The agent should select and load the appropriate AIWG assets, explain material changes, request any needed approval, and report verification evidence. Exact commands and flags appear only in the CLI reference.

Complete API documentation for the Snapshot Manager module.

Class: SnapshotManager

Manages pre-session and post-session state snapshots for External Agent Loop iterations.

Constructor

new SnapshotManager(projectRoot)

Parameters:

  • `projectRoot` (string) - Absolute path to the project root directory

Example:

import { SnapshotManager } from './snapshot-manager.mjs';

const snapshotMgr = new SnapshotManager('/mnt/dev-inbox/jmagly/ai-writing-guide');

Methods

capturePreSnapshot()

Captures the state before a Claude session starts.

capturePreSnapshot(projectRoot, iterationDir): PreSnapshot

Parameters:

  • `projectRoot` (string) - Project root directory path
  • `iterationDir` (string) - Directory to store snapshot (e.g., `.aiwg/ralph-external/iterations/001`)

Returns: `PreSnapshot` object

Side Effects:

  • Creates `{iterationDir}/pre-snapshot.json`
  • Creates iteration directory if it doesn't exist

Example:

const preSnapshot = snapshotMgr.capturePreSnapshot(
  '/path/to/project',
  '/path/to/project/.aiwg/ralph-external/iterations/001'
);

console.log('Starting commit:', preSnapshot.git.commit);
console.log('Key files:', preSnapshot.keyFiles.length);

capturePostSnapshot()

Captures the state after a Claude session completes.

capturePostSnapshot(projectRoot, iterationDir): PostSnapshot

Parameters:

  • `projectRoot` (string) - Project root directory path
  • `iterationDir` (string) - Directory containing pre-snapshot

Returns: `PostSnapshot` object

Throws:

  • Error if `pre-snapshot.json` doesn't exist in `iterationDir`

Side Effects:

  • Creates `{iterationDir}/post-snapshot.json`

Example:

const postSnapshot = snapshotMgr.capturePostSnapshot(
  '/path/to/project',
  '/path/to/project/.aiwg/ralph-external/iterations/001'
);

console.log('Ending commit:', postSnapshot.git.commit);
console.log('Files changed:', postSnapshot.fileDiffs.length);
console.log('Commits made:', postSnapshot.commits.length);

calculateDiff()

Calculates the difference between pre and post snapshots.

calculateDiff(preSnapshot, postSnapshot): SnapshotDiff

Parameters:

  • `preSnapshot` (PreSnapshot) - Pre-session snapshot object
  • `postSnapshot` (PostSnapshot) - Post-session snapshot object

Returns: `SnapshotDiff` object with structured comparison

Example:

const preSnapshot = JSON.parse(
  readFileSync('.aiwg/ralph-external/iterations/001/pre-snapshot.json', 'utf8')
);
const postSnapshot = JSON.parse(
  readFileSync('.aiwg/ralph-external/iterations/001/post-snapshot.json', 'utf8')
);

const diff = snapshotMgr.calculateDiff(preSnapshot, postSnapshot);

console.log(`Duration: ${diff.duration}`);
console.log(`Files added: ${diff.filesAdded.length}`);
console.log(`Files modified: ${diff.filesModified.length}`);
console.log(`AIWG artifacts created: ${diff.aiwgArtifactsCreated.length}`);
console.log(`Commits: ${diff.commitCount}`);

captureGitStatus()

Low-level method to capture current git repository status.

captureGitStatus(projectRoot): GitStatus

Parameters:

  • `projectRoot` (string) - Project root directory path

Returns: `GitStatus` object

Example:

const gitStatus = snapshotMgr.captureGitStatus('/path/to/project');

console.log('Branch:', gitStatus.branch);
console.log('Commit:', gitStatus.commit);
console.log('Staged files:', gitStatus.staged);
console.log('Untracked files:', gitStatus.untracked);

captureKeyFiles()

Captures snapshots of important project files.

captureKeyFiles(projectRoot): FileSnapshot[]

Parameters:

  • `projectRoot` (string) - Project root directory path

Returns: Array of `FileSnapshot` objects for each key file that exists

Tracked Files:

  • `package.json`
  • `package-lock.json`
  • `CLAUDE.md`
  • `CHANGELOG.md`
  • `.gitignore`
  • `tsconfig.json`

Example:

const keyFiles = snapshotMgr.captureKeyFiles('/path/to/project');

for (const file of keyFiles) {
  console.log(`${file.path}: ${file.hash.substring(0, 8)}... (${file.size} bytes)`);
}

captureAiwgState()

Captures state of the `.aiwg/` directory.

captureAiwgState(projectRoot): AiwgState

Parameters:

  • `projectRoot` (string) - Project root directory path

Returns: `AiwgState` object containing all `.aiwg/` files and internal Ralph state

Example:

const aiwgState = snapshotMgr.captureAiwgState('/path/to/project');

console.log('AIWG files tracked:', aiwgState.files.length);

if (aiwgState.ralphState) {
  console.log('Internal Ralph phase:', aiwgState.ralphState.currentPhase);
}

createFileSnapshot()

Creates a snapshot of a single file.

createFileSnapshot(projectRoot, filePath): FileSnapshot

Parameters:

  • `projectRoot` (string) - Project root directory path
  • `filePath` (string) - Absolute path to file

Returns: `FileSnapshot` object with hash, size, and mtime

Example:

const fileSnap = snapshotMgr.createFileSnapshot(
  '/path/to/project',
  '/path/to/project/src/module.ts'
);

console.log('File:', fileSnap.path);
console.log('Hash:', fileSnap.hash);
console.log('Size:', fileSnap.size);
console.log('Modified:', fileSnap.mtime);

calculateFileDiffs()

Calculates file differences between two git commits.

calculateFileDiffs(projectRoot, fromCommit, toCommit): FileDiff[]

Parameters:

  • `projectRoot` (string) - Project root directory path
  • `fromCommit` (string) - Starting commit hash
  • `toCommit` (string) - Ending commit hash

Returns: Array of `FileDiff` objects

Example:

const diffs = snapshotMgr.calculateFileDiffs(
  '/path/to/project',
  'abc123',
  'def456'
);

for (const diff of diffs) {
  console.log(`${diff.status}: ${diff.path}`);
  if (diff.diff) {
    console.log(diff.diff.substring(0, 100) + '...');
  }
}

getCommitsSince()

Gets all commits made since a specific commit.

getCommitsSince(projectRoot, sinceCommit): string[]

Parameters:

  • `projectRoot` (string) - Project root directory path
  • `sinceCommit` (string) - Commit hash to start from

Returns: Array of commit messages (newest first)

Example:

const commits = snapshotMgr.getCommitsSince('/path/to/project', 'abc123');

console.log(`${commits.length} commits made:`);
for (const commit of commits) {
  console.log(`  - ${commit}`);
}

findTestResults()

Searches for test results in common locations.

findTestResults(projectRoot, iterationDir): TestResults

Parameters:

  • `projectRoot` (string) - Project root directory path
  • `iterationDir` (string) - Iteration directory to search

Returns: `TestResults` object

Searched Locations: 1. `{iterationDir}/../*.log` - Iteration output logs 2. `{projectRoot}/test-results.json` 3. `{projectRoot}/.aiwg/testing/test-results.md` 4. `{projectRoot}/coverage/coverage-summary.json`

Example:

const testResults = snapshotMgr.findTestResults(
  '/path/to/project',
  '/path/to/project/.aiwg/ralph-external/iterations/001'
);

if (testResults.found) {
  console.log('Tests found in:', testResults.file);
  console.log('Summary:', testResults.summary);
}

parseTestOutput()

Parses test output to extract summary statistics.

parseTestOutput(content): Object|null

Parameters:

  • `content` (string) - Test output content (JSON or text)

Returns: Parsed test summary object, or `null` if no test patterns found

Supported Formats:

  • Jest/Vitest JSON output
  • Text patterns: "Tests: X passed, X total"
  • Mocha: "X passing"
  • TAP: "# tests X"

Example:

const output = readFileSync('test-output.log', 'utf8');
const summary = snapshotMgr.parseTestOutput(output);

if (summary) {
  if (summary.type === 'jest') {
    console.log(`${summary.passed}/${summary.total} tests passed`);
  } else if (summary.type === 'text') {
    console.log('Test output:', summary.snippet);
  }
}

formatDuration()

Formats duration in milliseconds to human-readable string.

formatDuration(ms): string

Parameters:

  • `ms` (number) - Duration in milliseconds

Returns: Human-readable duration string

Examples:

console.log(snapshotMgr.formatDuration(5000));      // "5s"
console.log(snapshotMgr.formatDuration(125000));    // "2m 5s"
console.log(snapshotMgr.formatDuration(7380000));   // "2h 3m"
console.log(snapshotMgr.formatDuration(21600000));  // "6h 0m"

Type Definitions

GitStatus

type GitStatus = {
  branch: string;          // Current branch name
  commit: string;          // Current HEAD commit hash
  staged: string[];        // Staged file paths
  unstaged: string[];      // Modified but unstaged file paths
  untracked: string[];     // Untracked file paths
}

FileSnapshot

type FileSnapshot = {
  path: string;            // Relative path from project root
  hash: string;            // SHA256 hash of file content
  size: number;            // File size in bytes
  mtime: string;           // Last modification time (ISO 8601)
}

AiwgState

type AiwgState = {
  files: FileSnapshot[];   // All files in .aiwg/ directory
  ralphState: Object|null; // Internal Ralph state if exists
}

PreSnapshot

type PreSnapshot = {
  timestamp: string;       // ISO 8601 timestamp
  git: GitStatus;          // Git repository status
  keyFiles: FileSnapshot[]; // Key project files
  aiwg: AiwgState;         // .the agent-owned directory operation state
}

FileDiff

type FileDiff = {
  path: string;            // File path
  status: 'added'|'modified'|'deleted'; // Change type
  diff?: string;           // Git diff output (for modified files)
}

TestResults

type TestResults = {
  found: boolean;          // Whether test results were found
  file?: string;           // Path to test output file
  summary?: Object;        // Parsed test summary
}

PostSnapshot

type PostSnapshot = {
  timestamp: string;       // ISO 8601 timestamp
  git: GitStatus;          // Updated git status
  fileDiffs: FileDiff[];   // Files changed since pre-snapshot
  commits: string[];       // Commits made during session
  aiwg: AiwgState;         // Updated .the agent-owned state operation
  tests: TestResults;      // Test results if available
}

SnapshotDiff

type SnapshotDiff = {
  duration: string;        // Human-readable duration
  durationMs: number;      // Duration in milliseconds
  filesAdded: string[];    // New files created
  filesModified: string[]; // Existing files modified
  filesDeleted: string[];  // Files deleted
  aiwgArtifactsCreated: string[];  // New .the agent-owned artifacts operation
  aiwgArtifactsUpdated: string[];  // Updated .the agent-owned artifacts operation
  commitCount: number;     // Number of commits made
  commits: string[];       // Commit messages
  hasTests: boolean;       // Whether test results exist
  testSummary?: Object;    // Test summary if available
}

Constants

KEY_FILES

Array of important project files tracked in snapshots:

const KEY_FILES = [
  'package.json',
  'package-lock.json',
  'CLAUDE.md',
  'CHANGELOG.md',
  '.gitignore',
  'tsconfig.json',
];

TEST_PATTERNS

Patterns used to search for test results:

const TEST_PATTERNS = [
  '.aiwg/ralph-external/outputs/*.log',
  'test-results.json',
  'coverage/lcov-report/index.html',
  '.aiwg/testing/*.md',
];

Error Handling

Common Errors

Pre-snapshot not found:

try {
  const postSnapshot = snapshotMgr.capturePostSnapshot(projectRoot, iterationDir);
} catch (error) {
  if (error.message.includes('Pre-snapshot not found')) {
    console.error('Must capture pre-snapshot before post-snapshot');
  }
}

Git not available:

const gitStatus = snapshotMgr.captureGitStatus(projectRoot);
if (gitStatus.commit === 'unknown') {
  console.warn('Not a git repository or git command failed');
}

File not found:

// captureKeyFiles() silently skips missing files
const keyFiles = snapshotMgr.captureKeyFiles(projectRoot);
console.log(`Found ${keyFiles.length} of ${KEY_FILES.length} key files`);

Performance Notes

  • File hashing: Uses SHA256, ~1ms per file
  • Git operations: Can be slow for large repositories
  • Directory traversal: Recursive scan of `.aiwg/` may take seconds for large projects
  • Diff generation: Large diffs (>1000 files) may take several seconds

References

  • @tools/ralph-external/snapshot-manager.mjs - Implementation
  • @tools/ralph-external/docs/snapshot-manager-usage.md - Usage guide
  • @test/unit/ralph-external/snapshot-manager.test.ts - Test suite