Watch Claude Code JSONL output files and emit structured events as a Node.js EventEmitter.
npm install ccsniffimport { watch } from 'ccsniff';
const watcher = watch()
.on('conversation_created', ({ conversation }) => {
console.log('New session:', conversation.title);
})
.on('streaming_progress', ({ block, role }) => {
if (block.type === 'text') process.stdout.write(block.text);
})
.on('streaming_complete', ({ conversationId }) => {
console.log('Done:', conversationId);
});
process.on('SIGINT', () => watcher.stop());CommonJS:
const { watch, JsonlWatcher } = require('ccsniff');ccsniff ships a mountable router that hosts all the read-only history endpoints used by AgentGUI's live client (/v1/history/snapshot, /sessions, /sessions/:sid/events, /search, /stream SSE, and POST /reindex). It reads ~/.claude/projects by default; override with the CLAUDE_PROJECTS_DIR env var. express is an optional peer dependency — install it in the host app.
import express from 'express';
import { createHistoryRouter } from 'ccsniff';
const app = express();
app.use(await createHistoryRouter({ projectsDir: process.env.CLAUDE_PROJECTS_DIR }));
app.listen(3000);npx ccsniff --since 24h --grep "rs-exec" --limit 50
npx ccsniff --since 7d --role user --json
npx ccsniff -f # tail new events live
npx ccsniff --rollup out.ndjson --since 7d
npx ccsniff --unsloth train.jsonl --since 7d --no-subagents
npx ccsniff --unsloth train.jsonl --unsloth-format sharegpt --since 7d--unsloth <out> writes one JSONL line per Claude Code session, ready for
Unsloth / TRL conversational fine-tuning. All filter flags (--since,
--project, --session, --no-subagents, ...) apply.
Two formats are supported via --unsloth-format:
messages(default) — OpenAI / ChatML shape with native tool calling:{"session_id":"...","messages":[ {"role":"user","content":"find foobar"}, {"role":"assistant","content":null,"tool_calls":[{"id":"tu1","type":"function","function":{"name":"Grep","arguments":"{\"pattern\":\"foobar\"}"}}]}, {"role":"tool","tool_call_id":"tu1","content":"hit at line 3"}, {"role":"assistant","content":"done"} ]}sharegpt—{conversations:[{from:human|gpt|tool, value}]}, compatible withstandardize_sharegpt. Tool calls are inlined into thegptturn as<tool_call>name(json-args)</tool_call>.
Sessions with no user/assistant turn pair are skipped (no training value).
A turnkey, zero-dep browser observatory ships with the package. It indexes
every session in ~/.claude/projects and exposes sessions, projects, tools,
timeline, errors, subagent tree, live stream, and BM25 full-text search.
npx ccsniff gui --open # auto-open in default browser
npx ccsniff gui --port 4791 # custom port (default 4791)Endpoints under /api/*: snapshot, sessions, projects, tools,
timeline, stats, errors, subagents, events, search, stream
(SSE).
Creates and starts a watcher. projectsDir defaults to ~/.claude/projects.
Class constructor. Call .start() manually after attaching listeners.
Scans for existing .jsonl files and begins watching. Chainable.
Closes file descriptors and directory watcher.
| Event | Payload |
|---|---|
conversation_created |
{ conversation: { id, title, cwd }, timestamp } |
streaming_start |
{ conversationId, conversation, timestamp } |
streaming_progress |
{ conversationId, conversation, block, role, seq, timestamp } |
streaming_complete |
{ conversationId, conversation, seq, timestamp } |
streaming_error |
{ conversationId, error, recoverable, timestamp } |
error |
Error |
block.type values: text, tool_use, tool_result, system, result, etc.
Node >= 18. Zero external dependencies.
MIT