{"id":51220385,"url":"https://github.com/raiyanyahya/recall","last_synced_at":"2026-07-02T13:07:08.159Z","repository":{"id":366106757,"uuid":"1274728677","full_name":"raiyanyahya/recall","owner":"raiyanyahya","description":"Fully local project memory for Claude Code. No API key, no external model, nothing sent anywhere.","archived":false,"fork":false,"pushed_at":"2026-06-20T09:41:15.000Z","size":65,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-20T10:12:08.429Z","etag":null,"topics":["ai","ai-agents","anthropic","claude","claude-code","claude-code-plugin","claude-plugin","context","developer-tools","llm","local-first","memory","offline","privacy","productivity","python","summarization","textrank"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raiyanyahya.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-19T20:36:41.000Z","updated_at":"2026-06-20T09:41:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/raiyanyahya/recall","commit_stats":null,"previous_names":["raiyanyahya/recall"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/raiyanyahya/recall","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiyanyahya%2Frecall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiyanyahya%2Frecall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiyanyahya%2Frecall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiyanyahya%2Frecall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raiyanyahya","download_url":"https://codeload.github.com/raiyanyahya/recall/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiyanyahya%2Frecall/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35048212,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-agents","anthropic","claude","claude-code","claude-code-plugin","claude-plugin","context","developer-tools","llm","local-first","memory","offline","privacy","productivity","python","summarization","textrank"],"created_at":"2026-06-28T07:00:23.373Z","updated_at":"2026-07-02T13:07:08.153Z","avatar_url":"https://github.com/raiyanyahya.png","language":"Python","funding_links":[],"categories":["Python","🛠️ Tools \u0026 Utilities"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e🔁\u003c/h1\u003e\n\u003ch3 align=\"center\"\u003eRecall — fully-local project memory for Claude Code\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://claude.com/claude-code\"\u003e\u003cimg alt=\"Built for Claude Code\" src=\"https://img.shields.io/badge/built%20for-Claude%20Code-CC785C\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/raiyanyahya/recall/actions/workflows/ci.yml\"\u003e\u003cimg alt=\"CI\" src=\"https://github.com/raiyanyahya/recall/actions/workflows/ci.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/raiyanyahya/recall/actions/workflows/codeql.yml\"\u003e\u003cimg alt=\"CodeQL\" src=\"https://github.com/raiyanyahya/recall/actions/workflows/codeql.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/raiyanyahya/recall\"\u003e\u003cimg alt=\"Coverage\" src=\"https://codecov.io/gh/raiyanyahya/recall/branch/master/graph/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg alt=\"License: MIT\" src=\"https://img.shields.io/badge/License-MIT-blue.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n\u003e Claude Code starts every session cold. Recall keeps a local log of your\n\u003e sessions and condenses it into a resume-ready summary — **entirely on your\n\u003e machine**. No API key, no external model, nothing sent anywhere. It's built for\n\u003e people running Claude Code locally on a subscription: the only AI in the loop is\n\u003e Claude Code itself; the summarization is done by a classical Python summarizer.\n\n## Why Recall\n\n- **Free on your subscription.** It solves the cold-start problem — no more\n  re-explaining the project each session — without a metered summarizer running up\n  a bill. The summary is a local algorithm, not an LLM call, so persistent memory\n  costs you nothing beyond the subscription you already pay for.\n- **Saves your usage credits.** Two ways: (1) the summary is built locally, so\n  capturing and updating your memory spends **zero** model tokens; and (2)\n  resuming from a compact `context.md` (~1–2K tokens) instead of re-explaining the\n  project from scratch each session means far fewer tokens spent per session —\n  stretching your subscription's usage limits (or, on the API, lowering billed\n  credits).\n- **Nothing leaves your machine.** Your transcripts (code, paths, sometimes\n  secrets) are never sent to any API. Most \"memory\" tools pipe your context to a\n  model endpoint; Recall makes a privacy guarantee they can't. See\n  [PRIVACY.md](PRIVACY.md) for the full policy.\n- **Zero-friction.** No `pip install`, no local model to run, no key to configure,\n  works offline. It starts working the moment the plugin loads.\n\nTwo files, written into your project under `.recall/`:\n\n- **`history.md`** — *the log.* Append-only. Every session is captured here as it\n  happens (your prompts, Claude's replies, the files touched and commands run).\n- **`context.md`** — *the summary.* Overwritten by the local summarizer — the\n  condensed \"where are we right now\" you load into the next session: goal,\n  summary, **next steps / open threads**, files touched, and where you left off.\n\n## \"Doesn't Claude Code already have memory?\"\n\nIt does — and Recall is complementary, not a replacement. The built-in options\nsolve different problems:\n\n- **`CLAUDE.md` (and the `#` shortcut)** is *hand-written* memory: rules and notes\n  **you** curate, loaded as **instructions Claude follows**. Great for \"how I want\n  you to work,\" but it's manual upkeep and it doesn't record what actually\n  happened in a session.\n- **`--continue` / `--resume`** replays a *prior conversation* — full fidelity, but\n  it reloads the whole transcript (token-heavy) and is tied to your local session\n  history on one machine, not a portable, readable digest.\n- **Context compaction** condenses a conversation *within* a session; it isn't a\n  durable record you reopen days later.\n\nRecall fills the gap between these: an **automatic, deterministic record of what\neach session did**, condensed into a compact resume point.\n\n| | `CLAUDE.md` / `#` | `--continue` / `--resume` | **Recall** |\n|---|---|---|---|\n| What it is | Hand-written notes \u0026 rules | Reloads a prior conversation | Auto-captured session log + local summary |\n| Upkeep | Manual | None (you pick the session) | None — written as you work |\n| Holds | Instructions to follow | The full prior transcript | Goal, files, commands, where you left off, next steps |\n| Cost to resume | Small | Large (replays full transcript) | ~1–2K tokens (compact digest) |\n| Form | Markdown you edit | Local session state | Plaintext in `.recall/` — diffable \u0026 shareable |\n| How Claude treats it | As instructions | As the conversation | Fenced as **untrusted reference data** |\n\nIn short: `CLAUDE.md` is *how I want you to work*; Recall is *here's what we did\nlast time and where we stopped* — produced offline, with no model tokens spent.\n\n## How it works\n\n| Moment | What happens |\n|---|---|\n| **During the session** | The `Stop` / `SessionEnd` hooks append new activity to `.recall/history.md`. Capture is incremental (only new turns) and fully local. |\n| **At session start** | The `SessionStart` hook surfaces `context.md` and has Claude ask you two things: resume from the saved context? and keep logging this session? |\n| **Before you wrap up** | You run `/recall:save`. The **local summarizer** reads `history.md` and (over)writes `context.md`. |\n| **…or automatically** | Set `auto_save_context: \"on_end\"` and `context.md` regenerates every time a session ends — no `/recall:save` needed. |\n\nThere is no LLM call anywhere — the summary is produced by **TF-IDF + TextRank**\n(extractive summarization) running locally.\n\n## The summarizer\n\n`scripts/summarizer.py` ranks the most central sentences of your session:\n\n1. TF-IDF sentence vectors\n2. a cosine-similarity graph between sentences\n3. **TextRank** — PageRank power iteration over that graph — to score sentences\n4. the top *N* are kept in original order\n\n`context.md` wraps that summary with deterministic facts pulled straight from the\ntranscript and git: the goal (your first ask), files touched, commands run, where\nyou left off, and `git diff --stat`.\n\n**No installs required.** The whole TF-IDF + TextRank implementation is vendored\nin `summarizer.py`. If `numpy` happens to be importable it's used to vectorize the\nmath (faster on big sessions); if not, an identical pure-Python TextRank runs\ninstead. Same algorithm, same result — numpy is an optional accelerator, never a\nrequirement. The save output tells you which path ran.\n\n## Commands\n\n- `/recall:save` — run the local summarizer → (over)write `context.md`.\n- `/recall:show` — print `context.md`.\n- `/recall:log` — tail `history.md`.\n\n## Configuration — `recall.config.json`\n\nDrop this in your project root to override defaults:\n\n| Key | Default | Purpose |\n|---|---|---|\n| `output_dir` | `\".recall\"` | Where `history.md` / `context.md` live. |\n| `capture_history` | `true` | Append session activity to `history.md`. |\n| `auto_save_context` | `\"off\"` | Regenerate `context.md` when a session ends: `\"off\"` or `\"on_end\"`. |\n| `summary_sentences` | `8` | How many sentences the summary keeps. |\n| `redact` | `true` | Strip obvious secrets before writing the md files. |\n| `include_git` | `true` | Add `git diff --stat` + recent commits to `context.md`. |\n| `max_input_chars` | `200000` | Cap on text fed to the summarizer (oldest dropped). |\n\n**Pause logging** for a project without editing config: create\n`.recall/.capture-paused`. Delete it to resume.\n\n## Privacy \u0026 security\n\nRecall makes **no network calls, uses no API key, and loads no third-party\nmodel.** The summarizer is local Python; the hooks are stdlib-only (numpy is an\noptional accelerator). It reads your session transcript and writes only under\n`output_dir`. Concretely:\n\n- **No credentials, ever.** The plugin has zero references to API keys, auth,\n  `ANTHROPIC_*`, or HTTP. If `claude` itself shows *\"Invalid API key\"*, that's the\n  CLI's own auth — usually a stale `ANTHROPIC_API_KEY` env var shadowing your\n  subscription login. `unset ANTHROPIC_API_KEY` (or run `env -u ANTHROPIC_API_KEY\n  claude …`). It has nothing to do with Recall.\n- **Redaction.** A best-effort pass strips common secret shapes (API keys, tokens,\n  `.env` assignments, PEM keys) before writing, since `context.md` / `history.md`\n  may be committed. Best-effort, not a guarantee — review before committing.\n- **Hardened git.** `git diff`/`log` are run with `core.fsmonitor`,\n  `diff.external`, hooks, and the pager disabled, so an untrusted cloned repo\n  can't use its own git config to execute code when Recall reads ground-truth.\n  Set `include_git: false` to skip git entirely.\n- **Confined writes.** `output_dir` is forced to stay inside the project; a\n  project-shipped config can't redirect writes to an absolute path or `../..`.\n- **Scoped transcript.** Recall only reads the transcript for the current\n  project (matched by cwd); it never falls back to another project's sessions.\n- **Trust boundary for shared memory.** `context.md` is injected into the model at\n  session start. If you **commit `.recall/` as shared team memory**, treat it\n  like any other shared input: a teammate (or a bad actor with repo write access)\n  could craft a `context.md` to attempt prompt-injection. SessionStart fences the\n  content and labels it untrusted data, and Claude asks before relying on it — but\n  if you don't fully trust who can write the repo, keep `.recall/` git-ignored\n  (the default).\n\n## Committing `.recall/`\n\nBoth are fine. Commit it for shared team memory, or git-ignore it for personal\nmemory (`.gitignore` ships ignoring it by default — flip the comment to commit).\n\n## Install\n\n**From the marketplace** (this repo is its own marketplace):\n\n```\n/plugin marketplace add raiyanyahya/recall\n/plugin install recall@recall\n```\n\n**Local dev** (no install step):\n\n```\nclaude --plugin-dir /path/to/recall\n```\n\nNo `pip install` — the summarizer is vendored and stdlib-only (numpy used as an\noptional accelerator if present). Work a session, run `/recall:save`, and open\na fresh session — Recall greets you with where you left off.\n\n## Development\n\n```bash\npython -m venv .venv \u0026\u0026 . .venv/bin/activate\npip install pytest ruff bandit numpy   # numpy optional\n\nruff check scripts tests benchmarks    # lint\nbandit -c pyproject.toml -r scripts    # security static analysis\npytest                                 # run the suite (also test without numpy)\npython benchmarks/bench.py             # perf + quality numbers (human-readable)\npython benchmarks/bench.py --check     # assert quality invariants (the CI gate)\nclaude plugin validate .               # official manifest validation\n```\n\n`benchmarks/bench.py` is a stdlib-only harness: alongside latency/throughput it\nscores the summarizer's salient-sentence selection against lead/tail/random\nbaselines on a labeled fixture set and checks the numpy and pure-Python cores\nselect the **same** sentences. `--check` gates those quality invariants (it never\ngates wall-clock timings). Redaction quality is covered by the unit suite\n(`tests/test_redact.py`), so no secret-shaped fixtures live in the benchmark.\n\nCI (`.github/workflows/`) runs lint + Bandit, the test suite across Python\n3.9–3.13 **with and without numpy** (both summarizer paths), the benchmark\nquality gate (both paths), CodeQL, secret scanning, and manifest JSON validation\non every push and PR. See\n[CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).\n\n## Layout\n\n```\nrecall/\n├── .claude-plugin/plugin.json   # manifest\n├── hooks/hooks.json             # SessionStart (ask/resume) · Stop+SessionEnd (capture)\n├── commands/                    # /recall:save · show · log\n├── scripts/\n│   ├── summarizer.py            # vendored TF-IDF + TextRank (numpy optional)\n│   ├── make_context.py          # build/overwrite context.md\n│   ├── capture.py               # append session activity to history.md\n│   ├── session_start.py         # surface context + ask the start questions\n│   ├── parse_transcript.py      # transcript → events + renderers\n│   └── config.py · common.py · redact.py\n├── tests/                       # pytest suite (summarizer, capture, security, …)\n├── benchmarks/bench.py          # perf + quality harness (CI quality gate)\n├── .github/                     # CI, CodeQL, secret scan, dependabot\n├── recall.config.json        # config template / defaults\n├── pyproject.toml               # ruff / pytest / bandit config (no runtime deps)\n├── LICENSE · SECURITY.md · CONTRIBUTING.md\n└── .gitignore\n```\n\n## Contributing \u0026 issues\n\nBugs and ideas are welcome — open an [issue](https://github.com/raiyanyahya/recall/issues/new/choose)\n(bug-report and feature templates provided) or a pull request. See\n[CONTRIBUTING.md](CONTRIBUTING.md) before submitting, and report security\nvulnerabilities privately per [SECURITY.md](SECURITY.md) rather than in a public issue.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraiyanyahya%2Frecall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraiyanyahya%2Frecall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraiyanyahya%2Frecall/lists"}