@lucasschirm/claude-session-sync 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +4 -2
- package/README.md +354 -0
- package/bin/hook +24 -23
- package/bin/session-end +24 -23
- package/bin/session-start +23 -22
- package/bin/transcript-watcher +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# @lucasschirm/claude-session-sync
|
|
2
|
+
|
|
3
|
+
A [Claude Code](https://code.claude.com/) plugin that synchronizes your session
|
|
4
|
+
data — transcripts, workspace configuration, and session telemetry — to
|
|
5
|
+
S3-compatible storage via the [`@lucasschirm/sal-sync`](../../sync) engine.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
Every Claude Code session goes through a lifecycle: start, tool calls, context
|
|
10
|
+
compaction, subagent spawns, and stop. This plugin hooks into those lifecycle
|
|
11
|
+
events to capture and upload session artifacts so they can be analyzed later by
|
|
12
|
+
the [Agentic Sessions Dashboard](../../../) (or any consumer of the sync
|
|
13
|
+
engine's output).
|
|
14
|
+
|
|
15
|
+
### Hooks
|
|
16
|
+
|
|
17
|
+
The plugin registers hooks for the following Claude Code lifecycle events
|
|
18
|
+
(defined in [`hooks/hooks.json`](hooks/hooks.json)):
|
|
19
|
+
|
|
20
|
+
| Event | What happens |
|
|
21
|
+
| -------------- | ----------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `SessionStart` | Records the session, spawns a detached transcript watcher that uploads incremental transcript deltas. |
|
|
23
|
+
| `PreCompact` | Flushes pending transcript deltas before context compaction. |
|
|
24
|
+
| `PostCompact` | Resumes capture after compaction. |
|
|
25
|
+
| `Stop` | Flushes a final delta when the turn ends. |
|
|
26
|
+
| `StopFailure` | Flushes a final delta when the turn ends due to an API error. |
|
|
27
|
+
| `SubagentStop` | Flushes subagent transcript deltas when a subagent finishes. |
|
|
28
|
+
| `SessionEnd` | Performs the final sync: flushes remaining deltas, uploads the session manifest, and shuts down. |
|
|
29
|
+
|
|
30
|
+
All hooks except `SessionEnd` run asynchronously (`"async": true`) so they
|
|
31
|
+
never block Claude. `SessionEnd` has a 60-second timeout to ensure the final
|
|
32
|
+
manifest is uploaded before the process exits.
|
|
33
|
+
|
|
34
|
+
### What gets captured
|
|
35
|
+
|
|
36
|
+
The sync engine captures only the artifacts in its versioned
|
|
37
|
+
[allowlist](../../sync/src/allowlist.ts):
|
|
38
|
+
|
|
39
|
+
- **Session & subagent transcript JSONL files** — captured raw by default,
|
|
40
|
+
subject to size limits. Opt out with `SAL_CAPTURE_TRANSCRIPTS=false`.
|
|
41
|
+
- **Workspace configuration** — `CLAUDE.md`, `.mcp.json`,
|
|
42
|
+
`.claude/settings.json`, `.claude/settings.local.json`,
|
|
43
|
+
`.claude/agents/**`, `.claude/skills/**`, `.claude/rules/**`.
|
|
44
|
+
- **Global configuration** — `~/.claude/settings.json`, `~/.claude/CLAUDE.md`,
|
|
45
|
+
`~/.claude/agents/**`, `~/.claude.json`.
|
|
46
|
+
- **Session telemetry** — `sessionId`, `projectId`, `harness`,
|
|
47
|
+
`harnessVersion`, `model`, `startedAt`, `endedAt`, `durationMs`,
|
|
48
|
+
`endReason`.
|
|
49
|
+
|
|
50
|
+
Configuration artifacts are sanitized before upload: known sensitive fields
|
|
51
|
+
(`env`, `password`, `secret`, `token`, `apiKey`, `authorization`, etc.) are
|
|
52
|
+
redacted, and bearer tokens, credential-bearing URLs, and private-key blocks
|
|
53
|
+
are stripped. See the full [privacy policy](../../sync/POLICY.md).
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
The plugin is distributed through the `session-analyzer` marketplace, which is
|
|
58
|
+
hosted in this repository at
|
|
59
|
+
[`.claude-plugin/marketplace.json`](../../../.claude-plugin/marketplace.json).
|
|
60
|
+
Installation is a two-step process: first add the marketplace, then install
|
|
61
|
+
the plugin.
|
|
62
|
+
|
|
63
|
+
### Step 1 — Add the marketplace
|
|
64
|
+
|
|
65
|
+
You can add the marketplace from GitHub, a git URL, or a local clone.
|
|
66
|
+
|
|
67
|
+
#### From GitHub (recommended)
|
|
68
|
+
|
|
69
|
+
Use the `owner/repo` shorthand:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
claude plugin marketplace add lucasschirm/session-analyzer
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Pin to a specific branch or tag with `@ref`:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
claude plugin marketplace add lucasschirm/session-analyzer@feature/claude-session-sync
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### From a git URL
|
|
82
|
+
|
|
83
|
+
For non-GitHub hosts or explicit HTTPS cloning:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
claude plugin marketplace add https://github.com/lucasschirm/session-analyzer.git
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### From a local clone (for development)
|
|
90
|
+
|
|
91
|
+
If you have this repo cloned locally:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
claude plugin marketplace add ./path/to/session-analyzer
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Marketplace scope
|
|
98
|
+
|
|
99
|
+
The `--scope` flag controls who sees the marketplace declaration:
|
|
100
|
+
|
|
101
|
+
| Scope | Setting file | Shared with team? |
|
|
102
|
+
| --------- | ------------------------------ | ----------------- |
|
|
103
|
+
| `user` | `~/.claude/settings.json` | No (personal) |
|
|
104
|
+
| `project` | `.claude/settings.json` | Yes (committed) |
|
|
105
|
+
| `local` | `.claude/settings.local.json` | No (gitignored) |
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Share the marketplace with your team
|
|
109
|
+
claude plugin marketplace add lucasschirm/session-analyzer --scope project
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Step 2 — Install the plugin
|
|
113
|
+
|
|
114
|
+
Once the marketplace is added, install the plugin:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
claude plugin install claude-session-sync@session-analyzer
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Or from inside an interactive Claude Code session:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
/plugin install claude-session-sync@session-analyzer
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The install command opens a details view where you select an installation
|
|
127
|
+
scope (user, project, or local — same semantics as above). After installing,
|
|
128
|
+
run `/reload-plugins` if prompted.
|
|
129
|
+
|
|
130
|
+
### From source (local development)
|
|
131
|
+
|
|
132
|
+
For local development, build the plugin bundle first, then add the marketplace
|
|
133
|
+
from your local clone:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Build the plugin bundle (esbuild single-file executables)
|
|
137
|
+
pnpm --filter @lucasschirm/claude-session-sync build
|
|
138
|
+
|
|
139
|
+
# Add the local marketplace and install
|
|
140
|
+
claude plugin marketplace add .
|
|
141
|
+
claude plugin install claude-session-sync@session-analyzer
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The build produces self-contained executables in `bin/` (no `node_modules`
|
|
145
|
+
required at runtime):
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
bin/session-start # SessionStart hook entry point
|
|
149
|
+
bin/session-end # SessionEnd hook entry point
|
|
150
|
+
bin/hook # PreCompact/PostCompact/Stop/StopFailure/SubagentStop
|
|
151
|
+
bin/transcript-watcher # Detached watcher process spawned by session-start
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Updating
|
|
155
|
+
|
|
156
|
+
To pull the latest version from the marketplace:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
claude plugin marketplace update session-analyzer
|
|
160
|
+
claude plugin update claude-session-sync@session-analyzer
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Or inside an interactive session:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
/plugin marketplace update session-analyzer
|
|
167
|
+
/plugin update claude-session-sync@session-analyzer
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Uninstalling
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
claude plugin uninstall claude-session-sync@session-analyzer
|
|
174
|
+
claude plugin marketplace remove session-analyzer
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Configuration
|
|
178
|
+
|
|
179
|
+
The sync engine is configured entirely through environment variables. Set them
|
|
180
|
+
in one of the Claude Code settings files (see below), or export them in your
|
|
181
|
+
shell before launching `claude`.
|
|
182
|
+
|
|
183
|
+
### Required
|
|
184
|
+
|
|
185
|
+
| Variable | Description |
|
|
186
|
+
| -------------------- | ----------------------------------------------------- |
|
|
187
|
+
| `SAL_PROJECT_ID` | Unique identifier for the project (e.g. `my-app`). |
|
|
188
|
+
| `SAL_STORAGE_TYPE` | Storage backend. Currently only `s3` is supported. |
|
|
189
|
+
| `SAL_STORAGE_BUCKET` | S3 bucket name. |
|
|
190
|
+
| `SAL_STORAGE_REGION` | AWS region (e.g. `us-east-1`). |
|
|
191
|
+
| `SAL_STORAGE_ID` | AWS access key ID. |
|
|
192
|
+
| `SAL_STORAGE_SECRET` | AWS secret access key. |
|
|
193
|
+
|
|
194
|
+
### Optional
|
|
195
|
+
|
|
196
|
+
| Variable | Default | Description |
|
|
197
|
+
| ------------------------------ | ----------- | ------------------------------------------------------------------ |
|
|
198
|
+
| `SAL_STORAGE_ENDPOINT` | _(none)_ | Custom S3-compatible endpoint (e.g. `http://localhost:4566` for LocalStack). |
|
|
199
|
+
| `SAL_STORAGE_SESSION_TOKEN` | _(none)_ | Temporary AWS session token (for STS credentials). |
|
|
200
|
+
| `SAL_SYNC_DISABLED` | `false` | Set to `true` to fully disable synchronization. |
|
|
201
|
+
| `SAL_CAPTURE_TRANSCRIPTS` | `true` | Set to `false` to skip transcript capture (config still syncs). |
|
|
202
|
+
| `SAL_SYNC_TIMEOUT` | `30000` | Per-upload timeout in milliseconds. |
|
|
203
|
+
| `SAL_SYNC_RETRIES` | `3` | Number of retry attempts on upload failure. |
|
|
204
|
+
| `SAL_SESSION_END_BUDGET_MS` | `120000` | Time budget for the final SessionEnd sync in milliseconds. |
|
|
205
|
+
| `SAL_HOOK_UPLOAD_TIMEOUT` | `10000` | Per-hook upload timeout in milliseconds. |
|
|
206
|
+
| `SAL_MAX_FILE_BYTES` | `10485760` | Max size per uploaded file (10 MB). |
|
|
207
|
+
| `SAL_MAX_TOTAL_BYTES` | `104857600` | Max total bytes per sync run (100 MB). |
|
|
208
|
+
| `SAL_MAX_FILES` | `1000` | Max files per sync run. |
|
|
209
|
+
| `SAL_MAX_TRANSCRIPT_BYTES` | `52428800` | Max transcript file size (50 MB). |
|
|
210
|
+
| `SAL_MAX_JSON_DEPTH` | `128` | Max JSON nesting depth for sanitization. |
|
|
211
|
+
| `SAL_MAX_JSONL_LINE_BYTES` | `1048576` | Max bytes per JSONL line (1 MB). |
|
|
212
|
+
|
|
213
|
+
### Configuring via Claude Code settings
|
|
214
|
+
|
|
215
|
+
Claude Code reads environment variables from the `env` key in its settings
|
|
216
|
+
files. There are three relevant scopes:
|
|
217
|
+
|
|
218
|
+
| File | Scope | Commit to git? |
|
|
219
|
+
| ----------------------------- | ------------------------------ | -------------- |
|
|
220
|
+
| `~/.claude/settings.json` | You, in every project | No |
|
|
221
|
+
| `.claude/settings.json` | Everyone in the project | Yes |
|
|
222
|
+
| `.claude/settings.local.json` | You, in this project only | No (gitignored)|
|
|
223
|
+
|
|
224
|
+
#### Option A: User-wide (recommended for personal use)
|
|
225
|
+
|
|
226
|
+
Edit `~/.claude/settings.json`:
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"env": {
|
|
231
|
+
"SAL_PROJECT_ID": "my-app",
|
|
232
|
+
"SAL_STORAGE_TYPE": "s3",
|
|
233
|
+
"SAL_STORAGE_BUCKET": "my-session-bucket",
|
|
234
|
+
"SAL_STORAGE_REGION": "us-east-1",
|
|
235
|
+
"SAL_STORAGE_ID": "AKIAIOSFODNN7EXAMPLE",
|
|
236
|
+
"SAL_STORAGE_SECRET": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
|
237
|
+
},
|
|
238
|
+
"enabledPlugins": {
|
|
239
|
+
"claude-session-sync@session-analyzer": true
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Option B: Project-local (per-project credentials)
|
|
245
|
+
|
|
246
|
+
Create `.claude/settings.local.json` in your project root (this file is
|
|
247
|
+
gitignored by default, so credentials stay local):
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"env": {
|
|
252
|
+
"SAL_PROJECT_ID": "my-app",
|
|
253
|
+
"SAL_STORAGE_TYPE": "s3",
|
|
254
|
+
"SAL_STORAGE_BUCKET": "my-session-bucket",
|
|
255
|
+
"SAL_STORAGE_REGION": "us-east-1",
|
|
256
|
+
"SAL_STORAGE_ID": "AKIAIOSFODNN7EXAMPLE",
|
|
257
|
+
"SAL_STORAGE_SECRET": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
258
|
+
"SAL_STORAGE_ENDPOINT": "http://localhost:4566"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
> **Note:** `pluginConfigs` values are only read from user settings
|
|
264
|
+
> (`~/.claude/settings.json`), `--settings`, and managed settings — not from
|
|
265
|
+
> project or local settings files. However, the `env` key **is** read from all
|
|
266
|
+
> settings scopes, so environment variables in `.claude/settings.local.json`
|
|
267
|
+
> work correctly for plugin configuration.
|
|
268
|
+
|
|
269
|
+
#### Option C: Shell environment
|
|
270
|
+
|
|
271
|
+
Export the variables before launching Claude Code:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
export SAL_PROJECT_ID="my-app"
|
|
275
|
+
export SAL_STORAGE_TYPE="s3"
|
|
276
|
+
export SAL_STORAGE_BUCKET="my-session-bucket"
|
|
277
|
+
export SAL_STORAGE_REGION="us-east-1"
|
|
278
|
+
export SAL_STORAGE_ID="AKIAIOSFODNN7EXAMPLE"
|
|
279
|
+
export SAL_STORAGE_SECRET="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
|
280
|
+
claude
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Using LocalStack for local development
|
|
284
|
+
|
|
285
|
+
For local testing without a real AWS account, run
|
|
286
|
+
[LocalStack](https://localstack.dev/) and point the plugin at it:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"env": {
|
|
291
|
+
"SAL_PROJECT_ID": "local-dev",
|
|
292
|
+
"SAL_STORAGE_TYPE": "s3",
|
|
293
|
+
"SAL_STORAGE_BUCKET": "sal-sessions",
|
|
294
|
+
"SAL_STORAGE_REGION": "us-east-1",
|
|
295
|
+
"SAL_STORAGE_ENDPOINT": "http://localhost:4566",
|
|
296
|
+
"SAL_STORAGE_ID": "test",
|
|
297
|
+
"SAL_STORAGE_SECRET": "test"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Opting out
|
|
303
|
+
|
|
304
|
+
| Goal | Setting |
|
|
305
|
+
| ----------------------------- | -------------------------------- |
|
|
306
|
+
| Disable transcript capture | `SAL_CAPTURE_TRANSCRIPTS=false` |
|
|
307
|
+
| Fully disable synchronization | `SAL_SYNC_DISABLED=true` |
|
|
308
|
+
|
|
309
|
+
When fully disabled, the engine performs no filesystem discovery beyond the
|
|
310
|
+
minimum required to determine that synchronization is disabled, and
|
|
311
|
+
`SessionStart` does not spawn the watcher.
|
|
312
|
+
|
|
313
|
+
## Development
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Build the plugin
|
|
317
|
+
pnpm --filter @lucasschirm/claude-session-sync build
|
|
318
|
+
|
|
319
|
+
# Run tests
|
|
320
|
+
pnpm --filter @lucasschirm/claude-session-sync test
|
|
321
|
+
|
|
322
|
+
# Typecheck
|
|
323
|
+
pnpm --filter @lucasschirm/claude-session-sync typecheck
|
|
324
|
+
|
|
325
|
+
# Lint
|
|
326
|
+
pnpm --filter @lucasschirm/claude-session-sync lint
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Package structure
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
packages/plugins/claude-session-sync/
|
|
333
|
+
├── .claude-plugin/
|
|
334
|
+
│ └── plugin.json # Plugin manifest
|
|
335
|
+
├── hooks/
|
|
336
|
+
│ └── hooks.json # Lifecycle hook definitions
|
|
337
|
+
├── src/
|
|
338
|
+
│ ├── claude.ts # Hook input parsing + Claude session mapping
|
|
339
|
+
│ ├── hook.ts # Generic hook entry (PreCompact/PostCompact/Stop/...)
|
|
340
|
+
│ ├── session-start.ts # SessionStart entry point
|
|
341
|
+
│ ├── session-end.ts # SessionEnd entry point
|
|
342
|
+
│ ├── transcript-watcher.ts # Detached watcher spawner
|
|
343
|
+
│ └── index.ts # Public API barrel
|
|
344
|
+
├── bin/ # Built executables (esbuild single-file bundles)
|
|
345
|
+
├── build.mjs # esbuild bundling script
|
|
346
|
+
├── tests/
|
|
347
|
+
│ ├── unit/ # Unit tests
|
|
348
|
+
│ └── e2e/ # End-to-end plugin lifecycle tests
|
|
349
|
+
└── package.json
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## License
|
|
353
|
+
|
|
354
|
+
ISC
|