Every coding-agent engine keeps some history of its own — Claude Code's JSONL
transcripts, a chat-log export, a directory of markdown diaries (OpenClaw's
memory/*.md). None of that reaches the samemind bundle by default: it sits
in the engine's private store, invisible to every other engine sharing the
bundle. samemind capture is the read-only bridge — no bespoke sync script
per engine, one small adapter registry instead (closes
alexgrebeshok-coder/samemind#1).
Related code: tools/capture.mjs. CLI:
npx samemind capture --engine <id> [--source <path>] [--since <ts>] [--dry-run]- Locate — an adapter finds the engine's native session files (a directory walk; read-only, never touches the source).
- Extract — the adapter distills each file into a short note: for Claude Code, the session's final assistant text plus light meta (session id, project/cwd, message count); for a markdown diary, a pointer note (title + first lines + path).
- Mask — obvious secret shapes (
npm_…,sk-…,ghp_…,AKIA…) are replaced with•••masked•••before anything is written. We are reading live transcripts — they sometimes contain pasted tokens. - Quarantine — the distilled text is run through the same
prompt-injection heuristic
memory_write_inboxuses (tools/lib/injection.mjs). Flagged content is never dropped, only wrapped in a fenced```quarantineblock withquarantine: true. - Append — new notes (not already captured) are appended to
inbox/<engine>.md, atomically, in the same append-only shape every other inbox writer in this package uses. - Record — captured keys (session ids for
claude-code, absolute paths forgeneric-markdown) are written to.samemind-capture-state.jsonin the bundle root, so a re-run only picks up what's actually new.
Nothing is promoted into the canon automatically — same rule as import:
inbox/<engine>.md is a curation queue, not the canon. A human or a curating
agent moves anything worth keeping into concepts//entities/projects/.
| Engine id | Default source | What one captured item is |
|---|---|---|
claude-code |
~/.claude/projects/**/*.jsonl (override with --source) |
One session: final assistant text block (truncated to ~1500 chars), session id, project (the transcript's cwd, falling back to the containing directory name), messages (turn count). Sessions with no assistant text at all (queue-only, aborted) are skipped — nothing to distill. |
generic-markdown |
none — --source <path> is required |
One new/changed .md file: title (first # heading, or the filename), the first non-heading lines as a preview, and the file's path relative to --source. Covers any directory of markdown diaries, e.g. OpenClaw's ~/.openclaw/workspace/memory/topics/. |
Adding a third engine is one more entry in the ADAPTERS registry in
tools/capture.mjs: { requiresSource, locate(source), extract(files, opts) }.
locate returns file paths; extract returns
{ key, date, heading, body }[] — key is what idempotency dedupes on.
--engine <id>— required; one of the adapter ids above.--source <path>— override the default location. Required forgeneric-markdown(there is no sane universal default for "a directory of diaries"); optional forclaude-code(mainly useful for testing against a fixture directory instead of the real~/.claude/projects).--since <ts>— an ISO-8601 (or anythingDate.parseaccepts) cutoff; items whose date is older are skipped. Applied after the idempotency filter — an item already captured never resurfaces just because--sincewidened, and widening--sinceon a later run does not re-capture something already recorded in the state file.--dry-run— runs locate/extract/mask/quarantine and reports what would be captured, but writes nothing: noinbox/<engine>.mdchange, no state-file change. A real run afterwards still captures everything the dry-run listed.
.samemind-capture-state.json lives in the bundle root, next to inbox/:
{
"engines": {
"claude-code": { "captured": ["<session-id>", "…"] },
"generic-markdown": { "captured": ["/abs/path/to/note.md", "…"] }
}
}A key present here is never re-captured, regardless of --since. This is
the only state capture writes outside inbox/ — everything else about the
run is read-only.
- Read-only to the source.
capturenever writes into~/.claude/projects/, a markdown diary directory, or any other engine store — only intoinbox/<engine>.mdand.samemind-capture-state.jsoninside the target bundle. - Secret masking is a heuristic, not a scanner. The four patterns
(
npm_/sk-/ghp_/AKIAprefixes) catch the common accidental-paste shapes; they are not a substitute for real secret scanning. Curate before promoting anything out ofinbox/into the canon. - Prompt-injection content is quarantined, not dropped — identical
contract to
memory_write_inbox(see README → MCP → Security). Live transcripts can contain adversarial or copy-pasted instruction-like text; it is still captured (memory is never silently lost) but fenced so nothing downstream executes it blindly. - Path safety:
inbox/<engine>.mdand the state file are the only write targets, both under the bundle root, written throughlib/atomic-write.mjs(temp file + rename).
npx samemind capture --engine claude-code --dry-run
# [dry-run] CAPTURE --engine claude-code
# new: 3
# + <session-id> (2026-07-12T09:41:00.000Z)
# + <session-id> (2026-07-12T08:15:00.000Z) [masked]
# + <session-id> (2026-07-11T22:03:00.000Z)
# skipped (already captured / before --since): 40
# (dry-run — nothing written)
npx samemind capture --engine claude-code
# … same report, this time with `inbox file: inbox/claude-code.md`
npx samemind capture --engine generic-markdown --source ~/.openclaw/workspace/memory/topics --since 2026-07-01- docs/interop.md —
export/import(foreign OKF-bundle interchange; a different problem — a whole bundle, not a live native session store). - docs/memory-protocol.md — the
memory_write_inboxcontractcapturemirrors (append-only, quarantine, never promotes to canon). - README → MCP → Security — the injection-quarantine and path-safety guarantees shared across every write path in this package.