diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 766c0fc..9125d3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,3 +143,77 @@ jobs: generate_release_notes: false draft: true prerelease: true + + auto-release: + name: Push(main) / Auto Release + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: test-merge + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Check if already tagged + id: check + run: | + # Skip if this commit already has a stable version tag + for tag in $(git tag --points-at HEAD 2>/dev/null); do + if echo "$tag" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "Commit already tagged as $tag, skipping." + exit 0 + fi + done + echo "skip=false" >> "$GITHUB_OUTPUT" + + - name: Setup Bun + if: steps.check.outputs.skip != 'true' + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + if: steps.check.outputs.skip != 'true' + run: bun install + + - name: Compute release metadata + if: steps.check.outputs.skip != 'true' + id: meta + run: | + bun run scripts/release-meta.ts --allow-invalid --github-output "$GITHUB_OUTPUT" + + # Squash merges lose individual commit types, so if bump is + # "none" but there are unreleased commits, default to patch. + BUMP=$(grep '^bump=' "$GITHUB_OUTPUT" | cut -d= -f2) + COUNT=$(grep '^commit_count=' "$GITHUB_OUTPUT" | cut -d= -f2) + if [ "$BUMP" = "none" ] && [ "$COUNT" -gt 0 ]; then + echo "Bump was 'none' with $COUNT commits — overriding to 'patch'" + LATEST=$(git tag --list 'v*.*.*' --sort=-version:refname \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1) + if [ -n "$LATEST" ]; then + IFS='.' read -r MAJ MIN PAT <<< "${LATEST#v}" + NEXT="v${MAJ}.${MIN}.$((PAT + 1))" + else + NEXT="v0.1.0" + fi + echo "next_version=${NEXT}" >> "$GITHUB_OUTPUT" + echo "bump=patch" >> "$GITHUB_OUTPUT" + fi + + - name: Create release + if: steps.check.outputs.skip != 'true' && steps.meta.outputs.bump != 'none' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.meta.outputs.next_version }} + target_commitish: ${{ github.sha }} + name: ${{ steps.meta.outputs.next_version }} + body: ${{ steps.meta.outputs.release_notes }} + generate_release_notes: false + draft: false + prerelease: false diff --git a/.gitignore b/.gitignore index 659c46f..2e3da59 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,13 @@ temp/ .smriti/CLAUDE.md .smriti/knowledge/ .smriti/index.json + +# Personal writing / local-only notes +docs/writing/ + +# Letta Code agent state +.letta/ + +# Zsh plugins (should not be in project repo) +zsh-autosuggestions/ +zsh-syntax-highlighting/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93fbf7b..a52b986 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: name: Gitleaks - Detect secrets entry: gitleaks detect --source . -c .gitleaks.toml language: system - stages: [commit] + stages: [pre-commit] pass_filenames: false always_run: true diff --git a/README.md b/README.md index ba9e970..ad3ee91 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,40 @@ Smriti — Shared memory for AI-powered engineering teams

-Built on top of [QMD](https://github.com/tobi/qmd) by Tobi Lütke. +

+ An exploration of memory in the agentic world +

--- -## The Problem +The agentic world is moving fast. Every team is shipping with AI — Claude Code, +Cursor, Codex, Cline. The agents are getting better. The tooling is maturing. + +But there's a gap nobody has fully closed: + +> **Agents don't remember.** + +Not from yesterday. Not from each other. Not from your teammates. Every session +starts from zero, no matter how much your team has already figured out. + +This isn't just a developer experience problem. It's a foundational gap in how +agents work. As they get more capable and longer-running, memory becomes a +prerequisite — not a feature. Without it, knowledge stays buried in chat +histories. Teams re-discover what they've already figured out. Decisions get +made twice. + +The answer, I think, mirrors how our own memory works: -Your team ships code with AI agents every day — Claude Code, Cursor, Codex. But -every agent has a blind spot: +> **Ingest → Categorize → Recall → Search** -> **They don't remember anything.** Not from yesterday. Not from each other. Not -> from your teammates. +That's the brain. That's what **Smriti** (Sanskrit: _memory_) is building +toward. -Here's what that looks like: +--- + +## The Problem, Up Close + +Here's what the gap looks like in practice: | Monday | Tuesday | | ------------------------------------------------------------- | --------------------------------------------------- | @@ -31,16 +52,17 @@ The result: - **Zero continuity** — each session starts from scratch, no matter how much your team has already figured out -The agents are brilliant. But they're amnesic. **This is the biggest gap in -AI-assisted development today.** +The agents are brilliant. But they're amnesic. **This is the biggest unsolved +gap in agentic AI today.** + +--- ## What Smriti Does -**Smriti** (Sanskrit: _memory_) is a shared memory layer that sits underneath -all your AI agents. +Smriti is a shared memory layer that sits underneath your AI agents. -Every conversation → automatically captured → indexed → -searchable. One command to recall what matters. +Every conversation → automatically captured → indexed → searchable. One command +to recall what matters. ```bash # What did we figure out about the auth migration? @@ -53,12 +75,67 @@ smriti list --project myapp smriti search "rate limiting strategy" --project api-service ``` -> **20,000 tokens** of past conversations → **500 tokens** of relevant -> context. Your agents get what they need without blowing up your token budget. +> **20,000 tokens** of past conversations → **500 tokens** of relevant context. +> Your agents get what they need without blowing up your token budget. -## The Workflow +Built on top of [QMD](https://github.com/tobi/qmd) by Tobi Lütke. Everything +runs locally — no cloud, no accounts, no telemetry. + +--- + +## Install + +**macOS / Linux:** + +```bash +curl -fsSL https://raw.githubusercontent.com/zero8dotdev/smriti/main/install.sh | bash +``` + +**Windows** (PowerShell): + +```powershell +irm https://raw.githubusercontent.com/zero8dotdev/smriti/main/install.ps1 | iex +``` + +Both installers will: + +- Install [Bun](https://bun.sh) if you don't have it +- Clone Smriti to `~/.smriti` +- Set up the `smriti` CLI on your PATH +- Configure the Claude Code auto-save hook + +**Requirements:** macOS, Linux, or Windows 10+ · Git · Bun ≥ 1.1 +(auto-installed) · Ollama (optional, for synthesis) + +```bash +smriti upgrade # update to latest +``` + +--- + +## Quick Start + +```bash +# 1. Ingest your recent Claude Code sessions +smriti ingest claude + +# 2. Search what your team has discussed +smriti search "database connection pooling" + +# 3. Recall with synthesis into one coherent summary (requires Ollama) +smriti recall "how did we handle rate limiting" --synthesize + +# 4. Share knowledge with your team through git +smriti share --project myapp +git add .smriti && git commit -m "chore: share session knowledge" + +# 5. Teammates pull it in +smriti sync --project myapp +``` + +--- -Here's what changes when your team runs Smriti: +## The Workflow **1. Conversations are captured automatically** @@ -101,49 +178,11 @@ teammates pull it and import it into their local memory. No cloud service, no account, no sync infrastructure — just git. ```bash -# Share what you've learned smriti share --project myapp --category decision - -# Pull in what others have shared smriti sync --project myapp ``` -## Install - -**macOS / Linux:** - -```bash -curl -fsSL https://raw.githubusercontent.com/zero8dotdev/smriti/main/install.sh | bash -``` - -**Windows** (PowerShell): - -```powershell -irm https://raw.githubusercontent.com/zero8dotdev/smriti/main/install.ps1 | iex -``` - -Both installers will: - -- Install [Bun](https://bun.sh) if you don't have it -- Clone Smriti to `~/.smriti` -- Set up the `smriti` CLI on your PATH -- Configure the Claude Code auto-save hook - -### Requirements - -- **macOS, Linux, or Windows 10+** -- **Git** -- **Bun** >= 1.1 (installed automatically) -- **Ollama** (optional — for local summarization and synthesis) - -### Upgrade - -```bash -smriti upgrade -``` - -Pulls the latest version from GitHub and reinstalls dependencies. Equivalent to -re-running the install script. +--- ## Commands @@ -175,8 +214,8 @@ smriti categorize # Auto-categorize sessions smriti projects # List all tracked projects smriti upgrade # Update smriti to the latest version -# Context and comparison -smriti context # Generate project context for .smriti/CLAUDE.md +# Context injection +smriti context # Generate project context → .smriti/CLAUDE.md smriti context --dry-run # Preview without writing smriti compare --last # Compare last 2 sessions (tokens, tools, files) smriti compare # Compare specific sessions @@ -187,6 +226,8 @@ smriti sync # Import teammates' shared knowledge smriti team # View team contributions ``` +--- + ## How It Works ``` @@ -221,308 +262,7 @@ Claude Code Cursor Codex Other Agents Everything runs locally. Your conversations never leave your machine. The SQLite database, the embeddings, the search indexes — all on disk, all yours. -## Ingest Architecture - -Smriti ingest uses a layered pipeline: - -1. `parsers/*` extract agent transcripts into normalized messages (no DB writes). -2. `session-resolver` derives project/session state, including incremental offsets. -3. `store-gateway` persists messages, sidecars, session meta, and costs. -4. `ingest/index.ts` orchestrates the flow with per-session error isolation. - -This keeps parser logic, resolution logic, and persistence logic separated and testable. -See `INGEST_ARCHITECTURE.md` and `src/ingest/README.md` for implementation details. - -## Tagging & Categories - -Sessions and messages are automatically tagged into a hierarchical category -tree. Tags flow through every command — search, recall, list, and share — so you -can slice your team's knowledge by topic. - -### Default Category Tree - -Smriti ships with 7 top-level categories and 21 subcategories: - -| Category | Subcategories | -| -------------- | ----------------------------------------------------------------------- | -| `code` | `code/implementation`, `code/pattern`, `code/review`, `code/snippet` | -| `architecture` | `architecture/design`, `architecture/decision`, `architecture/tradeoff` | -| `bug` | `bug/report`, `bug/fix`, `bug/investigation` | -| `feature` | `feature/requirement`, `feature/design`, `feature/implementation` | -| `project` | `project/setup`, `project/config`, `project/dependency` | -| `decision` | `decision/technical`, `decision/process`, `decision/tooling` | -| `topic` | `topic/learning`, `topic/explanation`, `topic/comparison` | - -### Auto-Classification - -Smriti uses a two-stage pipeline to classify messages: - -1. **Rule-based** — 24 keyword patterns with weighted confidence scoring. Each - pattern targets a specific subcategory (e.g., words like "crash", - "stacktrace", "panic" map to `bug/report`). Confidence is calculated from - keyword density and rule weight. -2. **LLM fallback** — When rule confidence falls below the threshold (default - `0.5`, configurable via `SMRITI_CLASSIFY_THRESHOLD`), Ollama classifies the - message. Only activated when you pass `--llm`. - -The most frequent category across a session's messages becomes the session-level -tag. - -```bash -# Auto-categorize all uncategorized sessions (rule-based) -smriti categorize - -# Include LLM fallback for ambiguous sessions -smriti categorize --llm - -# Categorize a specific session -smriti categorize --session -``` - -### Manual Tagging - -Override or supplement auto-classification with manual tags: - -```bash -smriti tag - -# Examples -smriti tag abc123 decision/technical -smriti tag abc123 bug/fix -``` - -Manual tags are stored with confidence `1.0` and source `"manual"`. - -### Custom Categories - -Add your own categories to extend the default tree: - -```bash -# List the full category tree -smriti categories - -# Add a top-level category -smriti categories add ops --name "Operations" - -# Add a nested category under an existing parent -smriti categories add ops/incident --name "Incident Response" --parent ops - -# Include a description -smriti categories add ops/runbook --name "Runbooks" --parent ops --description "Operational runbook sessions" -``` - -### How Tags Filter Commands - -The `--category` flag works across search, recall, list, and share: - -| Command | Effect of `--category` | -| --------------- | ------------------------------------------------------------------------------- | -| `smriti list` | Shows categories column; filters sessions to matching category | -| `smriti search` | Filters full-text search results to matching category | -| `smriti recall` | Filters recall context; works with `--synthesize` | -| `smriti share` | Controls which sessions are exported; files organized into `.smriti/knowledge/` | -| `smriti status` | Shows session count per category (no filter flag — always shows all) | - -**Hierarchical filtering** — Filtering by a parent category automatically -includes all its children. `--category decision` matches `decision/technical`, -`decision/process`, and `decision/tooling`. - -### Categories in Share & Sync - -**Categories survive the share/sync roundtrip exactly.** What gets serialized -during `smriti share` is exactly what gets deserialized during `smriti sync` — -the same category ID goes in, the same category ID comes out. No -reclassification, no transformation, no loss. The category a session was tagged -with on one machine is the category it will be indexed under on every other -machine that syncs it. - -When you share sessions, the category is embedded in YAML frontmatter inside -each exported markdown file: - -```yaml ---- -id: 2e5f420a-e376-4ad4-8b35-ad94838cbc42 -category: project -project: smriti -agent: claude-code -author: zero8 -shared_at: 2026-02-10T11:29:44.501Z -tags: ["project", "project/dependency"] --- -``` - -When a teammate runs `smriti sync`, the frontmatter is parsed and the category -is restored into their local `smriti_session_tags` table — indexed as `project`, -searchable as `project`, filterable as `project`. The serialization and -deserialization are symmetric: `share` writes `category: project` → `sync` reads -`category: project` → `tagSession(db, sessionId, "project", 1.0, "team")`. No -intermediate step reinterprets the value. - -Files are organized into subdirectories by primary category (e.g., -`.smriti/knowledge/project/`, `.smriti/knowledge/decision/`), but sync reads the -category from frontmatter, not the directory path. - -> **Note:** Currently only the primary `category` field is restored on sync. -> Secondary tags in the `tags` array are serialized in the frontmatter but not -> yet imported. If a session had multiple tags (e.g., `project` + -> `decision/tooling`), only the primary tag survives the roundtrip. - -```bash -# Share decisions — category metadata travels with the files -smriti share --project myapp --category decision - -# Teammate syncs — categories restored exactly from frontmatter -smriti sync --project myapp -``` - -### Examples - -```bash -# All architectural decisions -smriti search "database" --category architecture - -# Recall only bug-related context -smriti recall "connection timeout" --category bug --synthesize - -# List feature sessions for a specific project -smriti list --category feature --project myapp - -# Share only decision sessions -smriti share --project myapp --category decision -``` - -## Context: Token Reduction (North Star) - -Every new Claude Code session starts from zero — no awareness of what happened -yesterday, which files were touched, what decisions were made. `smriti context` -generates a compact project summary (~200-300 tokens) and injects it into -`.smriti/CLAUDE.md`, which Claude Code auto-discovers. - -```bash -smriti context # auto-detect project, write .smriti/CLAUDE.md -smriti context --dry-run # preview without writing -smriti context --project myapp # explicit project -smriti context --days 14 # 14-day lookback (default: 7) -``` - -The output looks like this: - -```markdown -## Project Context - -> Auto-generated by `smriti context` on 2026-02-11. Do not edit manually. - -### Recent Sessions (last 7 days) - -- **2h ago** Enriched ingestion pipeline (12 turns) [code] -- **1d ago** Search & recall pipeline (8 turns) [feature] - -### Hot Files - -`src/db.ts` (14 ops), `src/ingest/claude.ts` (11 ops), `src/search/index.ts` (8 -ops) - -### Git Activity - -- commit `main`: "Fix auth token refresh" (2026-02-10) - -### Usage - -5 sessions, 48 turns, ~125K input / ~35K output tokens -``` - -No Ollama, no network calls, no model loading. Pure SQL queries against sidecar -tables, rendered as markdown. Runs in < 100ms. - -### Measuring the Impact - -Does this actually save tokens? Honestly — we don't know yet. We built the tools -to measure it, ran A/B tests, and the results so far are... humbling. Claude is -annoyingly good at finding the right files even without help. - -But this is the north star, not the destination. We believe context injection -will matter most on large codebases without detailed docs, ambiguous tasks that -require exploration, and multi-session continuity. We just need the data to -prove it (or disprove it and try something else). - -So we're shipping the measurement tools and asking you to help. Run A/B tests on -your projects, paste the results in -[Issue #13](https://github.com/zero8dotdev/smriti/issues/13), and let's figure -this out together. - -#### A/B Testing Guide - -```bash -# Step 1: Baseline session (no context) -mv .smriti/CLAUDE.md .smriti/CLAUDE.md.bak -# Start a Claude Code session, give it a task, let it finish, exit - -# Step 2: Context session -mv .smriti/CLAUDE.md.bak .smriti/CLAUDE.md -smriti context -# Start a new session, give the EXACT same task, let it finish, exit - -# Step 3: Ingest and compare -smriti ingest claude -smriti compare --last --project myapp -``` - -#### Compare Command - -```bash -smriti compare # by session ID (supports partial IDs) -smriti compare --last # last 2 sessions for current project -smriti compare --last --project myapp # last 2 sessions for specific project -smriti compare --last --json # machine-readable output -``` - -Output: - -``` -Session A: Fix auth bug (no context) -Session B: Fix auth bug (with context) - -Metric A B Diff ----------------------------------------------------------------- -Turns 12 8 -4 (-33%) -Total tokens 45K 32K -13000 (-29%) -Tool calls 18 11 -7 (-39%) -File reads 10 4 -6 (-60%) - -Tool breakdown: - Bash 4 3 - Glob 3 0 - Read 10 4 - Write 1 4 -``` - -#### What We've Tested So Far - -| Task Type | Context Impact | Notes | -| ----------------------------------------- | -------------- | ---------------------------------------------------------------------- | -| Knowledge questions ("how does X work?") | Minimal | Both sessions found the right files immediately from project CLAUDE.md | -| Implementation tasks ("add --since flag") | Minimal | Small, well-scoped tasks don't need exploration | -| Ambiguous/exploration tasks | Untested | Expected sweet spot — hot files guide Claude to the right area | -| Large codebases (no project CLAUDE.md) | Untested | Expected sweet spot — context replaces missing documentation | - -**We need your help.** If you run A/B tests on your projects, please share your -results in [GitHub Issues](https://github.com/zero8dotdev/smriti/issues). -Include the `smriti compare` output and a description of the task. This data -will help us understand where context injection actually matters. - -### Token Savings (Search & Recall) - -Separate from context injection, Smriti's search and recall pipeline compresses -past conversations: - -| Scenario | Raw Conversations | Via Smriti | Reduction | -| ----------------------------------- | ----------------- | ----------- | --------- | -| Relevant context from past sessions | ~20,000 tokens | ~500 tokens | **40x** | -| Multi-session recall + synthesis | ~10,000 tokens | ~200 tokens | **50x** | -| Full project conversation history | 50,000+ tokens | ~500 tokens | **100x** | - -Lower token spend, faster responses, more room for the actual work in your -context window. ## Privacy @@ -534,68 +274,87 @@ Smriti is local-first by design. No cloud, no telemetry, no accounts. - Synthesis via local [Ollama](https://ollama.ai) (optional) - Team sharing happens through git — you control what gets committed +--- + ## FAQ -**When does knowledge get captured?** Automatically. Smriti hooks into your AI -coding tool (Claude Code, Cursor, etc.) and captures every session without any -manual step. You just code normally and `smriti ingest` pulls in the -conversations. +**When does knowledge get captured?** Automatically. Smriti hooks into Claude +Code and captures every session without any manual step. For other agents, run +`smriti ingest all` to pull in conversations on demand. **Who has access to my data?** Only you. Everything lives in a local SQLite -database (`~/.cache/qmd/index.sqlite`). There's no cloud, no accounts, no -telemetry. Team sharing is explicit — you run `smriti share` to export, commit -the `.smriti/` folder to git, and teammates run `smriti sync` to import. +database. There's no cloud, no accounts, no telemetry. Team sharing is +explicit — you run `smriti share`, commit the `.smriti/` folder, and teammates +run `smriti sync`. **Can AI agents query the knowledge base?** Yes. `smriti recall "query"` returns -relevant past context that agents can use. When you run `smriti share`, it -generates a `.smriti/CLAUDE.md` index so Claude Code automatically discovers -shared knowledge. Agents can search, grep, and recall from the full knowledge -base. +relevant past context. `smriti share` generates a `.smriti/CLAUDE.md` so Claude +Code automatically discovers shared knowledge at the start of every session. **How do multiple projects stay separate?** Each project gets its own `.smriti/` -folder in its repo root. Sessions are tagged with project IDs in the central -database. Search works cross-project by default, but you can scope to a single -project with `--project `. Knowledge shared via git stays within that -project's repo. +folder. Sessions are tagged with project IDs in the central database. Search +works cross-project by default, scoped with `--project `. **Does this work with Jira or other issue trackers?** Not yet — Smriti is -git-native today. Issue tracker integrations are on the roadmap. If you have -ideas, open a discussion in -[GitHub Issues](https://github.com/zero8dotdev/smriti/issues). +git-native today. Issue tracker integrations are on the roadmap. -**How does this help preserve existing features during changes?** The reasoning -behind each code change is captured and searchable. When an AI agent starts a -new session, it can recall _why_ something was built a certain way — reducing -the chance of accidentally breaking existing behavior. +**Further reading:** See [docs/cli.md](./docs/cli.md) for the full command +reference, [INGEST_ARCHITECTURE.md](./INGEST_ARCHITECTURE.md) for the ingestion +pipeline, and [CLAUDE.md](./CLAUDE.md) for the database schema and +architecture. -## Uninstall +--- -```bash -curl -fsSL https://raw.githubusercontent.com/zero8dotdev/smriti/main/uninstall.sh | bash -``` +## About -To also remove hook state, prepend `SMRITI_PURGE=1` to the command. +I've been coding with AI agents for about 8 months. At some point the +frustration became impossible to ignore — every new session, you start from +zero. Explaining the same context, the same decisions, the same constraints. +That's not a great developer experience. + +I started small: custom prompts to export Claude sessions. That grew old fast. I +needed categorization. Found QMD. Started building on top of it. Dogfooded it. +Hit walls. Solved one piece at a time. + +At some point it worked well enough that I shared it with some friends. Some +used it, some ignored it — fair, the AI tooling space is noisy. But I kept +exploring, and found others building toward the same problem: Claude-mem, Letta, +a growing community of people who believe memory is the next foundational layer +for AI. -## Documentation +That's what Smriti is, really. An exploration. The developer tool is one layer. +But the deeper question is: what does memory for autonomous agents actually need +to look like? The answer probably mirrors how our own brain works — **Ingest → +Categorize → Recall → Search**. We're figuring that out, one piece at a time. -See [CLAUDE.md](./CLAUDE.md) for the full reference — API docs, database schema, -architecture details, and troubleshooting. +I come from the developer tooling space. Bad tooling bothers me. There's always +a better way. This is that project. + +--- ## Special Thanks Smriti is built on top of [QMD](https://github.com/tobi/qmd) — a beautifully designed local search engine for markdown files created by -[Tobi Lütke](https://github.com/tobi), CEO of Shopify. +[Tobi Lütke](https://github.com/tobi), CEO of Shopify. QMD gave us fast, +local-first SQLite with full-text search, vector embeddings, and +content-addressable hashing — all on your machine, zero cloud dependencies. +Instead of rebuilding that infrastructure from scratch, we focused entirely on +the memory layer, multi-agent ingestion, and team sharing. -QMD gave us the foundation we needed: a fast, local-first SQLite store with -full-text search, vector embeddings, and content-addressable hashing — all -running on your machine with zero cloud dependencies. Instead of rebuilding that -infrastructure from scratch, we were able to focus entirely on the memory layer, -multi-agent ingestion, and team sharing that makes Smriti useful. +Thank you, Tobi, for open-sourcing it. + +--- -Thank you, Tobi, for open-sourcing QMD. It's a reminder that the best tools are -often the ones that quietly do the hard work so others can build something new -on top. +## Uninstall + +```bash +curl -fsSL https://raw.githubusercontent.com/zero8dotdev/smriti/main/uninstall.sh | bash +``` + +To also remove hook state, prepend `SMRITI_PURGE=1` to the command. + +--- ## License diff --git a/docs/architecture.md b/docs/architecture.md index 5ec1d33..1740953 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,149 +1,139 @@ # Architecture -## Overview +Smriti's architecture follows the same pattern as memory in your brain: +**Ingest → Categorize → Recall → Search**. -``` - Claude Code Cursor Codex Other Agents - | | | | - v v v v - ┌──────────────────────────────────────────┐ - │ Smriti Ingestion Layer │ - │ │ - │ src/ingest/claude.ts (JSONL parser) │ - │ src/ingest/codex.ts (JSONL parser) │ - │ src/ingest/cursor.ts (JSON parser) │ - │ src/ingest/generic.ts (file import) │ - └──────────────────┬───────────────────────┘ - │ - v - ┌──────────────────────────────────────────┐ - │ QMD Core (via src/qmd.ts) │ - │ │ - │ addMessage() content-addressed │ - │ searchMemoryFTS() BM25 full-text │ - │ searchMemoryVec() vector similarity │ - │ recallMemories() dedup + synthesis │ - └──────────────────┬───────────────────────┘ - │ - v - ┌──────────────────────────────────────────┐ - │ SQLite Database │ - │ ~/.cache/qmd/index.sqlite │ - │ │ - │ QMD tables: │ - │ memory_sessions memory_messages │ - │ memory_fts content_vectors │ - │ │ - │ Smriti tables: │ - │ smriti_session_meta (agent, project) │ - │ smriti_projects (registry) │ - │ smriti_categories (taxonomy) │ - │ smriti_session_tags (categorization) │ - │ smriti_message_tags (categorization) │ - │ smriti_shares (team dedup) │ - └──────────────────────────────────────────┘ -``` +Every layer has one job. Parsers extract conversations. The resolver maps +them to projects. The store persists them. Search retrieves them. Nothing +crosses those boundaries. -## QMD Integration +--- -Smriti builds on top of [QMD](https://github.com/tobi/qmd), a local-first search engine. QMD provides: - -- **Content-addressable storage** — Messages are SHA256-hashed, no duplicates -- **FTS5 full-text search** — BM25 ranking with Porter stemming -- **Vector embeddings** — 384-dim vectors via embeddinggemma (node-llama-cpp) -- **Reciprocal Rank Fusion** — Combines FTS and vector results +## System Overview -All QMD imports go through a single re-export hub at `src/qmd.ts`: - -```ts -// Every file imports from here, never from qmd directly -import { addMessage, searchMemoryFTS, recallMemories } from "./qmd"; -import { hashContent } from "./qmd"; -import { ollamaRecall } from "./qmd"; +``` +Claude Code Cursor Codex Cline Copilot + | | | | | + v v v v v +┌──────────────────────────────────────────────┐ +│ Smriti Ingestion Layer │ +│ │ +│ parsers/claude.ts (JSONL) │ +│ parsers/codex.ts (JSONL) │ +│ parsers/cursor.ts (JSON) │ +│ parsers/cline.ts (task files) │ +│ parsers/copilot.ts (VS Code storage) │ +│ parsers/generic.ts (file import) │ +│ │ +│ session-resolver.ts (project detection) │ +│ store-gateway.ts (persistence) │ +└──────────────────┬───────────────────────────┘ + │ + v +┌──────────────────────────────────────────────┐ +│ QMD Core (via src/qmd.ts) │ +│ │ +│ addMessage() content-addressed │ +│ searchMemoryFTS() BM25 full-text │ +│ searchMemoryVec() vector similarity │ +│ recallMemories() dedup + synthesis │ +└──────────────────┬───────────────────────────┘ + │ + v +┌──────────────────────────────────────────────┐ +│ SQLite (~/.cache/qmd/index.sqlite) │ +│ │ +│ QMD tables: │ +│ memory_sessions memory_messages │ +│ memory_fts (BM25) content_vectors │ +│ │ +│ Smriti tables: │ +│ smriti_session_meta (agent, project) │ +│ smriti_projects (registry) │ +│ smriti_categories (taxonomy) │ +│ smriti_session_tags (categorization) │ +│ smriti_message_tags (categorization) │ +│ smriti_shares (team dedup) │ +└──────────────────────────────────────────────┘ ``` -This creates a clean boundary — if QMD's API changes, only `src/qmd.ts` needs updating. +Everything runs locally. Nothing leaves your machine. -## Ingestion Pipeline +--- -Each agent has a dedicated parser. The flow: +## Built on QMD -1. **Discover** — Glob for session files in agent-specific log directories -2. **Deduplicate** — Check `smriti_session_meta` for already-ingested session IDs -3. **Parse** — Agent-specific parsing into a common `ParsedMessage[]` format -4. **Store** — Save via QMD's `addMessage()` (content-addressed, SHA256 hashed) -5. **Annotate** — Attach Smriti metadata (agent ID, project ID) to `smriti_session_meta` +Smriti builds on [QMD](https://github.com/tobi/qmd) — a local-first search +engine for markdown files by Tobi Lütke. QMD handles the hard parts: -### Project Detection (Claude Code) +- **Content-addressable storage** — messages are SHA256-hashed, no duplicates +- **FTS5 full-text search** — BM25 ranking with Porter stemming +- **Vector embeddings** — 384-dim via EmbeddingGemma (node-llama-cpp), + computed entirely on-device +- **Reciprocal Rank Fusion** — combines FTS and vector results -Claude Code stores sessions in `~/.claude/projects//`. The directory name encodes the filesystem path with `-` replacing `/`: +All QMD imports go through a single re-export hub at `src/qmd.ts`. No file +in the codebase imports from QMD directly — only through this hub. If QMD's +API changes, one file needs updating. +```ts +import { addMessage, searchMemoryFTS, recallMemories } from "./qmd"; +import { hashContent, ollamaRecall } from "./qmd"; ``` --Users-zero8-zero8.dev-openfga → /Users/zero8/zero8.dev/openfga -``` - -Since folder names can also contain dashes, `deriveProjectPath()` uses greedy `existsSync()` matching: it tries candidate paths from left to right, picking the longest existing directory at each step. -`deriveProjectId()` then strips the configured `PROJECTS_ROOT` (default `~/zero8.dev`) to produce a clean project name like `openfga` or `avkash/regulation-hub`. +--- -## Search Architecture - -### Filtered Search +## Ingestion Pipeline -`searchFiltered()` in `src/search/index.ts` extends QMD's FTS5 search with JOINs to Smriti's metadata tables: +Ingestion is a four-stage pipeline with clean separation between stages: -```sql -FROM memory_fts mf -JOIN memory_messages mm ON mm.rowid = mf.rowid -JOIN memory_sessions ms ON ms.id = mm.session_id -LEFT JOIN smriti_session_meta sm ON sm.session_id = mm.session_id -WHERE mf.content MATCH ? - AND sm.project_id = ? -- project filter - AND sm.agent_id = ? -- agent filter - AND EXISTS (...) -- category filter via smriti_message_tags -``` +1. **Parse** — agent-specific parsers extract conversations into a normalized + `ParsedMessage[]` format. No DB writes, no side effects. Pure functions. +2. **Resolve** — `session-resolver.ts` maps sessions to projects, handles + incremental ingestion (picks up where it left off), derives clean project + IDs from agent-specific path formats. +3. **Store** — `store-gateway.ts` persists messages, session metadata, + sidecars, and cost data. All writes go through here. +4. **Orchestrate** — `ingest/index.ts` drives the flow with per-session error + isolation. One broken session doesn't stop the rest. -### Recall +### Project Detection -`recall()` in `src/search/recall.ts` wraps search with: +Claude Code encodes project paths into directory names like +`-Users-zero8-zero8.dev-openfga` (slashes become dashes). Since folder +names can also contain real dashes, `deriveProjectPath()` uses greedy +`existsSync()` matching — trying candidate paths left to right, picking the +longest valid directory at each step. -1. **Session deduplication** — Keep only the best-scoring result per session -2. **Optional synthesis** — Sends results to Ollama's `ollamaRecall()` for a coherent summary +`deriveProjectId()` then strips `SMRITI_PROJECTS_ROOT` to produce a clean +name: `openfga`, `avkash/regulation-hub`. -When no filters are specified, it delegates directly to QMD's native `recallMemories()`. +--- -## Team Sharing +## Search -### Export (`smriti share`) +Smriti adds a metadata filter layer on top of QMD's native search: -Sessions are exported as markdown files with YAML frontmatter: - -``` -.smriti/ -├── config.json -├── index.json # Manifest of all shared files -└── knowledge/ - ├── decision/ - │ └── 2026-02-10_auth-migration-approach.md - └── bug/ - └── 2026-02-09_connection-pool-fix.md -``` +**`smriti search`** — FTS5 full-text with JOINs to Smriti's metadata tables. +Filters by project, agent, and category without touching the vector index. +Fast, synchronous, no model loading. -Each file contains: -- YAML frontmatter (session ID, category, project, agent, author, tags) -- Session title as heading -- Summary (if available) -- Full conversation in `**role**: content` format +**`smriti recall`** — Two paths depending on whether filters are applied: -Content hashes prevent re-exporting the same content. +- *No filters* → delegates to QMD's native `recallMemories()`: FTS + vector + + Reciprocal Rank Fusion + session dedup. Full hybrid pipeline. +- *With filters* → filtered FTS search + session dedup. Vector search is + currently bypassed when filters are active. (This is a known gap — see + [search.md](./search.md) for details.) -### Import (`smriti sync`) +**`smriti embed`** — builds vector embeddings for all unembedded messages. +Required before vector search works. Runs locally via node-llama-cpp. -Reads markdown files from `.smriti/knowledge/`, parses frontmatter and conversation, and imports via `addMessage()`. Content hashing prevents duplicate imports. +--- ## Database Schema -### QMD Tables (not modified by Smriti) +### QMD Tables | Table | Purpose | |-------|---------| @@ -156,10 +146,26 @@ Reads markdown files from `.smriti/knowledge/`, parses frontmatter and conversat | Table | Purpose | |-------|---------| -| `smriti_agents` | Agent registry (claude-code, codex, cursor) | +| `smriti_agents` | Agent registry (claude-code, codex, cursor...) | | `smriti_projects` | Project registry (id, filesystem path) | | `smriti_session_meta` | Maps sessions to agents and projects | | `smriti_categories` | Hierarchical category taxonomy | -| `smriti_session_tags` | Category tags on sessions (with confidence) | -| `smriti_message_tags` | Category tags on messages (with confidence) | +| `smriti_session_tags` | Category tags on sessions (with confidence score) | +| `smriti_message_tags` | Category tags on messages (with confidence score) | | `smriti_shares` | Deduplication tracking for team sharing | + +--- + +## Team Sharing + +Export (`smriti share`) converts sessions to markdown with YAML frontmatter +and writes them to `.smriti/knowledge/`, organized by category. The YAML +carries session ID, category, project, agent, author, and tags — enough to +reconstruct the full metadata on import. + +Import (`smriti sync`) parses frontmatter, restores categories, and inserts +via `addMessage()`. Content hashing prevents duplicate imports. The +roundtrip is symmetric: what gets written during share is exactly what gets +read during sync. + +See [team-sharing.md](./team-sharing.md) for the workflow. diff --git a/docs/cli.md b/docs/cli.md index 25b5071..13f1116 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1,40 +1,82 @@ -# CLI Reference +# Smriti CLI Reference + +Everything you can do with `smriti`. For the big picture, see the +[README](../README.md). + +--- + +## Global Flags + +```bash +smriti --version # Print version +smriti --help # Print command overview +smriti help # Same as --help +``` + +--- + +## Global Filters + +These flags work across `search`, `recall`, `list`, and `share`: + +| Flag | Description | +|------|-------------| +| `--category ` | Filter by category (e.g. `decision`, `bug/fix`) | +| `--project ` | Filter by project ID | +| `--agent ` | Filter by agent (`claude-code`, `codex`, `cursor`, `cline`, `copilot`) | +| `--limit ` | Max results returned | +| `--json` | Machine-readable JSON output | + +Hierarchical category filtering: `--category decision` matches `decision`, +`decision/technical`, `decision/process`, and `decision/tooling`. + +--- ## Ingestion ### `smriti ingest ` -Import conversations from an AI agent into Smriti's memory. +Pull conversations from an AI agent into Smriti's memory. -| Agent | Source | Format | -|-------|--------|--------| -| `claude` / `claude-code` | `~/.claude/projects/*/*.jsonl` | JSONL | -| `codex` | `~/.codex/**/*.jsonl` | JSONL | -| `cursor` | `.cursor/**/*.json` (requires `--project-path`) | JSON | -| `file` / `generic` | Any file path | Chat or JSONL | -| `all` | All known agents at once | — | +| Agent | Source | +|-------|--------| +| `claude` / `claude-code` | `~/.claude/projects/*/*.jsonl` | +| `codex` | `~/.codex/**/*.jsonl` | +| `cline` | `~/.cline/tasks/**` | +| `copilot` | VS Code `workspaceStorage` (auto-detected per OS) | +| `cursor` | `.cursor/**/*.json` (requires `--project-path`) | +| `file` / `generic` | Any file path | +| `all` | All known agents at once | ```bash smriti ingest claude smriti ingest codex +smriti ingest cline +smriti ingest copilot smriti ingest cursor --project-path /path/to/project smriti ingest file ~/transcript.txt --title "Planning Session" --format chat smriti ingest all ``` **Options:** -- `--project-path ` — Project directory (required for Cursor) -- `--file ` — File path (for generic ingest) -- `--format ` — File format (default: `chat`) -- `--title ` — Session title -- `--session ` — Custom session ID -- `--project ` — Assign to a project -## Search +| Flag | Description | +|------|-------------| +| `--project-path ` | Project directory (required for Cursor) | +| `--file ` | File path (alternative to positional arg for generic ingest) | +| `--format ` | File format (default: `chat`) | +| `--title ` | Session title override | +| `--session ` | Custom session ID | +| `--project ` | Assign ingested sessions to a specific project | + +--- + +## Search & Recall ### `smriti search ` -Hybrid search across all memory using BM25 full-text and vector similarity. +Hybrid full-text + vector search across all memory. Returns ranked results +with session and message context. ```bash smriti search "rate limiting" @@ -43,51 +85,59 @@ smriti search "deployment" --category decision --limit 10 smriti search "API design" --json ``` -**Options:** -- `--category ` — Filter by category -- `--project ` — Filter by project -- `--agent ` — Filter by agent (`claude-code`, `codex`, `cursor`) -- `--limit ` — Max results (default: 20) -- `--json` — JSON output +**Options:** All global filters apply. + +--- ### `smriti recall ` -Smart recall: searches, deduplicates by session, and optionally synthesizes results into a coherent summary. +Like search, but deduplicates results by session and optionally synthesizes +them into a single coherent summary via Ollama. ```bash smriti recall "how did we handle caching" smriti recall "database setup" --synthesize smriti recall "auth flow" --synthesize --model qwen3:0.5b --max-tokens 200 -smriti recall "deployment" --project api --json +smriti recall "deployment" --category decision --project api --json ``` **Options:** -- `--synthesize` — Synthesize results into one summary via Ollama -- `--model ` — Ollama model for synthesis (default: `qwen3:8b-tuned`) -- `--max-tokens ` — Max synthesis output tokens -- All filter options from `search` + +| Flag | Description | +|------|-------------| +| `--synthesize` | Synthesize results into one summary via Ollama (requires Ollama running) | +| `--model ` | Ollama model to use (default: `qwen3:8b-tuned`) | +| `--max-tokens ` | Max tokens for synthesized output | +| All global filters | `--category`, `--project`, `--agent`, `--limit`, `--json` | + +--- ## Sessions ### `smriti list` -List recent sessions with optional filtering. +List recent sessions with filtering. ```bash smriti list smriti list --project myapp --agent claude-code smriti list --category decision --limit 20 -smriti list --all --json +smriti list --all +smriti list --json ``` **Options:** -- `--all` — Include inactive sessions -- `--json` — JSON output -- All filter options from `search` + +| Flag | Description | +|------|-------------| +| `--all` | Include inactive/archived sessions | +| All global filters | `--category`, `--project`, `--agent`, `--limit`, `--json` | + +--- ### `smriti show ` -Display all messages in a session. +Display all messages in a session. Supports partial session IDs. ```bash smriti show abc12345 @@ -95,15 +145,27 @@ smriti show abc12345 --limit 10 smriti show abc12345 --json ``` +**Options:** + +| Flag | Description | +|------|-------------| +| `--limit ` | Max messages to display | +| `--json` | JSON output | + +--- + ### `smriti status` -Memory statistics: session counts, message counts, agent breakdowns, project breakdowns, category distribution. +Memory statistics: total sessions, messages, agent breakdown, project +breakdown, category distribution. ```bash smriti status smriti status --json ``` +--- + ### `smriti projects` List all registered projects. @@ -113,11 +175,28 @@ smriti projects smriti projects --json ``` +--- + +## Embeddings + +### `smriti embed` + +Build vector embeddings for all unembedded messages. Required for semantic +(vector) search to work. Runs locally via `node-llama-cpp` — no network +calls. + +```bash +smriti embed +``` + +--- + ## Categorization ### `smriti categorize` -Auto-categorize uncategorized sessions using rule-based matching and optional LLM classification. +Auto-categorize uncategorized sessions using rule-based keyword matching with +an optional LLM fallback for ambiguous cases. ```bash smriti categorize @@ -126,60 +205,159 @@ smriti categorize --llm ``` **Options:** -- `--session ` — Categorize a specific session only -- `--llm` — Use Ollama LLM for ambiguous classifications + +| Flag | Description | +|------|-------------| +| `--session ` | Categorize a specific session only | +| `--llm` | Enable Ollama LLM fallback for low-confidence classifications | + +--- ### `smriti tag ` -Manually tag a session with a category. +Manually assign a category to a session. Stored with confidence `1.0` and +source `"manual"`. ```bash smriti tag abc12345 decision/technical smriti tag abc12345 bug/fix ``` +--- + ### `smriti categories` -Show the category tree. +Display the full category tree. ```bash smriti categories ``` +**Default categories:** + +| Category | Subcategories | +|----------|---------------| +| `code` | `code/implementation`, `code/pattern`, `code/review`, `code/snippet` | +| `architecture` | `architecture/design`, `architecture/decision`, `architecture/tradeoff` | +| `bug` | `bug/report`, `bug/fix`, `bug/investigation` | +| `feature` | `feature/requirement`, `feature/design`, `feature/implementation` | +| `project` | `project/setup`, `project/config`, `project/dependency` | +| `decision` | `decision/technical`, `decision/process`, `decision/tooling` | +| `topic` | `topic/learning`, `topic/explanation`, `topic/comparison` | + +--- + ### `smriti categories add ` -Add a custom category. +Add a custom category to the tree. ```bash -smriti categories add infra/monitoring --name "Monitoring" --parent infra --description "Monitoring and observability" +smriti categories add ops --name "Operations" +smriti categories add ops/incident --name "Incident Response" --parent ops +smriti categories add ops/runbook --name "Runbooks" --parent ops --description "Operational runbook sessions" ``` -## Embeddings +**Options:** -### `smriti embed` +| Flag | Description | +|------|-------------| +| `--name ` | Display name (required) | +| `--parent ` | Parent category ID | +| `--description ` | Optional description | -Build vector embeddings for all unembedded messages. Required for semantic search. +--- + +## Context & Compare + +### `smriti context` + +Generate a compact project summary (~200–300 tokens) and write it to +`.smriti/CLAUDE.md`. Claude Code auto-discovers this file at session start. + +Runs entirely from SQL — no Ollama, no network, no model loading. Typically +completes in under 100ms. ```bash -smriti embed +smriti context +smriti context --dry-run +smriti context --project myapp +smriti context --days 14 +smriti context --json ``` +**Options:** + +| Flag | Description | +|------|-------------| +| `--project ` | Project to generate context for (auto-detected from `cwd` if omitted) | +| `--days ` | Lookback window in days (default: `7`) | +| `--dry-run` | Print output to stdout without writing the file | +| `--json` | JSON output | + +--- + +### `smriti compare ` + +Compare two sessions across turns, tokens, tool calls, and file reads. Useful +for A/B testing context injection impact. + +```bash +smriti compare abc123 def456 +smriti compare --last +smriti compare --last --project myapp +smriti compare --last --json +``` + +**Options:** + +| Flag | Description | +|------|-------------| +| `--last` | Compare the two most recent sessions (for current project) | +| `--project ` | Project scope for `--last` | +| `--json` | JSON output | + +Partial session IDs are supported (first 7+ characters). + +--- + ## Team Sharing ### `smriti share` -Export sessions as markdown files to a `.smriti/` directory for git-based sharing. +Export sessions as clean markdown files to `.smriti/knowledge/` for +git-based team sharing. Generates LLM reflections via Ollama by default. +Also writes `.smriti/CLAUDE.md` so Claude Code auto-discovers shared +knowledge. ```bash smriti share --project myapp smriti share --category decision smriti share --session abc12345 smriti share --output /custom/path +smriti share --no-reflect +smriti share --reflect-model llama3.2 +smriti share --segmented --min-relevance 7 ``` +**Options:** + +| Flag | Description | +|------|-------------| +| `--project ` | Export sessions for a specific project | +| `--category ` | Export only sessions with this category | +| `--session ` | Export a single session | +| `--output ` | Custom output directory (default: `.smriti/`) | +| `--no-reflect` | Skip LLM reflections (reflections are on by default) | +| `--reflect-model ` | Ollama model for reflections | +| `--segmented` | Use 3-stage segmentation pipeline — beta | +| `--min-relevance ` | Relevance threshold for segmented mode (default: `6`) | + +--- + ### `smriti sync` -Import team knowledge from a `.smriti/` directory. +Import team knowledge from a `.smriti/knowledge/` directory into local +memory. Deduplicates by content hash — same content won't import twice. ```bash smriti sync @@ -187,10 +365,32 @@ smriti sync --project myapp smriti sync --input /custom/path ``` +**Options:** + +| Flag | Description | +|------|-------------| +| `--project ` | Scope sync to a specific project | +| `--input ` | Custom input directory (default: `.smriti/`) | + +--- + ### `smriti team` -View team contributions (authors, counts, categories). +View team contributions: authors, session counts, and category breakdown. ```bash smriti team ``` + +--- + +## Maintenance + +### `smriti upgrade` + +Pull the latest version from GitHub and reinstall dependencies. Equivalent to +re-running the install script. + +```bash +smriti upgrade +``` diff --git a/docs/configuration.md b/docs/configuration.md index 713e8c9..0941099 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,81 +1,113 @@ # Configuration -Smriti uses environment variables for configuration. Bun auto-loads `.env` files, so you can set these in a `.env.local` file in the smriti directory. +Smriti uses environment variables for configuration. Bun auto-loads `.env` +files, so you can put these in `~/.smriti/.env` and they'll be picked up +automatically — no need to set them in your shell profile. + +Most people never need to touch these. The defaults work. The ones you're +most likely to change are `SMRITI_PROJECTS_ROOT` (to match where your +projects actually live) and `QMD_MEMORY_MODEL` (if you want a lighter Ollama +model). + +--- ## Environment Variables | Variable | Default | Description | |----------|---------|-------------| -| `QMD_DB_PATH` | `~/.cache/qmd/index.sqlite` | Path to the shared SQLite database | -| `CLAUDE_LOGS_DIR` | `~/.claude/projects` | Claude Code session logs directory | -| `CODEX_LOGS_DIR` | `~/.codex` | Codex CLI session logs directory | -| `SMRITI_PROJECTS_ROOT` | `~/zero8.dev` | Root directory for project detection | +| `QMD_DB_PATH` | `~/.cache/qmd/index.sqlite` | SQLite database path | +| `CLAUDE_LOGS_DIR` | `~/.claude/projects` | Claude Code session logs | +| `CODEX_LOGS_DIR` | `~/.codex` | Codex CLI session logs | +| `CLINE_LOGS_DIR` | `~/.cline/tasks` | Cline CLI tasks | +| `COPILOT_STORAGE_DIR` | auto-detected per OS | VS Code workspaceStorage root | +| `SMRITI_PROJECTS_ROOT` | `~/zero8.dev` | Root for project ID derivation | | `OLLAMA_HOST` | `http://127.0.0.1:11434` | Ollama API endpoint | -| `QMD_MEMORY_MODEL` | `qwen3:8b-tuned` | Ollama model for synthesis/summarization | -| `SMRITI_CLASSIFY_THRESHOLD` | `0.5` | Confidence below which LLM classification triggers | +| `QMD_MEMORY_MODEL` | `qwen3:8b-tuned` | Ollama model for synthesis | +| `SMRITI_CLASSIFY_THRESHOLD` | `0.5` | LLM classification trigger threshold | | `SMRITI_AUTHOR` | `$USER` | Author name for team sharing | +| `SMRITI_DAEMON_DEBOUNCE_MS` | `30000` | File-stability wait before auto-ingest | + +--- ## Projects Root -The `SMRITI_PROJECTS_ROOT` variable controls how Smriti derives project IDs from Claude Code session paths. +`SMRITI_PROJECTS_ROOT` is the most commonly changed setting. It controls how +Smriti derives clean project IDs from Claude Code session paths. -Claude Code encodes project paths in directory names like `-Users-zero8-zero8.dev-openfga`. Smriti reconstructs the real path and strips the projects root prefix: +Claude Code encodes project paths into directory names like +`-Users-zero8-zero8.dev-openfga`. Smriti reconstructs the real filesystem +path and strips the projects root prefix to produce a readable ID: -| Claude Dir Name | Derived Project ID | -|----------------|-------------------| -| `-Users-zero8-zero8.dev-openfga` | `openfga` | -| `-Users-zero8-zero8.dev-avkash-regulation-hub` | `avkash/regulation-hub` | -| `-Users-zero8-zero8.dev` | `zero8.dev` | +| Claude dir name | Projects root | Derived ID | +|-----------------|---------------|------------| +| `-Users-zero8-zero8.dev-openfga` | `~/zero8.dev` | `openfga` | +| `-Users-zero8-zero8.dev-avkash-regulation-hub` | `~/zero8.dev` | `avkash/regulation-hub` | +| `-Users-alice-code-myapp` | `~/code` | `myapp` | -To change the projects root: +If your projects live under `~/code` instead of `~/zero8.dev`: ```bash -export SMRITI_PROJECTS_ROOT="$HOME/projects" +export SMRITI_PROJECTS_ROOT="$HOME/code" ``` +--- + ## Database Location -By default, Smriti shares QMD's database at `~/.cache/qmd/index.sqlite`. This means your QMD document search and Smriti memory search share the same vector index — no duplication. +By default, Smriti shares QMD's database at `~/.cache/qmd/index.sqlite`. +This means QMD document search and Smriti memory search share the same vector +index — one embedding store, no duplication. -To use a separate database: +To keep them separate: ```bash export QMD_DB_PATH="$HOME/.cache/smriti/memory.sqlite" ``` +--- + ## Ollama Setup -Ollama is optional. It's used for: -- `smriti recall --synthesize` — Synthesize recalled context into a summary -- `smriti categorize --llm` — LLM-assisted categorization +Ollama is optional. Everything core — ingestion, search, recall, sharing — +works without it. Ollama only powers the features that require a language +model: + +- `smriti recall --synthesize` — Compress recalled context into a summary +- `smriti share` — Generate session reflections (skip with `--no-reflect`) +- `smriti categorize --llm` — LLM fallback for ambiguous categorization -Install and start Ollama: +Install and start: ```bash -# Install (macOS) +# macOS brew install ollama - -# Start the server ollama serve # Pull the default model ollama pull qwen3:8b-tuned ``` -To use a different model: +The default model (`qwen3:8b-tuned`) is good but large (~4.7GB). For a +lighter option: ```bash -export QMD_MEMORY_MODEL="mistral:7b" +export QMD_MEMORY_MODEL="qwen3:0.5b" +ollama pull qwen3:0.5b ``` -## Claude Code Hook +To point at a remote Ollama instance: + +```bash +export OLLAMA_HOST="http://192.168.1.100:11434" +``` -The install script sets up an auto-save hook at `~/.claude/hooks/save-memory.sh`. This requires: +--- -- **jq** — for parsing the hook's JSON input -- **Claude Code** — must be installed with hooks support +## Claude Code Hook -The hook is configured in `~/.claude/settings.json`: +The install script creates `~/.claude/hooks/save-memory.sh` and registers it +in `~/.claude/settings.json`. This is what captures sessions automatically +when you end a Claude Code conversation. ```json { @@ -86,7 +118,7 @@ The hook is configured in `~/.claude/settings.json`: "hooks": [ { "type": "command", - "command": "/path/to/.claude/hooks/save-memory.sh", + "command": "/Users/you/.claude/hooks/save-memory.sh", "timeout": 30, "async": true } @@ -97,4 +129,8 @@ The hook is configured in `~/.claude/settings.json`: } ``` -To disable the hook, remove the entry from `settings.json` or set `SMRITI_NO_HOOK=1` during install. +**Requires `jq`** — the hook parses JSON input from Claude Code. Install with +`brew install jq` or `apt install jq`. + +To disable: remove the entry from `settings.json`. To skip hook setup during +install, set `SMRITI_NO_HOOK=1` before running the installer. diff --git a/docs/getting-started.md b/docs/getting-started.md index a4fd4bc..8d328d3 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,17 +1,30 @@ # Getting Started +You're about to give your AI agents memory. + +By the end of this guide, your Claude Code sessions will be automatically +saved, searchable, and shareable — across sessions, across days, across your +team. + +--- + ## Install +**macOS / Linux:** + ```bash curl -fsSL https://raw.githubusercontent.com/zero8dotdev/smriti/main/install.sh | bash ``` -The installer will: -1. Check for (and install) [Bun](https://bun.sh) -2. Clone Smriti to `~/.smriti` -3. Install dependencies -4. Create the `smriti` CLI at `~/.local/bin/smriti` -5. Set up the Claude Code auto-save hook +**Windows** (PowerShell): + +```powershell +irm https://raw.githubusercontent.com/zero8dotdev/smriti/main/install.ps1 | iex +``` + +The installer: checks for Bun (installs it if missing) → clones Smriti to +`~/.smriti` → creates the `smriti` CLI → sets up the Claude Code auto-save +hook. ### Verify @@ -22,63 +35,104 @@ smriti help If `smriti` is not found, add `~/.local/bin` to your PATH: ```bash -echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc +echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc ``` +--- + ## First Run -### 1. Ingest your Claude Code conversations +### 1. Pull in your Claude Code sessions ```bash smriti ingest claude ``` -This scans `~/.claude/projects/` for all session transcripts and imports them. +Scans `~/.claude/projects/` and imports every conversation. On first run this +might take a moment if you've been coding with Claude for a while. -### 2. Check what was imported +### 2. See what you have ```bash smriti status ``` -Output shows session count, message count, and per-agent/per-project breakdowns. +Session counts, message counts, breakdown by project and agent. This is your +memory — everything Smriti knows about your past work. -### 3. Search your memory +### 3. Search it ```bash smriti search "authentication" ``` +Keyword search across every session. Try something you remember working +through in a past conversation. + ### 4. Recall with context ```bash smriti recall "how did we set up the database" ``` -This searches, deduplicates by session, and returns the most relevant snippets. +Like search, but smarter — deduplicates by session and surfaces the most +relevant snippets. Add `--synthesize` to compress results into a single +coherent summary (requires Ollama). -### 5. Build embeddings for semantic search +### 5. Turn on semantic search ```bash smriti embed ``` -After embedding, searches find semantically similar content — not just keyword matches. +Builds vector embeddings locally. After this, searches find semantically +similar content — not just keyword matches. "auth flow" starts surfacing +results that talk about "login mechanism." -## Auto-Save (Claude Code) +--- -If the installer set up the hook, every Claude Code conversation is saved automatically. No action needed — just code as usual. +## Auto-Save -To verify the hook is active: +If the install completed cleanly, you're done — every Claude Code session is +saved automatically when you end it. No manual step, no copy-pasting. + +Verify the hook is active: ```bash cat ~/.claude/settings.json | grep save-memory ``` +If the hook isn't there, re-run the installer or set it up manually — see +[Configuration](./configuration.md#claude-code-hook). + +--- + +## Share with Your Team + +Once you've built up memory, share the useful parts through git: + +```bash +smriti share --project myapp --category decision +cd ~/projects/myapp +git add .smriti/ && git commit -m "Share auth migration decisions" +git push +``` + +A teammate imports it: + +```bash +git pull && smriti sync --project myapp +smriti recall "auth migration" --project myapp +``` + +Their agent now has your context. See [Team Sharing](./team-sharing.md) for +the full guide. + +--- + ## Next Steps -- [CLI Reference](./cli.md) — All commands and options -- [Team Sharing](./team-sharing.md) — Share knowledge via git -- [Configuration](./configuration.md) — Environment variables and customization +- [CLI Reference](./cli.md) — Every command and option +- [Team Sharing](./team-sharing.md) — Share knowledge through git +- [Configuration](./configuration.md) — Customize paths, models, and behavior - [Architecture](./architecture.md) — How Smriti works under the hood diff --git a/docs/CI_HARDENING_EXECUTION_PLAN.md b/docs/internal/ci-hardening.md similarity index 100% rename from docs/CI_HARDENING_EXECUTION_PLAN.md rename to docs/internal/ci-hardening.md diff --git a/docs/DESIGN.md b/docs/internal/design.md similarity index 100% rename from docs/DESIGN.md rename to docs/internal/design.md diff --git a/docs/e2e-dev-release-flow-test.md b/docs/internal/e2e-release-flow.md similarity index 100% rename from docs/e2e-dev-release-flow-test.md rename to docs/internal/e2e-release-flow.md diff --git a/docs/search-recall-architecture.md b/docs/internal/search-analysis.md similarity index 100% rename from docs/search-recall-architecture.md rename to docs/internal/search-analysis.md diff --git a/docs/website.md b/docs/internal/website.md similarity index 100% rename from docs/website.md rename to docs/internal/website.md diff --git a/docs/WORKFLOW_AUTOMATION.md b/docs/internal/workflow-automation.md similarity index 100% rename from docs/WORKFLOW_AUTOMATION.md rename to docs/internal/workflow-automation.md diff --git a/docs/search.md b/docs/search.md new file mode 100644 index 0000000..d09204c --- /dev/null +++ b/docs/search.md @@ -0,0 +1,134 @@ +# Search & Recall + +Smriti has two ways to retrieve memory: `search` and `recall`. They use +different retrieval strategies and are optimized for different situations. + +--- + +## search vs recall + +| | `smriti search` | `smriti recall` | +|--|-----------------|-----------------| +| **Retrieval** | Full-text (BM25) | Full-text + vector (hybrid) | +| **Deduplication** | None — all matching messages | One best result per session | +| **Synthesis** | No | Yes, with `--synthesize` | +| **Best for** | Finding specific text, scanning results | Getting context before starting work | + +Use **search** when you know roughly what you're looking for and want to scan +results. Use **recall** when you want the most relevant context from your +history, deduplicated and optionally compressed. + +--- + +## How Search Works + +`smriti search` runs a BM25 full-text query against every ingested message. +It's fast, synchronous, and returns ranked results immediately — no model +loading. + +```bash +smriti search "rate limiting" +smriti search "auth" --project myapp --agent claude-code +smriti search "deployment" --category decision --limit 10 +``` + +Filters (`--project`, `--category`, `--agent`) narrow results with SQL JOINs +against Smriti's metadata tables. They compose — all filters apply together. + +--- + +## How Recall Works + +`smriti recall` goes further. It runs full-text search, deduplicates results +so you get at most one snippet per session (the highest-scoring one), and +optionally synthesizes everything into a single coherent summary. + +```bash +smriti recall "how did we handle rate limiting" +smriti recall "database setup" --synthesize +smriti recall "auth flow" --synthesize --model qwen3:0.5b +``` + +**Without filters:** recall uses QMD's full hybrid pipeline — BM25 + +vector embeddings + Reciprocal Rank Fusion. Semantic matches work here: "auth +flow" can surface results that talk about "login mechanism." + +**With filters:** recall currently uses full-text search only. The hybrid +pipeline is bypassed when `--project`, `--category`, or `--agent` is applied. +This is a known limitation — filtered recall loses semantic matching. It's +on the roadmap to fix. + +--- + +## Synthesis + +`--synthesize` sends the recalled context to Ollama and asks it to produce a +single coherent summary. This is the difference between getting 10 raw +snippets and getting a paragraph that distills what matters. + +```bash +smriti recall "connection pooling decisions" --synthesize +``` + +Requires Ollama running locally. See [Configuration](./configuration.md#ollama-setup) +for setup. Use `--model` to pick a lighter model if the default is too slow. + +--- + +## Vector Search + +Vector search finds semantically similar content — results that mean the same +thing even if they don't share the same words. It requires embeddings to be +built first: + +```bash +smriti embed +``` + +This runs locally via node-llama-cpp and EmbeddingGemma. It can take a few +minutes on a large history, but only processes new messages — subsequent runs +are fast. + +Once embeddings exist, unfiltered `smriti recall` automatically uses the full +hybrid pipeline (BM25 + vector + RRF). Filtered recall and `smriti search` +currently use BM25 only. + +--- + +## Filtering + +All filters compose and work across both commands: + +```bash +# Scope to a project +smriti recall "auth" --project myapp + +# Scope to a specific agent +smriti search "deployment" --agent cursor + +# Scope to a category +smriti recall "why did we choose postgres" --category decision + +# Combine them +smriti search "migration" --project api --category decision --limit 5 +``` + +Category filtering is hierarchical — `--category decision` matches +`decision`, `decision/technical`, `decision/process`, and +`decision/tooling`. + +--- + +## Token Compression + +The point of recall isn't just finding relevant content — it's making that +content usable in a new session without blowing up the context window. + +| Scenario | Raw | Via Smriti | Reduction | +|----------|-----|------------|-----------| +| Relevant context from past sessions | ~20,000 tokens | ~500 tokens | **40x** | +| Multi-session recall + synthesis | ~10,000 tokens | ~200 tokens | **50x** | +| Full project conversation history | 50,000+ tokens | ~500 tokens | **100x** | + +That's what `--synthesize` is for — not a summary for you to read, but +compressed context for your next agent session to start with. diff --git a/docs/team-sharing.md b/docs/team-sharing.md index 22c81ba..e9228fa 100644 --- a/docs/team-sharing.md +++ b/docs/team-sharing.md @@ -1,60 +1,107 @@ # Team Sharing -Smriti's team sharing works through git — no cloud service, no accounts, no sync infrastructure. +When you work through something hard with an AI agent — a tricky migration, +an architectural decision, a bug that took three hours to trace — that +knowledge shouldn't stay locked in your chat history. Smriti lets you export +it, commit it to git, and make it available to every agent your team uses. -## How It Works +No cloud service. No accounts. No sync infrastructure. Just git. -1. **Export** knowledge from your local memory to a `.smriti/` directory -2. **Commit** the `.smriti/` directory to your project repo -3. **Teammates pull** and import the shared knowledge into their local memory +--- + +## The Flow + +1. **Export** — `smriti share` converts your sessions into clean markdown + files and writes them to `.smriti/knowledge/` in your project directory +2. **Commit** — you push `.smriti/` to your project repo like any other file +3. **Import** — teammates run `smriti sync` to pull the knowledge into their + local memory +4. **Recall** — any agent on the team can now recall that context + +--- + +## End-to-End Example + +**Alice finishes a productive session on auth:** + +```bash +smriti share --project myapp --category decision -The `.smriti/` directory lives inside your project repo alongside your code. +cd ~/projects/myapp +git add .smriti/ +git commit -m "Share auth migration decisions" +git push +``` + +**Bob starts a new session the next morning:** + +```bash +cd ~/projects/myapp +git pull +smriti sync --project myapp + +smriti recall "auth migration" --project myapp +``` + +Bob's agent now has Alice's full context — the decisions made, the approaches +considered and rejected, the trade-offs. Alice didn't have to explain +anything. Bob didn't have to ask. + +--- ## Exporting Knowledge -### Share by project +### By project ```bash smriti share --project myapp ``` -This exports all sessions tagged with project `myapp` to the project's `.smriti/knowledge/` directory. +Exports all sessions tagged to that project. -### Share by category +### By category ```bash smriti share --category decision smriti share --category architecture/design ``` -### Share a specific session +Export only what matters. Decision sessions tend to have the highest +signal — they capture the *why* behind code choices, not just the *what*. + +### A single session ```bash smriti share --session abc12345 ``` -### Custom output directory +### Options -```bash -smriti share --project myapp --output /path/to/.smriti -``` +| Flag | Description | +|------|-------------| +| `--no-reflect` | Skip LLM session reflections (on by default — requires Ollama) | +| `--reflect-model ` | Ollama model for reflections | +| `--output ` | Custom output directory | +| `--segmented` | 3-stage segmentation pipeline (beta) | + +--- -## Output Format +## What Gets Exported ``` .smriti/ -├── config.json # Sharing configuration -├── index.json # Manifest of all shared files +├── config.json +├── index.json └── knowledge/ ├── decision/ │ └── 2026-02-10_auth-migration-approach.md - ├── bug-fix/ + ├── bug/ │ └── 2026-02-09_connection-pool-fix.md └── uncategorized/ └── 2026-02-08_initial-setup.md ``` -Each knowledge file is markdown with YAML frontmatter: +Each file is clean markdown with YAML frontmatter: ```markdown --- @@ -69,31 +116,36 @@ tags: ["decision", "decision/technical"] # Auth migration approach -> Summary of the session if available +> Generated reflection: Alice and the agent decided on a phased migration +> approach, starting with read-path only to reduce risk... **user**: How should we handle the auth migration? **assistant**: I'd recommend a phased approach... ``` -## Importing Knowledge +The reflection at the top is generated by Ollama — a short synthesis of what +was decided and why. Use `--no-reflect` to skip it. -When a teammate has shared knowledge: +--- + +## Importing Knowledge ```bash -git pull # Get the latest .smriti/ files -smriti sync --project myapp # Import into local memory +git pull +smriti sync --project myapp ``` -Or import from a specific directory: +Content is hashed before import — the same session imported twice creates no +duplicates. Run `smriti sync` as often as you like. + +Import from a specific directory: ```bash smriti sync --input /path/to/.smriti ``` -### Deduplication - -Content is hashed before import. If the same knowledge has already been imported, it's skipped automatically. You can safely run `smriti sync` repeatedly. +--- ## Viewing Contributions @@ -101,61 +153,31 @@ Content is hashed before import. If the same knowledge has already been imported smriti team ``` -Shows who has shared what: - ``` Author Shared Categories Latest alice 12 decision, bug/fix 2026-02-10 bob 8 architecture, code 2026-02-09 ``` -## Git Integration - -Add `.smriti/` to your repo: - -```bash -cd /path/to/myapp -git add .smriti/ -git commit -m "Share auth migration knowledge" -git push -``` - -### `.gitignore` Recommendations - -The `config.json` and `index.json` should be committed. If you want to be selective: - -```gitignore -# Commit everything in .smriti/ -!.smriti/ -``` - -## Workflow Example +--- -### Alice (shares knowledge) +## Claude Code Auto-Discovery -```bash -# Alice had a productive session about auth -smriti share --project myapp --category decision +When you run `smriti share`, it writes a `.smriti/CLAUDE.md` index file. +Claude Code auto-discovers this at the start of every session — giving it +immediate awareness of your team's shared knowledge without any manual +prompting. -# Commit to the project repo -cd ~/projects/myapp -git add .smriti/ -git commit -m "Share auth migration decisions" -git push -``` - -### Bob (imports knowledge) +--- -```bash -# Bob pulls the latest -cd ~/projects/myapp -git pull +## Notes -# Import Alice's shared knowledge -smriti sync --project myapp +**Categories survive the roundtrip.** The category a session was tagged with +on one machine is the category it's indexed under on every machine that syncs +it — no reclassification, no loss. -# Now Bob can recall Alice's context -smriti recall "auth migration" --project myapp -``` +**Only the primary category is restored on sync.** If a session had multiple +tags, only the primary one survives. Known limitation. -Bob's AI agent now has access to Alice's decisions without Alice needing to explain anything. +**You control what gets shared.** Nothing is exported unless you explicitly +run `smriti share`. Your local memory stays local until you decide otherwise.