DeepSeek Harness 的项目看板:看板卡片分组,卡片关联 landfill-project 记忆、挂载的仓库和会话。
32.8/100
综合分
4
GitHub 星标
207
npm 周下载
—/5
用户评分
数据截至 2026/10/15 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-project-managernpm 源 · dsh-project-manager 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · market
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 18 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-project-manager @ 0.20.1
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/22 04:37:51
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 153 处,展示前 88 条)
- 读取本地文件lib/index.js:98
return readdirSync(join(aiRoot, 'projects')).filter((n) => !n.startsWith('.') && !/ md$/.test(n)); - 读取本地文件lib/index.js:106
if (existsSync(join(aiRoot, 'projects', pid, 'memory'))) - 读取本地文件lib/index.js:175
srcs: r.srcs.map((s) => { const sub = s.mergeId ? merges.list().find((x) => x.id === s.mergeId) : undefined; return { key: s.key, kind: s.kind, chars: - 读取本地文件lib/index.js:191
return { key: m.key, from: m.from, found: !!st, project: st?.project ?? m.key.split('/')[0], slug: st?.slug ?? m.key.split('/')[1], next: st?.next ?? - 读取本地文件lib/index.js:198
folder: { group: g.name, value: g.lfProject ?? null, suggest: g.lfProject ? null : folderSuggest(g), registered: !!g.lfProject && existsSync(join(aiRo - 读取本地文件lib/index.js:199
cardInfo: { id: c.id, stage: g.statuses.find((s) => s.id === c.col)?.name ?? '', archived: !!c.archivedAt, repos: c.repos ?? [], tasks: c.tasks, notes - 执行外部命令固定命令lib/index.js:539
execFile('osascript', script.flatMap((l) => ['-e', l]), { timeout: 10 * 60_000, maxBuffer: 4 * 1024 * 1024 }, (err, stdout, stderr) => { - 发起网络请求lib/client.js:22
res = await fetch(API + path, init); - 写入剪贴板lib/client.js:1533
await navigator.clipboard.writeText(url); - 写入剪贴板lib/client.js:4327
await navigator.clipboard.writeText(text); - 写入剪贴板lib/client.js:4383
if (b.dataset.rawCopy !== void 0) navigator.clipboard.writeText(this.rawText()).then(() => this.host.toast(this.rawTab === "sys" ? "已复制系统提示原文" : "已复制落 - 发起网络请求lib/client.js:4910
fetch(DSM + "/capabilities", { cache: "no-store" }).then((r) => r.json()).then((b) => { - 发起网络请求lib/client.js:4944
r = await (await fetch(DSM + "/delete", { - 写入本地文件lib/store.js:18
mkdirSync(dataDir, { recursive: true }); - 读取本地文件lib/store.js:21
this.board = existsSync(this.boardPath) ? parseBoard(JSON.parse(readFileSync(this.boardPath, 'utf8'))) : emptyBoard(); - 读取本地文件lib/store.js:21
this.board = existsSync(this.boardPath) ? parseBoard(JSON.parse(readFileSync(this.boardPath, 'utf8'))) : emptyBoard(); - 写入本地文件lib/store.js:34
writeFileSync(tmp, JSON.stringify(draft, null, 2)); - 写入本地文件lib/store.js:35
renameSync(tmp, this.boardPath); - 写入本地文件lib/store.js:38
appendFileSync(this.eventsPath, JSON.stringify(ev) + '\n'); - 读取本地文件lib/store.js:52
if (!existsSync(this.eventsPath)) - 读取本地文件lib/states.js:44
projects = readdirSync(projectsDir); - 读取本地文件lib/states.js:53
slugs = readdirSync(designs); - 读取本地文件lib/states.js:62
st = statSync(path); - 读取本地文件lib/states.js:73
Object.assign(info, parseState(readFileSync(path, 'utf8'))); - 读取本地文件lib/states.js:78
Object.assign(info, parseState(readFileSync(path, 'utf8').slice(0, MAX_BYTES))); - 读取本地文件lib/states.js:96
st = statSync(path); - 写入本地文件lib/hub.js:18
mkdirSync(repos, { recursive: true }); - 删除本地文件lib/hub.js:36
rmSync(gp); - 读取本地文件lib/hub.js:42
if (!lstatSync(lp).isSymbolicLink()) - 删除本地文件lib/hub.js:45
rmSync(lp); - 写入本地文件lib/hub.js:50
mkdirSync(join(lp, '..'), { recursive: true }); - 读取本地文件lib/hub.js:51
if (existsSync(lp) || isLink(lp)) - 读取本地文件lib/hub.js:53
if (existsSync(target)) - 写入本地文件lib/hub.js:56
writeFileSync(join(root, 'AGENTS.md'), agentsMd(b, states)); - 写入本地文件lib/hub.js:63
mkdirSync(base, { recursive: true }); - 写入本地文件lib/hub.js:73
writeFileSync(join(dir, 'AGENTS.md'), groupAgentsMd(b, g, states)); - 删除本地文件lib/hub.js:80
rmSync(p); - 删除本地文件lib/hub.js:91
rmSync(dir); - 写入本地文件lib/hub.js:92
mkdirSync(dir, { recursive: true }); - 读取本地文件lib/hub.js:95
if (lstatSync(lp).isSymbolicLink() && wanted.get(name) !== linkTarget(lp)) - 删除本地文件lib/hub.js:96
rmSync(lp); - 读取本地文件lib/hub.js:100
if (existsSync(lp) || isLink(lp)) - 读取本地文件lib/hub.js:102
if (existsSync(target)) - 读取本地文件lib/merge.js:30
export function planLayout(srcs, aiRoot, reserved = [], home = homedir(), stat = statOf, hashFile = (p) => sha256(readFileSync(p))) { - 读取本地文件lib/merge.js:98
const s = statSync(p); - 读取本地文件lib/merge.js:167
const t = readFileSync(f, 'utf8'); - 读取本地文件lib/merge.js:179
names = readdirSync(dir); - 读取本地文件lib/merge.js:190
st = statSync(p); - 读取本地文件lib/merge.js:204
if (existsSync(f) && has(f)) - 写入本地文件lib/merge.js:225
save() { mkdirSync(this.d.dataDir, { recursive: true }); writeFileSync(this.file + '.tmp', JSON.stringify(this.recs, null, 1)); renameSync(this.file + - 写入本地文件lib/merge.js:225
save() { mkdirSync(this.d.dataDir, { recursive: true }); writeFileSync(this.file + '.tmp', JSON.stringify(this.recs, null, 1)); renameSync(this.file + - 写入本地文件lib/merge.js:225
save() { mkdirSync(this.d.dataDir, { recursive: true }); writeFileSync(this.file + '.tmp', JSON.stringify(this.recs, null, 1)); renameSync(this.file + - 写入本地文件lib/merge.js:364
mkdirSync(dirname(f), { recursive: true }); - 写入本地文件lib/merge.js:365
writeFileSync(f, '# 合并要点:' + s.key + '\n\n' + content.trim() + '\n'); - 写入本地文件lib/merge.js:545
mkdirSync(dirname(bk), { recursive: true }); - 删除本地文件lib/merge.js:682
rmdirSync(p); - 读取本地文件lib/migrate.js:25
const j = JSON.parse(readFileSync(this.file, 'utf8')); - 写入本地文件lib/migrate.js:31
save() { mkdirSync(this.d.dataDir, { recursive: true }); writeFileSync(this.file + '.tmp', JSON.stringify({ recs: this.recs, stays: this.stays }, null - 写入本地文件lib/migrate.js:31
save() { mkdirSync(this.d.dataDir, { recursive: true }); writeFileSync(this.file + '.tmp', JSON.stringify({ recs: this.recs, stays: this.stays }, null - 写入本地文件lib/migrate.js:31
save() { mkdirSync(this.d.dataDir, { recursive: true }); writeFileSync(this.file + '.tmp', JSON.stringify({ recs: this.recs, stays: this.stays }, null - 读取本地文件lib/migrate.js:39
if (!existsSync(this.d.lf.statePath(r.to))) - 读取本地文件lib/migrate.js:41
if (existsSync(this.d.lf.statePath(r.from))) - 读取本地文件lib/migrate.js:68
if (!existsSync(join(srcDir, 'STATE.md'))) - 读取本地文件lib/migrate.js:70
if (existsSync(this.d.lf.statePath(to))) - 读取本地文件lib/migrate.js:80
for (let i = 2; existsSync(retiredOf(dir)); i++) - 写入本地文件lib/migrate.js:154
mkdirSync(dirname(backup), { recursive: true }); - 写入本地文件lib/migrate.js:155
writeFileSync(backup, old); - 读取本地文件lib/routes.js:91
out[p] = existsSync(p); - 读取本地文件lib/routes.js:143
const s = statSync(p); - 读取本地文件lib/routes.js:318
if (!forkBackup && existsSync(s.store.boardPath)) { - 写入本地文件lib/routes.js:320
copyFileSync(s.store.boardPath, forkBackup); - 执行外部命令固定命令lib/landfill.js:38
execFileSync('/usr/bin/xcode-select', ['-p'], { stdio: 'ignore', timeout: 5000 }); - 读取本地文件lib/landfill.js:47
if (!existsSync(p.aiRoot)) { - 写入本地文件lib/landfill.js:48
mkdirSync(join(p.aiRoot, 'memory'), { recursive: true }); - 写入本地文件lib/landfill.js:49
writeFileSync(join(p.aiRoot, 'README.md'), AI_README, { flag: 'wx' }); - 写入本地文件lib/landfill.js:50
writeFileSync(join(p.aiRoot, 'conventions.md'), AI_CONVENTIONS, { flag: 'wx' }); - 写入本地文件lib/landfill.js:51
writeFileSync(join(p.aiRoot, 'memory', 'MEMORY_INDEX.md'), '# Memory Index\n', { flag: 'wx' }); - 读取本地文件lib/landfill.js:55
if (!existsSync(tool) && !isLink(tool)) { - 写入本地文件lib/landfill.js:56
mkdirSync(dirname(tool), { recursive: true }); - 写入本地文件lib/landfill.js:57
copyFileSync(join(p.pkgDir, 'bin', 'ai-memory'), tool, constants.COPYFILE_EXCL); - 读取本地文件lib/landfill.js:64
return lstatSync(p).isSymbolicLink(); - 读取本地文件lib/landfill.js:74
const j = JSON.parse(readFileSync(join(dirname(dirname(dirname(target))), 'package.json'), 'utf8')); - 读取本地文件lib/landfill.js:91
st = lstatSync(link); - 读取本地文件lib/landfill.js:99
if (!existsSync(raw)) - 删除本地文件lib/landfill.js:120
rmSync(link); - 删除本地文件lib/landfill.js:134
rmSync(link); - 执行外部命令来自配置/变量lib/landfill.js:151
const child = spawn(python, [tool, ...args], { env: { ...process.env, AI_MEMORY_ROOT: aiRoot, AI_MEMORY_WRITER: 'dsh-project-manager' }, stdio: ['pipe - 执行外部命令来自配置/变量lib/landfill.js:534
v = Number(JSON.parse(execFileSync(this.status.python, [this.status.tool, 'version'], { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ig
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/schemastery@deepseek-ai/dsh-tools@deepseek-ai/dsh-client-ui-slots给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
6 张 · 点击查看大图功能亮点
- 看板卡片分组
- 关联项目记忆
- 挂载仓库与会话
最近更新
在 GitHub 查看全部 →v0.20.12026-10-14
· 安装 / Install
dsh plugin add dsh-project-manager
· 0.20.1
· 修复:同一台机器上另一个 DSH home 装了本插件后,会把 ~/.agents/skills/landfill-project/SKILL.md 链接抢过去指向自己(那个 home 删掉后链接就断了)。现在指向本插件另一份安装、且版本不比自己旧时保持不动,只有旧版或断链才重建。
· 0.12–0.20 的全部内容见 v0.20.0 与 CHANGELOG.md。
· 校验和 / Checksum
dsh-project-manager-0.20.1.tgz sha256 e567493b925d7288f7a9cfffb0348dbe97b733b66850c2c2caea804981a7cadc
v0.20.02026-10-14
· 安装 / Install
dsh plugin add dsh-project-manager
备选 / Alternative: dsh plugin add github:hoyyang/dsh-project-manager
· 0.12 – 0.20 要点
· 落盘项目并入插件:会话自动注入卡片关联的 STATE + [必读] 全文,别处改了下一步补新版、压缩后重注、同版本只注一次;「落盘项目 / 回忆」一句话触发;没读全不能写、丢了 commit / 路径 / 版本号 / 单号会被拒;一个项目一个文件夹 + 文件索引;卡片档案块写进 STATE。
· 会话信息卡:输入框上方显示卡片、落盘项目、卡片资料;「查看 AI 收到的原文」;落盘这一轮的结局(完成 / 停止 / 出错 / 等你回复)如实显示。
· 同名检测:按卡片名提示、完全同名不让新建(改为「关联并落盘」)、名字相近提示。
· 按卡片组归档 + 按规范迁移:卡片组有自己的落盘文件夹;规范清单每次落盘检查,放错位置的整个文件夹自动迁过来(可撤销)。
· 合并落盘项目:几份合成一份,旧名字变别名,可撤销 / 删除 / 嵌套。
· 看板:卡片组标题旁卡片数 + 活跃会话数;新建会话立即反馈、侧栏直接显示卡片名;链接编辑 / 删除常驻;空会话行;卡片共用仓库;分叉自动关联。
· 修复 #2 DSH Desktop 下写操作 403 CROSS_ORIGIN;#1 Windows 绝对路径被拒(Windows 挂载用 junction)。
完整变更见 CHANGELOG.md。
· 校验和 / Checksum
dsh-project-manager-0.20.0.tgz sha256 cae34f13670291866d5c70967946d74a1758d571802fa777a2fbb6b449b3733a
v0.11.02026-10-11
· 安装 / Install
dsh plugin add dsh-project-manager
· 备选 / alternative
dsh plugin add github:hoyyang/dsh-project-manager
适配 / Tested with: dsh CLI 0.2.0-rc.2, DSH Desktop 2.0.17 (bundled dsh 0.2.0-rc.2).
· 0.11.0 变更要点
· 侧栏「项目区」每个卡片组行显示活跃会话数(0 为灰色,新建 / 归档 / 恢复即时更新)。
· 卡片组下的会话整行缩进到竖向引导线右侧,选中底色与其他插件的会话行样式一起缩进;展开的组名加粗。
· 从项目看板打开或新建会话后,所在卡片组(及项目区)收起着会自动展开并滚到该会话。
· 卡片详情「关联的落盘项目」默认只显示标题,一个按钮全部展开 / 收起。
· 修复:「添加挂载仓库」候选不再列出插件自己的卡片组子工作区。
· 0.5.0 – 0.10.0 的累计变更(卡片组排序、新建会话弹窗、相关说明 / 链接 / 文件、卡片归档、子工作区、卡片资料注入会话等)见 CHANGELOG。
· Highlights
· Active session count on each card-group row under 项目区; group sessions indented behind a guide line; opening a session from the board expands its group; linked landfill projects show titles first with one expand-all button.
· 校验 / Checksum
dsh-project-manager-0.11.0.tgz sha256 e456ced20f9e9a60111340c9afaf1ae566a67768abe52395b8738c909987fd0f
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成dsh-project-manager
banner
给 DeepSeek Harness 加一块项目看板,并把「项目记忆」做成插件的本职:从卡片打开的会话,自动带上这个项目完整、最新的落盘记忆(STATE + 必读子文档)和卡片资料;说一句「落盘项目」就把进展按规范写回去,换会话、换 CLI、隔几天都能接着做。
English · Releases · 更新日志
安装
dsh plugin add dsh-project-manager
备选(直接从 GitHub 装):
dsh plugin add github:hoyyang/dsh-project-manager
零配置、开箱即用:装完重启 dsh 即可,不需要账号、API Key 或任何设置项。落盘项目默认读写 ~/.ai(写入一律经插件自带的 ai-memory,需要 python3;Windows 上这块自动关闭,看板照常)。要改数据目录或权限模式见下文「配置」。
展开完整文档(README 剩余 1 节)
dsh-project-manager
banner
给 DeepSeek Harness 加一块项目看板,并把「项目记忆」做成插件的本职:从卡片打开的会话,自动带上这个项目完整、最新的落盘记忆(STATE + 必读子文档)和卡片资料;说一句「落盘项目」就把进展按规范写回去,换会话、换 CLI、隔几天都能接着做。
English · Releases · 更新日志
安装
dsh plugin add dsh-project-manager
备选(直接从 GitHub 装):
dsh plugin add github:hoyyang/dsh-project-manager
零配置、开箱即用:装完重启 dsh 即可,不需要账号、API Key 或任何设置项。落盘项目默认读写 ~/.ai(写入一律经插件自带的 ai-memory,需要 python3;Windows 上这块自动关闭,看板照常)。要改数据目录或权限模式见下文「配置」。
| 环境 | 版本 | 状态 |
| dsh CLI(@deepseek-ai/dsh,dsh web) | 0.2.0-rc.2 | 实测通过(0.20.1) |
| DSH Desktop | 2.0.17(内置 dsh 0.2.0-rc.2) | 实测通过(0.20.1) |
| 依赖范围(peerDependencies) | @deepseek-ai/cordis >=4.0.1 <5、@deepseek-ai/dsh-tools >=0.1.5-rc <2、@deepseek-ai/dsh-client-ui-slots >=0.1.5-rc <2、@deepseek-ai/schemastery ^3.18.1 | 声明范围,范围内其他版本未逐一验证 |
需要 dsh web 界面(CLI 的 dsh web 或 DSH Desktop)。侧栏增强(项目区数字、层级、「+」接管、空会话行)依赖 dsh 工作区列表的页面结构;dsh 改版后若对不上,这几项会自动不显示,看板、会话与落盘功能不受影响。
功能
项目看板
会话自动带上完整、最新的项目记忆
- 从卡片开会话 = 上下文已经在了:卡片关联的落盘项目(STATE + 标 [必读] 的子文档)由插件把全文直接放进会话,系统提示里另有一段实时生成的「卡片档案」(仓库、子任务、说明、链接、文件)。不用先说「回忆」,第一句就能接着干。
- 永远是最新版,且不重复:别的会话或 Codex / Claude Code 等其他 CLI 改了 STATE,下一步自动补上新版(标明替代哪一版、谁写的);会话压缩后重新补齐;同一版本只放一次,自己写的不回灌,分叉会话继承。
- 看得见 AI 收到了什么:会话信息卡里「查看 AI 收到的原文」逐字显示系统提示和注入的全文,标出版本、是否已在本会话、约多少 token,可一键复制。
AI 收到的原文
说一句「落盘项目」,按规范写回去
- 一句话触发:会话里说「落盘项目 XX」把进展写进 XX 的 STATE,说「回忆 XX」/「继续 XX」先读全再接着做;触发词来自 ~/.ai 里现有的项目名和别名。只认指令写法,讨论「落盘项目这个功能」时不会误触发;规划模式下只提示不拉回。
- 防丢事实:没读全不能写;新稿少了旧稿里的 commit、路径、版本号、单号又没声明删除,直接拒绝;写完回读校验并给出前后大小、删掉了哪些章节。用户要求了而这一轮没做到,回合结束前把模型拉回补做。
- 一个项目一个文件夹:STATE.md 是唯一入口,plans/(批准的计划自动存进来)notes/ evidence/ 里每个文件都要在「📁 文件索引」里有一行;每次落盘都整体重写 STATE、顺手整理文件夹(重复的合并、过时的退役到回收区)。
- 卡片档案进 STATE:插件按看板生成「🗂 卡片档案」块写进 STATE(AI 不写),其他工具读 STATE 也能看到卡片资料。
- 防绕过:write / edit / bash 直接改 ~/.ai 下的记忆文件会被拒绝;写入只经 ai-memory(文件锁 + 乐观并发),和其他 CLI 共用同一个 ~/.ai 也不会互相覆盖。
会话信息卡:这个会话属于哪张卡片、带着什么,一眼看清
会话信息卡
- 输入框上方一行:卡片组 / 卡片、落盘项目数、仓库 / 链接 / 子任务 / 说明胶囊,一键直达看板上的这张卡片。有关联是浅蓝,没关联是浅灰。
- 展开看全部:每个落盘项目一行(所属项目、别名、next_action、是否已注入、字数),可以单独「落盘」;下面是卡片资料。
- 落盘的结局如实显示:从信息卡点了落盘之后,这一轮写好了提示「落盘完成」;被你停止、出错、或者 AI 在问你,都会写明,并给「重新落盘 / 忽略」,不会一直转圈。
展开的会话信息卡
落盘出错时
同名检测:不让同一个项目被建成两份
- 还没输入就提示:卡片没关联落盘项目时,按卡片名找到别名相同的已有项目,提示「可能就是这个」。
- 完全同名不让新建:输入的文件名或别名和已有项目相同,主按钮换成「关联并落盘」,并给出改名建议;名字相近时只提示,确认不是同一个可以照常新建。
- 每个候选看得清:字数、更新时间、已经关联了哪些卡片、落盘时会不会迁移;可以「只关联」,也可以「关联并落盘」。
可能就是这个
已有同名落盘项目
名字相近
按卡片组归档,按规范自动迁移
- 每个卡片组有自己的落盘文件夹:~/.ai/projects/<文件夹>/。第一次在这个组落盘时,信息卡请你确认一次(按组名给建议,可改),卡片组设置里也能改。新建、合并的落盘项目都放进这里。
- 规范是一张带版本号的检查清单:每次落盘都整张检查,所以不管 STATE 是按哪个旧版本写的,下次落盘都会被拉到最新规范。位置不对的由插件把整个文件夹迁过来(子文档一起搬、里面的旧路径改指新位置、~/.ai 里的外部引用改指向、卡片关联换成新位置,旧文件夹进回收区);标题行、文件索引、别名行由落盘的 AI 按清单改好,不合规范不让写。
- 提前告诉你,事后能撤销:信息卡上提前标出「落盘时迁到 xxx」;迁移后显示「已按规范迁移」卡片,可以撤销(搬回原处,之后不再自动迁)或删除旧文件夹。跨组共用的留在第一个关联它的组;旧项目不会被主动批量迁移,只在被落盘时迁。
卡片组设置:落盘文件夹
合并落盘项目
- 几份合成一份:同一张卡片关联的几个落盘项目可以合并(勾选要合的,没选的不动)。AI 读全每一份、先写要点再整体重写成一份 STATE;插件校验来源里的关键事实都还在,再按规范把所有文件放进合并结果文件夹(重复的只留一份,~/.ai 外引用的文件复制进 evidence/)。
- 旧名字变成别名:说「回忆 + 旧名字」照样能找到;卡片关联和 ~/.ai 里指向旧路径的引用一并改好。
- 可撤销、可删除、可嵌套:合并前的文件夹进回收区,可以撤销(只退一步),也可以永久删除;合并结果还能再合并。
合并落盘项目
看板:卡片组管父项目,卡片管任务
- 按项目看全局:每个卡片组一条泳道,阶段(列)名称、数量、顺序都能改;卡片在阶段内拖动排序(键盘 Alt+↑/↓),拖到别的阶段即推进;卡片组可拖动排序、点头部展开 / 收起。
- 卡片组标题旁的统计:卡片数是蓝底胶囊(0 张变灰),旁边「● N 活跃会话」= 本组卡片关联、没归档的会话数,会话一开一归档就更新;仓库数和阶段数是灰色小字。
- 每个阶段随手加卡:阶段底部常驻「添加卡片」,回车连续添加;也能从落盘项目批量导入(一个 STATE 生成一张卡片并自动关联)。
- 卡片上只放两个小标志:会话标志(有活跃会话为绿色、全部已归档为灰色)和落盘项目数。
- 卡片归档:点归档图标或拖进「已归档卡片」列表;归档的卡片可看只读详情、「恢复活跃」回原阶段。
- agent 也能改看板:card_manage / repo_manage / phase_move / task_manage 四个工具,在项目会话里不传 card 就作用于本会话的卡片。
- 深色主题:跟随 dsh,看板右上角可一键切换。
卡片组标题旁的统计
卡片详情
卡片详情
- 以此卡片新建会话:点了立刻显示「正在新建会话…」并锁住按钮,侧栏同时出现占位行;建好后侧栏那一行就是「卡片名 · 未开始」。会话默认完全权限,标题自动改成卡片名或「卡片组 · 卡片」。
- 本卡片的会话置顶:活跃在前、已归档在后,各自按更新时间排序;可打开、归档(还在运行时先确认「停止并归档」,可撤销)、「恢复活跃」回原位置、解除关联;还没对话的会话可以删除(需要 dsh-manage-sessions)。
- 关联已有会话:搜索会话名或仓库,活跃 / 已归档两列按仓库分组,勾选后一次保存;一个会话只属于一张卡片,分叉出来的会话自动跟着源会话的卡片。
- 关联的落盘项目:默认只显示标题,「展开详情」一次展开全部(项目、更新日期、一行状态、next_action);下面「回忆」「落盘」两个按钮发到这张卡片最近的会话。
- 相关说明 / 相关链接 / 相关文件:说明不限字数、网址可点、过长折叠;链接有显示文字、类型(全局共用,可在「管理类型」里新增、改名、拖动排序)和完整网址,可批量添加,编辑 / 删除按钮常驻显示,可拖动排序、复制;文件用系统对话框多选或「粘贴路径」一次贴多行(逐行检查存在 / 是文件夹 / 不存在 / 已关联)。
- 阶段与子任务:步骤条点一下就移到该阶段;子任务勾选、删除、回车添加。
落盘项目展开详情
相关链接
关联已有会话
仓库
- 卡片共用仓库 + 卡片自己的仓库:卡片组设置里的仓库是「卡片共用」,新建卡片时自动带上;卡片还可以加自己的仓库。候选来自 dsh 已有工作区和任意卡片组 / 卡片添加过的仓库,也可以「选择文件夹…」。
- 项目会话统一路径:仓库以软链接挂在会话目录的 repos/<仓库>/ 下,并生成 AGENTS.md 列出仓库清单;从这个路径读写,仓库自己的 AGENTS.md / CLAUDE.md 才会加载。同名不同路径的自动改成「名-2」。
添加卡片共用仓库
侧栏「项目区」
侧栏项目区
- 项目区工作区:插件在工作区列表置顶一个「项目区」,每个卡片组是它的子工作区(视图选「按工作区树」时嵌套显示),项目会话按卡片组归在这里。
- 活跃会话数 + 层级:每个卡片组行显示没归档的会话数(0 为灰色,变化时轻跳);组下会话整行缩进到引导线右侧。
- 还没对话的会话也显示:dsh 默认隐藏空会话,插件把它们补画成「卡片名 · 未开始」行,当前打开的也一样,可直接删除。
- 从看板进入自动展开:从详情打开或新建会话后,所在卡片组收起着就自动展开并滚到这个会话。
- 「+」先选卡片:点「项目区」或卡片组行的「+」,先选卡片(只列本组卡片,带阶段、记忆数、会话数),再新建会话或打开它已关联的会话。
- 「项目看板」主按钮:与「插件」同一组,带「N 进行中」徽标(没归档、且至少有一个没归档会话的卡片数)。
选卡片浮层
和已有插件的差别:看板类插件多数按工作区记待办;本插件不做通用待办,只做「项目 → 落盘记忆 → 仓库 → 会话」这条链,而且把记忆的读、写、规范、迁移、合并都做进插件里:卡片即项目,会话自动继承完整、最新的项目上下文。
快速上手(30 秒)
1. 点击侧栏「插件」下面的「项目看板」,再点右上角「新建卡片组」,输入组名后回车。
2. 点击任意阶段底部的「添加卡片」,输入标题后回车。
3. 打开「卡片组设置」,在「卡片共用仓库」里添加仓库;需要的话把「落盘文件夹」也设好。
4. 在卡片详情点「以此卡片新建会话」:会话出现在「项目区」下这个卡片组里,已带上卡片资料。
5. 在会话里干完一段,说「落盘项目」(或点信息卡上的「新建并落盘」),进展就写进这张卡片的落盘项目;下次从卡片开会话,记忆自动就在。
| 操作 | 结果 |
| 以此卡片新建会话 | 按钮立即进入「正在新建会话…」,侧栏出现「卡片名 · 未开始」,卡片组行数字 +1 |
| 在会话里说「落盘项目」 | STATE 写进卡片组的落盘文件夹,带卡片档案块,回读校验通过,信息卡提示「落盘完成」 |
| 落盘中途点「停止」 | 信息卡显示「已停止,没写入」,给「重新落盘 / 忽略」 |
| 在信息卡输入已有项目的名字 | 按钮变成「关联并落盘」,标出「已存在」,给改名建议 |
| 对旧位置的落盘项目落盘 | 整个文件夹迁到卡片组的落盘文件夹,信息卡出现「已按规范迁移」,可撤销 |
| 另一个 CLI 改了 STATE | 本会话下一步收到新版全文,标明替代旧版和写入者 |
| 新稿删掉了旧稿里的 commit 号 | 写入被拒(DROPPED_ANCHORS),点名缺了哪些 |
| 拖动卡片到别的阶段 | 卡片移到新阶段,events.jsonl 追加一条 move 记录 |
在项目会话里让 agent 操作看板(不传 card 就作用于本会话的卡片):
把这张卡片推进到「验收」,再加一个子任务「补回归用例」
phase_move { "status": "verify" } → {"ok":true,"event":{"kind":"move","from":"开发","to":"验收"}}
task_manage { "op": "add", "text": "补回归用例" } → {"ok":true,"tasks":[…,{"text":"补回归用例","done":false}]}
读写落盘项目(插件注册的 landfill 工具,模型按 skill 协议调用):
landfill { "op": "recall" } → 分段读 STATE + [必读],读到 complete:true
landfill { "op": "persist", "content": "…", "drop": ["a1b2c3d"] } → {"key":"checkout/checkout-flow","verified":true,"migrated":{…}}
使用场景
- 长周期项目接续:今天落盘,明天从卡片开新会话,状态、决策、next_action 已经在上下文里,不用重新交代。
- 多个 CLI 共用一份记忆:DSH 里落盘,Codex / Claude Code 读同一个 ~/.ai 的 STATE;它们写了,DSH 这边下一步自动拿到新版。
- 一个需求跨多个仓库:卡片组挂上前端、SDK、基础设施几个仓库,项目会话里用 repos/<仓库>/ 统一读写。
- 收拾散落的记忆:同一件事以前落成了好几份,用「合并」合成一份,旧名字当别名;放错地方的,下次落盘自动迁到卡片组的文件夹。
- 把零散会话收拢到项目:之前在各处开的讨论会话,用「关联已有会话」挂到同一张卡片下。
- 每天开工先看全局:卡片组标题旁的卡片数和活跃会话数、卡片上的会话标志,打开看板就知道哪些项目在推进。
- 交接给同事或新会话:卡片的落盘项目、说明、链接、文件和会话列表就是交接清单。
- 让 agent 自己推进:在项目会话里让 agent 推进阶段、勾子任务,看板实时更新,每次变更写审计日志。
- 一单一卡处理问题单:卡片标题写问题单号,落盘项目以单号为别名;下次在任意会话说「回忆 + 单号」就能找回分析进度。
- 规范升级不用手工迁移:老版本写的 STATE 不用一次性整理,哪个项目再被落盘,哪个就自动迁到卡片组的文件夹并补齐规范项。
运行效果
- 从卡片开的会话第一步就带着 STATE 与 [必读] 全文,系统提示里只有一段「项目记忆」(卡片档案 + 落盘项目索引)。
- 落盘后 STATE 位于 ~/.ai/projects/<卡片组的落盘文件夹>/memory/designs/<名字>/STATE.md,带卡片档案块、文件索引、别名行。
- 会话信息卡实时显示落盘这一轮的结局;同名检测边输入边给出候选。
- 侧栏「项目区」下每个卡片组行带活跃会话数,会话整行缩进;新建的空会话直接显示卡片名。
- 每次移动、关联、挂载、迁移都追加到 events.jsonl,可追溯谁(用户 / agent)在什么时候做了什么。
- 合并完成后,合并结果文件夹里是 STATE.md、notes/(每份来源的要点)和 evidence/,合并前的文件夹在各自项目的 inbox/retired/ 下。
- 迁移完成后,旧文件夹在原项目的 inbox/retired/<时间>-migrate-<名字>/ 下,信息卡显示子文档数和改了几处外部引用。
- 「查看 AI 收到的原文」显示系统提示字数、注入全文份数与总字数、约多少 token。
深色主题
配置
零配置即可使用。需要时在 profile 的 cordis.patch.yml 加顶层 id 条目覆盖(不要放进 insert: 列表):
- id: dsh-project-manager
config:
dataDir: '' # 缺省 <DSH_HOME>/dsh-project-manager aiRoot: '' # 缺省 ~/.ai landfill: true # 落盘项目工具 / 识别 / 拦截 / 收尾检查(false = 只读扫描) agentsHome: '' # landfill-project skill 链接所在的 ~/.agents hubTitle: 项目区 projectSessionMode: danger-full-access # 或 inherit(跟随全局权限) memoryMaxBytes: 6000 # 系统提示里项目记忆段的上限,1000–32000
卡片组的落盘文件夹在「卡片组设置」里改,不需要写配置。
工作原理
- 数据:看板存在 <dataDir>/board.json(原子写 + 串行变更),审计追加到 events.jsonl;落盘项目按需扫描 ~/.ai/projects//memory/designs//STATE.md;写入只经 ~/.ai/tools/ai-memory(Python 标准库:fcntl 文件锁 + expected-hash + journal)。合并与迁移记录在 <dataDir>/merges.json / migrations.json。
- 注入:系统提示一段(卡片档案每步按看板现生成 + 落盘项目索引);STATE 与 [必读] 全文在 agent/pre-step 作为一次性消息注入,按「会话 × 文件版本」记已知,跨重启保存;看到压缩事件就重注。
- 落盘触发:agent/pre-step 识别用户原话并追加提示;tools/pre-execute 拦截直写;agent/turn-stopping 收尾检查并拉回;会话事件 turn/end 的结束原因(完成 / 停止 / 出错)驱动信息卡上的落盘状态。
- 规范与迁移:规范清单(v2:位置、标题行、文件索引、别名行含卡片名)在每次 persist 前检查;位置项由插件用 ai-memory retire-dir / write / import / update-state 整体搬迁,中途失败按已做的倒着撤回。
- 项目区:用官方 workspaceRegistry 登记「项目区」工作区(<dataDir>/hub)与每个卡片组的子工作区,仓库以软链接挂在 repos/<仓> 下。
- 界面:看板是主区面板;「项目看板」主按钮走官方 sidebar.panellist;会话信息卡走 conversation.input.dock;侧栏数字与层级是一张按行 id 生成的样式表,「+」接管、空会话行、自动展开是 DOM 增强,找不到对应行时安静跳过。
可靠性与验收
- 隔离验证:每个版本先在独立的 staging DSH_HOME 装配,冷启动静态检测(依赖链接 / bundle 清单 / 运行时导入 / 重复 entry 等六类)本插件项全绿后才装到主实例。
- 真实模型端到端:0.20 在 staging 用真实模型跑「落盘 → 自动迁移 → 规范检查 → 迁移卡 → 撤销」与「新建落盘进卡片组文件夹」;0.19 用 6 份真实落盘项目副本跑合并、嵌套合并、子删除、撤销逐字节还原。
- 真实 GUI 测试:Playwright 在真实 dsh web 上逐项点:组头统计、新建会话忙态与侧栏标题、链接按钮常驻、箭头方向、同名三种情况、卡片组落盘文件夹确认与非法名字提示、停止后状态与忽略、整页不移位,全部通过、0 页面报错。
- 主实例验收:每个版本装到日常使用的 dsh 后,用探针确认新逻辑已生效(0.20:组头统计、新建会话忙态与侧栏「卡片名 · 未开始」、链接按钮、箭头、同名检测)。
- 单元测试:96 项 node:test(看板模型、注入与已读判定、落盘防丢、合并全链路、规范清单、同名匹配、迁移与撤销、落盘状态跟踪)+ 9 项 ai-memory 测试。
- 失败会点名:NEED_FULL_READ、DROPPED_ANCHORS、UNINDEXED_FILES、SPEC_ISSUES、NEED_GROUP_FOLDER、MIGRATE_CLASH、FOLDER_TAKEN、REPO_MOUNTED 等错误码点名对象,不静默吞掉。
- 兼容与限制:「恢复活跃」和删除空会话依赖 dsh-manage-sessions,缺失时按钮置灰或不显示;落盘项目需要 python3(Windows 关闭);多个 dsh 进程共用同一 DSH_HOME 时看板没有跨进程锁(~/.ai 写入有)。
- 安全边界:项目会话默认完全访问(只对项目区目录的会话生效,可改 inherit);HTTP 路由只接受本机回环(Host 必须是 127.0.0.1 / localhost),写操作带 Origin 时必须同源(DSH Desktop 壳转发会去掉 Origin,此时放行),没有登录态校验;插件会写 ~/.ai、建 ~/.agents/skills/landfill-project 链接;不读写凭据、不访问网络、不执行 git;删除卡片组只删软链接,不删真实仓库。
常见问题
- 侧栏没看到「项目区」? 新建第一个项目会话后才会登记;在那之前用侧栏「项目看板」主按钮。
- 卡片组没有嵌套在「项目区」下? 在工作区列表标题旁的「视图选项」里把分组方式选成「按工作区树」。
- 第一次落盘为什么问我落盘文件夹? 新建和迁移的落盘项目要放进卡片组自己的文件夹,只问一次;以后在卡片组设置里改。
- 我的旧落盘项目会被搬走吗? 只有在你对它落盘、且它关联的卡片组设了落盘文件夹时才迁;迁移后可在信息卡撤销,撤销后不再自动迁。
- 落盘被拒了怎么办? 看返回的错误码:没读全就先回忆;少了关键事实就补回或写进 drop;SPEC_ISSUES 按列出的规范项改。
- 「恢复活跃」是灰的? 当前 dsh 没有 workspaceRegistry.unarchiveSession,装 dsh-manage-sessions 并应用它的核心补丁即可。
- 「选择文件夹…」变成了输入框? dsh 使用网页文件浏览器(非本机访问或远程)时打不开系统对话框,直接输入绝对路径。
卸载
1. 调用 POST /_dsh/dsh-project-manager/api/hub/cleanup(或在工作区列表手动移除「项目区」和各卡片组工作区),撤销插件登记的工作区条目;会话与文件不会被删除。
2. dsh plugin --profile web remove dsh-project-manager,重启 dsh。
3. 数据保留在 <DSH_HOME>/dsh-project-manager/(board.json、events.jsonl、merges.json、migrations.json、hub/、backups/),需要时手动删除。
4. 落盘项目:~/.ai(含插件补建的 tools/ai-memory)是共享记忆,卸载不删。~/.agents/skills/landfill-project/SKILL.md 若是指向插件的链接,卸载后会断开;要留 skill 就把 backups/ 里的备份(或插件包里的 SKILL.md)复制回去。
本地构建
npm install
npm run build # host:tsc → lib/
npm run build:client # client:tsdown → lib/client.js
npm run typecheck
npm test # node:test 单测 + ai-memory 测试
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
hoyyang/dsh-project-manager
DeepSeek Harness 的项目看板:看板卡片分组,卡片关联 landfill-project 记忆、挂载的仓库和会话。
相关插件 · 市场 / 管理
插件精选列表awesome-dsh-plugin/awesome-dsh-plugin
DeepSeek Harness(dsh)插件精选列表
★ 21,780未汉化DSH
73.5
新词雷达zhuyansen/new-term-radar
new-term-radar新词雷达
★ 1,947已汉化MCP
72.7
插件生态导航0xsline/awesome-deepseek-harness
安装 贡献指南 DeepSeek Docs 公开插件目录 Issues
★ 1,188已汉化DSH
72.6
技能注册中心iflytek/skillhub
企业级开源智能体技能注册中心——发布、发现并管理组织内可复用的技能包。
★ 5,347已汉化DSH
71.9
插件精选清单Anil-matcha/awesome-dsh-plugin
DeepSeek Harnessdsh精选指南——DeepSeek…
★ 1,013已汉化DSH
71.7
插件市场目录imsai-sh/awesome-deepseek-harness-plugins
面向 DeepSeek Harnessdsh生态的社区插件目录,共收录 13917 个插件含 PR 收录与…
★ 343已汉化DSH
71.6