ChannelCoder
A streamlined SDK and CLI for Claude Code that channels your prompts with powerful features like multi-place variable interpolation, file-based prompts, and streaming support.
Installation
npm install channelcoder
bun add channelcoder
npm install -g channelcoder
Quick Start
SDK Usage
import { claude } from 'channelcoder';
const result = await claude('What is TypeScript?');
const language = 'TypeScript';
await claude`Explain ${language} in simple terms`;
await claude('prompts/analyze.md', {
data: { taskId: 'FEAT-123', priority: 'high' }
});
await claude('Review this code', {
tools: ['Read', 'Grep'],
system: 'You are a code reviewer'
});
CLI Usage
The CLI provides multiple commands for different execution modes:
channelcoder interactive prompts/analyze.md -d taskId=FEAT-123
channelcoder run "Explain TypeScript"
channelcoder run prompts/analyze.md --data taskId=FEAT-123
channelcoder stream "Write a story"
channelcoder stream prompts/generate.md
channelcoder session list
channelcoder session load my-session
channelcoder session remove old-session
channelcoder worktree list
channelcoder worktree create feature/new
channelcoder worktree remove feature/old
Features
🎯 Simple Function API
Mirrors Claude CLI's mental model with a simple function:
await claude('Explain quantum computing');
await claude('prompts/complex-analysis.md');
await claude('Debug this issue', {
tools: ['Read', 'Bash'],
resume: sessionId,
maxTurns: 10
});
🔄 Variable Interpolation
Powerful multi-place variable support:
await claude('Analyze {code} for {issues}', {
data: {
code: 'const x = null',
issues: ['null safety', 'type errors']
}
});
await claude('prompts/review.md', {
data: { prTitle: 'Add auth system' }
});
🌊 Execution Modes
Different modes for different needs:
import { claude, interactive, stream, run, detached } from 'channelcoder';
const result = await claude('Generate tests');
console.log(result.data);
for await (const chunk of stream('Write documentation')) {
process.stdout.write(chunk.content);
}
await interactive('Debug this error');
const result3 = await detached('Long analysis task', {
logFile: 'analysis.log'
});
console.log('Started background process:', result3.data?.pid);
const result4 = await detached('Generate comprehensive report', {
logFile: 'report.log',
stream: true
});
🌿 Git Worktree Mode
Run Claude in isolated git worktrees for parallel development and clean experimentation:
await claude('Implement new feature', {
worktree: 'feature/auth'
});
await worktree('feature/payments', async (wt) => {
console.log(`Working in: ${wt.path}`);
await claude('Design payment system');
await claude('Implement payment processing');
return 'feature-complete';
}, {
base: 'main',
cleanup: false
});
import { worktreeUtils } from 'channelcoder';
const worktrees = await worktreeUtils.list();
await worktreeUtils.create('experiment/new-arch', { base: 'develop' });
await worktreeUtils.remove('feature/old');
if (await worktreeUtils.exists('feature/auth')) {
console.log('Auth worktree already exists');
}
Why Use Worktrees?
- Parallel Development: Work on multiple features without stashing/switching
- Clean Experiments: Test risky changes in isolated environments
- Context Preservation: Each worktree maintains its own file state
- Session Enhancement: Combine with sessions for branch-specific conversations
Worktree Options
interface WorktreeOptions {
branch?: string;
base?: string;
path?: string;
cleanup?: boolean;
create?: boolean;
}
Composing Features
Worktrees work seamlessly with other ChannelCoder features:
const s = session();
await s.claude('Start OAuth feature', { worktree: 'feature/oauth' });
await s.claude('Add tests');
await claude('Test in total isolation', {
worktree: 'experiment/risky',
docker: true
});
await worktree('feature/docs', async () => {
for await (const chunk of stream('Generate documentation')) {
process.stdout.write(chunk.content);
}
});
🐳 Docker Mode
Run Claude in an isolated Docker container for enhanced security with dangerous permissions:
await claude('Risky operation', { docker: true });
await claude('Analyze system', {
docker: { image: 'my-claude:latest' }
});
await claude('Process data', {
docker: {
image: 'claude-sandbox',
mounts: ['./data:/data:ro'],
env: { NODE_ENV: 'production' }
}
});
Setting Up Docker Mode
- Create a Dockerfile with Claude CLI installed:
FROM node:20-slim
RUN npm install -g @anthropic-ai/claude-code
WORKDIR /workspace
docker build -t my-claude .
await claude('Task', { docker: { image: 'my-claude' } });
Auto-Detection
When using docker: true, channelcoder will:
- Look for a
Dockerfile in your project
- Build an image automatically (with caching)
- Mount your working directory as
/workspace
Docker Options
interface DockerOptions {
auto?: boolean;
image?: string;
dockerfile?: string;
mounts?: string[];
env?: Record<string, string>;
}
CLI Reference
channelcoder <command> [options]
Commands:
run Execute prompt and exit (print mode) - non-interactive
interactive Interactive mode with Claude (default)
stream Stream responses in real-time
session Manage conversation sessions
worktree Manage git worktrees
Global Options:
-h, --help Show help
-v, --version Show version
Run 'channelcoder <command> --help' for command-specific options
Command Details
channelcoder run [prompt-file] [options]
Execute a prompt and print the result without interaction.
Options:
-p, --prompt <text> Inline prompt instead of file
-d, --data <key=value> Data for interpolation (repeatable)
-s, --system <prompt> System prompt (text or .md file)
-t, --tools <tools> Allowed tools (e.g., "Read Write")
--disallowed-tools Disallowed tools (comma-separated)
--append-system <text> Append to system prompt
--mcp-config <file> Load MCP servers from JSON file
--permission-tool <tool> MCP tool for permission prompts
-r, --resume <id> Resume conversation by session ID
-c, --continue Continue most recent conversation
--max-turns <n> Limit agentic turns
--json Output JSON format
-v, --verbose Verbose output
channelcoder interactive [prompt-file] [options]
Launch Claude in interactive mode (default command).
Options: Same as 'run' command
channelcoder stream [prompt-file] [options]
Stream responses in real-time.
Options: Same as 'run' command
channelcoder session <subcommand>
Manage conversation sessions.
Subcommands:
list List all saved sessions
load Load and continue a session
remove Remove a saved session
channelcoder worktree <subcommand>
Manage git worktrees for isolated development.
Subcommands:
list List all worktrees
create Create a new worktree
remove Remove a worktree
cleanup Clean up orphaned worktrees
Advanced Features Documentation
For detailed guides on advanced features, see the /documentation folder:
Frontmatter Syntax
File-based prompts support YAML frontmatter for configuration. Here are the actually supported options:
---
systemPrompt: "You are a helpful coding assistant"
systemPrompt: "./system-prompts/analyst.md"
appendSystemPrompt: "Always explain your reasoning"
allowedTools:
- "Read"
- "Write"
- "Edit"
- "Bash"
- "Bash(git:*)"
- "Bash(npm:test)"
- "Grep"
- "WebSearch"
disallowedTools:
- "Bash(rm:*)"
- "Bash(git:push)"
mcpConfig: "./mcp-servers.json"
permissionPromptTool: "mcp__auth__prompt"
input:
name: string
age?: number
tags: string[]
config:
port: number
host?: string
output:
success: boolean
result:
type: string
enum: [feature, bug, chore]
items:
- name: string
done: boolean
---
Your prompt content here...
Note: The following options are passed via CLI or SDK, not frontmatter:
outputFormat - Use --json flag or claude(prompt, { outputFormat: 'json' })
stream - Use --stream flag or stream() function
verbose - Use --verbose flag or claude(prompt, { verbose: true })
timeout - SDK only: claude(prompt, { timeout: 30000 })
Frontmatter Validation
The frontmatter is validated using a Zod schema. Invalid keys will cause an error:
import { FrontmatterSchema, type Frontmatter } from 'channelcoder';
const result = FrontmatterSchema.safeParse({
systemPrompt: "Valid",
temperature: 0.7
});
const config: Frontmatter = {
systemPrompt: "Assistant prompt",
appendSystemPrompt: "Be concise",
allowedTools: ["Read", "Write"],
disallowedTools: ["Bash(rm:*)"],
mcpConfig: "./mcp-servers.json",
permissionPromptTool: "mcp__auth__prompt",
input: { name: "string" },
output: { success: "boolean" }
};
Schema Definition Format
Schemas are defined using YAML notation that's automatically converted to Zod schemas:
---
input:
name: string
age?: number
tags: string[]
metadata:
created: string
updated?: string
---
Supported types:
string - Text values
number - Numeric values
boolean or bool - True/false values
array or type[] - Arrays
object - Nested objects
any - Any value
Note: For programmatic SDK usage, you can also use Zod schemas directly in the validation utilities.
Examples
File-based Prompts
Create a prompt file analyze.md:
---
input:
task: string
details?: boolean
systemPrompt: "You are a helpful analyst"
allowedTools:
- Read
- Grep
---
{details ? "Provide detailed breakdown." : "Summary only."}
Run it:
channelcoder analyze.md -d task="Review PR" -d details=true
Inline Prompts
channelcoder -p "Explain {concept}" -d concept="quantum computing"
channelcoder -p "Find files containing {pattern}" \
-d pattern="TODO" \
-t "Read Grep"
channelcoder -p "Write a haiku about {topic}" \
-d topic="coding" \
--stream
channelcoder --resume abc123 -p "Continue with the implementation"
channelcoder --continue -p "What about error handling?"
channelcoder analyze.md --max-turns 3
channelcoder query.md --mcp-config ./servers.json
channelcoder cleanup.md --disallowed-tools "Bash(rm:*),Bash(git:push)"
Complex Data
channelcoder -p "Process items: {items}" \
-d 'items=["apple","banana","orange"]'
channelcoder -p "Config: {config}" \
-d 'config={"port":3000,"host":"localhost"}'
SDK Reference
Basic Usage
import { claude } from 'channelcoder';
const result = await claude('Explain TypeScript');
const topic = 'async/await';
const result = await claude`Explain ${topic} with examples`;
const result = await claude('prompts/analyze.md', {
data: { taskId: 'FEAT-123' }
});
Options
const result = await claude('Your prompt', {
data: { key: 'value' },
system: 'You are a helpful assistant',
appendSystem: 'Be concise',
tools: ['Read', 'Write', 'Bash(git:*)'],
disallowedTools: ['Bash(rm:*)'],
mcpConfig: './mcp-servers.json',
permissionTool: 'mcp__auth__prompt',
resume: 'session-id-here',
continue: true,
maxTurns: 10,
mode: 'run',
includeEvents: true,
detached: true,
logFile: 'output.log',
stream: true,
verbose: true,
outputFormat: 'json',
timeout: 60000
});
Streaming
import { stream } from 'channelcoder';
for await (const chunk of stream('Generate a story')) {
if (chunk.type === 'content') {
process.stdout.write(chunk.content);
}
}
Error Handling
const result = await claude('prompt.md', { data });
if (!result.success) {
console.error('Error:', result.error);
if (result.warnings) {
console.warn('Warnings:', result.warnings);
}
}
if (result.warnings?.includes('max turns reached')) {
console.log('Hit maximum turn limit');
}
Interactive Mode
import { interactive } from 'channelcoder';
await interactive('Help me debug this issue');
Important: Interactive mode completely replaces your Node.js process with Claude:
- No Node.js parent process remains in memory
- Claude gets direct terminal control
- Exit codes go directly to the shell
- Perfect for long Claude sessions without memory overhead
Background Execution & Real-time Monitoring
ChannelCoder supports background execution with real-time monitoring capabilities:
import { detached, session } from 'channelcoder';
const result = await detached('Analyze large codebase', {
logFile: 'analysis.log'
});
console.log('Background process started with PID:', result.data?.pid);
const result2 = await detached('Generate comprehensive report', {
logFile: 'report.log',
stream: true
});
Session Background Execution:
const s = session({
autoSave: true
});
await s.detached('Long-running analysis', {
logFile: 'session-output.log',
stream: true
});
Stream Parser SDK
ChannelCoder includes a powerful Stream Parser SDK for parsing Claude's stream-json output from detached sessions and log files:
import { parseLogFile, monitorLog, streamParser } from 'channelcoder';
const parsed = await parseLogFile('session.log');
console.log(parsed.content);
console.log(parsed.totalCost);
console.log(parsed.events.length);
const cleanup = monitorLog('active.log', (event) => {
if (streamParser.isAssistantEvent(event)) {
console.log('Claude:', streamParser.extractAssistantText(event));
} else if (streamParser.isToolUseEvent(event)) {
console.log('Tool used:', event.tool);
} else if (streamParser.isResultEvent(event)) {
console.log('Completed! Cost:', event.cost_usd);
}
});
cleanup();
import { parseStreamEvent, eventToChunk } from 'channelcoder/streamParser';
const line = '{"type":"assistant","message":{...}}';
const event = parseStreamEvent(line);
const chunk = eventToChunk(event);
Type Guards for Event Handling:
import {
isSystemEvent,
isAssistantEvent,
isResultEvent,
isToolUseEvent,
isErrorEvent
} from 'channelcoder';
function processEvent(event: ClaudeEvent) {
if (isAssistantEvent(event)) {
const text = streamParser.extractAssistantText(event);
console.log('Assistant:', text);
} else if (isToolUseEvent(event)) {
console.log('Tool:', event.tool, event.input);
} else if (isResultEvent(event)) {
if (event.subtype === 'error') {
console.error('Failed:', event.error);
} else {
console.log('Success! Cost: $', event.cost_usd);
}
}
}
See examples/task-monitor-tui.ts for a complete real-time monitoring TUI built with the Stream Parser SDK.
Session Management
ChannelCoder provides built-in session management for maintaining conversation context:
import { session } from 'channelcoder';
const s = session({ autoSave: true });
await s.claude('What is TypeScript?');
await s.claude('Show me an example');
await s.save('learning-typescript');
const saved = await session.load('learning-typescript');
await saved.claude('What about generics?');
const sessions = await session.list();
console.log(s.id());
console.log(s.messages());
CLI Session Support:
channelcoder prompts/debug.md --session my-debug
channelcoder prompts/continue.md --load-session my-debug
channelcoder --list-sessions
Session-Required Prompts:
---
session:
required: true
systemPrompt: "You are debugging an ongoing issue"
---
Continue investigating the error we discussed.
Manual Session Management (without session wrapper):
await claude('Continue our discussion', {
resume: 'session-id-here'
});
await claude('Continue where we left off', {
continue: true
});
Prompt File Format
ChannelCoder uses Markdown files with YAML frontmatter:
---
input:
name: string
age?: number
tags: string[]
output:
success: boolean
message: string
systemPrompt: "path/to/system.md"
allowedTools:
- Read
- Write
- "Bash(git:*)"
---
Age: {age ? age : "not specified"}
Tags: {tags}
Requirements
- Claude Code CLI installed and configured
- Node.js 18+ or Bun runtime
Tips & Tricks
1. Tool Patterns
Allow specific command patterns:
channelcoder prompt.md -t "Bash(git:*) Read Write"
2. Conditional Content
Use JavaScript expressions in templates:
const isDev = true;
const items = ['a', 'b', 'c'];
await claude`
${isDev ? "Include debug info" : ""}
Process ${items.length} items
`;
3. System Prompts
Can be inline or file paths:
channelcoder -p "..." -s "Be concise"
channelcoder -p "..." -s "prompts/systems/expert.md"
4. Background Monitoring
Perfect for long-running tasks:
channelcoder analysis.md --detached --stream --log analysis.log
tail -f analysis.log | jq -r '.content'
tail -f analysis.log | jq -r 'select(.type=="tool_use").tool'
watch -n 1 "grep -c content analysis.log"
5. JSON Output
Perfect for automation:
result=$(cc prompt.md --json)
success=$(echo $result | jq -r '.success')
Examples
Check out the /examples directory for:
basic-usage.ts - Simple examples to get started
file-based-usage.ts - Using file-based prompts
launch-modes.ts - Different execution modes
detached-streaming.ts - Background execution with real-time monitoring
task-monitor-tui.ts - NEW Real-time TUI using Stream Parser SDK
demo-features.ts - Feature showcase (no execution)
release.ts - Real-world release automation
Run examples:
bun run example:quick
bun run examples/basic-usage.ts
bun run examples/launch-modes.ts run
bun run examples/release.ts
License
MIT
Contributing
Issues and PRs welcome at github.com/davidpp/channelcoder
Named after Claude Shannon, the father of information theory, ChannelCoder channels your prompts to Claude with maximum signal and minimum noise.