DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

heibaoxia /

heibaoxia/dsh-openspec-pipeline

Verified

OpenSpec 变更流水线(DSH 插件):把多个 change 排成一张有顺序的图,按依赖或并行开新对话执行;规划与任务进度读 openspec status/list,卡住时可一键 validate --strict 后归档。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@0049278a

dsh-openspec-pipeline

OpenSpec 变更流水线。一张图 = openspec/pipeline.json,每个节点是一个 change; 按依赖顺序(或并行)在选定工作区里新建一个顶层普通对话来执行。 对话里收到的第一句话就是 执行 <change> Change —— 没写附加提示词时,这就是全部。 节点可以只设一套「默认组合」(对话模式 / 运营商 / 模型 / 强度 / 附加提示词),新加的节点连 提示词一起带上,不用逐格点。

安装

桌面端(DeepSeek Harness 桌面版):侧栏「插件」→「添加插件」,粘贴仓库地址:

https://github.com/heibaoxia/dsh-openspec-pipeline

Web / CLI:

dsh plugin --profile web add github:heibaoxia/dsh-openspec-pipeline

装完重启 DSH(桌面端重启应用;CLI 重跑 dsh web),再刷新页面。面板顶栏出现 v0.11.0 就算装上了。

前置条件(缺了不会崩,只是少功能):

  • 目标仓库里要有 openspec/ 目录(openspec init 生成的);
  • openspec 命令要在 PATH 上。插件靠它读进度(list / status)、做归档(validate / archive)。 没有它:任务进度退回自己数 tasks.md,规划进度不显示,「归档」按钮会失败并写明原因。

装上之后从这里开始:面板顶部选工作区 → 列表里单击 change 加进图 → 拖线连到「开工」→ 点「开始」。

这一版不做什么、做什么

流水线仍然不验收、不归档。 0.8.0 起节点上的「验收方式」(机器验收 / 真人验收 / Playwright 代验)和 ⋯ 里的「碰到真人验收」都删了,settle(验收并归档)也一并删了。

要验什么、怎么归档,写进节点的附加提示词,由那个对话自己做。不写就只发一句 执行 <change> Change。

0.8.2 起加了一道「归档门禁」(只查目录,不做质量判断):

  • 这一轮正常收尾后,Host 看 openspec/changes/<x>/ 还在不在;
  • 进了 archive/ → 记「完成」,放行下一格;
  • 还在 changes/ → 记「等你」,写清“没归档”,后面的格子都不动。

0.9.2 起这道门禁会自己等:挂上「等你」之后 runner 不退出,每秒看一眼 archive/。 归档一出现(你手动 openspec archive,或在那个对话里补上),一秒内自动补记「完成」、 接着开下一格,不用再点「开始」。想提前收工就点「停止」。

也就是说:判断节点「完成」的标准是两条——这一轮正常收尾,而且 change 真的进了 archive/。只查目录,不要求 tasks 全勾;openspec validate 只在下面那个按钮上跑, 不参与「这一格算不算完成」的判定。

0.10.0 / 0.11.0 起多了三件事,都不改「流水线不做判断」这条底线:

  1. 「这一格做到哪」改读 openspec list --json,不再自己数 tasks.md。它一项一项给 completedTasks / totalTasks / status(complete / in-progress / no-tasks), 跟 OpenSpec 自己的口径一致。CLI 不在 PATH、或者退出码不是 0 时会退回自己数勾, 面板顶上写明原因——宁可少一个字段,也不能让面板白屏。

  2. 卡住时有一个「归档」按钮(对话已经停了、change 还在 changes/ 才出现): 先 openspec validate <change> --strict,把问题一条一条摊出来(可以复制给那个对话去改); 人点「仍然归档」才真的跑 openspec archive <change> -y。归完现有那个每秒看目录的 watcher 会自己接着开下一格,不用再点「开始」。

    这不是 0.8 删掉的 settle。 settle 是「AI 判断验收 + 归档」,带判断; 这个按钮只跑两条 openspec 命令,零判断。验证没过时它停下来问人,不会自己放行。

    为什么必须在归档前验:归档之后 change 就不在 changes/ 里了, openspec validate 用老名字、用带日期的新名字都只会回 Unknown item(1.13 实测)。 归档前那一刻是唯一能验的时机。

  3. 「propose 写完没」读 openspec status --all --json(0.11.0)。这一条治的是一个真问题: 只有 proposal.md 的 change,和「四件套只差 tasks.md」的 change,在 openspec list 里 都是 no-tasks、都是 0/0,面板分不出来。可这两者差得远——前者开跑等于让对话凭空开工, 后者差一步就能 apply。

    status 一次给全部未归档 change:artifacts(proposal / specs / design / tasks 各自的 done / ready / blocked)、isPlanningComplete,还有 nextSteps(下一步该跑哪条命令, 原话)。面板上就是黄色的 规划 2/4,鼠标停上去看「还差 specs、design」和「下一步:…」。

    这条也只显示、不拦:规划没写完的格子照样能进图、照样能跑——你可能就是想让它先把 propose 补完。要不要「规划没写完就不许开跑」,是一行判断的事,等你说了再加。

⚠️ 提示词里最好还是写归档那一步。不写的话,对话干完活不会自己归档,这一格会停在 「等你」——那时候点一下「归档」按钮,或者去那个对话里补一句,都行。

--strict 拦得住什么、拦不住什么(v1.13.2 实测,别指望它是万能的):

情况 默认档 --strict
requirement 正文里没有 SHALL / MUST 过 拦(WARNING 也算错)
scenario 只有标题、没有步骤 拦(ERROR) 拦
一个 scenario 都没有 拦(ERROR) 拦
scenario 只有 WHEN,没有 THEN 过 也过 ← 漏网
scenario 只有 THEN,没有 WHEN 过 也过 ← 漏网

也就是说它管的是「需求没写清必须/应当」「场景是空的」「根本没有场景」这三类, 管不了单边场景。所以按钮给的是报告 + 人决定,不是自动放行。

其余边界:

  • 对话没正常收尾(abort / error)→ 标「失败」,下游不开。
  • change 已经进了 archive/ 的格子,开跑时直接补记成「完成」,不会再拉对话。
  • 归档提醒是另一回事:0.8.1 起 Host 会盯着 changes/ 和 changes/archive/, 哪个 change 被移进 archive 就推一条消息给面板(见「怎么知道它完成 / 归档了」)。

启动纪律(重要)

这个插件同时有 Host 半身(lib/index.js)和 Web 客户端半身(lib/client.js)。 两边的 apply() 都不允许把异常抛出去:

  • 客户端半身抛异常 → DSH 的 web boot 判整页失败: web boot: N entry did not activate / dsh-openspec-pipeline: failed, 桌面端直接起不来,只能重置插件。
  • Host 半身抛异常或停在 pending(声明的服务一直不出现)→ assertEntriesActivated 同样致命。

所以代码里的约束是:

  1. lib/index.js 的 apply() 外面永远套一层 try/catch,里面失败只写日志、返回空清理函数; 工具注册、路由注册各自单独 try/catch。
  2. inject 只声明必然存在的服务(tools、webServer)。sessionController、agents、 workspaceRegistry 都用到时才 ctx.get(),缺了只让「开跑」失败,不让整个 Host 卡住。
  3. lib/client.js 每条 slots.inject / slots.register 单独兜住; 构造插件失败就交空壳;可选服务(betterSidebar)用有限次轮询,拿不到就放弃, 不用 ctx.inject 去等(那会造一个等不到服务的子 fiber)。
  4. 新增任何注册动作,都套进同样的 slot() / try 结构,别破坏这条纪律。

日志

<DSH_PROFILE_DIR>/openspec-pipeline.log

Windows 桌面端默认是:

C:\Users\<你>\.dsh\profiles\desktop\openspec-pipeline.log

里面有 Host 的 apply 进度、工具/路由注册结果、以及客户端半身上报的异常堆栈 (客户端通过 POST /openspec-pipeline/api/client-log 上报,永远回 200)。

桌面端起不来时的恢复

  1. 先看上面那个日志文件,确认是哪个半身、什么错。
  2. 编辑 ~/.dsh/profiles/desktop/package.json,把 dsh.profile.bundles 里的 dsh-openspec-pipeline 删掉(dependencies 里那行留着不影响),重启桌面端。
  3. 插件源码在 F:\project\dsh\plugins\dsh-openspec-pipeline,删 bundle 不会丢代码; 修好后把 bundle 加回来即可。

面板

页面只有三块:顶栏按钮、正方形画布、未归档 change 列表。

  • 顶栏:工作区下拉 / 刷新 / 保存 / 开始 / 停止 + 版本号。⋯ 里是并行上限、跑到哪停、默认组合、工作区路径(仓库还没登记成工作区时用它直接切)。
  • 画布:滚轮缩放(以指针为锚点),拖空白处平移,右下角显示缩放比例、点一下回到 100%。
  • 列表:列出所有未归档的 change,带「还没开工 0/19」「做到 3/9」「只差归档」「已在他处进行」「已在图里」这类标记。
    • 单击一行 → 加一个孤立节点进图(不自动连线),并立即落盘。
    • 右键已在图里的行 → 从图里删掉。
  • 节点:拖拽挪位置;从右边蓝点拉线到另一个节点 = 后者等前者;线中间的 × 断开。 双击节点打开设置:设置组合(含「用默认」「设为默认」)、对话模式(预置)、运营商、模型、思考强度、附加提示词。
  • 右键节点:显示这个节点为什么标红,并提供「从图里删掉这一格」。
  • 待处理提示:有节点「失败」或「等你」时,画布上方会逐条写出 N2 等你 · change 名 · 做到 3/9 · 原因,后面跟一个**「归档」按钮**(见下面「卡住的时候」)。
  • 手动排序提示:左栏是「手动排序」时,面板顶部会多一行提醒——新对话会排到分组末尾,切成「最近更新」才看得见。

头部版本号(当前 v0.11.0)可以用来确认页面加载的是新代码;还是旧版本就硬刷新/重启。

提示词:唯一能定制「这一格干什么」的地方

节点设置最下面那个框就是附加提示词。发出去的话按这个规矩拼:

节点上的提示词 对话收到的第一句话
空 执行 add-login Change
只做任务 1 到 3;做完跑 pnpm test;全过了再按 openspec-archive-change 归档。 执行 add-login Change + 空行 + 那段话

所以验收口径、要不要归档、要不要跑 Playwright,全都写在这里;插件只负责把话送到, 然后等这一轮结束。提示词跟着 openspec/pipeline.json 走,不进日志。

同一段话要在很多格重复时,把它连同四样设置存成一个组合(0.9.0 起组合带提示词), 以后选组合就一并换上;细节见「设置组合」。

工作区是插件自己的设置(不挑左侧对话)

顶栏第一个下拉就是工作区,选项来自宿主的 workspaceRegistry.list(),只列真的含 openspec/ 的。 选中后写进:

<DSH_PROFILE_DIR>/openspec-pipeline-settings.json

之后不管你从哪个对话打开面板,都读这一份,不再按当前对话的目录猜。这样同一个面板在任何 会话里打开,读写的都是同一张 openspec/pipeline.json、同一批 change。

解析顺序只有这三步,一步都不看当前对话:

  1. 插件设置里选过的仓库;
  2. 插件上次用过的仓库(openspec-pipeline-root.json);
  3. 登记过的 OpenSpec 工作区,按路径排序取第一个。

第 2/3 步一旦算出答案,立刻写进插件设置(settings.root),从此就是第 1 步。 所以第一次用之后,所有对话看到的是同一张图;想换仓库用顶栏下拉或 ⋯ 里的路径。

旧版第一步读的是「当前对话所在的目录」,所以在多个 OpenSpec 仓库(agent-forge / coatch / knowledge-skill / personal-agent …)之间切会话时,面板会跟着换成另一张图, 记忆也被来回覆盖。0.5.0 起这条路径已经删掉。

  • 选中的仓库还没登记成工作区时,插件会自动登记一次(否则新建的会话在侧栏里看不见)。
  • 只声明 openspec/ 的项目才能选;选到一半的目录会被拒绝并提示。
  • 第一次用、还没选过时,才回退到「上次用过的 → 任一登记过的 OpenSpec 工作区」。

每个节点是一个顶层普通对话

点「开始」时,插件走和 GUI「新建对话」同一条路:

sessionController.create({ workspaceId, agentPreset })  // 建普通会话并挂到工作区
sessionController.selectModel({ sessionId, provider, model, reasoningEffort })
sessionController.prompt({ sessionId, content, requestId })  // 投第一句话
// 等这一轮的 turn/end 落进会话日志;到了再查 archive/:进了才算完成、放下游

所以:

  • 新对话出现在插件工作区的分组里,点进去能看它实时干活,也能自己接着聊;
  • 左栏能不能看到它,取决于左栏的排序方式:
    • 「最近更新」:新对话会排到分组顶部,正在跑的会话会把可见条数抬到它(分组得先展开);
    • 「手动排序」:新对话会被追加到分组末尾,而分组默认只画前 5 条(自动抬升上限 50 条), 位置靠后就会看不到。切成「最近更新」,或在分组里把它置顶,就能看见;
    • 分组本身是折叠的也看不到:点一下分组标题展开。插件没有展开分组的公开接口, 面板只能提示,不能替你点;
  • 面板顶部会多出一行「N3 · 跑着(change 名)打开这个对话」。它按 sessionId 直接打开, 不受左栏排序影响;左侧看不到时就从这里进;
  • 面板在流水线运行期间每 15 秒让客户端重拉一次会话列表;点「开始」之后还会补读三次 (0.8s / 2.5s / 6s)。这只保证客户端拿到会话摘要,不改变左栏的排序和可见条数;
  • 每个节点一个新会话,互不共享上下文;
  • 节点选模型时,DSH 本来会把这个模型顺手写成全局默认模型;插件在同一个设置队列里 紧接着写回原值,所以跑完流水线不会改你新开对话的默认模型;
  • 点「停止」只取消当前这一轮(sessionController.cancel),不会把已经开起来的对话关掉。

之前的子会话方案(subagents.startActivation)已经去掉,不会再拉那种只能挂在父对话下面的 隐形子会话。

开始就是批准

没有「批准日期」这一项。点「开始」= 你当场点头,然后直接开跑。想改主意就点「停止」。

0.9.1 起开跑不再给全图盖时间戳:approved 只在哪一格真正开工时写哪一格,所以刚点「开始」 时 pipeline.json 里只有正在跑的那几格有时间,后面的格子还是空的。这张图也是一轮一轮算 的——每一轮从当前图算出「谁现在该跑」,开完就等它收尾,再算下一轮;没有在开跑时把整条链 排进队列。所以跑一半把下游断开再保存,它就不会被拉起来。

跑一半改图 / 跑完这一格就停

  • 运行中也能保存:连线、位置、设置、增删只作用在还没开始的格子;正在跑和已经跑过的 格子保留服务器状态,不会被面板里的草稿覆盖。 所以 N2 跑着的时候把 N3 从线上摘掉、点「保存」,N2 收尾后就真的不会拉 N3。
  • ⋯ →「跑完当前这一格就停」:勾上并保存后,当前这一轮跑完、手上没有别的活,runner 就自己收工,后面的节点一个都不碰。运行中勾也一样生效(保存一次即可)。
  • 点「停止」仍然是取消当前这一轮;它和上面两个开关是两件不同的事。

节点失败 / 等你,怎么继续

情况 动作
对话跑偏 / 中途断了,需要接着做 双击节点确认设置 → 点顶部「开始」。会把 failed / needs_human 放回待跑,开一个新对话重跑。
这一格不做了 右键节点 →「从图里删掉这一格」。
显示「等你」:没归档 对话收尾了,但 changes/<x>/ 还在。往下有两个办法:点上面那行的**「归档」按钮**(见下节),或者去那个对话里补一句 openspec archive <x> -y。归完一秒内自己往下走,不用点「开始」。
显示「等你」:他处进行 change 最近 15 分钟内被改过、而且做了一半,像是别的对话在同时做。流水线不开工,确认那边收工后再点「开始」。

卡住的时候:点一下归档

对话跑完了、也停住了,但 change 还在 changes/ 里——这一格就会挂在「等你」,后面的格子全不动。 面板会在画布上方列出这一格,后面跟一个**「归档」按钮**。点它会发生两件事,都是代码在跑:

  1. openspec validate <change> --strict
    • 过了 → 继续第 2 步;
    • 没过 → 不归档。把问题一条一条摆出来([WARNING] spec.md:... 这种), 旁边有「复制问题」——粘给那个对话让它去改,改完再点一次。问题下面是「仍然归档」和「关闭」。
  2. openspec archive <change> -y
    • 成功 → 这一格记「完成」,现有那个每秒看目录的 watcher 自己开下一格;
    • 失败 → 把 openspec archive 的原话贴出来(多半是解析不出来,或者任务还没写完)。

「仍然归档」只在验证没过的时候出现:它跳过第 1 步的判断直接归,节点说明里会写清 「validate 没过,人选择了仍然归档」——留个凭据,以后翻得到。

什么时候按钮不出现:这一格还在跑(先停)、change 已经归档了、或者 change 已经找不到。

「完成」是终态:这一格不会自己重跑。要重跑就先把节点状态放回待跑——目前靠「开始」只回收 failed / needs_human;已经「完成」的格子只能删掉重加(或者改 pipeline.json)。

模型从哪来

模型下拉不是手填的,直接从宿主 llm 服务读:

  • llm.listProviders() → 运营商(id + 显示名)
  • llm.listModels(provider) → 这个运营商下的模型(同一个 id 在不同运营商里各算一个,不会混)
  • llm.resolveModelInfo(provider, model) → 这个模型支持哪些思考强度;选中模型后强度下拉只列它支持的

目录在宿主里缓存 60 秒;点「刷新」会带着它一起重读。空结果不缓存: 宿主刚启动、llm 适配器还没注册完时列表会是空的,下次请求会自动重读;面板打开节点设置 发现运营商为空时也会补读(最多两次)。某个运营商读不出来时只留空列表,不会拖垮整张表 (原因写进 openspec-pipeline.log)。

设置组合:一套设置反复用

双击节点打开设置,最上面有「设置组合」,记五项:对话模式、运营商、模型、思考强度、附加提示词。

  • 沿用上一个节点:从它的前置节点(deps 只有一个时)复制四样运行设置,不带提示词 —— 上一格的口径是给上一个 change 写的,搬过来通常是错的。
  • 用默认:把默认组合的五项直接套到这一格。
  • 设为默认 / 取消默认:把当前选中的组合设成默认组合(下拉里带 ★)。
  • 套用到下游全部:沿依赖线往下的所有节点一次刷成同一套配方(含提示词)。
  • 套用到所有节点:整张图(「开工」除外)一次刷成同一套配方(含提示词)。
  • 存为组合:给当前这五项起个名字存起来,下次在「设置组合」下拉里直接选;组合存在 openspec/pipeline.json 的 profiles 里,跟着仓库走。选中组合会套到当前节点。 第一次存组合时会顺手设为默认。

提示词在组合里的规矩:

  • 选组合 / 用默认 / 套用到下游 / 套用到全部,提示词一起换;组合里没写提示词,这一格的 提示词就被清空(组合说了算,不搞半套)。
  • 新加的节点(列表单击、add-all)连提示词一起播种默认组合,所以「每格都要归档」这类 通用尾巴存进默认组合就够了。
  • 开跑时的兜底不碰提示词:节点四样全留空才会用默认组合,而那只取四样运行设置; 节点上手写的提示词不会被默认组合悄悄盖掉。

默认组合

默认组合存在 pipeline.json 的 defaultProfileId(指向 profiles 里的一项),跟着仓库走:

  • 新加的节点(列表单击、add-all)会自动带上默认的五项(含提示词),面板里看得见;
  • 四样全留空的老节点,开跑时兜底用默认组合的四样;只要设了其中一样,就按节点自己的来, 不再和默认混。提示词始终取节点自己的(理由见上);
  • 组合被删掉时默认一起清空,不会留一个指空的 id。

面板打开节点设置时最下面会写当前默认是哪个组合;没设时提示怎么设。 ⋯ 里也有一个「默认组合」下拉,选完点「保存」同样生效。

套用改的是整套配方(五项);连线、位置不会被覆盖。组合的增删立即落盘,套用到节点 仍按手改处理(点「保存」生效)。

节点状态与重跑

  • 画布上的状态以宿主落盘的状态为准,不是面板里的草稿副本。
  • 状态只有五种:待跑 / 跑着 / 完成 / 等你 / 失败。 (0.7 文件里遗留的「验收中 / 归档中」读回来按「失败」处理,点「开始」就能回收。)
  • 点「开始」会把上次 failed / needs_human 的格子放回待跑,所以失败一次不会永久卡死。
  • 面板不轮询仓库;但 Host 有推送(下一节),所以跑完/归档后面板会自己变。

怎么知道它完成 / 归档了(0.8.2:归档了才算完成,面板还会收到消息)

Host 到面板有一条 SSE 长连接:GET /openspec-pipeline/api/events。 事件只说明「变了」,面板收到就静默重读一次状态,自己不轮询磁盘。

开销按「有没有人听」算:

  • 面板没打开:没有连接,也没有 watcher —— 零开销;
  • 面板打开:一条空闲 socket(25 秒一次心跳,几十字节)+ 两个 fs.watch 句柄;
  • 有变化时:才读一次 pipeline.json 和那两个目录。
什么时候推 面板上看到什么
runner 写状态(跑着 / 完成 / 失败 / 等你) 画布上那一格自己变成新状态,不用点刷新
openspec/changes/<x>/ 被移进 changes/archive/ 提示行出现「已归档:<x>(archive/…)」,节点标红「已归档」,列表里它消失

归档是怎么判断的:只盯 openspec/changes/ 和 openspec/changes/archive/ 两个目录 (fs.watch,persistent: false,不递归、不扫全仓库)。目录从前者消失、在后者的名字以 -<change> 结尾,就算「已归档」。这条 watcher 不跑 openspec validate —— 它只报告目录事实 (validate 是「归档」按钮那一步的事,见「卡住的时候:点一下归档」)。

所以:

  • 对话在提示词里跑了 openspec archive,归档一完成,面板几秒内自己变;
  • 归档失败(目录还在 changes/):不会误报“已归档”,节点会停在「等你」并写明没归档;runner 继续等,归档一到就自动继续(0.9.2);
  • 面板没开着的时候连接不存在,事件不补发;下次打开面板会直接读到最新状态 (已归档的格子显示「已归档」)。

什么时候会自己读一次

面板自己不轮询仓库。会读盘的时刻:

  1. 面板第一次打开;
  2. 切换到另一个对话(座位变了)——右侧标签一直开着也一样会重读;
  3. 点「刷新」/「刷新进度」;
  4. 点「开始」之后补读三次(0.8s / 2.5s / 6s);
  5. 收到 Host 推送(上表那两种情况,0.8.1 起)。

画布是草稿,别被「刷新没反应」骗了

画布画的是你面板里的草稿,不是服务器的副本。只要这个面板里有过没保存的改动 (拖过节点、连过线、改过设置、加过格子都算),点「刷新」就只更新「服务器事实」 (状态、说明、进度、sessionId),不会拿服务器的图覆盖你的草稿——否则刚拖的位置、 刚删的线会自己弹回来。

这时如果服务器上的图真的变了(多了/少了格子、连线不同),面板会在刷新后直接说:

服务器上这张图变了(多了/少了格子或连线),你这边有没保存的改动,所以画布还是你的草稿。 [以服务器为准]

点「以服务器为准」= 丢弃草稿、按服务器重画;想留着改动就先点「保存」。 判断「图变没变」只看节点、change、连线,状态变化不算。

四种状态:这一格到底走到哪

change 的事实和节点的状态是两回事,面板把两样都摆出来:

change 的事实 判据(从哪读) 节点上写什么
propose 还没写完 openspec status 的 isPlanningComplete 是 false(四件套差一件以上) 规划 1/4、规划 3/4(黄色)
还没开始做 status 是 no-tasks(还没写任务清单),或者 in-progress 且勾了 0 个 0/9 或 没任务清单
做到一半 in-progress 且勾了 > 0 个 3/9
做完了,只差归档 status 是 complete,目录还在 changes/ 只差归档
做完并已归档 不在 list 里,且 archive/ 里有 已归档

前三行读 openspec list --json:completedTasks / totalTasks / status (complete / in-progress / no-tasks)。第四行 list 看不到——它只列未归档的, 所以靠目录判断。

注意第一行和第二行在 list 里长得一模一样(都是 no-tasks 0/0)。分出它们的是 openspec status --all --json 的 isPlanningComplete 和 artifacts。这也是为什么两个命令都要读。

