A CLI tool that resolves, audits, visualizes, and cleans the heap of context and state Codex and Claude Code accumulate.
The name: midden, an archaeological term for a refuse heap — kitchen scraps, broken pottery, lost things — that tells you what life was like in the layer below. Agent memory and ~/.claude.json are exactly that.
For installing skills, managing MCP server lists, or browsing the marketplace, plenty of tools already exist. Use those.
midden picks up where they leave off. Coding agents accumulate generated memories, layered repository instructions, session evidence, project mappings, local settings, and ephemeral worktrees, while almost nothing in the loop makes that state legible or removes it. midden surfaces what's actually active for a directory with provenance, flags what's stale or leaking, and prunes the state nothing else cleans up.
brew install starhaven-io/tap/middencargo install middenDownload a prebuilt binary from GitHub Releases. There is no committed CHANGELOG — release notes live on the GitHub Releases page. Binaries are available for:
- Linux amd64, glibc —
x86_64-unknown-linux-gnu - Linux arm64, glibc —
aarch64-unknown-linux-gnu - Linux amd64, musl (static) —
x86_64-unknown-linux-musl - Linux arm64, musl (static) —
aarch64-unknown-linux-musl - macOS Apple Silicon —
aarch64-apple-darwin
The gnu builds link against the system glibc. They are built on Ubuntu 24.04, so they require glibc 2.39 or newer (Ubuntu 24.04+, Debian 13+, Fedora 40+). On older releases — Ubuntu 22.04, Debian 12, RHEL 9 and the like — use the statically linked musl builds instead. The musl builds carry no glibc requirement and also run on musl-based distributions such as Alpine, as well as minimal or distroless containers.
To try unreleased changes from main:
cargo install --git https://github.com/starhaven-io/middenAll commands default to safe modes: dry-run for prune, read-only for show and memory inventory, and report-only for doctor. Writes always require an explicit flag, create a timestamped backup first, and replace the file atomically, preserving its file mode. Use --json for machine-readable output.
# Compare Codex and Claude memory sources for this repo
midden memory show
# Inspect only one provider
midden memory show --provider codex
# Include unrelated and unassociated memory sources
midden memory show --all
# Show what's actually active for a directory with provenance
midden show
# Target a specific repo
midden show /path/to/repo
# Hygiene + audit lint
midden doctor
# Auto-resolve safe findings (orphaned projects, etc.)
midden doctor --fix
# Garbage-collect dead `projects` entries (dry-run)
midden prune
# Apply the prune
midden prune --apply
# Also report orphaned transcript artifacts under ~/.claude/projects/
midden prune --transcripts
# Remove dead project entries and orphaned transcript artifacts
midden prune --transcripts --apply
# Only consider ephemeral worktree entries
midden prune --worktrees-only --apply
# Override the safety gate when claude is running
midden prune --apply --force
# Unmask secret-looking values in output (dangerous)
midden show --show-secrets
# Generate shell completions
midden completions zshmemory show resolves Codex and Claude Code through one normalized, read-only inventory while preserving each provider's native loading behavior:
$ midden memory show .
memory for /Users/me/myproject
codex memory enabled management read-only
instructions
[repository; loaded] /Users/me/myproject/AGENTS.md (2.4 KiB)
retained memory
[global; loaded] /Users/me/.codex/memories/memory_summary.md (1.1 KiB)
claude memory enabled management read-only
instructions
[repository; loaded] /Users/me/myproject/CLAUDE.md (31 B)
[repository; loaded] /Users/me/myproject/AGENTS.md (2.4 KiB)
imported by /Users/me/myproject/CLAUDE.md
retained memory
[repository; loaded] /Users/me/.claude/projects/example/memory/MEMORY.md (3.2 KiB)
provider coverage
codex: 1 instruction, 1 retained memory
claude: 2 instructions, 1 retained memory
Codex discovery follows AGENTS.override.md, AGENTS.md, configured fallback names, and the combined instruction byte limit. It inventories the generated summary, durable-memory index, and evidence stores under the configured Codex home without recursively reading rollout history.
Claude discovery includes managed, user, ancestor, project, local, imported, and path-scoped instruction sources. It associates per-repository auto-memory with the target from transcript cwd evidence rather than decoding Claude's lossy project-directory slugs, and reports MEMORY.md as startup context with topic files available on demand.
The default provider is all. --provider codex or --provider claude filters the same schema to one adapter. --all additionally includes unrelated and unassociated sources for forensic work. Memory content is not printed; JSON output contains source metadata, loading state, association, capabilities, and warnings.
Resolve every configuration surface for a target directory with provenance:
$ midden show .
resolved for /Users/me/myproject
settings
permissions.defaultMode = "bypass"
[user shadowed] /Users/me/.claude/settings.json = "ask"
[project] /Users/me/myproject/.claude/settings.json = "bypass"
permissions.deny = ["Bash(rm:*)", "Read(./.env)"]
[user merged] /Users/me/.claude/settings.json = ["Bash(rm:*)"]
[project merged] /Users/me/myproject/.claude/settings.json = ["Read(./.env)"]
CLAUDE.md
[user] /Users/me/.claude/CLAUDE.md (8421 bytes)
[project] /Users/me/myproject/CLAUDE.md (10442 bytes)
hooks
PreToolUse
[local] Bash (command): bash -c '… DCO sign-off check …'
/Users/me/myproject/.claude/settings.local.json
mcp servers
[user] github -> https://api.githubcopilot.com/mcp
/Users/me/.claude.json
[project] astro-docs -> https://mcp.docs.astro.build/mcp
/Users/me/myproject/.mcp.json
Settings precedence is Managed → Local → Project → User. Scalars from a higher scope override; arrays concat and deduplicate across scopes. show tags every value with its source and marks contributions shadowed by a higher scope. CLAUDE.md files do not follow precedence — all applicable files load simultaneously, so midden lists every contributor and runs a heuristic contradiction-detection pass instead of picking a winner.
MCP servers are gathered from all four scopes: user (~/.claude.json), local (the per-project entry inside ~/.claude.json — where claude mcp add writes by default), project (.mcp.json), and managed (.claude/managed-mcp.json).
Secrets are masked to abcd*** by default — both by key name (*_token, *_api_key, password, credential, …) and by value shape under innocent keys: known token prefixes (sk-, ghp_, xoxb-, AWS key ids, JWTs, private-key blocks), user:pass URLs, credential-named query parameters, and Bearer tokens inside hook commands. Pass --show-secrets to unmask.
Hygiene + audit lint over the layered state, emitting structured Findings:
$ midden doctor
warn [missing-credential-deny] no deny rule covers .env, secrets — Claude could read credentials here
at /Users/me/.claude/settings.json:permissions.deny
fix: add e.g. "Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)" to permissions.deny in ~/.claude/settings.json to cover every project
warn [orphaned-project] worktree directory no longer exists: /Users/me/Developer/foo/.claude/worktrees/witty-curie (auto-fixable)
at /Users/me/.claude.json:projects./Users/me/Developer/foo/.claude/worktrees/witty-curie
fix: remove this entry with `midden prune --apply`
0 error, 2 warn, 0 info — 1 auto-fixable
Run with --fix to apply auto-fixable findings. As with prune, this writes a timestamped backup first and refuses to write while a claude process is running (override with --force).
Garbage-collect dead projects entries from ~/.claude.json (dry-run by default):
$ midden prune
43 project entries total; 24 orphaned (13 worktree, 11 other):
- /Users/me/Developer/Brewy
- /Users/me/Developer/Brewy/.claude/worktrees/competent-fermat [worktree]
- /Users/me/Developer/macOSdb
…
would shrink .claude.json by ~20.0 KiB (69.9 KiB -> 49.8 KiB).
dry run. re-run with --apply to remove these entries.
quit all Claude Code sessions first; it rewrites this file live.
An entry is a removal candidate only if its directory is provably absent from disk — a path that merely fails to stat (permission denied, an unreachable mount) is kept, and midden never guesses from value contents. --worktrees-only restricts to entries under a .claude/worktrees/ path. If nearly all entries resolve missing — usually a sign you are on a different machine or an unmounted volume rather than that they are all dead — prune --apply refuses unless you pass --force.
Pass --transcripts to also inspect ~/.claude/projects/, where Claude Code stores per-project session transcripts. These directories are named with lossy path slugs, so midden never decodes the directory name. Instead it reads only the head of each *.jsonl transcript and uses the first cwd field it can derive. A transcript directory is skipped if its transcripts disagree, no cwd can be derived, or it has no *.jsonl files.
When a derived cwd is provably absent, prune --transcripts reports the session artifacts it would remove: *.jsonl files and bare UUID-named session artifact directories. memory/ is durable user data and is never deleted. If only memory/ remains, the transcript project directory is kept and reported as memory preserved; unknown entries are left in place and reported as partially cleaned. Unlike .claude.json rewrites, transcript deletion does not create .bak copies, because copying hundreds of MB of append-only logs would make cleanup impractical; the same dry-run, running-claude, mass-deletion, and --force gates still apply.
prune --transcripts also reports the largest kept transcript directories. Those are not removal candidates today because their project directories still exist, but the inventory shows where live Claude Code history is consuming disk so retention-policy work can be deliberate rather than guesswork.
| ID | Severity | Auto-fixable | What it catches |
|---|---|---|---|
orphaned-project |
Warn | yes | projects entry whose directory no longer exists |
claude-json-bloat |
Info | no | ~/.claude.json over 512 KiB (Claude Code never prunes it) |
orphaned-transcript |
Warn | no | Transcript directory whose derived cwd no longer exists |
claude-transcript-storage |
Info | no | Kept transcript history under ~/.claude/projects/ exceeds 64 MiB |
stale-worktree |
Info | no | Ephemeral worktree dir untouched for >30 days |
config-path-inaccessible |
Warn | no | Config/worktree path could not be inspected because of permissions or filesystem errors |
malformed-json-config |
Warn | no | Settings or MCP JSON could not be parsed, so key-aware checks were skipped |
secret-in-malformed-config |
Error | no | Token-shaped secret appears in malformed committed JSON |
secret-in-committed-settings |
Error | no | Suspect secret in a settings.json that git doesn't ignore — by key name or value shape (masked by default) |
secret-in-committed-mcp |
Error | no | Suspect secret in a .mcp.json / managed-mcp.json that git doesn't ignore — by key name or value shape; pure ${VAR} references are exempt |
local-settings-tracked |
Warn | no | settings.local.json tracked by git (meant to stay machine-local) |
local-settings-not-ignored |
Warn | no | settings.local.json not gitignored — one git add from being committed |
missing-credential-deny |
Warn | no | No permissions.deny covers .env or secrets/ paths |
skill-missing-skill-md |
Warn | no | Skill directory missing its SKILL.md |
empty-config-file |
Warn | no | Slash command or subagent markdown file is empty |
missing-frontmatter |
Warn | no | Subagent markdown file missing the required --- YAML frontmatter (slash commands don't need it) |
mcp-server-unreachable |
Warn | no | MCP server defined with no command or url (any scope, including local) |
mcp-server-plaintext-http |
Warn | no | MCP server uses plaintext http:// or ws:// for a non-local URL (localhost, *.localhost, loopback, 0.0.0.0, and [::] are local) |
mcp-server-disabled |
Info | no | MCP server defined but disabled — disabled: true, or listed in the project's disabledMcpjsonServers |
stale-mcp-approval |
Info | no | enabledMcpjsonServers/disabledMcpjsonServers names a server .mcp.json no longer defines |
midden does not yet have a config file — all behavior is controlled by CLI flags. Settings precedence and CLAUDE.md merge rules come from Claude Code itself, not midden.
| Flag | Purpose |
|---|---|
--config <PATH> |
Override the path to ~/.claude.json (for testing) |
--claude-home <PATH> |
Override the path to ~/.claude/ (for testing) |
--codex-home <PATH> |
Override $CODEX_HOME / ~/.codex/ (for testing) |
--json |
Emit machine-readable JSON instead of styled text |
--color auto|always|never |
Control color output |
--show-secrets |
Unmask secret-looking values in show / doctor output |
--force |
Allow writes while a claude process is running, and override the mass-deletion guard |
| Code | Meaning |
|---|---|
| 0 | Clean — no findings, or successful apply |
| 1 | Findings present (doctor with errors) |
| 2 | Error — bad input, missing file, write blocked by running claude |
When output is piped and the reader exits early (midden show | head), midden
dies of SIGPIPE like any other Unix filter — shells report that as 141, never
as a panic.
A justfile provides common tasks:
just build # Build the project
just build-release # Build in release mode
just test # Run tests
just clippy # Run clippy
just fmt # Format code
just typos # Check for typos
just deny # cargo-deny: license + advisory + source checks
just lychee # Check README links
just audit # Audit GitHub Actions workflows (zizmor)
just check # Run all checks
just install-hooks # Install git hooks: pre-push check + DCO sign-off (once per clone)Commits must follow Conventional Commits format and include a DCO sign-off (git commit -s). Run just install-hooks once per clone to enable the git hooks (a pre-push just check and DCO sign-off enforcement).
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only).
Copyright (C) 2026 Patrick Linnane