安装
在 DeepSeek Harness 里通过 dsh-market 安装
dsh plugin --profile web add dshmarket
或使用命令行
dsh plugin --profile web add dsh-claude-memory
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络。请先审阅源码,并尽量锁定 commit(github:owner/repo#sha)。
README
一个只读的 DeepSeek Harness 插件,把 Claude Code 已有的项目记忆接进 DSH —— 让 Claude Code 额度耗尽后,任务能在 DSH 里接着做。
English: 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(其@pathimport 会被内联展开)。 - 注册一个
claude_memory工具,让模型按需打开某条记忆、列项目、或跨主题搜索 —— 完整正文默认不进 prompt。 - 在内容到达模型服务商之前就把凭据形状的文本打码。
- 绝不写入、移动或删除 Claude home 下的任何文件。
安装
dsh plugin --profile web add dsh-claude-memory
需要重启 profile —— dsh.profile.bundles 里的新 bundle 是启动时读取的,不热加载。
从本地目录安装:
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 之后):
claude-memory:global——~/.claude/CLAUDE.md,@path已内联。DSH 自带的指令 加载器不解析 import,且读的是$DSH_HOME/AGENTS.md而非~/.claude/CLAUDE.md, 所以这里是用户级 Claude Code 记忆唯一的通路。claude-memory:memory—— 解析到的项目MEMORY.md索引,外加按新鲜度排序的其他 项目列表。
完整正文只通过工具按需返回,一次一个文件,单次 20 KB 上限。
项目解析
决定"该读哪份记忆"的是会话的工作目录,不是 dsh 服务启动的目录 —— 服务从某个
checkout 启动、会话在另一个目录跑的时候,两者必然不同。
解析顺序:
- exact ——
<home>/projects/<cwd 编码>/memory/存在; - git-root —— 外层 git 仓库根对应的键有记忆;
- 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 时跳过 |
共 76 项断言,无网络、无模型调用。
案例研究:两件容易做错的事
两个发现都来自阅读生态里已有的插件与一份真实记忆库。对任何"桥接另一个 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/,路径编码是项目目录把 / 换成 -。
问题在于哪个目录算项目。在作者机器上,对一个位于更大仓库内、自身没有 .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
评论
评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。