linksee-memory 0.15.1 → 0.16.0
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 +44 -4
- package/dist/lib/distill-triage.d.ts +29 -0
- package/dist/lib/distill-triage.js +104 -0
- package/dist/lib/guard.js +2 -1
- package/dist/mcp/server.js +20 -3
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# linksee-memory
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Claude Code forgets everything when you start a new session. Your successor knows even less.**
|
|
4
4
|
>
|
|
5
|
-
> **Linksee Memory
|
|
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
|
|
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,
|
|
9
|
+
> `npx -y linksee-memory setup` — one command, on in every repo.
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/linksee-memory)
|
|
12
12
|
[](./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 |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export declare const SETTLE_SECONDS: number;
|
|
3
|
+
export declare const STALE_DAYS = 90;
|
|
4
|
+
export declare const SHORT_CHARS = 40;
|
|
5
|
+
export type TriageVerdict = 'auto-noise' | 'auto-stale' | 'keep';
|
|
6
|
+
export declare function classifyRaw(what: string, ageDays: number, accessCount: number): {
|
|
7
|
+
verdict: TriageVerdict;
|
|
8
|
+
reason: string;
|
|
9
|
+
};
|
|
10
|
+
export interface TriageReport {
|
|
11
|
+
scanned: number;
|
|
12
|
+
noise: number;
|
|
13
|
+
stale: number;
|
|
14
|
+
remaining: number;
|
|
15
|
+
dryRun: boolean;
|
|
16
|
+
samples: {
|
|
17
|
+
noise: string[];
|
|
18
|
+
stale: string[];
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Walk every needs_distill memory outside the settle window and archive the ones a rule can
|
|
23
|
+
* settle. Idempotent — archived rows no longer match the WHERE clause.
|
|
24
|
+
*/
|
|
25
|
+
export declare function triageDistillQueue(db: Database.Database, opts?: {
|
|
26
|
+
dryRun?: boolean;
|
|
27
|
+
}): TriageReport;
|
|
28
|
+
/** Count of rows still awaiting a human/agent rewrite (outside the settle window). */
|
|
29
|
+
export declare function distillPending(db: Database.Database): number;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// distill-triage — keep the distill queue honest without an LLM.
|
|
2
|
+
//
|
|
3
|
+
// The Stop hook stores raw user utterances with needs_distill:true (no LLM runs in the hook
|
|
4
|
+
// path — anchor #70), and the agent is meant to rewrite them via recall({ dream: true }). On
|
|
5
|
+
// the author's machine that queue reached 389 and nobody drained it, because most of it was
|
|
6
|
+
// never a decision: "OK. Aからいこう", a pasted file path, "はい。そうしましょう". Asking an
|
|
7
|
+
// agent to distill those is asking it to find meaning that is not there.
|
|
8
|
+
//
|
|
9
|
+
// So: classify before asking. Two verdicts need no intelligence, only a rule —
|
|
10
|
+
// auto-noise the utterance carries no decision object (too short, a bare acknowledgement or
|
|
11
|
+
// imperative, a path/URL/attachment reference)
|
|
12
|
+
// auto-stale never recalled and older than STALE_DAYS — whatever it was, nobody came back
|
|
13
|
+
//
|
|
14
|
+
// Both are archived, never deleted: type → note, state → superseded, distill_verdict records
|
|
15
|
+
// why. needs_distill stays TRUE — anchor #70 says the flag must not be dropped, and it stays
|
|
16
|
+
// truthful: no agent ever rewrote this row. The queue, the pending count and the boot digest
|
|
17
|
+
// exclude rows that carry a verdict. Layer and protection are untouched (a caveat stays a
|
|
18
|
+
// caveat — it just stops being asked about). Reversible: delete distill_verdict from the JSON.
|
|
19
|
+
//
|
|
20
|
+
// Measured before shipping (2026-09-07, 389 raw): noise 88, stale 51, queue 250 — and the
|
|
21
|
+
// noise sample was read by a human before the rule was trusted.
|
|
22
|
+
export const SETTLE_SECONDS = 30 * 60; // a session still in motion is not triaged (matches dream)
|
|
23
|
+
export const STALE_DAYS = 90;
|
|
24
|
+
export const SHORT_CHARS = 40;
|
|
25
|
+
const ACK_ONLY = /^(ok|okay|はい|そうだね|そうですね|うん|了解|いいね|ありがとう|お願い|進めて|次いこう|やろう|それで|続けて|よし|では|じゃあ|なるほど)[\s。、.,!!]*$/i;
|
|
26
|
+
const ACK_LEAD = /^(ok|okay|はい|そうだね|そうですね|うん|了解|いいね|ありがとう|よし|では|じゃあ|なるほど)[\s。、.,!!]+/i;
|
|
27
|
+
const BARE_REF = /^(@"|@[A-Za-z]:|"?[A-Za-z]:\\|https?:\/\/)/;
|
|
28
|
+
const DECISION_OBJECT = /決めた|採用|確定|却下|やめ|禁止|方針|に(する|しよう)|でいこう|で行こう|decid|chose|going with|switch(ing)? to|settled on|approved|instead/i;
|
|
29
|
+
export function classifyRaw(what, ageDays, accessCount) {
|
|
30
|
+
const w = (what ?? '').trim();
|
|
31
|
+
if (w.length < SHORT_CHARS)
|
|
32
|
+
return { verdict: 'auto-noise', reason: `shorter than ${SHORT_CHARS} chars` };
|
|
33
|
+
if (ACK_ONLY.test(w))
|
|
34
|
+
return { verdict: 'auto-noise', reason: 'acknowledgement only' };
|
|
35
|
+
if (BARE_REF.test(w) && w.length < 120)
|
|
36
|
+
return { verdict: 'auto-noise', reason: 'bare path/URL reference' };
|
|
37
|
+
if (ACK_LEAD.test(w) && !DECISION_OBJECT.test(w) && w.length < 80)
|
|
38
|
+
return { verdict: 'auto-noise', reason: 'acknowledgement with no decision object' };
|
|
39
|
+
if (ageDays > STALE_DAYS && accessCount === 0)
|
|
40
|
+
return { verdict: 'auto-stale', reason: `never recalled in ${STALE_DAYS}+ days` };
|
|
41
|
+
return { verdict: 'keep', reason: '' };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Walk every needs_distill memory outside the settle window and archive the ones a rule can
|
|
45
|
+
* settle. Idempotent — archived rows no longer match the WHERE clause.
|
|
46
|
+
*/
|
|
47
|
+
export function triageDistillQueue(db, opts = {}) {
|
|
48
|
+
const now = Math.floor(Date.now() / 1000);
|
|
49
|
+
const rows = db
|
|
50
|
+
.prepare(`SELECT id, content, access_count, created_at FROM memories
|
|
51
|
+
WHERE json_valid(content)
|
|
52
|
+
AND json_extract(content, '$.needs_distill') = 1
|
|
53
|
+
AND json_extract(content, '$.distill_verdict') IS NULL
|
|
54
|
+
AND created_at < ?`)
|
|
55
|
+
.all(now - SETTLE_SECONDS);
|
|
56
|
+
const report = { scanned: rows.length, noise: 0, stale: 0, remaining: 0, dryRun: !!opts.dryRun, samples: { noise: [], stale: [] } };
|
|
57
|
+
const update = db.prepare('UPDATE memories SET content = ? WHERE id = ?');
|
|
58
|
+
const apply = db.transaction(() => {
|
|
59
|
+
for (const r of rows) {
|
|
60
|
+
let c;
|
|
61
|
+
try {
|
|
62
|
+
c = JSON.parse(r.content);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
report.remaining++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const what = String(c.what ?? '');
|
|
69
|
+
const ageDays = (now - r.created_at) / 86400;
|
|
70
|
+
const { verdict, reason } = classifyRaw(what, ageDays, r.access_count ?? 0);
|
|
71
|
+
if (verdict === 'keep') {
|
|
72
|
+
report.remaining++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (verdict === 'auto-noise')
|
|
76
|
+
report.noise++;
|
|
77
|
+
else
|
|
78
|
+
report.stale++;
|
|
79
|
+
const bucket = report.samples[verdict === 'auto-noise' ? 'noise' : 'stale'];
|
|
80
|
+
if (bucket.length < 5)
|
|
81
|
+
bucket.push(what.replace(/\s+/g, ' ').slice(0, 80));
|
|
82
|
+
if (opts.dryRun)
|
|
83
|
+
continue;
|
|
84
|
+
c.distill_verdict = verdict;
|
|
85
|
+
c.distill_reason = reason;
|
|
86
|
+
c.type = 'note';
|
|
87
|
+
c.state = 'superseded';
|
|
88
|
+
update.run(JSON.stringify(c), r.id);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
apply();
|
|
92
|
+
return report;
|
|
93
|
+
}
|
|
94
|
+
/** Count of rows still awaiting a human/agent rewrite (outside the settle window). */
|
|
95
|
+
export function distillPending(db) {
|
|
96
|
+
const now = Math.floor(Date.now() / 1000);
|
|
97
|
+
const r = db
|
|
98
|
+
.prepare(`SELECT COUNT(*) AS c FROM memories
|
|
99
|
+
WHERE json_valid(content) AND json_extract(content, '$.needs_distill') = 1
|
|
100
|
+
AND json_extract(content, '$.distill_verdict') IS NULL AND created_at < ?`)
|
|
101
|
+
.get(now - SETTLE_SECONDS);
|
|
102
|
+
return r?.c ?? 0;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=distill-triage.js.map
|
package/dist/lib/guard.js
CHANGED
|
@@ -264,6 +264,7 @@ export function buildBootDigest(db, opts = {}) {
|
|
|
264
264
|
try {
|
|
265
265
|
distill = db.prepare(`SELECT COUNT(*) AS n FROM memories
|
|
266
266
|
WHERE layer IN ('learning', 'caveat') AND json_valid(content)
|
|
267
|
+
AND json_extract(content, '$.distill_verdict') IS NULL
|
|
267
268
|
AND (json_extract(content, '$.needs_distill') = 1
|
|
268
269
|
OR json_extract(content, '$.why') = 'Decision detected by pattern match — may need agent enrichment'
|
|
269
270
|
OR json_extract(content, '$.why') = 'User-stated warning/prohibition — auto-extracted by caveat pattern match')`).get().n;
|
|
@@ -285,7 +286,7 @@ export function buildBootDigest(db, opts = {}) {
|
|
|
285
286
|
parts.push(`• ${f.statement ?? f.rationale}`);
|
|
286
287
|
}
|
|
287
288
|
if (distill > 0) {
|
|
288
|
-
parts.push('', `🧪 ${distill} auto-captured memories
|
|
289
|
+
parts.push('', `🧪 ${distill} auto-captured memories still need a rewrite (rules already settled the acknowledgements and stale ones). Do 3: recall({ dream: true }) → remember({ memory_id, content }) with "distilled": true.`);
|
|
289
290
|
}
|
|
290
291
|
if (anchors.length > 0)
|
|
291
292
|
parts.push('', 'Honor these unless you explicitly supersede them (resolve_drift action=supersede).');
|
package/dist/mcp/server.js
CHANGED
|
@@ -24,6 +24,7 @@ import { getTruthView, getDecisionDetail, resolveDrift } from '../lib/truth-engi
|
|
|
24
24
|
import { declareAnchor, setNodeFields } from '../lib/drift-anchors.js';
|
|
25
25
|
import { getReinjectionFriction, setGateMode } from '../lib/guard.js';
|
|
26
26
|
import { logAnchorTouch } from '../lib/anchor-touch.js';
|
|
27
|
+
import { triageDistillQueue, distillPending } from '../lib/distill-triage.js';
|
|
27
28
|
import { whereAmI, listMapProjects } from '../lib/map-view.js';
|
|
28
29
|
import { readFileSync, existsSync } from 'node:fs';
|
|
29
30
|
import { fileURLToPath } from 'node:url';
|
|
@@ -90,6 +91,13 @@ setTimeout(() => {
|
|
|
90
91
|
if (shouldRun) {
|
|
91
92
|
runConsolidate(db, { scope: 'all', min_age_days: 7 });
|
|
92
93
|
process.stderr.write('[linksee-memory] auto-consolidate complete\n');
|
|
94
|
+
// Triage the distill queue by rule before any agent is asked to think about it.
|
|
95
|
+
try {
|
|
96
|
+
const t = triageDistillQueue(db);
|
|
97
|
+
if (t.noise + t.stale > 0)
|
|
98
|
+
process.stderr.write(`[linksee-memory] distill triage: archived ${t.noise} noise + ${t.stale} stale, ${t.remaining} remain\n`);
|
|
99
|
+
}
|
|
100
|
+
catch { /* never block startup */ }
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
103
|
catch { /* non-fatal */ }
|
|
@@ -195,6 +203,7 @@ const TOOLS = [
|
|
|
195
203
|
where: { description: 'Locate on the Current Truth Map. A topic string, or true to auto-locate from the files you edited recently. Returns your node, journey stage, blast radius, and the decision behind it.', anyOf: [{ type: 'string' }, { type: 'boolean' }] },
|
|
196
204
|
project: { type: 'string', description: 'With where: the Map project slug, when several maps are imported.' },
|
|
197
205
|
dream: { type: 'boolean', default: false, description: 'Return the full triage set: North Star, orphaned proposals (each with candidate_id), distill_queue, friction. Resolve proposals with resolve_drift({ candidate_id, action }); rewrite distill items with remember({ memory_id, content }).' },
|
|
206
|
+
distill: { type: 'number', description: 'With dream: how many raw memories to return for rewriting (default 8, max 25). The queue is value-ordered; rule-verdicted items never appear.' },
|
|
198
207
|
overview: { type: 'boolean', default: false, description: 'Return the entity list instead of the session brief when no query is given.' },
|
|
199
208
|
kind: { type: 'string', enum: ['person', 'company', 'project', 'concept', 'file', 'other'], description: 'For overview mode: filter by entity kind.' },
|
|
200
209
|
min_memories: { type: 'number', description: 'For overview mode: minimum memory count. Default 1.', default: 1 },
|
|
@@ -1431,7 +1440,7 @@ async function handleSessionBrief() {
|
|
|
1431
1440
|
north_star: d.north_star ? { id: d.north_star.id, statement: String(d.north_star.statement).slice(0, 160) } : null,
|
|
1432
1441
|
proposals: d.total ?? 0,
|
|
1433
1442
|
proposals_top: (d.candidates ?? []).slice(0, 3).map((c) => ({ candidate_id: c.candidate_id ?? c.id, statement: String(c.statement ?? c.target_statement ?? '').slice(0, 120) })),
|
|
1434
|
-
distill_queue: d.distill_total ?? 0,
|
|
1443
|
+
distill_queue: d.distill_total ?? 0, // rows still needing a rewrite; rule-verdicted rows are not counted
|
|
1435
1444
|
friction: d.friction_total ?? 0,
|
|
1436
1445
|
};
|
|
1437
1446
|
}
|
|
@@ -1855,6 +1864,7 @@ function handleDream(args) {
|
|
|
1855
1864
|
// utterances (no LLM there); the agent rewrites them here into clean what/why via
|
|
1856
1865
|
// remember(memory_id, content). Matches needs_distill (new) AND the legacy hardcoded
|
|
1857
1866
|
// why-strings so the existing backlog is drainable without a backfill write.
|
|
1867
|
+
const distillLimit = Math.max(1, Math.min(25, Number(args?.distill ?? 8) || 8));
|
|
1858
1868
|
let distillQueue = [];
|
|
1859
1869
|
try {
|
|
1860
1870
|
const rows = db.prepare(`
|
|
@@ -1865,10 +1875,15 @@ function handleDream(args) {
|
|
|
1865
1875
|
-- settle window: the Stop hook extracts every turn, so the newest rows belong to a
|
|
1866
1876
|
-- session still in motion. Don't ask the agent to distill the conversation it is in.
|
|
1867
1877
|
AND m.created_at < unixepoch() - 1800
|
|
1878
|
+
AND json_extract(m.content, '$.distill_verdict') IS NULL
|
|
1868
1879
|
AND (json_extract(m.content, '$.needs_distill') = 1
|
|
1869
1880
|
OR json_extract(m.content, '$.why') = 'Decision detected by pattern match — may need agent enrichment'
|
|
1870
1881
|
OR json_extract(m.content, '$.why') = 'User-stated warning/prohibition — auto-extracted by caveat pattern match')
|
|
1871
|
-
ORDER BY m.
|
|
1882
|
+
ORDER BY (m.layer = 'caveat') DESC,
|
|
1883
|
+
(e.name <> 'Local') DESC,
|
|
1884
|
+
length(json_extract(m.content, '$.what')) DESC,
|
|
1885
|
+
m.created_at DESC
|
|
1886
|
+
LIMIT ${distillLimit}
|
|
1872
1887
|
`).all();
|
|
1873
1888
|
distillQueue = rows.map((r) => {
|
|
1874
1889
|
let c = {};
|
|
@@ -1898,7 +1913,9 @@ function handleDream(args) {
|
|
|
1898
1913
|
friction,
|
|
1899
1914
|
friction_total: friction.length,
|
|
1900
1915
|
distill_queue: distillQueue,
|
|
1901
|
-
distill_total:
|
|
1916
|
+
distill_total: distillPending(db),
|
|
1917
|
+
distill_shown: distillQueue.length,
|
|
1918
|
+
distill_hint: 'Ordered by value (caveats first, named entities, longer text). Pass distill: N (max 25) for a longer drain. Rules already set a distill_verdict on acknowledgements, bare paths and 90-day-old never-recalled items — you only see what needs judgement.',
|
|
1902
1919
|
message: friction.length > 0
|
|
1903
1920
|
? 'No North Star declared yet — but the re-injection layer surfaced friction below: anchors being violated despite being re-surfaced. Declare a North Star (declare_anchor node_type:"north_star"), and act on the friction items.'
|
|
1904
1921
|
: 'No North Star declared yet. Declare one with declare_anchor(node_type: "north_star") before dreaming.',
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linksee-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"mcpName": "io.github.michielinksee/linksee-memory",
|
|
5
|
-
"description": "Local-first
|
|
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
|
],
|