.metalworks/ folder in your project, created the same way git init
creates .git.
This is what lets the CLI and the Claude Code plugin work in steps: one
command produces a report, the next takes its report_id and builds on it.
It’s opt-in — zero footprint until you ask
metalworks writes nothing to disk unless you create a project. A one-offmw.research(...) or metalworks research run with no project keeps everything in memory and
leaves no trace — exactly like running git status outside a repo.
Create the memory when you want it to stick:
.metalworks/ in the current directory. From then on, runs persist and commands
chain. (In Python, the facade auto-detects a project by walking up from the working directory,
just like git finds .git.)
What’s inside .metalworks/
| File | Holds | Committed? |
|---|---|---|
project.json | The manifest — project id, slug, idea, and a list of every run. | Yes |
config.toml | Non-secret settings (provider, model). Secrets only ever come from env vars. | Yes |
corpus.db | Your saved research data — the posts and comments you’ve read. Kept out of git so your raw data stays private. | No |
runs/<report_id>/research.json | The full Research bundle for one run — saved and safe to commit. | Yes |
runs/<report_id>/research.md | A human-readable summary of the same run. | Yes |
artifacts/<kind>.json | The latest output of each later stage (positioning, marketing site, content plan, …). | Yes |
metalworks init writes a .gitignore inside .metalworks/ that excludes corpus.db for
you, so committing the directory captures your research and outputs without the bulky,
re-buildable cache.
How commands chain
Every run mints areport_id. List your runs to find it, then pass it to any later command:
report_id and a new runs/<id>/ directory — your history
is the set of run directories plus git, nothing is overwritten. Later-stage artifacts record
the report_id they were built from, so a stale positioning brief is detectable by comparing
its stamp to your latest run.
You can also update a report instead of starting fresh: metalworks research refresh <report_id> re-runs it against everything you’ve collected since and shows what changed, saved
as a new version. metalworks research versions <report_id> lists every version; older ones
are kept exactly as they were. See your research data.
In Python the chaining is implicit — you hold the bundle and pass it along — but the same
persistence happens underneath when a project exists:
Where memory lives when there’s no project
| Situation | Store used | Footprint |
|---|---|---|
Inside a .metalworks/ project | SQLite on .metalworks/corpus.db | Persists; memory accumulates across commands |
| No project (casual use) | In-process memory | Zero — nothing written |
~/.metalworks/ for runs you make outside any project,
and that’s where the Reddit post-log.jsonl audit trail and any connected-account tokens
(encrypted at rest) live.
Under the hood: the stores
The corpus database is backed by a set of typed repositories —CorpusRepo (source-neutral
records, comments, embeddings), RunRepo (runs + finished reports), OpportunityRepo,
InboxRepo, AccountRepo, BriefRepo. Two backends ship in core: MemoryStores (the zero-footprint default) and
SqliteStores (the project file). They’re protocols, so you can point metalworks at a hosted
backend (Postgres/PostgREST) — see Bring your own store.
See also
- Python SDK reference —
Metalworks(store=...)and the.research()flow. - CLI —
init,research run,research list,build init. - Data model — what’s inside a run’s
research.json. - Bring your own store — swap SQLite for a hosted backend.