Search, filter, edit, and prune Zed agent conversation threads
Project description
Zed Context Manipulator
Search, filter, edit, and prune the agent conversation threads that the Zed editor stores locally. It ships both a scriptable command-line interface and an interactive terminal UI (TUI).
Zed keeps every agent thread in a single SQLite database
(threads.db), with each thread's messages stored as a compressed JSON blob.
Over time these grow large -- especially when the agent reads images or large
files -- bloating the database and inflating the context that gets re-sent to
the model. This tool lets you reclaim that space and curate your history
surgically, without hand-editing SQLite.
[!WARNING] This program edits Zed's private database. Quit Zed before writing changes. Every write is a dry run unless you pass
--write, and every write makes a timestamped.bakcopy first. When in doubt, test against a copy (cp threads.db /tmp/threads.dbthen--db /tmp/threads.db).
Table of contents
- Features
- Installation
- Quick start
- How Zed stores threads
- Concepts: threads, messages, parts
- Command-line interface
- Filter reference
- Recipes
- TUI guide
- Development
- License
Features
- Full-text search across thread titles and message content, with plain or regular-expression matching.
- Rich filtering by thread ID, title, content, project folder, AI model, agent profile, parent thread, creation/update date, message count, and whether a thread contains images.
- Part-level targeting -- act on individual pieces of a conversation: user/assistant text, thinking blocks, images, tool calls, tool results, and context mentions.
- Drop reads and other tool calls by tool name, by file path glob or regular expression, by URL/command/query, by size, or by error status.
- Positional pruning -- target the oldest N messages, the newest N, an index range, or "everything in the middle" while protecting the head/tail.
- Three drop modes: replace with a lightweight
placeholder, fullyremove, orstrip-images(keep the text of a tool result but discard the image bytes). Keep the newest N images per thread with--keep-latest-images. - Edit and replace part text programmatically (regular expression
--replace) or literally (--set-text), in bulk across matching threads. - Thread management: delete entire threads, reassign them to another project folder, rename them, change the agent profile, or change which model runs next.
- Interactive TUI to browse, search, select parts, stage changes, and
write them -- with
$EDITORintegration for long edits. - Safe by default: dry-run unless
--write, automatic timestamped backup before every write, optionalVACUUMto shrink the file. - Handles both of Zed's on-disk thread encodings transparently and preserves fields it does not understand.
Installation
Requires Python 3.11+. The only runtime dependency is
zstandard.
With pipx (recommended)
pipx install zed-context-manipulator
With pip
pip install zed-context-manipulator
From source (development)
git clone https://github.com/Skyluker4/zed-context-manipulator.git
cd zed-context-manipulator
python3 -m venv .venv
. .venv/bin/activate
pip install -e ".[dev]"
Both zed-context-manipulator and the short alias zcm are installed.
Quick start
# What's in my database?
zcm stats
# List threads, newest first.
zcm list
# Search every thread for a phrase.
zcm search "slot conflict"
# Preview pruning every read_file image but keep the newest one per thread.
zcm drop --images --keep-latest-images 1
# Actually do it (quit Zed first!), shrinking the file afterwards.
zcm drop --images --keep-latest-images 1 --write --vacuum
# Browse and curate interactively.
zcm tui
The database is found automatically at
${XDG_DATA_HOME:-~/.local/share}/zed/threads/threads.db. Override it any time
with --db /path/to/threads.db.
How Zed stores threads
Everything lives in one SQLite table, threads:
| column | meaning |
|---|---|
id |
UUID of the thread |
summary |
the thread title shown in Zed |
updated_at |
last-modified timestamp (ISO 8601) |
created_at |
creation timestamp |
data_type |
payload encoding (zstd or json) |
data |
the thread document (zstd-compressed JSON) |
parent_id |
parent thread, if any |
folder_paths |
project folder(s), newline-separated |
folder_paths_order |
display order of those folders |
The decoded document contains the message list plus metadata such as the
next model to use ({"provider": ..., "model": ...}) and the agent
profile. This tool understands the document well enough to edit messages and
metadata while leaving everything else untouched.
Concepts: threads, messages, parts
- A thread is one conversation.
- A thread contains ordered messages, each with a
role(userorassistant). Inshowand the TUI a message is taggedcurrentorlegacy, reflecting Zed's newer or older on-disk encoding; both are handled identically. - A message contains ordered parts. A part is the smallest thing you can select, drop, or edit:
| part type | what it is |
|---|---|
text |
normal user or assistant text |
thinking |
assistant reasoning / thinking blocks |
image |
an embedded image |
tool_use |
a tool call the agent made (e.g. read_file, terminal) |
tool_result |
the output returned to the agent (often the bulky bit) |
mention |
a context mention (e.g. another thread, a file) |
Every part has a stable ID shown as message:slot:index (for example
21:tool_result:0). Tool parts also expose a target -- the file path,
glob, URL, command, or query pulled from the tool's input -- which is what the
path and regular expression filters match against.
Command-line interface
zcm [--db PATH] [-s] <command> [options]
Run zcm <command> --help for the full, authoritative option list.
Global options
| option | description |
|---|---|
--db PATH |
Path to threads.db (default: Zed's data directory). |
-s, --case-sensitive |
Make all matching case-sensitive (default: insensitive). |
--version |
Print the version and exit. |
list
List threads that match the thread filters.
zcm list # newest first
zcm list --project m3u8-extractor # only one project
zcm list --sort size --count-messages # biggest first, show message counts
zcm list --min-thread-size 1M # only threads at least 1 MiB on disk
zcm list --format ids # just ids (handy for scripting)
zcm list --format json # machine-readable
show
Print one thread's messages and parts, with part IDs, sizes, tool names, and targets. Accepts a full or prefix thread ID, or any thread filter (shows the first match).
zcm show 6395f4e5 # by id prefix
zcm show --title-contains "Signal" # by title
zcm show 6395f4e5 --type image # only image parts
zcm show 6395f4e5 --sort size # biggest parts first
zcm show 6395f4e5 --message 21 --full # one message, full text
Use --sort {document,size,length,kind} (with optional --reverse) to reorder
the parts. --sort size is the quickest way to find what is bloating the
database; --sort length finds the longest text (e.g. a giant log or
thinking block). Each part line shows both its byte size and, for text-bearing
parts, its character count.
search
Search titles and part text across all matching threads; matches are highlighted.
zcm search "permission denied"
zcm search "fatal: .*not a git repo" --regex
zcm search TODO --type text --role assistant --max-per-thread 3
stats
Aggregate overview: thread/message/image counts, plus top projects, models, tools, and part types. Honors thread filters.
zcm stats
zcm stats --project portage
drop
Drop or prune matching parts. Dry run unless --write. Requires at least
one selector (a part filter, a position selector, --images, or
--keep-latest-images) unless you pass --all.
# Replace every terminal result with a placeholder in one thread.
zcm drop --thread-id <id> --type tool_result --tool terminal
# Drop all reads of PNG files anywhere.
zcm drop --tool read_file --path-glob '**/*.png' --write
# Strip images but keep the newest one in each thread.
zcm drop --images --keep-latest-images 1 --write --vacuum
# Remove (not just placeholder) every thinking block.
zcm drop --type thinking --mode remove --write
| option | description |
|---|---|
--mode {placeholder,remove,strip-images} |
how to drop (default placeholder). |
--images |
preset: target image parts and strip them. |
--keep-latest-images N |
keep the newest N image-bearing parts per thread. |
--invert |
act on parts that do not match the filter. |
--all |
allow dropping with no filter (dangerous). |
edit
Rewrite part text in bulk. Use a regular expression replacement or set the text literally.
# Redact a token everywhere it appears in text/results.
zcm edit --part-regex "ghp_[A-Za-z0-9]+" --replace "ghp_[A-Za-z0-9]+" \
--with "<redacted>" --write
# Replace one part's entire text.
zcm edit --thread-id <id> --part-contains "old note" --set-text "new note" --write
thread
Whole-thread operations. Combine with thread filters to act in bulk.
# Change which model will run next.
zcm thread --thread-id <id> --set-model "zed.dev:gpt-5.5" --write
# Move every thread from one project to another.
zcm thread --project old-name --reassign /home/me/new-project --write
# Rename and re-profile.
zcm thread --thread-id <id> --set-title "Cleanup notes" --set-profile ask --write
# Delete matching threads (prompts unless -y).
zcm thread --title-contains "New Thread" --max-messages 0 --delete --write -y
--set-model takes PROVIDER:MODEL, e.g. zed.dev:claude-opus-4-8,
openai:gpt-5.5, or Local Proxy:unsloth/GLM-5.
backup
Write a timestamped copy next to the database and exit.
zcm backup
tui
Launch the interactive UI. Thread filters pre-narrow the list.
zcm tui
zcm tui --project portage
zcm tui --read-only
Filter reference
Thread filters
| option | matches |
|---|---|
--thread-id ID |
exact ID (repeatable, comma-separated) |
--title-contains / --title-regex |
the title |
--content-contains / --content-regex |
title and all message text |
--folder / --folder-glob / --project |
project folder path / glob / basename |
--model / --model-regex |
next model, as provider/model |
--profile |
agent profile |
--parent-id |
parent thread ID |
--created-after / --created-before |
creation date (ISO 8601) |
--updated-after / --updated-before |
update date |
--has-images / --no-images |
presence of images |
--min-messages / --max-messages |
message count |
--min-thread-size / --max-thread-size |
stored thread size (accepts K/M/G) |
--limit N |
stop after N matching threads |
Part filters
| option | matches |
|---|---|
--role {user,assistant} |
message role |
--type {text,thinking,image,tool_use,tool_result,mention,other} |
part type |
--tool NAME |
tool name (repeatable, comma-separated) |
--path-glob GLOB |
tool target path (glob; repeatable) |
--path-regex / --target-regex |
tool target path / any target (regular expression) |
--part-contains / --part-regex |
the part's text |
--errors-only / --no-errors |
tool-result error status |
--images-only |
parts that carry image data |
--min-size / --max-size |
serialized part size in bytes (accepts K/M/G) |
--min-length / --max-length |
text length in characters |
size counts on-disk bytes (so it includes image/base64 data); length counts
characters of human-readable text. A large diff can be big in size yet short
in length. Both axes work on every part type and can be combined, e.g.
--min-size 40K --max-length 3000 finds heavy-but-terse results. Sort by either
with show --sort size / show --sort length (and in the TUI with s).
Position selectors (within each thread)
| option | selects |
|---|---|
--oldest N |
the first N messages |
--newest N |
the last N messages |
--index-min / --index-max |
messages in an index range |
--middle |
the middle, with --keep-oldest/--keep-newest protecting the ends |
Dates apply at the thread level (Zed does not timestamp individual messages); positions apply within a thread.
Recipes
Reclaim space from image-heavy threads (the classic use case).
zcm drop --images --keep-latest-images 1 --write --vacuum
Drop only the reads of a noisy directory, everywhere.
zcm drop --tool read_file --path-glob '**/node_modules/**' --write
Trim long sessions: keep the last 10 messages, placeholder the rest.
zcm drop --middle --keep-newest 10 --type tool_result --type tool_use --write
Forget everything before a date.
zcm thread --updated-before 2025-01-01 --delete --write -y
Find what is bloating a thread, then prune the biggest parts.
zcm list --min-thread-size 1M --sort size # heaviest threads
zcm show <id> --sort size # heaviest parts in one thread
zcm drop --thread-id <id> --min-size 50K --write
Bulk-redact secrets that leaked into results.
zcm edit --type tool_result --replace "AKIA[0-9A-Z]{16}" --with "<aws-key>" --write
Point a batch of threads at a different next model.
zcm thread --model "claude-opus-4-6" --set-model "zed.dev:claude-opus-4-8" --write
TUI guide
Run zcm tui. All changes are staged in memory and only written when you
press w, which always makes a backup first.
Thread list
| key | action |
|---|---|
j / k, arrows |
move |
PgUp / PgDn, g / G |
page / jump to ends |
Enter / l |
open thread |
/ |
search (title, project, content) |
s |
cycle sort (updated / size / title) |
d |
toggle delete mark |
r |
reassign project folder |
m |
change next model (provider:model) |
t |
change title |
w |
write staged changes |
? |
help |
q |
quit |
Thread detail
| key | action |
|---|---|
j / k, arrows |
move |
space |
select / deselect a part |
a / c |
select all / clear selection |
d |
stage drop (placeholder) |
D |
stage remove (delete the part) |
i |
stage strip-images |
e |
edit text inline |
E |
edit text in $EDITOR |
u |
clear staged change |
/ |
find within the thread |
s |
cycle part sort (document / size / length) |
| Enter | view full part text |
w |
write staged changes |
q / h / Esc |
back to the list |
Development
python3 -m venv .venv
. .venv/bin/activate
pip install -e ".[dev]"
ruff check .
ruff format .
CI runs Super-Linter (Ruff for Python) on every push and pull request, and builds/publishes the package on tagged releases. Dependencies are kept current with Dependabot.
When testing changes, always point --db at a copy of a real database and
rely on the default dry-run output before re-running with --write.
License
Licensed under the GNU Affero General Public License v3.0 only
(AGPL-3.0-only). See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zed_context_manipulator-1.0.0.tar.gz.
File metadata
- Download URL: zed_context_manipulator-1.0.0.tar.gz
- Upload date:
- Size: 72.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30cd4361d0cd7d583ce5e7aae345e97705b5d7eb6c07817952bdb263804b5360
|
|
| MD5 |
335839c9e9e0f6a3948cab97cfc6ca84
|
|
| BLAKE2b-256 |
ab27313d14c406502a42649c08a8ebeb92e42afab7d35c25c8b6f573092b61b4
|
Provenance
The following attestation bundles were made for zed_context_manipulator-1.0.0.tar.gz:
Publisher:
publish.yml on Skyluker4/zed-context-manipulator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zed_context_manipulator-1.0.0.tar.gz -
Subject digest:
30cd4361d0cd7d583ce5e7aae345e97705b5d7eb6c07817952bdb263804b5360 - Sigstore transparency entry: 1730176261
- Sigstore integration time:
-
Permalink:
Skyluker4/zed-context-manipulator@dbdda334f0c5d192fd3839b5d464512afc9c9cc4 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Skyluker4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dbdda334f0c5d192fd3839b5d464512afc9c9cc4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zed_context_manipulator-1.0.0-py3-none-any.whl.
File metadata
- Download URL: zed_context_manipulator-1.0.0-py3-none-any.whl
- Upload date:
- Size: 52.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62d6cb0101e002457cbf3f08bcfb0f3a5de99d47ebe9fe14c182b33089c7d797
|
|
| MD5 |
730d5bb5b50f9cfb3f81675b20a61206
|
|
| BLAKE2b-256 |
eaa488a77d85ab506759ae77219b497ae3b38a4d4880e3f3a098d03bdf482147
|
Provenance
The following attestation bundles were made for zed_context_manipulator-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on Skyluker4/zed-context-manipulator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zed_context_manipulator-1.0.0-py3-none-any.whl -
Subject digest:
62d6cb0101e002457cbf3f08bcfb0f3a5de99d47ebe9fe14c182b33089c7d797 - Sigstore transparency entry: 1730176430
- Sigstore integration time:
-
Permalink:
Skyluker4/zed-context-manipulator@dbdda334f0c5d192fd3839b5d464512afc9c9cc4 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Skyluker4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dbdda334f0c5d192fd3839b5d464512afc9c9cc4 -
Trigger Event:
push
-
Statement type: