🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

任务栈 Ray1270/dsh-task-stack

DeepSeek Harnessv0.2.0
Ray127031 天前有提交已提供中文文档扫描:含敏感能力

为 DeepSeek Harness 智能体提供按会话持久化的任务栈——focus_task / focus_complete / read_focus 加 /focus,零提示词注入

31.5/100
综合分
2
GitHub 星标
56
npm 周下载
—/5
用户评分

数据截至 2026/10/4 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @ray1270/dsh-task-stack
npm 源 · @ray1270/dsh-task-stack 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · market
装得上吗
静态安装检查通过
未做真实安装
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 31 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包@ray1270/dsh-task-stack @ 0.2.0
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/11 07:00:01

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件
查看扫描证据(共 7 处,展示前 7 条)
  • 读取本地文件
    lib/store.js:179
    entries = await readdir(directory);
  • 读取本地文件
    lib/store.js:197
    const info = await stat(path);
  • 删除本地文件
    lib/store.js:209
    await rm(path, { force: true });
  • 读取本地文件
    lib/store.js:352
    text = await readFile(location.path, 'utf8');
  • 写入本地文件
    lib/store.js:444
    await mkdir(dirname(path), { recursive: true });
  • 写入本地文件
    lib/store.js:456
    await rename(temp, path);
  • 删除本地文件
    lib/store.js:459
    await rm(temp, { force: true });
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cosmokit@deepseek-ai/dsh-agent@deepseek-ai/dsh-brand@deepseek-ai/dsh-commands@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-invariants@deepseek-ai/dsh-llm@deepseek-ai/dsh-ptc-runtime@deepseek-ai/dsh-sandbox@deepseek-ai/dsh-sandbox-policy@deepseek-ai/dsh-scope
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 按会话持久化任务栈
  • 零提示词注入
