rawclaw

module
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT

README

RawClaw

Your AI already knew. It just couldn't find it.

Every decision your agent ever made is sitting in ~/.claude/projects. The only way back to it is grep — which matches the string you typed, not the decision you meant: your past self phrased it differently, the answer got folded into a compaction summary, and grep walks right past it. So your agent re-derives what it already worked out.

Here's the cost — real recall hunts from one developer's history:

recalling one past decision grep calls
a deployment/auth decision 76
how PR-review state got tracked 68
where the messaging stack landed 51
a git-worktree-per-desk workflow 49
model-routing between two services 26
handling externalized tool-results 13

Dozens of greps to recover one fact — and plenty still came up empty. RawClaw replaces the "grep storm" with one search that hands you the whole arc: goal → match → resolution.

76 → 1.

rawclaw "where did we land on auth"

Claude Code deletes your history after 30 days

Claude Code removes conversation files older than ~30 days (cleanupPeriodDays). RawClaw keeps its own copy: sessions stay searchable and readable after the original file is gone. Results mark them source file gone — retained history, and rawclaw delete removes them for good.

Want RawClaw to track your Claude Code retention setting instead? Set RAWCLAW_RETENTION=mirror and sessions drop when their source file does.

Setup: let your agents discover it

Installing the binary gives you the tool; rawclaw setup tells your agents about it. One command wires a short session-start note into Claude Code (and Codex, when installed) so every new session knows rawclaw exists and how to search with it — no hand-editing config files.

rawclaw setup        # shows the plan, asks y/N
rawclaw setup --yes  # non-interactive

Global by default (rawclaw searches every project); --project narrows the note to the current project only. Everything else already hooked into your configs is left untouched, and re-running never duplicates. rawclaw setup --eject removes exactly what setup installed — nothing more.

What it does

Claude Code quietly saves every conversation you have with it as JSONL transcripts under ~/.claude/projects. RawClaw indexes them with SQLite FTS5 and searches them the way you actually type.

  • Goal → match → resolution. Every hit returns the session's opening (what it set out to do), the matched message in context, and the closing (what was decided) — so one result usually answers the question without opening a file.
  • All your projects by default. One query spans every Claude Code folder you've worked in (--this-project to narrow).
  • Natural phrasing works. Multi-word queries OR their terms and rank by how many match — you don't need the exact wording.
  • Reads the full structure, shows the signal. Subagent threads, tool calls, compaction summaries, and thinking blocks are all indexed, but search defaults to clean human conversation (--include-tools / --include-subagents to widen).
  • Built for agents too. rawclaw "query" returns ranked refs with a never-silent completeness envelope; read <ref> returns a bounded excerpt instead of a whole transcript; --json on every command.

Who it's for

  • Anyone who lives in Claude Code and wants to find a past decision in one search instead of a dozen greps.
  • AI / agent builders who need programmatic recall of prior sessions — JSON in, JSON out.
  • CI / automation / scripts — non-interactive, composes with jq/fzf, real exit codes.
  • Resource-constrained / self-hosted / Raspberry Pi. A single static binary + SQLite FTS5: low RAM, no GPU, no runtime to install. Keyword search needs no network and no API key.

Install

go install github.com/MoonCaves/rawclaw/cmd/rawclaw@latest

Or grab a release binary — no runtime dependencies, a single static binary — from the releases page and put it on your PATH.

RawClaw is a single static binary: pure Go, no cgo, cross-compiles to Linux / macOS / ARM. The keyword core has no runtime dependencies, no LLM, and needs no API keys.


Keeping it current

RawClaw updates itself — the binary you install by hand is the last one you install by hand:

rawclaw upgrade            # update in place to the latest release
rawclaw upgrade --check    # report whether a newer release exists (exit 10 if so)

upgrade downloads the release for your OS/arch, sha256-verifies it against the release's published checksums (a mismatch aborts without touching your installed binary), then atomically replaces the running binary with rollback on failure. --check only reports — it downloads nothing and exits 10 when an update is available (so scripts can gate on it). An unstamped dev build won't replace itself without --force.


Usage

rawclaw "where did we set up auth"          # search (default): ranked hits + read-refs (all projects)
rawclaw --this-project "auth"               # narrow to the current project
rawclaw read <session8>:<uuid8>             # bounded excerpt around a ref (--more/--around/--budget/--focus)
rawclaw outline <session8>                  # the session's goal → resolution arc
rawclaw                                      # browse: your most recent sessions
rawclaw --resume <session8>                 # paste-ready `claude --resume` for that session
rawclaw --stats                             # corpus overview (this project; --all for everything)
rawclaw "query" --json                      # machine-readable output for scripts/agents
rawclaw "query" --since 2026-01-01 --before 2026-02-01   # date-scoped
rawclaw --list                              # list searchable projects
rawclaw delete --yes --files <session8>     # delete a session non-interactively (--files: originals still on disk)
rawclaw version                             # print the version + build stamp
rawclaw --timeout 2m "query"                # raise the self-terminating deadline (0 disables it)

Query tips: a single distinctive word is sharpest · "exact phrase" for adjacency · term* for a prefix · a NOT b to exclude · --include-path / --exclude-path to scope by project · --json works on every shape.

Built to never hang. Every run is bounded by a self-terminating watchdog so an agent never needs an external timeout(1): --timeout 2m raises the deadline, --timeout 0 disables it, and RAWCLAW_TIMEOUT (a Go duration like 45s or 2m) overrides the default. The default is 30s; exceeding the deadline exits 124 (the timeout(1) convention). The syncing archive verbs (archive init/push/pull) are the exception: their transfers are bounded by stall detection instead — a hung transfer dies in under a minute, a slow-but-moving push runs as long as it keeps moving (an explicit --timeout still applies a hard cap). delete --yes (alias -y) skips the confirmation prompt for non-interactive use; when the delete would remove original transcript files still on disk, --yes alone refuses — add --files to authorize that too.

For agents

The surface is agent-first by default — an LLM recalls its own history without pasting whole transcripts:

rawclaw "query"                        # ranked hits, each with a copyable read-ref + completeness envelope
rawclaw read <session8>:<uuid8>        # a bounded excerpt around a ref (--more/--around/--budget/--focus)
rawclaw outline <session8>             # the goal → resolution arc, to pick where to read next

--json throughout — so an agent searches, picks a ref, and reads a bounded slice instead of its whole context on one transcript. Search is the default verb; read and outline are top-level verbs.


How it works

A small SQLite FTS5 index over your transcripts, refreshed incrementally on each run — only changed sessions re-index, so it stays fast even on a large history. Within a session, messages are ordered by insertion id (not timestamp, which can be non-monotonic), and shaped into the goal → match → resolution view.

Optional semantic search — bring your own embedder

RawClaw is keyword-only out of the box — no LLM, no model, no API key; that is the whole product. Semantic search is an opt-in upgrade: point one environment variable at any embeddings endpoint, and RawClaw reciprocal-rank-fuses vector hits with the keyword hits, so a paraphrase whose words never literally appear still finds the right conversation.

export RAWCLAW_EMBED_ENDPOINT=http://localhost:11434/api/embeddings   # any local Ollama
export RAWCLAW_EMBED_MODEL=nomic-embed-text
rawclaw --reindex-vectors          # embed your history once (incremental, resumable)
rawclaw "monthly billing for the paid tier"   # now lexical + semantic, fused
  • Local, fully private: Ollama — no API key, nothing leaves your machine.
  • Hosted: OpenAI, Voyage, or any OpenAI-compatible gateway (RAWCLAW_EMBED_WIRE=openai + RAWCLAW_EMBED_KEY).

Vectors live as BLOBs in the same on-disk index; cosine scoring and fusion run in pure Go — no LLM, no extra service, no numpy, no GPU. Unset the env (or pass --no-vector) and you're back to byte-identical keyword search.

Topic tags — where the conversation pivoted

Every session gets tagged at its topic-change points with concept keywords, so an agent can always find the moment a conversation pivoted — rawclaw topics "<concept>" drops it right there.

Optional transcript archive — a git remote as durable, multi-machine storage

Raw transcripts die upstream (Claude Code's ~30-day purge) and live on only one machine until they don't. The archive gives them one durable home: any private git remote (GitHub, Gitea, a bare repo over SSH — no rawclaw-specific server).

rawclaw archive init git@github.com:you/rawclaw-transcripts.git   # clone + register this machine (--name to pick the dir name)
rawclaw archive push                                              # upload this machine's Claude + Codex transcripts
rawclaw archive pull                                              # fetch the other machines' transcripts
rawclaw archive status                                            # remote, clone, last push/pull, per-machine freshness

The remote argument accepts a shorthand: you/rawclaw-transcripts (or a bare you, which assumes a repo named rawclaw-transcripts) expands to git@github.com:you/rawclaw-transcripts.git; gitlab.com/you/repo and sr.ht/~you/repo work too. A full URL is used as-is.

Each machine gets a top-level directory in the repo (<machine>/<source>/...), so pushes from different machines never conflict on content; concurrent pushes are resolved with a bounded pull-rebase-and-retry. A second push moves only changed files. The local clone lives in the state dir and is a rebuildable cache — deleting it just forces a re-clone (archive pull rebuilds everything); a clone left broken by a kill at any point (mid-clone, mid-rebase, even a stale git lock after a power loss) is detected and rebuilt automatically on the next push or pull. Rebuilds are evidence-gated: if the broken clone still holds commits that never reached the remote, rawclaw refuses to wipe it and tells you how to recover them instead.

Deletes propagate — but only your own, and only explicit ones. rawclaw delete <session8> (or a --before/--project/--max-messages filter) removes the session's transcript file when it is still on disk plus rawclaw's copy (index + archive); deleting a retained session — one whose transcript the source tool already purged — removes only rawclaw's copy, leaving Claude Code / Codex transcript files untouched. The confirmation says which of the two you're doing; non-interactively, --yes alone covers retained-only deletes and a delete that removes original files requires --yes --files. A session removed with rawclaw delete is also removed from the archive on this machine's next push, so an explicit delete is never resurrected by a later pull — and once other machines pull, their search indexes drop it too (for archive scopes, gone from the archive means gone). Nothing else ever deletes from the archive: transcripts purged upstream (or pruned locally by RAWCLAW_RETENTION=mirror) keep their archive copies — the archive is the durable mirror. Other machines' directories are read-only from here; a delete filter that matches another machine's sessions is refused with a pointer at the origin machine.

Cross-machine search is automatic. After a pull, a plain rawclaw "query" covers every other machine's pushed sessions like local hits — labeled <machine>/<project>, provenance-stamped with the owning machine's id, readable and outlinable by ref. Your own machine's directory in the clone is ignored (the live local tree is fresher), so nothing ever double-counts. --resume on a foreign session tells you which machine it lives on and the command to run there. A machine that hasn't pushed in over a day shows up in search output as possibly stale — its results are still served.

Staying current is automatic. Once the archive is configured, ordinary usage keeps it fresh: after a search/read/outline prints its results, rawclaw fires a detached background sync (push + throttled pull) — your command never waits on the network, a hung push can never hold a tool call open, and a burst of searches costs one sync per five-minute window. Receipts land in archive/autosync.log under the state dir; set RAWCLAW_ARCHIVE_AUTOSYNC=off to disable the background sync, or RAWCLAW_ARCHIVE=off to switch the whole archive feature off (searches go local-only, nothing syncs) without touching its config. For machines that mostly run agents rather than searches, rawclaw archive enable-timer installs an hourly archive push under your user account (a launchd agent on macOS, a systemd user timer on Linux) — never wired silently, and --eject removes exactly what was added. Two rawclaw processes syncing at once (timer + search, two shells) is safe: a machine-wide lock serializes them, and the loser skips cleanly.

The remote MUST be a private repository. Transcripts contain whatever you and your agents pasted into sessions — API keys, tokens, private code. RawClaw prints this warning at archive init and cannot verify a host's visibility settings for you.

Live peek — what is that machine's agent doing right now?

The archive is durable, not instant. For seconds-fresh visibility, rawclaw live skips it entirely and reads the other machine's in-progress session over one SSH hop:

rawclaw live box-a               # list box-a's recent sessions, newest first
rawclaw live box-a 3f2a91c0      # render that session's current transcript (messages written seconds ago included)

The machine name doubles as the ssh destination — an ~/.ssh/config Host alias just works — or map it explicitly in the archive config ("ssh": {"box-a": "user@10.0.0.5"}). The far end needs sshd plus a rawclaw on its non-interactive PATH; without that, live fails with a pointed error and your freshness is whatever the last archive push uploaded. --json gives agents the structured form (raw message text, tool calls and all); --tail N widens the transcript window. The rendered transcript follows the same display posture as read and outline — tool calls stripped unless --include-tools asks for them. No summarizing, no interpretation.


Roadmap

Where RawClaw is headed — new source formats (Codex/Gemini/opencode), semantic tuning, and the trade-offs we deliberately won't make — is in ROADMAP.md.


License

MIT

Directories

Path Synopsis
cmd
rawclaw command
Command rawclaw is the lightweight, headless search engine over the Claude Code transcript record (a single static pure-Go binary).
Command rawclaw is the lightweight, headless search engine over the Claude Code transcript record (a single static pure-Go binary).
internal
adapters
Package adapters holds concrete Embedder adapters for the embed.Embedder port.
Package adapters holds concrete Embedder adapters for the embed.Embedder port.
agentproto
Package agentproto is the search→narrow→bounded-read protocol for LLM agents: an agent recalls prior conversations WITHOUT pasting whole transcripts — it gets ranked conversation refs, then reads BOUNDED excerpts on demand.
Package agentproto is the search→narrow→bounded-read protocol for LLM agents: an agent recalls prior conversations WITHOUT pasting whole transcripts — it gets ranked conversation refs, then reads BOUNDED excerpts on demand.
archive
Package archive implements the transcript archive: one git repository shared by all of a user's machines, holding each machine's raw transcript trees under a top-level directory per machine (`<machine>/<source>/...`).
Package archive implements the transcript archive: one git repository shared by all of a user's machines, holding each machine's raw transcript trees under a top-level directory per machine (`<machine>/<source>/...`).
archive/archivetest
Package archivetest stands up throwaway transcript-archive fixtures: an isolated HOME with one local Claude project, a configured archive against a local bare remote, and one FOREIGN machine dir pushed by a simulated second machine and pulled into the local clone.
Package archivetest stands up throwaway transcript-archive fixtures: an isolated HOME with one local Claude project, a configured archive against a local bare remote, and one FOREIGN machine dir pushed by a simulated second machine and pulled into the local clone.
cli
Package cli is the thin composition root: the cobra command tree, flag wiring, the flat-output printers, and the JSON emitters.
Package cli is the thin composition root: the cobra command tree, flag wiring, the flat-output printers, and the JSON emitters.
embed
Package embed defines the optional vector-channel PORTS — Embedder and VectorStore — that the keyword engine degrades around gracefully.
Package embed defines the optional vector-channel PORTS — Embedder and VectorStore — that the keyword engine degrades around gracefully.
index
Package index owns ingest orchestration over the on-disk SQLite/FTS5 store: schema ensuring (over internal/store's DDL), file fingerprinting, incremental reindexing, and corpus stats.
Package index owns ingest orchestration over the on-disk SQLite/FTS5 store: schema ensuring (over internal/store's DDL), file fingerprinting, incremental reindexing, and corpus stats.
lifecycle
Package lifecycle implements USER-DRIVEN session lifecycle operations over Claude Code transcripts: archiving a session's .jsonl out of the active projects tree, and deleting sessions behind a filter gate.
Package lifecycle implements USER-DRIVEN session lifecycle operations over Claude Code transcripts: archiving a session's .jsonl out of the active projects tree, and deleting sessions behind a filter gate.
live
Package live implements the live peek: "what is the agent on that machine doing right now".
Package live implements the live peek: "what is the agent on that machine doing right now".
model
Package model defines the normalized transcript message a Source adapter yields for the index to ingest — the ingest-side data contract, parallel to internal/embed's vector ports (defined ahead of its adapters, ship-empty).
Package model defines the normalized transcript message a Source adapter yields for the index to ingest — the ingest-side data contract, parallel to internal/embed's vector ports (defined ahead of its adapters, ship-empty).
parse
Package parse holds text-extraction and parse primitives — flattening one transcript JSON record into a searchable string, tool-stripping, role lookup, ISO-timestamp parsing, and display formatting.
Package parse holds text-extraction and parse primitives — flattening one transcript JSON record into a searchable string, tool-stripping, role lookup, ISO-timestamp parsing, and display formatting.
paths
Package paths holds transcript-directory discovery: the projects root, cwd→project-dir resolution (by matching the cwd recorded inside transcripts), contained-JSONL enumeration with symlink-out containment, and session-id resolution.
Package paths holds transcript-directory discovery: the projects root, cwd→project-dir resolution (by matching the cwd recorded inside transcripts), contained-JSONL enumeration with symlink-out containment, and session-id resolution.
provenance
Package provenance mints and derives the identity facts stamped onto indexed rows (D3): the machine's stable self-minted id, the cheap file content fingerprint used as a change watermark, and the session id derived from a transcript path.
Package provenance mints and derives the identity facts stamped onto indexed rows (D3): the machine's stable self-minted id, the cheap file content fingerprint used as a change watermark, and the session id derived from a transcript path.
query
Package query holds pure query-layer string functions: term parsing, FTS5 query sanitizing, stopword stripping, boolean-operator translation, and the path / min-messages predicates.
Package query holds pure query-layer string functions: term parsing, FTS5 query sanitizing, stopword stripping, boolean-operator translation, and the path / min-messages predicates.
render
Package render holds the terminal output formatters for the discovery / scroll / browse shapes.
Package render holds the terminal output formatters for the discovery / scroll / browse shapes.
retention
Package retention is the durable-retention policy tree (D1/D2/D5) applied to an indexed scope: the single decision function shared by acting reconciles and read-only probes, the reconcile pass that acts on it, and the user's mirror-mode opt-out.
Package retention is the durable-retention policy tree (D1/D2/D5) applied to an indexed scope: the single decision function shared by acting reconciles and read-only probes, the reconcile pass that acts on it, and the user's mirror-mode opt-out.
retrieve
Package retrieve holds FTS5 keyword recall: search, the linear-scan fallback, cross-project the cross-project search, and the anchor helpers (lineage walk + ranked anchor messages) that the view layer composes into bookend windows.
Package retrieve holds FTS5 keyword recall: search, the linear-scan fallback, cross-project the cross-project search, and the anchor helpers (lineage walk + ranked anchor messages) that the view layer composes into bookend windows.
scopes
Package scopes builds the search-scope list spanning every runtime — the one place that knows about the concrete Source adapters.
Package scopes builds the search-scope list spanning every runtime — the one place that knows about the concrete Source adapters.
semantic
Package semantic is the optional vector channel: a float32-BLOB VectorStore living in the SAME cache db as the keyword index (under its own schema gate), brute-force cosine KNN in plain Go, and reciprocal-rank fusion.
Package semantic is the optional vector channel: a float32-BLOB VectorStore living in the SAME cache db as the keyword index (under its own schema gate), brute-force cosine KNN in plain Go, and reciprocal-rank fusion.
source
Package source defines the ingest PORT: a small interface each runtime's transcript reader implements, so the index can ingest Claude Code, Codex, and future runtimes without knowing the on-disk format underneath.
Package source defines the ingest PORT: a small interface each runtime's transcript reader implements, so the index can ingest Claude Code, Codex, and future runtimes without knowing the on-disk format underneath.
source/claude
Package claude is the Source adapter for Claude Code transcripts under ~/.claude/projects (or $CLAUDE_CONFIG_DIR/projects): one project directory per working dir, one *.jsonl per session, subagents under a subagents/ subdir.
Package claude is the Source adapter for Claude Code transcripts under ~/.claude/projects (or $CLAUDE_CONFIG_DIR/projects): one project directory per working dir, one *.jsonl per session, subagents under a subagents/ subdir.
source/codex
Package codex is the Source adapter for OpenAI Codex CLI transcripts under $CODEX_HOME/sessions (default ~/.codex/sessions): date-partitioned rollout-<ts>-<uuid>.jsonl files, each a self-contained session whose first record is a session_meta header.
Package codex is the Source adapter for OpenAI Codex CLI transcripts under $CODEX_HOME/sessions (default ~/.codex/sessions): date-partitioned rollout-<ts>-<uuid>.jsonl files, each a self-contained session whose first record is a session_meta header.
store
FTS read surface: the two keyword-recall queries (flat hits and anchor rows) over messages_fts + messages + sessions, with dynamic filter composition.
FTS read surface: the two keyword-recall queries (flat hits and anchor rows) over messages_fts + messages + sessions, with dynamic filter composition.
store/storetest
Package storetest provides interface-built test fixtures for the store schema (D7): a production-schema db via store.Rebuild, plus row inserters.
Package storetest provides interface-built test fixtures for the store schema (D7): a production-schema db via store.Rebuild, plus row inserters.
timefmt
Package timefmt is the single seam for user-facing time rendering.
Package timefmt is the single seam for user-facing time rendering.
view
Package view does result-shaping: the bookends+window anchored view, org-wide discovery (lineage-deduped, RRF-fused when an embedder is wired), scroll (keep-reading), and browse (no-query recent sessions).
Package view does result-shaping: the bookends+window anchored view, org-wide discovery (lineage-deduped, RRF-fused when an embedder is wired), scroll (keep-reading), and browse (no-query recent sessions).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL