Skip to content

Files & config

threadle writes to exactly one directory. Everything else on your disk it treats as read-only.

~/.config/threadle/
├── settings.json
├── graphs/
│ ├── starter.json
│ └── <id>.json
├── payloads/
│ ├── <aa>/<sha256>.json
│ ├── <aa>/<sha256>.md
│ └── tags.json
├── nodes/
│ └── <name>/node.json · node.ts
├── skills/
│ ├── custom/
│ └── imported/
├── runs/
│ ├── jobs.jsonl
│ ├── injects.jsonl
│ └── logs/<jobId>.jsonl
├── search.db
├── models-pricing.json
├── cursor-usage.json
└── tmp/

PathContentsClearable in UI
settings.jsonEditor, pricing source, billing mode, examples toggle — see Settingsno
graphs/One JSON per workflow or sub-workflow, named by graph id. The seeded starter graph is always starter.jsonno
payloads/Content-addressed context payloads, sharded by the first two hex characters of their SHA-256yes (destructive)
payloads/tags.jsonSidecar map of hash → tags, so stored payloads stay immutable
nodes/Custom nodes — a node.json manifest or a TypeScript class entry, plus whatever the node shipsno
skills/customSkillsets you authored inside threadleno
skills/importedSkillsets imported into threadle. Project-repo skills override these on a name clashyes
runs/jobs.jsonlAppend-only record of every run shown in the Runs viewyes
runs/logs/<jobId>.jsonlPer-run log, one JSON line per emitted line ({ts, lane, line})yes
runs/injects.jsonlWhich payload was injected into which session — powers Lineageyes (destructive)
search.dbSQLite full-text index over transcripts and payloads. Deleting it just makes the next search sloweryes
models-pricing.jsonCached model price tableyes
cursor-usage.jsonToken counts captured from the Cursor CLI’s own usage object after a run
tmp/Scratch files written while injecting context into sessions; files older than 72 h are garbage-collectedyes

Settings → threadle internals lists these live, with sizes and file counts, and clears the safe ones.


A payload’s identity is sha256(kind \0 provider \0 sessionId \0 content). Distill the same session twice with the same result and you get one file, not two.

Each payload is stored twice in the same shard directory:

  • <hash>.json — the payload object (kind, source, content, metadata)
  • <hash>.md — a plain-text sidecar so the directory is readable by a human or by grep. Written for every kind except files.

Writes are atomic: content goes to <hash>.json.tmp and is renamed into place. Graph writes use the same trick with a random suffix.

Because the hash is the identity, a portable graph strips payloadHash on export — the blob is local to the machine that made it.


VariableEffect
THREADLE_CONFIG_DIRReplaces ~/.config/threadle entirely. Every path above is relative to it

Set it to keep separate profiles, or to point threadle at a scratch directory during experiments:

Terminal window
THREADLE_CONFIG_DIR=/tmp/threadle-scratch threadle --no-open

Provider storage is parsed in place. Nothing under these paths is ever created, modified, or deleted by threadle.

ProviderPathEnv override
Claude Code~/.claude/projects/<slug>/<uuid>.jsonl, ~/.claude/history.jsonl, ~/.claude/sessions/*.json, ~/.claude/agents/*.mdCLAUDE_CONFIG_DIR
opencode~/.local/share/opencode/opencode.db — opened readOnly: trueOPENCODE_DATA_DIR
Cursor~/.cursor/projects/<slug>/agent-transcripts/…, ~/.cursor/chats/<md5>/…/meta.jsonCURSOR_CONFIG_DIR

Two more files are read but not written: ~/.claude.json (for the detected plan shown in Settings) and ~/.cache/opencode/models.json (preferred source for model prices).

Project directories are read for skills and rules files, and for agent definitions in <projectDir>/.claude/agents/. When you run an agent, the agent’s own CLI writes its own session storage exactly as it would from a terminal — that writing belongs to the tool, not to threadle.


  • Settings — the keys inside settings.json
  • Trust model — the read-only guarantee in full
  • Providers — how each storage format is parsed
  • CLI — where a headless run persists its results