@lucasschirm/claude-session-sync
v0.2.16
Published
Claude Code plugin adapter for the @lucasschirm/sal-sync session data sync engine.
Readme
@lucasschirm/claude-session-sync
A Claude Code plugin that synchronizes your session
data — transcripts, workspace configuration, and session telemetry — to
S3-compatible storage via the @lucasschirm/sal-sync engine.
What it does
Every Claude Code session goes through a lifecycle: start, tool calls, context compaction, subagent spawns, and stop. This plugin hooks into those lifecycle events to capture and upload session artifacts so they can be analyzed later by the Agentic Sessions Dashboard (or any consumer of the sync engine's output).
Hooks
The plugin registers hooks for the following Claude Code lifecycle events
(defined in hooks/hooks.json):
| Event | What happens |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| SessionStart | Records the session, spawns a detached transcript watcher that uploads incremental transcript deltas. |
| PreCompact | Flushes pending transcript deltas before context compaction. |
| PostCompact | Resumes capture after compaction. |
| Stop | Flushes a final delta when the turn ends. |
| StopFailure | Flushes a final delta when the turn ends due to an API error. |
| SubagentStop | Flushes subagent transcript deltas when a subagent finishes. |
| SessionEnd | Performs the final sync: flushes remaining deltas, uploads the session manifest, and shuts down. |
All hooks except SessionEnd run asynchronously ("async": true) so they
never block Claude. SessionEnd has a 60-second timeout to ensure the final
manifest is uploaded before the process exits.
What gets captured
The sync engine captures only the artifacts in its versioned allowlist:
- Session & subagent transcript JSONL files — captured raw by default,
subject to size limits. Opt out with
SAL_CAPTURE_TRANSCRIPTS=false. - Workspace configuration —
CLAUDE.md,.mcp.json,.claude/settings.json,.claude/settings.local.json,.claude/agents/**,.claude/skills/**,.claude/rules/**. - Global configuration —
~/.claude/settings.json,~/.claude/CLAUDE.md,~/.claude/agents/**,~/.claude.json. - Session telemetry —
sessionId,projectId,harness,harnessVersion,model,startedAt,endedAt,durationMs,endReason.
Configuration artifacts are sanitized before upload: known sensitive fields
(env, password, secret, token, apiKey, authorization, etc.) are
redacted, and bearer tokens, credential-bearing URLs, and private-key blocks
are stripped. See the full privacy policy.
Installation
The plugin is distributed through the session-analyzer marketplace, which is
hosted in this repository at
.claude-plugin/marketplace.json.
Installation is a two-step process: first add the marketplace, then install
the plugin.
Step 1 — Add the marketplace
You can add the marketplace from GitHub, a git URL, or a local clone.
From GitHub (recommended)
Use the owner/repo shorthand:
claude plugin marketplace add lucasschirm/session-analyzerPin to a specific branch or tag with @ref:
claude plugin marketplace add lucasschirm/session-analyzer@feature/claude-session-syncFrom a git URL
For non-GitHub hosts or explicit HTTPS cloning:
claude plugin marketplace add https://github.com/lucasschirm/session-analyzer.gitFrom a local clone (for development)
If you have this repo cloned locally:
claude plugin marketplace add ./path/to/session-analyzerMarketplace scope
The --scope flag controls who sees the marketplace declaration:
| Scope | Setting file | Shared with team? |
| --------- | ------------------------------ | ----------------- |
| user | ~/.claude/settings.json | No (personal) |
| project | .claude/settings.json | Yes (committed) |
| local | .claude/settings.local.json | No (gitignored) |
# Share the marketplace with your team
claude plugin marketplace add lucasschirm/session-analyzer --scope projectStep 2 — Install the plugin
Once the marketplace is added, install the plugin:
claude plugin install claude-session-sync@session-analyzerOr from inside an interactive Claude Code session:
/plugin install claude-session-sync@session-analyzerThe install command opens a details view where you select an installation
scope (user, project, or local — same semantics as above). After installing,
run /reload-plugins if prompted.
From source (local development)
For local development, build the plugin bundle first, then add the marketplace from your local clone:
# Build the plugin bundle (esbuild single-file executables)
pnpm --filter @lucasschirm/claude-session-sync build
# Add the local marketplace and install
claude plugin marketplace add .
claude plugin install claude-session-sync@session-analyzerThe build produces self-contained executables in bin/ (no node_modules
required at runtime):
bin/session-start # SessionStart hook entry point
bin/session-end # SessionEnd hook entry point
bin/hook # PreCompact/PostCompact/Stop/StopFailure/SubagentStop
bin/transcript-watcher # Detached watcher process spawned by session-start
bin/claude-sync # Standalone CLI for manual sync/list/downloadStandalone CLI
In addition to the Claude Code hooks, this package ships a standalone CLI
binary (claude-sync) that lets you manually upload, list, and download
sessions from S3 storage. It's useful for backfilling historical sessions,
inspecting what's been synced, or restoring sessions to a new machine.
Installation
The CLI is included in the same npm package. You can run it via npx without
installing anything:
npx @lucasschirm/claude-session-sync syncOr install it globally for a shorter claude-sync command:
npm install -g @lucasschirm/claude-session-sync
claude-sync syncCommands
sync
Upload all local Claude Code sessions for the current project to S3.
claude-sync syncFrom the project directory, the CLI:
- Finds the corresponding Claude Code project folder in
~/.claude/projects/. - Lists all local
.jsonltranscript files. - For each session, checks if it already exists in S3 — if so, skips it.
- The first session uploaded captures workspace + global + session config. Subsequent sessions capture only session-scoped transcripts (config is uploaded once).
- Prints a per-session summary and a total.
list
List objects in the configured storage. Supports five forms:
# List all projects in storage
claude-sync list
# List sessions for the current project (requires SAL_PROJECT_ID)
claude-sync list --current
# List sessions for a specific project
claude-sync list <project-id>
# List files in a session
claude-sync list <project-id> --session=<session-id>
# List files under a session sub-path
claude-sync list <project-id> --session=<session-id> --path=<path>--path is relative to the session folder. For example, --path=session lists all files under the session/ scope, and --path=session/configs lists files under that sub-path.
Examples:
PROJECT ID SESSIONS FILES SIZE LAST MODIFIED
-------------------------------------------------------------------------
session-analyzer 12 410 84.3 MB 2026-08-18 19:36
my-other-project 3 18 2.1 MB 2026-08-17 10:11
2 project(s), 410 files, 86.4 MB totalSESSION ID FILES SIZE LAST MODIFIED
-------------------------------------------------------------------------
d1acf718-cd8d-4c1d-84fd-b074d231995b 43 21.1 MB 2026-08-18 19:36
test-summary-001 8 119.6 KB 2026-08-18 19:35
2 session(s), 51 files, 21.2 MB totalKEY SIZE LAST MODIFIED
-------------------------------------------------------------------------
manifest.json 2.4 KB 2026-08-18 19:36
session/transcript.jsonl 1.1 MB 2026-08-18 19:36
workspace/package.json 3.2 KB 2026-08-18 19:35
3 file(s), 1.1 MB totaldownload
Download session files from S3 to a local directory.
# Download a specific session
claude-sync download --session-id=<session-id> --output=<dir>
# Download all sessions for the project
claude-sync download all --output=<dir>Files are restored to <output>/<projectId>/<sessionId>/<scope>/<relativePath>.
Configuration
The CLI reads configuration from environment variables, falling back to
.claude/settings.local.json env key for any variables not set in the
process environment. This means you can configure it once in
.claude/settings.local.json and the CLI will pick it up automatically.
See the Configuration section below for the full list of required and optional variables.
If required variables are missing, the CLI prints an error with example
export commands and a .claude/settings.local.json template:
Error: required configuration is missing or incomplete.
The following environment variables must be set:
SAL_PROJECT_ID — Unique identifier for the project.
...
Set them via environment variables before running the CLI:
export SAL_PROJECT_ID=session-analyzer
export SAL_STORAGE_TYPE=s3
...
npx @lucasschirm/claude-session-sync sync
Or add them to .claude/settings.local.json:
{
"env": {
"SAL_PROJECT_ID": "session-analyzer",
...
}
}Updating
To pull the latest version from the marketplace:
claude plugin marketplace update session-analyzer
claude plugin update claude-session-sync@session-analyzerOr inside an interactive session:
/plugin marketplace update session-analyzer
/plugin update claude-session-sync@session-analyzerUninstalling
claude plugin uninstall claude-session-sync@session-analyzer
claude plugin marketplace remove session-analyzerConfiguration
The sync engine is configured entirely through environment variables. Set them
in one of the Claude Code settings files (see below), or export them in your
shell before launching claude.
Required
| Variable | Description |
| -------------------- | ----------------------------------------------------- |
| SAL_PROJECT_ID | Unique identifier for the project (e.g. my-app). |
| SAL_STORAGE_TYPE | Storage backend. Currently only s3 is supported. |
| SAL_STORAGE_BUCKET | S3 bucket name. |
| SAL_STORAGE_REGION | AWS region (e.g. us-east-1). |
| SAL_STORAGE_ACCESS_KEY_ID | AWS access key ID. |
| SAL_STORAGE_SECRET_ACCESS_KEY | AWS secret access key. |
Optional
| Variable | Default | Description |
| ------------------------------ | ----------- | ------------------------------------------------------------------ |
| SAL_STORAGE_ENDPOINT | (none) | Custom S3-compatible endpoint (e.g. http://localhost:4566 for LocalStack). |
| SAL_STORAGE_SESSION_TOKEN | (none) | Temporary AWS session token (for STS credentials). |
| SAL_SYNC_DISABLED | false | Set to true to fully disable synchronization. |
| SAL_CAPTURE_TRANSCRIPTS | true | Set to false to skip transcript capture (config still syncs). |
| SAL_SYNC_TIMEOUT | 30000 | Per-upload timeout in milliseconds. |
| SAL_SYNC_RETRIES | 3 | Number of retry attempts on upload failure. |
| SAL_SESSION_END_BUDGET_MS | 120000 | Time budget for the final SessionEnd sync in milliseconds. |
| SAL_HOOK_UPLOAD_TIMEOUT | 10000 | Per-hook upload timeout in milliseconds. |
| SAL_MAX_FILE_BYTES | 10485760 | Max size per uploaded file (10 MB). |
| SAL_MAX_TOTAL_BYTES | 104857600 | Max total bytes per sync run (100 MB). |
| SAL_MAX_FILES | 1000 | Max files per sync run. |
| SAL_MAX_TRANSCRIPT_BYTES | 52428800 | Max transcript file size (50 MB). |
| SAL_MAX_JSON_DEPTH | 128 | Max JSON nesting depth for sanitization. |
| SAL_MAX_JSONL_LINE_BYTES | 1048576 | Max bytes per JSONL line (1 MB). |
Configuring via Claude Code settings
Claude Code reads environment variables from the env key in its settings
files. There are three relevant scopes:
| File | Scope | Commit to git? |
| ----------------------------- | ------------------------------ | -------------- |
| ~/.claude/settings.json | You, in every project | No |
| .claude/settings.json | Everyone in the project | Yes |
| .claude/settings.local.json | You, in this project only | No (gitignored)|
Option A: User-wide (recommended for personal use)
Edit ~/.claude/settings.json:
{
"env": {
"SAL_PROJECT_ID": "my-app",
"SAL_STORAGE_TYPE": "s3",
"SAL_STORAGE_BUCKET": "my-session-bucket",
"SAL_STORAGE_REGION": "us-east-1",
"SAL_STORAGE_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
"SAL_STORAGE_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
},
"enabledPlugins": {
"claude-session-sync@session-analyzer": true
}
}Option B: Project-local (per-project credentials)
Create .claude/settings.local.json in your project root (this file is
gitignored by default, so credentials stay local):
{
"env": {
"SAL_PROJECT_ID": "my-app",
"SAL_STORAGE_TYPE": "s3",
"SAL_STORAGE_BUCKET": "my-session-bucket",
"SAL_STORAGE_REGION": "us-east-1",
"SAL_STORAGE_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
"SAL_STORAGE_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"SAL_STORAGE_ENDPOINT": "http://localhost:4566"
}
}Note:
pluginConfigsvalues are only read from user settings (~/.claude/settings.json),--settings, and managed settings — not from project or local settings files. However, theenvkey is read from all settings scopes, so environment variables in.claude/settings.local.jsonwork correctly for plugin configuration.
Option C: Shell environment
Export the variables before launching Claude Code:
export SAL_PROJECT_ID="my-app"
export SAL_STORAGE_TYPE="s3"
export SAL_STORAGE_BUCKET="my-session-bucket"
export SAL_STORAGE_REGION="us-east-1"
export SAL_STORAGE_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export SAL_STORAGE_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
claudeUsing LocalStack for local development
For local testing without a real AWS account, run LocalStack and point the plugin at it:
{
"env": {
"SAL_PROJECT_ID": "local-dev",
"SAL_STORAGE_TYPE": "s3",
"SAL_STORAGE_BUCKET": "sal-sessions",
"SAL_STORAGE_REGION": "us-east-1",
"SAL_STORAGE_ENDPOINT": "http://localhost:4566",
"SAL_STORAGE_ACCESS_KEY_ID": "test",
"SAL_STORAGE_SECRET_ACCESS_KEY": "test"
}
}Opting out
| Goal | Setting |
| ----------------------------- | -------------------------------- |
| Disable transcript capture | SAL_CAPTURE_TRANSCRIPTS=false |
| Fully disable synchronization | SAL_SYNC_DISABLED=true |
When fully disabled, the engine performs no filesystem discovery beyond the
minimum required to determine that synchronization is disabled, and
SessionStart does not spawn the watcher.
Development
# Build the plugin
pnpm --filter @lucasschirm/claude-session-sync build
# Run tests
pnpm --filter @lucasschirm/claude-session-sync test
# Typecheck
pnpm --filter @lucasschirm/claude-session-sync typecheck
# Lint
pnpm --filter @lucasschirm/claude-session-sync lintPackage structure
packages/plugins/claude-session-sync/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest
├── hooks/
│ └── hooks.json # Lifecycle hook definitions
├── src/
│ ├── claude.ts # Hook input parsing + Claude session mapping
│ ├── hook.ts # Generic hook entry (PreCompact/PostCompact/Stop/...)
│ ├── session-start.ts # SessionStart entry point
│ ├── session-end.ts # SessionEnd entry point
│ ├── transcript-watcher.ts # Detached watcher spawner
│ ├── cli.ts # Standalone CLI entry point (claude-sync)
│ ├── cli/ # CLI command modules
│ │ ├── env.ts # Environment resolution (process.env + settings.local.json)
│ │ ├── config.ts # Config validation with example error messages
│ │ ├── project.ts # Claude project folder resolution
│ │ ├── sync-command.ts # "sync" command
│ │ ├── list-command.ts # "list" command
│ │ └── download-command.ts # "download" command
│ └── index.ts # Public API barrel
├── bin/ # Built executables (esbuild single-file bundles)
├── build.mjs # esbuild bundling script
├── tests/
│ ├── unit/ # Unit tests
│ └── e2e/ # End-to-end plugin lifecycle tests
└── package.jsonLicense
ISC
