linksee-memory 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # linksee-memory
2
2
 
3
- > **Your agent forgets everything when a session ends. Worse — it silently drifts from what you decided last week.**
3
+ > **Claude Code forgets everything when you start a new session. Your successor knows even less.**
4
4
  >
5
- > **Linksee Memory catches when your project drifts from its own decisions** — the option abandoned at a fork, the pipeline that quietly stalled, the code that contradicts what you agreed — and a re-injection guard re-surfaces the locked decision **before** the agent acts. Rules you've explicitly hardened get blocked.
5
+ > **Linksee Memory lets you hand a project over — to your next session, to Cursor or Codex, to the person after you — with the reasons attached.** Record a decision once (`remember({ content, anchor: {} })`) and it comes back *before* the agent acts on it: re-injected on session start and ahead of every Edit / Write / Bash that touches it. Change your mind on the record and it stops nagging; leave it and it keeps the next agent from repeating last week's mistake.
6
6
  >
7
- > Underneath sits a local-first cross-LLM memory MCP — one SQLite file that **Claude Code, Cursor, Windsurf, OpenAI Codex, and Gemini CLI** all read from. Not just "what happened" but **WHY**: 6-layer structured memory with precision recall and an AST-aware diff cache (50–99% token savings on re-reads).
7
+ > Underneath: **drift detection** across every project you run in parallel — which ones quietly left their goal (🔴), which were checked and hold (🔵), which nobody has looked at yet (⚫). One local SQLite file that **Claude Code, Cursor, Windsurf, OpenAI Codex and Gemini CLI** all read. Nothing leaves your machine.
8
8
  >
9
- > `npx -y linksee-memory setup` — one command, done.
9
+ > `npx -y linksee-memory setup` — one command, on in every repo.
10
10
 
11
11
  [![npm](https://img.shields.io/npm/v/linksee-memory.svg)](https://www.npmjs.com/package/linksee-memory)
12
12
  [![license](https://img.shields.io/npm/l/linksee-memory.svg)](./LICENSE)
@@ -19,6 +19,46 @@
19
19
  <!-- releases: tag-driven, published from CI via npm trusted publishing (OIDC).
20
20
  See .github/workflows/publish.yml — no token, no OTP, provenance attached automatically. -->
21
21
 
22
+ ## Questions this answers
23
+
24
+ These are the questions people actually type — measured, verbatim (2026-09-04). If yours is here, this is the answer.
25
+
26
+ **Claude Code forgets everything when I start a new session. How do I make it remember decisions across sessions?**
27
+ Run `npx -y linksee-memory setup`. Every new session opens with `recall()` — a brief of what needs attention, where you are, what's unresolved — and your locked decisions are re-injected before the agent acts. Nothing to re-explain.
28
+
29
+ **Claude Codeがセッションをまたぐと過去の経緯を忘れてしまいます。記憶を持たせる方法を教えてください。**
30
+ `npx -y linksee-memory setup` の一回で入ります。新しいセッションは `recall()` の brief(注意すべきこと・いまどこか・未解決)から始まり、固定した決定は行動の直前に再注入されます。説明し直す必要はありません。
31
+
32
+ **What is the best MCP server for persistent memory across AI coding agents?**
33
+ Honest answer: if "remember across sessions" is all you need, a `CLAUDE.md` is enough — we measured it. Linksee is for the three moments it isn't: right before a mistake, when you change your mind, and when you run many projects and need to see which one drifted.
34
+
35
+ **AIコーディングエージェント用の記憶MCPサーバーでおすすめはありますか?**
36
+ 正直に言うと、「セッションを跨いで覚える」だけなら `CLAUDE.md` で足ります(実測しました)。Linksee が要るのは、間違いかけたとき・方針を変えたとき・並行案件のどれが外れたかを知りたいとき、の3場面です。
37
+
38
+ **I want one shared memory between Claude Code, Cursor, and OpenAI Codex. What tools exist for that?**
39
+ One SQLite file. Claude Code, Cursor, Windsurf, OpenAI Codex and Gemini CLI all read and write the same memory — a decision made in one shows up in the others.
40
+
41
+ **Claude CodeとCursorとCodexで同じ記憶を共有したいのですが、どうすればいいですか?**
42
+ SQLite ファイル1つを全員で読みます。Claude Code / Cursor / Windsurf / Codex / Gemini CLI のどこで決めた決定も、他のエージェントの行動の直前に出てきます。
43
+
44
+ **My AI agent keeps re-implementing things we already decided against. How can I detect that a codebase has drifted from past decisions?**
45
+ Declare the decision (`remember({ content, anchor: { violation_signal: [...] } })`). The guard then warns — or blocks, if you hardened it — the moment an edit contradicts it, and `drift_status` shows every decision that reality has quietly left.
46
+
47
+ **過去に決めた設計方針とコードがずれていないかを検出できるツールはありますか?**
48
+ 決定を宣言しておくと(`remember({ content, anchor: {...} })`)、それに反する編集の直前に警告(hardened なら拒否)が出ます。`drift_status` は「宣言と現実がずれた決定」を証拠つきで一覧します。
49
+
50
+ **Is there a local-first, self-hosted alternative to Mem0 for agent memory?**
51
+ Yes. No account, no API key, no cloud — one local file, MIT licensed. `npx -y linksee-memory setup` and it's on.
52
+
53
+ **Mem0 vs Zep vs Letta for a coding agent's long-term memory — which should I pick?**
54
+ We installed them and ran one scenario across all of them. Storing and recalling a decision: everyone passes. The difference appears *before a mistake* and *when you change your mind* — Linksee is built for those two moments; the others leave them to you.
55
+
56
+ **How do I stop Claude Code from repeating the same mistake it made last week?**
57
+ Record it as a caveat (`remember({ content, layer: 'caveat' })`). Caveats are protected from forgetting and come back when the same ground is touched again — and if you anchor it, the guard stops the repeat before it lands.
58
+
59
+ **開発の意思決定履歴をMCPサーバーで残しておく定番のやり方はありますか?**
60
+ `remember({ content, anchor: {} })` の1回で、記録と強制が同時に入ります。`drift_status` がその台帳で、各決定が いま守られているか(🔵)・ずれているか(🔴)・誰も確かめていないか(⚫)を示します。
61
+
22
62
  ## 🪄 Three spells to remember
23
63
 
24
64
  | Say this | What happens |
@@ -95,6 +95,14 @@ export type ResolutionAction = 'fix' | 'supersede' | 'acknowledge' | 'dismiss';
95
95
  export interface ResolveInput {
96
96
  /** For dismiss: silence only this match term. Omitted → silence the anchor at the gate. */
97
97
  hit_term?: string;
98
+ /**
99
+ * For dismiss: false = close these drift edges as false positives but leave the gate
100
+ * watching. The default (true) also stops the gate. Two different verdicts hide behind
101
+ * "dismiss": "this detection was wrong" and "stop detecting this". Anchor #2 ("no destructive
102
+ * migrations") had a false positive from migrate.ts — the right answer was to close the edge
103
+ * and keep the gate, since "ALTER TABLE memories DROP" is exactly what it exists to catch.
104
+ */
105
+ gate?: boolean;
98
106
  anchor_id: number;
99
107
  action: ResolutionAction;
100
108
  rationale?: string;
@@ -543,12 +543,17 @@ export function resolveDrift(db, input) {
543
543
  // real detections survive.
544
544
  if (input.action === 'dismiss') {
545
545
  db.prepare("UPDATE drift_edges SET status = 'dismissed' WHERE anchor_id = ? AND status = 'open'").run(input.anchor_id);
546
- try {
547
- db.prepare(`INSERT INTO gate_dismissals (anchor_id, hit_term, rationale) VALUES (?, ?, ?)
548
- ON CONFLICT(anchor_id, COALESCE(hit_term, '')) DO UPDATE SET rationale = excluded.rationale`).run(input.anchor_id, input.hit_term ? input.hit_term.toLowerCase() : null, input.rationale ?? null);
549
- resolution.gate_dismissed = input.hit_term ? input.hit_term.toLowerCase() : 'all matches';
546
+ if (input.gate === false) {
547
+ resolution.gate_dismissed = 'none (edges closed; gate still watching)';
548
+ resolution.gate = false;
550
549
  }
551
- catch { /* pre-v16 DB → edges-only dismiss, as before */ }
550
+ else
551
+ try {
552
+ db.prepare(`INSERT INTO gate_dismissals (anchor_id, hit_term, rationale) VALUES (?, ?, ?)
553
+ ON CONFLICT(anchor_id, COALESCE(hit_term, '')) DO UPDATE SET rationale = excluded.rationale`).run(input.anchor_id, input.hit_term ? input.hit_term.toLowerCase() : null, input.rationale ?? null);
554
+ resolution.gate_dismissed = input.hit_term ? input.hit_term.toLowerCase() : 'all matches';
555
+ }
556
+ catch { /* pre-v16 DB → edges-only dismiss, as before */ }
552
557
  }
553
558
  // If action is 'fix', mark open edges as resolved
554
559
  if (input.action === 'fix') {
@@ -280,13 +280,14 @@ const TOOLS = [
280
280
  },
281
281
  {
282
282
  name: 'resolve_drift',
283
- description: 'Record a resolution for a drifting anchor — the human feedback loop.\n\n6 actions:\n• fix — "we fixed the code/reality to match intent" → state becomes aligned\n• supersede — "intent evolved, this is the new direction" → state becomes aligned\n• acknowledge — "we know, parking it for now" → state becomes held (with optional review date)\n• dismiss — "false positive, not actually drifting" → edges dismissed AND the gate stops firing on it (pass hit_term to silence just that word)\n• harden — "re-injected but still violated, enforce it" → card_policy.gate_mode=hard (PreToolUse will BLOCK)\n• soften — "back off to a warning" → gate_mode=soft\n\nWHEN TO CALL:\n• After drift_status shows 🔴 drift or 🟡 review items\n• When the user says "that\'s fixed" / "ignore that" / "we changed direction"\n• When acknowledging a known gap with a review date',
283
+ description: 'Record a resolution for a drifting anchor — the human feedback loop.\n\n6 actions:\n• fix — "we fixed the code/reality to match intent" → state becomes aligned\n• supersede — "intent evolved, this is the new direction" → state becomes aligned\n• acknowledge — "we know, parking it for now" → state becomes held (with optional review date)\n• dismiss — "false positive, not actually drifting" → edges dismissed AND the gate stops firing on it (hit_term: just that word; gate:false: close the edges but keep the gate watching)\n• harden — "re-injected but still violated, enforce it" → card_policy.gate_mode=hard (PreToolUse will BLOCK)\n• soften — "back off to a warning" → gate_mode=soft\n\nWHEN TO CALL:\n• After drift_status shows 🔴 drift or 🟡 review items\n• When the user says "that\'s fixed" / "ignore that" / "we changed direction"\n• When acknowledging a known gap with a review date',
284
284
  inputSchema: {
285
285
  type: 'object',
286
286
  properties: {
287
287
  anchor_id: { type: 'number', description: 'The drift_anchor ID to resolve' },
288
288
  action: { type: 'string', enum: ['fix', 'supersede', 'acknowledge', 'dismiss', 'harden', 'soften', 'surface'], description: "Resolution action. With candidate_id (an orphaned proposal): 'surface' keeps it visible for the human, 'dismiss' retires it." },
289
289
  candidate_id: { type: 'number', description: 'Resolve an orphaned proposal (from recall({ dream: true })) instead of an anchor: pass its candidate_id with action surface | dismiss and a rationale that references the North Star. (Absorbs resolve_proposal.)' },
290
+ gate: { type: 'boolean', default: true, description: "For dismiss: false = these detections were false positives, close them, but KEEP the gate watching this anchor (its signals still fire). Use when the anchor's rule is right and only this evidence was wrong — e.g. a lexical hit in a file that is not what the rule is about. Default true also silences the gate (whole anchor, or just hit_term)." },
290
291
  rationale: { type: 'string', description: 'Why this resolution (recorded for audit trail)' },
291
292
  review_after: { type: 'string', description: 'For acknowledge: ISO date to re-check (e.g. "2026-07-04")' },
292
293
  superseded_by: { type: 'number', description: 'For supersede: the new anchor ID that replaces this one' },
@@ -1751,6 +1752,7 @@ function handleResolveDrift(args) {
1751
1752
  review_after: args.review_after,
1752
1753
  superseded_by: args.superseded_by,
1753
1754
  hit_term: args.hit_term,
1755
+ gate: args.gate,
1754
1756
  });
1755
1757
  return JSON.stringify(result);
1756
1758
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "linksee-memory",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "mcpName": "io.github.michielinksee/linksee-memory",
5
- "description": "Local-first agent memory MCP — cross-agent brain with drift detection, 6-layer structured memory + token-saving file diff cache",
5
+ "description": "Hand a project over — to your next session, to Cursor/Codex/Gemini, to your successor — with the reasons attached. Local-first memory MCP for coding agents: decisions re-injected before the agent acts, drift detection across every project you run. One SQLite file, nothing leaves your machine.",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "linksee-memory": "dist/mcp/server.js",
@@ -39,14 +39,24 @@
39
39
  "mcp",
40
40
  "model-context-protocol",
41
41
  "memory",
42
- "agent",
43
42
  "agent-memory",
43
+ "persistent-memory",
44
+ "long-term-memory",
45
+ "session-memory",
46
+ "handover",
47
+ "hand-off",
48
+ "decision-log",
49
+ "drift-detection",
50
+ "guardrails",
44
51
  "claude",
45
52
  "claude-code",
46
53
  "cursor",
54
+ "windsurf",
55
+ "codex",
56
+ "gemini-cli",
47
57
  "chatgpt",
48
- "sqlite",
49
58
  "local-first",
59
+ "sqlite",
50
60
  "token-savings",
51
61
  "cross-agent"
52
62
  ],