把改动缘由与取舍沉淀成可校验的仓库笔记
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add iuuuuuuuu/dsh-agent-notes该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 14 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 90 处,展示前 70 条)
- 读取本地文件lib/notes.js:68
if (existsSync(skillsRoot)) { - 读取本地文件lib/notes.js:70
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) { - 读取本地文件lib/notes.js:73
if (existsSync(scripts)) candidates.push(scripts); - 读取本地文件lib/notes.js:92
if (existsSync(legacyScripts)) candidates.push(legacyScripts); - 读取本地文件lib/notes.js:95
try { return statSync(dir).isDirectory(); } catch { return false; } - 读取本地文件lib/notes.js:142
if (existsSync(join(current, '.git'))) return current; - 执行外部命令固定命令lib/notes.js:226
const result = spawnSync('git', ['remote', 'get-url', 'origin'], { - 执行外部命令固定命令lib/notes.js:315
const result = spawnSync('git', ['worktree', 'list', '--porcelain'], { - 写入本地文件lib/notes.js:520
if (statSync(from).isFile()) copyFileSync(from, join(tmp, name)); - 写入本地文件lib/notes.js:524
writeFileSync(join(tmp, 'package.json'), '{"type":"module"}', 'utf8'); - 写入本地文件lib/notes.js:539
if (applied.length > 0) writeFileSync(target, code, 'utf8'); - 删除本地文件递归lib/notes.js:548
cleanup: () => { try { rmSync(tmp, { recursive: true, force: true }); } catch { } } - 执行外部命令来自配置/变量lib/notes.js:984
child = spawn(command, args, { - 写入本地文件lib/notes.js:1234
mkdirSync(dir, { recursive: true }); - 读取本地文件lib/state.js:74
if (!existsSync(file)) return fallback; - 读取本地文件lib/state.js:75
const parsed = JSON.parse(readFileSync(file, 'utf8')); - 写入本地文件lib/state.js:92
mkdirSync(dirname(file), { recursive: true }); - 写入本地文件lib/state.js:94
writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, 'utf8'); - 写入本地文件lib/state.js:95
renameSync(temp, file); - 读取本地文件lib/skill.js:220
const raw = readFileSync(SKILL_FILE, 'utf8'); - 读取本地文件lib/skill.js:265
if (!existsSync(SKILL_FILE)) return { candidates: [], complete: true }; - 读取本地文件lib/skill.js:271
if (!existsSync(SKILL_FILE)) return undefined; - 发起网络请求lib/self-update.js:57
const response = await fetch(url, { signal }); - 读取本地文件lib/self-update.js:175
if (!existsSync(file)) return undefined; - 读取本地文件lib/self-update.js:176
const value = readFileSync(file, 'utf8').trim(); - 写入本地文件lib/self-update.js:192
writeFileSync(join(skillDir, SYNCED_VERSION_FILE), `${version}\n`, 'utf8'); - 读取本地文件lib/self-update.js:255
current = existsSync(target) ? readFileSync(target, 'utf8') : undefined; - 读取本地文件lib/self-update.js:255
current = existsSync(target) ? readFileSync(target, 'utf8') : undefined; - 写入本地文件lib/self-update.js:273
mkdirSync(dirname(target), { recursive: true }); - 写入本地文件lib/self-update.js:275
writeFileSync(temp, entry.content, 'utf8'); - 写入本地文件lib/self-update.js:277
renameSync(temp, target); - 删除本地文件lib/self-update.js:280
try { rmSync(temp, { force: true }); } catch { } - 读取本地文件lib/version-notice.js:54
const raw = readFileSync(`${PACKAGE_ROOT}package.json`, 'utf8'); - 读取本地文件lib/project-skill.js:38
entries = readdirSync(dir, { withFileTypes: true }); - 读取本地文件lib/project-skill.js:62
return existsSync(file) ? readFileSync(file, 'utf8') : undefined; - 读取本地文件lib/project-skill.js:62
return existsSync(file) ? readFileSync(file, 'utf8') : undefined; - 读取本地文件lib/project-skill.js:97
if (!existsSync(projectDir)) return result; - 读取本地文件lib/project-skill.js:99
if (!existsSync(join(projectDir, 'SKILL.md'))) return result; - 写入本地文件lib/project-skill.js:178
mkdirSync(dirname(target), { recursive: true }); - 写入本地文件lib/project-skill.js:179
writeFileSync(target, content, 'utf8'); - 写入本地文件lib/projection.js:284
mkdirSync(dirname(file), { recursive: true }); - 写入本地文件lib/projection.js:286
writeFileSync(temp, `${JSON.stringify({ global: value })}\n`, 'utf8'); - 写入本地文件lib/projection.js:287
renameSync(temp, file); - 读取本地文件lib/projection.js:302
if (!existsSync(file)) return null; - 读取本地文件lib/projection.js:303
const parsed = JSON.parse(readFileSync(file, 'utf8')); - 读取本地文件lib/board-route.js:363
if (!existsSync(BOARD_HTML)) { - 读取本地文件lib/board-route.js:367
const html = readFileSync(BOARD_HTML, 'utf8'); - 读取本地文件lib/board-route.js:440
if (!existsSync(target) || !statSync(target).isFile()) continue; - 读取本地文件lib/board-route.js:440
if (!existsSync(target) || !statSync(target).isFile()) continue; - 读取本地文件lib/board-route.js:441
sendJson(res, 200, { path: rel, body: readFileSync(target, 'utf8'), root }); - 读取本地文件lib/archive-layout.js:166
return `sha256:${createHash('sha256').update(readFileSync(absolutePath)).digest('hex')}`; - 读取本地文件lib/archive-layout.js:208
if (!existsSync(archivedRoot)) return { errors: [], artifacts: [] }; - 读取本地文件lib/archive-layout.js:216
entries = readdirSync(archivedRoot, { withFileTypes: true }); - 读取本地文件lib/archive-layout.js:230
children = readdirSync(join(archivedRoot, entry.name), { withFileTypes: true }); - 读取本地文件lib/archive-layout.js:250
content = readFileSync(join(archivedRoot, rel), 'utf8'); - 读取本地文件lib/archive-layout.js:270
if (existsSync(manifestPath)) { - 读取本地文件lib/notes-layout.js:158
if (!existsSync(notesRoot)) { - 读取本地文件lib/notes-layout.js:166
entries = readdirSync(notesRoot, { withFileTypes: true }); - 读取本地文件lib/notes-layout.js:251
entries = readdirSync(lifecycleDir, { withFileTypes: true }); - 读取本地文件lib/notes-layout.js:313
entries = readdirSync(classDir, { withFileTypes: true }); - 读取本地文件lib/board.js:58
if (!existsSync(root)) return []; - 读取本地文件lib/board.js:62
if (!existsSync(lifecycleDir)) continue; - 读取本地文件lib/board.js:65
classDirs = readdirSync(lifecycleDir, { withFileTypes: true }).filter((e) => e.isDirectory()); - 读取本地文件lib/board.js:75
files = readdirSync(classPath, { withFileTypes: true }).filter((e) => e.isFile()); - 读取本地文件lib/board.js:85
stat = statSync(full); - 读取本地文件lib/board.js:87
text = readFileSync(full, 'utf8'); - 读取本地文件build.mjs:40
const pkg = JSON.parse(readFileSync(join(baseDir, 'package.json'), 'utf8')); - 读取本地文件build.mjs:41
const source = readFileSync(join(baseDir, 'src', 'client.js'), 'utf8'); - 写入本地文件build.mjs:72
mkdirSync(join(baseDir, 'lib'), { recursive: true }); - 写入本地文件build.mjs:73
writeFileSync(join(baseDir, 'lib', 'client.js'), bundle, 'utf8');
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成dsh-agent-notes
把「为什么这样改、放弃了什么」变成仓库里可机械校验的决策笔记,装进 DeepSeek Harness。
方法提炼自 czm15053/write-notes-like-deepseek(它又提炼自 DeepSeek Harness 的 .agents/notes/ 实践)。
本插件把那套方法做成 DSH 原生插件:技能、会话开关、真门禁、上游合并。
它解决什么
代码能表达「系统现在怎么跑」,表达不了「为什么必须这样跑、以及放弃了什么」。于是每个新会话都会:自作聪明地「优化」掉一段当年为避免死锁而刻意写别扭的代码;为赶局部需求打穿全局结构;把三个月前已证明会内存溢出的方案再提一遍。
本插件让规矩绕不开:和代码同仓库、同一次改动、机械校验、旧决定封存带封印。
安装
从本地目录装进你的 profile(开发期)
dsh plugin add --profile <你的profile> /path/to/dsh-agent-notes
或从 GitHub
dsh plugin add --profile <你的profile> github:iuuuuuuuu/dsh-agent-notes
注意:plugin add 只写依赖,不会把插件加进启用列表。
展开完整文档(README 剩余 1 节)
dsh-agent-notes
把「为什么这样改、放弃了什么」变成仓库里可机械校验的决策笔记,装进 DeepSeek Harness。
方法提炼自 czm15053/write-notes-like-deepseek(它又提炼自 DeepSeek Harness 的 .agents/notes/ 实践)。
本插件把那套方法做成 DSH 原生插件:技能、会话开关、真门禁、上游合并。
它解决什么
代码能表达「系统现在怎么跑」,表达不了「为什么必须这样跑、以及放弃了什么」。于是每个新会话都会:自作聪明地「优化」掉一段当年为避免死锁而刻意写别扭的代码;为赶局部需求打穿全局结构;把三个月前已证明会内存溢出的方案再提一遍。
本插件让规矩绕不开:和代码同仓库、同一次改动、机械校验、旧决定封存带封印。
安装
从本地目录装进你的 profile(开发期)
dsh plugin add --profile <你的profile> /path/to/dsh-agent-notes
或从 GitHub
dsh plugin add --profile <你的profile> github:iuuuuuuuu/dsh-agent-notes
注意:plugin add 只写依赖,不会把插件加进启用列表。 实测确认:它把包名写进 profiles/<profile>/package.json 的 dependencies,但 dsh.profile.bundles 里没有这一项 —— 而 DSH 是按 bundles 逐层加载插件的(每层读该包自己的 cordis.patch.yml)。所以只 add 的话插件装了却不生效,--dump-config 里也看不到它。
还需要手动把包名加进 bundles:
// $DSH_HOME/profiles/<你的profile>/package.json
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-agent-notes" // ← 加这一行 ] } }
然后重启 DSH。验证是否真加载:dsh --profile <你的profile> --dump-config 里应出现 # == dsh-agent-notes 与 - id: agent-notes。若只在依赖里而不在 bundles 里,启动日志不会有报错,插件却完全没跑 —— 这是最容易踩空的一步。
用起来
/agent-notes 查看开关与笔记树状态 /agent-notes on 开启本会话的决策笔记(持久化,重载后保持) /agent-notes on --global 改全局默认(影响所有未单独覆盖的会话) /agent-notes reset 清除本会话覆盖,回到全局默认 /agent-notes init 在当前仓库建出 .agents/notes 骨架 /agent-notes gate 跑真门禁(也可 gate tree / gate format / gate archived)
默认是关闭的
装好插件后,新会话和旧会话默认都不启用,需要时手动打开(点胶囊,或 /agent-notes on)。这样装上插件不会悄悄改变任何既有会话的行为。
想让所有会话默认开启,二选一:
/agent-notes on --global # 运行时改全局默认,落盘持久化
或在插件配置里写 enabled: true(仅在该配置生效的 profile 里)。
会话级开关优先级高于全局:单独关掉某个会话后,改全局也不会影响它。
界面上的状态提示
不用敲命令也能一眼看到当前会话是否生效:输入框工具行左侧有一枚绿色胶囊「● 笔记规范」,就挨着「完全权限/工作区内修改」那一排,点击即切换;关闭时胶囊变灰。
它读的是会话投影(agentNotes),所以刷新、重连、切换会话都自动正确。
点击开关走的是官方斜杠命令通道(/agent-notes on|off),因此和手敲命令 完全等价,并且开关操作零 token。
设计说明:投影只折叠宿主自己写的 command/run 事件,本插件不写会话日志。
原因是插件零 @deepseek-ai 依赖,拿不到 appendPluginRecord;而退路
session.append('plugin:…') 实测会写出 ignorable=undefined 的残废记录
(pluginRecordOf 读不回),看着成功实则污染日志。走命令事件则完全绕开这个坑。
笔记看板:对话区多一个「笔记」tab
对话 / 轨迹 旁边会多出一个 「笔记」 tab,里面就是上游那套成品看板(架构基线 / 演进时间线 / 避坑智库 / 决策清单四个视图),不自绘 UI:
tab 里用 iframe 加载 vendored 的上游资产 skills/agent-notes/assets/agent-notes-board.html,宿主只负责把当前会话仓库的笔记实时注入它预留的 window.__INLINE_DATA__ 插槽。
这样做的理由:官方看板是 76K 的成熟 UI(交叉引用入度、因果链路、多维筛选),自己重画一套只会更丑,而且必然与上游越走越远。走 iframe 后视觉与交互完全跟随上游更新,本插件只维护数据适配层(lib/board.js 的 buildInlineData)。
它读的是实时磁盘内容,不是静态快照:宿主注册只读 HTTP 路由(/agent-notes/ui、/agent-notes/board、/agent-notes/note、/agent-notes/health),每次切到该 tab 现读。
为什么不用会话投影:投影的 view 必须是同步纯函数,只能读会话日志折叠出的
state,读不到文件系统(已核对源码 view: state => wire.view(state))。
安全边界:路由只接受固定路径,只读 notesRoot 之下,并拒绝任何 ..;会话解析不出笔记根时返回 409,绝不猜一个路径,也不退回进程目录。
注入数据必须防住两件事(都有回归测试):
1. 笔记正文里出现 、 本仓库 --(插件启动时异步)--> 用户机器
两段都不再需要人记得。任何一段失败都会让 job 变红,不会静默跳过。
为什么用 git ls-remote 而不是 GitHub API:未认证请求 60 次/小时就 403,定时任务天天跑必撞墙。git 协议没这个限制。
为什么拿到 commit 而不直接锚分支名:jsDelivr 对分支名的清单缓存会滞后,锚 commit 才能拿到该次提交的真实树。
副本更新后,模型什么时候看到新规则
lib/skill.js 的 get() 每次调用都现读磁盘,没有缓存。所以:
- 自更新写入新内容后会调 invalidate(),宿主丢弃缓存的技能目录,模型下一次加载技能就是新内容,不需要重启 DSH;
- 但技能内容不是每轮都注入的 —— 它按需加载(SkillDefinition 语义),已加载过的会话可能还持有旧正文。
判据:让模型重新触发一次技能加载即可;要绝对确定就重启宿主。
自检与测试 sh
npm test # 行为测试(真跑子进程、真写文件)
npm run gate:fault-injection # 证明门禁会红,而不是恒绿
npm run verify # 包自检:清单 / patch / 零依赖 / 双语文档与徽标 / vendored 与上游哈希
npm run sync:latest # 上游是否已漂移(会真变红)
npm run sync:check # 副本与钉住 ref 是否逐字相同
npm run evidence # 实机证据:用 DSH 自带的真实 SkillRegistry 验证注册
npm run check # 以上四条串跑
fault-injection 会为每条门禁注入它该拦的故障(非法分类、坏文件名、缺备选方案、implemented 残留提案标题、篡改/删除已封印归档……),断言必须非零退出;再清掉故障断言回到零。任何一条对故障无反应,该脚本自身非零退出——它自己也引入了一条必然失败的断言验证过会红。
npm run evidence 与集成测试需要找到 DSH 安装位置。会自动探测 $DSH_HOME、~/.dsh 及其各 profile 的 node_modules;桌面端内置安装等非标准位置请显式指定:
sh
DSH_DIST=/path/to/dsh/lib/bin.js npm run evidence
探测不到时 evidence 明确失败(退出码 2),不假装通过;集成测试则降级为同契约承接器,并在输出里标注实际用的是哪种模式。
设计约束(为什么零依赖)
本插件不 import 任何 @deepseek-ai/。原因是实测的:profile 的 node_modules 里 @deepseek-ai 只解析到 cosmokit/schemastery,插件运行时拿不到 DSH 的类型包。因此:
- 宿主 API 全部按鸭子类型使用(ctx.skills / ctx.tools / ctx.commands / ctx.systemPrompt / ctx.agents),缺失时对应能力降级关闭而不是崩;
- 只用 node 内建模块;
- 工具用裸对象注册(DSH 接受原始 ToolDefinition,MCP 工具就是这么进来的)。
tools/verify-plugin.mjs 会把这条约束当门禁检查:谁往 lib/ 里加了 @deepseek-ai/ import,自检立刻红。
组件
| 文件 | 职责 |
| lib/index.js | 组装;会话级规范注入的挂载/卸载 |
| lib/state.js | 全局默认 + 会话覆盖双持久化(原子写) |
| lib/notes.js | 笔记树探测、真门禁执行、脚手架 |
| lib/skill.js | agent-notes 技能 provider(剥 frontmatter) |
| lib/policy.js | 注入的规范文本 |
| lib/commands.js | /agent-notes 命令 |
| lib/tools.js | agent_notes_gate / agent_notes_status 工具 |
| skills/agent-notes/ | vendored 上游内容(SKILL.md、references、templates、scripts、assets) |
| tools/ | 上游合并、自检、故障注入、测试 |
已知限制
- 看板模板引 Google Fonts 外链,离线环境下会退化到系统字体;功能不受影响。
- 上游内容同步依赖 jsDelivr 可访问;不可达时 sync 会明确失败,不会静默跳过。
- DSH 处于 developer preview,peerDependencies 未声明(本插件不依赖 DSH 包),但宿主 API 若变更需跟随。
- 会话开关按会话 id持久化;会话被删除后,其覆盖项仍留在状态文件中,目前不做 GC。
- 宿主仓库的 CI 接入需自行配置;插件不代为修改仓库。
许可
MIT。vendored 上游内容版权归其作者,见 skills/agent-notes/ 内注释与上游仓库。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
iuuuuuuuu/dsh-agent-notes
把改动缘由与取舍沉淀成可校验的仓库笔记
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9