claude-mem-lite 2.26.1 → 2.28.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +22 -5
- package/README.zh-CN.md +47 -63
- package/cli.mjs +1 -1
- package/commands/mem.md +1 -1
- package/commands/memory.md +1 -1
- package/commands/recall.md +1 -1
- package/commands/recent.md +1 -1
- package/commands/search.md +1 -1
- package/commands/timeline.md +1 -1
- package/commands/tools.md +9 -20
- package/commands/update.md +1 -1
- package/hook-llm.mjs +31 -4
- package/hook-memory.mjs +9 -3
- package/hook.mjs +19 -1
- package/hooks/hooks.json +10 -0
- package/install.mjs +23 -3
- package/mem-cli.mjs +155 -23
- package/package.json +5 -1
- package/registry-enricher.mjs +101 -0
- package/registry-github.mjs +54 -0
- package/registry-importer.mjs +352 -0
- package/registry.mjs +36 -2
- package/scripts/pre-skill-bridge.js +78 -0
- package/scripts/prompt-search-utils.mjs +47 -2
- package/scripts/user-prompt-search.js +105 -5
- package/server.mjs +184 -23
- package/tool-schemas.mjs +9 -2
- package/utils.mjs +6 -2
package/README.md
CHANGED
|
@@ -90,6 +90,7 @@ The original sends **everything to the LLM and hopes it filters well**. claude-m
|
|
|
90
90
|
- **Cross-source normalization** -- `mem_search` normalizes scores across observations, sessions, and prompts before merging, preventing any source from dominating results
|
|
91
91
|
- **Exponential recency decay** -- Type-differentiated half-lives (decisions: 90d, discoveries: 60d, bugfixes: 14d, changes: 7d) consistently applied in all ranking paths
|
|
92
92
|
- **Prompt-time memory injection** -- UserPromptSubmit hook automatically searches and injects relevant past observations with recency and importance weighting
|
|
93
|
+
- **Smart skill invocation** -- Auto-loaded and searched managed skills/agents include portable `~` paths with `Read()` guidance; native plugin skills recommend `Skill("full:name")`; prevents `Skill()` misuse for managed resources that aren't registered with Claude Code's native handler
|
|
93
94
|
- **Dual injection dedup** -- `user-prompt-search.js` and `handleUserPrompt` coordinate via temp file to prevent duplicate memory injection
|
|
94
95
|
- **Result-dedup cooldown** -- User-prompt memory injection uses result-overlap detection (>80% ID overlap → skip) instead of time-based cooldown, allowing topic switches within seconds while preventing redundant injections
|
|
95
96
|
- **OR query fallback** -- When AND-joined FTS5 queries return zero results, automatically relaxes to OR-joined queries for broader recall (applied in both user-prompt-search and hook-memory paths)
|
|
@@ -154,7 +155,7 @@ Source files stay in the cloned repo. Update via `git pull && node install.mjs i
|
|
|
154
155
|
### What happens during installation
|
|
155
156
|
|
|
156
157
|
1. **Install dependencies** -- `npm install --omit=dev` (compiles native `better-sqlite3`)
|
|
157
|
-
2. **Register MCP server** -- `mem` server with
|
|
158
|
+
2. **Register MCP server** -- `mem` server with 16 tools (search, recent, recall, timeline, get, save, update, stats, delete, compress, maintain, export, fts_check, browse, registry, use)
|
|
158
159
|
3. **Configure hooks** -- `PostToolUse`, `SessionStart`, `Stop`, `UserPromptSubmit` lifecycle hooks
|
|
159
160
|
4. **Create data directory** -- `~/.claude-mem-lite/` (hidden) for database, runtime, and managed resource files
|
|
160
161
|
5. **Auto-migrate** -- If `~/.claude-mem/` (original claude-mem) or `~/claude-mem-lite/` (pre-v0.5 unhidden) exists, migrates database and runtime files to `~/.claude-mem-lite/`, preserving the original untouched
|
|
@@ -195,8 +196,8 @@ rm -rf ~/claude-mem-lite/ # pre-v0.5 unhidden (if not auto-moved)
|
|
|
195
196
|
ep-flush-*.json # Flushed episodes awaiting processing
|
|
196
197
|
reads-<project>.txt # Read file paths (collected on flush)
|
|
197
198
|
managed/
|
|
198
|
-
skills/ # Standalone skills
|
|
199
|
-
agents/ # Agent plugins
|
|
199
|
+
skills/ # Standalone skills: {name}/SKILL.md
|
|
200
|
+
agents/ # Agent plugins: {group}/agents/{name}.md + skills/*/SKILL.md
|
|
200
201
|
repos/ # Shallow-cloned source repos
|
|
201
202
|
```
|
|
202
203
|
|
|
@@ -220,7 +221,8 @@ rm -rf ~/claude-mem-lite/ # pre-v0.5 unhidden (if not auto-moved)
|
|
|
220
221
|
| `mem_export` | Export observations as JSON or JSONL for backup or migration. Filters by project, type, date range. |
|
|
221
222
|
| `mem_fts_check` | Check FTS5 index integrity or rebuild indexes. Use when search results seem wrong or after DB recovery. |
|
|
222
223
|
| `mem_browse` | Tier-grouped memory dashboard. Shows observations organized by memory tier (working/active/archive). |
|
|
223
|
-
| `mem_registry` | Manage resource registry: search for skills/agents by need, list resources, view stats, import/remove tools, reindex. |
|
|
224
|
+
| `mem_registry` | Manage resource registry: search for skills/agents by need, list resources, view stats, import/remove tools, reindex. Search results differentiate managed (Read path) vs native (Skill full name) invocation. |
|
|
225
|
+
| `mem_use` | Load a skill or agent from the managed registry by name. Returns full content with portable `~` path for reload via `Read()`. |
|
|
224
226
|
|
|
225
227
|
### Skill Commands (in Claude Code chat)
|
|
226
228
|
|
|
@@ -323,6 +325,9 @@ UserPromptSubmit (two parallel paths)
|
|
|
323
325
|
-> [user-prompt-search.js] Auto-search memory via FTS5 + active file context
|
|
324
326
|
-> [user-prompt-search.js] Inject relevant past observations with recency/importance weighting
|
|
325
327
|
-> [user-prompt-search.js] Write injected IDs to temp file for dedup
|
|
328
|
+
-> [user-prompt-search.js] L1 skill auto-load: match managed skill names in prompt
|
|
329
|
+
-> Load content with portable ~ path + Read() guidance
|
|
330
|
+
-> source="managed-skill|managed-agent", path="~/.claude-mem-lite/managed/..."
|
|
326
331
|
-> [hook.mjs handleUserPrompt] Capture user prompt text to user_prompts table
|
|
327
332
|
-> [hook.mjs handleUserPrompt] Increment session prompt counter
|
|
328
333
|
-> [hook.mjs handleUserPrompt] Handoff: detect continuation intent → inject previous session context
|
|
@@ -346,6 +351,15 @@ Registry pipeline:
|
|
|
346
351
|
-> registry-indexer.mjs indexes content into FTS5 with metadata
|
|
347
352
|
-> registry-retriever.mjs provides BM25-ranked search with synonym expansion
|
|
348
353
|
-> mem_registry MCP tool exposes search/list/stats/import/remove/reindex actions
|
|
354
|
+
|
|
355
|
+
Smart invocation (three layers):
|
|
356
|
+
L1 auto-load: UserPromptSubmit matches managed skill name in prompt
|
|
357
|
+
-> Loads content with path="~/.claude-mem-lite/managed/.../SKILL.md"
|
|
358
|
+
-> Guides: Read("path") or mem_use(name="..."), never Skill()
|
|
359
|
+
L2 bridge: PreToolUse hook intercepts Skill("name") for managed resources
|
|
360
|
+
-> Outputs content, prevents native handler failure
|
|
361
|
+
L3 explicit: mem_use(name="...") loads full content with reload path
|
|
362
|
+
Search: managed resources → Read(path), native plugins → Skill("full:name")
|
|
349
363
|
```
|
|
350
364
|
|
|
351
365
|
Composite scoring for search results: BM25 relevance (40%) + repo stars (15%) + success rate (15%) + adoption rate (10%) + freshness (10%) + exploration bonus (10%). Domain filtering ensures platform-specific resources (iOS, Go, Rust) only surface for matching projects.
|
|
@@ -466,7 +480,10 @@ claude-mem-lite/
|
|
|
466
480
|
scripts/
|
|
467
481
|
setup.sh # Setup hook: npm install + migration (hidden dir + old dir)
|
|
468
482
|
post-tool-use.sh # Bash pre-filter: skips noise in ~5ms, tracks Read paths
|
|
469
|
-
user-prompt-search.js # UserPromptSubmit hook: auto-search memory
|
|
483
|
+
user-prompt-search.js # UserPromptSubmit hook: auto-search memory + L1 skill auto-load
|
|
484
|
+
pre-skill-bridge.js # PreToolUse hook: L2 skill bridge for managed resources
|
|
485
|
+
pre-tool-recall.js # PreToolUse hook: file lesson recall before Edit/Write
|
|
486
|
+
prompt-search-utils.mjs # Shared logic: skip patterns, intent detection, name matching
|
|
470
487
|
convert-commands.mjs # Converts command .md → SKILL.md in managed plugins
|
|
471
488
|
index-managed.mjs # Offline indexer for managed resources
|
|
472
489
|
# Test & benchmark (dev only)
|
package/README.zh-CN.md
CHANGED
|
@@ -79,13 +79,11 @@
|
|
|
79
79
|
- **原子写入** -- 所有文件写入(episode、CLAUDE.md)使用 write-to-tmp + rename 防止崩溃时损坏
|
|
80
80
|
- **健壮锁机制** -- PID 感知的锁文件,自动清理过期(>30s)或孤儿(PID 已死)锁
|
|
81
81
|
- **过期会话清理** -- 活跃超过 24 小时的会话在下次启动时自动标记为 abandoned
|
|
82
|
-
-
|
|
83
|
-
- **资源注册表** -- 对已安装的 skill 和 agent 建立 FTS5
|
|
82
|
+
- **智能调用** -- 三层调用系统:L1 自动加载(UserPromptSubmit 匹配 skill 名注入内容 + `Read()` 路径),L2 Bridge(PreToolUse 拦截 `Skill()` 误调),L3 显式调用(`mem_use` MCP 工具)。managed 资源用 `Read("~/.claude-mem-lite/managed/.../SKILL.md")`,原生插件用 `Skill("full:name")`
|
|
83
|
+
- **资源注册表** -- 对已安装的 skill 和 agent 建立 FTS5 索引,支持复合评分和调用追踪。搜索结果区分 managed(Read 路径)vs native(Skill 全名)调用方式
|
|
84
84
|
- **统一资源发现** -- 共享文件系统遍历层(`resource-discovery.mjs`),运行时扫描器和离线索引器共用,支持扁平目录、插件嵌套和松散 `.md` 文件
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
- **领域同义词扩展** -- 调度查询自动扩展领域同义词(如 "修复" → fix, debug, bugfix, repair, error)
|
|
88
|
-
- **持久化冷却机制** -- 5 分钟跨会话冷却 + 同会话去重,避免重复推荐
|
|
85
|
+
- **领域同义词扩展** -- 注册表搜索查询自动扩展领域同义词(如 "修复" → fix, debug, bugfix, repair, error)
|
|
86
|
+
- **持久化冷却机制** -- 5 分钟跨会话冷却 + 同会话去重,避免重复推荐 skill 自动加载
|
|
89
87
|
- **双模式 LLM 调用** -- 自动检测 `ANTHROPIC_API_KEY` 直连 API;无 key 时回退到 `claude -p` CLI
|
|
90
88
|
- **Haiku 熔断器** -- 连续 3 次 LLM 失败后,禁用 Haiku 调度 5 分钟,防止级联延迟
|
|
91
89
|
- **否定意图感知** -- 正确处理 "不要测试了,先修 bug" 等复杂提示,排除被否定的意图,支持中英文混合输入
|
|
@@ -185,8 +183,8 @@ rm -rf ~/claude-mem-lite/ # v0.5 前的非隐藏目录(如未自动迁移)
|
|
|
185
183
|
ep-flush-*.json # 已刷新的 episode,等待处理
|
|
186
184
|
reads-<project>.txt # Read 文件路径(刷新时收集)
|
|
187
185
|
managed/
|
|
188
|
-
skills/ # 独立 skill
|
|
189
|
-
agents/ # Agent
|
|
186
|
+
skills/ # 独立 skill:{name}/SKILL.md
|
|
187
|
+
agents/ # Agent 插件:{group}/agents/{name}.md + skills/*/SKILL.md
|
|
190
188
|
repos/ # 浅克隆的源代码仓库
|
|
191
189
|
```
|
|
192
190
|
|
|
@@ -210,7 +208,8 @@ rm -rf ~/claude-mem-lite/ # v0.5 前的非隐藏目录(如未自动迁移)
|
|
|
210
208
|
| `mem_export` | 导出观察为 JSON 或 JSONL 格式,支持按项目、类型、日期范围过滤。 |
|
|
211
209
|
| `mem_fts_check` | 检查 FTS5 索引完整性或重建索引。搜索结果异常或数据库恢复后使用。 |
|
|
212
210
|
| `mem_browse` | 分层记忆仪表盘。按记忆层级(working/active/archive)分组展示观察。 |
|
|
213
|
-
| `mem_registry` |
|
|
211
|
+
| `mem_registry` | 管理资源注册表:按需搜索技能/代理、列表、统计、导入/移除、重索引。搜索结果区分 managed(Read 路径)和 native(Skill 全名)调用方式。 |
|
|
212
|
+
| `mem_use` | 从 managed 注册表加载 skill 或 agent。返回完整内容 + `~` 便携路径供 `Read()` 重载。 |
|
|
214
213
|
|
|
215
214
|
### 技能命令(在 Claude Code 聊天中使用)
|
|
216
215
|
|
|
@@ -280,7 +279,6 @@ SessionStart
|
|
|
280
279
|
-> 清理孤儿/过期锁文件
|
|
281
280
|
-> 查询最近观察(24 小时内)
|
|
282
281
|
-> 注入上下文到 CLAUDE.md + 标准输出
|
|
283
|
-
-> 调度:根据用户提示推荐 skill/agent(Tier 0→1→2→3)
|
|
284
282
|
|
|
285
283
|
PostToolUse(每次工具执行)
|
|
286
284
|
-> Bash 预过滤器 ~5ms 跳过噪声(Read 路径追踪到 reads 文件)
|
|
@@ -293,74 +291,57 @@ PostToolUse(每次工具执行)
|
|
|
293
291
|
-> 错误触发回忆:搜索记忆中相关的历史修复
|
|
294
292
|
|
|
295
293
|
PreToolUse(工具执行前)
|
|
296
|
-
->
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
->
|
|
302
|
-
->
|
|
303
|
-
->
|
|
294
|
+
-> L2 Skill Bridge:拦截对 managed 资源的 Skill() 调用
|
|
295
|
+
-> 匹配 managed 路径 → 输出内容 + mem_use() 提示
|
|
296
|
+
-> 未匹配 → 静默放行到原生 handler
|
|
297
|
+
|
|
298
|
+
UserPromptSubmit(两个并行路径)
|
|
299
|
+
-> [user-prompt-search.js] 通过 FTS5 + 活跃文件上下文自动搜索记忆
|
|
300
|
+
-> [user-prompt-search.js] 注入相关历史观察(按时效和重要性加权)
|
|
301
|
+
-> [user-prompt-search.js] L1 Skill 自动加载:匹配 prompt 中的 managed skill 名
|
|
302
|
+
-> 加载内容 + 便携 ~ 路径 + Read() 调用指引
|
|
303
|
+
-> source="managed-skill|managed-agent", path="~/.claude-mem-lite/managed/..."
|
|
304
|
+
-> [hook.mjs] 捕获用户提示文本到 user_prompts 表
|
|
305
|
+
-> [hook.mjs] 递增会话提示计数器
|
|
306
|
+
-> [hook.mjs] 交接:检测继续意图 → 注入上一次会话上下文
|
|
307
|
+
-> [hook.mjs] 语义记忆注入(hook-memory.mjs),通过临时文件去重
|
|
304
308
|
|
|
305
309
|
Stop
|
|
306
310
|
-> 刷新最终 episode 缓冲区
|
|
307
311
|
-> 保存交接快照(/exit 时)
|
|
308
|
-
-> 收集调度反馈:采纳检测 + 结果评分
|
|
309
312
|
-> 标记会话为已完成
|
|
310
313
|
-> 启动 LLM 摘要 worker(轮询等待)
|
|
311
314
|
```
|
|
312
315
|
|
|
313
|
-
###
|
|
316
|
+
### 智能调用系统
|
|
314
317
|
|
|
315
|
-
|
|
318
|
+
三层调用系统确保 managed 资源(`~/.claude-mem-lite/managed/` 中的 skill 和 agent)能被正确调用:
|
|
316
319
|
|
|
317
320
|
```
|
|
318
|
-
|
|
319
|
-
->
|
|
320
|
-
->
|
|
321
|
-
->
|
|
322
|
-
->
|
|
323
|
-
|
|
324
|
-
Tier 1:上下文信号提取(<1ms)
|
|
325
|
-
-> 意图:从用户提示提取(测试、修复、部署、审查...)
|
|
326
|
-
-> 技术栈:从最近文件扩展名推断(.ts → typescript)
|
|
327
|
-
-> 动作:从工具名推断(Edit → edit, Bash+jest → test)
|
|
328
|
-
-> 错误领域:分类错误(type-error, test-fail, build-fail...)
|
|
321
|
+
L1 自动加载(UserPromptSubmit,<50ms)
|
|
322
|
+
-> 匹配 prompt 中的 managed skill/agent 名称
|
|
323
|
+
-> 加载 SKILL.md / {name}.md 内容
|
|
324
|
+
-> 输出:Read("~/.claude-mem-lite/managed/.../path.md") 调用指引
|
|
325
|
+
-> 截断时提供 mem_use(name="...") 备选
|
|
329
326
|
|
|
330
|
-
|
|
331
|
-
->
|
|
332
|
-
->
|
|
333
|
-
->
|
|
327
|
+
L2 Bridge(PreToolUse Skill hook,<30ms)
|
|
328
|
+
-> 拦截 Skill("name") 调用,查询 managed 注册表
|
|
329
|
+
-> 匹配到 → 输出内容 + mem_use() 提示(防止原生 handler 报错)
|
|
330
|
+
-> 未匹配 → 放行到原生 Skill handler
|
|
334
331
|
|
|
335
|
-
|
|
336
|
-
->
|
|
337
|
-
->
|
|
338
|
-
-> PreToolUse 禁用(2 秒 hook 超时不够)
|
|
332
|
+
L3 显式调用(mem_use MCP 工具)
|
|
333
|
+
-> 按名称精确匹配 + FTS5 模糊回退
|
|
334
|
+
-> 返回完整内容 + 便携路径供 Read() 重载
|
|
339
335
|
```
|
|
340
336
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
| Hook | 时间预算 | 层级 | 用途 |
|
|
344
|
-
|------|---------|------|------|
|
|
345
|
-
| SessionStart | 10s | 0→1→2→3 | 分析上次会话的 next_steps,提前推荐 skill/agent |
|
|
346
|
-
| UserPromptSubmit | 2s | 0→1→2 | 主要调度触发点 — 用户实际提示意图最明确 |
|
|
347
|
-
| PreToolUse | 2s | 0→1→2 | 根据当前操作上下文实时推荐 |
|
|
348
|
-
|
|
349
|
-
**反馈闭环(Stop hook):**
|
|
350
|
-
|
|
351
|
-
会话结束时,系统回顾本次会话中所有推荐:
|
|
352
|
-
- **采纳检测** -- Claude 是否实际使用了推荐的 skill(`Skill` 工具)或 agent(`Task` 工具)?
|
|
353
|
-
- **结果检测** -- 会话是否成功(有编辑无报错)、部分成功(报错后修复)、还是失败?
|
|
354
|
-
- **评分计算** -- 采纳 + 成功 = 1.0,采纳 + 部分 = 0.5,采纳 + 失败 = 0.2
|
|
355
|
-
- 统计数据回流到复合评分,持续改进调度质量
|
|
356
|
-
|
|
357
|
-
**注入模板:**
|
|
337
|
+
**调用方式区分:**
|
|
358
338
|
|
|
359
|
-
| 资源类型 | 位置 |
|
|
360
|
-
|
|
361
|
-
|
|
|
362
|
-
|
|
|
363
|
-
|
|
|
339
|
+
| 资源类型 | 位置 | 调用方式 |
|
|
340
|
+
|---------|------|---------|
|
|
341
|
+
| Managed skill | `~/.claude-mem-lite/managed/skills/` | `Read("~/.../SKILL.md")` 或 `mem_use(name="...")` |
|
|
342
|
+
| Managed agent | `~/.claude-mem-lite/managed/agents/` | `Read("~/.../{name}.md")` 或 `mem_use(name="...", type="agent")` |
|
|
343
|
+
| 原生插件 skill | `~/.claude/plugins/cache/` | `Skill("plugin:skill-name")` |
|
|
344
|
+
| 用户自建 skill | `~/.claude/skills/` | `Skill("name")` |
|
|
364
345
|
|
|
365
346
|
### Episode 编码
|
|
366
347
|
|
|
@@ -464,7 +445,6 @@ claude-mem-lite/
|
|
|
464
445
|
# 智能调度
|
|
465
446
|
dispatch.mjs # 三级调度编排:快速过滤、上下文信号、FTS5、Haiku
|
|
466
447
|
dispatch-inject.mjs # 注入模板渲染:skill/agent 推荐
|
|
467
|
-
dispatch-feedback.mjs # 闭环反馈:采纳检测、结果追踪
|
|
468
448
|
registry.mjs # 资源注册表 DB:schema、CRUD、FTS5、调用追踪
|
|
469
449
|
registry-retriever.mjs # FTS5 检索:同义词扩展与复合评分
|
|
470
450
|
registry-scanner.mjs # 文件系统扫描器:读取内容 + 哈希,委托发现层
|
|
@@ -477,6 +457,10 @@ claude-mem-lite/
|
|
|
477
457
|
scripts/
|
|
478
458
|
setup.sh # Setup 钩子:npm install + 迁移(隐藏目录 + 旧目录)
|
|
479
459
|
post-tool-use.sh # Bash 预过滤器:~5ms 跳过噪声,追踪 Read 路径
|
|
460
|
+
user-prompt-search.js # UserPromptSubmit 钩子:自动搜索记忆 + L1 skill 自动加载
|
|
461
|
+
pre-skill-bridge.js # PreToolUse 钩子:L2 managed skill 桥接
|
|
462
|
+
pre-tool-recall.js # PreToolUse 钩子:Edit/Write 前文件教训回忆
|
|
463
|
+
prompt-search-utils.mjs # 共享逻辑:跳过模式、意图检测、名称匹配
|
|
480
464
|
convert-commands.mjs # 将 command .md 转换为托管插件中的 SKILL.md
|
|
481
465
|
index-managed.mjs # 托管资源离线索引器
|
|
482
466
|
# 测试和基准(仅开发)
|
package/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const CLI_COMMANDS = new Set(['search', 'recent', 'recall', 'get', 'timeline', 'save', 'stats', 'context', 'browse', 'delete', 'update', 'export', 'compress', 'maintain', 'fts-check', 'registry', 'help']);
|
|
2
|
+
const CLI_COMMANDS = new Set(['search', 'recent', 'recall', 'get', 'timeline', 'save', 'stats', 'context', 'browse', 'delete', 'update', 'export', 'compress', 'maintain', 'fts-check', 'registry', 'import', 'enrich', 'help']);
|
|
3
3
|
const INSTALL_COMMANDS = new Set(['install', 'uninstall', 'status', 'doctor', 'cleanup', 'cleanup-hooks', 'self-update', 'release']);
|
|
4
4
|
|
|
5
5
|
const cmd = process.argv[2];
|
package/commands/mem.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Search and manage project memory (observations, sessions, prompts)
|
|
2
|
+
description: "Search and manage project memory (observations, sessions, prompts). Use when: user asks about past work, wants to find a previous bugfix, check project history, save a decision, or manage stored memories"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Memory
|
package/commands/memory.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Save content to memory — with explicit content, instructions, or auto-summarize current session
|
|
2
|
+
description: "Save content to memory — with explicit content, instructions, or auto-summarize current session. Use when: the user asks to remember something, after solving a non-obvious problem, or to capture key session findings"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Memory Save
|
package/commands/recall.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Recall past observations for a file before editing
|
|
2
|
+
description: "Recall past observations for a file before editing. Use when: about to edit a file, investigating a file with past issues, or before refactoring to check for past lessons"
|
|
3
3
|
argument-hint: <file_path>
|
|
4
4
|
---
|
|
5
5
|
|
package/commands/recent.md
CHANGED
package/commands/search.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Search memory for past bugfixes, decisions, discoveries
|
|
2
|
+
description: "Search memory for past bugfixes, decisions, discoveries. Use when: encountering a familiar error, investigating a module before changes, or looking for prior solutions to a similar problem"
|
|
3
3
|
argument-hint: <query>
|
|
4
4
|
---
|
|
5
5
|
|
package/commands/timeline.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Browse memory timeline around an observation
|
|
2
|
+
description: "Browse memory timeline around an observation. Use when: exploring what happened before/after a specific event, understanding the sequence of changes that led to a bug, or reviewing chronological context"
|
|
3
3
|
argument-hint: <observation_id>
|
|
4
4
|
---
|
|
5
5
|
|
package/commands/tools.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Import skills and agents from GitHub repositories into the tool resource registry
|
|
2
|
+
description: "Import skills and agents from GitHub repositories into the tool resource registry. Use when: looking for a skill to solve a problem, importing tools from a repo, or managing installed tools"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Tool Import
|
|
@@ -19,25 +19,14 @@ When the user invokes `/mem:tools`:
|
|
|
19
19
|
|
|
20
20
|
### With GitHub URL
|
|
21
21
|
|
|
22
|
-
1.
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
- `repo_url`: the GitHub URL
|
|
31
|
-
- `intent_tags`: comma-separated intent keywords (what the tool helps with)
|
|
32
|
-
- `domain_tags`: comma-separated technology/domain tags
|
|
33
|
-
- `capability_summary`: one-line description of what the tool does
|
|
34
|
-
- `trigger_patterns`: when to recommend this tool (natural language)
|
|
35
|
-
- `keywords`: additional search terms
|
|
36
|
-
- `tech_stack`: technology stack tags
|
|
37
|
-
- `use_cases`: usage scenarios
|
|
38
|
-
4. Call `mem_registry(action="import", ...)` for each tool with extracted metadata
|
|
39
|
-
5. Call `mem_registry(action="reindex")` to update FTS5 index
|
|
40
|
-
6. Report imported tools in a table format
|
|
22
|
+
1. Call `mem_registry(action="import_url", url="<github-url>", enrich=true)` to import all skills/agents
|
|
23
|
+
2. The import pipeline automatically:
|
|
24
|
+
- Fetches repo file tree via GitHub API
|
|
25
|
+
- Discovers SKILL.md/AGENT.md files
|
|
26
|
+
- Parses frontmatter and extracts metadata
|
|
27
|
+
- Downloads files to managed/ directory
|
|
28
|
+
- Runs LLM enrichment for semantic tags (when enrich=true)
|
|
29
|
+
3. Report imported tools in a table format
|
|
41
30
|
|
|
42
31
|
### With GitHub URL + instructions
|
|
43
32
|
|
package/commands/update.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Auto-maintain memory and resource registry — deduplicate, merge, decay, cleanup, reindex
|
|
2
|
+
description: "Auto-maintain memory and resource registry — deduplicate, merge, decay, cleanup, reindex. Use when: search results seem noisy, after bulk imports, or during periodic maintenance"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Memory & Registry Maintenance
|
package/hook-llm.mjs
CHANGED
|
@@ -24,7 +24,25 @@ function buildFtsTextField(obs) {
|
|
|
24
24
|
const factsText = Array.isArray(obs.facts) ? obs.facts.join(' ') : '';
|
|
25
25
|
const aliasesText = obs.searchAliases || '';
|
|
26
26
|
const bigramText = cjkBigrams((obs.title || '') + ' ' + (obs.narrative || ''));
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
// Degraded fallback: when LLM enrichment is missing, extract lightweight keywords
|
|
29
|
+
// from title + narrative so degraded observations remain FTS-searchable
|
|
30
|
+
let fallbackText = '';
|
|
31
|
+
if (!conceptsText && !factsText && !aliasesText) {
|
|
32
|
+
const raw = (obs.title || '') + ' ' + (obs.narrative || '');
|
|
33
|
+
// Extract file basenames (without extension) as searchable terms
|
|
34
|
+
const fileNames = [...new Set(
|
|
35
|
+
[...raw.matchAll(/\b([\w.-]+\.(?:mjs|js|ts|tsx|jsx|py|rs|go|vue|css|html|json|yaml|yml|md|sh|sql|toml|cfg))\b/g)]
|
|
36
|
+
.map(m => m[1].replace(/\.[^.]+$/, ''))
|
|
37
|
+
)];
|
|
38
|
+
// Extract error keywords from "→ ERROR: ..." patterns
|
|
39
|
+
const errorTerms = raw.split(/→ ERROR[: ]+/).slice(1)
|
|
40
|
+
.map(s => s.split(/[;{[\]"\\|→\n]/)[0].trim())
|
|
41
|
+
.filter(t => t.length >= 4 && t.length <= 50);
|
|
42
|
+
fallbackText = [...fileNames, ...errorTerms].join(' ');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { conceptsText, factsText, textField: [conceptsText, factsText, aliasesText, bigramText, fallbackText].filter(Boolean).join(' ') };
|
|
28
46
|
}
|
|
29
47
|
|
|
30
48
|
/**
|
|
@@ -244,8 +262,14 @@ export function buildDegradedTitle(episode) {
|
|
|
244
262
|
// Extract meaningful error text from "cmd → ERROR: ..." format
|
|
245
263
|
const errMatch = errEntry.desc.match(/→ ERROR: (.{3,80})/);
|
|
246
264
|
if (errMatch) {
|
|
247
|
-
// Clean JSON/noise from the error snippet
|
|
248
|
-
const cleaned = errMatch[1]
|
|
265
|
+
// Clean JSON/noise/tabs/CI-status from the error snippet
|
|
266
|
+
const cleaned = errMatch[1]
|
|
267
|
+
.replace(/\t/g, ' ')
|
|
268
|
+
.replace(/[{"[\]]/g, '')
|
|
269
|
+
.replace(/\\n/g, ' ')
|
|
270
|
+
.replace(/\b(?:in_progress|completed|queued|failure|success|waiting)\b/gi, '')
|
|
271
|
+
.replace(/\s{2,}/g, ' ')
|
|
272
|
+
.trim();
|
|
249
273
|
if (cleaned.length >= 4) errorHint = `: ${truncate(cleaned, 50)}`;
|
|
250
274
|
}
|
|
251
275
|
}
|
|
@@ -267,7 +291,10 @@ export function buildDegradedTitle(episode) {
|
|
|
267
291
|
// No files: strip raw output (JSON, arrays, long tails) from Bash descriptions
|
|
268
292
|
const desc = episode.entries[0]?.desc || '(no description)';
|
|
269
293
|
return desc.replace(/ → (?:ERROR: )?[[{].*$/, hasError ? ' (error)' : '')
|
|
270
|
-
.replace(/ → .*---EXIT:\d+$/, hasError ? ' (error)' : '')
|
|
294
|
+
.replace(/ → .*---EXIT:\d+$/, hasError ? ' (error)' : '')
|
|
295
|
+
.replace(/\t/g, ' ')
|
|
296
|
+
.replace(/\s{2,}/g, ' ')
|
|
297
|
+
.trim();
|
|
271
298
|
}
|
|
272
299
|
|
|
273
300
|
/**
|
package/hook-memory.mjs
CHANGED
|
@@ -14,7 +14,7 @@ const MAX_FILE_RECALL = 2;
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Search for relevant past observations to inject as memory context.
|
|
17
|
-
* Quality gates: importance>=1 (with 0.6x penalty), type-boosted, lesson-boosted, BM25-thresholded (
|
|
17
|
+
* Quality gates: importance>=1 (with 0.6x penalty), type-boosted, lesson-boosted, BM25-thresholded (adaptive: 0 for <5 obs, 1.5 otherwise).
|
|
18
18
|
* @param {import('better-sqlite3').Database} db Memory database
|
|
19
19
|
* @param {string} userPrompt User's prompt text
|
|
20
20
|
* @param {string} project Current project
|
|
@@ -101,8 +101,14 @@ export function searchRelevantMemories(db, userPrompt, project, excludeIds = [])
|
|
|
101
101
|
})
|
|
102
102
|
.sort((a, b) => b.score - a.score);
|
|
103
103
|
|
|
104
|
-
//
|
|
105
|
-
|
|
104
|
+
// Adaptive threshold: BM25 IDF collapses when corpus has <5 observations,
|
|
105
|
+
// producing scores ~0.00001 even for exact matches. At 5+ obs, IDF provides
|
|
106
|
+
// meaningful discrimination and the calibrated 1.5 threshold works well.
|
|
107
|
+
const obsCount = db.prepare(
|
|
108
|
+
'SELECT COUNT(*) as c FROM observations WHERE project = ? AND COALESCE(compressed_into, 0) = 0',
|
|
109
|
+
).get(project)?.c || 0;
|
|
110
|
+
const threshold = obsCount < 5 ? 0 : 1.5;
|
|
111
|
+
if (scored.length === 0 || scored[0].score < threshold) return [];
|
|
106
112
|
|
|
107
113
|
// Update access_count for injected memories
|
|
108
114
|
const result = scored.slice(0, MAX_MEMORY_INJECTIONS);
|
package/hook.mjs
CHANGED
|
@@ -169,7 +169,7 @@ async function handlePostToolUse() {
|
|
|
169
169
|
// Build episode entry
|
|
170
170
|
const entry = {
|
|
171
171
|
tool: tool_name,
|
|
172
|
-
desc: scrubSecrets(makeEntryDesc(tool_name, toolInput, resp)),
|
|
172
|
+
desc: scrubSecrets(makeEntryDesc(tool_name, toolInput, resp, bashSig)),
|
|
173
173
|
files,
|
|
174
174
|
ts: Date.now(),
|
|
175
175
|
isError: bashSig?.isError || false,
|
|
@@ -517,6 +517,24 @@ async function handleSessionStart() {
|
|
|
517
517
|
`).run();
|
|
518
518
|
if (boosted.changes > 0) debugLog('DEBUG', 'auto-maintain', `boosted ${boosted.changes} frequently-accessed observations`);
|
|
519
519
|
|
|
520
|
+
// Auto-dedup: merge near-identical observations (same title, same project, within 1h)
|
|
521
|
+
const dupPairs = db.prepare(`
|
|
522
|
+
SELECT a.id as keep_id, b.id as remove_id
|
|
523
|
+
FROM observations a
|
|
524
|
+
JOIN observations b ON a.title = b.title AND a.project = b.project
|
|
525
|
+
AND a.id < b.id
|
|
526
|
+
AND ABS(a.created_at_epoch - b.created_at_epoch) < 3600000
|
|
527
|
+
AND COALESCE(a.compressed_into, 0) = 0
|
|
528
|
+
AND COALESCE(b.compressed_into, 0) = 0
|
|
529
|
+
LIMIT 20
|
|
530
|
+
`).all();
|
|
531
|
+
if (dupPairs.length > 0) {
|
|
532
|
+
const removeIds = dupPairs.map(p => p.remove_id);
|
|
533
|
+
const ph = removeIds.map(() => '?').join(',');
|
|
534
|
+
db.prepare(`UPDATE observations SET superseded_at = ?, superseded_by = 'auto-dedup' WHERE id IN (${ph})`).run(Date.now(), ...removeIds);
|
|
535
|
+
debugLog('DEBUG', 'auto-maintain', `auto-deduped ${dupPairs.length} near-identical observations`);
|
|
536
|
+
}
|
|
537
|
+
|
|
520
538
|
// Mark maintenance as done (24h gate) — even though compression runs in background
|
|
521
539
|
writeFileSync(maintainFile, JSON.stringify({ epoch: Date.now() }));
|
|
522
540
|
// Weekly summary grouping runs in background to avoid blocking SessionStart
|
package/hooks/hooks.json
CHANGED
package/install.mjs
CHANGED
|
@@ -452,7 +452,7 @@ async function install() {
|
|
|
452
452
|
]
|
|
453
453
|
};
|
|
454
454
|
|
|
455
|
-
const
|
|
455
|
+
const memPreToolRecall = {
|
|
456
456
|
matcher: 'Edit|Write|NotebookEdit',
|
|
457
457
|
hooks: [
|
|
458
458
|
{
|
|
@@ -463,10 +463,30 @@ async function install() {
|
|
|
463
463
|
]
|
|
464
464
|
};
|
|
465
465
|
|
|
466
|
+
const memPreSkillBridge = {
|
|
467
|
+
matcher: 'Skill',
|
|
468
|
+
hooks: [
|
|
469
|
+
{
|
|
470
|
+
type: 'command',
|
|
471
|
+
command: `node "${join(SCRIPTS_PATH, 'pre-skill-bridge.js')}"`,
|
|
472
|
+
timeout: 3
|
|
473
|
+
}
|
|
474
|
+
]
|
|
475
|
+
};
|
|
476
|
+
|
|
466
477
|
// Filter out existing mem hooks, then append fresh ones
|
|
467
|
-
|
|
478
|
+
// PreToolUse has two separate matchers, so we register both
|
|
479
|
+
const hookConfigs = {
|
|
480
|
+
PreToolUse: [memPreToolRecall, memPreSkillBridge],
|
|
481
|
+
PostToolUse: [memPostToolUse],
|
|
482
|
+
SessionStart: [memSessionStart],
|
|
483
|
+
Stop: [memStop],
|
|
484
|
+
UserPromptSubmit: [memUserPrompt],
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
for (const [event, configs] of Object.entries(hookConfigs)) {
|
|
468
488
|
const existing = Array.isArray(settings.hooks[event]) ? settings.hooks[event].filter(cfg => !isMemHook(cfg)) : [];
|
|
469
|
-
settings.hooks[event] = [...existing,
|
|
489
|
+
settings.hooks[event] = [...existing, ...configs];
|
|
470
490
|
}
|
|
471
491
|
|
|
472
492
|
writeSettings(settings);
|