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

项目记忆库 Jerry-Zhang-74/dsh-memory

DeepSeek Harness
Jerry-Zhang-7435 天前有提交已提供中文文档扫描:未发现敏感能力

让每个项目文件夹拥有跨会话的长期记忆

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · chat
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 35 天前
档位由哪些信号合成

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

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

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

写入剪贴板(不计风险)
查看扫描证据(共 1 处,展示前 1 条)
  • 写入剪贴板
    lib/client.js:304
    await navigator.clipboard.writeText(text)
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 按项目划分记忆库
  • 常驻上下文记忆索引
  • 对话中整理编辑

为 DeepSeek Harness 带来 Claude 风格的项目记忆:按项目划分的记忆库、常驻上下文的记忆索引、对话中随手整理与编辑,还支持批量导入导出。

README

由 DeepSeek 最新模型翻译生成
dsh-memory 为 DeepSeek Harness 打造的 Claude 风格项目记忆。 记忆比对话活得更久:你在某个会话里说过的事实,会出现在之后每一个绑定同一项目文件夹的会话里。记忆通过日常对话形成、修订、遗忘,也能批量导入导出。 没有运行时依赖,没有构建步骤——纯 ESM,装上就能用。 它解决什么问题 DSH 的会话彼此隔离:新会话对你之前聊过的东西一无所知。内置的 memory 工具是配置档目录的长期记忆;这个插件补上的是按工作目录划分的记忆: - 在 E:\MyApp 里定下的偏好、约定和架构决策,只出现在涉及 E:\MyApp 的会话里。 - 一个项目可以横跨多个文件夹(monorepo、前端加后端)。/memory bind 把另一个文件夹挂到同一个记忆库。 - 记忆是磁盘上的 markdown 文件——可读、可改、对 git 友好。
展开完整文档(README 剩余 1 节)
dsh-memory

为 DeepSeek Harness 打造的 Claude 风格项目记忆。

记忆比对话活得更久:你在某个会话里说过的事实,会出现在之后每一个绑定同一项目文件夹的会话里。记忆通过日常对话形成、修订、遗忘,也能批量导入导出。

没有运行时依赖,没有构建步骤——纯 ESM,装上就能用。

它解决什么问题

DSH 的会话彼此隔离:新会话对你之前聊过的东西一无所知。内置的 memory 工具是配置档目录的长期记忆;这个插件补上的是按工作目录划分的记忆:

- 在 E:\MyApp 里定下的偏好、约定和架构决策,只出现在涉及 E:\MyApp 的会话里。
- 一个项目可以横跨多个文件夹(monorepo、前端加后端)。/memory bind 把另一个文件夹挂到同一个记忆库。
- 记忆是磁盘上的 markdown 文件——可读、可改、对 git 友好。

快速上手

安装完成后(见下方安装),在任意会话里:

/memory 当前项目记忆的概览 /memory list 列出全部 /memory show <id> 完整读取一条 /memory import <path> 批量导入,交给整理会话处理 /memory import-now <path> 批量导入,直接合并 /memory export 把记忆包写到磁盘 /memory bind [folder] 把另一个文件夹挂到同一个记忆库 /memory projects 列出这台机器上所有记忆项目

你也可以直接说话——不用记命令:

“记住这个项目用 pnpm,不用 npm” “把那条 tabs 的记忆改成 2 空格” “关于构建配置,你记得什么?”
“忘掉那条关于旧 API 的” “导入这段记忆:”(然后粘贴)

模型会自己调用 memory 工具。

从 Claude 迁移

Claude 没有记忆导出格式——官方途径就是复制粘贴。所以导入器是照着真实导出实际长成的样子写的。在同一个人的导出里发现了四种形态,全部支持:

| 形态 | 长什么样 | 怎么读 |
| 新版 Settings 导出 | === MEMORY EXPORT ===、## IDENTITY,然后是 [unknown] - fact 行 | 每个小节一条记忆,小节名成为类型,[unknown] 标记被剥掉 |
| 旧版散文导出 | Work context / Personal context / Top of mind 标题下是段落 | 每个标题一条记忆 |
| 截图转写 | ### Title (updated 2026-08-03) 加上 Summary: / Details: | 标题清理干净,日期保留为元数据,同名卡片合并 |
| 旧版加粗导出 | Work context 下是一段文字 | 每个加粗标题一条记忆 |

小节名决定 type:INSTRUCTIONS / PREFERENCES / IDENTITY / Personal context 变成 user;PROJECTS / CAREER / Academic 变成 project;TECHNICAL SETUP / Environment 变成 reference。每个导入的条目都会打上 claude-import 和 section:<name> 标签。

两种运行方式:

/memory import <path> # curator conversation; best for large imports

或者把内容粘贴进去,让模型自己决定——它会调用 memory_import,先预览,只有你确认后才写入。

模型能用的工具

memory

| action | 作用 |
| save | 创建条目(name / body / type / description / tags / scope / pinned) |
| recall | 按查询给记忆排序;两个库都搜 |
| read | 按 id 或 name 取回完整条目 |
| update | 打补丁改字段;改 scope 会把条目移动到另一个库 |
| edit | 把正文里精确匹配的 oldText 替换成 newText |
| forget | 永久删除条目 |
| list | 列出条目,标注所属库,可按 type / tag / scope 过滤 |
| core | 读取或替换任一库中始终在上下文里的核心笔记 |
| bind / unbind | 挂载或卸载文件夹 |
| projects | 列出本机上所有记忆项目 |
| stats | 两个库的大小与结构 |

type 取以下之一:user(关于这个人的长期事实)、project(这个代码库或工作区)、feedback(纠正与偏好)、reference(指针与链接)。

memory_context

在明确的字符预算下组装记忆块,可选按查询聚焦。当始终存在的索引不够用时用它。

memory_files

Anthropic memory_20250818 动词接口——view、create、str_replace、insert、delete、rename——作用于虚拟根目录 /memories。提供它是为了让已经熟悉这些动词的模型不用重新学就能驱动这个库。

memory_import

对话式导入:把粘贴的文本作为 content 传入,或把文件、文件夹作为 path 传入。默认先预览,确认后提交。能识别上面迁移表里的所有格式,外加 JSON、JSONL、ZIP 和纯文本。

记忆如何进入对话

不依赖模型记得去看。每个会话都会拿到一个分层块:

- 核心笔记(core.md)——手工整理,始终相关;
- 记忆索引——每个条目一行(name、type、summary、tags、id),上限 200 行或 25 KB,先到先算;
- 只包含已固定条目的完整正文。

其余内容都只差一次 memory 调用。无论库长到多大,常驻开销都有上限。

刷新是变更驱动,不是定时驱动:先比较库的廉价签名(core.md 和 index.md 的 mtime 与大小、项目 id、文件夹数量),只有变了才重建块。刚保存的记忆下一步就在上下文里,而且每一步都不会重新读盘。

自行判断

提示词里明确说了,索引是刻意做紧凑的,而且依赖细节之前,应先通读全文。模型因此自行决定:

- 到底需不需要记忆(闲聊通常不需要);
- 该查哪个库(项目库还是个人库);
- 该读哪些条目(recall 负责排序,read 精确取一条);
- 新事实该归到哪里(scope)。

这就是这里「接入每一段对话,自行决定是否以及如何使用记忆」的含义:一份记录存在什么的目录,正文按需取用,而不是把历史一股脑塞进每次请求。

两个库:项目记忆与个人档案

两者分开存储、分开显示,因为它们回答的是不同问题:

| | 项目记忆 | 个人档案 |
| 存什么 | 关于这个文件夹的事实:架构、约定、构建怪癖 | 关于人的事实:偏好、背景、进行中的承诺 |
| 适用范围 | 绑定到该文件夹的会话 | 每一段会话,无论什么项目 |
| 归属 | 按工作目录自动分配 | 不挂到任何文件夹 |
| scope 取值 | project(默认) | global(别名 personal) |

保证:

- 任何文件夹都不会解析进档案库,所以项目不会意外变成这个人的档案。
- 改 scope 会移动条目,而不是给它换个标签——把项目事实提升到档案库后,原处不留副本。
- 读取不需要指定库:read 和 recall 会同时搜索两边,并给每条结果打上标记。
- 列在项目下的条目绝不会混进档案库,反之亦然。

UI 把它们显示为两个独立分区。

$DSH_HOME/storages/dsh-memory/projects/
├── <projectId>/ project memory (a bound folder set)
└── _personal/ the personal archive (no folders)

记忆页面

设置 → Memory 是一份卡片列表:

- 分成 Project memory 和 Personal archive,各自按类型分组(You / Topics / Areas)。
- 每行读作名称 — 一行摘要 — 时间。点击某行会打开全文,连同它的类型、标签、字符数和精确时间戳。
- Copy all 把整个库以 Markdown 放进剪贴板,并给一句简短确认,而不是写文件。

项目之外的对话

存在三个席位,关键区别在于离开项目不等于丢掉记忆。Claude 和 ChatGPT 就是这么做的:账号级记忆跟着人进入每一段对话,而项目自己的记忆留在项目内部。

| 席位 | 预设 | 携带的记忆 | 工具 |
| 项目会话 | standard、code、minimal、cordis | 项目记忆 + 个人档案 | 完整智能体 |
| 项目之外,带记忆 | chat | 仅个人档案 | 仅记忆 |
| 项目之外,什么都不带 | chat-empty | 无 | 无 |

chat — 项目之外,携带个人记忆

~/.dsh/.agent-presets/chat/
├── preset.yml name: 聊天 └── agent.cordis.yml persona (complete: true) + dsh-memory (personalOnly: true)

personalOnly: true 让个人归档成为该挂载点的全部记忆库:

- 普通 save 直接落进归档,不会拿进程工作目录凭空造出一个项目;
- read / recall / list 只搜归档,所以某个文件夹专属的笔记绝不会在这里冒出来;
- 注入的块只有归档本身——没有项目标题,因为压根没有项目;
- 而在这里保存的个人笔记确实会出现在每个项目会话里,这正是它属于账号级、而非本地级的原因。

没有文件、shell、搜索或委派工具:模型带着记忆到来,别的什么都没有,所以它没法假装看见一个自己无权访问的仓库。

chat-lite — 只有记忆,没有任何项目形态的东西

~/.dsh/.agent-presets/chat-lite/
├── preset.yml name: 独立对话 └── agent.cordis.yml persona (complete: true) + dsh-memory

形态相同,但记忆挂载点横跨两个记忆库。当你想要一场对话,它知道当前文件夹的记忆,又不带上编程工具集时,这个预设很有用。

chat-empty — 什么都不挂

~/.dsh/.agent-presets/chat-empty/
├── preset.yml name: 空白对话 └── agent.cordis.yml persona (complete: true)

只有一行:persona。没有工具,没有记忆,任何项目的东西都不带。当你想要一场两个方向都不泄漏任何东西的对话时,选它。

隔离是怎么做到的

以上每一个预设里,complete: true 都让 persona 成为整个系统提示词,所以全局身份和工具指引什么都不贡献。预设注册的工具也落在预设自己的作用域层里,而智能体通过作用域父子关系加入那一层——因此标准组合里的东西一概不继承。

~/.dsh/.agent-presets 是用户自己的预设目录,每次读取都会重新扫描,所以新增或改过的预设不用重启 DSH 就能生效。

存储布局

$DSH_HOME/storages/dsh-memory/
├── projects.json registry: project id → bound folders
├── state/host-active.json activation marker (see below)
├── state/agent-status.json latest self-check result, readable remotely
├── state/supervisor-heartbeat.json external supervisor heartbeat
└── projects/<projectId>/
├── project.json project metadata
├── core.md the always-in-context core note
├── index.md generated index, one line per entry
├── memories/<type>-<slug>.md one file per memory
├── imports/<timestamp>/ staged imports plus an audit trail
└── exports/<timestamp>/ written export bundles

一条记忆就是 YAML frontmatter 加一段 Markdown 正文:

id: "project-uses-pnpm"
name: "This project uses pnpm"
description: "package manager convention"
type: "project"
scope: "project"
tags: ["tooling"]
这个仓库用 pnpm;不允许 npm install。锁文件是 pnpm-lock.yaml。

modified 和 updated 一起写入,因为第一方 per-memory markdown 格式用的就是这个名字,这样这些文件才能被那套工具读取。四个 type 取值也沿用同样的先例。

写入是原子的(临时文件加重命名),带重试,并且对 Windows 共享冲突有兜底——没有这套机制,后台刷新正在读 core.md 时来了一个写入,就会报 EPERM。

导入与导出

导出 text /memory export

在 exports/<timestamp>/ 下写三样东西:

- 整个库的每个记忆一个文件的副本(memories/ 加 core.md),方便携带和 diff;
- <timestamp>.memory.json —— 单个自描述包,带 format / version 信封;
- <timestamp>.memory.md —— 一份人或模型都能读的摘要。

导出是纯序列化,不经过模型,所以没有任何内容被改写,也没有字段在中途丢失。

导入 text /memory import <path> # curator conversation (recommended) /memory import-now <path> # merge directly, no conversation

import 先暂存,再审核:

- 原始字节和解析结果落到 imports/<timestamp>/(import.json 机器可读,import.md 人可读);
- 打开一个隔离会话,附带明确的整理指令:去重、合并进已有内容、拒绝凭据和个人身份信息、丢掉闲聊和过期的任务状态、把碎片改写成自包含的事实;
- 这个会话是侧边栏里的真实会话,所以你能看到它做了什么,也能继续跟它对话。

因此导入的内容绝不会悄悄变成权威内容,导入失败也不会损坏库——暂存区就是审计记录。

重复项按 name + normalized body 指纹检测并计数。

安装

插件通过 DSH 的 bundle 补丁层挂载;不修改任何 DSH 源码。
bash
dsh plugin --profile desktop add link:E:/DSH-Memory

