基于项目逻辑的dsh记忆系统,项目基于交接文档,知识基于项目沉淀。以人实际工作的逻辑,做知识的聚合和交接。开放书写规范以…
36.4/100
综合分
2
GitHub 星标
250
npm 周下载
—/5
用户评分
数据截至 2026/10/22 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-memjnpm 源 · dsh-memj 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 12 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-memj @ 0.1.5
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/26 11:43:21
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 83 处,展示前 47 条)
- 读取本地文件lib/index.js:204
await readFile(p, "utf8"); - 写入本地文件lib/index.js:210
await mkdir(templatesDir(), { recursive: true }); - 读取本地文件lib/index.js:449
text = await readFile(templatePath(id), "utf8"); - 读取本地文件lib/index.js:458
const json = await readFile(legacyTemplatePath(id), "utf8"); - 读取本地文件lib/index.js:505
files = await readdir(templatesDir()); - 写入本地文件lib/index.js:532
await mkdir(templatesDir(), { recursive: true }); - 删除本地文件lib/index.js:546
await rm(p, { force: true }); - 读取本地文件lib/index.js:1405
const raw = await readFile(tagAutoStatePath(), "utf8"); - 写入本地文件lib/index.js:1424
await mkdir(dirname(p), { recursive: true }); - 写入本地文件lib/index.js:1426
await writeFile(tmp, JSON.stringify(st, null, 2), "utf8"); - 写入本地文件lib/index.js:1427
await rename(tmp, p); - 读取本地文件lib/index.js:3465
const text = await readFile(enIndexPath(), "utf8"); - 写入本地文件lib/index.js:3476
await mkdir(dirname(target), { recursive: true }); - 发起网络请求lib/index.js:12408
const res = await fetch(opts.endpoint, { - 发起网络请求lib/index.js:13575
const r = await fetch(url, { - 发起网络请求lib/index.js:13614
const r = await fetch(url, { - 执行外部命令来自配置/变量lib/index.js:14078
execFile(entry.command, [...entry.args, ...args], { - 发起网络请求lib/index.js:14911
const res = await fetch(endpoint, { - 发起网络请求lib/index.js:20867
const res = await fetch(pick.endpoint, { - 发起网络请求lib/client.js:168
const res = await fetch(`${PLUGIN_BASE}/api/${kind}`, { cache: "no-store" }); - 发起网络请求lib/client.js:381
const res = await fetch(`${PLUGIN_BASE}/api/health`, { cache: "no-store" }); - 读取本地文件lib/search-CwYiAh_G.js:1211
names = await readdir(dir); - 读取本地文件lib/search-CwYiAh_G.js:1639
if (abs === void 0 || !existsSync(abs)) return { - 读取本地文件lib/search-CwYiAh_G.js:1686
if (!existsSync(path)) return false; - 读取本地文件lib/search-CwYiAh_G.js:1693
if (!existsSync(path)) return false; - 读取本地文件lib/search-CwYiAh_G.js:1709
if (abs === void 0 || !existsSync(abs)) return false; - 读取本地文件lib/search-CwYiAh_G.js:3569
const text = await readFile(promptOverridesPath(), "utf8"); - 写入本地文件lib/search-CwYiAh_G.js:3585
await mkdir(promptsDir(), { recursive: true }); - 写入本地文件lib/search-CwYiAh_G.js:3841
await mkdir(promptHistoryDir(), { recursive: true }); - 写入本地文件lib/search-CwYiAh_G.js:3846
await writeFile(path, text, "utf8"); - 删除本地文件lib/search-CwYiAh_G.js:3979
await rm(join(dir, f), { force: true }); - 执行外部命令固定命令lib/search-CwYiAh_G.js:4239
execFile("git", [...cfg, ...args], { - 写入本地文件lib/files-baWgIuqu.js:28
await mkdir(dir, { recursive: true }); - 读取本地文件lib/files-baWgIuqu.js:39
return await readFile(path, "utf8"); - 写入本地文件lib/files-baWgIuqu.js:68
await writeFile(tmp, content, "utf8"); - 删除本地文件lib/files-baWgIuqu.js:71
await unlink(tmp).catch(() => {}); - 写入本地文件lib/files-baWgIuqu.js:97
await rename(from, to); - 读取本地文件lib/files-baWgIuqu.js:113
const names = await readdir(dir); - 删除本地文件lib/files-baWgIuqu.js:121
await unlink(path).catch(() => {}); - 读取本地文件lib/files-baWgIuqu.js:141
await stat(path); - 发起网络请求lib/web/app.js:263
const res = await fetch(`${API}${path}`, { cache: 'no-store' }) - 发起网络请求lib/web/app.js:275
const res = await fetch(`${API}${path}`, { - 写入剪贴板lib/web/app.js:963
await navigator.clipboard.writeText(sessionId) - 发起网络请求lib/web/app.js:5915
const r = await fetch(`${API}/templates`) - 发起网络请求lib/web/app.js:5952
const r = await fetch(API + '/kinds') - 发起网络请求lib/web/app.js:6190
const r = await fetch(API + '/kind/upsert', { - 发起网络请求lib/web/app.js:6233
const r = await fetch(API + '/kind/delete', {
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-api-session-controller@deepseek-ai/dsh-atomic-write@deepseek-ai/dsh-client-ui-commands@deepseek-ai/dsh-client-ui-renderer@deepseek-ai/dsh-client-ui-session@deepseek-ai/dsh-client-ui-sidebar@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-commands@deepseek-ai/dsh-home-paths@deepseek-ai/dsh-host-webserver给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
6 张 · 点击查看大图功能亮点
- 项目知识聚合与交接
- 交接文档管理
- 提示词前端编辑热加载
展开完整介绍(105 字)↓
基于项目逻辑的dsh记忆系统,项目基于交接文档,知识基于项目沉淀。以人实际工作的逻辑,做知识的聚合和交接。开放书写规范以及全插件提示词的前端编辑(热加载),用户可自行优化,或者让模型帮插件通过修改提示词自进化。
最近更新
在 GitHub 查看全部 →v0.1.52026-10-20
· 本版重点
· 正文图片终于能显示了 🖼️
之前 100% 裂图:marked 只生成 不做地址解析,浏览器按页面地址解析相对路径 ⇒ 必然 404。
现在服务端按「条目 ref 所在目录」读图(白名单 + 不许越界),前端把相对 src 改写成 /asset?entry=…&path=…。
· 「就此提问」在每一段都能用
块引用 / 多行表格这类块以前永远匹配不到 DOM(实测 5816 块里 17.5%),
现在服务端把每块拆成「叶子行」逐行匹配;且块匹配不到时降级而不是把整个功能关掉。
按钮也移到了选区下方(上方会与浏览器自带的选择框重叠)。
· 外链改在右侧栏内置浏览器打开
点外链 → 右侧栏内置浏览器;开不了(如 Web 版没有 browser tab 类型)自动降级新标签页并说明原因。
· 面板内升级
头部基础信息右边出现「检测到有新版本」胶囊 → 弹窗 → 一键升级。
走国内镜像(npmmirror),支持桌面版(Electron 需 ELECTRON_RUN_AS_NODE=1 + dsh-desktop-host/lib/cli.js)。
· 入库自动刷新
新增 entry / 项目 / 主题后,面板列表自动更新(≤10 秒)。
用目录指纹判断变没变(81ms,而全量扫描 450–755ms);只在页面可见时轮询、正在编辑时不刷新、保住滚动位置。
· 窄屏断点 900 → 700
主流手机 CSS 视口宽度都在 360–430,平板竖屏 744–834。
旧断点把平板竖屏也判成窄屏;700 之后手机仍走窄屏、平板放开成两栏、右侧栏 iframe 不受影响。
---
· 安装 / 升级
dsh plugin --profile web add dsh-memj@0.1.5
⚠️ 装完需要重启 DSH 才生效。
v0.1.42026-10-19
· DSH 0.2.1 兼容
· 修:context() 必须标 required: true
DSH 0.2.1 给 systemPrompt.context() 的注册项新增了 required?: boolean,
并引入 suppressRuntimeContext() / includeRuntimeContext:false ——
它们会把所有没标 required 的 context 一律摘掉(官方 README 原话:
*"Contributions marked required remain visible"*)。
memj 有两条 context,两条都是操作型上下文:
| context | 内容 | 被摘掉的后果 |
|---|---|---|
| memj:usage-guidance | 全部使用指导(何时调哪个工具 / 挂靠判据 / 归档流程) | 模型仍有 8 个工具,但不知道该在什么时候用、该填什么 |
| memj:reading-doc | 「文档内提问」注入的正文本身 | 模型手里没有正文,答非所问 |
⚠️ 两者都不报任何错 —— 只表现为「memj 忽然不好用了」。
· 记:headless 的能力边界(有意,附理由)
dsh-headless 明确不挂 HTTP server(其 patch 原话:*"It mounts no Host,
HTTP server, Web Runtime, or browser plugin"*)⇒ 在 --profile headless 下
memj 的 inject 永不满足、整个回调不执行。
这是有意的边界:知识写入面与人侧界面是同一交付物,拆开没有意义。
代码里已写明不要把它改成可选及其理由(那样会加载出一个"工具能用、设置页全 404"的半插件)。
· 核查结论:零断裂
对照 0.2.0-rc.2 → 0.2.1-rc.2 逐包核查:
· memj 真实 import 的 11 个符号:0 个断裂(同一位置);
· 用到的 20 个 ctx 服务:除与本插件用法无关的三个外全部相同;
· credentials 的四个方法(resolve/describe/set/unset):全在,形状未变;
· 消息 source.kind = plugin:dsh-memj:两版都合法。
· 新增套件
scripts/test-dsh021-compat.mjs(14 项,含反向自证)—— 已进套件链,56 → 57。
⚠️ 它守的是一处完全静默的失效:required 字段 0.2.1 才有,
而本仓 devDep 是 0.1.5 的类型 ⇒ tsc 看不见它(删掉也不报错)。
v0.1.32026-10-16
自 v0.1.2 以来的内容(12 个提交,33 文件 / +8021 −100)。
· 🔴 修一个会造成数据损坏的严重缺陷:封面 tags 序列化
如果你在用 0.1.2 或更早的版本,建议尽快升级。
事故:一个项目的交接文档被撑到 128 MB / 单行 1.34 亿字符,把 DSH 拖崩;重启后每次读那份文档都奇慢。
根因是封面 tags 的读写不对称(两个缺陷叠加):
· renderCover 把 tags 写成裸的逗号分隔串(tags: a,b)—— 在 YAML 里那是一个普通标量字符串,不是数组(theme 那行和条目 frontmatter 本来就是数组,只有封面这行漏了);
· 于是读回时按逗号 split,切出来的片段不还原转义。
合起来是放大器:某标签需要加引号(以 @ - : # [ ] 等开头,或含 : )⇒ 写成 "@通知" ⇒ 读回时那对引号留在标签内容里 ⇒ 下一轮再写又判定不安全 ⇒ 再转义一层 ⇒ 反斜杠 1→3→7→15…(2ⁿ−1)。
⚠️ 而 mutateCover 每次只改一条封面、却会重写整份文档的全部封面 ⇒ 改任意一条都在加轮数 ⇒ 攒够约 27 轮就炸。这解释了「之前一直没事、突然就炸」——前 26 轮都看不出来。
修复:tags 改用 renderYamlArray(写成 [a, b],由解析器反转义);并补上「逗号在任何位置都不能裸写」(修上面那条时暴露的另一个独立缺陷:在流式数组里 , 就是分隔符,标签 x,y 会被静默劈成两半)。
· 新增功能
文档内提问(reading) —— 一条知识/一份交接文档读起来太长时,可以直接就着某一段向它提问,问答会落成一条 kind: chat 条目并与那一段绑定;支持段落标记、选中工具条、「去会话」跳转、轮末自动同步、以及从 chat 条目「返回源知识」。
面板界面状态跨会话共享 —— 右边栏是每个会话一份,此前切会话回来面板就「回到默认页」。现在界面状态跨会话共享(零内核改动)。
· 验证
· pnpm run typecheck ✅
· 全套件 56/56 通过(v0.1.2 时 53 个,本次新增 3 个 reading 相关套件)
· check-pack 实测 tarball 31 文件 / 2402 KB,README 引用的 14 张截图都在包里
· check-peer-range 通过(6 个真实 DSH 版本装得上,上界有效)
· ⭐ 实测产物:npm pack 出 dsh-memj-0.1.3.tgz,解包后包内 version = 0.1.3、31 文件
· ⭐ 修复专项:renderCover → coverFromYaml 往返 ×10,9 类标签全部长度恒定、往返还原(修复前:@ 开头那条 23 → 8211 膨胀;含 : 那条直接抛 YAML 错)
· 安装 / 升级
dsh plugin --profile web add dsh-memj@0.1.3
⚠️ 升级请带上版本号——否则锁文件会把你钉在旧版。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成dsh-memj —— 让记忆跟着项目走
一个 DeepSeek Harness 记忆插件。
最大的卖点:你可以用它,做一个你的记忆系统
它给你足够颗粒度的读写工具,用硬校验守住数据安全与系统稳定;剩下的最大自由度留给你 —— 下面这些都能在界面上直接改,不改一行代码:
| 你能改的 | 改了就变 |
| 知识类型 | 知识分几类、叫什么、什么时候用 |
| 文档模板 | 记忆长什么样:有哪些节、每一节怎么写 |
| 全部提示词 | 模型被怎么指挥、协议怎么定 |
一个例子:我没有做「子代理异步沉淀」。
但你可以改工具协议 / 提示词,让模型在收尾前 fork 一个子代理去沉淀—— 这是系统性的功能变化,一样不改代码。
(再往前一步,就是给钩子写接口工具。)
一句话:你可以用这个记忆系统,做一个你的记忆系统。
核心设计思路
- 项目 = 记忆的实体 —— 一份规范由你自由设计的项目文档(默认就是一个 handoff),它就是这个项目的总入口,跨会话、跨实例都用它;
- 交接是主路径 —— 把一轮工作压成下一轮的起点,压缩可控、可编辑、可留档;
- 插件自动识别会话属于哪个项目,沉淀的知识与项目绑定;
- 按你实际的多项目开发情况去找共性,把通用知识升级为主题 / 全局;
- 传统的 tag / kind / 搜索照常可用,可以嵌套叠加。
展开完整文档(README 剩余 1 节)
dsh-memj —— 让记忆跟着项目走
一个 DeepSeek Harness 记忆插件。
最大的卖点:你可以用它,做一个你的记忆系统
它给你足够颗粒度的读写工具,用硬校验守住数据安全与系统稳定;剩下的最大自由度留给你 —— 下面这些都能在界面上直接改,不改一行代码:
| 你能改的 | 改了就变 |
| 知识类型 | 知识分几类、叫什么、什么时候用 |
| 文档模板 | 记忆长什么样:有哪些节、每一节怎么写 |
| 全部提示词 | 模型被怎么指挥、协议怎么定 |
一个例子:我没有做「子代理异步沉淀」。
但你可以改工具协议 / 提示词,让模型在收尾前 fork 一个子代理去沉淀—— 这是系统性的功能变化,一样不改代码。
(再往前一步,就是给钩子写接口工具。)
一句话:你可以用这个记忆系统,做一个你的记忆系统。
核心设计思路
- 项目 = 记忆的实体 —— 一份规范由你自由设计的项目文档(默认就是一个 handoff),它就是这个项目的总入口,跨会话、跨实例都用它;
- 交接是主路径 —— 把一轮工作压成下一轮的起点,压缩可控、可编辑、可留档;
- 插件自动识别会话属于哪个项目,沉淀的知识与项目绑定;
- 按你实际的多项目开发情况去找共性,把通用知识升级为主题 / 全局;
- 传统的 tag / kind / 搜索照常可用,可以嵌套叠加。
⇒ ⭐ 用最自然的项目逻辑、开发逻辑,完成知识的初筛与关联。
库就是一个目录(~/.dsh-memj/),拷走即迁移;项目文档与知识全部是 Markdown,可读、可 diff、可随时用编辑器打开。
快速开始
⭐ 推荐:直接在 DSH 界面里装 —— 左侧「插件」页 → 右上角 「添加插件」 → 输入框填 dsh-memj → 「安装」。
在 DSH 界面里添加插件
不用敲命令、不用重启,装完侧边栏就有入口。(安装源按你的网络情况选即可。)
也可以走命令行(等价):
dsh plugin --profile web add dsh-memj
- 📦 npm:<https://www.npmjs.com/package/dsh-memj>(pnpm add dsh-memj 也可)
- 🐙 GitHub:<https://github.com/jojoman2024/dsh-memj>
- ⚠️ 需 DSH 0.2.0-rc.2(该版本在 npm 上是 @next 标签;latest 是更老的 0.1.0-rc.6)—— 详见 安装 一节
目录
卖点与设计核心:
- 最大的卖点:你可以用它,做一个你的记忆系统
- 核心设计思路
- 知识跟着项目走 —— 项目是记忆的主线:每条知识都归属一个项目,其下再按主题 / 全局 / 类型 / 标签多维度复用
- 交接是主路径 —— 项目文档本身也是交接文档,把一轮工作压成下一轮的起点,压缩可控、可编辑、可留档
- 一切都开放 —— 文档模板、知识类型、全部提示词都可在前端进行编辑。
其余部分:
- 归属三层与升级机制
- 项目文档长什么样
- 知识怎么存怎么找
- 四种使用方式
- 三种召回机制
- 人侧面板
- 快速开始
- 安装
- 库长什么样
- 配置
- 边界与已知限制
- 开发
① 知识跟着项目走
这是整个插件的设计核心。
memj记忆插件,基于项目的根本逻辑,沉淀的知识跟着项目走,在根据项目共性升级成为主题知识、全局知识。
按照最自然的行为逻辑,天然聚合知识,贴近用户本身的需求
项目是主线:每条知识都归属一个项目,项目文档就是它的总入口:
┌──────────────────────────────────────────────┐
│ 项目 = 记忆的主线 │ │ │ │ HANDOFF-<项目>.md │ │ ├─ 叙事:这个项目一路怎么走过来的 │ │ └─ 知识索引:它旗下每条知识的一行说明 │ └──────────────────────────────────────────────┘ │ │ │ 聚合出共性 │ 再提炼 │ 同时打上 │ ▼ ▼ ▼ 主题(跨项目通用) → 全局(无条件注入) 类型 · 标签
⭐ 但主线不等于唯一的检索维度 —— 一条知识同时带着:
| 维度 | 作用 |
| 项目 | 主线。它在哪个项目里被做出来的 |
| 主题 | 跨项目复用:这条知识别的项目也用得上 |
| 全局 | 跨主题通用,无条件注入 |
| 类型 | 它是事实 / 陷阱 / 流程 / 决策 / 留档 |
| 标签 | 自由词,和标题、描述同级参与检索打分 |
⇒ 所以模型既能「顺着项目」检索(我先看看这个项目里有什么),也能「按话题」横着捞(凡跟「pnpm/发布」有关的知识,不管在哪个项目)。
两条路都通,而且可以叠加(例如「在主题 X 下,找带标签 Y 的」)。
三个视图,对应三种问法
① 知识 —— 一条知识长什么样
知识知识的封面归属可由用户二次编辑。
② 项目 —— 记忆的主线
项目
项目的归属同样可以编辑。项目文档的章节书写规范可在项目文档里单独编辑,包括压缩和归档规则。
③ 主题 —— 跨项目的那条线
主题
用户在新会话交接时,可以直接交接与项目相关的主题,获取相关主题的通用知识和关键上下文。
每个项目一份项目文档,它是这个项目的记忆总入口:
- 新会话接续这个项目时,读这一份就够——不用先猜「该搜什么关键词」;
- 每条知识都挂在某个项目下,索引就写在那个项目的文档里 ⇒ 顺着项目走下去,知识自然被带出来。
它和「一个扁平的大知识库」有什么不同:
| | 扁平知识库 | memj |
| 主线 | 没有,全靠关键词 | 项目——有确定归属,不会散落 |
| 找东西的入口 | 你得先想出关键词 | 以项目自然聚类知识,缩小召回范围更贴实际需求 |
| 换个角度找 | —— | 主题 / 标签横着捞,两条路都通 |
| 换了会话 / 换了机器 | 上下文要重建 | 沿着项目捡回来 |
⭐ 判据一句话:换一台机器、换一个会话,沿着「项目」能不能把上下文捡回来? 能 —— 这就是它要达到的效果。
⚠️ 为什么把项目当主线,而不是只按话题组织:话题会重叠、会变名、颗粒度难以控制, 光靠话题收不住——一件事做完了,相关的话题还在长。
而项目是有边界的:一件事做完了就是做完了,边界清楚。
⇒ 主线负责「收得住」,主题、标签、关键字负责「捞得着」。
② 交接:把一轮工作压成下一轮的起点
交接(handoff)是本插件的主路径。
它解决的问题是:怎么让老窗口的上下文压缩可控,可留档。怎么让新窗口不用从头交代背景。 说一句「做一次交接」或输入 /memj-handoff,它会引导模型把这一轮的成果落成两样东西:
| 落到哪 | 是什么 | 给谁看 |
| 项目文档 | 这一轮的进展、踩的坑、定下的方案 | 下一个接手的人(或模型) |
| 知识条目 | 可复用的结论:下次遇到同类问题该怎么办 | 将来任何一次检索或注入 |
⭐ 为什么必须是它,而不是「让模型写个总结」:
| | 随手写的总结 | memj 的交接 |
| 写到哪 | 聊天记录里,翻不回去 | 项目文档,有确定位置 |
| 写什么 | 模型自由发挥 | 由你写的规范决定(每节该写什么) |
| 下次怎么用 | 你得记得有这回事 | 新会话读项目文档即可接续 |
| 会不会丢 | 压缩后可能被丢掉 | 落盘成文件,永不被压缩 |
| 能不能改 | 改不了 | 普通 Markdown,直接改 |
| 会不会越写越长 | —— | 有留档机制(原文照收,不是有损摘要) |
⭐ 本质是一次主动压缩——把一轮百万 token 的过程,压成几 KB 「下一轮真正需要知道的」。但这次压缩是可控、可编辑、可留档的。
- 可控:压缩成什么样,由你写在项目文档的规范里(哪一节留什么、多少字该整理)。
- 可编辑:压完就是普通 Markdown,你可以直接改;改完下次交接照样沿用。
- 可留档:项目文档越写越长时,交接把最早写下的那些小节整段原文收进一条 history 留档知识,原处只留一行指针(完整 id + 一句话主题)—— ⭐ 所以压缩可以一直做下去,而原文一个字都没丢、随时能按 id 读回来。
⚠️ history 留档照常存在库里、在知识库页看得见、按 id 读得回,但不参与检索(搜索与热知识注入都会跳过它)——它不回答「下次该怎么做」,只负责「当初到底写了什么」。
⭐ 若把一段被取代的原文写成别的类型,它就会正常参与检索(通常不是你想要的)。
💡 这就是它和「让模型摘要一下」最关键的区别:摘要是有损的、而且你事后无从核对;留档是无损的、指针一直指得到原文。
它同时是「人」和「模型」之间的交接:你随时可以打开项目文档看它记了什么、写得不对就直接改——记忆不是你无法插手的东西。
不做全量交接也行:只想沉淀知识、不想动项目文档,或者反过来只更新文档不落知识,都可以分开做(见 四种使用方式)。
③ 一切都开放:模板、类型、提示词都由你定
memj 把三处本来该你说了算的东西全部开放出来。这三处正好对应「记忆长什么样」、「知识分几类」、「模型被怎么指挥」。
a) 文档模板与书写规范 —— 决定记忆长什么样
节名、节数、每节要干什么,都由你定:
模板
- 模板只在【新建项目】那一刻生效;已有项目记着自己的结构,改模板不影响它们。
- ⚠️ 节名不是文案,是程序协议 —— 读文档、重建骨架、统计信息都按节名来。
- ⭐ 每节标题下面那段规范,就是「这一节该写什么」(建模版时从模板复制过来),模型读文档时会拿到它、按它写;
- ⭐ 规范可以就地编辑:项目详情页每一节的规范块上都有「编辑」入口。
⇒ 你可以把项目文档改成你自己的工作方式:做研究的用「公司画像/竞争格局」,做工程的用「接口契约/踩坑记录」。留档规则也写在这里 (例如「这一节超过 20000 字符就把最早的小节收进 history 留档」)。
b) 知识类型 —— 决定知识分几类
知识类型
一条知识属于哪一类形态,由你的词表决定。内置五种: fact 事实 / trap 陷阱 / howto 流程 / decision 决策 / history 留档。
改它同时影响三处:模型能填哪些值(工具参数)、写库时的下拉、以及「这类知识该怎么写」的指导语。
- 内置类型的 id 与显示名锁死(库里已有几百条知识在用它们);
- 「什么时候用 / 怎么写」可改,改错了随时改回;
c) 提示词 —— 决定模型被怎么指挥
写给模型的每一段文本——协议、使用指导、11 个工具的说明、命令文案、各类内部提示词(评分、标签规范化、翻译、模板写作指导)——都能改:提示词
| 特性 | 说明 |
| 86 条 / 其中 64 条可编辑 | 按「谁在什么时候看到它」分组,每组带一句说明 |
| 改完即生效 | 存在库里,运行时内存覆盖源码设置(命令描述除外) |
| 可随时恢复默认 | 每条都能以源码设置为基线一键恢复 |
| 能写回源码 | 把库里的临时覆盖固化进源码,成为新的默认值 |
| 不怕改坏 | 带校验。长度上限;插槽校验(漏了/写错名字会被拒,并列出可用插槽) |
⭐ 它为什么重要:模型的行为很大程度上由提示词决定。
把这些交给你 ⇒ 如果用户有意愿和能力,完全可以设计目标让模型去循环优化提示词,在不改代码的情况下,实现插件的自进化。
⭐ 前缀缓存:使用指导走 context()(消息尾部),改它不破缓存;协议走 section()(消息最前面 node 0),改它会破一次缓存——页面上明确标出。
⚠️ 命令描述是例外:注册时已固化,改它要重启。界面上标注了每条的热生效档位。
归属三层与升级机制
知识按 项目 → 主题 → 全局 三层聚合。归属是累加,不是搬家:一条知识加了主题仍然是项目知识;加入全局仍然命中原来的主题 —— 所以它不会"升级上去就从项目里消失"。
项目(project) 只对这个项目成立的知识 ← 绝大多数知识在这里 ↓ 提炼共性主题(theme) 跨项目通用的那类(如「DSH 插件开发」) ↓ 再提炼全局(global) 跨主题通用,无条件注入
⭐ 这三层是"主线 + 复用"的关系,不是替代:项目回答"它在哪做出来的",主题回答"别的项目能不能直接用",全局回答"是不是每次都该带上"。一条知识可以同时命中三者。
升级是显式的、可审阅的:
- 升主题:写库时声明 theme,并必须引一句该主题描述里的话作为依据(工具会核对这句话是否真的在描述里——引不出会警告,提醒你重新判断)。
- 升全局:登记提名,由人侧审批页批准后才真正生效—— 模型不能自己批准。
审批页·待批列表
上图就是那个审批关口:左边是待批清单,右边能看这条知识的完整归属与索引(project / theme / kind / desc),下方是原文,看清楚再点「通过 / 否决」。
⇒ 这是本项目的一条硬边界:提名只给人看,模型能提、不能批。
⚠️ 升主题的门槛是有意的:判据是 「这条知识给该主题描述的那个场景下的【别的项目】用,成立吗?」 只对本项目成立的(某次改动、某个接口、某处实测数据)不挂——漏挂只是主题视图里少一条(可事后补),误挂会污染所有项目看到的主题视图。
⭐ 反过来说:一条知识没挂主题,不代表它不能被检索 ——它照样能按项目找到、按类型筛、按标签查出。
项目文档长什么样
每个项目一份 HANDOFF-<项目>.md,是活文档——记录这个项目一路怎么走过来的,不是"此刻的快照"。
它由正文和前端不可见的知识索引组成:
目标这一节该写什么 —— 模型读文档时会拿到这段规范,按它写(这一节的内容……)
当前进度(这一节的内容……)
……(节名与节数由你的模板决定)
(文件末尾:这个项目旗下全部知识的索引,每条一行)
- 标题 · 场景描述 · 归属 · 类型 · id
⭐ 索引只存在于项目文档里(唯一源头) ⇒ 不会出现"两份清单对不上"。
检索时只读这些索引,不必打开任何知识全文。
知识怎么存、怎么找
一条知识 = 一个独立 Markdown 文件(entries/<slug>-<uuid8>.md)。
项目文档(索引:标题 + 场景描述 + 归属) │ 只需要它就能判断"有没有我要的东西" ▼ 知识全文(独立文件,确认要用时才读;长文档还能按节读)
⭐ 这个拆分解决的是「查得动」与「读得少」的矛盾:合在一起的话,每次检索都得把几千字的知识全文读一遍,而其中绝大多数这次用不上。
模型侧典型三步:
memj_info 拿语境 id(有哪些项目 / 主题) ↓ memj_search 筛知识 —— 只回标题与描述 ↓ memj_read 取全文(长文档给节名清单,用 section 取具体节)
memj_search 的四个筛选维度可以任意叠加:
| 参数 | 作用 |
| project | 按归属项目筛 —— 顺着主线看"这个项目里有什么" |
| theme | 按命中主题筛 —— 横着捞"别的项目里同类的东西" |
| kind | 按形态筛(事实 / 陷阱 / 流程 / 决策 / 留档),与上面两个正交 |
| query | 检索词数组,在标题 / 描述 / 标签上打分(各自权重不同) |
⇒ 例如「在主题 X 下,找带某关键词的陷阱类知识」是一次调用就能表达的。
11 个工具,各管一件事:
| 工具 | 干什么 |
| memj_info | 只报语境(有哪些项目/主题及 id),不返回知识 |
| memj_search | 只找:按 project / theme / kind / query 筛,只回标题与描述 |
| memj_read | 只读:按 id 取全文;长文档给节名清单,用 section 取具体节 |
| memj_snapshots | 读本会话的读快照清单(跨项目证据的来源) |
| memj_handoff | ⭐ 写库主入口:叙事 + 索引 + 建主题 + 落盘知识 |
| memj_promote | 推进归属 / 改索引字段 / 项目与主题级操作 |
| memj_update | 更新已有一条知识的全文与索引字段 |
| memj_rebuild | 从项目文档重建派生层(主题 / 全局) |
| memj_template | 管理文档模板(两段式确认) |
| memj_normalize_tags | 标签规范化(合并同义、去泛化、补英文) |
| memj_score_turn | 轮末复盘评分(由子代理调用) |
⭐ memj_read 匹配不到节名时会回【节名清单】,这不是报错而是设计:否则会形成"想按节读就得先知道节名、取节名只能先读全文"的死循环。
四种使用方式
① 交接(主路径)
说「做一次交接」或 /memj-handoff —— 项目文档 + 知识一次落盘。
② 单独沉淀知识
只想记一条、不想写交接?说「把这条记到知识库」,用 step: 'knowledge' 只落知识,项目叙事一字不动。
③ 只写 / 只改项目文档
step: 'narrative' 只更新文档;docSection + docOp 可以只动一节 (append / prepend / replace / drop),不必重写整篇。
④ 直接问库
模型按需调 memj_search / memj_read——两条路都能走:
- 顺着项目问:「dsh-memj 这个项目里有哪些关于发布的坑?」
- 按话题横着问:「不管哪个项目,凡是跟 pnpm / 打包 / 发布有关的都捞出来」
- 按形态问:「有哪些是"陷阱"类的?」(kind 维)
- 叠加:「在主题『DSH 插件开发』下,找带某关键词的流程类知识」
你也可以用斜杠命令: /memj-get-handoff-project、/memj-get-handoff-theme、/memj-review。
⭐ 叙事与知识分离:项目文档写"这个项目是怎么走到今天的",知识写"下次遇到同类问题该怎么办"。前者解决接续,后者解决复用。
三种召回机制
memj 默认不推——它假设模型会主动查。但 LLM 的工具调用主动性并不稳定,所以另配了三种可选的召回通道(都默认关,用哪个开哪个)。
① 热知识注入 —— 把相关知识推进上下文
会话开始时,把与本次会话相关的知识(标题 + 场景描述)注入进去;并带一个看门狗:检测它们是否还在模型可见的窗口里, 丢了就补注、有新的就增量补(只补缺的那几条,不重发全量)。
来源有四层,逐条可设:用户画像 / 全局知识 / 项目知识 / 主题知识。
人侧面板分三个视图,各回答一个问题:
① 会话实时状态 —— 这一次到底注进去了什么
热知识·会话实时状态
回答"为什么模型看到了 / 没看到":
- 判定与绑定:这个会话被判定成哪个项目,以及怎么判的 —— 上图显示判成了 dsh-memj,来源标注为 doc-reads 自动;也可以手动绑定,或勾上"每轮额外用 LLM 判定"(会额外消耗 token)。
- 热知识注入情况:本次共 17 条 / 约 4410 字符,逐条列出它来自哪一层(画像 / 全局知识 / 项目知识)、以什么形态注入(标题形态 = 只给标题 + 描述;正文形态 = 连知识全文一起给),以及是否 已注入。
- ⚠️ 这一页只读(要改条目去「项目热知识」页)—— 它是验收视图,不是编辑视图。
② 全局热知识 —— 无条件注入的那部分
热知识·全局热知识
- 用户画像:你手写的内容(Markdown,原样注入),逐条可「编辑 / 取消纳入 / 删除」。
- 全局知识:跨主题通用的那些 —— 本页不做编辑(要改去知识库页),所以按钮只有「取消纳入」和切换注入形态(正文形态 ↔ 改为标题形态)。
- ⭐ 每条都标了字符数,超预算的会标红(上图那条 2087 字符就标了「超过 1000 字符」)—— 因为「正文形态」是连知识全文一起注入,一条就能吃掉大半个预算。
③ 项目热知识 —— 按项目圈定"这个项目该带哪些"
热知识·项目热知识
左侧是从知识库里筛出的候选(可按类型 / 项目 / 主题筛,也能「全部加入 / 全部移出」),点「加入列表」把它纳入;右侧是已纳入的清单,每条可:
- 置顶(排在前面,优先注入);
- 切换注入形态(标题形态 ↔ 正文形态);
- 移出列表。
⇒ 三个视图合起来是"配置 → 生效 → 验收"的闭环:在 ②③ 里配,在会话里生效,在 ① 里看得见到底注进去了什么。
② 轮末评分 —— 决定"下次先注入谁"
每轮结束时起一个子代理复盘这一轮:问的不是"用到了什么",而是「假如它手上有这条知识,会不会做得更好」——发现"该给但没给"的那些并加分。
分数按项目存,热知识注入时取有效分前 N 名(滚动窗口,长期不用会自然掉出去)。
⭐ 价值在于发现缺口:已经注入过的知识再加分没有意义。
③ 主动注入 —— 模型没想到用工具时,替它把知识取来
一个 sidecar 判定:主模型思考结束后,判断这一步需不需要知识、以及模型的动作。如果所需知识在库里、而模型没有尝试去用工具,就直接把所需知识注入会话。
- 判定模型可配置:推荐 JEV 这类决策模型,或 DS4.1 等高吐出模型 (关掉思考等级时也可用)。
- ⚠️ 属实验功能,效果与你的模型/场景强相关,可自行摸索。
- 因为部分决策模型(如 JEV)对中文输入支持有限,专门配了 英文翻译通路并配合召回索引 —— 翻译模型可自行配置。
三道门(意图 / 充足 / 需求)与预算、并发、拆批都是可调旋钮, 每次召回的判据都落盘(recall-log/),调参时从真实分布读,而不是猜。
主动注入
人侧面板
入口在 DSH 侧边栏底部 —— 「memj 记忆库」:
侧边栏入口
点它在独立浏览器标签页打开(/plugins/dsh-memj/), 不挤占会话窗口,而且窄屏(手机)同样可用(两级导航 + 折叠筛选)—— 手机上看记忆库不必和会话抢地方。
| 页 | 干什么 |
| 知识库 | 搜 / 筛 / 读全文;编辑归属;按 id 直达 |
| 项目 | 项目文档全文 + 规范就地编辑 + 它聚合的知识与主题 |
| 主题 | 跨项目的共性知识视图 |
| 热知识 | 开关、会话实时状态、按项目的清单、全局知识、用户画像 |
| 审批 | 批准 / 否决 global 提名;格式非法的知识;断链外链 |
| 归档 | 把不再活跃的知识 / 项目下架(可随时恢复)—— ⚠️ 与上面的 history 留档是两回事 |
设置页 8 个子页:沉淀 · 热知识注入 · 主动注入 · 英文索引 · 提示词 ·决策模型配置 · 模板 · 知识类型。
设置页·决策模型配置
上图是其中的「决策模型配置」子页 —— 它管的是主动注入与轮末评分 这两条通路「用哪个模型来判」。
⭐ 为什么它必须单开一页:这类决策模型(如 JEV)的协议与 OpenAI 兼容接口不同(走 /v1/systemone,请求 {model, state, questions}、响应 {answers: {...}}), 装不进 DSH 原生的「模型」设置页(那页只支持 OpenAI / Anthropic 协议)。
- 支持多 provider:每个 provider = 一个端点 + 它自己的模型列表;
- 密钥名按 provider id 派生(knox → KNOX_API_KEY),不用你填;
- 两条通路(主动注入 / 热知识轮末评分)各选各的,可以选不同模型;
- ⚠️ 密钥只写不回显 —— 页面上只显示「密钥就绪 N/M」,不显示值;
- 每个 provider 的密钥互相独立,改一个不影响别的。
安装
插件是一个 DSH bundle(package.json 里声明了 dsh.bundle.patch)。
⚠️ 先确认 DSH 版本
本插件要求 DSH >=0.1.5-rc.2 <0.3.0(package.json 的 engines.dsh 与 peerDependencies 都写着)。
🔴 别直接按 latest 装 —— DSH 各包在 npm 上:
| dist-tag | 版本 | 能用吗 |
| latest | 0.1.0-rc.6 | ❌ 太老 |
| next | 0.2.0-rc.2 | ✅ 用这个 |
| alpha | 0.2.1-alpha.2 | ✅ 也可以 |
⇒ 装 DSH 时带上 @next:
pnpm add @deepseek-ai/dsh-agent@next # 而不是 @latest
⭐ 一条命令自检(会打印你当前装的版本):
node -p "require('@deepseek-ai/dsh-tools/package.json').version"
从 DSH 界面安装(推荐)
⭐ 左侧「插件」页 → 右上角 「添加插件」 → 输入框填 dsh-memj → 「安装」。
在 DSH 界面里添加插件
- 不用敲命令、不用重启:装完稍等几秒,侧边栏入口与 /plugins/dsh-memj/ 就绪;
- 安装源按你的网络情况选即可(国内可走镜像源);
- 升级同理:输入框里填 dsh-memj@<版本>(原因见下「升级时请带上版本号」)。
常规安装(命令行 / npm)
包页:<https://www.npmjs.com/package/dsh-memj>
dsh plugin --profile web add dsh-memj
也可以直接 pnpm add dsh-memj(等价;dsh plugin add 多做的是下面那两步自动登记)。
一条命令就够 —— 它会:① 在 profile 目录里跑 pnpm 把它装进 dependencies; ② 发现包里声明了 dsh.bundle ⇒ 自动把 dsh-memj 加进 dsh.profile.bundles; ③ 立刻装载。
⭐ 实测(全新 home + 全新 profile + 真 tarball):命令 exit 0,两条清单都自动写对了(不用手改 package.json、不用手建 junction); ⭐ 不重启就生效 —— 在正在运行的实例上装完,约 6 秒后侧边栏入口与 /plugins/dsh-memj/ 全部可用(进程启动时间未变,可证没有重启)。
更简单的,可以走 DSH 界面的插件管理页安装,同样不需要重启。
⚠️ profile 名按你自己的来:默认是 web,也可以是 tui / headless / 你自己建的。
⚠️ 升级时:请带上版本号
dsh plugin --profile web add dsh-memj@0.1.5 # ✅ 显式要这个版本
🔴 只写包名(add dsh-memj)可能把你留在旧版本:pnpm 会优先复用 profile 里 pnpm-lock.yaml 已经锁定的那个版本 —— 只要它满足 package.json 里的范围(如 ^0.1.0),pnpm 就认为「已经装好了」,不会去 npm 上看有没有新版。
⚠️ 这不是 npm 包或 GitHub 仓库的问题:全新环境(没有锁文件)装到的就是 latest;只有装过旧版的机器会被自己的锁文件钉住。
(界面上安装时,在输入框里填 dsh-memj@<版本> 即可。)
⭐ 锁文件本身是好东西,别手动删:它还钉着 tarball 的 integrity 哈希(防同名同版本被换内容)。
从 GitHub 直接安装(不经过 npm)
包已提交 lib/ 构建产物 ⇒ 仓库可以直接装(不需要本地构建):
dsh plugin --profile web add https://github.com/jojoman2024/dsh-memj
⭐ 这也是本仓特意提交 lib/ 的原因 —— 生态里多数 DSH 插件仓都这么做。
⚠️ 但日常建议用 npm 那条:它有版本号可追溯,files 白名单也保证包内只含运行时需要的东西(node_modules / scripts / src 之类不会被装进去)。
开发时安装(本地源码)
改源码时要 link:,这样跑的就是工作区里那份:
{
"dependencies": { "dsh-memj": "link:D:/.../adapt-memj" },
"dsh": { "profile": { "bundles": ["...", "dsh-memj"] } }
}
⚠️ 这条路上 dsh plugin ... add 那三步得自己做:在 node_modules 里建指向插件目录的 junction,在 bundles 数组里手工加一行。漏任一处都只是「没加载」,不会报错。
cordis.patch.yml(随包发出去的那个)
🔴 dsh.bundle.patch 是必需的:package.json 声明了它,缺文件会在 profile 加载期硬失败(dsh-app-boot 读 overlay 时直接抛)。
cordis.patch.yml
- insert:
- id: memj
name: dsh-memj
⚠️ 它只管「把插件挂进 profile」这一件事 —— 这个插件host 与 client 两半都有 (工具/协议/命令/路由 在 host,侧边栏入口与 /memj-review 弹窗在 client),而 client 的清单在 package.json 的 dsh.client 里,不在本文件。
⭐ id 是 memj,不是包名 dsh-memj ——设置面板要往 profile 的 patch 里写 - id: <这个值> 的 config 覆盖, 写错 id 的后果极隐蔽:boot 时只打一行 patch: entry "dsh-memj" not found 警告,配置静默不生效,而面板还显示「已保存」。
库长什么样
~/.dsh-memj/ ← 整个库 = 一个目录,拷走即迁移 ├─ projects/HANDOFF-<项目>.md ← 项目文档:【唯一写入源头】(叙事 + 知识索引) ├─ entries/<slug>-<uuid8>.md ← 知识全文(一条知识一个文档) ├─ themes/THEME-<id>.md ← 主题层:派生(手改会被 rebuild 覆盖) ├─ GLOBAL.md ← 全局层:派生 ├─ reads/session-<id>.jsonl ← 读快照(跨项目证据) ├─ doc-reads/session-<id>.jsonl ← 项目文档读记录 ├─ recall-log/ score-log/ ← 两张追加型 jsonl 日志 ├─ hot/ ← 热知识:画像 / 逐条设置 / 逐项目清单 / 会话绑定 / 评分表 ├─ tags/vocabulary.json ← 标签词表 ├─ prompts/overrides.json ← ⭐ 提示词覆盖值(运行期唯一源头) ├─ templates/<id>.md ← 项目文档模板 ├─ kinds.md ← 知识类型词表 ├─ nominations.json ← 待批 global 提名 ├─ backup/ ← 知识全文镜像 + 它自己的 git 仓库 ├─ recall/en-index.json ← 英文召回索引 └─ .startup-diag.txt ← 启动诊断(排障第一手证据)
库根可用 MEMJ_DATA_DIR 覆盖(空白环境变量视为未设置)。
⚠️ 量库体积时 Get-ChildItem -Recurse 要加 -Force: backup/.git/ 是隐藏目录,不加会静默跳过 ⇒ 得到一个偏小 43% 的数字。
三种数据的性质(务必分清)
| | 纯投影(derived) | 积累的判断(source) | 用户数据 |
| 例 | themes/ · GLOBAL.md · recall/en-index.json | tags/vocabulary.json · hot/scores.json | prompts/ · templates/ · kinds.md |
| 丢了 | 重建就有 | 🔴 永久丢失 | 手工重配 |
| 维护法 | 全量重建 + diff | ⚠️ 只能增量对账,绝不重建 | 随库迁移 |
🔴 最大的一处陷阱:给 tags/vocabulary.json 照抄 en-index 那套「重建」逻辑,会静默抹掉所有积累的裁决——而且看起来像「修好了」(条数对、结构对,就是判断没了)。
配置
配置走 profile 的 cordis.patch.yml(- id: memj 那条的 config),或者直接用人侧设置页改(写的是同一个文件)。
78 个配置键,按功能分 8 组:读取 / 沉淀 / 热知识 / 知识评分 / 英文索引 / 标签规范化 / 主动注入 / JEV。设置页每一项下面都有一行「保存后」说明它怎么生效(✅ 立即生效(无需重启) 或 ⚠️ 需重启实例才生效)。
⚠️ 三个总开关默认关(hotInject / hotScoreOnTurnEnd / activeInject),与 memj 的一贯姿态一致:插件不推、模型主动查,用户显式开启才注入。
关着的时候一个监听器都不挂(零开销,也不出现在事件链上),诊断行会如实区分 xxx-registered 与 xxx-skipped(disabled)。
⚠️ 设置按「版块」保存,不按页 —— 每个版块有自己的保存按钮与结果行。
决策模型(JEV)配置
JEV 不是 OpenAI 兼容协议(走 /v1/systemone),DSH 原生「模型」设置页装不下它 ⇒ 本插件自带「决策模型配置」子页,支持多 provider,密钥只写不回显(存进凭据 seam,界面上永远只显示"已配置")。
边界与已知限制
- 不做向量检索 / rerank:检索是整词 includes + 权重累加(查询词由模型提炼)。
⚠️ 因此不做二次切分(入缓 这种无意义字串永远不会被凭空造出来)。
- 不做多实例隔离:一个库给全部实例用。⚠️ 多个实例同时评同一个项目时会丢更新(评分表的互斥作用域是本进程)——单用户单实例下够用,已知边界。
- 不自动修复断链的外部引用:只报告 + 给人一个"重填路径"的入口,不猜路径。
- 不迁移工作区里现存的 HANDOFF-.md。
- 主动注入是实验功能:效果与所选决策模型强相关,请自行摸索与调参。
- 库文件走 node:fs 直接读写:插件自己的 API 依赖"本机回环 + 你自己开的端口", ⚠️ 不要把它暴露到公网。
开发
pnpm run build # tsdown(host + client 两个条目)→ copy-web → 产物自检 → 提示词账本对账
pnpm run typecheck # tsc(src) + tsc(src/client)
pnpm run test # 57 个套件
pnpm run check # 上面三合一
🔴 别自己拼构建命令行:tsdown.config.ts 导出的是数组(两个条目),会打印两次 Build complete;用管道截断输出会杀掉上游进程 ⇒ lib/ 里只剩 client.js。用 scripts/build.mjs,它把顺序写死并做产物自检。
⚠️ run-all-tests.mjs 用 spawnSync(stdio:'pipe'):在管道受限的环境下(如某些沙箱)它会把全部套件报红且失败详情为空。那是环境问题、不是回归。
挂载与生效
| 改了什么 | 怎么生效 |
| src/web/(前端静态资源) | 刷新页面即可(每次请求现读磁盘) |
| src/*.ts(host 半边) | 🔴 必须重启实例(启动时 import 完,之后不再读盘) |
| 【提示词】设置页里的任何文本 | 改完即生效(命令描述除外,那个要重启) |
| cordis.patch.yml 的 config | 看 profile 的 patchReload:live ⇒ 热重载;startup ⇒ 要重启(没写 = live) |
⚠️ pnpm run build 之后必须重启才能让运行中的实例生效。
⭐ 改了前端刷新就变、只有 API 是旧的这种情况是有意为之(静态文件每次读盘),别据此判断"构建已生效"。
实现要点(改代码前先读)
- 库的读写必须走 node:fs,不能用 ctx.fs:后者受 DSH_PERMISSION_MODE / sandboxPolicy 管辖,默认 workspace-write 的可写根只有 workspace + tmp,而库在 ~/.dsh-memj/ ⇒ 会被 FS_SANDBOX_DENIED 拒掉。
- ctx.inject(['systemPrompt','tools','commands'], cb) 缺任何一个服务,回调就永不执行且无报错 —— 表现是"插件加载了但工具/命令一个都没有"。
所以启动诊断写进 .startup-diag.txt。
- 🔴 api.ts 的路由必须在 index.ts 里逐个注册:漏一个 ⇒ 请求落到 SPA 兜底、返回 HTML 而不是 JSON,前端 JSON.parse 报错,而服务端毫无异常。
- ⭐ 同一事实只能有一份实现:paths / store / parse / prompts / sections / edit —— 任何新增的"第二条维护路径"都必须同时新增一条能报红的检查。
- source.kind 是 plugin:dsh-memj(0.2.0 起没有 catch-all plugin 了)。
- 给模型写提示词时,绝不把运行时数字写进常驻文本(那会导致前缀缓存永不命中)。
- 🔴 改"看起来是 bug"的地方之前,先确认它不是"在替别的东西兜底" ——实例:知识类型的 enum 被 { ...kindParam(...) } 展开求值成静态值,看起来是"getter 失效";但它恰好满足了 DSH"参数必须是静态数据记录"的要求 ⇒ 按"修 bug"的思路加回 getter,4 个工具整条注册失败。
⭐ 判据:"能注册上" ≠ "设计对了" —— 两者可能由同一个 bug 维持。
发布(给维护者)
pnpm publish # prepublishOnly = build + 发布闸门,两道都过才发得出去
🔴🔴 lib/ 是构建产物、被 .gitignore 排除,而 files 又必须列它 ——这两条都对,错在它们之间没有守卫:
从 git 克隆出来的仓库里直接 npm pack,打出来的是个空壳包(只有 LICENSE / README.md / cordis.patch.yml / package.json,没有 lib/),而 npm 不报错 —— files 里列了不存在的路径,它只是跳过。
⚠️ 回执上 name / version / shasum 一应俱全,看起来完全正常。
⭐ 判据:要断言的是「产物」,不是「配置」。所以 scripts/check-pack.mjs 实测打包(npm pack --dry-run --json)再断言里面真有 lib/index.js / lib/client.js, 不看 files 字段;README 引用的截图也一并断言(否则 npm 上图全裂、GitHub 上却正常)。
它同时挂在 prepublishOnly 和测试套件里(前者保证发布时必过,后者保证平时就能报红)。
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
jojoman2024/dsh-memj
基于项目逻辑的dsh记忆系统,项目基于交接文档,知识基于项目沉淀。以人实际工作的逻辑,做知识的聚合和交接。开放书写规范以…
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,462已汉化DSH
73
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,848已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,246已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
DS Bot 是一个基于 DeepSeek Harness简称 dsh的 bot 插件,插件将 dsh 改造为 bot…
★ 4,717已汉化DSH
71.1
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,711已汉化DSH
71.1