dsh-claude-memory
Read-only bridge that surfaces Claude Code's existing project memory in DSH — git-root-aware resolution, credential redaction before injection, and an on-demand recall tool. Zero runtime dependencies.
Install
dsh plugin --profile web add dsh-claude-memory
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-claude-memory
一个只读的 DeepSeek Harness 插件,把
Claude Code 已有的项目记忆接进 DSH —— 让 Claude Code 额度耗尽后,任务能在 DSH 里接着做。
English: [README.md](README.md)
~/.claude/CLAUDE.md ─┐ @import 已内联 │ ~/.claude/projects/<key>/ ├─► 收敛 ─► 脱敏 ─► 字节预算 ─► DSH system prompt memory/MEMORY.md (索引) │ + claude_memory 工具 memory/<topic>.md (正文) ─┘
它做什么
- 把解析到的项目
MEMORY.md索引注入 system prompt,同时注入用户级
~/.claude/CLAUDE.md(其 @path import 会被内联展开)。
- 注册一个
claude_memory工具,让模型按需打开某条记忆、列项目、或跨主题搜索 ——
完整正文默认不进 prompt。
- 在内容到达模型服务商之前就把凭据形状的文本打码。
- 绝不写入、移动或删除 Claude home 下的任何文件。
安装
dsh plugin --profile web add dsh-claude-memory
需要重启 profile —— dsh.profile.bundles 里的新 bundle 是启动时读取的,不热加载。
包内声明了 engines.dsh 为 >=0.1.2-rc.1 <0.3.0:DSH 0.2 的运行时版本不匹配时会拒绝加载,
除非显式授予版本豁免。已在 0.1.2-rc.1 与 0.2.1-rc.4 上验证。
从本地目录安装:
dsh plugin --profile web add /path/to/dsh-claude-memory
验证配置树:
dsh --profile web --dump-config | grep -A 10 claude-memory
配置
默认值在包内的 cordis.patch.yml;可在 profile 的 patch 层或 --patch 覆盖层里改。
字段 · 默认 · 含义
claudeHome · ~/.claude · Claude Code home,所有读取都收敛在这个根下
cwd · process.cwd() · 仅兜底。会话的 agent.session.header.cwd 永远优先
includeDescendants · true · 是否列出有记忆的兄弟/子项目
descendantLimit · 12 · 最多列出几个其他项目
maxIndexBytes · 24000 · 注入记忆块的字节预算
maxGlobalBytes · 6000 · CLAUDE.md 加内联 import 的字节预算
enableMemory · true · 是否注入记忆块
enableGlobalInstructions · true · 是否注入用户级指令
enableTool · true · 是否注册 claude_memory 工具
redactMode · on · on 打码、report 只统计、off 关闭
refreshMs · 20000 · prompt 组装时若缓存超过此值就重读
实际进入模型的内容
两个 system prompt 段落(order 10 / 11,紧随部署 persona 之后):
1. claude-memory:global —— ~/.claude/CLAUDE.md,@path 已内联。DSH 自带的指令
加载器不解析 import;用户级文件它读的是 $DSH_HOME/AGENTS.md 而不是
~/.claude/CLAUDE.md(0.2 也会读项目级 CLAUDE.md,那是另一个文件),
所以这里是用户级 Claude Code 记忆唯一的通路。
2. claude-memory:memory —— 解析到的项目 MEMORY.md 索引,外加按新鲜度排序的其他
项目列表。
完整正文只通过工具按需返回,一次一个文件,单次 20 KB 上限。
项目解析
决定"该读哪份记忆"的是会话的工作目录,不是 dsh 服务启动的目录 —— 服务从某个
checkout 启动、会话在另一个目录跑的时候,两者必然不同。
解析顺序:
1. exact —— <home>/projects/<cwd 编码>/memory/ 存在;
2. git-root —— 外层 git 仓库根对应的键有记忆;
3. freshest —— 都没有时,在祖先与子项目里选索引更新时间最新的(并明确告知模型这是猜测)。
规则 2 的存在是因为 Claude Code 把记忆归档在 git 仓库根下,而不是原始工作目录 ——
见下方案例研究。规则 2、3 下其他项目按新鲜度倒序列出,最新那个一次工具调用就能取。
agent.session.header.cwd 与第一方 dsh-agent-instructions 读的是同一个来源。段落先以
全局注册作兜底,再在 agent/created 时按 agent 作用域 shadow;工具则按调用方
exec.agent 逐次解析,因此子 agent 或在别处启动的会话看到的是自己的项目。
claude_memory 工具
action · 参数 · 返回
projects · limit? · 所有有记忆的项目、条数、当前标记
index · project? · 某个项目的 MEMORY.md
read · project?、file? · 某个主题文件;不给 file 则列出主题
search · query、project?、limit? · 匹配行,格式 file:line: text
project 可传完整键、键后缀或唯一子串。所有返回都过同一个脱敏器并受 20 KB 上限约束。
安全模型
威胁模型很具体:为 A 厂商模型写的笔记,即将发给 B 厂商。
- 只读:没有任何代码路径写入、移动或删除
claudeHome下的文件。 - 收敛:每个路径都经
realpath解析且必须落在claudeHome内;指向外部的符号链接
会被拒绝;含路径分隔符的主题名会被拒绝。
- 注入前脱敏:13 条规则覆盖厂商 key 形状、Bearer token、PEM 块、JWT、带熵阈值的
password/token/secret 赋值、内联凭据的连接串、长 hex 串。误报代价是一行被打码,
漏报代价是把凭据发出去 —— 所以策略偏保守。
- 不可信框定:注入块明确告诉模型这些是背景笔记而非指令,记忆文本不能操纵 agent。
- 零依赖:只 import
node:*,信任链里没有第三方代码,且在node_modules为空
(不含 harness 包)的 profile 里也能解析。
- prompt 文本是字符串:provider 读同步刷新的缓存,永不返回 Promise。
脱敏是基于模式的,不是分类器,抓不住"不像凭据的凭据"。用 redactMode: report 审计
"哪些会被打码",用 enableMemory: false 作为总开关。
测试
npm test # smoke + plugin + schema + live npm run test:unit # 仅 fixture,不需要真实 ~/.claude
文件 · 覆盖范围
test/smoke.mjs · 路径、脱敏、存储、预算、工具 —— 全部跑在系统临时目录里的合成 fixture 上
test/plugin.mjs · 假 Cordis ctx 下的 loader 契约,含 agent 作用域 shadow
test/schema.mjs · 手写工具 schema 过真实 @deepseek-ai/dsh-tools 校验器;无 DSH 时跳过
test/live.mjs · 可选:对真实 ~/.claude 检查"没有凭据形状文本进入 prompt";无 store 时跳过
共 82 项断言,无网络、无模型调用。CI 会固定 @deepseek-ai/dsh-tools 版本并真实执行
test/schema.mjs,运行时不变量一变就直接失败,而不是跳过。
案例研究:两件容易做错的事
两个发现都来自阅读生态里已有的插件与一份真实记忆库。对任何"桥接另一个 agent 文件"的
DSH 插件都适用。
1. prompt provider 是同步的 —— async provider 会渲染成 Promise
dsh-system-prompt 解析段落与上下文文本时不做 await(lib/index.js:330,337),随后
插值器调用 text.indexOf(lib/index.js:152)。所以声明成 async () => string 的 provider
返回的是 Promise 而不是字符串。
生态里最知名的 Claude Code 桥接插件
(YYTbit/dsh-plugin-claude-bridge,
src/index.ts:75,92,107)注册的正是 async provider。能加载不等于能渲染 —— 插件挂载
正常,组装时才失败。本插件用同步刷新的缓存,返回普通字符串。
2. Claude Code 把记忆归档在 git 仓库根下
记忆位于 ~/.claude/projects/<项目路径编码>/memory/,路径编码是项目目录把 ASCII 字母和数字以外的每个字符换成 -(/home/me/.config 编码为
-home-me--config);超过 200 字符的编码会截到 200 并追加 -<完整路径哈希>,保证深路径仍可区分。同一仓库的所有 linked worktree 共用主 worktree 的键。
问题在于哪个目录算项目。在作者机器上,对一个位于更大仓库内、自身没有 .git 的文档
hub 实测:
观察项 · 值
该 hub 会话记录的 cwd · hub 目录
它写入 memory/ 的 Write/Edit 调用 · 12 次,全部落在外层仓库的键下
该会话引用外层仓库 memory/MEMORY.md 的次数 · 235
引用它自己目录 memory/ 路径的次数 · 0
hub / 父目录 / 祖父目录是否有 .git · 无 / 无 / 有
会话记录按 cwd 编码存放,记忆按 git 仓库根存放。按 process.cwd() 解析、或只认
精确 cwd 的桥接插件,在任何 hub 目录下都会静默读错项目的笔记。另外记忆目录是按需创建
的:那台机器 50 个项目目录里只有 20 个有非空索引,21 个是空 memory/ —— 所以"没有记忆
目录"很正常,不代表那个目录没被用过。
已知边界
- 不读会话流水。它读记忆文件,不读 transcript。"我一小时前在做什么"需要
*.jsonl,
不在本插件范围内。
- 不强制 Claude Code 自己的索引上限。Claude Code 只加载
MEMORY.md的前 200 行或
25 KB,超限的条目在那边会被静默丢弃;本插件按 maxIndexBytes 注入,不对该上限告警。
- 项目键有损。
-既是分隔符也是目录名的合法字符,所以展示路径靠遍历文件系统反查,
失败时退回键尾。
- 没有文件监听。新鲜度靠 prompt 组装时检查
refreshMs,所以一轮对话中途的改动会在
下一次组装生效。
- 只注入解析到的那一个项目的索引。其他项目需要工具调用。
许可证
MIT
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::2,最后更新 2026-10-29