{"_id":"@ayberkaya/memshot","_rev":"3-043fa4c848e09cbdee3332bda2829ead","name":"@ayberkaya/memshot","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@ayberkaya/memshot","version":"0.1.0","keywords":["llm","agent","memory","context","tiered","token-budget","rag","langchain","openai","anthropic"],"author":{"name":"Ayberk Kaya"},"license":"MIT","_id":"@ayberkaya/memshot@0.1.0","maintainers":[{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"}],"homepage":"https://github.com/ayberkaya/memshot#readme","bugs":{"url":"https://github.com/ayberkaya/memshot/issues"},"dist":{"shasum":"5caadc8575d46984ed9c6f6726605de5a43ce08a","tarball":"https://registry.npmjs.org/@ayberkaya/memshot/-/memshot-0.1.0.tgz","fileCount":28,"integrity":"sha512-zQATHrCD7Oq5HEOpnD7kuc+SDQIQzzVzCJZ83rk2fHscJ9E75bDOBYjTWamB6LwvOPyCgXwcBOW5b3RB7bv2JA==","signatures":[{"sig":"MEUCIHKNdcvE2tEysHUpN2yYUY6gsWfIaMZKHyXAAAEExHHnAiEArS9hKv1CkTRZo92fiq1pAEHw3gnFVpnqZBz37SIDilw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":126071},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./adapters/next":{"types":"./dist/adapters/next.d.ts","import":"./dist/adapters/next.js","require":"./dist/adapters/next.cjs"},"./adapters/express":{"types":"./dist/adapters/express.d.ts","import":"./dist/adapters/express.js","require":"./dist/adapters/express.cjs"},"./adapters/claude-hook":{"types":"./dist/adapters/claude-hook.d.ts","import":"./dist/adapters/claude-hook.js","require":"./dist/adapters/claude-hook.cjs"}},"gitHead":"02fcf525fb579e04cad570ab73dcf2ad6c1b0bc1","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","benchmark":"npx tsx examples/benchmark.ts","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"},"repository":{"url":"git+https://github.com/ayberkaya/memshot.git","type":"git"},"_npmVersion":"11.11.0","description":"Tiered, token-budget-aware memory for any LLM agent","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","tsup":"^8.2.0","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^22.0.0"},"peerDependencies":{"gpt-tokenizer":"^2.0.0"},"peerDependenciesMeta":{"gpt-tokenizer":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/memshot_0.1.0_1782427742276_0.04505262446362801","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ayberkaya/memshot","version":"0.2.0","keywords":["llm","agent","memory","context","tiered","token-budget","rag","langchain","openai","anthropic"],"author":{"name":"Ayberk Kaya"},"license":"MIT","_id":"@ayberkaya/memshot@0.2.0","maintainers":[{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"}],"homepage":"https://github.com/ayberkaya/memshot#readme","bugs":{"url":"https://github.com/ayberkaya/memshot/issues"},"bin":{"memshot":"dist/cli.cjs"},"dist":{"shasum":"0f823c0f6769e849e4061ef940eb1ae32fbe0e0e","tarball":"https://registry.npmjs.org/@ayberkaya/memshot/-/memshot-0.2.0.tgz","fileCount":35,"integrity":"sha512-JmotDFhM8Az4X4quyZpyuKNajfx9uR0LE5ZHaqXCqYq4Ms+LPtEE4hhzJS4FRpDc+8jyQOgCTrrUyFxlnamg1g==","signatures":[{"sig":"MEUCIBBj3xlb+zy0Ma0JLReUvlmO8TdYjKvmV8STcZ7F/E8MAiEAkydYhALvKjS+49ViMQ6V15YoOFj4Pv3KqYSJDgj+Z5k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":338895},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./adapters/next":{"types":"./dist/adapters/next.d.ts","import":"./dist/adapters/next.js","require":"./dist/adapters/next.cjs"},"./adapters/express":{"types":"./dist/adapters/express.d.ts","import":"./dist/adapters/express.js","require":"./dist/adapters/express.cjs"},"./adapters/claude-hook":{"types":"./dist/adapters/claude-hook.d.ts","import":"./dist/adapters/claude-hook.js","require":"./dist/adapters/claude-hook.cjs"}},"gitHead":"837a6dbf0198a9e22150391bbf25e0a2190b9be5","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","benchmark":"npx tsx examples/benchmark.ts","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"},"repository":{"url":"git+https://github.com/ayberkaya/memshot.git","type":"git"},"_npmVersion":"11.11.0","description":"Tiered, token-budget-aware memory for any LLM agent","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","tsup":"^8.2.0","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^22.0.0","gpt-tokenizer":"^2.0.0"},"peerDependencies":{"gpt-tokenizer":"^2.0.0"},"peerDependenciesMeta":{"gpt-tokenizer":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/memshot_0.2.0_1782465926662_0.036301694638167126","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@ayberkaya/memshot","version":"0.2.1","description":"Tiered, token-budget-aware memory for any LLM agent","author":{"name":"Ayberk Kaya"},"license":"MIT","homepage":"https://github.com/ayberkaya/memshot#readme","repository":{"type":"git","url":"git+https://github.com/ayberkaya/memshot.git"},"bugs":{"url":"https://github.com/ayberkaya/memshot/issues"},"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","bin":{"memshot":"dist/cli.cjs"},"sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./adapters/express":{"types":"./dist/adapters/express.d.ts","import":"./dist/adapters/express.js","require":"./dist/adapters/express.cjs"},"./adapters/next":{"types":"./dist/adapters/next.d.ts","import":"./dist/adapters/next.js","require":"./dist/adapters/next.cjs"},"./adapters/claude-hook":{"types":"./dist/adapters/claude-hook.d.ts","import":"./dist/adapters/claude-hook.js","require":"./dist/adapters/claude-hook.cjs"}},"scripts":{"build":"tsup","prepublishOnly":"npm run build","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","dev":"tsup --watch","benchmark":"npx tsx examples/benchmark.ts"},"keywords":["llm","agent","memory","context","tiered","token-budget","rag","langchain","openai","anthropic"],"dependencies":{},"devDependencies":{"typescript":"^5.5.0","tsup":"^8.2.0","vitest":"^2.0.0","@types/node":"^22.0.0","tsx":"^4.0.0","gpt-tokenizer":"^2.0.0"},"peerDependencies":{"gpt-tokenizer":"^2.0.0"},"peerDependenciesMeta":{"gpt-tokenizer":{"optional":true}},"gitHead":"cf5d99dc30efccd42fa3ea99296277e471ea888c","_id":"@ayberkaya/memshot@0.2.1","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-Fd82+lZC9cglqb27kWC9dZX2nyfPgzrmZOMGVZxdsBwpRfy1lWB6TSg+m2CbOtlThmPERNTL4KHdDFpJ5/T88w==","shasum":"d3b27adefa47e850df79261b840e61ecc0171c67","tarball":"https://registry.npmjs.org/@ayberkaya/memshot/-/memshot-0.2.1.tgz","fileCount":35,"unpackedSize":340565,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC6lmpK+vwgiNS8DCKMsFvPm6rBbNKtu9YSSOs7weC7cwIhAIXSgpDhw/A349GJoufa9+WrOJbcJ9LsWq3Bw9OLufu1"}]},"_npmUser":{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"},"directories":{},"maintainers":[{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/memshot_0.2.1_1782466704775_0.110350671212746"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-25T22:49:02.180Z","modified":"2026-06-26T09:38:24.999Z","0.1.0":"2026-06-25T22:49:02.418Z","0.2.0":"2026-06-26T09:25:26.809Z","0.2.1":"2026-06-26T09:38:24.908Z"},"bugs":{"url":"https://github.com/ayberkaya/memshot/issues"},"author":{"name":"Ayberk Kaya"},"license":"MIT","homepage":"https://github.com/ayberkaya/memshot#readme","keywords":["llm","agent","memory","context","tiered","token-budget","rag","langchain","openai","anthropic"],"repository":{"type":"git","url":"git+https://github.com/ayberkaya/memshot.git"},"description":"Tiered, token-budget-aware memory for any LLM agent","maintainers":[{"name":"ayberkaya","email":"ayberk35kaya@gmail.com"}],"readme":"# memshot\n\n[![npm version](https://img.shields.io/npm/v/%40ayberkaya%2Fmemshot)](https://www.npmjs.com/package/@ayberkaya/memshot)\n[![license](https://img.shields.io/npm/l/%40ayberkaya%2Fmemshot)](./LICENSE)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/%40ayberkaya%2Fmemshot)](https://bundlephobia.com/package/@ayberkaya/memshot)\n\n**Your agent's memory shouldn't cost 15,000 tokens before the user says hello.**\n\nmemshot is a tiered, token-budget-aware memory library for LLM agents. Zero runtime dependencies. No vector DB. No server. Works anywhere JavaScript runs — Node, Deno, Bun, edge functions, the browser.\n\n---\n\n## The problem\n\nMost memory libraries have no selection layer. Inject everything on every call. At 500 stored memories:\n\n```\nnaive injection:\n████████████████████████████████████████████████  36,552 tokens\n                                                  ^^^^^^^^^^^^^^^^^^\n                                           9.1× your context budget\n```\n\nThe signal-to-noise ratio collapses. Relevant context drowns in noise.\n\n## How memshot fixes it\n\n```\nmemshot (4000-token budget):\n████  3,957 tokens  (62 items selected from 500)\n      ↑ only what's relevant to this prompt\n```\n\nmemshot selects which memories to inject using three tiers:\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│  prompt: \"we discussed billing last week, what did we decide?\"      │\n└─────────────────────┬───────────────────────────────────────────────┘\n                      │\n         ┌────────────▼────────────┐\n         │       hot tier          │  always injected (once:true = once/session)\n         │  \"User's name is Ayberk\"│\n         └────────────┬────────────┘\n                      │\n         ┌────────────▼────────────┐\n         │       warm tier         │  injected when triggers regex matches prompt\n         │  billing/pricing rules  │  ← /billing|pricing/i matched\n         └────────────┬────────────┘\n                      │\n         ┌────────────▼────────────┐\n         │       cold tier         │  corpus BM25 + frecency → greedy knapsack\n         │  top-N relevant history │  until budget exhausted\n         └────────────┬────────────┘\n                      │\n         ┌────────────▼────────────┐\n         │    4000-token budget    │\n         │    injected to LLM      │\n         └─────────────────────────┘\n```\n\n| Tier | Selection | Use for |\n|------|-----------|---------|\n| **hot** | Always included. `once: true` injects once per `sessionId`. | Identity, system facts, permanent instructions |\n| **warm** | Included when any `triggers` regex matches the prompt. No triggers = always warm. | Domain knowledge, project context |\n| **cold** | Corpus-aware BM25 keyword relevance + frecency decay, selected greedily by score/token ratio. | Conversation history, decisions, event log |\n\n---\n\n## Install\n\n```bash\nnpm install @ayberkaya/memshot\n# or: bun add @ayberkaya/memshot\n# or: npx memshot   (CLI, no install required)\n```\n\nZero runtime dependencies. Optional: `npm install gpt-tokenizer` for exact token counts.\n\n---\n\n## Quickstart\n\n```ts\nimport { Memory, fileStore } from \"@ayberkaya/memshot\"\n\nconst mem = new Memory({ budget: 4000, store: fileStore(\"./memories\") })\n\n// Add memories to tiers\nawait mem.add({ content: \"User's name is Ayberk. Prefers TypeScript.\", tier: \"hot\" })\nawait mem.add({ content: \"Billing: ship Stripe subscriptions first, add metering later.\", tier: \"warm\", triggers: [/billing|pricing/i] })\nawait mem.add({ content: \"Meeting 2026-06-20: decided to delay enterprise tier until Q3.\", tier: \"cold\" })\n\n// Resolve against the current prompt — returns only what fits in budget\nconst { text, tokensUsed, tiersUsed } = await mem.resolve(userPrompt, { sessionId: \"abc123\" })\n\n// Prepend to your system prompt\nconst response = await openai.chat.completions.create({\n  messages: [\n    { role: \"system\", content: `${text}\\n\\n${yourSystemPrompt}` },\n    { role: \"user\", content: userPrompt }\n  ]\n})\n```\n\n`fileStore` persists to disk as JSON files. For in-process use, tests, and edge functions: swap in `memoryStore()`.\n\n---\n\n## Benchmark\n\n500 memories (5 hot, 45 warm, 450 cold), 4000-token budget, billing-related prompt:\n\n```\nmemshot benchmark — 500 memories, 4000-token budget (gpt-tokenizer cl100k)\n─────────────────────────────────────────────────\n               naive   memshot   savings\nitems            500        62    -87.6%\ntokens used   36,552     3,957    -89.2%\n─────────────────────────────────────────────────\nreproduce: npm run benchmark\n```\n\nRun it yourself:\n\n```bash\ngit clone https://github.com/ayberkaya/memshot\ncd memshot && npm install\nnpm run benchmark\n```\n\n---\n\n## API Reference\n\n### `new Memory(config)`\n\n| Field | Type | Required | Default | Description |\n|-------|------|----------|---------|-------------|\n| `budget` | `number` | yes | — | Max tokens to inject per `resolve` call |\n| `store` | `Store` | yes | — | `fileStore(dir)` or `memoryStore()` |\n| `tokenizer` | `Tokenizer` | no | heuristic | Plug in `gpt-tokenizer` for exact counts |\n| `ledger` | `SessionLedger` | no | in-memory | Tracks `once: true` per session |\n\n### `mem.add(item, opts?)`\n\n```ts\nawait mem.add({ content, tier, triggers?, once?, tags? })\n\n// Dedup: skip if near-duplicate already exists (Jaccard ≥ 0.85)\nawait mem.add({ content, tier }, { dedupe: true })\nawait mem.add({ content, tier }, { dedupe: { threshold: 0.9, strategy: \"update\", scope: \"all\" } })\n```\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `content` | `string` | Text to store |\n| `tier` | `\"hot\" \\| \"warm\" \\| \"cold\"` | Selection tier |\n| `triggers` | `RegExp[]` | Warm: inject when one of these matches the prompt |\n| `once` | `boolean` | Hot: inject only once per `sessionId` |\n| `tags` | `string[]` | Arbitrary labels on retrieved items |\n\n### `mem.resolve(prompt, opts?)`\n\n```ts\nconst result = await mem.resolve(prompt, {\n  sessionId?: string,\n  now?: number,       // override Date.now() for tests\n  trace?: boolean     // include per-item score breakdown\n})\n```\n\nReturns `ResolveResult`:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `text` | `string` | Ready-to-inject block; prepend to system prompt |\n| `items` | `MemoryItem[]` | Selected items in tier order |\n| `tokensUsed` | `number` | Total tokens consumed |\n| `tiersUsed` | `{ hot, warm, cold: number }` | Items per tier |\n| `dropped` | `{ warm, cold: number }` | Items excluded by budget |\n| `trace?` | `ResolveTrace` | Per-item breakdown (only when `trace: true`) |\n\n### `mem.resolve` with trace\n\n```ts\nconst { trace } = await mem.resolve(prompt, { trace: true })\n\nfor (const entry of trace.entries) {\n  console.log(entry.tier, entry.included ? \"✓\" : \"✗\", entry.reason)\n  if (entry.scores) {\n    console.log(\"  bm25:\", entry.scores.bm25Normalized.toFixed(3),\n                \"frecency:\", entry.scores.frecencyNormalized.toFixed(3),\n                \"composite:\", entry.scores.composite.toFixed(3))\n  }\n}\n```\n\nTrace scope: items that entered budget allocation (all included + budget-dropped). Items filtered before scoring (unseen `once:true` hot, trigger-miss warm) are not yet traced.\n\n### `mem.stats()`\n\n```ts\nconst stats = await mem.stats()\n// {\n//   total: 500,\n//   byTier: { hot: 5, warm: 45, cold: 450 },\n//   tokens: { total: 52000, byTier: {...}, average: 104 },\n//   oldest: { id: \"...\", createdAt: 1719000000000 },\n//   newest: { id: \"...\", createdAt: 1719400000000 },\n//   cold: { totalAccesses: 1230, averageAccessCount: 2.7 }\n// }\n```\n\n### `mem.update(id, patch)`\n\n```ts\nawait mem.update(id, { content: \"Updated decision: defer until Q4.\" })\nawait mem.update(id, { tier: \"hot\", once: true })\n```\n\nPatchable fields: `content`, `tier`, `tags`, `triggers`, `once`. Engine-managed fields (`createdAt`, `accessCount`, `lastAccessedAt`) are excluded to preserve frecency integrity.\n\n### `mem.delete(id)` / `mem.clear()`\n\n```ts\nawait mem.delete(itemId)\nawait mem.clear()\n```\n\n---\n\n## CLI\n\n```\n$ npx memshot add \"User prefers TypeScript strict mode.\" --tier hot\nadded  1782466602860-g6e4cu  [hot]\n\n$ npx memshot add \"Billing: ship Stripe first, metering later.\" --tier warm --trigger \"/billing/i\"\nadded  1782466603120-h7f2ab  [warm]\n\n$ npx memshot list\nID                    TIER    TOKENS   CONTENT\n────────────────────────────────────────────────────────────────────────────────\n1782466602860-g6e4cu  hot     6        User prefers TypeScript strict mode.\n1782466603120-h7f2ab  warm    8        Billing: ship Stripe first, metering later.\n\n$ npx memshot resolve \"what are the billing rules?\" --budget 4000 --trace\ntokens used: 14 / 4000\ntiers:      hot=1 warm=1 cold=0\ndropped:    warm=0 cold=0\n\nselected:\n  1782466602860-g6e4cu  [hot]   User prefers TypeScript strict mode.\n  1782466603120-h7f2ab  [warm]  Billing: ship Stripe first, metering later.\n\nID                    TIER    INC  TOKENS   COMPOSITE   REASON\n──────────────────────────────────────────────────────────────────────────────────────────\n1782466602860-g6e4cu  hot     ✓    6        —           always injected\n1782466603120-h7f2ab  warm    ✓    8        —           trigger matched, fit budget\n\n$ npx memshot stats\ntotal: 2 memories\n\n  hot   1 items    6 tokens\n  warm  1 items    8 tokens\n  cold  0 items    0 tokens\n\ntokens:  total=14  avg=7.0\noldest:  1782466602860-g6e4cu  (2026-06-26T...)\nnewest:  1782466603120-h7f2ab  (2026-06-26T...)\n```\n\n`--trace` prints a table with id, tier, ✓/✗, tokens, composite score, and reason for every considered item.\n\nPersistence: `MEMSHOT_DIR` env var (default `./.memshot`).\n\n---\n\n## Web playground\n\n**[Live demo →](https://ayberkaya.github.io/memshot/playground/)**\n\nOr open `playground/index.html` locally — no build step, no server, no npm install.\n\n---\n\n## Integration recipes\n\n### OpenAI\n\n```ts\nimport OpenAI from \"openai\"\nimport { Memory, fileStore } from \"@ayberkaya/memshot\"\n\nconst mem = new Memory({ budget: 4000, store: fileStore(\"./memories\") })\nconst openai = new OpenAI()\n\nasync function chat(userMessage: string, sessionId: string) {\n  const { text } = await mem.resolve(userMessage, { sessionId })\n  return openai.chat.completions.create({\n    model: \"gpt-4o\",\n    messages: [\n      { role: \"system\", content: `${text}\\n\\nYou are a helpful assistant.` },\n      { role: \"user\", content: userMessage }\n    ]\n  })\n}\n```\n\n### Anthropic\n\n```ts\nimport Anthropic from \"@anthropic-ai/sdk\"\nimport { Memory, fileStore } from \"@ayberkaya/memshot\"\n\nconst mem = new Memory({ budget: 4000, store: fileStore(\"./memories\") })\nconst anthropic = new Anthropic()\n\nasync function chat(userMessage: string, sessionId: string) {\n  const { text } = await mem.resolve(userMessage, { sessionId })\n  return anthropic.messages.create({\n    model: \"claude-sonnet-4-6\",\n    max_tokens: 1024,\n    system: `${text}\\n\\nYou are a helpful assistant.`,\n    messages: [{ role: \"user\", content: userMessage }]\n  })\n}\n```\n\n### LangChain\n\n```ts\nimport { ChatOpenAI } from \"@langchain/openai\"\nimport { SystemMessage, HumanMessage } from \"@langchain/core/messages\"\nimport { Memory, fileStore } from \"@ayberkaya/memshot\"\n\nconst mem = new Memory({ budget: 4000, store: fileStore(\"./memories\") })\nconst model = new ChatOpenAI({ model: \"gpt-4o\" })\n\nasync function chat(userMessage: string, sessionId: string) {\n  const { text } = await mem.resolve(userMessage, { sessionId })\n  return model.invoke([\n    new SystemMessage(`${text}\\n\\nYou are a helpful assistant.`),\n    new HumanMessage(userMessage)\n  ])\n}\n```\n\n---\n\n## Adapters\n\n### Express\n\n```ts\nimport { memshotMiddleware } from \"@ayberkaya/memshot/adapters/express\"\n\napp.use(memshotMiddleware(mem, {\n  getPrompt: (req) => req.body.messages.at(-1)?.content ?? \"\",\n  getSessionId: (req) => req.headers[\"x-session-id\"] ?? \"\"\n}))\n\napp.post(\"/chat\", (req, res) => {\n  const { text } = req.memshot  // already resolved\n  // use text as system prompt prefix\n})\n```\n\n### Next.js route handler\n\n```ts\nimport { withMemshot } from \"@ayberkaya/memshot/adapters/next\"\n\nexport const POST = withMemshot(mem, async (req, { memshot }) => {\n  const systemPrefix = memshot.text\n  return Response.json({ ok: true })\n})\n```\n\n### Claude Code hook (UserPromptSubmit)\n\n```ts\nimport { createClaudeHook } from \"@ayberkaya/memshot/adapters/claude-hook\"\n\nconst hook = createClaudeHook(mem)\nawait hook.run()\n```\n\nRegister in `.claude/settings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      { \"type\": \"command\", \"command\": \"node ./hooks/memory.js\" }\n    ]\n  }\n}\n```\n\n---\n\n## Why not X?\n\nThis table is honest. \"~\" means partial or depends on configuration.\n\n| | **memshot** | **mem0** | **basic-memory** | **Zep** |\n|---|---|---|---|---|\n| Zero runtime deps | ✓ | ✗ | ✗ | ✗ |\n| Token-budget-aware | ✓ | ✗ | ✗ | ? |\n| No vector DB | ✓ | ✗ † | ✓ | ✗ |\n| TypeScript-native | ✓ | ✗ | ✗ | ✗ |\n| No server needed | ✓ | ~ ‡ | ✓ * | ✗ |\n| Edge / Serverless | ✓ | ✗ | ✗ | ✓ |\n\n† mem0 OSS uses ChromaDB (in-memory by default, optional persistence). Hosted platform uses Qdrant.<br>\n‡ mem0 OSS `Memory()` runs in-process with in-memory Chroma — no separate server for dev. Production hosted mode requires the mem0 Platform server.<br>\n\\* basic-memory's primary integration is `uvx basic-memory mcp` which runs a separate MCP server process. Direct Python import works without a server.<br>\n? Zep token budget control is not documented; marked unknown rather than ✗.<br>\n✗ Zep Cloud (`@getzep/zep-cloud`) is fetch-based and runs in edge environments; the self-hosted Zep server is a separate Go service.\n\n**memshot's actual differentiator:** it is the only library in this list that (1) runs in pure JavaScript with zero deps, (2) exposes an explicit token budget with greedy knapsack selection, and (3) ships a tier model that maps naturally to how agent context works. If you need semantic search, graph memory, or a managed service, the others are better tools.\n\n---\n\n[![Star History Chart](https://api.star-history.com/svg?repos=ayberkaya/memshot&type=Date)](https://star-history.com/#ayberkaya/memshot&Date)\n\n---\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n","readmeFilename":"README.md"}