agent-carry 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/CHANGELOG.md +5 -0
- package/README.md +47 -127
- package/package.json +3 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,156 +1,76 @@
|
|
|
1
1
|
# agent-carry
|
|
2
2
|
|
|
3
|
-
Move a coding-agent session to another machine
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- **Path-portable.** Your project root, home directory and session-store path become tokens on export and are
|
|
17
|
-
rewritten for the target machine on import. Object keys too: file-history snapshots are maps keyed by path.
|
|
18
|
-
- **Merge-safe.** Records have stable `uuid`s, so import is a union, not an overwrite. Importing twice changes
|
|
19
|
-
nothing, and a session that continued on two machines becomes a two-branch history instead of a conflict.
|
|
20
|
-
The local file is the source of truth: its lines are kept byte for byte.
|
|
21
|
-
- **Redacted.** Credential-shaped strings (cloud keys, tokens, JWTs, private keys, `*_SECRET=...`, URL passwords)
|
|
22
|
-
are removed before anything is written. The export prints what it removed.
|
|
23
|
-
- **Complete.** Subagent transcripts and offloaded tool results travel with the session, and the references to
|
|
24
|
-
them are rewritten.
|
|
25
|
-
- **Inspectable.** A bundle is newline-delimited JSON. `carry inspect` shows its header; `less` and `jq` show the rest.
|
|
3
|
+
Move a coding-agent session to another machine or folder and keep working. Supports Claude Code today.
|
|
4
|
+
|
|
5
|
+
Claude Code stores sessions per absolute project path, and the transcripts are full of absolute paths, so a
|
|
6
|
+
copied session can't be resumed elsewhere. `carry` fixes that:
|
|
7
|
+
|
|
8
|
+
- **Path-portable:** project, home and store paths become tokens on export and are rewritten on import.
|
|
9
|
+
- **Merge-safe:** import is a union by record `uuid`. Importing twice changes nothing; a session continued on two machines becomes a two-branch history.
|
|
10
|
+
- **Redacted:** credential-shaped strings (cloud keys, tokens, JWTs, private keys, `*_SECRET=...`, URL passwords) are removed on export, with a report.
|
|
11
|
+
- **Complete:** subagent transcripts and tool results travel with the session.
|
|
12
|
+
- **Inspectable:** a bundle is newline-delimited JSON (gzipped).
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
26
15
|
|
|
27
16
|
```
|
|
28
|
-
npm i -g agent-carry
|
|
17
|
+
npm i -g agent-carry # installs `carry` (also available as `agent-carry`)
|
|
29
18
|
|
|
30
|
-
#
|
|
19
|
+
# machine A, in the project folder
|
|
31
20
|
carry list
|
|
32
21
|
carry export --latest -o session.carry.gz
|
|
33
22
|
|
|
34
|
-
#
|
|
23
|
+
# machine B, in the same project (any path)
|
|
35
24
|
carry import session.carry.gz
|
|
36
|
-
claude --resume <id>
|
|
25
|
+
claude --resume <id> # printed by import
|
|
37
26
|
```
|
|
38
27
|
|
|
39
|
-
|
|
28
|
+
Encrypt a bundle with `CARRY_PASSPHRASE` or `--passphrase-file FILE` (AES-256-GCM, scrypt key). Never pass the
|
|
29
|
+
passphrase as an argument.
|
|
40
30
|
|
|
41
|
-
###
|
|
31
|
+
### Sync through a shared folder
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
replicates (Syncthing, Dropbox, iCloud, a git working tree, a network share).
|
|
33
|
+
Point both machines at a folder your sync tool already replicates (Dropbox, iCloud, Syncthing, a git tree).
|
|
45
34
|
|
|
46
35
|
```
|
|
47
|
-
|
|
48
|
-
carry
|
|
49
|
-
carry pull ~/Sync/carry # other devices' sessions -> this machine
|
|
36
|
+
carry push ~/Sync/carry # this project's sessions -> folder
|
|
37
|
+
carry pull ~/Sync/carry # other devices' sessions -> this machine
|
|
50
38
|
```
|
|
51
39
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
40
|
+
Projects are matched by git remote, not path. Each file has one writer, so there are no sync conflicts.
|
|
41
|
+
|
|
42
|
+
### Claude Code plugin
|
|
43
|
+
|
|
44
|
+
`plugin/` adds `/carry:share` and `/carry:pickup`. With `CARRY_SYNC_DIR` set, bundled hooks pull on session
|
|
45
|
+
start and push on session end (hooks not yet exercised in a live Claude Code).
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
end. (Hooks are written to the plugin spec but I have not exercised them inside a live Claude Code yet.)
|
|
47
|
+
## Commands
|
|
59
48
|
|
|
60
49
|
| command | |
|
|
61
50
|
|---|---|
|
|
62
|
-
| `carry list [--all]` | sessions for this folder
|
|
63
|
-
| `carry export <id\|--latest> [-o FILE]` |
|
|
64
|
-
| `carry inspect FILE` | header
|
|
51
|
+
| `carry list [--all]` | sessions for this folder, or all |
|
|
52
|
+
| `carry export <id\|--latest> [-o FILE]` | `--no-redact`, `--redact-emails`, `--strip-thinking`, `--allow-thinking-secrets`, `--project-root DIR` |
|
|
53
|
+
| `carry inspect FILE` | header, parts, redaction counts |
|
|
65
54
|
| `carry import FILE [--project-root DIR] [--dry-run]` | write into the target folder's session store |
|
|
66
|
-
| `carry push DIR` / `carry pull DIR
|
|
67
|
-
|
|
68
|
-
Passphrase: `CARRY_PASSPHRASE` or `--passphrase-file`. AES-256-GCM with a scrypt-derived key (node:crypto), header
|
|
69
|
-
authenticated. Never pass a passphrase as an argument; it would land in shell history.
|
|
70
|
-
|
|
71
|
-
`--claude-home` and `--home` override `~/.claude` and the home directory, which is how the tests run two
|
|
72
|
-
machines in one process.
|
|
73
|
-
|
|
74
|
-
## Compared with ctxhop
|
|
75
|
-
|
|
76
|
-
[ctxhop](https://github.com/CCCCY-ci/ctxhop) is the larger, more mature tool and solves a wider problem. If you
|
|
77
|
-
want a continuously syncing, multi-device product with its own encrypted store, use it. Where the two differ
|
|
78
|
-
(from reading its README and adapter source, not from running it):
|
|
79
|
-
|
|
80
|
-
| | agent-carry | ctxhop |
|
|
81
|
-
|---|---|---|
|
|
82
|
-
| Scope | Claude Code sessions; share one, or sync a project through a folder | Claude Code + Codex, cross-agent switch, workspace and Git handoff, device authorisation, key rotation |
|
|
83
|
-
| Setup | none for a file; a shared folder for sync | `init` with an S3/R2/directory backend, passphrase, device invites |
|
|
84
|
-
| Runtime | Node, zero dependencies | Go binary, installers |
|
|
85
|
-
| Paths inside commands, prose and tool output | rewritten (boundary-aware); 0 leaks on 1,150 real sessions | left alone by design (only known path fields and bare paths are tokenised), so source paths and usernames stay in the history |
|
|
86
|
-
| Secrets inside transcript content | redacted before leaving the machine, with a report | not scanned; protected by encryption in transit and at rest |
|
|
87
|
-
| Sharing with someone else | a single inspectable file | designed for your own authorised devices |
|
|
88
|
-
| Inspectability | newline-delimited JSON, readable with `jq` | encrypted objects in a remote |
|
|
89
|
-
| Platforms | macOS, Linux | adds Windows |
|
|
90
|
-
|
|
91
|
-
So: carry is better when you want to *hand a session to someone*, keep secrets out of it, or sync with
|
|
92
|
-
nothing but a folder. ctxhop is better for an always-on personal sync across many devices and agents.
|
|
93
|
-
|
|
94
|
-
## What is verified
|
|
95
|
-
|
|
96
|
-
- `npm test`: 36 tests on synthetic two-machine setups: planted secrets and paths, merge, tampering, encryption, folder sync convergence.
|
|
97
|
-
- `npm run check:real` round-trips every session in your own `~/.claude` into a temp store under a different
|
|
98
|
-
root, read-only on the real one. On the author's 1,150 sessions (58k records): no record lost, no dangling
|
|
99
|
-
parent that wasn't already dangling, no source path left outside thinking blocks, re-import byte-identical.
|
|
100
|
-
The same run redacted 200+ credential-shaped strings, so the problem the redactor solves is not hypothetical.
|
|
101
|
-
|
|
102
|
-
## What is not verified, or not handled
|
|
103
|
-
|
|
104
|
-
- **`claude --resume` on an imported session** is verified on one real session (900 KB, 418 paths rewritten,
|
|
105
|
-
Claude Code 2.1.299): resumed from a folder it never ran in, it recalled its first request and reported the new
|
|
106
|
-
working directory. That is one session on one version. Run `scripts/verify-resume.sh` on yours. The transcript
|
|
107
|
-
format is internal and undocumented and can change; carry parses tolerantly and carries unknown lines through.
|
|
108
|
-
- **Thinking blocks are signed**, so they are carried verbatim: not path-rewritten, not redacted. If one contains
|
|
109
|
-
a credential-like string, export **refuses** unless you pass `--strip-thinking` (drop thinking blocks; resume
|
|
110
|
-
with stripped thinking is untested) or `--allow-thinking-secrets`. `carry push` skips those sessions.
|
|
111
|
-
- **Redaction is pattern-based and best effort.** It will not find a password that looks like a sentence, or a
|
|
112
|
-
letters-only lower-case `password=correcthorse` (lower-case names need a digit in the value, to avoid mangling
|
|
113
|
-
code like `token = parseToken(x)`). ALL_CAPS names like `DB_PASSWORD=` are always redacted. Base64 is only
|
|
114
|
-
caught when it decodes to something the other rules catch. A bundle
|
|
115
|
-
is not encrypted. Don't commit one to a public repo, and read `carry inspect` before sharing.
|
|
116
|
-
- **Cowork / desktop**: not checked. If it stores sessions somewhere else, it needs its own adapter.
|
|
117
|
-
- **Push re-uploads sessions you pulled** (one file per device, merged on pull), so folder size grows with devices x sessions.
|
|
118
|
-
- **Windows paths** (drive letters, backslashes) are not handled. Folder names longer than ~200 characters may
|
|
119
|
-
be shortened differently by Claude Code; import warns.
|
|
120
|
-
- **File-history backups** (`~/.claude/file-history`, used for rewind) are not carried.
|
|
121
|
-
- Relative traversal text like `../../Users/x` is tokenised for privacy, so it is cosmetically wrong after import.
|
|
122
|
-
|
|
123
|
-
## Verify resume yourself
|
|
55
|
+
| `carry push DIR` / `carry pull DIR` | folder sync (`--name`, `--dry-run`, `--quiet`) |
|
|
124
56
|
|
|
125
|
-
|
|
126
|
-
node src/cli.js list --all # pick a session with real content
|
|
127
|
-
sh scripts/verify-resume.sh <session-id-prefix>
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Exports the session, imports it into a throwaway folder, runs `claude -p --resume` there, and deletes both the
|
|
131
|
-
folder and the imported copy. The original is only read. PASS means the answer matches that session's first
|
|
132
|
-
request and names the new folder.
|
|
57
|
+
## Limits
|
|
133
58
|
|
|
134
|
-
|
|
59
|
+
- **Resume** is verified on one real session (Claude Code 2.1.299); the transcript format is internal and can change. Run `scripts/verify-resume.sh <id>` to check yours.
|
|
60
|
+
- **Thinking blocks are signed**, so they travel verbatim. If one contains a credential-like string, export refuses unless you pass `--strip-thinking` (resume with stripped thinking is untested) or `--allow-thinking-secrets`. `push` skips such sessions.
|
|
61
|
+
- **Redaction is best effort.** It misses passwords that read like sentences and letters-only lowercase `password=word`. Read `carry inspect` before sharing, and encrypt anything you send to someone else.
|
|
62
|
+
- Not handled: Windows paths, Cowork/desktop sessions, file-history (rewind) backups.
|
|
63
|
+
- `push` re-uploads pulled sessions, so a sync folder grows with devices × sessions.
|
|
135
64
|
|
|
136
|
-
|
|
137
|
-
session and its sidecar files are found and read (`src/store.js`), how a transcript line is identified and
|
|
138
|
-
summarised (`src/transcript.js`), and the `AGENT` constant stamped into bundles (`src/bundle.js`). Path rewriting,
|
|
139
|
-
redaction, merging, encryption and folder sync work on parsed JSON and don't care which agent produced it.
|
|
140
|
-
An adapter for Codex would supply those four pieces for its own session format.
|
|
141
|
-
|
|
142
|
-
## Layout
|
|
65
|
+
## Development
|
|
143
66
|
|
|
144
67
|
```
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
src/redact.js secret patterns
|
|
148
|
-
src/merge.js uuid union; local lines kept first
|
|
149
|
-
src/store.js list / read a session and its sidecar files
|
|
150
|
-
src/bundle.js build, read, import; integrity hash; path-traversal guard
|
|
151
|
-
src/crypto.js passphrase encryption (scrypt + AES-256-GCM)
|
|
152
|
-
src/sync.js push / pull over a shared folder
|
|
153
|
-
src/cli.js carry
|
|
68
|
+
npm test # 36 tests, synthetic two-machine setups
|
|
69
|
+
npm run check:real # read-only round trip of your own ~/.claude sessions
|
|
154
70
|
```
|
|
155
71
|
|
|
156
|
-
|
|
72
|
+
To add an agent, supply its session-folder mapping (`src/paths.js`), session reader (`src/store.js`), transcript
|
|
73
|
+
record identity (`src/transcript.js`) and the `AGENT` constant (`src/bundle.js`). Path rewriting, redaction,
|
|
74
|
+
merging, encryption and sync are agent-independent. Codex is not implemented yet.
|
|
75
|
+
|
|
76
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-carry",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Move coding-agent sessions between machines and folders: path-portable, redacted, encrypted, merge-safe bundles. Claude Code today; adapter-ready for Codex and others.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"carry": "src/cli.js"
|
|
7
|
+
"carry": "src/cli.js",
|
|
8
|
+
"agent-carry": "src/cli.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"src",
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"name": "carry",
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"description": "Move coding-agent sessions between machines and folders: /carry:share exports a redacted, path-portable bundle, /carry:pickup imports one.",
|
|
5
|
-
"author": { "name": "
|
|
5
|
+
"author": { "name": "Harsh Gajjar", "email": "harshgajjar2812@gmail.com" },
|
|
6
6
|
"license": "MIT"
|
|
7
7
|
}
|