🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

记忆续存 kirkchinese/DSH-Recollect

DeepSeek Harness
kirkchinese0 天前有提交已提供中文文档扫描:含敏感能力

为 DeepSeek Harness 打造的兼容 Claude Code…

32.2/100
综合分
3
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add kirkchinese/DSH-Recollect
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · chat
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 0 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件
查看扫描证据(共 81 处,展示前 62 条)
  • 读取本地文件
    index.js:53
    st = statSync(dotgit);
  • 读取本地文件
    index.js:60
    try { text = readFileSync(dotgit, 'utf8'); } catch { }
  • 读取本地文件
    index.js:125
    persisted = { paused: {}, ...JSON.parse(readFileSync(stateFile, 'utf8')) };
  • 写入本地文件
    index.js:130
    await mkdir(dirname(stateFile), { recursive: true });
  • 写入本地文件
    index.js:131
    await writeFile(stateFile, JSON.stringify(persisted, null, 1));
  • 写入本地文件
    index.js:184
    mkdirSync(memoryDir, { recursive: true });
  • 写入本地文件
    index.js:240
    mkdirSync(dir, { recursive: true });
  • 写入本地文件
    index.js:244
    mkdirSync(dir, { recursive: true });
  • 读取本地文件
    index.js:246
    if (!lstatSync(dir).isDirectory() || !within(realpathSync(cwd), real)) refused = true;
  • 读取本地文件
    index.js:249
    if (!refused && existsSync(p)) {
  • 读取本地文件
    index.js:250
    const st = lstatSync(p);
  • 读取本地文件
    lib/remote.js:10
    export const PACKAGE = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).name;
  • 读取本地文件
    lib/instructions.js:35
    st = await stat(path);
  • 读取本地文件
    lib/instructions.js:45
    return await readFile(path, 'utf8');
  • 读取本地文件
    lib/instructions.js:78
    const real = await realpath(abs).catch(() => abs);
  • 读取本地文件
    lib/instructions.js:115
    const real = await realpath(dir).catch(() => undefined);
  • 读取本地文件
    lib/instructions.js:120
    entries = await readdir(dir, { withFileTypes: true });
  • 读取本地文件
    lib/instructions.js:131
    const st = await stat(p).catch(() => null);
  • 读取本地文件
    lib/paths.js:53
    const isDir = async p => (await stat(p).catch(() => null))?.isDirectory() === true;
  • 读取本地文件
    lib/paths.js:78
    const st = lstatSync(path);
  • 读取本地文件
    lib/paths.js:80
    return st.isFile() && validHeadText(readFileSync(path, 'utf8'));
  • 读取本地文件
    lib/paths.js:85
    const st = await lstat(path);
  • 读取本地文件
    lib/paths.js:86
    if (st.isSymbolicLink()) return validHeadLink(dir, await readlink(path));
  • 读取本地文件
    lib/paths.js:87
    return st.isFile() && validHeadText(await readFile(path, 'utf8'));
  • 读取本地文件
    lib/memtool.js:55
    const rootReal = await realpath(memoryDir);
  • 读取本地文件
    lib/memtool.js:59
    const real = await realpath(probe).catch(() => undefined);
  • 读取本地文件
    lib/memtool.js:66
    const st = await lstat(abs).catch(() => undefined);
  • 写入本地文件
    lib/memtool.js:79
    await mkdir(dirname(abs), { recursive: true });
  • 写入本地文件
    lib/memtool.js:81
    await writeFile(tmp, content, 'utf8');
  • 写入本地文件
    lib/memtool.js:82
    await rename(tmp, abs);
  • 读取本地文件
    lib/memtool.js:97
    const text = await readFile(abs, 'utf8').catch(() => undefined);
  • 读取本地文件
    lib/memtool.js:128
    if (await stat(join(dirname(abs), c)).then(s => s.isFile(), () => false)) {
  • 读取本地文件
    lib/memtool.js:150
    const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
  • 删除本地文件
    lib/memtool.js:269
    await rm(abs);
  • 读取本地文件
    lib/importer.js:18
    const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
  • 读取本地文件
    lib/importer.js:43
    const st = await lstat(from).catch(() => undefined);
  • 写入本地文件
    lib/importer.js:45
    if (!dryRun) await mkdir(to, { recursive: true });
  • 读取本地文件
    lib/importer.js:50
    const existing = await readFile(dest, 'utf8').catch(() => undefined);
  • 写入本地文件
    lib/importer.js:54
    await mkdir(dirname(dest), { recursive: true });
  • 写入本地文件
    lib/importer.js:55
    await copyFile(src, dest);
  • 读取本地文件
    lib/importer.js:58
    } else if (existing === await readFile(src, 'utf8')) {
  • 读取本地文件
    lib/importer.js:64
    const srcIndex = await readFile(join(from, INDEX_FILE), 'utf8').catch(() => '');
  • 读取本地文件
    lib/importer.js:67
    const destIndex = await readFile(destPath, 'utf8').catch(() => '');
  • 写入本地文件
    lib/importer.js:79
    await writeFile(destPath, `${base === '' ? '' : `${base}\n`}${add.join('\n')}\n`);
  • 读取本地文件
    lib/share.js:34
    const exists = (await lstat(dir).catch(() => undefined))?.isDirectory() === true;
  • 读取本地文件
    lib/recall.js:21
    try { entries = await readdir(dir, { withFileTypes: true }); } catch { return; }
  • 读取本地文件
    lib/recall.js:27
    const stat = await lstat(path);
  • 读取本地文件
    lib/recall.js:34
    const text = await readFile(path, 'utf8');
  • 读取本地文件
    lib/recall.js:139
    const text = (await readFile(path, 'utf8')).replace(/^ FEFF/, '');
  • 读取本地文件
    lib/dream.js:12
    return (await stat(lockPath(memoryDir)).catch(e => { if (e.code === 'ENOENT') return null; throw e; }))?.mtimeMs ?? 0;
  • 读取本地文件
    lib/dream.js:22
    const holder = Number.parseInt(await readFile(path, 'utf8').catch(() => ''), 10);
  • 写入本地文件
    lib/dream.js:25
    await writeFile(path, String(pid));
  • 读取本地文件
    lib/dream.js:26
    if ((await readFile(path, 'utf8')).trim() !== String(pid)) return null;
  • 读取本地文件
    lib/dream.js:31
    if ((await readFile(lock.path, 'utf8').catch(() => '')).trim() !== String(lock.pid)) return;
  • 删除本地文件
    lib/dream.js:32
    if (!lock.priorMtime) await unlink(lock.path).catch(e => { if (e.code !== 'ENOENT') throw e; });
  • 写入本地文件
    lib/dream.js:33
    else { await writeFile(lock.path, ''); const time = new Date(lock.priorMtime); await utimes(lock.path, time, time); }
  • 写入本地文件
    lib/dream.js:33
    else { await writeFile(lock.path, ''); const time = new Date(lock.priorMtime); await utimes(lock.path, time, time); }
  • 读取本地文件
    lib/dream.js:49
    const entries = await readdir(dir, { withFileTypes: true }).catch(e => { if (e.code === 'ENOENT') return []; throw e; });
  • 读取本地文件
    lib/dream.js:53
    const path = join(dir, log.name), s = await stat(path);
  • 读取本地文件
    lib/extract.js:43
    const entries = await readdir(dir, { withFileTypes: true }).catch(e => { if (e.code === 'ENOENT') return []; throw e; });
  • 读取本地文件
    lib/extract.js:50
    const [data, info] = await Promise.all([readFile(path, 'utf8'), stat(path)]);
  • 读取本地文件
    lib/extract.js:50
    const [data, info] = await Promise.all([readFile(path, 'utf8'), stat(path)]);
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-llm@deepseek-ai/dsh-tools@deepseek-ai/dsh-session@deepseek-ai/schemastery@deepseek-ai/dsh-typert-protocol
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
展开完整介绍(97 字)↓

为 DeepSeek Harness 打造的兼容 Claude Code 的记忆系统:CLAUDE.md/AGENTS.md 分层指令 + MEMORY.md 持久记忆,所有模型同一套完整指导。

README

由 DeepSeek 最新模型翻译生成
Recollect English · npm · 更新记录 · 问题反馈 · 兄弟项目 CiteCiter Recollect 换一个模型,记忆也不会丢。 Recollect 是 DeepSeek Harness(DSH) 的插件:一套兼容 Claude Code 的记忆系统。它沿用 Claude Code 的约定:按目录分层的 CLAUDE.md、@import、.claude/rules,以及以 MEMORY.md 为索引的持久记忆。DeepSeek、GPT、Qwen、Claude 等所有模型都拿到同一套完整的记忆指导,不会因为换了模型就被降级。 为什么需要它 在 Claude Code 里,记忆写作指导会随模型变化:不在它模型目录里的模型只能拿到一段很短的说明,"长期积累"、"用户说记住就立即保存"、各类型的示例都被删掉了。这类模型因此很少主动写记忆。Recollect 用自己编写的提示词(中文和英文两套)和完全一样的行为约定,让每个模型都能: - 在合适的时机主动记录用户画像、工作方式反馈、项目背景和外部资源指针; - 每个会话开始时读到记忆索引,并在需要时读取或召回记忆正文; - 记忆会过期,所以推荐之前先核验。
展开完整文档(README 剩余 1 节)
Recollect

English · npm · 更新记录 · 问题反馈 · 兄弟项目 CiteCiter

Recollect

换一个模型,记忆也不会丢。

Recollect 是 DeepSeek Harness(DSH) 的插件:一套兼容 Claude Code 的记忆系统。它沿用 Claude Code 的约定:按目录分层的 CLAUDE.md、@import、.claude/rules,以及以 MEMORY.md 为索引的持久记忆。DeepSeek、GPT、Qwen、Claude 等所有模型都拿到同一套完整的记忆指导,不会因为换了模型就被降级。

为什么需要它

在 Claude Code 里,记忆写作指导会随模型变化:不在它模型目录里的模型只能拿到一段很短的说明,"长期积累"、"用户说记住就立即保存"、各类型的示例都被删掉了。这类模型因此很少主动写记忆。Recollect 用自己编写的提示词(中文和英文两套)和完全一样的行为约定,让每个模型都能:

- 在合适的时机主动记录用户画像、工作方式反馈、项目背景和外部资源指针;
- 每个会话开始时读到记忆索引,并在需要时读取或召回记忆正文;
- 记忆会过期,所以推荐之前先核验。

与 Claude Code 的兼容范围

| 内容 | 兼容程度 |
| 指令文件 | 直接读取同一份文件:~/.claude/CLAUDE.md、~/.claude/rules/、项目里的 CLAUDE.md、.claude/CLAUDE.md、.claude/rules/、CLAUDE.local.md、@import、/etc/claude-code。另外还会读取 AGENTS.md |
| 记忆格式 | 一致:每条记忆一个文件,带 frontmatter(name、description、metadata.type),四种类型,MEMORY.md 作索引 |
| 记忆存储 | 默认分开存放:Recollect 写在 ~/.dsh/memory/projects/<slug>/,Claude Code 写在 ~/.claude/projects/<slug>/memory/。两边的 slug 计算规则相同 |
| 已有的 Claude Code 记忆 | 用 /memory-import 复制过来(all 导入全部项目,--dry-run 只预览)。来源只读,不会覆盖已有文件,索引按链接去重后合并 |
| 共用同一份记忆 | 可选全局共用(memory.shareWithClaudeCode: true)或按项目共用(memory.shareProjects)。面板中的「与 Claude Code 共用本项目」开关控制当前项目;「预览导入 → 确认导入」按钮用于一次性复制。共用时两边会修改同一批文件,Claude Code 的后台整理也会整理它们 |
| 未实现 | 团队记忆(CLAUDE_MEMORY_STORES)、账号记忆、# 快捷写入 |

安装

| DSH 版本 | Recollect 版本 |
| 0.2.0-rc.2 及以后(含官方桌面版;已测 0.2.1-rc.2、0.2.1-rc.5) | 0.5.0 |

官方桌面版: 在插件页安装 npm 包 @kirkchinese/dsh-recollect,或在 Git 地址一栏填写 https://github.com/kirkchinese/DSH-Recollect.git,然后按提示重启。

命令行与 Web:

dsh plugin --profile web add @kirkchinese/dsh-recollect@0.5.0

也可以从仓库安装某个发布标签:

dsh plugin --profile web add "git+https://github.com/kirkchinese/DSH-Recollect.git#v0.5.0"

安装后重启 DSH 并刷新页面,新会话生效。Recollect 会接管指令文件的加载,并自动过滤 DSH 自带的 AGENTS.md 加载器注入的内容,避免同一份文件被读两次。

在 Windows 上安装

在 PowerShell 中通过 npm 包安装(官方桌面版也可以在插件页填写同一个 npm 包名):

dsh plugin --profile web add "@kirkchinese/dsh-recollect@0.5.0"

安装后重启 DSH 并刷新页面。已知的平台差异:

- 后台提取和 dream 的子代理只放行 bash 只读命令(以及受限的记忆删除命令);Windows 上 DSH 使用 pwsh,因此它们不能运行 shell 命令。这两个功能默认关闭;常规记忆读写、共用与导入不受此限制。
- instructions.managedDirs 默认是 /etc/claude-code,Windows 通常没有这个目录;需要组织策略时改成实际存在的绝对目录。
- slug 与 Claude Code 使用同一规则:C:\Users\me\proj 会变成 C--Users-me-proj,按项目共用时能匹配同一份记忆。

版本支持策略

engines.dsh 只设下限 >=0.2.0-rc.2,DSH peer 依赖也使用开放范围,因此新的 DSH rc 不会被安装检查拦下。如果宿主接口变化,相关功能会报错,而不是因为版本上限导致整个插件被拒绝加载。CI 覆盖上表两个已测宿主版本,以及 Linux / Windows;开放范围并不代表未来接口永远不变。

它做什么

| 功能 | 默认 | 说明 |
| 指令文件 | 开 | 按层级加载:组织策略 /etc/claude-code → 用户(~/.claude/CLAUDE.md、~/.dsh/AGENTS.md、~/.agents/AGENTS.md、~/.claude/rules)→ 从根目录到当前目录逐层加载 CLAUDE.md/AGENTS.md(@import 就地展开,最多 4 层)→ .claude/CLAUDE.md → rules(递归 .md)→ CLAUDE.local.md/AGENTS.local.md。各层并列拼接,不截断;单个文件超过 4 MiB 时跳过 |
| 持久记忆 | 开 | system prompt 里放完整的记忆写作指导:四种类型、不该存什么、两步保存法、访问与核验规则。首条消息注入 MEMORY.md 索引,超过 200 行或 25 000 字符时截断并附警告 |
| 刷新 | 开 | resume 时只注入发生变化的文件;上下文压缩把指令摘要掉以后整份重新注入;切换目录或暂停、恢复记忆时也会刷新 |
| 子目录发现 | 开 | 读写子目录里的文件时,注入该目录的指令文件,以及 frontmatter 里 paths: 匹配的条件 rule |
| 写后检查 | 开 | 写入记忆后立即提醒:索引超限、单个文件超过召回上限、缺少 description、出现 -2 式的续写文件 |
| 召回 | 关 | 每次提问时检索最多 5 条相关记忆正文。bm25 在本地检索,支持中英日韩;selector 用一次模型调用挑选 |
| 后台提取 | 关 | 每轮结束后由一个分叉的子智能体补写主对话漏记的内容,写入范围被限制在记忆目录内 |
| 整理(dream) | 关 | 距上次整理超过 24 小时、且期间有 5 个以上会话时,合并、修正和精简记忆目录。也可以用 /dream 手动触发 |
| 智能体记忆 | 关 | 给指定预设的智能体一份独立记忆,作用域为 user、project 或 local |

记忆存放在 ~/.dsh/memory/projects/<git 根目录对应的 slug>/,同一仓库的不同子目录和 worktree 共用一份。

写入方式会自动适配沙箱。 沙箱允许写记忆目录时,模型直接用内置的 write/edit;在 workspace-write 等模式下记忆目录位于工作区之外,Recollect 会挂载专用工具 memory_list/read/write/edit/delete,指导文本也随之切换。这些工具只能写记忆目录里的 .md 文件,并会拒绝写入密钥。只读沙箱下不写记忆。

设置页

有两个入口:「设置」弹窗左侧的「记忆」分区,以及侧边栏「插件」→ Recollect → 组件行「recollect」。两处是同一份表单,可以调整全部选项:记忆开关与写入方式、记忆文本语言、是否与 Claude Code 共用目录、指令文件的措辞强度与排除规则、召回、后台提取和整理。保存后立即生效,并写入 profile 覆盖;当前对话里已经注入的内容会在下次刷新时更新。

「记忆」分区顶部是「当前加载情况」:选一个工作目录(默认取最近的根会话,排除子代理),列出会注入给模型的指令文件和记忆索引(类型与字符数),以及记忆目录里的主题文件(类型、描述、更新时间)。

面板显示 Claude Code 的记忆目录和主题文件条数。「与 Claude Code 共用本项目」按 Git 根目录保存选择;关闭时会移除该项目的全部别名。全局共用、设置固定 memory.directory 或表单不可写时,开关不可用。要保留独立副本,点击「预览导入」,检查复制/相同/冲突数量后再「确认导入」;冲突保留现有文件。切换目录或配置版本变化会清除旧预览。导入后,使用同一目标记忆目录的根会话会在下一次请求时刷新。

命令

| 命令 | 作用 |
| /memory | 查看记忆状态、写入方式和已加载的全部指令与记忆文件 |
| /pause-memory | 在本会话中暂停或恢复记忆,状态在重启后保留 |
| /remember <内容> | 让模型把这句话存进记忆 |
| /memory-import [all] [--dry-run] | 复制 Claude Code 已有记忆;单项目已共用则拒绝,all 跳过按项目共用的目录,--dry-run 只预览 |
| /dream | 立即整理本项目的记忆目录(需先开启 dream) |

记忆关闭时禁止导入。全局共用时 all 拒绝执行;单项目只在该项目实际共用时拒绝。固定 memory.directory 优先于共用选择;仅配置 shareProjects 时,all 会把所有项目导入固定目录,不跳过任何项目。来源始终只读,目标同名文件不覆盖,索引按链接去重合并。命令输出随 language 使用中文或英文。

配置

也可以直接编辑 profile 的 cordis.patch.yml。手写覆盖会替换整个 config 对象,未写出的字段使用默认值:

- id: recollect
name: '@kirkchinese/dsh-recollect'
config:
language: zh # 记忆文本与命令输出语言:zh / en memory: shareWithClaudeCode: false # 所有项目共用 Claude Code 记忆
shareProjects: [] # 按 Git 根目录匹配的绝对项目目录
directory: "" # 固定目录优先于全局和按项目共用 claudeHome: "" # 留空使用 $CLAUDE_CONFIG_DIR 或 ~/.claude recall: enabled: true method: bm25 # 或 selector extract: enabled: false dream: enabled: false minHours: 24 minSessions: 5 instructions: authority: strong # strong:指令文件优先于默认行为(与 Claude Code 一致);soft:仅作参考共用优先级:固定 memory.directory → 全局 shareWithClaudeCode → 按项目 shareProjects → 独立的 memory.root/projects/<slug>/。空或不完整的 .git 目录不再算仓库根;无有效仓库时使用工作目录。

全部字段见 lib/config.js。环境变量 DSH_RECOLLECT_DISABLE_MEMORY=1 关闭记忆(设为 0 则强制开启),DSH_RECOLLECT_DISABLE_INSTRUCTIONS=1 关闭指令文件。

与 Claude Code 的差异

- 所有模型拿到同一套完整指导,不按模型名单降级或关闭记忆。
- 提示词都是独立编写的,提供中文和英文两套,不包含 Claude Code 的原文。召回使用独立实现的 BM25F。
- 除 CLAUDE.md 全套文件外,也读取 AGENTS.md、.agents/rules、AGENTS.local.md;同一目录内内容相同的文件只注入一次。
- 注入前清洗不可见字符和控制字符,并转义文件内容和路径中的 </system-reminder>。
- 新增 /remember。设置在「插件」页里调整;/memory 以文本形式显示当前状态和已加载的文件。

已知限制

- DSH 在上下文溢出时,会在同一步内压缩并重试,不再经过注入流程,所以这一次重试看不到指令块,要到下一步才补回。
- headless 模式在一轮结束后立即退出,后台提取不会运行。
- 整理锁的做法是写入 PID 后回读校验,不是原子操作;两个宿主进程同时整理同一目录时可能冲突。同一个 DSH 主目录请不要同时运行两个宿主进程。

开发

npm test # node --test,需要能解析到 DSH 宿主包

本项目与 Anthropic 无关。"Claude Code" 只用来说明兼容的文件约定。

许可证

MIT
预览分享卡片长什么样 ↓kirkchinese/DSH-Recollect
分享 · DeepSeek Harness Hub
kirkchinese/DSH-Recollect

为 DeepSeek Harness 打造的兼容 Claude Code…

这是你的仓库?认领条目

回到顶部