ccs-cloner 0.2.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/LICENSE +21 -0
- package/README.md +388 -0
- package/dist/cli.js +29387 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Lee Moore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# ccs-cloner
|
|
2
|
+
|
|
3
|
+
Clone and modify Claude Code sessions with context reduction.
|
|
4
|
+
|
|
5
|
+
## Why This Exists
|
|
6
|
+
|
|
7
|
+
Claude Code sessions accumulate context over time: tool calls with large inputs/outputs, orphaned conversation branches from rollbacks, and thinking blocks. When sessions hit context limits, continuation becomes impossible.
|
|
8
|
+
|
|
9
|
+
ccs-cloner creates a lean copy of a session by:
|
|
10
|
+
|
|
11
|
+
1. Extracting only the active conversation branch (discarding orphaned rollback branches)
|
|
12
|
+
2. Removing tool calls from early turns where detailed results no longer matter
|
|
13
|
+
3. Optionally truncating remaining tool content instead of full removal
|
|
14
|
+
4. Automatically removing thinking blocks when tools are modified
|
|
15
|
+
|
|
16
|
+
The cloned session appears in `claude --resume` and can be continued with reduced context.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Clone and build from source
|
|
22
|
+
git clone https://github.com/liminal-ai/ccs-cloner.git
|
|
23
|
+
cd ccs-cloner
|
|
24
|
+
bun install
|
|
25
|
+
bun link
|
|
26
|
+
|
|
27
|
+
# Or install from npm (when published)
|
|
28
|
+
# bun add -g ccs-cloner
|
|
29
|
+
# npm install -g ccs-cloner
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Requires Bun 1.0+ or Node.js 20+.
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# List recent sessions to find the session ID
|
|
38
|
+
ccs-cloner list
|
|
39
|
+
|
|
40
|
+
# Clone a session with default 80% tool removal
|
|
41
|
+
ccs-cloner clone abc123 --strip-tools
|
|
42
|
+
|
|
43
|
+
# Clone with higher removal for very long sessions
|
|
44
|
+
ccs-cloner clone abc123 --strip-tools=95
|
|
45
|
+
|
|
46
|
+
# Clone and truncate (not remove) the remaining 20% of tools
|
|
47
|
+
ccs-cloner clone abc123 --strip-tools --truncate-remaining
|
|
48
|
+
|
|
49
|
+
# Get detailed info about a session before cloning
|
|
50
|
+
ccs-cloner info abc123
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Commands
|
|
54
|
+
|
|
55
|
+
### clone
|
|
56
|
+
|
|
57
|
+
Clone a session with optional modifications.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
ccs-cloner clone <sessionId> [options]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Arguments:**
|
|
64
|
+
|
|
65
|
+
- `sessionId` - Session UUID to clone (required)
|
|
66
|
+
|
|
67
|
+
**Options:**
|
|
68
|
+
|
|
69
|
+
| Flag | Description |
|
|
70
|
+
|------|-------------|
|
|
71
|
+
| `--strip-tools` | Remove tools from first 80% of turns |
|
|
72
|
+
| `--strip-tools=N` | Remove tools from first N% of turns (0-100) |
|
|
73
|
+
| `--truncate-remaining` | Truncate tool content in turns not fully stripped. Reduces tool inputs/outputs to 2 lines or 120 chars. Requires `--strip-tools` |
|
|
74
|
+
| `--output, -o <path>` | Output path (default: auto-generated in same project directory) |
|
|
75
|
+
| `--claude-dir <path>` | Claude data directory (default: `~/.claude`) |
|
|
76
|
+
| `--json` | Output result as JSON |
|
|
77
|
+
| `--verbose, -v` | Verbose output with statistics |
|
|
78
|
+
|
|
79
|
+
**Examples:**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Default: remove tools from 80% of turns
|
|
83
|
+
ccs-cloner clone abc-123-def --strip-tools
|
|
84
|
+
|
|
85
|
+
# Aggressive: remove from 95% of turns
|
|
86
|
+
ccs-cloner clone abc-123-def --strip-tools=95
|
|
87
|
+
|
|
88
|
+
# Moderate removal + truncation of remaining tools
|
|
89
|
+
ccs-cloner clone abc-123-def --strip-tools=60 --truncate-remaining
|
|
90
|
+
|
|
91
|
+
# Custom output location
|
|
92
|
+
ccs-cloner clone abc-123-def --strip-tools -o ./backup.jsonl
|
|
93
|
+
|
|
94
|
+
# JSON output for scripting
|
|
95
|
+
ccs-cloner clone abc-123-def --strip-tools --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### list
|
|
99
|
+
|
|
100
|
+
List Claude Code sessions.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ccs-cloner list [options]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Options:**
|
|
107
|
+
|
|
108
|
+
| Flag | Description |
|
|
109
|
+
|------|-------------|
|
|
110
|
+
| `--project, -p <path>` | Filter by project path (substring match) |
|
|
111
|
+
| `--limit, -n <count>` | Maximum sessions to show (default: 20) |
|
|
112
|
+
| `--claude-dir <path>` | Claude data directory (default: `~/.claude`) |
|
|
113
|
+
| `--json` | Output as JSON |
|
|
114
|
+
| `--verbose, -v` | Show additional details |
|
|
115
|
+
|
|
116
|
+
**Examples:**
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# List 20 most recent sessions
|
|
120
|
+
ccs-cloner list
|
|
121
|
+
|
|
122
|
+
# Filter by project path
|
|
123
|
+
ccs-cloner list -p my-project
|
|
124
|
+
|
|
125
|
+
# Show more sessions
|
|
126
|
+
ccs-cloner list -n 50
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### info
|
|
130
|
+
|
|
131
|
+
Show detailed information about a session.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
ccs-cloner info <sessionId> [options]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Arguments:**
|
|
138
|
+
|
|
139
|
+
- `sessionId` - Session UUID to inspect (required)
|
|
140
|
+
|
|
141
|
+
**Options:**
|
|
142
|
+
|
|
143
|
+
| Flag | Description |
|
|
144
|
+
|------|-------------|
|
|
145
|
+
| `--claude-dir <path>` | Claude data directory (default: `~/.claude`) |
|
|
146
|
+
| `--json` | Output as JSON |
|
|
147
|
+
| `--verbose, -v` | Show additional details |
|
|
148
|
+
|
|
149
|
+
**Output includes:**
|
|
150
|
+
|
|
151
|
+
- Session ID and project path
|
|
152
|
+
- Turn count and entry count
|
|
153
|
+
- File size and timestamps
|
|
154
|
+
- Tool call count
|
|
155
|
+
- Whether session contains thinking blocks
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
ccs-cloner uses [c12](https://github.com/unjs/c12) for configuration loading.
|
|
160
|
+
|
|
161
|
+
### Config File
|
|
162
|
+
|
|
163
|
+
Create `ccs-cloner.config.ts` in your project root:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import type { UserConfiguration } from "ccs-cloner";
|
|
167
|
+
|
|
168
|
+
const config: UserConfiguration = {
|
|
169
|
+
// Override Claude data directory
|
|
170
|
+
claudeDataDirectory: "/custom/path/to/.claude",
|
|
171
|
+
|
|
172
|
+
// Default percentage when --strip-tools has no value (default: 80)
|
|
173
|
+
defaultToolRemovalPercentage: 80,
|
|
174
|
+
|
|
175
|
+
// Default output format: "human" or "json"
|
|
176
|
+
outputFormat: "human",
|
|
177
|
+
|
|
178
|
+
// Enable verbose output by default
|
|
179
|
+
verboseOutput: false,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export default config;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Supported config file names:
|
|
186
|
+
|
|
187
|
+
- `ccs-cloner.config.ts`
|
|
188
|
+
- `ccs-cloner.config.js`
|
|
189
|
+
- `.ccs-clonerrc`
|
|
190
|
+
- `.ccs-clonerrc.json`
|
|
191
|
+
- `.ccs-clonerrc.yaml`
|
|
192
|
+
|
|
193
|
+
### Environment Variables
|
|
194
|
+
|
|
195
|
+
| Variable | Description |
|
|
196
|
+
|----------|-------------|
|
|
197
|
+
| `CCS_CLONER_CLAUDE_DIR` | Claude data directory |
|
|
198
|
+
| `CCS_CLONER_OUTPUT_FORMAT` | Output format (`human` or `json`) |
|
|
199
|
+
| `CCS_CLONER_VERBOSE` | Enable verbose output (`true` or `false`) |
|
|
200
|
+
|
|
201
|
+
### Precedence
|
|
202
|
+
|
|
203
|
+
Configuration sources are merged in order (later overrides earlier):
|
|
204
|
+
|
|
205
|
+
1. Defaults
|
|
206
|
+
2. Config file
|
|
207
|
+
3. Environment variables
|
|
208
|
+
4. CLI flags
|
|
209
|
+
|
|
210
|
+
## How It Works
|
|
211
|
+
|
|
212
|
+
### Active Branch Extraction
|
|
213
|
+
|
|
214
|
+
Claude Code sessions are stored as JSONL files with a tree structure using `uuid` and `parentUuid` fields. When you use rollback or continue from an earlier point, the old branch becomes orphaned but remains in the file.
|
|
215
|
+
|
|
216
|
+
ccs-cloner walks the `parentUuid` chain from the leaf node (identified via `summary.leafUuid` or latest timestamp) back to the root, keeping only entries in the active conversation path. Orphaned branches are discarded.
|
|
217
|
+
|
|
218
|
+
### Tool Removal Zones
|
|
219
|
+
|
|
220
|
+
Tool removal operates on "turns" (user message + assistant response pairs). With `--strip-tools=80`:
|
|
221
|
+
|
|
222
|
+
1. Calculate 80% of total turns
|
|
223
|
+
2. In the first 80% of turns: completely remove all `tool_use` and `tool_result` blocks
|
|
224
|
+
3. In the remaining 20%: tools are preserved (or truncated if `--truncate-remaining`)
|
|
225
|
+
|
|
226
|
+
The percentage calculation uses `Math.max(1, Math.floor(...))` to ensure at least one turn is affected when percentage > 0.
|
|
227
|
+
|
|
228
|
+
### Thinking Block Removal
|
|
229
|
+
|
|
230
|
+
When any tools are removed (`--strip-tools` with percentage > 0), all thinking blocks are automatically removed from the entire session. This is because thinking blocks often reference tool results that may no longer exist.
|
|
231
|
+
|
|
232
|
+
### Session Index Update
|
|
233
|
+
|
|
234
|
+
After writing the cloned session file, ccs-cloner:
|
|
235
|
+
|
|
236
|
+
1. Creates the session's todos file
|
|
237
|
+
2. Creates the session-env directory
|
|
238
|
+
3. Updates the project's `sessions-index.json`
|
|
239
|
+
|
|
240
|
+
This ensures the cloned session appears in `claude --resume`.
|
|
241
|
+
|
|
242
|
+
## SDK Usage
|
|
243
|
+
|
|
244
|
+
ccs-cloner exports its core functions for programmatic use:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import {
|
|
248
|
+
executeCloneOperation,
|
|
249
|
+
listAllProjects,
|
|
250
|
+
listSessionsInProject,
|
|
251
|
+
findSessionFileById,
|
|
252
|
+
parseSessionFile,
|
|
253
|
+
extractActiveBranchFromSession,
|
|
254
|
+
removeToolCallsFromHistory,
|
|
255
|
+
} from "ccs-cloner";
|
|
256
|
+
|
|
257
|
+
// Clone a session programmatically
|
|
258
|
+
const result = await executeCloneOperation({
|
|
259
|
+
sourceSessionId: "abc-123-def",
|
|
260
|
+
toolRemovalConfig: {
|
|
261
|
+
toolRemovalPercentage: 80,
|
|
262
|
+
truncateRemainingTools: true,
|
|
263
|
+
thinkingRemovalPercentage: 100,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
console.log(result.clonedSessionId);
|
|
268
|
+
console.log(result.operationStatistics);
|
|
269
|
+
|
|
270
|
+
// List all projects
|
|
271
|
+
const projects = await listAllProjects();
|
|
272
|
+
for (const project of projects) {
|
|
273
|
+
console.log(project.path, project.folder);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Find and parse a session
|
|
277
|
+
const sessionPath = await findSessionFileById("abc-123-def");
|
|
278
|
+
const { entries } = await parseSessionFile(sessionPath);
|
|
279
|
+
|
|
280
|
+
// Extract active branch from entries
|
|
281
|
+
const activeBranch = extractActiveBranchFromSession(entries);
|
|
282
|
+
console.log(activeBranch.extractionStatistics.orphanedEntriesDiscarded);
|
|
283
|
+
|
|
284
|
+
// Remove tools from entries
|
|
285
|
+
const removalResult = removeToolCallsFromHistory(activeBranch.entriesInActiveChain, {
|
|
286
|
+
toolRemovalPercentage: 80,
|
|
287
|
+
truncateRemainingTools: false,
|
|
288
|
+
thinkingRemovalPercentage: 100,
|
|
289
|
+
});
|
|
290
|
+
console.log(removalResult.statistics.toolCallsRemoved);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Exported Functions
|
|
294
|
+
|
|
295
|
+
**Core Operations:**
|
|
296
|
+
|
|
297
|
+
- `executeCloneOperation(options)` - Full clone pipeline
|
|
298
|
+
- `extractActiveBranchFromSession(entries, leafUuid?)` - Extract active branch
|
|
299
|
+
- `removeToolCallsFromHistory(entries, options)` - Remove/truncate tools
|
|
300
|
+
- `filterCloneableEntries(entries)` - Filter non-cloneable entry types
|
|
301
|
+
- `repairBrokenParentReferences(entries)` - Fix parent chain after filtering
|
|
302
|
+
- `identifyTurnBoundaries(entries)` - Calculate turn boundaries
|
|
303
|
+
- `countTurns(entries)` - Count turns in session
|
|
304
|
+
|
|
305
|
+
**IO Operations:**
|
|
306
|
+
|
|
307
|
+
- `findSessionFileById(sessionId, claudeDir?)` - Find session file path
|
|
308
|
+
- `listAllProjects(claudeDir?)` - List all project directories
|
|
309
|
+
- `listSessionsInProject(projectPath)` - List sessions in a project
|
|
310
|
+
- `parseSessionFile(path)` - Parse JSONL session file
|
|
311
|
+
- `parseSessionContent(content)` - Parse JSONL string
|
|
312
|
+
- `serializeSessionEntries(entries)` - Serialize entries to JSONL
|
|
313
|
+
- `writeSessionFile(path, content)` - Write session file
|
|
314
|
+
- `addSessionToIndex(projectDir, sessionId, path, metadata)` - Update index
|
|
315
|
+
|
|
316
|
+
**Configuration:**
|
|
317
|
+
|
|
318
|
+
- `loadConfiguration(cliConfig?)` - Load merged configuration
|
|
319
|
+
- `getDefaultClaudeDir()` - Get default Claude directory path
|
|
320
|
+
|
|
321
|
+
### Exported Types
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import type {
|
|
325
|
+
// Session types
|
|
326
|
+
SessionLineItem,
|
|
327
|
+
ContentBlock,
|
|
328
|
+
ToolUseBlock,
|
|
329
|
+
ToolResultBlock,
|
|
330
|
+
ThinkingBlock,
|
|
331
|
+
|
|
332
|
+
// Operation types
|
|
333
|
+
CloneOperationOptions,
|
|
334
|
+
CloneOperationResult,
|
|
335
|
+
CloneOperationStatistics,
|
|
336
|
+
ToolRemovalOptions,
|
|
337
|
+
ToolRemovalResult,
|
|
338
|
+
|
|
339
|
+
// Branch types
|
|
340
|
+
ActiveBranchChain,
|
|
341
|
+
UuidGraph,
|
|
342
|
+
|
|
343
|
+
// Configuration
|
|
344
|
+
UserConfiguration,
|
|
345
|
+
ResolvedConfiguration,
|
|
346
|
+
} from "ccs-cloner";
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Development
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Install dependencies
|
|
353
|
+
bun install
|
|
354
|
+
|
|
355
|
+
# Run tests
|
|
356
|
+
bun test
|
|
357
|
+
|
|
358
|
+
# Type check
|
|
359
|
+
bun run typecheck
|
|
360
|
+
|
|
361
|
+
# Lint
|
|
362
|
+
bun run lint
|
|
363
|
+
|
|
364
|
+
# Format
|
|
365
|
+
bun run format
|
|
366
|
+
|
|
367
|
+
# Build
|
|
368
|
+
bun run build
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Project Structure
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
src/
|
|
375
|
+
cli.ts # CLI entry point
|
|
376
|
+
index.ts # SDK exports
|
|
377
|
+
commands/ # CLI command definitions
|
|
378
|
+
core/ # Core logic (extraction, removal, etc.)
|
|
379
|
+
io/ # File system operations
|
|
380
|
+
config/ # Configuration loading
|
|
381
|
+
output/ # Output formatting
|
|
382
|
+
types/ # TypeScript type definitions
|
|
383
|
+
errors/ # Custom error classes
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## License
|
|
387
|
+
|
|
388
|
+
MIT
|