Local-first · Multi-agent · SQLite

Untangle your
agent history

Coding agents already write everything to disk. Spaghetti indexes ~/.claude and ~/.codex into one local SQLite workspace — searchable conversations, browsable artifacts, and an SDK you can build on.

MIT License Node ≥ 18 v0.5.17 Claude · Codex
~120ms
Warm start, unchanged files
2×
Faster cold start with Rust
FTS5
Full-text search, always on
Local
No cloud. Your machine only.
Why Spaghetti

Your agent history, finally usable

Not another chat archive. A structured multi-agent index of what coding agents leave on disk — built for search, inspection, and tooling.

⌕

Search that scales

Content-synced FTS5 over every message. Query across projects, agents, and years of work — hundreds of thousands of rows stay responsive.

▣

Multi-agent by design

Claude Code, OpenAI Codex, and Grok side by side in one index. Agent tabs, source columns, and scoped reads keep native identities distinct.

⬡

Local-first by design

SQLite under ~/.spaghetti. No accounts, no cloud sync, no phone-home. Your history never leaves the machine.

⚡

Rust observation engine

One napi-rs owner handles all built-in adapters, bounded reconciliation, SQLite projections, queries, and durable live changes.

⧉

SDK + React exports

The same pipeline powers the CLI. Scripts, Electron apps, and React UIs share @vibecook/spaghetti-sdk.

◎

Three ingest planes

Static disk index, live filesystem deltas, and runtime hooks/channels — keep history warm while agents write without polluting the durable store.

Architecture

One pipeline. Multiple agents. One index.

Stream from disk, write once to SQLite, resume with fingerprints. Claude Code and Codex share a single source_id-tagged store — no per-agent databases.

SOURCE INGEST INDEX CONSUMERS AgentSources multi-agent roots ~/.claude ~/.codex sessions · rollouts subagents · tools todos · plans memory · history source_id column ENGINE · RS Native Rust napi-rs · rayon · rusqlite Default · ~2× cold start REPOSITORY ORACLE Differential tests not package-exported Not a production path SQLITE Local Index ~/.spaghetti/cache/ source_id · FTS5 one index · schema v7 CLI · TUI spag · Ink SDK API createObservationService React · Live /react · api.live fingerprints · byte-offset resume · warm start ~120ms
Rust observation owner
Repository-only differential oracle
Shared SQLite schema + FTS5
Optional live updates
01

Discover

Per-source adapters walk ~/.claude and ~/.codex; each owns classify + extract.

02

Stream parse

Buffered JSONL reads — Claude sessions and Codex rollouts never need to fit in RAM.

03

Single-write

One SQLite connection, rows stamped with source_id, FTS rebuilt at finalize.

04

Fingerprint

Store path, mtime, size, and byte offset for incremental warm starts per source.

05

Query

Source-scoped lists, FTS5 search, and token totals (official or estimated) across agents.

Two planes

History, deltas, and live agent state

Both planes share the durable index. The index is a pure function of files on disk — Spaghetti reads what agents leave behind and never holds a handle to an agent process.

1

Static disk

Cold/warm full parse of agent homes into SQLite + FTS5. Schema v7 with composite project keys (source_id, slug).

2

Live disk Δ

Opt-in watchers for Claude and Codex files. Typed Change events after commit — { live: true } → api.live.

3

Runtime

Hooks and channel sessions via plugins. Read-only relative to the index; if it matters historically, the agent wrote a file first.

One production owner

Observation and queries, together in Rust.

RFC 011 gives discovery, decoding, SQLite writes, queries, live recovery, and durable changes one authority.

◈ Differential oracle

Repository tests only

The retired TypeScript pipeline remains in source control only to compare accepted fixtures during migration.

  • Absent from package exports
  • Cannot be selected by CLI or playground
  • Old Rust bulk/live bridge is feature-gated too
  • Never used as a missing-addon fallback
ⓘ
Rust-only policy: old SPAG_ENGINE, SPAG_NATIVE_INGEST, and persisted engine: "ts" values are ignored. A missing native addon is an actionable startup error.
Coverage

What Spaghetti indexes

From session transcripts to orchestration workflows — structured tables, not opaque blobs.

Both agents

Projects & sessions

source_id, composite project PK, branches, token totals (official or ~ estimated)

Both agents

Messages

Full JSONL / rollouts with FTS text; Claude usage columns · Codex token_count

Claude

Subagents

Transcripts, agent types, nested workflow agents

Claude

Workflows

Agent-orchestration runs with nested subagent graphs

Claude

Todos & plans

Session todos, plan markdown, task metadata

Claude

Memory & tools

MEMORY.md, persisted tool results, file history

Codex

Rollouts & tokens

sessions/**/rollout-*.jsonl, token_count events, tiktoken estimate

TS only

Config · plugins · analytics

Claude settings, marketplaces, telemetry, session-env

CLI & TUI

Terminal-native from day one

Full-screen Ink TUI with bare spag, or one-shot commands when you just need a fast answer. Same index either way.

spag Interactive TUI
spag projects List indexed projects
spag sessions . Sessions for the current repo
spag messages . latest Latest session transcript
spag search "…" Full-text search
spag doctor Health-check paths & plugins

Full command reference →

SDK

Build on the same index

One async API. Scripts, dashboards, and Electron apps all read the same Rust-owned canonical index. React components live under @vibecook/spaghetti-sdk/react.

API reference →
example.ts
import {
  createObservationService,
} from '@vibecook/spaghetti-sdk';

const api = createObservationService({
  dbPath: '/path/to/spaghetti-rs.db',
  sources: [
    { adapterId: 'claude-code', roots: ['/home/me/.claude'] },
    { adapterId: 'codex', roots: ['/home/me/.codex'] },
  ],
  live: true,
});
await api.initialize();

const sources = await api.getSourceIds();
const projects = await api.getProjectList();
const sessions = await api.getSessionList(
  projects[0],
  { sourceId: projects[0].members[0].sourceId },
);

await api.dispose();
Get started

Install in one line

Ship the CLI for daily use, or embed the SDK in any Node project.

shell
npm install -g @vibecook/spaghetti
spag