memgrep
by gitwork-oss
README.md
# memgrep
Local agent memory, a coding loop, Cursor from your phone, and playbooks you can schedule.
**Docs:** [https://memgrep.gitwork.dev](https://memgrep.gitwork.dev)
memgrep is a local control plane for Cursor. It started as searchable agent memory. The scope is larger now:
| Pillar | What it does |
| --- | --- |
| **Memory** | Ingest Cursor / Claude Code / Kiro chats. Hybrid recall (vector + keyword). `remember` playbooks and decisions. Fully local (SQLite + HNSW + on-device embeddings). |
| **Loop** | Per-project coding loops: task in, exit conditions, exit actions (including `github_pr`). Runs until PASS, then optional PR / follow-ups. Editable config in `<cwd>/.memgrep/`; named pointer under `~/.memgrep/loops/<name>/`. |
| **Telegram** | Allowlisted bot drives a **real** local Cursor agent (`@cursor/sdk`) in a real cwd, with memgrep MCP attached mid-task. |
| **Jobs** | Cron + remembered playbook + Cursor. Schedule the workflows you already trust. |
| **MCP** | One server for agents: memory + jobs + loop + docs (Word templates) + optional suites (Cursor, Jira, Neon, gcloud, PostHog, Upstash, Product Hunt, …). |
**The point:** lock workflows you already figured out. Store once (`remember` / ingest). Recall mid-task instead of reinventing steps every chat. Loop until done. Schedule what should run on a clock. Drive it from the IDE or from your phone.
## Demo

## Why
Agent gateways can vibe a workflow every session. That burns tokens on steps you already solved.
memgrep is for the opposite:
1. **Durable playbooks** - store the procedure, attach it via MCP, cron it if needed.
2. **Memory across tools** - a fix from last month's Cursor chat is recallable in today's agent.
3. **Remote coding without a second platform** - Telegram is the channel; Cursor is the runtime; memgrep is memory, loop, and scheduler.
4. **Loops that finish work** - not a one-shot prompt: implement, verify exits, run exit actions.
## Quickstart
Requires Node.js 18+. Native addons build on install. The embedding model (~25 MB) downloads once; memory search is offline after that. Cursor / Telegram / jobs / loop need network and a [`CURSOR_API_KEY`](https://cursor.com/dashboard/integrations).
**1. Memory**
```bash
npm install -g memgrep
memgrep ingest
memgrep recall "how did we fix the auth race?"
memgrep copy
```
**2. Coding loop**
```bash
memgrep loop init my-app --cwd ~/dev/my-app
memgrep loop use my-app
memgrep loop run --task "Add health check endpoint and tests"
memgrep loop status
memgrep loop runs
```
**3. Cursor from your phone**
```bash
memgrep telegram # BotFather token + Cursor API key + project cwd
memgrep telegram install # or: memgrep telegram install --all
memgrep telegram service # Loaded: yes?
```
**4. Scheduled playbooks** (`notify` mode needs Telegram)
```bash
memgrep remember "Smoke: reply with one line ok + time. Do not edit files." --title smoke-playbook
memgrep jobs add --name smoke-5m --cron "*/5 * * * *" \
--playbook-query "smoke playbook" --cwd ~/dev/project \
--prompt "Reply with one line: smoke ok and the current time. Do not edit files." \
--mode notify --profile default
memgrep jobs install
memgrep jobs run smoke-5m
memgrep jobs service
```
**One-shot local stack** (Telegram `--all`, jobs LaunchAgent, loopback MCP):
```bash
npm run build
node dist/cli.js cursor setup # once
node dist/cli.js loop init default --cwd ~/dev/project # or loop setup
npm start
npm stop
```
MCP stays on `http://127.0.0.1:3921/mcp`. Public tunnels are opt-in (any vendor); see [Optional public MCP](#optional-public-mcp-agnostic-tunnel).
### Always-on on macOS
| Service | Install | Status | Logs |
| --- | --- | --- | --- |
| Telegram bots + MCP | `memgrep telegram install` / `--all` | `memgrep telegram service` | `~/.memgrep/logs/telegram-launchd.log` |
| Jobs scheduler | `memgrep jobs install` | `memgrep jobs service` | `~/.memgrep/logs/jobs-launchd.log` |
| Background ingest | `memgrep ingest install` | `memgrep ingest service` | `~/.memgrep/logs/ingest-launchd.log` |
| Edge node (cloud hub) | `memgrep edge pair` then `edge install` | `memgrep edge service` | `~/.memgrep/logs/edge-*.log` |
After upgrade: stop foreground pollers, re-run `telegram install` / `jobs install` / `ingest install` / `edge install`, confirm **Loaded: yes**. Restart:
```bash
launchctl kickstart -k gui/$(id -u)/com.memgrep.telegram
launchctl kickstart -k gui/$(id -u)/com.memgrep.jobs
launchctl kickstart -k gui/$(id -u)/com.memgrep.ingest
launchctl kickstart -k gui/$(id -u)/com.memgrep.edge
```
These pause while the Mac sleeps; missed job ticks beyond a 6h grace window are skipped.
## Command map
```bash
# Memory
memgrep scan | ingest | ingest install | remember | list | recall | show | copy | delete
# Edge node ↔ cloud hub (macOS / Linux / Windows)
memgrep edge token | pair | install | service | status
# Loop (per-project profiles)
memgrep loop init <name> [--cwd <path>]
memgrep loop use <name>
memgrep loop setup|status [--profile <name>]
memgrep loop run --task "..." [--profile <name>]
memgrep loop runs [runId]
memgrep loop input|exit|action set|rm ...
# Cursor agent (MCP suite)
memgrep cursor setup|status
# Telegram
memgrep telegram | telegram setup|list|status|install|service|uninstall
# Jobs
memgrep jobs add|list|show|run|logs|daemon|install|service|...
# MCP server
memgrep serve [--http] [--host 127.0.0.1] [--port 3921] [--token <token>] [--allowed-host <host>]
# Docs (Word templates → .memgrep/docs; MCP docs_* always on)
memgrep docs setup|status|list|fill|edit
# Optional suites (tools omitted until configured)
memgrep jira|neon|gcloud|posthog|upstash|producthunt setup|status
# Loop: use `loop init` / `loop setup` (not `<suite> setup`)
# File search (offline semantic grep)
memgrep index <dir>
memgrep search "query"
```
Full walkthroughs: [docs](https://memgrep.gitwork.dev).
## Agent memory
**In:** `ingest` (Cursor, Claude Code, Kiro) or `remember` (your own note / playbook).
**Out:** `recall` (hybrid by default: vector + FTS5/BM25 via RRF), `list`, `show`, `copy`.
```bash
memgrep scan [--source kiro] [--new] [--last <n>]
memgrep ingest [--source cursor,claude,kiro]
memgrep ingest --pick 2,5
memgrep ingest --last [n]
memgrep ingest <file...>
memgrep remember "we chose X over Y because Z" --title decision
memgrep recall "<query>" [-k <n>] [--mode hybrid|vector|keyword]
```
Memory lives in `~/.memgrep` (`MEMGREP_HOME` to override). Re-ingest is idempotent by content hash.
| Tool | Source | Notes |
| --- | --- | --- |
| Cursor | `~/.cursor/projects/*/agent-transcripts/` | Full user + assistant turns |
| Claude Code | `~/.claude/projects/*/*.jsonl` | Full user + assistant turns |
| Kiro IDE | Kiro `globalStorage` workspace sessions | User turns and titles (assistant output is opaque) |
| Antigravity | Not yet | Encrypted protobuf; agents can still *query* via MCP |
| Anything else | `memgrep remember "<text>"` | Manual notes, decisions, postmortems |
New sources: implement `TranscriptSource` and pass it to `ingestTranscripts`.
### Give agents access (MCP)
One MCP server. Register once per client.
**No global install** (recommended):
```json
{
"mcpServers": {
"memgrep": {
"command": "npx",
"args": ["-y", "memgrep", "serve"]
}
}
}
```
**Global install:**
```json
{
"mcpServers": {
"memgrep": {
"command": "memgrep",
"args": ["serve"]
}
}
}
```
Config: Cursor `~/.cursor/mcp.json`, Claude Code `claude mcp add memgrep -- npx -y memgrep serve`, Kiro `~/.kiro/settings/mcp.json`.
**Always on the wire:** `recall`, `get_chat`, `list_chats`, `remember`, `resolve_open`, `jobs_*`, `docs_*`.
**When configured:** `loop_*`, `cursor_*`, plus optional suites below.
### Optional MCP suites
Unconfigured credential suites are omitted from the tool list. **Docs** is always registered.
| Suite | Configure | Purpose |
| --- | --- | --- |
| `docs` | `memgrep docs setup` (optional; creates dirs) | Jinja-style Word fill (`docs_fill`, `docs_serve`, …) under `.memgrep/templates` → `.memgrep/docs` |
| `cursor` | `memgrep cursor setup` | Local `@cursor/sdk` agent (`cursor_workspaces`, `cursor_status`, `cursor_run`) |
| `loop` | `memgrep loop init` / `loop setup` | Coding loop (`loop_run`, `loop_status`, upsert defaults) |
| `jira` | `memgrep jira setup` | Issues, comments, transitions |
| `neon` | `memgrep neon setup` | Read-only Neon project / branch metadata |
| `gcloud` | `memgrep gcloud setup` | Logs + GCE inspect (ADC / service account) |
| `posthog` | `memgrep posthog setup` | Analytics queries / flags |
| `upstash` | `memgrep upstash setup` | Redis REST helpers |
| `producthunt` | `memgrep producthunt setup` | PH read APIs |
### Optional public MCP (agnostic tunnel)
1. `npm start` or `memgrep serve --http` on `127.0.0.1:3921`
2. Point any tunnel at that port
3. Allow the public Host and require a bearer token:
```bash
export MEMGREP_MCP_TOKEN="$(cat ~/.memgrep/mcp-token)"
export MEMGREP_PUBLIC_URL=https://your-tunnel.example/mcp
# or MEMGREP_PUBLIC_HOST / MEMGREP_ALLOWED_HOSTS / ~/.memgrep/mcp-public-url
```
## Coding loop
Agnostic loop: free-text **task**, optional **inputs**, **exit conditions**, **exit actions**. The loop implements, verifies exits, then runs builtins (e.g. `github_pr`) and any remaining agent actions. Completion can notify via Telegram.
```bash
memgrep loop init prepaid --cwd ~/dev/prepaid
memgrep loop use prepaid
memgrep loop setup # edit cwd / git defaults
memgrep loop status --profile prepaid
memgrep loop run --task "Ship refunds health check" --profile prepaid # foreground (CLI)
memgrep loop runs
```
Config lives in the project at `<cwd>/.memgrep/` (edit in your IDE; safe to commit), including `AGENTS.md` (how to add inputs/exits/actions for agents). Home keeps a thin pointer at `~/.memgrep/loops/<name>/project.json` and the template at `~/.memgrep/loop.base/`. Active: `~/.memgrep/loop.active` or `MEMGREP_LOOP_PROFILE`. Legacy home-only `loops/<name>/loop.json` still works until you re-init.
MCP: `loop_run` starts detached in the background; also `loop_run_status`, `loop_status`, `loop_upsert_*` / `loop_remove_*`. Requires Cursor; Jira optional for `jiraKey` context only.
## Scheduled playbooks (jobs)
A **job** is cron + pointer to a remembered playbook. The daemon fires Cursor in the job cwd with memgrep MCP; the agent `get_chat`s the playbook and runs your prompt.
```bash
memgrep jobs add \
--name email-scan-am \
--cron "30 8 * * 1-5" \
--playbook-query "email scan" \
--cwd ~/dev/career-ops \
--prompt "Scan unread mail and summarize; do not send replies" \
--mode auto
memgrep jobs list
memgrep jobs run email-scan-am
memgrep jobs logs email-scan-am
memgrep jobs install # LaunchAgent com.memgrep.jobs
```
Stored under `~/.memgrep/jobs/`. Default mode is `notify` (Telegram summary). Use `--mode auto` carefully for read-only jobs. Same jobs are manageable from Cursor or Telegram via MCP.
## Cursor from your phone (Telegram)
Chat with a **local** Cursor agent from Telegram. You do not need the same Wi-Fi. Usage bills against your Cursor plan. Needs a [`CURSOR_API_KEY`](https://cursor.com/dashboard/integrations).
```bash
memgrep telegram
memgrep telegram setup career
memgrep telegram --profile career
memgrep telegram --all
memgrep telegram install --all
memgrep telegram service
```
Profiles: `~/.memgrep/telegram/<profile>.json`. The bot embeds loopback MCP so Cursor can call memory, jobs, loop, and configured suites mid-task.
On your phone: free text / `/ask`, `/ws` workspaces, `/cwd`, `/new`, `/model`, `/mode`, `/status`, `/recall`, `/list`, `/show`, `/open`, `/help`. Only allowlisted Telegram user ids get answers.
**Env overrides:** `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALLOWED_USER_IDS`, `CURSOR_API_KEY`, `MEMGREP_TELEGRAM_CWD`, `MEMGREP_TELEGRAM_MODEL`, `MEMGREP_TELEGRAM_PROFILE`.
**Split processes:** `memgrep serve --http` + `memgrep telegram --no-server` (`MEMGREP_MCP_URL` to override).
## File search
Semantic grep over any folder, fully offline:
```bash
npx memgrep index ./docs
npx memgrep search "how do I configure auth?"
```
`index` options: `--out` (default `.memgrep`), `--model` (any [Transformers.js-compatible](https://huggingface.co/models?library=transformers.js&pipeline_tag=feature-extraction) embedding model).
`search` options: `--index` (default `.memgrep`), `-k` for the number of results.
## Library usage
Same engine as an embeddable library (SQLite for semantic search, not a hosted DB):
```typescript
import { VectorIndex } from 'memgrep';
const index = await VectorIndex.create({ model: 'Xenova/all-MiniLM-L6-v2' });
await index.add([
{ id: 'doc1', text: 'To reset your password, click the forgot password link.' },
{ id: 'doc2', text: 'Our refund policy allows returns within 30 days.', metadata: { url: '/refunds' } },
]);
const hits = await index.search('I forgot my login', { k: 5 });
await index.save('./my-index');
const loaded = await VectorIndex.load('./my-index');
```
Also exported: `Embedder`, `chunkText`, `MemoryStore`, `ingestTranscripts`, and the per-tool parsers. Use `Embedder` + `chunkText` if you already have pgvector / LanceDB / Qdrant and only want local embeddings.
## How it works
**Chunks are searched; chats are returned.**
1. Transcripts parse to clean `User:` / `Assistant:` dialogue (tool noise stripped).
2. Text is chunked (~1000 chars, 200 overlap) and embedded locally (384-dim, Transformers.js).
3. Vectors go to HNSW; chats + chunk text to SQLite; FTS5/BM25 kept in sync via triggers.
4. Queries run vector + keyword in parallel; **RRF** merges. Exact ids ride keyword; meaning rides vectors.
5. Ingest is idempotent by content hash. Vector index is a rebuildable cache; SQLite is source of truth. Next `recall` / `ingest` / `serve` self-heals a divergent index.
## Limitations, honestly
- Hybrid search helps exact ids; very short or heavily punctuated strings can still miss.
- Kiro ingestion is partial. Antigravity cannot be ingested today.
- `delete` is not permanent against re-ingest if the source transcript still exists.
- One writer at a time; no cross-process lock yet (self-heal repairs loss on next open).
- Recall quality tracks what was said in dialogue; signal that lived only in tool output searches poorly. A one-line `remember` often wins.
- Telegram and jobs need a host that stays awake. LaunchAgents pause while the Mac sleeps.
- Loop and Cursor suites need a valid Cursor API key and allowlisted cwd.
## Roadmap
- Tombstones so `delete` survives re-ingest
- More transcript sources (Antigravity if the format opens, Codex CLI, Windsurf)
- File-watch ingest (`--watch`) on top of the interval daemon
- Linux systemd units alongside macOS LaunchAgents
- Telegram `/jobs` slash shortcuts (MCP already covers manage-from-chat)
- Browser / WASM HNSW for the library
## Development
```bash
npm install
npm run build
npm test
```
Docs site: `cd docs && npm install && npm run dev` (port 4401). Live: [memgrep.gitwork.dev](https://memgrep.gitwork.dev).
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues