Dehydrate — a hygiene tool for your CLAUDE.md
Dehydrate is a hygiene feature of Hydrate. It shrinks a bloated
CLAUDE.md into structured Hydrate memory without losing Claude Code
performance. The markdown in your repo stays maintainable; Claude Code still gets
the full context via memory injection.
It reads CLAUDE.md and any docs you point it at, extracts atomic facts into
Hydrate's project-scoped store, and rewrites CLAUDE.md into a stub, a
compressed summary, or leaves it alone — your call. From then on, the always-on
UserPromptSubmit hook injects the extracted facts at prompt time instead
of Claude Code re-reading a thousand-line file every turn.
Dehydrate is a hygiene feature, available on every tier including Free. It doesn't add new cost savings on top of Hydrate — it preserves Hydrate's savings while letting you shrink the source markdown. The civichub benchmark showed that a properly dehydrated project got the same cost reduction as the un-dehydrated equivalent (−22% vs vanilla Claude Code). No new saving, same saving, 82% less
CLAUDE.md.
Dehydrate is not a backup. It's ingestion. To move an existing Hydrate project between machines, use
hydrate backup&hydrate restore. And there is norehydrate— once the facts are in Hydrate, the hook handles injection.
The three modes
The big knob is --mode, which decides what to do with CLAUDE.md after the facts land in Hydrate. Facts get ingested either way.
- stub — replaces
CLAUDE.mdwith a ~5-line pointer to Hydrate plus any operational content preserved verbatim (commands, setup, hooks, environment variables). The shortest useful form. - summary (default) — replaces
CLAUDE.mdwith a human-readable NLP summary of the original plus preserved operational content. Non-Hydrate readers (colleagues, reviewers, CI) still see a useful overview. - full — leaves
CLAUDE.mduntouched. Facts still land in Hydrate. For dual-redundant memory; also the default for A/B benchmarks where you want to isolate the effect of structured injection on top of an unchanged baseline.
Dry-run first, always
Dehydrate refuses to touch disk or the DB without --apply. The default
invocation prints a plan and exits.
$ hydrate dehydrate .
Scanning /Users/seamus/Documents/Dev/go/myproj (mode=summary)
CLAUDE.md 1247 lines 48.3KB [new]
AGENTS.md 412 lines 14.8KB [new]
README.md 230 lines 8.1KB [new]
docs/UI_PATTERNS.md 584 lines 21.5KB [new]
docs/DATABASE.md 318 lines 11.2KB [new]
5 sources (5 to ingest, 0 unchanged)
Plan:
1. Run LLM fact extraction on 5 source files (estimated ~$0.020)
2. Insert extracted facts into Hydrate (scope=project, dedupe by content_hash)
3. Back up CLAUDE.md → CLAUDE.md.pre-hydrate.bak
4. Rewrite CLAUDE.md (mode=summary)
5. Write HYDRATE.md ingestion ledger
Dry run — no changes made. Re-run with --apply to commit. Before & after — a small example
Given a CLAUDE.md like:
# MyProject
This project is a Go service for serving user preferences over HTTP.
## Architecture
The service is built on top of pgvector for embeddings and sqlite for
local state. Retrieval runs through a three-stage pipeline: classify,
search, synthesise.
## Build Commands
$ make build
$ make test
$ make run
## Philosophy
We believe in small, composable, testable code.
After hydrate dehydrate . --apply in mode=summary:
# CLAUDE.md
> **Project memory is in [Hydrate](https://gethydrate.dev).**
> Run `hydrate facts list --scope=project` for the full store.
> This file is a human-readable summary regenerated on each `hydrate dehydrate --apply`.
## Summary
The service is built on top of pgvector for embeddings and sqlite for local state.
## Operational
## Build Commands
$ make build
$ make test
$ make run
The Build Commands section stays verbatim (operational). The Architecture prose and Philosophy riff get collapsed into the Summary section. The knowledge they carried — "the service uses pgvector", "we chose sqlite for local state", "retrieval is three-stage" — is now atomic facts in Hydrate, injected by the hook on every prompt.
Idempotent by design
Dehydrate writes a HYDRATE.md sidecar recording each ingested file's
SHA256 + the fact IDs it produced. Re-running on an unchanged repo is a no-op.
Re-running after editing one doc re-extracts only that doc — the other sources
are skipped because their hashes still match the ledger.
Safety
- Originals always backed up to
CLAUDE.md.pre-hydrate.bakbefore rewrite. - Writes happen in a single SQLite transaction; a failure mid-ingest rolls back.
- Reversible:
hydrate dehydrate . --revertrestoresCLAUDE.mdfrom the backup and deletes exactly the facts the ledger attributes to prior runs. No other project data is touched. - Dry-run is the default. Nothing changes until you type
--apply.
Flags
hydrate dehydrate [DIR] [--mode=<stub|summary|full>] [--apply]
[--include=<glob,glob>] [--exclude=<glob,glob>]
[--project=<slug>] [--user=<id>] [--db=<path>]
hydrate dehydrate [DIR] --revert # restore CLAUDE.md + delete ledger facts Defaults:
DIR— current working directory.--mode=summary— rewriteCLAUDE.mdto a summary + preserved operational content.--include=CLAUDE.md,AGENTS.md,README.md,ARCHITECTURE.md,docs/**/*.md--exclude=docs/archive/**,docs/drafts/**
When to use which mode
- Use
summary(the default) whenCLAUDE.mdis read by humans — reviewers, colleagues, CI bots — as well as your Claude Code session. They still get a skimmable overview; the agent gets the full structured store from the hook. - Use
stubwhen you're the only reader and you want the shortest possible file committed to the repo. Every line in the stubbedCLAUDE.mdis either a pointer or operational. - Use
fullwhen you want to A/B test Hydrate injection without changing the document the agent also sees. Also the safe choice if you're unsure — nothing inCLAUDE.mdis touched.
Troubleshooting
"No source files matched" — your project doesn't have
CLAUDE.md or docs/*.md at the default paths. Pass
--include to widen the net.
"no LLM provider configured" — set OPENAI_API_KEY,
ANTHROPIC_API_KEY, or HYDRATE_LLM_ENDPOINT before
--apply. Dry-run doesn't call the LLM and doesn't need credentials. (You
bring your own LLM key; Hydrate doesn't meter this.)
"CLAUDE.md unchanged after --apply" — you ran
--mode=full, which ingests into Hydrate without touching the source markdown.
That's the intended behaviour. Use --mode=summary or --mode=stub
if you want the file rewritten.