v0.1.12026-10-04
· 本版变化 补齐工程化:让仓库本身可被信任地使用与验证。 · 持续集成 新增 .github/workflows/ci.yml:每次 push / PR 都跑完整的发布闸门——pnpm install --frozen-lockfile → node scripts/release-check.mjs(10 步:typecheck、build、产物自检、无宿主 probe、4 套测试、demo)→ npm pack --dry-run。 矩阵当前只放 Windows:测试套件在 Windows 上开发与验证,而当时机器上既没有 WSL 也没有 Docker,无法证明它们在 Linux 上能过——没跑过的腿不该提交上去让人收获一个原因不明的红。Linux 腿已写好并以注释形式留在 workflow 里。 · README · 顶部加三个徽章:npm version / license / CI · 项目结构段补全(commands.ts、CI workflow、LICENSE、各测试脚本) · package.json 补 repository / homepage / bugs 字段。 · 安装 dsh plugin --profile add @ray1270/dsh-task-stack · 兼容性 DSH 0.2.0-rc.2;@deepseek-ai/cordis ^4.0.2、dsh-tools、dsh-commands、schemastery ^3.18.2 均为 peerDependencies,由宿主运行时提供。不注册任何 prompt section、不做自动注入。
v0.1.02026-10-04
· 首次发布 给 DeepSeek Harness Agent 用的可持久化任务栈:模型显式调用三个工具 push / pop / 查看自己的多步任务焦点,状态以纯文本 JSON 落在会话工作区里。 · 跨上下文压缩存活 —— 状态在磁盘上,压缩掉对话也不丢 · 跨会话恢复可找回 —— 同一 sessionId + cwd 重新挂载即读回同一份文件 · 零自动注入 —— 不写系统提示词、不注入上下文,Agent 不主动调用就一个 token 都不花 · 三个工具 | 工具 | 作用 | | --- | --- | | focus_task | 把一个多步目标压栈;原来的栈顶转为 paused | | focus_complete | 弹出栈顶、记一行结论、让下一层重新变 active | | read_focus | 读取整个栈与最近历史(唯一查看方式,没有自动注入) | 另有斜杠命令 /focus:查看 / done / clear,不花模型轮次。 · 存储 · 每个会话一个 JSON 文件://.json · 原子写入(tmp → fsync → rename),读者只会看到完整的旧文档或新文档 · per-file 异步锁,并发调用不会丢更新 · 文件缺失 / 读失败 / JSON 坏 / 版本不符 / 会话不符 → 退化成空栈或丢弃坏帧并附 warning,从不抛异常 · 配置(Cordis 在插件启动前校验) | 字段 | 默认 | 含义 | | --- | --- | --- | | stateDir | .dsh/task-stack | 状态文件目录,相对会话工作区 | | maxStackDepth | 20 | 栈上最多同时开着的任务数 | | historyLimit | 100 | 历史最多保留多少条 | | statePruneDays | 0 | 删除超过这么多天没写过的会话状态(0 = 不清理) | 越界值直接拒绝加载该插件并在启动日志指出字段,不会静默夹紧成另一个策略。 · 验证 · 69 项断言(store 22 / tools 18 / config 12 / commands 13 / lifecycle 4) · lifecycle 套件把插件挂成真实 Cordis 子 fiber 并覆盖真实 ToolRuntime,证明卸载/热重载不留残留 · 加载器集成走真实的 cordis-plugin-loader · 安装 dsh plugin --profile add @ray1270/dsh-task-stack
v0.2.02026-10-04
· 本版新增:只读 HTTP 快照端点 供 Web GUI 面板读取当前会话的任务栈: GET /api/dsh-task-stack/snapshot?sessionId=&cwd= 返回 depth / top / stack / history / warnings。 | 情况 | 响应 | | --- | --- | | 正常 | 200 + JSON,cache-control: no-store | | 未认证(无浏览器 cookie)/ Host 不受信 | 401 / 403 | | 组合里没有 connection 服务(无法鉴权) | 503,失败关闭 | | 缺 sessionId、cwd 非绝对路径 | 400 | | 非 GET/HEAD | 405 + Allow | | 会话没有状态文件 | 200,空栈(不是 404) | · 安全 raw WebServer 路由不继承 Connection 的鉴权,因此端点自行调用 connection.requestRejection(req)——与第一方 API 同一道 Host/Origin + 浏览器 cookie 校验。组合里没有 connection 时失败关闭返回 503,而不是无鉴权放行。 · 只读 端点只暴露 getSnapshot;关闭任务仍走 focus_complete 或 /focus。写入面需要单独的安全设计,不在本版范围内。 · 破坏性变更 无。三个工具、/focus 命令、配置字段、状态文件格式都没有变化,从 0.1.x 升级不影响现有任务栈数据。 · 验证 · 发布闸门 11 步全绿,共 80 项断言 · 新增 scripts/test-http.mjs:挂载真实 @deepseek-ai/dsh-host-webserver 到 OS 分配的端口,用真实 socket 驱动,覆盖 fence 双向、缺 connection 返回 503、卸载时撤销路由 · 真实宿主实测:带浏览器 cookie → 200 且与磁盘状态逐项一致;匿名 → 401;被拒 Host → 403 · 安装 dsh plugin --profile add @ray1270/dsh-task-stack --- 注:npm 上的 0.2.0 是这个 tag 指向的内容(shasum 130e8109a3259781ba8a5213697f661790ba012f,17 个文件)。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
@ray1270/dsh-task-stack 给 DeepSeek Harness Agent 用的可持久化任务栈:Agent 显式调用三个工具来 push / pop / 查看自己的多步任务焦点,状态以纯文本 JSON 落在会话工作区里。 - 跨上下文压缩存活 —— 状态在磁盘上,压缩掉对话也不丢 - 跨会话恢复可找回 —— 同一 sessionId + cwd 重新挂载即读回同一份文件 - 零自动注入 —— 不写系统提示词、不注入上下文,Agent 不主动调用就一个 token 都不花 focus_task("给 CLI 加 JSON 导入命令") └─ 遇到需要单独完成的多步目标 → focus_task("顺便把导入的错误提示改掉") └─ focus_complete("错误提示已改,附带单测") ← 栈顶回到 "给 CLI 加 JSON 导入命令",重新变 active focus_complete("导入命令已加,冒烟测试通过") 三个工具 模型看到的描述都按「什么时候该用 / 什么时候不该用 / 返回什么」写。 focus_task(description) 把一行任务描述压到栈顶;原先 active 的任务自动变 paused。返回新的栈深度与栈顶任务。 - 该用:用户要的东西需要好几步才能交付,尤其是一个请求里有多个可分离目标时。
展开完整文档(README 剩余 1 节)
@ray1270/dsh-task-stack

