Skip to content

Add claude/snapshot — persistent memory across sessions#396

Merged
joelteply merged 1 commit into
mainfrom
feature/claude-snapshot
Mar 25, 2026
Merged

Add claude/snapshot — persistent memory across sessions#396
joelteply merged 1 commit into
mainfrom
feature/claude-snapshot

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

Second self-built tool. ./jtag claude/snapshot saves structured work-state to ~/.continuum/claude/snapshots/latest.json. Next session reads it to resume instantly.

Captures: summary, pending work, next steps, key decisions, issues touched, git state (branch, uncommitted, recent commits). All automatic except the summary param.

Pair with claude/context

# End of session — save state
./jtag claude/snapshot --summary="..." --nextSteps="..."

# Start of next session — load state
./jtag claude/context
cat ~/.continuum/claude/snapshots/latest.json

Test plan

  • npm run build:ts — compiles clean
  • npm start — deploys, command registered
  • ./jtag claude/snapshot --summary="test" — saves snapshot, creates latest.json
  • Snapshot contains git branch, uncommitted files, recent commits automatically

Closes #395 (partially — context + snapshot done, resume and deeper integration remain)

Saves structured work-state snapshots to ~/.continuum/claude/snapshots/.
Each snapshot captures: what was being worked on, pending work, next
steps, key decisions, issues touched, and automatic git state.

latest.json always points to the most recent snapshot. Next session
reads it to resume without context loss. This is the second self-built
tool — moving from primitive MEMORY.md toward real persistent state.
Copilot AI review requested due to automatic review settings March 25, 2026 03:12
@joelteply joelteply merged commit 9577619 into main Mar 25, 2026
@joelteply joelteply deleted the feature/claude-snapshot branch March 25, 2026 03:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new claude/snapshot JTAG command intended to persist Claude’s current work state to disk so future sessions can resume quickly, complementing the existing claude/context tooling.

Changes:

  • Registers the new claude/snapshot command in generated command registries/schemas/constants.
  • Implements the command (server writes snapshot JSON + latest.json; browser delegates to server) and introduces shared types + generator spec.
  • Adds command package scaffolding (README, npm metadata, unit/integration test files).

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/shared/generated-command-constants.ts Adds CLAUDE_SNAPSHOT command constant.
src/server/generated.ts Registers server-side command class for claude/snapshot.
src/browser/generated.ts Registers browser-side command class for claude/snapshot.
src/generator/specs/claude-snapshot.json New generator spec describing params/results/examples.
src/generated-command-schemas.json Adds generated schema entry for claude/snapshot.
src/commands/claude/snapshot/shared/ClaudeSnapshotTypes.ts Defines params/results and factory helpers + typed executor.
src/commands/claude/snapshot/server/ClaudeSnapshotServerCommand.ts Implements snapshot creation, git introspection, and filesystem persistence.
src/commands/claude/snapshot/browser/ClaudeSnapshotBrowserCommand.ts Browser delegation wrapper via remoteExecute.
src/commands/claude/snapshot/README.md Command documentation (usage, params, testing).
src/commands/claude/snapshot/package.json Command package metadata + scripts.
src/commands/claude/snapshot/.npmignore Publish ignore rules for the command package.
src/commands/claude/snapshot/test/unit/ClaudeSnapshotCommand.test.ts Added unit-test scaffold (currently script-style).
src/commands/claude/snapshot/test/integration/ClaudeSnapshotIntegration.test.ts Added integration-test scaffold (live-system runner).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +62 to +65
// Save to ~/.continuum/claude/snapshots/
const homeDir = process.env.HOME || '/tmp';
const snapshotDir = path.join(homeDir, '.continuum', 'claude', 'snapshots');
fs.mkdirSync(snapshotDir, { recursive: true });
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snapshot path resolution uses process.env.HOME || '/tmp', which is not reliable cross-platform (e.g. Windows) and can unexpectedly write to /tmp. The repo already centralizes the Continuum home root via SystemPaths/GlobalPaths (based on os.homedir()); use that as the base for claude/snapshots so snapshots always land under the canonical ~/.continuum root.

Copilot uses AI. Check for mistakes.
pendingWork: params.pendingWork || null,
nextSteps: params.nextSteps || null,
decisions: params.decisions || null,
issuesWorked: params.issuesWorked ? params.issuesWorked.split(',').map(s => s.trim()) : [],
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issuesWorked parsing will include empty strings when the input has extra commas/whitespace (e.g. "376, ,"), which then gets persisted into the snapshot. Consider trimming and filtering out empty entries so the stored array only contains real issue identifiers.