然后重启 DSH。
json
{
"name": "dsh-memory",
"type": "module",
"main": "lib/host/index.js",
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
yaml

cordis.patch.yml

- insert:
- id: memory
name: dsh-memory

插件集在 DSH 启动时就固定了。 改动 package.json、cordis.patch.yml 或宿主侧代码都需要重启;只有客户端 bundle 内容不用重启就能重新加载。~/.dsh/.agent-presets 下的用户预设是例外——每次读取都会重新扫描预设。

卸载 bash
dsh plugin --profile desktop remove dsh-memory

然后重启。库仍留在 $DSH_HOME/storages/dsh-memory/。

检查是否加载成功
apply() 会写一个标记,这样从进程外面就能确认激活状态,不用靠猜:
bash
cat ~/.dsh/storages/dsh-memory/state/host-active.json
json
{ "plugin": "memory", "activatedAt": "2026-09-29T16:15:40.918Z", "pid": 40608 }

加载器自己的视角也行:plugin_manager list_plugins 应该显示 include:memory,且 fiberPhase: "active"。

无人值守也能一直跑着

宿主机侧的改动只在启动时加载,而重启没法从会话内部驱动——从里面强制停止会弹出确认对话框,可能根本没人点。所以重启机制放在 DSH 外面。

tools/supervisor.mjs 就是那个外部程序。它只干一件很窄的事:DSH 没在跑,就把它打开。 它从不关闭任何东西,所以永远不会牵扯到对话框。它注册在启动文件夹里,并且:

- 每 8 秒轮询一次;
- 应用消失后等 15 秒再重新拉起;
- 每次运行最多重启 5 次,这样崩溃循环会停下来,而不是一直空转;
- 写 state/supervisor-heartbeat.json,方便检查它自己是否还活着。

计划任务(DSH-Memory-Watchdog)用同样的思路,按 5 分钟一次的节奏跑,作为第二道防线。

两者合起来就是:随时正常关闭 DSH,它会自己回来,并且加载新代码。

开发笔记

下面都是真实踩过的坑,代码里已经修好,留在这里是因为每一个都很容易再犯。

Config 导出必须是 Standard Schema

Cordis 加载器在调用 apply() 之前会校验插件的 Config: js const result = runtime.Config['~standard'].validate(config)

普通对象会抛 TypeError: Cannot read properties of undefined (reading 'validate'),fiber 永久变成 failed,而唯一的报错只有一句含糊的 failed to import。DSH 自带的 194 个插件里有 150 个根本没导出 Config——它是可选的。在补丁里用 config: 配置,默认值在自己的 apply() 里合并。

提示词段落名在每个作用域内必须唯一

重名会直接中断会话创建,报 prompt section "..." is already registered,而不只是丢掉那个块。所以固定段落名会让第二个存在的智能体直接坏掉。这里的注册按项目 id 做键,注册前会先查活跃注册表,因为同一个插件体在一个进程里可能被挂载多次(宿主机组合加一个预设),而每个实例都有自己的模块状态。

不要从 link: 挂载的插件里 import @deepseek-ai/

通过 link: 挂载的插件会解析到它的真实路径,Node 从那里就找不到 DSH 自己的包了(ERR_MODULE_NOT_FOUND)。这个插件零运行时依赖:工具定义由 lib/host/schema.js 从编写规范编译出来。

Windows 上的原子写入会撞上共享冲突

fs.rename 没法替换另一个句柄正打开着读取的目标,普通运行中也会出现:后台刷新正在读 core.md,同时来了一个写入。重试加复制兜底,再配一个并发压力测试。

基于时间的提示词缓存会提供过期上下文

TTL 设得太短,意味着回合中途保存的记忆,下一步就可能读不到。改为由变更驱动新鲜度。

对着真实服务测试,别用桩

重复分节这个 bug 能发出去,是因为测试用了假的 systemPrompt,往数组里塞数据,检测不出重复。prompt-section.test.mjs 挂载真实服务,并创建多个智能体。

目录结构 text lib/host/ ├── index.js 插件主体:工具、命令、按会话的提示词分节 ├── store.js 记忆库:项目解析、原子写入、索引、排序 ├── tools.js memory 和 memory_context 工具 ├── transfer.js 导入/导出格式、ZIP 读取器、暂存、合并 ├── claude-memory.js 四种 Claude 导出形态 ├── anthropic-compat.js /memories 动词接口 ├── import-tool.js 对话式导入 ├── chat.js 导入整理对话 └── schema.js 本地工具 schema 编译器(替代一个依赖)

chat-plugin/ dsh-chat:独立侧边对话(/chat)

tools/
├── supervisor.mjs 外部程序:DSH 没运行时把它打开 ├── watchdog.mjs 计划任务兜底,策略相同 ├── restart-driver.mjs 分离式重启辅助程序 ├── verify-after-restart.mjs 重启后的证据 └── status.mjs 写入可远程读取的状态面板

test/ 九个测试套件,73 个用例 docs/ DSH 插件架构参考 research/ 先例调研加下载的源码(不发布)

运行全部测试: bash
node --test test/.test.mjs
CLAUDE_EXPORT_DIR=/path/to/claude/export node --test test/claude-memory.test.mjs
node test/activate.mjs # activation against the real cordis runtime

先例

先例调研的发现:

- Anthropic 没有发布 JSON 交换格式。 记忆工具就是 /memories 下的纯文本文件;claude.ai 导入是把散文粘贴进去,由模型处理;Claude Code 完全没有记忆导入导出。所以持久交换格式是无人认领的地盘——这里的 bundle 是新东西,不是抄来的。
- 文件布局确实沿用了第一方先例:Claude Code 的自动记忆用一个索引文件(每条记忆一行,全量加载),加每条记忆一个 markdown 文件,带 type,取值 user|feedback|project|reference,还有 modified 时间戳,预算 200 行 / 25 KB。这里采用这套布局,而不是自己发明。
- 先例里反复出现的失败是静默数据丢失——导入器只留 20 个字段中的 7 个,导出器在 10,000 条记录处截断,导出漏掉整类记录。所以这里的导出是纯序列化,不带模型涉及其中,而暂存会保留原始字节。

许可证

MIT — 见 LICENSE。
预览分享卡片长什么样 ↓Jerry-Zhang-74/dsh-memory
分享 · DeepSeek Harness Hub
Jerry-Zhang-74/dsh-memory

让每个项目文件夹拥有跨会话的长期记忆

这是你的仓库?认领条目

回到顶部