linksee-memory 0.12.0 → 0.12.1

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.
@@ -479,15 +479,15 @@ function appendAxisFilters(sql, params, filters) {
479
479
  return sql;
480
480
  }
481
481
  function runFtsQuery(query, layer, limit, axis) {
482
- let sql = `
483
- SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
484
- m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
485
- m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
486
- bm25(memories_fts) as bm25_score
487
- FROM memories_fts
488
- JOIN memories m ON m.id = memories_fts.rowid
489
- JOIN entities e ON e.id = m.entity_id
490
- WHERE memories_fts MATCH ?
482
+ let sql = `
483
+ SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
484
+ m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
485
+ m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
486
+ bm25(memories_fts) as bm25_score
487
+ FROM memories_fts
488
+ JOIN memories m ON m.id = memories_fts.rowid
489
+ JOIN entities e ON e.id = m.entity_id
490
+ WHERE memories_fts MATCH ?
491
491
  `;
492
492
  const params = [query];
493
493
  if (layer) {
@@ -501,14 +501,14 @@ function runFtsQuery(query, layer, limit, axis) {
501
501
  return db.prepare(sql).all(...params);
502
502
  }
503
503
  function runLikeQuery(query, entityName, layer, limit, axis) {
504
- let sql = `
505
- SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
506
- m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
507
- m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
508
- 0 as bm25_score
509
- FROM memories m
510
- JOIN entities e ON e.id = m.entity_id
511
- WHERE 1=1
504
+ let sql = `
505
+ SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
506
+ m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
507
+ m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
508
+ 0 as bm25_score
509
+ FROM memories m
510
+ JOIN entities e ON e.id = m.entity_id
511
+ WHERE 1=1
512
512
  `;
513
513
  const params = [];
514
514
  if (entityName) {
@@ -839,8 +839,8 @@ function handleForget(args) {
839
839
  }
840
840
  // Auto-sweep — also respect pin (importance >= 0.9) as protection
841
841
  const rows = db
842
- .prepare(`SELECT id, layer, importance, access_count, last_accessed_at, protected, altitude
843
- FROM memories
842
+ .prepare(`SELECT id, layer, importance, access_count, last_accessed_at, protected, altitude
843
+ FROM memories
844
844
  WHERE protected = 0 AND importance < 0.9`)
845
845
  .all();
846
846
  const now = Math.floor(Date.now() / 1000);
@@ -889,17 +889,17 @@ function handleConsolidate(args) {
889
889
  // read-only audit: count candidates using the same rules.
890
890
  const now = Math.floor(Date.now() / 1000);
891
891
  const ageCutoff = now - (args.min_age_days ?? 7) * 86400;
892
- const candidates = db.prepare(`
893
- SELECT m.entity_id, e.name as entity_name, m.layer, COUNT(*) as c
894
- FROM memories m
895
- JOIN entities e ON e.id = m.entity_id
896
- WHERE m.protected = 0
897
- AND m.importance < 0.9
898
- AND m.layer IN ('context', 'emotion', 'implementation')
899
- AND m.created_at <= ?
900
- GROUP BY m.entity_id, m.layer
901
- HAVING c >= 2
902
- ORDER BY c DESC
892
+ const candidates = db.prepare(`
893
+ SELECT m.entity_id, e.name as entity_name, m.layer, COUNT(*) as c
894
+ FROM memories m
895
+ JOIN entities e ON e.id = m.entity_id
896
+ WHERE m.protected = 0
897
+ AND m.importance < 0.9
898
+ AND m.layer IN ('context', 'emotion', 'implementation')
899
+ AND m.created_at <= ?
900
+ GROUP BY m.entity_id, m.layer
901
+ HAVING c >= 2
902
+ ORDER BY c DESC
903
903
  `).all(ageCutoff);
904
904
  const totalReplaced = candidates.reduce((s, c) => s + c.c, 0);
905
905
  return JSON.stringify({
@@ -973,19 +973,19 @@ function handleListEntities(args) {
973
973
  const minMemories = Math.max(1, Number(args?.min_memories ?? 1));
974
974
  const limit = Math.max(1, Math.min(200, Number(args?.limit ?? 30)));
975
975
  const offset = Math.max(0, Number(args?.offset ?? 0));
976
- let sql = `
977
- SELECT e.id, e.name, e.kind, e.canonical_key, e.momentum_score,
978
- e.updated_at, e.created_at,
979
- COUNT(m.id) as memory_count,
980
- MAX(m.last_accessed_at) as last_memory_access,
981
- SUM(CASE WHEN m.layer = 'goal' THEN 1 ELSE 0 END) as goal_count,
982
- SUM(CASE WHEN m.layer = 'caveat' THEN 1 ELSE 0 END) as caveat_count,
983
- SUM(CASE WHEN m.layer = 'learning' THEN 1 ELSE 0 END) as learning_count,
984
- SUM(CASE WHEN m.layer = 'implementation' THEN 1 ELSE 0 END) as impl_count,
985
- SUM(CASE WHEN m.importance >= 0.9 THEN 1 ELSE 0 END) as pinned_count
986
- FROM entities e
987
- LEFT JOIN memories m ON m.entity_id = e.id
988
- WHERE 1=1
976
+ let sql = `
977
+ SELECT e.id, e.name, e.kind, e.canonical_key, e.momentum_score,
978
+ e.updated_at, e.created_at,
979
+ COUNT(m.id) as memory_count,
980
+ MAX(m.last_accessed_at) as last_memory_access,
981
+ SUM(CASE WHEN m.layer = 'goal' THEN 1 ELSE 0 END) as goal_count,
982
+ SUM(CASE WHEN m.layer = 'caveat' THEN 1 ELSE 0 END) as caveat_count,
983
+ SUM(CASE WHEN m.layer = 'learning' THEN 1 ELSE 0 END) as learning_count,
984
+ SUM(CASE WHEN m.layer = 'implementation' THEN 1 ELSE 0 END) as impl_count,
985
+ SUM(CASE WHEN m.importance >= 0.9 THEN 1 ELSE 0 END) as pinned_count
986
+ FROM entities e
987
+ LEFT JOIN memories m ON m.entity_id = e.id
988
+ WHERE 1=1
989
989
  `;
990
990
  const params = [];
991
991
  if (kind) {
@@ -1037,29 +1037,29 @@ function handleRecallFile(args) {
1037
1037
  return JSON.stringify({ ok: true, count: 0, note: 'No edits found for that path substring.' });
1038
1038
  }
1039
1039
  // Daily breakdown
1040
- const daily = db.prepare(`
1041
- SELECT DATE(occurred_at, 'unixepoch') as day, operation, COUNT(*) as edits
1042
- FROM session_file_edits WHERE file_path LIKE ?
1043
- GROUP BY day, operation ORDER BY day
1040
+ const daily = db.prepare(`
1041
+ SELECT DATE(occurred_at, 'unixepoch') as day, operation, COUNT(*) as edits
1042
+ FROM session_file_edits WHERE file_path LIKE ?
1043
+ GROUP BY day, operation ORDER BY day
1044
1044
  `).all(`%${sub}%`);
1045
1045
  // Distinct context_snippets (intents) — deduped, ordered by recency
1046
- const intents = db.prepare(`
1047
- SELECT DISTINCT context_snippet, MAX(occurred_at) as last_at, COUNT(*) as freq
1048
- FROM session_file_edits
1049
- WHERE file_path LIKE ? AND context_snippet IS NOT NULL AND LENGTH(context_snippet) > 20
1050
- GROUP BY context_snippet
1051
- ORDER BY last_at DESC
1052
- LIMIT ?
1046
+ const intents = db.prepare(`
1047
+ SELECT DISTINCT context_snippet, MAX(occurred_at) as last_at, COUNT(*) as freq
1048
+ FROM session_file_edits
1049
+ WHERE file_path LIKE ? AND context_snippet IS NOT NULL AND LENGTH(context_snippet) > 20
1050
+ GROUP BY context_snippet
1051
+ ORDER BY last_at DESC
1052
+ LIMIT ?
1053
1053
  `).all(`%${sub}%`, maxIntents);
1054
1054
  // Linked memories
1055
- const memories = db.prepare(`
1056
- SELECT DISTINCT m.id, m.layer, m.content, m.importance, e.name as entity_name
1057
- FROM session_file_edits sfe
1058
- JOIN memories m ON m.id = sfe.memory_id
1059
- JOIN entities e ON e.id = m.entity_id
1060
- WHERE sfe.file_path LIKE ?
1061
- ORDER BY m.importance DESC
1062
- LIMIT 20
1055
+ const memories = db.prepare(`
1056
+ SELECT DISTINCT m.id, m.layer, m.content, m.importance, e.name as entity_name
1057
+ FROM session_file_edits sfe
1058
+ JOIN memories m ON m.id = sfe.memory_id
1059
+ JOIN entities e ON e.id = m.entity_id
1060
+ WHERE sfe.file_path LIKE ?
1061
+ ORDER BY m.importance DESC
1062
+ LIMIT 20
1063
1063
  `).all(`%${sub}%`);
1064
1064
  // Distinct file paths matched (the substring may match multiple files)
1065
1065
  const paths = db.prepare(`SELECT file_path, COUNT(*) as edits FROM session_file_edits WHERE file_path LIKE ? GROUP BY file_path ORDER BY edits DESC`).all(`%${sub}%`);
@@ -1126,10 +1126,10 @@ async function handleConsolidateWithSampling(args) {
1126
1126
  const ageCutoff = Math.floor(Date.now() / 1000) - (typeof args?.min_age_days === 'number' ? args.min_age_days : 7) * 86400;
1127
1127
  const snapshot = new Map();
1128
1128
  const candidateRows = db
1129
- .prepare(`SELECT m.id, m.content, e.name as entity_name
1130
- FROM memories m JOIN entities e ON e.id = m.entity_id
1131
- WHERE m.protected = 0
1132
- AND m.layer IN ('context','emotion','implementation')
1129
+ .prepare(`SELECT m.id, m.content, e.name as entity_name
1130
+ FROM memories m JOIN entities e ON e.id = m.entity_id
1131
+ WHERE m.protected = 0
1132
+ AND m.layer IN ('context','emotion','implementation')
1133
1133
  AND m.created_at <= ?`)
1134
1134
  .all(ageCutoff);
1135
1135
  for (const r of candidateRows)
@@ -1352,8 +1352,8 @@ function resolveProjectFromRecentEdits() {
1352
1352
  if (projects.length <= 1)
1353
1353
  return { project: projects[0] };
1354
1354
  const rows = db
1355
- .prepare(`SELECT file_path FROM session_file_edits
1356
- WHERE occurred_at > unixepoch() - 86400
1355
+ .prepare(`SELECT file_path FROM session_file_edits
1356
+ WHERE occurred_at > unixepoch() - 86400
1357
1357
  ORDER BY occurred_at DESC LIMIT 50`)
1358
1358
  .all();
1359
1359
  if (rows.length === 0)
@@ -1504,25 +1504,25 @@ function handleFlagProposals(args) {
1504
1504
  const sessionContext = args.session_context ?? 'conversation session';
1505
1505
  const PROPOSAL_TAG = 'orphaned_proposal';
1506
1506
  const now = Math.floor(Date.now() / 1000);
1507
- const insertAnchor = db.prepare(`
1508
- INSERT INTO drift_anchors (
1509
- kind, statement, rationale, domain, decision_mode,
1510
- confidence, lifecycle, status, owner,
1511
- evidence_refs, created_at, updated_at
1512
- ) VALUES (
1513
- 'decision', ?, ?, ?, 'hypothesis',
1514
- ?, 'active', 'active', 'agent',
1515
- ?, ?, ?
1516
- )
1507
+ const insertAnchor = db.prepare(`
1508
+ INSERT INTO drift_anchors (
1509
+ kind, statement, rationale, domain, decision_mode,
1510
+ confidence, lifecycle, status, owner,
1511
+ evidence_refs, created_at, updated_at
1512
+ ) VALUES (
1513
+ 'decision', ?, ?, ?, 'hypothesis',
1514
+ ?, 'active', 'active', 'agent',
1515
+ ?, ?, ?
1516
+ )
1517
1517
  `);
1518
- const insertCandidate = db.prepare(`
1519
- INSERT INTO memory_write_candidates (
1520
- scope, candidate_type, target_node_id, rationale,
1521
- confidence, evidence_refs, status, created_at
1522
- ) VALUES (
1523
- 'orphaned_proposal', 'update_node', ?, ?,
1524
- ?, ?, 'pending_review', ?
1525
- )
1518
+ const insertCandidate = db.prepare(`
1519
+ INSERT INTO memory_write_candidates (
1520
+ scope, candidate_type, target_node_id, rationale,
1521
+ confidence, evidence_refs, status, created_at
1522
+ ) VALUES (
1523
+ 'orphaned_proposal', 'update_node', ?, ?,
1524
+ ?, ?, 'pending_review', ?
1525
+ )
1526
1526
  `);
1527
1527
  const results = [];
1528
1528
  const txn = db.transaction(() => {
@@ -1566,13 +1566,13 @@ function handleFlagProposals(args) {
1566
1566
  function handleDream(args) {
1567
1567
  const domainFilter = args?.domain;
1568
1568
  // 1. Fetch active North Star anchor(s)
1569
- const northStars = db.prepare(`
1570
- SELECT id, statement, rationale, domain, confidence, lifecycle,
1571
- datetime(review_after, 'unixepoch') as review_date,
1572
- datetime(created_at, 'unixepoch') as declared_at
1573
- FROM drift_anchors
1574
- WHERE node_type = 'north_star' AND status = 'active'
1575
- ORDER BY created_at DESC
1569
+ const northStars = db.prepare(`
1570
+ SELECT id, statement, rationale, domain, confidence, lifecycle,
1571
+ datetime(review_after, 'unixepoch') as review_date,
1572
+ datetime(created_at, 'unixepoch') as declared_at
1573
+ FROM drift_anchors
1574
+ WHERE node_type = 'north_star' AND status = 'active'
1575
+ ORDER BY created_at DESC
1576
1576
  `).all();
1577
1577
  // Re-injection friction — the active-observability loop closing back into reflection.
1578
1578
  // "Re-surfaced N×, yet still contradicted in reality" is the machine evidence behind #15443.
@@ -1589,18 +1589,18 @@ function handleDream(args) {
1589
1589
  // why-strings so the existing backlog is drainable without a backfill write.
1590
1590
  let distillQueue = [];
1591
1591
  try {
1592
- const rows = db.prepare(`
1593
- SELECT m.id, m.layer, m.content, datetime(m.created_at, 'unixepoch') AS created, e.name AS entity
1594
- FROM memories m JOIN entities e ON e.id = m.entity_id
1595
- WHERE m.layer IN ('learning', 'caveat')
1596
- AND json_valid(m.content)
1597
- -- settle window: the Stop hook extracts every turn, so the newest rows belong to a
1598
- -- session still in motion. Don't ask the agent to distill the conversation it is in.
1599
- AND m.created_at < unixepoch() - 1800
1600
- AND (json_extract(m.content, '$.needs_distill') = 1
1601
- OR json_extract(m.content, '$.why') = 'Decision detected by pattern match — may need agent enrichment'
1602
- OR json_extract(m.content, '$.why') = 'User-stated warning/prohibition — auto-extracted by caveat pattern match')
1603
- ORDER BY m.created_at DESC LIMIT 8
1592
+ const rows = db.prepare(`
1593
+ SELECT m.id, m.layer, m.content, datetime(m.created_at, 'unixepoch') AS created, e.name AS entity
1594
+ FROM memories m JOIN entities e ON e.id = m.entity_id
1595
+ WHERE m.layer IN ('learning', 'caveat')
1596
+ AND json_valid(m.content)
1597
+ -- settle window: the Stop hook extracts every turn, so the newest rows belong to a
1598
+ -- session still in motion. Don't ask the agent to distill the conversation it is in.
1599
+ AND m.created_at < unixepoch() - 1800
1600
+ AND (json_extract(m.content, '$.needs_distill') = 1
1601
+ OR json_extract(m.content, '$.why') = 'Decision detected by pattern match — may need agent enrichment'
1602
+ OR json_extract(m.content, '$.why') = 'User-stated warning/prohibition — auto-extracted by caveat pattern match')
1603
+ ORDER BY m.created_at DESC LIMIT 8
1604
1604
  `).all();
1605
1605
  distillQueue = rows.map((r) => {
1606
1606
  let c = {};
@@ -1638,26 +1638,26 @@ function handleDream(args) {
1638
1638
  }
1639
1639
  // 2. Fetch pending orphaned proposals
1640
1640
  const candidateQuery = domainFilter
1641
- ? `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
1642
- mc.rationale as candidate_rationale, mc.confidence,
1643
- mc.evidence_refs, mc.status,
1644
- datetime(mc.created_at, 'unixepoch') as flagged_at,
1645
- da.statement, da.rationale as anchor_rationale, da.domain,
1646
- da.evidence_refs as anchor_evidence
1647
- FROM memory_write_candidates mc
1648
- JOIN drift_anchors da ON mc.target_node_id = da.id
1649
- WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
1650
- AND da.domain = ?
1641
+ ? `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
1642
+ mc.rationale as candidate_rationale, mc.confidence,
1643
+ mc.evidence_refs, mc.status,
1644
+ datetime(mc.created_at, 'unixepoch') as flagged_at,
1645
+ da.statement, da.rationale as anchor_rationale, da.domain,
1646
+ da.evidence_refs as anchor_evidence
1647
+ FROM memory_write_candidates mc
1648
+ JOIN drift_anchors da ON mc.target_node_id = da.id
1649
+ WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
1650
+ AND da.domain = ?
1651
1651
  ORDER BY mc.created_at DESC`
1652
- : `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
1653
- mc.rationale as candidate_rationale, mc.confidence,
1654
- mc.evidence_refs, mc.status,
1655
- datetime(mc.created_at, 'unixepoch') as flagged_at,
1656
- da.statement, da.rationale as anchor_rationale, da.domain,
1657
- da.evidence_refs as anchor_evidence
1658
- FROM memory_write_candidates mc
1659
- JOIN drift_anchors da ON mc.target_node_id = da.id
1660
- WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
1652
+ : `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
1653
+ mc.rationale as candidate_rationale, mc.confidence,
1654
+ mc.evidence_refs, mc.status,
1655
+ datetime(mc.created_at, 'unixepoch') as flagged_at,
1656
+ da.statement, da.rationale as anchor_rationale, da.domain,
1657
+ da.evidence_refs as anchor_evidence
1658
+ FROM memory_write_candidates mc
1659
+ JOIN drift_anchors da ON mc.target_node_id = da.id
1660
+ WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
1661
1661
  ORDER BY mc.created_at DESC`;
1662
1662
  const candidates = domainFilter
1663
1663
  ? db.prepare(candidateQuery).all(domainFilter)
@@ -1737,12 +1737,12 @@ function handleResolveProposal(args) {
1737
1737
  throw new Error('verdict must be "surface" or "dismiss"');
1738
1738
  }
1739
1739
  // Verify candidate exists and is pending
1740
- const candidate = db.prepare(`
1741
- SELECT mc.id, mc.target_node_id, mc.status, mc.evidence_refs,
1742
- da.statement
1743
- FROM memory_write_candidates mc
1744
- JOIN drift_anchors da ON mc.target_node_id = da.id
1745
- WHERE mc.id = ? AND mc.scope = 'orphaned_proposal'
1740
+ const candidate = db.prepare(`
1741
+ SELECT mc.id, mc.target_node_id, mc.status, mc.evidence_refs,
1742
+ da.statement
1743
+ FROM memory_write_candidates mc
1744
+ JOIN drift_anchors da ON mc.target_node_id = da.id
1745
+ WHERE mc.id = ? AND mc.scope = 'orphaned_proposal'
1746
1746
  `).get(candidateId);
1747
1747
  if (!candidate) {
1748
1748
  throw new Error(`Candidate #${candidateId} not found or not an orphaned proposal`);
package/package.json CHANGED
@@ -1,95 +1,96 @@
1
- {
2
- "name": "linksee-memory",
3
- "version": "0.12.0",
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",
6
- "type": "module",
7
- "bin": {
8
- "linksee-memory": "dist/mcp/server.js",
9
- "linksee-memory-import": "dist/bin/import-sessions.js",
10
- "linksee-memory-sync": "dist/bin/sync-session.js",
11
- "linksee-memory-install-skill": "dist/bin/install-skill.js",
12
- "linksee-memory-stats": "dist/bin/stats.js",
13
- "linksee-memory-setup": "dist/bin/setup.js",
14
- "linksee-memory-declare": "dist/bin/declare-anchor.js",
15
- "linksee-memory-detect": "dist/bin/detect-drift.js",
16
- "linksee-memory-guard": "dist/bin/guard-hook.js",
17
- "linksee-memory-map": "dist/bin/map-import.js",
18
- "linksee-memory-export": "dist/bin/export-report.js"
19
- },
20
- "main": "./dist/mcp/server.js",
21
- "files": [
22
- "dist/**/*.js",
23
- "dist/**/*.d.ts",
24
- "dist/db/schema.sql",
25
- "dist/skill/SKILL.md",
26
- "README.md",
27
- "LICENSE"
28
- ],
29
- "scripts": {
30
- "build": "tsc && node -e \"require('fs').copyFileSync('src/db/schema.sql','dist/db/schema.sql'); require('fs').mkdirSync('dist/skill',{recursive:true}); require('fs').copyFileSync('src/skill/SKILL.md','dist/skill/SKILL.md')\"",
31
- "start": "node dist/mcp/server.js",
32
- "dev": "tsx src/mcp/server.ts",
33
- "migrate": "node dist/db/migrate.js",
34
- "migrate:dev": "tsx src/db/migrate.ts",
35
- "prepublishOnly": "node scripts/release-gate.mjs && npm run build"
36
- },
37
- "keywords": [
38
- "mcp",
39
- "model-context-protocol",
40
- "memory",
41
- "agent",
42
- "agent-memory",
43
- "claude",
44
- "claude-code",
45
- "cursor",
46
- "chatgpt",
47
- "sqlite",
48
- "local-first",
49
- "token-savings",
50
- "cross-agent"
51
- ],
52
- "author": "Synapse Arrows PTE. LTD.",
53
- "license": "MIT",
54
- "homepage": "https://github.com/michielinksee/linksee-memory",
55
- "repository": {
56
- "type": "git",
57
- "url": "git+https://github.com/michielinksee/linksee-memory.git"
58
- },
59
- "bugs": {
60
- "url": "https://github.com/michielinksee/linksee-memory/issues"
61
- },
62
- "dependencies": {
63
- "@babel/parser": "^7.25.0",
64
- "@modelcontextprotocol/sdk": "^1.0.0",
65
- "better-sqlite3": "^12.9.0",
66
- "sqlite-vec": "^0.1.6",
67
- "yaml": "^2.9.0"
68
- },
69
- "devDependencies": {
70
- "@types/better-sqlite3": "^7.6.11",
71
- "@types/node": "^22.7.0",
72
- "tsx": "^4.19.0",
73
- "typescript": "^5.6.0"
74
- },
75
- "engines": {
76
- "node": ">=20"
77
- },
78
- "overrides": {
79
- "ip-address": "^10.2.0",
80
- "hono": "^4.12.21",
81
- "qs": "^6.15.2",
82
- "esbuild": "^0.28.1"
83
- },
84
- "pnpm": {
85
- "onlyBuiltDependencies": [
86
- "better-sqlite3"
87
- ],
88
- "overrides": {
89
- "ip-address": "^10.2.0",
90
- "hono": "^4.12.21",
91
- "qs": "^6.15.2",
92
- "esbuild": "^0.28.1"
93
- }
94
- }
95
- }
1
+ {
2
+ "name": "linksee-memory",
3
+ "version": "0.12.1",
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",
6
+ "type": "module",
7
+ "bin": {
8
+ "linksee-memory": "dist/mcp/server.js",
9
+ "linksee-memory-import": "dist/bin/import-sessions.js",
10
+ "linksee-memory-sync": "dist/bin/sync-session.js",
11
+ "linksee-memory-install-skill": "dist/bin/install-skill.js",
12
+ "linksee-memory-stats": "dist/bin/stats.js",
13
+ "linksee-memory-setup": "dist/bin/setup.js",
14
+ "linksee-memory-declare": "dist/bin/declare-anchor.js",
15
+ "linksee-memory-detect": "dist/bin/detect-drift.js",
16
+ "linksee-memory-guard": "dist/bin/guard-hook.js",
17
+ "linksee-memory-map": "dist/bin/map-import.js",
18
+ "linksee-memory-export": "dist/bin/export-report.js"
19
+ },
20
+ "main": "./dist/mcp/server.js",
21
+ "files": [
22
+ "dist/**/*.js",
23
+ "dist/**/*.d.ts",
24
+ "dist/db/schema.sql",
25
+ "dist/skill/SKILL.md",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc && node -e \"require('fs').copyFileSync('src/db/schema.sql','dist/db/schema.sql'); require('fs').mkdirSync('dist/skill',{recursive:true}); require('fs').copyFileSync('src/skill/SKILL.md','dist/skill/SKILL.md')\"",
31
+ "start": "node dist/mcp/server.js",
32
+ "dev": "tsx src/mcp/server.ts",
33
+ "migrate": "node dist/db/migrate.js",
34
+ "migrate:dev": "tsx src/db/migrate.ts",
35
+ "prepublishOnly": "node scripts/release-gate.mjs && npm run build",
36
+ "version": "node scripts/sync-version.mjs"
37
+ },
38
+ "keywords": [
39
+ "mcp",
40
+ "model-context-protocol",
41
+ "memory",
42
+ "agent",
43
+ "agent-memory",
44
+ "claude",
45
+ "claude-code",
46
+ "cursor",
47
+ "chatgpt",
48
+ "sqlite",
49
+ "local-first",
50
+ "token-savings",
51
+ "cross-agent"
52
+ ],
53
+ "author": "Synapse Arrows PTE. LTD.",
54
+ "license": "MIT",
55
+ "homepage": "https://github.com/michielinksee/linksee-memory",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/michielinksee/linksee-memory.git"
59
+ },
60
+ "bugs": {
61
+ "url": "https://github.com/michielinksee/linksee-memory/issues"
62
+ },
63
+ "dependencies": {
64
+ "@babel/parser": "^7.25.0",
65
+ "@modelcontextprotocol/sdk": "^1.0.0",
66
+ "better-sqlite3": "^12.9.0",
67
+ "sqlite-vec": "^0.1.6",
68
+ "yaml": "^2.9.0"
69
+ },
70
+ "devDependencies": {
71
+ "@types/better-sqlite3": "^7.6.11",
72
+ "@types/node": "^22.7.0",
73
+ "tsx": "^4.19.0",
74
+ "typescript": "^5.6.0"
75
+ },
76
+ "engines": {
77
+ "node": ">=20"
78
+ },
79
+ "overrides": {
80
+ "ip-address": "^10.2.0",
81
+ "hono": "^4.12.21",
82
+ "qs": "^6.15.2",
83
+ "esbuild": "^0.28.1"
84
+ },
85
+ "pnpm": {
86
+ "onlyBuiltDependencies": [
87
+ "better-sqlite3"
88
+ ],
89
+ "overrides": {
90
+ "ip-address": "^10.2.0",
91
+ "hono": "^4.12.21",
92
+ "qs": "^6.15.2",
93
+ "esbuild": "^0.28.1"
94
+ }
95
+ }
96
+ }