Docs / Operations

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 no rehydrate — 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.md with 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.md with 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.md untouched. 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.bak before rewrite.
  • Writes happen in a single SQLite transaction; a failure mid-ingest rolls back.
  • Reversible: hydrate dehydrate . --revert restores CLAUDE.md from 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 — rewrite CLAUDE.md to 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) when CLAUDE.md is 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 stub when you're the only reader and you want the shortest possible file committed to the repo. Every line in the stubbed CLAUDE.md is either a pointer or operational.
  • Use full when 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 in CLAUDE.md is 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.