Records every Claude Code prompt next to the diff it actually produced, then makes that history searchable: a local CLI, a .NET API with hybrid vector + full text search, and a React view that walks each session as a winding trail.
Prompt Trail answers a question that gets lost the moment a coding session ends: which prompt actually caused which change. `prompt-trail init` installs hooks into Claude Code, and from then on every prompt is captured alongside a shadow git repository that snapshots the working tree around it, so each entry carries the diff it produced, not just the text that was typed. Entries land in a local SQLite database and stay browsable offline through the CLI (`log`, `show`, `search`, `stats`), with an `--accepted` filter that hides the prompts that changed nothing.
The backend is an ASP.NET Core API over PostgreSQL. Search is hybrid: pgvector for semantic similarity and a generated full text vector for keyword matching, fused with Reciprocal Rank Fusion and then adjusted by a recency half life and a boost for the project you are currently in. The ranking weights are read through IOptionsMonitor, so they can be retuned without restarting the server. Authentication runs on GitHub OAuth with JWT bearer tokens.
Enrichment summarizes each turn and embeds its problem and its solution as separate vectors: the two halves of a prompt turn out to be worth searching independently. Summarization is routed per row across three modes: everything to a local Ollama model, everything to Claude Haiku, or hybrid, where local absorbs the steady load for free and only the overflow past a queue threshold spills to Haiku. Embeddings are always local (nomic-embed-text, 768 dimensions, matching the vector column width).
Two findings shaped that layer. Ollama's default 4096 token context silently truncated the oldest tokens, exactly where the system prompt sat, so the model described diffs instead of intent; raising the context window fixed summaries that looked like a weak model problem. And concurrency against the local server is pinned at one: at two, each in flight generation allocated its own KV cache on top of a ~5 GB resident model, free memory hit 17%, and throughput collapsed roughly tenfold. Swapping was the bottleneck, not parallelism.
The frontend is React 19 on Vite with Tailwind, TanStack Query, and React Router: a dashboard, a searchable prompt feed with filters, per prompt detail with the full diff, and a session view that renders the trail as a wandering path rather than a flat list, in a deliberately aged parchment theme to make a history log feel like something you would read back.