Examples
WebSocket Relay
Spawn Claude Code on a server, relay events to browser clients over WebSocket.
typescript
import { spawn } from 'child_process'
import { createInterface } from 'readline'
import { WebSocketServer } from 'ws'
import { parseLine, Translator, createMessage } from 'claude-code-parser'
const claude = spawn('claude', [
'-p', '--input-format', 'stream-json',
'--output-format', 'stream-json', '--verbose',
], { cwd: '/path/to/repo', stdio: ['pipe', 'pipe', 'inherit'] })
const translator = new Translator()
const wss = new WebSocketServer({ port: 8080 })
// Claude stdout → parse → translate → broadcast to WS clients
const rl = createInterface({ input: claude.stdout! })
rl.on('line', (line) => {
const event = parseLine(line)
if (!event) return
for (const ev of translator.translate(event)) {
const msg = JSON.stringify(ev)
wss.clients.forEach(ws => ws.send(msg))
}
})
// WS client messages → Claude stdin
wss.on('connection', (ws) => {
ws.on('message', (data) => {
const msg = JSON.parse(String(data))
switch (msg.type) {
case 'chat':
claude.stdin!.write(createMessage.user(msg.content))
break
case 'approve':
claude.stdin!.write(createMessage.approve(msg.toolUseId))
break
case 'deny':
claude.stdin!.write(createMessage.deny(msg.toolUseId))
break
}
})
})Parse Saved Log Files
Read a captured NDJSON session log and extract structured data.
typescript
import { readFileSync } from 'fs'
import { parseLine, Translator } from 'claude-code-parser'
const log = readFileSync('session.ndjson', 'utf-8')
const translator = new Translator()
let totalCost = 0
const toolCalls: string[] = []
for (const line of log.split('\n')) {
const event = parseLine(line)
if (!event) continue
for (const ev of translator.translate(event)) {
if (ev.type === 'tool_use') {
toolCalls.push(ev.toolName)
}
if (ev.type === 'turn_complete' && ev.costUsd) {
totalCost += ev.costUsd
}
}
}
console.log(`Total cost: $${totalCost.toFixed(4)}`)
console.log(`Tools used: ${toolCalls.join(', ')}`)CI Cost Tracker
Extract cost and token usage from Claude Code in a CI pipeline.
typescript
import { spawn } from 'child_process'
import { createInterface } from 'readline'
import { parseLine, Translator, createMessage } from 'claude-code-parser'
const claude = spawn('claude', [
'-p', '--input-format', 'stream-json',
'--output-format', 'stream-json', '--verbose',
'--permission-mode', 'bypassPermissions',
], { stdio: ['pipe', 'pipe', 'inherit'] })
const translator = new Translator()
const rl = createInterface({ input: claude.stdout! })
rl.on('line', (line) => {
const event = parseLine(line)
if (!event) return
for (const ev of translator.translate(event)) {
if (ev.type === 'turn_complete') {
// Post to CI — GitHub Actions output, PR comment, etc.
console.log(`::notice::Claude cost: $${ev.costUsd?.toFixed(4)} | Tokens: ${ev.inputTokens}→${ev.outputTokens}`)
}
if (ev.type === 'error') {
console.error(`::error::Claude error: ${ev.message}`)
process.exit(1)
}
}
})
claude.stdin!.write(createMessage.user('Run the test suite and report results'))Browser NDJSON Viewer
Parse NDJSON in the browser (no Node.js APIs used in core).
typescript
import { parseLine, Translator } from 'claude-code-parser'
const translator = new Translator()
// Fetch a saved session log
const response = await fetch('/logs/session-2026-03-22.ndjson')
const text = await response.text()
for (const line of text.split('\n')) {
const event = parseLine(line)
if (!event) continue
for (const ev of translator.translate(event)) {
renderEvent(ev) // Your UI rendering function
}
}Tool Approval Flow
Interactive tool approval via stdin messages.
typescript
import { parseLine, Translator, createMessage } from 'claude-code-parser'
import type { ToolUseEvent } from 'claude-code-parser'
// ... setup claude process and readline ...
const pendingApprovals = new Map<string, ToolUseEvent>()
rl.on('line', (line) => {
const event = parseLine(line)
if (!event) return
for (const ev of translator.translate(event)) {
if (ev.type === 'tool_use') {
// Auto-approve Read/Grep, require manual approval for others
if (['Read', 'Grep', 'Glob'].includes(ev.toolName)) {
claude.stdin!.write(createMessage.approve(ev.toolUseId))
} else {
pendingApprovals.set(ev.toolUseId, ev)
console.log(`Approve ${ev.toolName}? (tool_use_id: ${ev.toolUseId})`)
}
}
}
})
// Later, when user approves/denies:
function handleUserDecision(toolUseId: string, approved: boolean) {
if (approved) {
claude.stdin!.write(createMessage.approve(toolUseId))
} else {
claude.stdin!.write(createMessage.deny(toolUseId))
}
pendingApprovals.delete(toolUseId)
}