claude-mem-lite 3.29.0 → 3.31.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/server.mjs +50 -32
- package/tool-schemas.mjs +2 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "claude-mem-lite",
|
|
13
|
-
"version": "3.
|
|
13
|
+
"version": "3.31.0",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
|
|
16
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.31.0",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sdsrss"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.31.0",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.2",
|
package/server.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { deepSearch, resolveDeepMode, shouldEscalateToDeep, autoDeepLlmReady } f
|
|
|
14
14
|
import { selectCompressionCandidates, groupByProjectWeek, compressGroup } from './lib/compress-core.mjs';
|
|
15
15
|
import { buildNotLowSignalSql } from './lib/low-signal-patterns.mjs';
|
|
16
16
|
import { resolveAnchorToken, formatAnchorError, resolveQueryAnchor, fetchRecentTimeline, fetchTimelineWindow } from './lib/timeline-core.mjs';
|
|
17
|
-
import { buildSearchFtsQuery, parseDateBounds, coreRunSearchPipeline } from './lib/search-core.mjs';
|
|
17
|
+
import { buildSearchFtsQuery, parseDateBounds, parseDuration, coreRunSearchPipeline } from './lib/search-core.mjs';
|
|
18
18
|
import {
|
|
19
19
|
cleanupBroken, decayAndMarkIdle, boostAccessed, demotePinned, mergeDuplicates,
|
|
20
20
|
recoverOrphanedChildren,
|
|
@@ -267,8 +267,11 @@ async function runSearchPipeline(db, args, { llm, rerankLlm } = {}) {
|
|
|
267
267
|
const ftsQuery = buildSearchFtsQuery(args.query, { or: args.or });
|
|
268
268
|
const currentProject = inferProject();
|
|
269
269
|
|
|
270
|
-
const bounds = parseDateBounds(args.date_from, args.date_to);
|
|
271
|
-
if (!bounds.ok)
|
|
270
|
+
const bounds = parseDateBounds(args.date_from, args.date_to, args.date_since);
|
|
271
|
+
if (!bounds.ok) {
|
|
272
|
+
if (bounds.bad === 'since') throw new Error(`Invalid date_since: "${bounds.value}" (use <N><unit>, e.g. 7d, 24h, 90m, 2w)`);
|
|
273
|
+
throw new Error(`Invalid date_${bounds.bad}: "${bounds.value}" (use ISO 8601 or YYYY-MM-DD)`);
|
|
274
|
+
}
|
|
272
275
|
const { epochFrom, epochTo } = bounds;
|
|
273
276
|
|
|
274
277
|
// MCP defaults to 'auto' (escalate on weak results) unless overridden by
|
|
@@ -343,41 +346,56 @@ server.registerTool(
|
|
|
343
346
|
|
|
344
347
|
// ─── Tool: mem_recent ────────────────────────────────────────────────────────
|
|
345
348
|
|
|
349
|
+
// In-process test seam (mirrors handleSearchForTest, #8743): threads an injected
|
|
350
|
+
// db through the SAME body the registered handler runs. NOTE: a `project` arg is
|
|
351
|
+
// still resolved via resolveProject() against the MODULE db, not the injected one.
|
|
352
|
+
export async function handleRecentForTest(db, args) {
|
|
353
|
+
return runRecent(db, args);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function runRecent(db, args) {
|
|
357
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
358
|
+
const limit = args.limit ?? 10;
|
|
359
|
+
const project = args.project || inferProject();
|
|
360
|
+
|
|
361
|
+
const params = [];
|
|
362
|
+
const wheres = ['COALESCE(compressed_into, 0) = 0', 'superseded_at IS NULL'];
|
|
363
|
+
if (project) { wheres.push('project = ?'); params.push(project); }
|
|
364
|
+
// date_since: relative lower bound on created_at (CLI `recent --since` parity).
|
|
365
|
+
if (args.date_since !== undefined) {
|
|
366
|
+
const d = parseDuration(args.date_since);
|
|
367
|
+
if (!d.ok) throw new Error(`Invalid date_since: "${args.date_since}" (use <N><unit>, e.g. 7d, 24h, 90m, 2w)`);
|
|
368
|
+
wheres.push('created_at_epoch >= ?'); params.push(Date.now() - d.ms);
|
|
369
|
+
}
|
|
370
|
+
params.push(limit);
|
|
371
|
+
|
|
372
|
+
const rows = db.prepare(`
|
|
373
|
+
SELECT id, type, title, subtitle, project, created_at, created_at_epoch
|
|
374
|
+
FROM observations
|
|
375
|
+
WHERE ${wheres.join(' AND ')}
|
|
376
|
+
ORDER BY created_at_epoch DESC
|
|
377
|
+
LIMIT ?
|
|
378
|
+
`).all(...params);
|
|
379
|
+
|
|
380
|
+
if (rows.length === 0) {
|
|
381
|
+
return { content: [{ type: 'text', text: `No recent observations${project ? ` (${project})` : ''}.` }] };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const lines = [`Recent observations (${project || 'all'}):\n`];
|
|
385
|
+
for (const r of rows) {
|
|
386
|
+
lines.push(`#${r.id} ${typeIcon(r.type)} [${r.type}] ${truncate(r.title || r.subtitle || '(untitled)')} | ${r.project} | ${fmtDate(r.created_at)}`);
|
|
387
|
+
}
|
|
388
|
+
lines.push(`\nWorkflow: mem_get(ids=[...]) for full details | mem_timeline(anchor=ID) for context`);
|
|
389
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
390
|
+
}
|
|
391
|
+
|
|
346
392
|
server.registerTool(
|
|
347
393
|
'mem_recent',
|
|
348
394
|
{
|
|
349
395
|
description: descriptionOf('mem_recent'),
|
|
350
396
|
inputSchema: memRecentSchema,
|
|
351
397
|
},
|
|
352
|
-
safeHandler(async (args) =>
|
|
353
|
-
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
354
|
-
const limit = args.limit ?? 10;
|
|
355
|
-
const project = args.project || inferProject();
|
|
356
|
-
|
|
357
|
-
const params = [];
|
|
358
|
-
const wheres = ['COALESCE(compressed_into, 0) = 0', 'superseded_at IS NULL'];
|
|
359
|
-
if (project) { wheres.push('project = ?'); params.push(project); }
|
|
360
|
-
params.push(limit);
|
|
361
|
-
|
|
362
|
-
const rows = db.prepare(`
|
|
363
|
-
SELECT id, type, title, subtitle, project, created_at, created_at_epoch
|
|
364
|
-
FROM observations
|
|
365
|
-
WHERE ${wheres.join(' AND ')}
|
|
366
|
-
ORDER BY created_at_epoch DESC
|
|
367
|
-
LIMIT ?
|
|
368
|
-
`).all(...params);
|
|
369
|
-
|
|
370
|
-
if (rows.length === 0) {
|
|
371
|
-
return { content: [{ type: 'text', text: `No recent observations${project ? ` (${project})` : ''}.` }] };
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const lines = [`Recent observations (${project || 'all'}):\n`];
|
|
375
|
-
for (const r of rows) {
|
|
376
|
-
lines.push(`#${r.id} ${typeIcon(r.type)} [${r.type}] ${truncate(r.title || r.subtitle || '(untitled)')} | ${r.project} | ${fmtDate(r.created_at)}`);
|
|
377
|
-
}
|
|
378
|
-
lines.push(`\nWorkflow: mem_get(ids=[...]) for full details | mem_timeline(anchor=ID) for context`);
|
|
379
|
-
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
380
|
-
})
|
|
398
|
+
safeHandler(async (args) => runRecent(db, args))
|
|
381
399
|
);
|
|
382
400
|
|
|
383
401
|
// ─── Tool: mem_timeline ─────────────────────────────────────────────────────
|
package/tool-schemas.mjs
CHANGED
|
@@ -85,6 +85,7 @@ export const memSearchSchema = {
|
|
|
85
85
|
project: z.string().optional().describe('Filter by project name'),
|
|
86
86
|
date_from: z.string().optional().describe('Start date (ISO 8601 or YYYY-MM-DD)'),
|
|
87
87
|
date_to: z.string().optional().describe('End date (ISO 8601 or YYYY-MM-DD). Date-only format is inclusive (covers full day)'),
|
|
88
|
+
date_since: z.string().optional().describe('Relative lower bound from now: 7d/24h/90m/2w/30s. Use for "recent" queries instead of computing a date_from; ignored when date_from is set'),
|
|
88
89
|
importance: coerceInt.pipe(z.number().int().min(1).max(3)).optional().describe('Minimum importance (1=routine, 2=notable, 3=critical)'),
|
|
89
90
|
branch: z.string().optional().describe('Filter by git branch name'),
|
|
90
91
|
tier: z.enum(['working', 'active', 'archive']).optional().describe('Filter by memory tier (working=current session, active=within decay window, archive=old/compressed)'),
|
|
@@ -100,6 +101,7 @@ export const memSearchSchema = {
|
|
|
100
101
|
export const memRecentSchema = {
|
|
101
102
|
limit: coerceInt.pipe(z.number().int().min(1).max(100)).optional().describe('Max results (default 10)'),
|
|
102
103
|
project: z.string().optional().describe('Filter by project (default: inferred from CWD)'),
|
|
104
|
+
date_since: z.string().optional().describe('Relative lower bound from now: 7d/24h/90m/2w/30s. Only items newer than the window (pair with a high limit for "everything since X")'),
|
|
103
105
|
};
|
|
104
106
|
|
|
105
107
|
// Anchor accepts plain int, "123" string-int, or prefixed token from search output:
|