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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +388 -0
  3. package/dist/cli.js +29387 -0
  4. 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