Suggested change
issuesWorked: params.issuesWorked ? params.issuesWorked.split(',').map(s => s.trim()) : [],
issuesWorked: params.issuesWorked ? params.issuesWorked.split(',').map(s => s.trim()).filter(Boolean) : [],

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +79
fs.mkdirSync(snapshotDir, { recursive: true });

const filePath = path.join(snapshotDir, `${snapshotId}.json`);
fs.writeFileSync(filePath, JSON.stringify(snapshot, null, 2));

// Also write latest.json for easy access
const latestPath = path.join(snapshotDir, 'latest.json');
fs.writeFileSync(latestPath, JSON.stringify(snapshot, null, 2));

return createClaudeSnapshotResultFromParams(params, {
success: true,
snapshotId,
filePath,
timestamp,
});
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File writes here are synchronous and unguarded; any filesystem error (permissions, disk full, invalid home dir) will throw and the command won’t return a structured ClaudeSnapshotResult.error. Consider wrapping the mkdir/write steps in try/catch and returning success:false with a PersistenceError (or using the artifacts layer / an atomic write pattern) so callers get a consistent, serializable error and latest.json can’t be left partially-written.

Suggested change
fs.mkdirSync(snapshotDir, { recursive: true });
const filePath = path.join(snapshotDir, `${snapshotId}.json`);
fs.writeFileSync(filePath, JSON.stringify(snapshot, null, 2));
// Also write latest.json for easy access
const latestPath = path.join(snapshotDir, 'latest.json');
fs.writeFileSync(latestPath, JSON.stringify(snapshot, null, 2));
return createClaudeSnapshotResultFromParams(params, {
success: true,
snapshotId,
filePath,
timestamp,
});
let filePath: string | null = null;
try {
fs.mkdirSync(snapshotDir, { recursive: true });
filePath = path.join(snapshotDir, `${snapshotId}.json`);
fs.writeFileSync(filePath, JSON.stringify(snapshot, null, 2));
// Also write latest.json for easy access
const latestPath = path.join(snapshotDir, 'latest.json');
fs.writeFileSync(latestPath, JSON.stringify(snapshot, null, 2));
return createClaudeSnapshotResultFromParams(params, {
success: true,
snapshotId,
filePath,
timestamp,
});
} catch (err) {
return createClaudeSnapshotResultFromParams(params, {
success: false,
snapshotId,
filePath,
timestamp,
error: {
type: 'PersistenceError',
message: 'Failed to persist Claude snapshot to disk.',
details:
err instanceof Error
? { name: err.name, message: err.message }
: String(err),
},
});
}

Copilot uses AI. Check for mistakes.
### Save end-of-session snapshot

```bash
undefined
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Examples section currently renders undefined, so users don’t have a working CLI example to follow. Replace this with a real ./jtag claude/snapshot --summary=... invocation (and optionally show --nextSteps, etc.).

Suggested change
undefined
./jtag claude/snapshot \
--summary="Wrapped up work on payment retries and logging" \
--pendingWork="Refactor retry backoff, add more test coverage" \
--nextSteps="Implement exponential backoff, ship hotfix" \
--decisions="Chose to log retry attempts at INFO level" \
--issuesWorked="376,335,317"

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +106
```bash
# Run unit tests (no server required)
npx tsx commands/Claude Snapshot/test/unit/ClaudeSnapshotCommand.test.ts
```
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documented unit-test command uses commands/Claude Snapshot/... (with a space/capitalization), which doesn’t match the actual repo path (commands/claude/snapshot/...) and will fail if copied verbatim. Update the path in the README to the real location under src/commands/claude/snapshot/....

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +10
"test": "npm run test:unit && npm run test:integration",
"test:unit": "npx vitest run test/unit/*.test.ts",
"test:integration": "npx tsx test/integration/ClaudeSnapshotIntegration.test.ts",
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test:unit runs vitest, but this file is a self-executing tsx script (no describe/it), so vitest run test/unit/*.test.ts will discover 0 tests and won’t exercise the command logic. Either convert this to proper Vitest tests or change the script to run the file with tsx so CI/local runs actually validate behavior.

Copilot uses AI. Check for mistakes.
* This is a REFERENCE EXAMPLE showing best practices for command testing.
*
* Generated by: ./jtag generate
* Run with: npx tsx commands/Claude Snapshot/test/unit/ClaudeSnapshotCommand.test.ts
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header comment suggests running npx tsx commands/Claude Snapshot/..., but the actual path is commands/claude/snapshot/... under src/. This mismatch makes the “Run with” instruction incorrect when copied verbatim.

Suggested change
* Run with: npx tsx commands/Claude Snapshot/test/unit/ClaudeSnapshotCommand.test.ts
* Run with: npx tsx src/commands/claude/snapshot/test/unit/ClaudeSnapshotCommand.test.ts

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude Code as first citizen: self-tooling, session persistence, and deep integration

2 participants