ccs-cloner
v0.3.5
Published
CLI tool to clone and modify Claude Code sessions
Maintainers
Readme
ccs-cloner
Clone and modify Claude Code sessions with context reduction.
Why This Exists
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.
ccs-cloner creates a lean copy of a session by:
- Removing tool calls from old turns while preserving recent ones
- Truncating tool content in intermediate turns for gradual context reduction
- Automatically removing thinking blocks when
--strip-toolsis used
The cloned session appears in claude --resume and can be continued with reduced context.
Installation
# Install globally via npm
npm install -g ccs-cloner
# Or via bun
bun add -g ccs-cloner
# Or build from source
git clone https://github.com/liminal-ai/ccs-cloner.git
cd ccs-cloner
bun install && bun linkRequires Bun 1.0+ or Node.js 18+.
Quick Start
TIP: Configure Claude Code to show session ID in status line for easy access.
# List recent sessions to find the session ID
ccs-cloner list
# Clone with default preset (keep 20 tool-turns)
ccs-cloner clone abc123 --strip-tools
# Clone with aggressive preset (keep 10 tool-turns)
ccs-cloner clone abc123 --strip-tools=aggressive
# Clone with extreme preset (remove all tools)
ccs-cloner clone abc123 --strip-tools=extreme
# Get detailed info about a session before cloning
ccs-cloner info abc123Presets
Tool removal uses presets that define how many "turns with tools" to keep and how many of those to truncate.
| Preset | Keep | Truncate | Behavior |
|--------|------|----------|----------|
| default | 20 turns | 50% | 10 truncated, 10 full fidelity |
| aggressive | 10 turns | 50% | 5 truncated, 5 full fidelity |
| extreme | 0 | - | All tools removed |
Using --strip-tools or --strip-tools=default applies your configured default preset (initially default, configurable via defaultPreset in config).
How Presets Work
Tool removal targets turns that have tool calls, not all turns. This ensures consistent behavior across multiple clones of the same session.
Of the kept turns:
- Oldest portion (truncate %) -> tool content reduced to ~2 lines
- Newest portion -> full fidelity preserved
This provides graceful degradation: recent tool context is preserved exactly, older tool context is summarized, and ancient tool context is removed entirely.
Custom Presets
Define in ccs-cloner.config.ts:
export default {
customPresets: {
minimal: { name: "minimal", keepTurnsWithTools: 5, truncatePercent: 80 },
thorough: { name: "thorough", keepTurnsWithTools: 30, truncatePercent: 30 },
},
defaultPreset: "minimal", // Use custom preset as default
};Use with: ccs-cloner clone abc123 --strip-tools=minimal
Commands
clone
Clone a session with optional modifications.
ccs-cloner clone <sessionId> [options]Arguments:
sessionId- Session UUID to clone (required)
Options:
| Flag | Description |
|------|-------------|
| --strip-tools | Remove tools using default preset |
| --strip-tools=<preset> | Remove tools using named preset (default, aggressive, extreme, or custom) |
| --dsp | Include --dangerously-skip-permissions in resume command |
| --output, -o <path> | Output path (default: auto-generated in same project directory) |
| --claude-dir <path> | Claude data directory (default: ~/.claude) |
| --json | Output result as JSON |
| --verbose, -v | Verbose output with statistics |
Examples:
# Default preset: keep 20 tool-turns
ccs-cloner clone abc-123-def --strip-tools
# Aggressive: keep only 10 tool-turns
ccs-cloner clone abc-123-def --strip-tools=aggressive
# Extreme: remove all tools
ccs-cloner clone abc-123-def --strip-tools=extreme
# Include --dangerously-skip-permissions in resume command
ccs-cloner clone abc-123-def --strip-tools --dsp
# Custom output location
ccs-cloner clone abc-123-def --strip-tools -o ./backup.jsonl
# JSON output for scripting
ccs-cloner clone abc-123-def --json --strip-toolslist
List Claude Code sessions.
ccs-cloner list [options]Options:
| Flag | Description |
|------|-------------|
| --project, -p <path> | Filter by project path (substring match) |
| --limit, -n <count> | Maximum sessions to show (default: 20) |
| --claude-dir <path> | Claude data directory (default: ~/.claude) |
| --json | Output as JSON |
| --verbose, -v | Show additional details |
Examples:
# List 20 most recent sessions
ccs-cloner list
# Filter by project path
ccs-cloner list -p my-project
# Show more sessions
ccs-cloner list -n 50info
Show detailed information about a session.
ccs-cloner info <sessionId> [options]Arguments:
sessionId- Session UUID to inspect (required)
Options:
| Flag | Description |
|------|-------------|
| --claude-dir <path> | Claude data directory (default: ~/.claude) |
| --json | Output as JSON |
| --verbose, -v | Show additional details |
Output includes:
- Session ID and project path
- Turn count and entry count
- File size and timestamps
- Tool call count
- Whether session contains thinking blocks
Configuration
ccs-cloner uses c12 for configuration loading.
Config File
Create ccs-cloner.config.ts in your project root:
import type { UserConfiguration } from "ccs-cloner";
const config: UserConfiguration = {
// Override Claude data directory
claudeDataDirectory: "/custom/path/to/.claude",
// Default preset when --strip-tools has no value
defaultPreset: "default",
// Custom presets
customPresets: {
minimal: { name: "minimal", keepTurnsWithTools: 5, truncatePercent: 80 },
},
// Default output format: "human" or "json"
outputFormat: "human",
// Enable verbose output by default
verboseOutput: false,
};
export default config;Supported config file names:
ccs-cloner.config.tsccs-cloner.config.js.ccs-clonerrc.ccs-clonerrc.json.ccs-clonerrc.yaml
Environment Variables
| Variable | Description |
|----------|-------------|
| CCS_CLONER_CLAUDE_DIR | Claude data directory |
| CCS_CLONER_OUTPUT_FORMAT | Output format (human or json) |
| CCS_CLONER_VERBOSE | Enable verbose output (true or false) |
Precedence
Configuration sources are merged in order (later overrides earlier):
- Defaults
- Config file
- Environment variables
- CLI flags
How It Works
Active Branch Extraction (Disabled)
Note: This feature is currently disabled due to issues with cross-file parent references (subagent sessions). It is being evaluated for fixing or removal.
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.
When enabled, ccs-cloner walks the parentUuid chain from the leaf node back to the root, keeping only entries in the active conversation path. Orphaned branches would be discarded.
Tool Removal Algorithm
Tool removal uses a "keep last N turns-with-tools" model:
- Identify: Find all turns that contain tool calls
- Keep: Preserve the last N of those turns
- Truncate: Of kept turns, truncate the oldest X%
- Remove: Remove tools from everything else
This ensures consistent behavior across multiple clones. Unlike percentage-based removal, this algorithm doesn't degrade over repeated clones.
Thinking Block Removal
When --strip-tools is used on a session containing tools, all thinking blocks are automatically removed from the entire session. This is because thinking blocks often reference tool results that may no longer exist.
Session Index Update
After writing the cloned session file, ccs-cloner:
- Creates the session's todos file
- Creates the session-env directory
- Updates the project's
sessions-index.json
This ensures the cloned session appears in claude --resume.
SDK Usage
ccs-cloner exports its core functions for programmatic use:
import {
executeCloneOperation,
listAllProjects,
listSessionsInProject,
findSessionFileById,
parseSessionFile,
removeToolCallsFromHistory,
BUILT_IN_PRESETS,
resolveToolRemovalOptions,
} from "ccs-cloner";
// Clone a session with default preset
const result = await executeCloneOperation({
sourceSessionId: "abc-123-def",
toolRemovalConfig: {
preset: "default",
},
});
console.log(result.clonedSessionId);
console.log(result.operationStatistics);
// Clone with aggressive preset
const aggressiveResult = await executeCloneOperation({
sourceSessionId: "abc-123-def",
toolRemovalConfig: {
preset: "aggressive",
},
});
// Clone with custom values (override preset)
const customResult = await executeCloneOperation({
sourceSessionId: "abc-123-def",
toolRemovalConfig: {
preset: "default",
keepTurnsWithTools: 15, // override preset value
truncatePercent: 75, // override preset value
},
});
// List all projects
const projects = await listAllProjects();
for (const project of projects) {
console.log(project.path, project.folder);
}
// Find and parse a session
const sessionPath = await findSessionFileById("abc-123-def");
const { entries } = await parseSessionFile(sessionPath);
// Remove tools from entries directly
const resolved = resolveToolRemovalOptions({ preset: "default" });
const removalResult = removeToolCallsFromHistory(entries, resolved);
console.log(removalResult.statistics.turnsWithToolsRemoved);
console.log(removalResult.statistics.turnsWithToolsTruncated);
console.log(removalResult.statistics.turnsWithToolsPreserved);Exported Functions
Core Operations:
executeCloneOperation(options)- Full clone pipelineremoveToolCallsFromHistory(entries, options)- Remove/truncate toolsfilterCloneableEntries(entries)- Filter non-cloneable entry typesrepairBrokenParentReferences(entries)- Fix parent chain after filteringidentifyTurnBoundaries(entries)- Calculate turn boundariescountTurns(entries)- Count turns in session
Preset System:
BUILT_IN_PRESETS- Built-in preset definitionsresolvePreset(name, customPresets?)- Resolve preset by nameresolveToolRemovalOptions(options, customPresets?)- Resolve to concrete valuesisValidPresetName(name, customPresets?)- Check if preset existslistAvailablePresets(customPresets?)- List all preset names
IO Operations:
findSessionFileById(sessionId, claudeDir?)- Find session file pathlistAllProjects(claudeDir?)- List all project directorieslistSessionsInProject(projectPath)- List sessions in a projectparseSessionFile(path)- Parse JSONL session fileparseSessionContent(content)- Parse JSONL stringserializeSessionEntries(entries)- Serialize entries to JSONLwriteSessionFile(path, content)- Write session fileaddSessionToIndex(projectDir, sessionId, path, metadata)- Update index
Configuration:
loadConfiguration(cliConfig?)- Load merged configurationgetDefaultClaudeDir()- Get default Claude directory path
Exported Types
import type {
// Session types
SessionLineItem,
ContentBlock,
ToolUseBlock,
ToolResultBlock,
ThinkingBlock,
// Operation types
CloneOperationOptions,
CloneOperationResult,
CloneOperationStatistics,
ToolRemovalOptions,
ToolRemovalResult,
ToolRemovalStatistics,
ToolRemovalPreset,
ResolvedToolRemovalOptions,
// Configuration
UserConfiguration,
ResolvedConfiguration,
} from "ccs-cloner";Development
# Install dependencies
bun install
# Run tests
bun test
# Type check
bun run typecheck
# Lint
bun run lint
# Format
bun run format
# Build
bun run buildProject Structure
src/
cli.ts # CLI entry point
index.ts # SDK exports
commands/ # CLI command definitions
config/ # Configuration and presets
core/ # Core logic (extraction, removal, etc.)
io/ # File system operations
output/ # Output formatting
types/ # TypeScript type definitions
errors/ # Custom error classesChangelog
See CHANGELOG.md for version history.
License
MIT