给 DeepSeek Harness Agent 用的可持久化任务栈:Agent 显式调用三个工具来 push / pop / 查看自己的多步任务焦点,状态以纯文本 JSON 落在会话工作区里。

- 跨上下文压缩存活 —— 状态在磁盘上,压缩掉对话也不丢
- 跨会话恢复可找回 —— 同一 sessionId + cwd 重新挂载即读回同一份文件
- 零自动注入 —— 不写系统提示词、不注入上下文,Agent 不主动调用就一个 token 都不花

focus_task("给 CLI 加 JSON 导入命令")
└─ 遇到需要单独完成的多步目标 → focus_task("顺便把导入的错误提示改掉") └─ focus_complete("错误提示已改,附带单测") ← 栈顶回到 "给 CLI 加 JSON 导入命令",重新变 active
focus_complete("导入命令已加,冒烟测试通过")

三个工具

模型看到的描述都按「什么时候该用 / 什么时候不该用 / 返回什么」写。

focus_task(description)

把一行任务描述压到栈顶;原先 active 的任务自动变 paused。返回新的栈深度与栈顶任务。

- 该用:用户要的东西需要好几步才能交付,尤其是一个请求里有多个可分离目标时。推你最外层要对用户负责的那个目标,不是你自己计划的每一步。
- 不该用:单轮问答;栈顶任务内部的例行步骤;复述用户消息。
- 栈到达 maxStackDepth 时拒绝并说明原因,不抛异常。

focus_complete(conclusion)

弹出栈顶任务,记下一行结论,下面那个任务重新变 active。返回被关闭的任务、重新激活的任务、剩余深度。

- 该用:栈顶任务的多步工作完成、产物已经落地时,且在写最终答复之前调用——这样之后即使发生上下文压缩,也能看到已关闭的结论。
- 不该用:放弃、暂停、改名。目标变了应该改述栈顶任务,而不是把它挂着。
- 空栈时拒绝并提示:说明这次工作根本没 push 过任务。

read_focus()

以 markdown 返回完整栈(active + paused)与最近若干条已完成记录。

- 该用:上下文压缩之后、恢复会话时、用户问「你现在在做什么」时、决定该 push 还是 complete 之前。
- 不该用:每一步之后都调。你自己近几轮还看得到焦点时就别花 token。
- 因为本插件不注入提示词,这是唯一能看到任务栈的办法。

/focus 斜杠命令(人用,不花模型 token)

除了三个工具,插件还注册一个 /focus 命令,直接作用于当前会话的任务栈——排查问题或手工收尾时不用消耗一轮对话。

| 输入 | 作用 |
| /focus | 打印任务栈(active/paused 标注 + 深度)与最近历史 |
| /focus done <结论> | 关闭栈顶任务并把结论写进 history,等价于一次 focus_complete |
| /focus clear | 清空整个栈,每个被清的任务都会记入 history(结论为 cleared via /focus),不会静默丢失 |

/focus clear 是幂等的:空栈时回一句"已经是空的",不算错误。/focus done 空栈时会明确告诉你栈是空的——那是"没 push 过任务"的信号,而不是需要重试。

安装

方式 A:作为 profile bundle 安装(常规使用)

从 npm 安装(推荐)
dsh plugin --profile desktop add @ray1270/dsh-task-stack

或者克隆仓库后按本地目录安装(适合要改源码时)
git clone https://github.com/Ray1270/dsh-task-stack.git
cd dsh-task-stack
dsh plugin --profile desktop add $pwd

dsh plugin add 会把包装进 profile 的 node_modules 并把它登记进该 profile package.json 的 dsh.profile.bundles。profile 应用本包自带的 cordis.patch.yml,其中的加载行 name: @ray1270/dsh-task-stack 就从 profile 的 node_modules 解析。重启 DSH Desktop 后新会话即可直接让模型调用这三个工具、使用 /focus。

若不想走包管理器安装,也可以手工两步(等价)。在克隆出来的仓库根目录里执行:

1) 建 junction,让 profile 能按包名解析到这个目录 $plugin = (Get-Location).Path $link = "$env:DSH_HOME\profiles\desktop\node_modules\@ray1270\dsh-task-stack" New-Item -ItemType Directory -Force -Path (Split-Path $link) | Out-Null cmd /c mklink /J "$link" "$plugin"

2) 编辑 $env:DSH_HOME\profiles\desktop\package.json,在 dsh.profile.bundles 数组里加一项 "@ray1270/dsh-task-stack" 不要写 "dependencies": { "@ray1270/dsh-task-stack": "link:...." }:profile 在 C: 而插件在 D:,path.resolve 到盘根就停,多少个 .. 都跨不了盘,一条解析不了的 link: 会让以后的 pnpm install 直接失败。Bundle 靠 profile node_modules 里的 junction 解析,不需要依赖项。

方式 B:免安装的临时 overlay(开发调试)

注意 --patch 属于启动器:必须写在 profile 名之前
dsh --profile web --patch ".\dev.patch.yml" --no-open

dev.patch.yml 用 name: ./lib/index.js,由加载器改写成 patch 文件旁边的 file:// URL,因此不需要安装、不需要 node_modules 解析。cordis.patch.yml 用的裸包名只在包已安装(或可被 node_modules 解析)时才有效。

两个坑:
- dsh web --patch x.yml 是错的(web 子命令不认识 --patch),要写 dsh --profile web --patch x.yml。
- patch 行里的相对目录(name: ./)也不行:Node 对目录 URL 抛 ERR_UNSUPPORTED_DIR_IMPORT,且 file: 说明符不走 exports。

HTTP 快照端点(Web 侧栏数据面)

在 Web 组合(dsh --profile web)里,插件会额外注册一个只读端点,供 GUI 面板读取任务栈:

GET /api/dsh-task-stack/snapshot?sessionId=<id>&cwd=<绝对路径>

返回:

{
"sessionId": "session-…", "cwd": "D:\\…", "depth": 2,
"top": { "id": "task-…", "description": "…", "status": "active", "createdAt": "…" },
"stack": [ /* 最深在前,栈顶在最后 / ],
"history": [ / 最旧在前 / ],
"warnings": []
}

行为约定:

| 情况 | 响应 |
| 正常 | 200 + JSON,cache-control: no-store |
| 未认证(无浏览器 cookie)/ Host 不在信任范围 | 401 / 403 |
| 组合里没有 connection 服务(无法鉴权) | 503,失败关闭而不是无鉴权放行 |
| 缺 sessionId、cwd 非绝对路径 | 400 |
| 非 GET/HEAD | 405 + Allow |
| 会话没有状态文件 | 200,depth: 0 的空栈(不是 404) |

三点实现说明:

- 它不继承栅栏。 raw WebServer 路由不吃 connection 的鉴权,所以端点自己调 connection.requestRejection(req)——与第一方 API 同一道 Host/Origin + 浏览器 cookie 校验。这条路径实测过:带 cookie 200、匿名 401。
- 只读。 端点只暴露 getSnapshot;关闭任务仍走 focus_complete 或 /focus。写入面需要单独的安全设计,不在本版本内。
- 可选激活。 webServer / connection 都不写进 inject(终端与 headless 组合没有它们,硬依赖会让插件整个 pending),而是用 ctx.inject(['webServer'], …) 等服务出现后再注册;connection 在每次请求时解析——两者都 inject: [webRuntime],激活顺序不定,捕获一次会把 undefined 存下来(这个 bug 我踩过,表现为端点一直 503)。

手动验证(宿主起来后):

1) 用启动时打印的 URL 换浏览器 cookie(303 + Set-Cookie) curl.exe -i "http://127.0.0.1:3080/?token=<token>"

2) 带 cookie 请求快照 curl.exe "http://127.0.0.1:3080/api/dsh-task-stack/snapshot?sessionId=<id>&cwd=D%3A%5Cpath" -H "Cookie: dsh-auth-…=…"

3) 不带 cookie 必须被拒 curl.exe -i "http://127.0.0.1:3080/api/dsh-task-stack/snapshot?sessionId=<id>"

配置

在 profile patch 层的该行 config: 下配置,全部可选;Cordis 会在插件启动之前校验,越界直接拒绝加载该插件并在启动日志里指出字段(不会静默夹紧成另一个策略)。

| 字段 | 默认 | 约束 | 含义 |
| stateDir | .dsh/task-stack | 非空、相对路径 | 状态文件目录,相对会话工作区 |
| maxStackDepth | 20 | 整数 ≥ 1 | 栈上最多同时开着的任务数;超过则 focus_task 拒绝 |
| historyLimit | 100 | 整数 ≥ 1 | history 最多保留多少条完成记录,从最旧的开始裁剪 |
| statePruneDays | 0 | 整数 ≥ 0(0 = 不清理) | 插件加载时删除超过这么多天没写过的会话状态文件 |

- insert:
- id: @ray1270/dsh-task-stack
name: @ray1270/dsh-task-stack
config:
stateDir: .dsh/task-stack
maxStackDepth: 20
historyLimit: 100
statePruneDays: 30 # 可选:清掉一个月没动过的会话

statePruneDays 是尽力而为的维护动作,以 process.cwd() 为工作区、在插件加载后异步执行,只删 stateDir 里直接存放的 .json:

- 文件的 mtime 就是它最后一次写入时间;
- 目录不存在、没有任何文件要删 → 静默跳过,不是错误;
- 崩在写入中途留下的 .tmp 不删(留给人工注意);
- 每个删除都走同一把 per-file 锁,不会和正在进行的 read-modify-write 交错;
- 清理慢或失败只写一行日志,绝不影响插件激活。

状态文件

路径:<session-cwd>/<stateDir>/<sessionId>.json,一个会话一个文件,同工作区的不同会话互不干扰。

{
"version": 1,
"sessionId": "session-087e04a2-f613-413f-ab38-e3a3ff2f1e75",
"stack": [
{
"id": "task-35e0216c-75cc-472c-8e89-1fbbc05b1ac9",
"description": "给 CLI 加 JSON 导入命令", "createdAt": "2026-10-01T07:24:46.200Z", "status": "active" } ],
"history": [
{
"id": "task-c28dd757-3568-4ccb-a3b7-0ea05b81bccb",
"description": "重写导入的错误提示", "conclusion": "错误提示已改,附带单测", "createdAt": "2026-10-01T07:24:46.208Z", "completedAt": "2026-10-01T07:24:46.214Z" } ] }

- stack 里恰好只有一个 status: "active"(栈顶);读到的文件若有多个 active,会被就地修复
- 写入是原子的:先写 <file>.<pid>.<n>.tmp → fsync → rename 覆盖,读者只会看到完整的旧文档或新文档,崩溃也不会留半截 JSON
- 同一文件的读写用 per-file 异步锁(promise 链)串行化,不同文件互不阻塞
- 文件缺失按空栈处理(不算警告);读失败 / JSON 坏 / 版本不符 / 会话不符 / 帧结构坏 → 退化成空栈或丢弃坏帧,附一条 warnings 且从不抛异常

开发与验证

在克隆出来的仓库根目录里执行
pnpm typecheck # tsc strict(含 noUncheckedIndexedAccess / exactOptionalPropertyTypes)
pnpm build # tsc + 产物自检
pnpm test # 69 项:store 22 + tools 18 + config 12 + commands 13 + lifecycle 4 + demo
pnpm demo # 真实 ToolRuntime 里把三个工具与 /focus 跑一遍,打印 markdown 与落盘文件
pnpm probe # 无宿主装载:真 Cordis Context + 注册面断言 + 全生命周期实跑

| 脚本 | 覆盖 |
| scripts/test-store.mjs | 路径解析、文档结构、push/pop 语义、栈满/空栈、history 裁剪、200 并发压测无丢失无 .tmp 残留、坏 JSON/坏版本/坏帧/串会话降级、prune 阈值与边界、clearStack 幂等 |
| scripts/test-tools.mjs | 三个 defineTool 契约、参数校验(空串/错类型/缺参)、无 agent 报错、渲染 markdown、拒绝路径的渲染、history 裁剪对模型可见 |
| scripts/test-config.mjs | Config schema 默认值/越界拒绝/JSON schema、自定义 stateDir/maxStackDepth/historyLimit 端到端生效、手写坏文件不崩、statePruneDays 真的在加载时清理(子进程换 cwd 测) |
| scripts/test-commands.mjs | /focus 的三种输入与全部拒绝路径、命令与工具共用同一份状态、坏文件降级为 warning |
| scripts/test-lifecycle.mjs | 用真实 ToolRuntime + 真实子 fiber:加载→卸载→重载无残留、无 "already registered"、store 可重建 |
| scripts/demo.mjs | 可读演示:真 registry 上跑完整生命周期与 /focus,打印模型侧 markdown 与状态文件 |
| scripts/probe-load.mjs | 真实 Cordis Context 里 apply,断言三个工具 + /focus 都注册,并实跑一次完整生命周期 |

宿主侧的 loader 集成由工作区根目录的 ../loader-harness.mjs 验证(走真 @deepseek-ai/cordis-plugin-loader,四种模式:裸名/相对/坏锚点/非法配置)。

发布

0) 本地全绿(prepublishOnly 也会再跑一遍 build + test)
pnpm build && pnpm test

1) 登录(首次)
npm login

2) 预览包里到底装了什么,并让发布闸门跑一遍(不发布)
npm pack --dry-run

3) 发布bash
npm publish --access public # scope 包首次发布必须显式 --access public

要点:

- 发布闸门挂在 prepack 上(pack 与 publish 都会触发),由 scripts/release-check.mjs 用纯 Node 顺序跑 10 步:typecheck → build → check-build → probe → 五套测试 → demo。它不调用任何包管理器,所以不会被 PATH 上那个会写 crashpad 日志并异常退出的 pnpm 干扰;任一步失败会以自己的输出退出,npm publish 会如实报出真正的原因。
- 想单独跑闸门:node scripts/release-check.mjs;
- files 只包含 lib、两份 patch、README.md——src 与 scripts 不进包(lib 由闸门里的 build 步骤保证是最新的);
- 包名与 cordis.patch.yml 里的加载行 name 必须一致,否则 profile 装了也解析不到。工作区根目录的 rename-package.mjs 会一次性同步所有出现位置并校验(含 YAML 引号,以及"目录名必须保持裸名"的断言);
- 版本按 semver:修 bug 走 patch,新增工具/命令走 minor,改配置语义走 major;
- 首次发布后别人这样装:dsh plugin --profile <名称> add @ray1270/dsh-task-stack。

兼容性

- DSH:0.2.0-rc.2(开发与验证所用版本)
- 依赖:@deepseek-ai/cordis ^4.0.2、@deepseek-ai/dsh-tools、@deepseek-ai/dsh-commands、@deepseek-ai/schemastery ^3.18.2(均为 peerDependencies,由宿主在运行时提供)
- 不注册任何 prompt section、不做自动注入

项目结构

dsh-task-stack/
├── package.json # dsh.bundle 声明;peerDependencies 声明宿主依赖 ├── cordis.patch.yml # bundle 加载行(包名,随安装生效) ├── dev.patch.yml # 免安装 overlay(./lib/index.js,随 --patch 生效) ├── tsconfig.json ├── LICENSE # MIT ├── .github/workflows/ci.yml # 每次 push 跑同一个发布闸门 ├── src/ │ ├── index.ts # 插件入口:name / inject / Config / apply │ ├── tools.ts # 三个 defineTool:描述、schema、渲染、执行 │ ├── commands.ts # /focus 斜杠命令 │ ├── store.ts # 路径解析 + 原子写入 + per-file 锁 + 容错读取 │ └── types.ts # 共享类型与默认值 └── scripts/ ├── release-check.mjs # 发布闸门:10 步顺序执行(CI 与 prepack 共用) ├── test-store.mjs # 状态层 22 项 ├── test-tools.mjs # 工具层 18 项 ├── test-config.mjs # 配置与边界 12 项 ├── test-commands.mjs # /focus 13 项 ├── test-lifecycle.mjs # 真实 ToolRuntime 卸载/重载 4 项 ├── probe-load.mjs # 无宿主装载 + 全生命周期实跑 ├── demo.mjs # 可读演示 ├── check-build.mjs # 构建产物自检 └── publish-release.mjs # tag → 发布(可本地干跑)

发布流程

推一个 v tag 就会自动发布到 npm 并建 GitHub Release(.github/workflows/release.yml): powershell

1) 升版本(改 package.json 的 version),确认闸门全绿

pnpm release-check

2) 提交并推 tag —— 这一步触发自动化,之后无需手工操作

git commit -am "chore: release 0.3.0"
git tag -a v0.3.0 -m "v0.3.0"
git push && git push origin v0.3.0

自动化做四件事:装依赖 → node scripts/publish-release.mjs --tag $GITHUB_REF_NAME(内部跑 npm publish,其 prepack 就是那套发布闸门)→ 产出 tarball → 建 Release 并附上该 tarball。

需要一次配置:在仓库 Settings → Secrets and variables → Actions 加一个 NPM_TOKEN(npm 的 Automation 或 Publish token,且必须勾选 Bypass 2FA;fine-grained token 目前还不能发布)。

三种 ref 的三种行为

脚本按 ref 判断意图,因为 workflow_dispatch 拿到的 GITHUB_REF_NAME 是分支名而不是 tag:

| 触发 | ref | 行为 |
| 推 v0.3.0 tag | v0.3.0 | 校验 tag == package.json 版本 → 发布 |
| Actions 页面手动 Run workflow | main | 校验模式:报告凭据状态、不发布、成功结束 |
| 普通分支 push | 分支名 | 同上(CI 工作流才是分支上的主验证) |

推真实 tag 但与 package.json 版本不符时仍然直接失败——用错版本号发布会污染 npm,那是事故而不是可以抹平的小事。

两个刻意的设计,都是被事故逼出来的

- tag 必须与 package.json 的版本一致,否则失败。
- 已发布过的版本视为成功,而不是报错。因为「registry 已记录版本、客户端却收到失败响应」会让这个版本号永久不可用(0.2.0 就是这么丢的:重试同一个版本号只会得到 409)——所以重试必须能收敛。

可以本地干跑看 CI 会做什么: powershell
node scripts/publish-release.mjs --dry-run # 只决策,不发布
node scripts/publish-release.mjs --tag main # 校验模式(等同手动触发)

许可

MIT
预览分享卡片长什么样 ↓Ray1270/dsh-task-stack
分享 · DeepSeek Harness Hub
Ray1270/dsh-task-stack

为 DeepSeek Harness 智能体提供按会话持久化的任务栈——focus_task / focus_complete / read_focus 加 /focus,零提示词注入

这是你的仓库?认领条目

回到顶部