节点那一行同时还有流水线的状态:待跑 / 跑着 / 完成 / 等你 / 失败。 两样叠起来才是完整的一句话,比如:

  • N2 · 跑着 · 3/9 —— 正在干,还有 6 个任务
  • N2 · 等你 · 3/9 —— 对话停了,但活没干完(这时候点归档是没意义的,它会 validate 不过)
  • N2 · 等你 · 只差归档 —— 活干完了、没归档。按钮就是给这一格用的
  • N2 · 完成 · 已归档 —— 收尾了

一个分不清的地方:in-progress + 勾了 0 个,光看文件分不清是「还没开始」还是 「刚开始干、第一个任务还没勾完」。agent-forge 里正在跑的 workspaces-from-canvas 就是 0/20 的 in-progress。这一格用节点自己的状态补:开过对话就是「跑着」,没开过就是「待跑」。

列表里的标记

标记 判据 能不能进图
还没开工 0/9 / 做到 3/9 勾数(来自 list --json) 单击就能进图
任务全勾了 9/9,只差归档 status 是 complete 也能进图;跑一遍就是开个对话,归档点按钮或写进提示词
他处进行 · N 分钟前改过 做了一半(勾了任务但没勾完),且文件在最近 15 分钟内改过 照样能加,但流水线开跑时会跳过它

注意:刚写好的 change(只有提案、或一项任务都没勾)文件时间也一定是新的, 但那只是「刚写完」,不会被判成「他处进行」——新建的提案可以正常进图。

「已在他处进行」是为了不跟你在别的对话里的手工推进重复。判定只看文件改动时间; 流水线自己拉起对话造成的改动会被认作自己人,不会误标。

图里的红灯

刷新时如果发现图里的节点对不上仓库,会标红并写清楚原因,删不删由你:

  • 已归档:openspec/changes/ 里没了,但 archive/ 里找得到。
  • 已找不到:哪都找不到(改名或删了)。
  • 已在他处进行:change 做了一半,且最近 15 分钟被改过,像是别的对话在做。

右键节点 →「从图里删掉这一格」是立即删除并落盘;流水线正在跑时后端会拒绝保存, 这时改动留在面板里,停掉之后再点「保存」。

依赖:openspec 命令

0.10.0 起,任务做到哪和归档都靠 openspec 这条命令:

  • openspec list --json —— 读勾数和官方状态;
  • openspec status --all --json —— 读规划写到第几件(0.11.0);
  • openspec validate <change> --strict --json —— 归档前体检;
  • openspec archive <change> -y —— 真正合并进主规格。

它不在 PATH 里也不会让插件坏掉:任务进度退回自己数 tasks.md,面板顶上会写明 「读不到 openspec 命令」。但归档按钮会失败——按钮就是靠这条命令干活的。

装法(任选):npm i -g @fission-ai/openspec,或者照 OpenSpec 官网的安装脚本。 装完在仓库根目录跑一次 openspec init(已有 openspec/ 的仓库不用)。

Host 调用它的方式是 cmd.exe /d /s /c openspec ...(Windows)/直接 openspec ...(其它平台), 参数一个一个传、不拼字符串;change 名只允许 [A-Za-z0-9._-],不合规就直接拒绝,不进命令行。

离线回归

不需要起 DSH,直接跑(用假 ctx):

node plugins/dsh-openspec-pipeline/test/default-preset-run.mjs
node plugins/dsh-openspec-pipeline/test/archive-gate.mjs
node plugins/dsh-openspec-pipeline/test/archived-node.mjs
node plugins/dsh-openspec-pipeline/test/event-stream.mjs
node plugins/dsh-openspec-pipeline/test/root-resolution.mjs
node plugins/dsh-openspec-pipeline/test/archive-action.mjs
node plugins/dsh-openspec-pipeline/test/planning-state.mjs

archive-action.mjs 会真的调 openspec(在临时目录里造两个 change:一个格式完整、一个残缺), 要证明四件事:CLI 状态读得到、完整的一次就归、残缺的只回报告不归档、人点强制才过。 planning-state.mjs 造三个完成度不同的 change(1/4、3/4、4/4),证明 list 分不出的 「只写了提案」和「只差 tasks」被 status 分开了。 机器上没有 openspec 时这两个都会自己跳过,不算失败。

—/ 5

No ratings yet

Verified DSH bundle

Commit 0049278acebe

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout