claude-mem-lite 2.26.0 → 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.
Files changed (62) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.mcp.json +0 -0
  4. package/LICENSE +0 -0
  5. package/README.md +22 -5
  6. package/README.zh-CN.md +47 -63
  7. package/bash-utils.mjs +0 -0
  8. package/cli.mjs +1 -1
  9. package/commands/mem.md +1 -1
  10. package/commands/memory.md +1 -1
  11. package/commands/recall.md +1 -1
  12. package/commands/recent.md +1 -1
  13. package/commands/search.md +1 -1
  14. package/commands/timeline.md +1 -1
  15. package/commands/tools.md +9 -20
  16. package/commands/update.md +1 -1
  17. package/format-utils.mjs +0 -0
  18. package/haiku-client.mjs +0 -0
  19. package/hash-utils.mjs +0 -0
  20. package/hook-context.mjs +0 -0
  21. package/hook-episode.mjs +0 -0
  22. package/hook-handoff.mjs +1 -2
  23. package/hook-llm.mjs +35 -8
  24. package/hook-memory.mjs +9 -3
  25. package/hook-semaphore.mjs +0 -0
  26. package/hook-shared.mjs +0 -0
  27. package/hook-update.mjs +0 -0
  28. package/hook.mjs +19 -1
  29. package/hooks/hooks.json +10 -0
  30. package/install-metadata.mjs +0 -0
  31. package/install.mjs +23 -3
  32. package/mem-cli.mjs +155 -23
  33. package/nlp.mjs +0 -0
  34. package/package.json +5 -1
  35. package/project-utils.mjs +0 -0
  36. package/registry/preinstalled.json +0 -0
  37. package/registry-enricher.mjs +101 -0
  38. package/registry-github.mjs +54 -0
  39. package/registry-importer.mjs +352 -0
  40. package/registry-indexer.mjs +0 -0
  41. package/registry-retriever.mjs +0 -0
  42. package/registry-scanner.mjs +0 -0
  43. package/registry.mjs +36 -2
  44. package/resource-discovery.mjs +0 -0
  45. package/schema.mjs +0 -0
  46. package/scoring-sql.mjs +0 -0
  47. package/scripts/launch.mjs +0 -0
  48. package/scripts/pre-skill-bridge.js +78 -0
  49. package/scripts/pre-tool-recall.js +0 -0
  50. package/scripts/prompt-search-utils.mjs +47 -2
  51. package/scripts/user-prompt-search.js +105 -5
  52. package/secret-scrub.mjs +0 -0
  53. package/server-internals.mjs +0 -0
  54. package/server.mjs +184 -23
  55. package/skill.md +0 -0
  56. package/skip-tools.mjs +0 -0
  57. package/stop-words.mjs +0 -0
  58. package/synonyms.mjs +0 -0
  59. package/tfidf.mjs +0 -0
  60. package/tier.mjs +0 -0
  61. package/tool-schemas.mjs +9 -2
  62. package/utils.mjs +9 -2
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "2.26.0",
13
+ "version": "2.28.1",
14
14
  "source": "./",
