Skip to main content
Glama
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

![memgrep demo](demo/preview.gif)

## 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)