# 任务看板(dsh-task-board) [English](README.en.md) | 中文 [![npm](https://img.shields.io/npm/v/@firetruck666/dsh-task-board)](https://www.npmjs.com/package/@firetruck666/dsh-task-board) [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE) [![Node](https://img.shields.io/badge/Node-%5E22.19.0%20%7C%7C%20%3E%3D24.0.0-339933)](README.md#环境要求) DeepSeek Harness 的任务看板插件。它在 Web 界面的侧边栏加一个「任务看板」入口,用五列看板管理任务;任务交给 DSH 自己的会话真实执行,状态自动回写到卡片上。 插件不修改 DSH 源码,卸载后界面恢复原状。看板数据保存在 DSH 主进程(host)一侧,电脑和手机打开同一个部署看到的是同一块板,改动经 SSE 实时同步;窄屏自动进入紧凑布局。 ## 目录 - [环境要求](#环境要求) - [安装](#安装) - [更新](#更新) - [主要能力](#主要能力) - [数据保存在哪里](#数据保存在哪里) - [从源码构建](#从源码构建) - [贡献指南](#贡献指南) - [常见问题](#常见问题) - [命名约定](#命名约定) - [许可证](#许可证) ## 环境要求 - DeepSeek Harness `0.1.7-rc.2` 或更高。`0.1.7-rc.2` 是**已验证的最低版本**,本插件当前就运行在这个版本上。更高的版本会跟随更新,但没有逐一验证过,所以不保证。加载失败时先按「常见问题」里的说明处理。 - Node.js `^22.19.0` 或 `>= 24.0.0` - pnpm 10 或更高 - DSH 的 `web` profile 本插件只运行在 Web 图形界面上。 ## 安装 三种方式,选一种。三条命令都在你自己的终端里执行。 ### 从 npm 安装 ```sh dsh plugin --profile web add @firetruck666/dsh-task-board ``` 适合日常使用。拿到的是最近一次正式发布的版本。 ### 从 GitHub 安装 ```sh dsh plugin --profile web add github:FiretrUCK666/dsh-task-board ``` 拿到的是仓库 `main` 分支的最新代码。它和 npm 装出来的一样是可直接使用的安装包,不是开发环境(没有测试、没有构建工具,改不了代码)。适合想第一时间用上还没发版改动的人;稳定性取决于仓库当时的状态。 ### 本地开发安装 把仓库下载到本地后,在这个仓库目录里执行: ```sh pnpm install pnpm build dsh plugin --profile web add . ``` 这条路径用于改代码。详见下面的「从源码构建」。 本地安装是指向工作目录的链接(不是复制),所以挂载一次即可:改完代码只需 `pnpm build` 再重启 `dsh web`,不必重跑上面的安装命令。改 client 半区刷新页面即可,改 host 半区才需要重启——见「常见问题」里的「改了代码没生效」。 ### 三种方式的区别 前两种是安装:拿到的都是可以直接使用的包,只包含发布清单里的文件(运行代码、源码、挂载声明、说明文档)。第三种是开发:拿到的是完整仓库,能改代码、能运行测试。 | | 从 npm | 从 GitHub | 本地 | | --- | --- | --- | --- | | 用途 | 安装使用 | 安装使用 | 开发 | | 拿到什么 | 最近一次发布版 | `main` 最新代码 | 你本地改动 | | 版本特点 | 稳定 | 最新,可能不稳定 | 你自己决定 | | 更新依据 | npm 上的版本号 | 仓库最新提交 | 安装的即本地目录,无需更新 | 从 npm 和从 GitHub 装出来的文件基本一致,差异只在版本新旧。想用还没发版的改动就从 GitHub 装,想要稳定版本就从 npm 装。 ### 安装之后 停掉正在运行的 `dsh web`,重新启动它。只刷新页面不够——插件的 host 半区在服务端进程里加载,必须重启才生效。重启后刷新页面,侧边栏就会出现「任务看板」入口。 入口和 DSH 自带的「插件」面板并排:点它,看板占用中间主区域;再点一次同一个入口、或点侧边栏里的任意会话,即可回到对话。 这个插件**只有一个开关:开或关**。位置有两处,做的是同一件事:侧边栏「插件」→ 往下拉到「已安装」,那一行右边的开关;或点开「任务看板」进它自己的页面,右上角那个开关。**关掉 = 插件不加载**(侧边栏入口、看板、后台接口一起停),**打开 = 恢复**。 ### 不需要手动配置任何东西 装完就能用,不用改配置文件、不用手写任何声明。而且**不要去改 `cordis.patch.yml`**——插件那一行住在插件包里,安装命令只负责把这个包登记进 profile,两者一配合就装配好了;手工再补一行会造成同一个插件出现两条。 | 环节 | 谁做的 | | --- | --- | | 把插件登记进本机 profile | 安装命令(往 `dsh.profile.bundles` 加一个包名) | | 插件那一行(id、包名) | 插件包里的 `cordis.patch.yml`,由 `package.json` 的 `dsh.bundle.patch` 指向,装配时自动生效 | | 界面入口、看板舞台 | 插件自己注册到 DSH 的官方扩展点,启动时自动完成 | | 数据存哪 | 自动使用 `~/.dsh/storages/dsh_task_board/` 目录,第一次运行时创建 | | 插件显示的名字、说明、图标 | 包里的 `locale/` 与 `icon.svg`,DSH 直接读取 | | 那个开关的位置 | 插件管理页的开关写 profile 里这一行的 `disabled`(关掉时才会写上) | 唯一必须你做的一步是**重启 `dsh web`**(host 半区在服务端进程里,不重启不会加载)。三种安装方式(npm、GitHub、本地)在这件事上完全一样。 这个插件没有设置项,也不需要设置面板:看板的行为(任务、定时、巡航、规则)全在看板界面上直接编辑,而开与关就是上面那个开关。 卸载: ```sh dsh plugin --profile web remove @firetruck666/dsh-task-board ``` 同样需要重启 `dsh web`。卸载不会删除你的任务数据。 ## 更新 最直接:看板 Header 右侧工具栏里有常驻的「检查更新」按钮,点一下即对比当前版本与最新版。有新版时它会给出你这种安装方式对应的更新命令,复制后在自己的终端里执行。 装了插件市场的话,在「已安装」标签页里点「更新」。也可以直接重新执行一次安装命令: ```sh dsh plugin --profile web add @firetruck666/dsh-task-board@latest ``` 或者: ```sh dsh plugin --profile web add github:FiretrUCK666/dsh-task-board ``` 更新后同样要重启 `dsh web`。 ## 主要能力 **看板** —— 五列:待规划 / 待办 / 进行中 / 待审核 / 已完成。卡片是摘要,会话是内容:点开卡片 消掉「新 N」,而「待你决断」要你打开**那个会话的评论区**或点「通过 / 打回」才消。任务经 DSH 会话真实执行,跑完自然落「待审核」。换栏与新完成自动置顶,你手动拖过的位置不会被顶掉。 **任务清单** —— 侧栏面板列表里紧跟「任务看板」的第二行,点开看清单、再点一下回会话,与看板完全 对称:铺满整页、侧栏收起时成图标、手机自适应。清单是**第二份文档**而不是看板的一个视图,一条事项 不必变成一张卡;挂上了卡就读那张卡的实时状态。每条有短编号 `#12`(由文档发出、永远不可写)、 勾选步骤、四档优先级、标签,以及**各自独立**的三个时间(最早开始 / 截止 / 硬期限)。 它内部**分页**,每页回答一个不同的问题,不是一片数据的几种画法: | 页 | 回答 | 页轨上的数 | | --- | --- | --- | | **收件** | 刚记下、还没给任何结构的那些。给了优先级、日期、标签或挂上卡,它就自己离开这页 | 有几条 | | **清单** | 你的全部条目,**一张表**:勾选 / 状态 / 标题 / 优先级 / 截止 / 标签。表头可点,点了就按那一列排 | 有几条 || **日程** | 有时间的那部分,按天排;**没到最早开始的不排进来**,另有一个「还没到开始时间」的折叠区并写清为什么 | 有几条 | **三页永远在轨上,空的那页写 0**——「被问到而答案是零」和「这个问题根本不存在」是两件事。 清单表里**状态是一列**(**待办 / 进行中 / 受阻 / 已完成**),所以一个状态只在一处说一次。 标签、停滞、归档、筛选结果这些「换个看法」不占轨,点进去才出现。截止那一列按轻重说不同的话: **超期**是红色,**落后**与「就是今天」是墨色深浅,而**只有「硬期限」会让一行变成真正的逾期**—— 一个日期被跳过和一个承诺被打破是两件事,界面分开说。 **清单页是唯一的工作面**,因为整理、挑拣、批量都是几十条以上才出现的问题。它顶部是**三张 统计卡**(**落后 / 卡住 / 没日期**,点一下就是把清单按那件事筛开——它们答的是「你马上要管 哪几件」,不是「有几个状态」),下面一条**筛选**(**状态 / 日期 / 标签**;优先级不在这里, 因为它答的是「给光标这一行设成什么」而不是「看哪些」,所以它在 `⌘K` 里),再下面是那张表。 **排序六档**(顺序 / 最早开始 / 截止 / 硬期限 / 优先级 / 标题)。 **多选批量**(状态、优先级、日期、问 AI、删除)都在这一页;勾上几行,底部出现一条批量条。 **收件页刻意不给批量**——刚敲完一行字的人眼睛还在输入框上,而批量回答的是另一个问题。 **行高不是一个开关。** 它是定值:行高由触控目标尺寸与字阶定死,因为它是**排版与无障碍共同 定下的事实**,不是读者的一个偏好。密度才是偏好,而「一枚只挪内部间距、挪不动行高的开关」 比没有更贵——读者按下去只看到「紧凑」两个字换了个人说,于是学会不再信它。 **删除是一条有去处的路**:删除(一次按压 + 一次撤销,没有确认弹窗)→ **找回** → **彻底删除**。 前三步的删除把墓碑连同那一行本身留下,所以 30 天内都找得回(这和删卡片不同);**彻底删除 拿掉的是正文,墓碑的戳留着**——因为那枚戳是「一台睡了一周的设备拿着旧副本把行塞回来」的 唯一挡板,删掉它就不是彻底删除,是「彻底删除还能被撤销」。清单页那一行把期限直接写出来, 旁边的入口打开归档抽屉,里面每一行都有找回和彻底删除。快记框支持内联写法:`#标签`、`!1`–`!4` 优先级、`@今天` / `@硬 9/30` 期限、行首 `- [ ]` 生成步骤;**边打边把识别结果显示成标签, 认错了点一下就还原成普通文字**。模型能做的与人在界面上做的是同一套,包括勾一步、把一条变成 看板卡片、恢复删掉的一条。 **和 AI 对话驱动** —— 两个斜杠命令(`/task`、`/task-continue`)与三个工具。命令把**你写的那句 话**交给当前会话的模型,它读得到当下全部上下文,所以「把刚才说的三件事记下来」它自己就知道是哪 三件;该记几条、要不要改旧的、要不要顺手建卡,**由它判断,本插件不写死流程**。能力清单由模型 **按需查**而不是塞进提示词(过期的清单比没有清单更糟)。AI 能做的与人在界面上做的是同一套实现。 界面上锁死的动作 AI 也做不了,且会说明为什么。 **多端同步** —— 真相在 host 的 `~/.dsh/storages/dsh_task_board/`,人可读可备份。浏览器是乐观 副本:点开即进,断线照常可用,恢复后自动追平。并发编辑按记录合并、不依赖设备时钟。定时与巡航 这类时间驱动的行为**同一时刻只由一个界面执行**(引擎席位),另一台切到前台立刻接管。 **自动化** —— 任务级(cron 定时 / 完成后接续)与会话级(给某个会话按时间表或完成后发指令), 两半独立。另有批量自动巡航(带并发上限与定时窗口)与「并行数」闸门。 **界面** —— 以面板自身宽度自适应,与侧栏开合无关。卡片可拖列、可多选批量。详情页可存为模板, 运行配置可存预设并设默认。通知中心聚合等你处理的会话与未读待审核;动态页按日分组。 触屏没有悬停,所以所有必要说明都做成可点可达。 ## 数据保存在哪里 - 看板真相:`~/.dsh/storages/dsh_task_board/` 目录。`documents/` 下**每种数据一个文件**:`board.json` 是任务台账、巡航、定时预设、运行配置预设与删除墓碑;`items.json` 是任务清单。人可读、原子写入,可以直接整目录备份;删除这个目录等于清空看板与清单。 - 浏览器 localStorage 保存离线镜像和本地状态。两份**承载数据**的镜像是 `dsh.taskBoard.v1`(看板)与 `dsh.taskBoard.items.v1`(任务清单)—— **两份都要备,只备一份等于清单那份丢了**。其余的键 (`cruise` / `presets` / `runPresets` / `templates` / `drafts` / `preSync`)是这个浏览器自己的状态或 一次性备份,不是另一份真相。**键名以 `src/client/` 里实际写下的为准**;这里列的是**哪些值得你 操心**,不是全部键的清单。 - 第一次连接时如果本地数据与 host 有分歧,本地副本备份到 `dsh.taskBoard.preSync.v1`,host 为准。 - host 没有挂载存储后端时,看板自动退回纯 localStorage 模式。 ## 从源码构建 前置:Node.js 22 或 24、pnpm、能访问官方 npm 源(类型和运行时 API 全部来自 `@deepseek-ai/*`,不需要 DSH 源码 checkout)。 ```sh git clone https://github.com/FiretrUCK666/dsh-task-board.git cd dsh-task-board pnpm install pnpm build # 产出 lib/index.js 和 lib/client.js pnpm typecheck # 类型检查 pnpm test # 单元与契约测试 pnpm verify # 独立插件静态门禁 ``` `lib/` 是构建产物,但它随仓库一起提交。原因是从 GitHub 或 npm 安装时只会复制文件,不会执行构建,仓库里没有 `lib/` 的话装完就启动不了。改完源码要重新构建,并把 `lib/` 和源码放在同一次提交里(CI 会检查两者是否一致)。 仓库根目录的 `AGENTS.md` 记录了项目的约定、架构索引与发布流程,是 AI 助手在这个仓库里工作时用的依据。 ## 贡献指南 欢迎提 Issue 与 Pull Request。动手前先读 [CONTRIBUTING.md](CONTRIBUTING.md):开发环境、提交 PR 前要跑的检查、硬性规范都在里面。报告加载失败类问题时,附上三个信息:DeepSeek Harness 版本、本插件版本、报错原文。 ## 常见问题 **插件页面上的四个开关是什么。** 侧边栏「插件」→ 已安装 → 点「任务看板」→「包含的组件」列出四行,每行一个开关: | 那一行 | 关掉之后 | | --- | --- | | 任务看板 | 侧栏面板列表里没有「任务看板」,看板面板不再出现 | | 任务清单 | 侧栏面板列表里没有「任务清单」,清单面板不再出现 | | **AI 接口** | **所有会话的 AI 都看不到本插件的工具、斜杠命令与系统提示**——它们从未被注册,不是被拒绝 | | 任务看板(插件本体) | 整个插件不加载,上面三行随之无意义 | 「AI 接口」这一行是**装配层的开关**:关掉它,插件的那几个模块根本不加载,所以没有任何一次运行 时判断可以漏掉。清单与看板各自要开关,是因为它们是两个界面;而一个 npm 包只有一份浏览器产物, 所以这两行由宿主侧的模块宣告,浏览器开机时读一次。 **看板上的状态词都什么意思。** 它们读的是**同一份推导**,所以同一张卡在任何一处都是同一个答案: | 你看到的词 | 它在说什么 | 什么时候消失 | | --- | --- | --- | | **待你决断**(失败时写「失败待决断」) | 卡在「待审核」列,有跑完但那个**会话**你还没看过的结果 | 打开那个会话的评论区,或点「通过 / 打回」 | | **等你处理 N 项** | N 个会话正挂起等你回答(审批 / 计划确认 / 提问) | 你在那个会话里回答完 | | **待审核 M 张** | M 张卡在「待审核」列且那个会话你还没看过 | 同「待你决断」 | | **新 N / 新留言** | 这张卡有还没看过的**新内容** | 点开卡片即消失 | | **待处理 N** | N 个会话在等你(N > 1 时) | 逐个回答完 | | **N 次执行** | 这张卡跑过几轮(历史,不是催你) | 不会自己消失 | | **失败 · 已暂停** | 自动化因上次失败而暂停 | 在详情里重新启用 | **「待你决断」和「新 N」是两件事**:「新」是「这张卡你没打开过」,「待你决断」是「那个会话你没 看过、也还没裁决」。**取消不算「待你决断」**——取消是中止,它只出现在评论线程和动态里。 **装完刷新页面看不到入口。** host 半区在服务端进程里加载,必须重启 `dsh web`,只刷新页面不够。 **从旧版本升级上来,入口和设置的位置变了。** 新版把看板接进了 DSH 的官方界面机制(这也是它能在界面改版后继续正常显示的原因): 入口从「侧边栏底部的一行」变成「侧边栏的面板图标,与 DSH 自带的『插件』面板并排」, 设置项从「设置 → 任务看板」搬到**插件自己的页面**(侧边栏「插件」→ 已安装 → 点「任务看板」)—— 开关和插件本体放在同一页。功能没有减少,任务数据也没有变化。 如果你看到的是旧位置,说明这一端还跑着旧的前端资源——刷新页面即可。 **升级 DeepSeek Harness 之后插件加载失败(页面提示 Failed to load plugins)。** DeepSeek Harness 的内部接口会随版本变化,本插件需要跟着改。先做这两步: 1. 把本插件更新到最新版,然后重启 `dsh web`: ```sh dsh plugin --profile web add @firetruck666/dsh-task-board@latest ``` 2. 仍然失败,说明本插件还没跟上你用的那个 DSH 版本。请到 [Issues](https://github.com/FiretrUCK666/dsh-task-board/issues) 提交,附上三个信息:你的 DeepSeek Harness 版本、本插件版本(**在插件页的「已安装」列表里看——不是设置页,本插件没有设置项**)、页面上那段报错原文。有这三样就能直接定位。 **看板能打开,但多设备不同步、任务清单也一直是空的。** 这是 host 半区没加载成功的表现,也是升级 DSH 之后最容易被忽略的一种:看板本身仍在页面上正常工作,但数据只留在这一个浏览器里,另一台设备看到的不是同一块板,定时与巡航也不会真正触发。任务清单在这种情况下会明说「暂时读不到 host 上的清单,正在用本机的副本」,不会假装自己一条都没有。处理方式与上一条相同——更新插件、重启 `dsh web`。 **打开右侧栏之后看板变了样子。** 这是正常的响应式行为,不是故障。DSH 的右侧栏默认占视口约 45%、最少 300px,中栏硬保 400px,所以打开它之后**中间那块板本身变窄了**:看板的响应式看的是**自己盒子的宽度**(不是窗口宽度),板盒一旦窄过它的紧凑档阈值,五列就会变成横向滑动的列轨、并出现五等分的列导航标签。把右侧栏收回去就恢复。这条规则本意就是「板盒多宽就摆多宽」,侧栏开合、分屏、手机上都成立。 **改了代码没生效。** 改 host 半区(`src/index.ts`、`src/host/`)需要重启 `dsh web`;改 client 半区刷新页面即可。两种情况都要先 `pnpm build`。 **手机上打开位置不对,或反复开合侧栏会跳位。** 这是修复过的问题。如果仍然出现,先确认服务端进程是重启过的新版本:插件在板头会显示「服务端未重启 · 点此了解」,点开可以看到当前连接的服务端进程的启动时间。如果时间明显是旧的,说明这个地址连到了另一个没重启的实例。 **两端同时开着会不会重复执行定时任务。** 不会。定时、巡航、接续这类行为由 host 租约仲裁,同一时刻只有一个界面在执行,另一台设备切到前台后会接管并补上期间漏掉的状态。 **任务数据在哪,换电脑会丢吗。** 在 `~/.dsh/storages/dsh_task_board/` 目录。换电脑时把整个目录拷过去即可;浏览器里的 localStorage 只是离线镜像。 **找不到任务清单的入口。** 清单是**侧栏面板列表里紧跟「任务看板」的第二行**——把左栏展开就能看到两行图标,点第二行打开清单, 再点一下回到会话。侧栏收成一条窄图标带时,两行挨着排在一起。 如果那一行**根本没有**:多半是这一端还在跑旧的前端资源,**刷新页面**即可;还不行就去看插件页上 「任务清单」那个开关是不是被关了(见上面「四个开关」)。清单关掉不影响看板。 **切到看板面板时清单不见了。** 那不是坏了,是**主舞台一次只显示一个面板**——它本来就是这么设计的,不是两个能同屏的东西。 想回清单,点侧栏里清单那一行;想回对话,点「返回对话」或再点一次当前那一行。 **`dsh plugin` 报错找不到 pnpm。** 先安装 pnpm(`npm install -g pnpm`),再重新执行安装命令。 ## 命名约定 | 用途 | 值 | | --- | --- | | 插件 id | `dsh-task-board` | | npm 包名 | `@firetruck666/dsh-task-board` | | 权限预设路由 | `/api/dsh-task-board/permissions` | | 看板数据路由 | `/api/dsh-task-board/board`(含 `/items`、`/surfaces`、`/ask`、`/lease`、`/command`、`/events`) | | 其余 host 路由 | `/api/dsh-task-board/session-state`、`/update`、`/client-report` | | host 存储单元 | `dsh_task_board`(`documents/` 下每种数据一个文件) | | 看板舞台 slot | `main`(`key: dsh-task-board`) | | 任务清单舞台 slot | `main`(`key: dsh-task-board-items`,与看板同一个座位、不同键) | | 侧栏入口 slot | `sidebar.panellist`(`id` 与各自的 `main` 键一致) | | localStorage 键 | `dsh.taskBoard.v1` 等 | 插件 id 和包名是两件事:id 决定加载器行、浏览器资源路径、路由、存储单元和上面那几个扩展点;包名只是 pnpm 安装时的标识。包名带作用域不会改变 id。 本插件没有设置项,所以也没有设置路由和设置面板:插件市场里那条开关写的是 profile 行的 `disabled`(关掉时才写上),插件本身不声明任何设置字段。 ## 许可证 MIT,见 LICENSE 文件。