Table of Contents
- The Problem Nobody Talks About Until It's Too Late
- Why Conversations Disappear — and Why That Matters
- Claude Code Hooks — The Mechanism That Makes This Work
- Inside the Processor — Node.js, jq, and the Export Format
- Installation, Edge Cases, and Making It Part of Your Workflow
- Conclusion: Stop Losing Your Work
The Problem Nobody Talks About Until It's Too Late
You've just spent 45 minutes debugging a gnarly async race condition with Claude Code. The solution is elegant, the explanation is clear, and you've got a mental model you didn't have before. Then you type /clear — or worse, Claude's context compaction kicks in automatically — and that entire conversation is gone. Not archived. Not recoverable. Just gone.
This is the quiet tax every Claude Code user pays. Context compaction exists for good reason: it keeps long sessions manageable. But the side effect is brutal — you lose the conversational thread, the reasoning, the back-and-forth that led to the solution. The final code might survive, but the why disappears.
By the end of this post, you'll understand exactly how the Chat Auto Exporter Claude Code plugin works under the hood — the hook system it exploits, the export format it produces, and how to get it running in under two minutes. We'll cover the hook lifecycle, the Node.js processor with jq fallback, and the timestamped .txt output format. If you've felt this pain, this is the fix.
Why Conversations Disappear — and Why That Matters
The Context Compaction Problem
Claude Code is designed for long, iterative coding sessions. But every LLM has a context window ceiling. When your conversation grows large enough, Claude Code triggers context compaction — a process that summarizes earlier parts of the session to free up token space. This is smart engineering. It lets you keep working without hitting hard limits.
The problem is that compaction is lossy by design. The summary replaces the raw conversation. You might retain the gist, but you lose exact phrasing, specific error messages Claude analyzed, the exploratory branches you rejected, and the intermediate reasoning steps. For developers who treat Claude Code as a thinking partner, that's significant information loss.
The /clear command is even more aggressive — it wipes the session entirely. Sometimes you want that. But sometimes you hit it by reflex and immediately regret it.
What You Actually Lose
Think of a Claude Code session like a whiteboard session with a senior engineer. You're not just capturing the final diagram — you're capturing the process: the questions asked, the wrong turns corrected, the "ah, that's why" moments. When context compaction fires or you /clear, the whiteboard gets erased.
Concretely, here's what disappears:
- Diagnostic reasoning — Claude's step-by-step analysis of your bug
- Rejected approaches — alternatives Claude considered and why it ruled them out
- Clarifying exchanges — your follow-up questions and Claude's refinements
- Tool call outputs — the raw results from bash commands or file reads that informed the solution
The final code in your editor reflects the conclusion of that reasoning. The conversation is the reasoning itself.
Why Existing Solutions Fall Short
There are a few approaches floating around for conversation preservation. Some developers use claude-conversation-extractor to scrape JSONL files from Claude's storage. Others use browser-based exporters that work on the web interface but don't touch Claude Code at all. Some just manually copy-paste.
These are all reactive. You remember to export after you've lost something. What you actually want is automatic, zero-friction export that fires before the conversation disappears — without you having to think about it. That's exactly what the hook system enables. And that's where the plugin comes in.
Claude Code Hooks — The Mechanism That Makes This Work
What Are Hooks?
Claude Code's hook system lets you attach shell commands to specific lifecycle events in a session. Think of them as middleware for your AI coding assistant. When an event fires — a user submits a prompt, a session ends, context compaction begins — Claude Code runs your registered hook command.
This is the same pattern you know from Git hooks (pre-commit, post-merge) or npm lifecycle scripts. The difference is that Claude Code hooks receive structured JSON payloads describing the event context, which means your hook can make intelligent decisions based on what's actually happening.
As of early 2026, Claude Code exposes several hook points. The three that matter for conversation export are:
PreCompact— fires before context compaction runs, giving you a window to capture the full conversationSessionEnd— fires when a session terminates cleanlyUserPromptSubmit— fires each time you submit a prompt (useful for incremental saves)
How the Plugin Registers Its Hooks
The Chat Auto Exporter plugin registers all three hooks in your Claude Code configuration. When you install it via /plugin install, it writes hook entries that point to the plugin's processor script. The hooks are defined in the plugin manifest and wired into Claude Code's .claude/settings.json.
Here's what the hook registration looks like conceptually:
{
"hooks": {
"PreCompact": "node ~/.claude/plugins/chat-autoexporter/processor.js",
"SessionEnd": "node ~/.claude/plugins/chat-autoexporter/processor.js",
"UserPromptSubmit": "node ~/.claude/plugins/chat-autoexporter/processor.js"
}
}
Each hook invocation passes a JSON payload via stdin. The processor reads that payload, determines the event type, and decides whether to write an export.
The Hook Payload Structure
This is where it gets interesting. The JSON payload Claude Code sends to your hook includes the conversation transcript — the actual message array, with roles (user, assistant) and content. The PreCompact payload is particularly valuable because it contains the full uncompacted conversation, right before it would be summarized away.
The processor script reads stdin, parses the JSON, extracts the messages array, and formats it into a human-readable transcript. The key insight is timing: PreCompact fires with complete data before anything is destroyed. If you only hook SessionEnd, you might already be working with a compacted context.
Inside the Processor — Node.js, jq, and the Export Format
The Node.js Processor
The heart of the plugin is a Node.js script that handles all three hook events. Node.js was the right call here — it's available in virtually every developer environment, handles JSON natively without dependencies, and is fast enough that the hook adds imperceptible latency to your workflow.
The processor's logic is straightforward:
const chunks = [];
process.stdin.on('data', chunk => chunks.push(chunk));
process.stdin.on('end', () => {
const payload = JSON.parse(Buffer.concat(chunks).toString());
const messages = payload.messages || [];
if (messages.length === 0) return;
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const outputPath = `.claude/chat-exports/chat-${timestamp}.txt`;
const formatted = formatMessages(messages);
fs.mkdirSync('.claude/chat-exports', { recursive: true });
fs.writeFileSync(outputPath, formatted);
});
The script reads the full stdin payload, checks for messages, generates a timestamp, and writes the export. The mkdirSync with recursive: true means you don't need to create the output directory manually — it appears the first time an export runs.
The jq Fallback
Here's a practical design decision worth noting: not every environment has Node.js available. The plugin handles this with a jq fallback. If the Node.js invocation fails, a shell wrapper attempts to use jq to extract and format the conversation data.
jq is a lightweight command-line JSON processor that's common in Unix environments. The fallback isn't as capable as the full Node.js processor — it produces simpler output and handles edge cases less gracefully — but it means the plugin degrades usefully rather than failing silently. For most developers, Node.js will be present and the fallback never fires.
The Export Format
Exports land in .claude/chat-exports/ as timestamped .txt files — for example, chat-2026-03-31T14-22-05-000Z.txt. The format is intentionally terminal-style: plain text, readable without any tooling, structured like a conversation transcript.
=== Claude Code Chat Export ===
Exported: 2026-03-31T14:22:05.000Z
Event: PreCompact
[USER]
Why is my useEffect running twice in development?
[ASSISTANT]
In React 18's Strict Mode, effects intentionally run twice in development...
Plain text was a deliberate choice. It's grep-able, diff-able, indexable by any search tool, and readable in any editor. You don't need a special viewer. If you want to search across all your exported sessions for every time you asked about async/await, grep -r "async/await" .claude/chat-exports/ works immediately.
Installation, Edge Cases, and Making It Part of Your Workflow
Installing in Two Minutes
The fastest path is the plugin install command built into Claude Code:
/plugin marketplace add christancho/chat-autoexporter
/plugin install chat-autoexporter@christancho
/reload-pluginsThat's it. Claude Code fetches the plugin manifest, registers the hooks, and you're done. The next time context compaction fires or you end a session, an export appears in .claude/chat-exports/.
If you prefer manual installation or want to inspect the code before running it (a reasonable instinct), clone the repo and follow the manual setup in the README. The plugin has no external npm dependencies — it's pure Node.js standard library — so there's nothing to npm install.
Handling Edge Cases
A few scenarios worth knowing about:
Short sessions: The UserPromptSubmit hook fires on every prompt, but the processor checks message count before writing. Very short exchanges (one or two messages) still get exported, which can generate noise. You can add a minimum message threshold to the processor if you want to suppress trivial exports.
Multiple projects: The export path .claude/chat-exports/ is relative to your working directory. Each project gets its own exports folder. This is usually what you want — exports are co-located with the project they relate to.
.gitignore: You probably don't want to commit raw conversation exports. Add .claude/chat-exports/ to your .gitignore. The plugin doesn't do this automatically.
Large sessions: Very long conversations produce large .txt files. This is rarely a problem in practice, but if you're running multi-hour sessions with heavy tool use, exports can reach several hundred KB. Still trivially small for storage, but worth knowing.
Making Exports Actually Useful
Having exports is only half the value. The other half is being able to find what you need. A few practices that work well:
- Name your sessions — Start each session with a brief description of what you're working on. It becomes the first
[USER]entry in the export, making files self-documenting. - Build a search alias — Add
alias csearch='grep -r --include="*.txt"'to your shell config. Thencsearch "database migration" .claude/chat-exports/finds every session where you discussed migrations. - Periodic review — Exported sessions are surprisingly useful for writing documentation, postmortems, or ADRs (Architecture Decision Records). The reasoning is already written — you just need to extract it.
The broader pattern here connects to a growing practice of treating AI sessions as engineering artifacts — not throwaway interactions, but records worth preserving alongside your code.
Conclusion: Stop Losing Your Work
Here's what this plugin actually gives you, distilled:
- The hook system is the right abstraction —
PreCompact,SessionEnd, andUserPromptSubmittogether cover every scenario where conversation data would otherwise disappear. - Zero-friction is non-negotiable — automatic export on lifecycle events means you never have to remember to save. The cost of forgetting is zero.
- Plain text is the right format — timestamped
.txtfiles in.claude/chat-exports/work with every tool in your existing workflow without configuration. - The Node.js + jq design is pragmatic — it works in virtually every developer environment and degrades gracefully when Node.js isn't available.
Your immediate next steps:
- Run these two commands inside Claude Code right now.
/plugin marketplace add christancho/chat-autoexporter
/plugin install chat-autoexporter@christancho
/reload-plugins - Add
.claude/chat-exports/to your.gitignore - Start your next session with a one-line description of what you're solving
- After the session, open the export and see what you would have lost
The deeper implication here is that as AI coding assistants become more capable, the conversations we have with them become more valuable — not less. The reasoning, the exploration, the rejected paths: that's engineering knowledge. Treating it as ephemeral is a mistake we'll regret as these tools compound in sophistication.
The Chat Auto Exporter plugin is a small fix to a real problem. Install it before your next session disappears.
Hope this helps,
Christian


Member discussion