把工作组织成目标看板的 DeepSeek HarnessDSH插件 —— 基于图的目标管理Graph-based…
数据截至 2026/11/4 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
dsh plugin --profile web add miuzel/dsh-graph状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 22:56:48
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
查看扫描证据(共 476 处,展示前 106 条)
- 读取本地文件core/cache.ts:17
for (const name of ["events.jsonl", "project.yaml", "order.json", "rules.md"]) { try { const s=statSync(join(key,name)); h.update(name+":"+s.mtimeMs+" - 写入本地文件core/events.ts:52
appendFileSync( - 读取本地文件core/events.ts:67
if (!existsSync(file)) return []; - 读取本地文件core/events.ts:69
const lines = readFileSync(file, "utf8").split("\n"); - 写入本地文件core/events.ts:222
mkdirSync(memDir, { recursive: true }); - 读取本地文件core/events.ts:233
const lease = JSON.parse(readFileSync(lock, "utf8")); - 删除本地文件core/events.ts:240
if (!alive && Number(lease.expires) < Date.now()) { unlinkSync(lock); continue; } - 删除本地文件core/events.ts:247
try { return fn(); } finally { closeSync(fd); try { unlinkSync(lock); } catch {} } - 写入本地文件core/events.ts:256
mkdirSync(memDir, { recursive: true }); - 读取本地文件core/events.ts:267
if (!existsSync(file)) return []; - 读取本地文件core/events.ts:269
const lines = readFileSync(file, "utf8").split("\n"); - 读取本地文件core/events.ts:289
const file = join(root, "memory", "memory.jsonl"); if (!existsSync(file)) return []; - 读取本地文件core/ops.ts:244
const actual = realpathSync(forWrite && !existsSync(resolved) ? dirname(resolved) : resolved); - 读取本地文件core/ops.ts:245
const expected = forWrite && !existsSync(resolved) ? dirname(resolved) : resolved; - 读取本地文件core/ops.ts:443
if (existsSync(backlog)) { - 读取本地文件core/ops.ts:444
for (const f of readdirSync(backlog)) { - 读取本地文件core/ops.ts:448
for (const af of readdirSync(backlogArchived)) { - 读取本地文件core/ops.ts:451
if (existsSync(nested)) out.push(nested); - 写入本地文件core/ops.ts:548
mkdirSync(join(root, d), { recursive: true }); - 写入本地文件core/ops.ts:550
if (fresh) writeFileSync(events, "", "utf8"); - 写入本地文件core/ops.ts:552
if (!existsSync(index)) writeFileSync(index, "{}\n", "utf8"); - 写入本地文件core/ops.ts:555
writeFileSync( - 写入本地文件core/ops.ts:798
mkdirSync(dir, { recursive: true }); - 写入本地文件core/ops.ts:800
copyFileSync(target, join(dir, `HANDOFF-${ts}.md`)); - 删除本地文件core/ops.ts:4714
try { rmSync(join(sharedCardsDir(root), `${sharedId}.md`), { force: true }); } catch { } - 删除本地文件core/ops.ts:4986
try { rmSync(newFile, { force: true }); } catch (re) { restoreErr = re; } - 删除本地文件core/ops.ts:5010
rmSync(file, { force: true }); - 删除本地文件core/ops.ts:5018
rmSync(newFile, { force: true }); - 删除本地文件core/ops.ts:5125
try { rmSync(newFile, { force: true }); } catch (re) { restoreErr = re; } - 删除本地文件core/ops.ts:5149
rmSync(file, { force: true }); - 执行外部命令固定命令core/ops.ts:8471
head = execFileSync("git", ["rev-parse", "HEAD"], { cwd: workspace, encoding: "utf8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] }).trim(); - 读取本地文件core/platform.ts:156
entries = readdirSync(parentDir); - 读取本地文件core/platform.ts:165
lstatSync(join(parentDir, requested)); - 写入本地文件core/platform.ts:234
renameSync(src, dest); - 写入本地文件core/platform.ts:239
renameSync(src, dest); - 读取本地文件core/platform.ts:245
if (existsSync(dest)) { - 写入本地文件core/platform.ts:246
renameSync(dest, backup); - 写入本地文件core/platform.ts:249
renameSync(src, dest); - 删除本地文件core/platform.ts:251
try { unlinkSync(backup); } catch { } - 读取本地文件core/platform.ts:254
if (backedUp && existsSync(backup)) { - 写入本地文件core/platform.ts:255
try { renameSync(backup, dest); } catch { } - 写入本地文件core/platform.ts:295
chmodSync(fdOrPath, mode); - 读取本地文件core/platform.ts:322
const st = lstatSync(filePath); - 读取本地文件core/platform.ts:323
const text = content ?? readFileSync(filePath, "utf8"); - 读取本地文件core/root.ts:54
if (existsSync(gitPath)) { - 读取本地文件core/root.ts:55
return statSync(gitPath).mtimeMs; - 读取本地文件core/root.ts:83
while (!existsSync(probe)) { const parent = resolve(probe, ".."); if (parent === probe) break; probe = parent; } - 执行外部命令来自配置/变量core/root.ts:136
execSync: (cmd: string, options: any) => execSync(cmd, options), - 执行外部命令来自配置/变量core/root.ts:137
execFileSync: (file: string, args: string[], options: any) => execFileSync(file, args, options), - 读取本地文件core/root.ts:421
if (existsSync(localRoot) && existsSync(resolve(localRoot, "events.jsonl"))) { - 读取本地文件core/transaction.ts:132
return Date.now() - lstatSync(lockPath).mtimeMs; - 读取本地文件core/transaction.ts:169
before = lstatSync(lockPath); - 写入本地文件core/transaction.ts:174
try { chmodSync(lockPath, 0o600); } catch { } - 读取本地文件core/transaction.ts:176
try { holder = readFileSync(lockPath, "utf8").trim(); } catch { holder = null; } - 写入本地文件core/transaction.ts:194
renameSync(lockPath, quarantine); - 读取本地文件core/transaction.ts:199
try { moved = lstatSync(quarantine); } catch { moved = null; } - 写入本地文件core/transaction.ts:202
try { renameSync(quarantine, lockPath); } catch { } - 删除本地文件递归core/transaction.ts:206
rmSync(quarantine, { recursive: true, force: true }); - 写入本地文件core/transaction.ts:225
writeFileSync(lockPath, String(pid), { flag: "wx" }); - 读取本地文件core/transaction.ts:230
if (existsSync(lockPath)) { - 读取本地文件core/transaction.ts:232
const holder = readFileSync(lockPath, "utf8").trim(); - 写入本地文件core/transaction.ts:244
writeFileSync(lockPath, String(pid), { flag: "w" }); - 删除本地文件core/transaction.ts:316
rmSync(lockPath, { force: true }); - 写入本地文件core/transaction.ts:460
writeFileSync(tmp, content, "utf8"); - 删除本地文件core/transaction.ts:463
try { if (existsSync(tmp)) rmSync(tmp, { force: true }); } catch { } - 读取本地文件core/version-lane.ts:43
if (!existsSync(vfile)) throw new GraphError(`版本 ${slug} 不存在`); - 读取本地文件core/version-lane.ts:44
const doc = parseDoc(readFileSync(vfile, "utf8")); - 读取本地文件core/version-lane.ts:51
const doc = parseDoc(readFileSync(vfile, "utf8")); - 写入本地文件core/version-lane.ts:53
writeFileSync(vfile, serializeDoc(doc), "utf8"); - 读取本地文件core/version-lane.ts:63
if (existsSync(goalsDir)) { - 读取本地文件core/version-lane.ts:64
const entries = readdirSync(goalsDir).filter((d) => !d.startsWith(".")); - 读取本地文件core/version-lane.ts:68
if (existsSync(goalFile)) return true; - 写入本地文件core/version-lane.ts:117
writeFileSync(gf, serializeDoc(doc), "utf8"); - 写入本地文件core/version-lane.ts:135
writeFileSync(gf, serializeDoc(doc), "utf8"); - 写入本地文件core/version-lane.ts:153
writeFileSync(gf, serializeDoc(doc), "utf8"); - 写入本地文件core/version-lane.ts:208
mkdirSync(join(vdir, "goals"), { recursive: true }); - 写入本地文件core/version-lane.ts:219
writeFileSync(join(vdir, "version.md"), serializeDoc(doc), "utf8"); - 删除本地文件core/version-lane.ts:399
if (existsSync(cardsDir)) rmdirSync(cardsDir); - 删除本地文件core/version-lane.ts:402
if (existsSync(goalFile)) rmSync(goalFile); - 删除本地文件core/version-lane.ts:404
rmdirSync(entryPath); - 删除本地文件core/version-lane.ts:406
rmdirSync(goalsDir); - 删除本地文件core/version-lane.ts:414
if (existsSync(cardsDir)) rmdirSync(cardsDir); - 删除本地文件core/version-lane.ts:416
if (existsSync(goalFile)) rmSync(goalFile); - 执行外部命令固定命令core/worktree.ts:26
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); - 读取本地文件core/worktree.ts:64
if (!existsSync(file)) return true; - 读取本地文件core/worktree.ts:161
if (!existsSync(file)) return null; - 读取本地文件core/worktree.ts:163
const parsed = JSON.parse(readFileSync(file, "utf8")) as Partial<BoardOwnerMarker>; - 读取本地文件core/worktree.ts:261
try { const gf = findGoalFile(root, assoc.goal); const af = join(dirname(gf), "attempts", assoc.attempt, "attempt.md"); if (existsSync(af)) attemptMet - 读取本地文件core/worktree.ts:517
if (existsSync(join(dirname(gf), "attempts", c.attempt, "attempt.md"))) return true; - 执行外部命令固定命令core/worktree.ts:664
return execFileSync("git", args, { - 读取本地文件core/worktree.ts:1164
if (existsSync(targetPath2)) { - 写入本地文件core/worktree.ts:1194
mkdirSync(resolve(mainWorktree, ".worktrees"), { recursive: true }); - 读取本地文件dsh-graph-host/index.js:822
manifest = JSON.parse(readFileSync(manifestUrl, "utf8")); - 读取本地文件dsh-graph-host/index.js:880
return readFileSync(new URL(rel, import.meta.url), "utf8"); - 执行外部命令固定命令dsh-graph-host/index.js:1835
const text = execFileSync("git", ["worktree", "list", "--porcelain"], { - 执行外部命令固定命令dsh-graph-host/index.js:1927
execFileSync("git", ["merge-base", "--is-ancestor", compareHead, entry.head], { - 读取本地文件dsh-graph-host/index.js:2101
try { if (existsSync(join(dir, GRAPH_LEGACY_SETTINGS_MARKER))) return null; } catch { } - 读取本地文件dsh-graph-host/index.js:2107
if (!existsSync(file)) continue; - 读取本地文件dsh-graph-host/index.js:2108
const got = coerceLegacyGraphSettings(parseYaml(readFileSync(file, "utf8"))?.[GRAPH_LEGACY_SETTINGS_SECTION]); - 写入本地文件dsh-graph-host/index.js:2140
writeFileSync(join(legacy.dir, GRAPH_LEGACY_SETTINGS_MARKER), `${new Date().toISOString()} ${trace}\n`); - 读取本地文件dsh-graph-host/index.js:2801
attemptEntries = readdirSync(attemptsDir); - 写入本地文件dsh-graph-host/index.js:2819
mkdirSync(attemptsDir, { recursive: true }); - 执行外部命令固定命令dsh-graph-host/index.js:3078
return execFileSync("git", ["rev-parse", "--verify", `${raw}^{commit}`], { - 执行外部命令固定命令dsh-graph-host/index.js:3089
return execFileSync("git", ["diff", "--name-only", baseline, candidate], { - 写入本地文件dsh-graph-host/index.js:5042
writeFileSync(orderFile, JSON.stringify(order, null, 2), "utf8"); - 写入本地文件dsh-graph-host/index.js:6681
writeFileSync(
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
界面预览
3 张 · 点击查看大图功能亮点
- 目标看板组织工作
- 二维泳道看板
- 文件与事件流存储
展开完整介绍(139 字)↓
把工作组织成目标看板的 DeepSeek Harness (dsh) 插件:目标 / 判据 / 上下文卡片 / 执行 attempt 的二维泳道看板,数据以文件+事件流落在 .dsh-graph · Goal-kanban plugin for DeepSeek Harness
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
dsh-graph
把工作组织成目标看板的 DeepSeek Harness(DSH)插件 —— 基于图的目标管理(Graph-based Goal Management)。
当前版本 v0.23.0 —— npm 包名 dsh-graph,一个包同时提供面向智能体的 53 个 graph_ 工具(含 /api/dsh-graph REST 端点)与内嵌 DSH Web 的二维泳道看板。
最新亮点(v0.23.0)
- 长任务智能关照与僵局干预:主管空闲期间,对进行中的子代理长任务按生效间隔自动发起巡检与状态把关;连续未回复或进展停滞时自动升级等待人工,到期只触发巡检,不武断中断执行;支持整组关照的暂停、继续与重置。
- 子代理时间预算调度与动态基准重算:支持在目标中显式配置预计任务时间预算(未配置则继承项目/全局默认阈值);首次派发、运行中调整预算,或主管向运行中子代理发送新消息时,立即依据最新时间基准重新计算关照截止,并保持至少 5 分钟无答有效冷却,防止频繁唤醒。
- 运行轮次精确隔离与历史保真追溯:子代理运行按起止时点划定独立轮次,停止即停表,重新运行自起新轮;精确记录每轮起始预算与变更时间线,禁止以停止时刻预算冒充整轮预算;正常宿主重建遇到同一运行中任务时不拆分轮次,并如实标注观察空窗。
- 主管会话智能保活机制:名下有子代理在运行且主管处于空闲时,自动在适当时机进行有界续期与单次防离线保活,避免长时间未交互导致上下文失效。
- 插件 HTTP 接口安全加固与派发去重:全面封闭无凭证访问风险,对齐宿主统一鉴权与身份白名单校验;强化派发准入门禁,严格防止同一目标并发派发重复执行尝试,杜绝状态冲突与资源争用。
变更史见 CHANGELOG.md。
平台状态
| 平台 | 本版状态 |
| Linux / WSL2 | ✅ 已实测通过(本版全量测试 fail 0;H 系列共享探针在本版全绿) |
| 原生 Windows | ✅ 本版已实测通过(2026-11-02,win32/x64 + Node v24.21.0,宿主 0.2.1-rc.5,通过 22 / 失败 0 / 告警 0):详见 platform-gate §7.13 回填表 —— T1 静态/台账(67 项 268 处命中未登记 0)/ 派发可用(零硬编码 shell 名)/ H 组合探针(H1–H6 除 H3 外全绿)全部通过;T3 看板文件系统生命周期 32 步盘面断言全通过;T4/T5 实例启动就绪与 REST 鉴权换票冒烟通过。 |
| macOS | ⏳ 本版未验证:v0.23.0 未在原生 macOS 上执行真机门禁 ⇒ 不得读出「已通过」;结论同样以平台门禁 §7 回填表(不随包发布)为准。历史记录见 §7.4 / §7.7,只覆盖各自版本的产品代码。 |
三平台共用同一安装包。已知限制:① macOS 默认文件系统 APFS 大小写不敏感——仅大小写不同的目标编号 / 版本泳道会落到同一实体,请勿只用大小写区分;② macOS 上经显式传入且含符号链接的工作区路径(如位于 /tmp、/var 之下)会被拒绝并报 graph root symlink is not allowed,由 process.cwd() 推导的路径不受影响。
HTTP 鉴权(Web 入口)
Web 宿主里的 /api/dsh-graph/ 走单一受保护入口:只注册一条 prefix 路由。每个请求先由宿主公开的 connection.admit 鉴权,再判定 owner,之后才分发业务。未通过 ⇒ 401(匿名或凭证无效)、403(信任边界或非 owner)、503(宿主没有 admit 等能力,无法判定,fail-closed)。该入口只影响 HTTP*:graph_ 工具面不经过它。
鉴权行为只在 DSH 0.2.1-rc.2 实测;engines.dsh 声明的其它宿主版本未逐版验证。完整口径见 dsh-graph-host/README.md。
安装
dsh plugin --profile <name> add dsh-graph
需要 Node ≥ 22。依赖说明、宿主兼容范围(engines.dsh 声明与实测宿主)、侧边栏用法、数据目录等完整内容,见 dsh-graph-host/README.md(即 npm 包内 README)。升级宿主后若 Web 看板半边不激活(web boot: … did not activate),见该 README 的「升级残留自检与清理」。
核心概念
- 基于图的目标管理:目标是自足实体 —— 自然语言任务 + 动态生成的取证计划与质量判据;任务类型不预先模板化,结构化的是生命周期与求值语义。
- 四阶段生命周期:描述 → 收集 → 执行 → 确认,由引擎强制的状态机:draft → planning → collecting → ready → in_progress → review → delivered(任意阶段可进入 blocked)。
- 判据先于执行:进入执行前先登记质量判据,评审按逐条判据核验产出物。
- 上下文卡片:目标 Runner 的种子上下文,生命周期 empty → collecting → filled → reviewed;形态分文本 / 文件 / 图片 / 数据。
- 排期:Backlog(暂存池)↔ Version(批量质量管理)↔ 独立目标(standalone);看板泳道顺序是展示态,可拖拽调整。
- 分级评审与策略配置:支持配置项目专属模块区域 review.regions、冻结契约 review.contract_paths 与排除前缀 review.non_product_prefixes,未登记区域自动安全升级 strict,空契约默认 M1 不触发。
- 换会话交接:graph_handoff 生成交接文档(board 投影 + 长期记忆 + 环境事实),graph_claim_supervisor 由新会话幂等接管。
- 目标间关系:目标之间可标记「取代 / 调整 / 补充 / 相关」四类关系(graph_set_relation,可增可删);关系只记在目标 frontmatter 这一处真源,看板卡片与目标弹窗直接可见。
- Profile 全局设置:子代理默认 provider / model、推理档位、执行模式、提示词语言与补充提示词,可在「设置 → 看板设置」「右侧栏 → 插件 → dsh-graph」两处任一打开,两处读写同一份 profile 配置(workspace project.yaml 明确配置优先)。
提供的工具
53 个 graph_ 工具,按功能分组(逐个说明见 dsh-graph-host/README.md 或 graph_help):
| 分组 | 工具 |
| 目标生命周期 | graph_create_goal · graph_rename_goal · graph_set_description · graph_set_goal_type · graph_set_goal_tags · graph_set_goal_budget · graph_amend_goal · graph_transition · graph_postpone_goal · graph_archive_goal · graph_unarchive_goal · graph_delete_goal · graph_clean_worktree · graph_list_worktrees |
| 目标关系 | graph_set_relation |
| 质量判据 | graph_set_criteria |
| 上下文卡片 | graph_add_card · graph_fill_card · graph_review_card · graph_bind_collect_card · graph_delete_card · graph_convert_card_to_shared · graph_convert_card_to_owned · graph_attach_shared_card · graph_detach_shared_card · graph_list_shared_cards |
| 附件 | graph_store_attachment · graph_delete_attachment |
| 排期 | graph_move_goal |
| 执行派发 | graph_start_attempt · graph_start_review · graph_set_directive · graph_record_attempt_handoff · graph_unbind_goal_child · graph_abandon_attempt |
| 记忆 | graph_memory_add · graph_memory_recall · graph_memory_remove · graph_memory_replace |
| 配置管理 | graph_get_settings · graph_update_settings |
| 校验 / 对账 | graph_validate · graph_rebuild |
| 状态汇报 | graph_report_status · graph_report_supervisor_status · graph_care_outcome |
| 评审裁决 | graph_resolve_accept |
| 历史讨论 | graph_add_comment |
| 完成摘要 | graph_write_results · graph_refresh_results |
| 换会话 | graph_handoff · graph_claim_supervisor |
| 帮助 | graph_help |
看板(浏览器客户端)
浏览器二维泳道看板:横向是生命周期阶段列(描述 / 收集 / 执行 / 确认 / 交付 / 阻塞),每个版本一条泳道,另有 Backlog 与独立目标区;支持拖拽排期、判据 / 上下文卡片抽屉、实时状态显示、阻塞折叠等。以下为虚构演示数据(nebula-notes)截图:
看板总览目标详情弹窗侧边栏看板(窄档:阶段列纵向堆叠、工具条折叠为 ⋯ 工具)
侧边栏与数据目录
侧边栏入口、窄档(<480px)分档行为与「怎么把看板放进窄档」的实测说明,以及数据目录 <workspace>/.dsh-graph(纯文本 + 只追加 events.jsonl,git 友好、可 graph_rebuild 对账)的完整说明,均见 dsh-graph-host/README.md。
仓库结构(monorepo)
- core/ —— 核心层源码(唯一事实源),经 scripts/sync-core.sh 编译成 dist/core/.js 进发布包;核心层不依赖 DSH。
- dsh-graph-host/ —— 单包发布物源码:index.js(工具 + REST 端点)、lib/client/.js(看板源模块,构建产物为 dist/lib/client.js)、cordis.patch.yml、supervisor-guide.{zh,en}.md、README.md、LICENSE。
- schema/、docs/、scripts/ —— 数据 / 设计文档 / 构建脚本;一切构建产物落在 dist/(gitignored)。
开发
bash scripts/build.sh # 同步 core + 客户端产物 + 复制发布资产到 dist/
node --test core/tests/.test.ts # 全量测试
./node_modules/.bin/tsc --noEmit -p tsconfig.json
隔离测试实例(不碰主 GUI):DSH_HOME 与 workspace 均在 ./tmp/dsh-test/ 下 bash scripts/dsh-test-web.sh <DSH版本> [--port PORT]
构建与实验一律在隔离 worktree(.worktrees/<goal>-att-<NN>)内进行;约定见 AGENTS.md,隔离实例参数与开发回路见 docs/dev-instance-guide.md。README 截图用 scripts/dsh-graph-mock-seed.mjs 生成虚构演示数据后复现。
License
MIT(Copyright © 2026 miuzel)—— 见 dsh-graph-host/LICENSE。预览分享卡片长什么样 ↓
把工作组织成目标看板的 DeepSeek HarnessDSH插件 —— 基于图的目标管理Graph-based…
相关插件 · 工具 / 效率
这是你的仓库?认领条目