15
15
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall"
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.26.0",
3
+ "version": "2.28.1",
4
4
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/.mcp.json CHANGED
File without changes
package/LICENSE CHANGED
File without changes
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 15 tools (search, recent, recall, timeline, get, save, update, stats, delete, compress, maintain, export, fts_check, browse, registry)
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 (flat layout)
199
- agents/ # Agent plugins (nested: agents/*.md + skills/*/SKILL.md)
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 on user prompts
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
- - **智能调度** -- 三级渐进式调度系统,在 SessionStart、UserPromptSubmit、PreToolUse 三个时机自动推荐最合适的 skill agent
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
- - **双语意图识别** -- 同时理解中文和英文用户意图(15+ 英文 + 12+ 中文意图类别)
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 插件(嵌套:agents/*.md + skills/*/SKILL.md
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
- -> 调度:根据当前操作上下文推荐 skill/agent(Tier 0→1→2)
297
-
298
- UserPromptSubmit
299
- -> 捕获用户提示文本到 user_prompts 表
300
- -> 递增会话提示计数器
301
- -> 交接:检测继续意图 注入上一次会话上下文
302
- -> 调度:根据用户实际提示推荐 skill/agent(Tier 0→1→2)
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
- 调度系统在编码会话中主动推荐合适的 skill 和 agent,采用三级渐进式架构:
318
+ 三层调用系统确保 managed 资源(`~/.claude-mem-lite/managed/` 中的 skill 和 agent)能被正确调用:
316
319
 
317
320
  ```
318
- Tier 0:快速过滤(<1ms
319
- -> 跳过只读工具(Read、Glob、Grep、LSP...)
320
- -> 跳过简单 Bash 查询(ls、cat、git status...)
321
- -> 跳过 Claude 已选择 Skill 或 Task agent 的情况
322
- -> 跳过 MCP 内部工具
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
- Tier 2:FTS5 检索(<5ms
331
- -> 用领域同义词扩展信号(15+ 英文、12+ 中文类别)
332
- -> BM25 排名搜索资源注册表
333
- -> 复合评分:BM25(40%)+ 仓库星标(15%)+ 成功率(15%)+ 采纳率(10%)
327
+ L2 Bridge(PreToolUse Skill hook,<30ms
328
+ -> 拦截 Skill("name") 调用,查询 managed 注册表
329
+ -> 匹配到 → 输出内容 + mem_use() 提示(防止原生 handler 报错)
330
+ -> 未匹配 放行到原生 Skill handler
334
331
 
335
- Tier 3:Haiku 语义调度(~500ms,仅 SessionStart)
336
- -> FTS5 置信度低或前几名结果分数接近时激活
337
- -> LLM 生成语义搜索查询以精细化检索
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
- | Skill | `~/.claude/skills/`(原生) | 简短提示:使用 `/skill <name>` |
362
- | Skill | 托管目录 | 注入完整 skill 内容(最大 3KB) |
363
- | Agent | 任意 | 注入 agent 定义,用于 `Task` 工具委托 |
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/bash-utils.mjs CHANGED
File without changes
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
@@ -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
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Show recent memory observations
2
+ description: "Show recent memory observations. Use when: checking what happened recently, reviewing session progress, or verifying recent changes were captured"
3
3
  argument-hint: [count]
4
4
  ---
5
5
 
@@ -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
 
@@ -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. Use WebFetch to fetch the repository README and skill/agent files:
23
- - Try `https://raw.githubusercontent.com/{owner}/{repo}/main/README.md`
24
- - Look for skill definitions (`.md` files with frontmatter), agent definitions, plugin.json
25
- - If the repo has a `commands/` directory, fetch skill files from there
26
- 2. Identify all skills and agents in the repository
27
- 3. For each tool found, extract metadata using your understanding of the content:
28
- - `name`: tool name (lowercase, hyphenated)
29
- - `resource_type`: "skill" or "agent"
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
 
@@ -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/format-utils.mjs CHANGED
File without changes
package/haiku-client.mjs CHANGED
File without changes
package/hash-utils.mjs CHANGED
File without changes
package/hook-context.mjs CHANGED
File without changes
package/hook-episode.mjs CHANGED
File without changes
package/hook-handoff.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // Extracted for testability — hook.mjs has module-level side effects
3
3
 
4
4
  import { basename } from 'path';
5
- import { truncate, extractMatchKeywords, tokenizeHandoff, isSpecificTerm } from './utils.mjs';
5
+ import { truncate, extractMatchKeywords, tokenizeHandoff, isSpecificTerm, LOW_SIGNAL_TITLE } from './utils.mjs';
6
6
  import {
7
7
  HANDOFF_EXPIRY_CLEAR, HANDOFF_EXPIRY_EXIT, HANDOFF_MATCH_THRESHOLD, CONTINUE_KEYWORDS,
8
8
  } from './hook-shared.mjs';
@@ -78,7 +78,6 @@ export function buildAndSaveHandoff(db, sessionId, project, type, episodeSnapsho
78
78
  }
79
79
 
80
80
  // 5. Key decisions — high importance observations (skip low-signal degraded titles)
81
- const LOW_SIGNAL_TITLE = /^(Error (while working|in)|Modified |Worked on |Reviewed \d+ files:)/;
82
81
  const decisions = db.prepare(`
83
82
  SELECT title FROM observations
84
83
  WHERE memory_session_id = ? AND COALESCE(importance, 1) >= 2
package/hook-llm.mjs CHANGED
@@ -6,7 +6,7 @@ import { existsSync, readFileSync, unlinkSync, readdirSync } from 'fs';
6
6
  import {
7
7
  jaccardSimilarity, truncate, clampImportance, computeRuleImportance,
8
8
  inferProject, parseJsonFromLLM,
9
- computeMinHash, estimateJaccardFromMinHash, cjkBigrams, EDIT_TOOLS, debugCatch, debugLog, OBS_BM25,
9
+ computeMinHash, estimateJaccardFromMinHash, cjkBigrams, EDIT_TOOLS, LOW_SIGNAL_TITLE, debugCatch, debugLog, OBS_BM25,
10
10
  getCurrentBranch,
11
11
  } from './utils.mjs';
12
12
  import { acquireLLMSlot, releaseLLMSlot } from './hook-semaphore.mjs';
@@ -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
- return { conceptsText, factsText, textField: [conceptsText, factsText, aliasesText, bigramText].filter(Boolean).join(' ') };
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
  /**
@@ -63,7 +81,7 @@ export function saveObservation(obs, projectOverride, sessionIdOverride, externa
63
81
  // "Error in X", "Modified X" titles are low-specificity → use longer dedup window
64
82
  // 7-day exact match prevents cross-day accumulation of "Modified package.json" noise;
65
83
  // 3-day Jaccard catches near-duplicates without blocking legitimately new observations
66
- const LOW_SIGNAL = /^(Error (while working|in)|Error: |Modified |Worked on |Reviewed \d+ files:|# |node |npm |npx |\(no description\)|\(error\)$)/;
84
+ const LOW_SIGNAL = LOW_SIGNAL_TITLE;
67
85
  if (obs.title && LOW_SIGNAL.test(obs.title)) {
68
86
  const sevenDaysAgo = now.getTime() - 7 * 86400000;
69
87
  const threeDaysAgo = now.getTime() - 3 * 86400000;
@@ -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].replace(/[{"[\]]/g, '').replace(/\\n/g, ' ').trim();
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
  }
@@ -266,8 +290,11 @@ export function buildDegradedTitle(episode) {
266
290
  }
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
- return desc.replace(/ → (?:ERROR: )?[\[{].*$/, hasError ? ' (error)' : '')
270
- .replace(/ → .*---EXIT:\d+$/, hasError ? ' (error)' : '');
293
+ return desc.replace(/ → (?:ERROR: )?[[{].*$/, 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
  /**
@@ -301,7 +328,7 @@ export function buildImmediateObservation(episode) {
301
328
  const ruleImportance = computeRuleImportance(episode);
302
329
  // Low-signal degraded titles ("Error in...", "Modified...") should not inflate importance.
303
330
  // Cap at 1 unless rule-based signals indicate genuine importance (error-in-test → 3, config → 2).
304
- const LOW_SIGNAL = /^(Error (while working|in)|Error: |Modified |Worked on |Reviewed \d+ files:|# |node |npm |npx |\(no description\)|\(error\)$)/;
331
+ const LOW_SIGNAL = LOW_SIGNAL_TITLE;
305
332
  const isLowSignal = LOW_SIGNAL.test(title);
306
333
  let importance;
307
334
  if (isReviewPattern) {
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 (>=1.5).
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
- // Strict threshold: raised from 1.0 to 1.5 to compensate for wider pool
105
- if (scored.length === 0 || scored[0].score < 1.5) return [];
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);
File without changes
package/hook-shared.mjs CHANGED
File without changes
package/hook-update.mjs CHANGED
File without changes
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
@@ -28,6 +28,16 @@
28
28
  "timeout": 3
29
29
  }
30
30
  ]
31
+ },
32
+ {
33
+ "matcher": "Skill",
34
+ "hooks": [
35
+ {
36
+ "type": "command",
37
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-skill-bridge.js\"",
38
+ "timeout": 3
39
+ }
40
+ ]
31
41
  }
32
42
  ],
33
43
  "PostToolUse": [
File without changes