nx-kv 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +31 -21
  3. package/assets/groups.json +12 -4
  4. package/assets/kv-localget/SKILL.md +69 -0
  5. package/assets/kv-submit/SKILL.md +81 -0
  6. package/assets/kv-submit/references/context-layering.md +121 -0
  7. package/assets/kv-submit/references/submit-workflow.md +109 -0
  8. package/assets/kv-waittask/SKILL.md +133 -0
  9. package/assets/kv-waittask/references/kv-waittask-workflow.md +157 -0
  10. package/assets/nx-kv/references/agent-workflow.md +3 -3
  11. package/package.json +2 -2
  12. package/src/index.js +1 -1
  13. package/src/modules/{sessiontask → localtask}/index.js +68 -45
  14. package/src/modules/localtask/panel-guard.js +155 -0
  15. package/src/modules/{sessiontask → localtask}/popup.js +5 -5
  16. package/src/modules/{sessiontask → localtask}/service.js +167 -12
  17. package/src/modules/{sessiontask → localtask}/view.jsx +56 -16
  18. package/src/runtime/cli.js +23 -2
  19. package/src/runtime/registry.js +2 -2
  20. package/src/runtime/server.js +17 -1
  21. package/src/web/frontend/App.jsx +19 -0
  22. package/src/web/frontend/registry.js +1 -1
  23. package/src/web/public/assets/{CliHints-BcGuVv3q.js → CliHints-B0Qwrf-6.js} +1 -1
  24. package/src/web/public/assets/{index-BC4hXOgL.js → index-D0ldg3Y8.js} +2 -2
  25. package/src/web/public/assets/{view-hrrRARfz.js → view-B9RikVKT.js} +1 -1
  26. package/src/web/public/assets/{view-Xx2TV723.js → view-BIjk8I3i.js} +1 -1
  27. package/src/web/public/assets/view-CU5VSo_g.js +1 -0
  28. package/src/web/public/assets/{view-Bq7gBnSO.js → view-D1a7SUrR.js} +1 -1
  29. package/src/web/public/index.html +1 -1
  30. package/assets/kv-sessiontask/SKILL.md +0 -112
  31. package/assets/kv-sessiontask/references/sessiontask-workflow.md +0 -143
  32. package/src/web/public/assets/view-DpLqgnYO.js +0 -1
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: kv-waittask
3
+ description: 在一个对话里通过工具调用实时接收任务(本机人机接力),替代在原始输入框打字——每完成一步、提交结果后,都必须把 nx-kv localtask wait --summary "<当前现状总结>" 作为该步的结束动作强制调用,领取新任务(循环不自行终止,只有 timeout 才能结束本轮);命令阻塞并自动打开浏览器弹窗,用户在面板看清现状、针对该现状回填后解除阻塞、把新任务作为工具结果返回,agent 继续;队列按工作目录划分,同目录的任务(含别人委托排队的)按序领取。触发词:本地任务、下一条任务、继续给任务、连续提交任务、补充任务、回填任务、现状总结、在网页/弹窗里给任务、阻塞等待、挂起等用户、人机接力、等用户喂任务、每步结束领新任务、localtask wait、localtask(旧称 sessiontask)、kv-waittask。凡是「agent 不结束本轮、靠一个阻塞工具调用等浏览器里继续喂任务」的场景,都走本 skill。反例:要「主动领取排给本目录的委托任务」用 kv-localget;要「把需求打包提交给别的 agent」用 kv-submit;要操作远程 KV 清单(todo)用 nx-kv。
4
+ ---
5
+
6
+ # kv-waittask — 对话内本地任务接力(等待侧)
7
+
8
+ 本质是一个强制循环:**每完成一步 → 提交结果 → 必须调用 wait 领取新任务**。
9
+ 循环不自行终止——**每一步的结束动作都只能是 `nx-kv localtask wait`**;「做完
10
+ 就收尾停住」是违规,唯一合法的停止条件是 wait 返回 `timeout`。
11
+
12
+ 具体形态:agent 每做完一步、把结果提交后,调用一个**阻塞式**工具
13
+ `nx-kv localtask wait`,并带上一句**当前现状总结**;它自动打开浏览器弹窗。
14
+ 用户在面板/弹窗一眼看到现状,针对该现状回填,阻塞解除、新任务作为工具结果返回,
15
+ agent 接着干;做完再 wait——如此循环。别人通过 kv-submit / 面板 / CLI 排进
16
+ **本目录队列**的任务,wait 也会按序取走。
17
+
18
+ > 纯本机协调:弹窗由 wait 进程临时起的 http 服务器直出,数据存本机文件
19
+ > (`~/.nx-kv/localtasks.json`),**不需要登录 KV 后端**。
20
+
21
+ ## 四条硬规则(最重要)
22
+
23
+ 1. **每一步的结束都必须是 wait——循环强制,不许做完就停。** 每完成一步(结果
24
+ 已通过正常输出提交),立即调用 wait 领取新任务;队列里有排队的任务就直接
25
+ 领走继续干,没有就阻塞等用户回填。把 wait 安排在本轮所有分析与工具调用
26
+ 之后,调用之后不要再安排任何别的工具、输出或推理——它会阻塞,直到拿到
27
+ 新任务或超时。**只有 timeout(第 4 条)能结束这个循环。**
28
+ 2. **这次工具调用的超时设为 `300000` ms(5 分钟)。** CLI 默认阻塞 180 秒、
29
+ 上限 600 秒;5 分钟覆盖默认值并留余量。若显式传了更长的 `--timeout`,
30
+ 工具超时要同步放大到比它更大。
31
+ 3. **wait 必须带 `--summary "<当前现状总结>"`。** 总结写**当前做到哪、卡在哪、
32
+ 等用户决定什么**,让用户不读对话也能在面板快速了解现状;它展示在弹窗/面板
33
+ 顶部,并作为「本轮完成总结」记录在被取走的任务上。它**只是状态展示**——
34
+ 措辞不影响领取顺序,不必刻意保持逐字一致。
35
+ 4. **返回 `status:"timeout"` 时直接收尾。** 这是循环唯一合法的出口:用户在
36
+ 阻塞时限内没回填,就不要再追问、不要再次调用 wait,把当前结论交代清楚、
37
+ 结束本轮即可。
38
+
39
+ ## 队列模型(一句话版)
40
+
41
+ **工作目录即主题,主题拥有一个任务队列。** wait 不传 `--cwd` 时以进程当前目录
42
+ (目录名)定位主题;同一目录的所有任务共用一个队列,wait 总是取走其中**最早的
43
+ 待领取任务**。队列里有任务时 wait 立即返回(不开窗、不阻塞);队列为空才开窗
44
+ 阻塞。不同目录 → 不同主题 → 互不串。
45
+
46
+ ## 核心命令
47
+
48
+ ```bash
49
+ # 阻塞等待本目录的下一条任务——每轮最后一步,强制(务必加 --json)
50
+ nx-kv localtask wait --summary "<当前现状总结>" --json
51
+ ```
52
+
53
+ 返回形状:
54
+
55
+ | 返回 | 含义 | agent 怎么做 |
56
+ | --- | --- | --- |
57
+ | `{status:"ok", queued:true, task}` | 队列里已有**排队任务**(含 kv-submit 委托的) | 直接执行 task |
58
+ | `{status:"ok", delivered:true, task}` | 用户在弹窗/面板**实时回填** | 执行 task |
59
+ | `{status:"timeout", cwd, summary, timeoutMs}` | 阻塞时限内未收到回填 | 收尾、结束本轮 |
60
+
61
+ 成功结果里带 `cwd` 与 `summary`;`task` 形如
62
+ `{id, text, status, createdAt, consumedAt, roundSummary}`,**任务内容在 `task.text`**。
63
+
64
+ ## 一次循环长什么样
65
+
66
+ ```
67
+ agent:领到 task.text → 干活 → 提交阶段结果
68
+ → 必须立即:nx-kv localtask wait --summary "已完成登录,等待选择工作空间" --json
69
+ (队列空 → 阻塞,弹窗/面板顶部显示这句现状;队列里有任务 → 直接领走继续)
70
+ 用户:看清现状 → 回填「选择工作空间 A」→ 提交
71
+ agent:工具结果拿到 task.text → 干活 → 提交结果
72
+ → 必须立即再 wait……(循环)
73
+ 若阻塞时限内没回填:wait 返回 timeout → agent 收尾,结束本轮(循环唯一出口)
74
+ ```
75
+
76
+ **每一行的「必须立即」不能省、不能晚、不能换成别的话:** 不调 wait 等于
77
+ 退出循环,队列里排着的任务(别人提交的、用户排队喂的)就没人领。
78
+
79
+ ## 弹窗与常驻面板
80
+
81
+ - 弹窗顶部「当前现状」显示 wait 传入的现状总结;大输入框 **Enter 提交、
82
+ Shift+Enter 换行**;状态栏倒计时归零即本轮结束;下方是该主题的任务表
83
+ (待领取 / 已领取,可删除)。
84
+ - 常驻面板(`nx-kv serve`)的「本地任务」页按主题展示所有目录的队列与现状,
85
+ 可直接点选回填;正在阻塞的 wait 通过轮询(约每 0.5 秒)取走,不依赖弹窗本身。
86
+ - 弹窗没自动打开时,终端输出里有本次地址(`http://127.0.0.1:<端口>/`),手动
87
+ 打开即可;该窗口只在本次 wait 期间有效,wait 结束即关闭。
88
+
89
+ ## 配套命令(队列管理,agent 一般不直接用)
90
+
91
+ ```bash
92
+ nx-kv localtask list # 主题总览(各目录、现状、待领取数)
93
+ nx-kv localtask add "<回填内容>" --cwd "<目录>" # 给某目录的队列排任务
94
+ nx-kv localtask get --id <n> [--cwd "<目录>"] # 查看单条
95
+ nx-kv localtask update --id <n> --text "<新内容>" # 编辑任务内容
96
+ nx-kv localtask remove --id <n> [--cwd "<目录>"] # 删除单条任务
97
+ nx-kv localtask reorder --id <n> --before-id <m> # 调整待领取优先级(或 --after-id)
98
+ nx-kv localtask topic remove --cwd "<工作目录>" # 删除整个主题(等待中需 --force)
99
+ nx-kv localtask topic remove-many --keys "<key,key>" # 批量删除主题(或 --cwds / --all)
100
+ ```
101
+
102
+ > 删除主题:面板「本地任务」页每张主题卡有「删除主题」,勾选后可「批量删除」。
103
+ > 等待中的主题默认不删(避免让阻塞的 agent 干等),确认要删会二次提示并以
104
+ > `--force` 强删、agent 随后超时收尾。
105
+
106
+ ## 先判断你要做哪件事
107
+
108
+ | 你要做的事 | 用哪个 |
109
+ | --- | --- |
110
+ | 干完活,**等用户在浏览器里继续喂下一条** | 本 skill(wait) |
111
+ | 主动**领取排给本目录的委托任务**(他人 / 其它 agent 提交的) | kv-localget |
112
+ | 把当前对话的需求**打包提交给别的 agent** | kv-submit |
113
+ | 操作远程 KV 后端上的清单(todo,需登录) | nx-kv |
114
+
115
+ 要完整时序、返回字段、排队语义与避坑清单,读 [[kv-waittask-workflow]](按需加载,
116
+ 只是查命令不必读)。
117
+
118
+ ## 与其它 skill 的分工
119
+
120
+ - **kv-waittask(本 skill)**:等待侧——阻塞等浏览器回填,人机接力的「等」。
121
+ - **kv-localget**:领取侧——主动把排给本目录的任务领走执行,附队列管理。
122
+ - **kv-submit**:提交侧——总结上下文、提炼用户需求,委托给别的目录的 agent。
123
+ - **nx-kv**:远程 KV 后端的 todo 清单(需登录、按 groupId 隔离)。
124
+
125
+ 三者共享同一份本机任务存储与按目录划分的队列:kv-submit 提交的、面板回填的、
126
+ CLI add 排队的,wait / kv-localget 都能取走。可与 nx-kv 同时安装,各管一段。
127
+
128
+ ## 安装
129
+
130
+ ```bash
131
+ nx-kv skill install kv-waittask # 装到 ~/.claude/skills/kv-waittask
132
+ nx-kv skill list # 列出 nx-kv / kv-waittask / kv-submit / kv-localget
133
+ ```
@@ -0,0 +1,157 @@
1
+ # kv-waittask-workflow — 阻塞等待用户回填任务的完整 SOP
2
+
3
+ > 归属 [[kv-waittask]]。当你要在一个对话里**不结束本轮**、靠一个阻塞工具
4
+ > 调用等浏览器里继续回填任务时,按本 SOP 走。
5
+ >
6
+ > **循环是强制的:每完成一步、提交结果后,结束动作只能是 wait——领取新任务。
7
+ > 循环不自行终止,唯一合法的出口是 wait 返回 `timeout`。**
8
+
9
+ ## 一、端到端时序
10
+
11
+ ```
12
+ 1. agent 正常干活(分析、读写文件、调用其它工具)。
13
+ 2. 当前任务做完、阶段结果提交后【必须】:
14
+ → 把「nx-kv localtask wait --summary "<当前现状总结>" --json」
15
+ 作为本轮【最后一个动作】调用,领取新任务;工具调用超时设为 300000 ms。
16
+ 不许做完就收尾停住——「结束动作不是 wait」即为违规。
17
+ 3. wait 的行为:
18
+ a. 若本目录主题队列已有待领取任务(含 kv-submit 委托排队的)→ 立即取走、
19
+ 不开窗、不阻塞(queued:true);
20
+ b. 否则在本机临时起一个 http 服务器(127.0.0.1,随机端口),
21
+ 自动打开浏览器弹窗,然后阻塞。
22
+ 4. 用户在弹窗/面板看清现状,回填新任务 → 提交(Enter 或按钮)。
23
+ 5. wait 解除阻塞,stdout(--json) 返回 {status:"ok", task},进程退出。
24
+ 6. agent 从工具结果读 task.text,继续干活、提交结果;做完【必须】回到第 2 步——
25
+ 循环,不许自行中断。
26
+ 7. 若阻塞时限(默认 180 秒)内没有回填:wait 返回 {status:"timeout"}(exit 0),
27
+ agent 不再等待,收尾并结束本轮——这是整个循环唯一的合法出口。
28
+ ```
29
+
30
+ 关键点:**第 2 步不是可选的**——它既是「等用户回填」的手段,也是「领取队列里
31
+ 已排队任务」的手段;不调 wait,排着的任务(kv-submit 委托的、用户提前排队的)
32
+ 就永远没人领。另外,第 2 步之后、工具结果回来之前,agent 不产生任何其它动作。
33
+ 用户的回填正是通过这个工具结果进入对话的,等于用工具调用替代了在原始输入框
34
+ 打字,agent 这一轮不会因为「等输入」而提前结束。
35
+
36
+ ## 二、现状总结怎么写
37
+
38
+ wait 必带一句当前现状总结(≤2000 字,连续空白会折叠为单空格):
39
+
40
+ - 它**只是状态展示**:显示在弹窗/面板顶部,并作为「本轮完成总结」记录在被取走
41
+ 的任务上(`task.roundSummary`),供用户和后续追溯。
42
+ - **它不参与队列分桶**:领取顺序只由本目录队列的先后决定,与措辞无关——同一
43
+ 目录的多次 wait(即便现状措辞不同)共用同一个任务队列。所以不必刻意保持
44
+ 逐字一致,每次如实写当前现状即可。
45
+ - 总结要让用户**不读整段对话也能快速了解现状**,建议包含三要素:
46
+ 1. 已经做到哪一步 / 产出了什么;
47
+ 2. 当前卡在哪、有哪些可选项;
48
+ 3. 具体等用户决定或补充什么。
49
+ - 一句话为宜。示例:
50
+ - `已完成登录,等待选择工作空间`
51
+ - `已生成 3 个方案(A/B/C),等待你选一个再细化`
52
+ - `面板已能提交,但删除还没接,等待确认是否现在补`
53
+
54
+ ## 三、队列语义(按目录划分、可提前排队)
55
+
56
+ - **工作目录即主题**:wait 不传 `--cwd` 时按进程当前目录的**目录名**定位主题
57
+ (Windows 折叠大小写,同名目录共用队列);`add` 可用 `--cwd` 给任意目录的
58
+ 队列排任务。不同目录 → 不同主题 → 队列互不串。
59
+ - 用户在弹窗、常驻面板「本地任务」页、或别人用 `localtask add`(kv-submit)
60
+ 提交的回填,都会以 `pending` 进入对应目录的队列。
61
+ - wait 总是取走该队列**最早的一条 pending**,标记为已领取(consumed)后返回:
62
+ - 队列非空 → wait 立刻返回(`queued:true`),连弹窗都不打开;
63
+ - 队列为空 → 才开窗阻塞(`delivered:true`)。
64
+ - 已领取的任务保留为历史(状态「已领取」),不会被二次取走。
65
+
66
+ 这让「趁 agent 还在忙,先把后面几条排好」成为可能:agent 每次 wait 都能立刻
67
+ 拿到下一条,不打断节奏。
68
+
69
+ ## 四、返回形状与退出码
70
+
71
+ 成功(exit 0):
72
+
73
+ ```json
74
+ {
75
+ "status": "ok",
76
+ "cwd": "D:\\code\\a_js\\proj\\demo",
77
+ "summary": "已完成登录,等待选择工作空间",
78
+ "task": { "id": 3, "text": "选择工作空间 A", "status": "consumed",
79
+ "createdAt": "2026-10-11 10:00:00", "consumedAt": "2026-10-11 10:00:04",
80
+ "roundSummary": "已完成登录,等待选择工作空间" },
81
+ "delivered": true,
82
+ "queued": false
83
+ }
84
+ ```
85
+
86
+ - `delivered:true` = 实时回填;`queued:true` = 取到队列里排队的任务(二者互斥)。
87
+ - **任务内容永远读 `task.text`**,多行换行原样保留。
88
+
89
+ 超时(exit 0,业务结果而非错误):
90
+
91
+ ```json
92
+ { "status": "timeout", "cwd": "D:\\code\\a_js\\proj\\demo",
93
+ "summary": "已完成登录,等待选择工作空间", "timeoutMs": 180000 }
94
+ ```
95
+
96
+ - 超时**不是失败**:退出码 0,按「用户没继续」收尾,不要按错误处理。
97
+ - 阻塞时限:默认用页面配置(推荐 180 秒,最大 600 秒),可用 `--timeout <秒>`
98
+ 覆盖(超上限自动截断)。
99
+ - 参数非法(如缺 `--summary`、缺 `--id`)才是错误(exit 1,文本带「用法」),
100
+ 按宿主失败流程处理。
101
+
102
+ ## 五、弹窗任务表怎么用(可转述给用户)
103
+
104
+ - 顶部「当前现状」直接显示 agent 传入的现状总结,先看这句再决定回填什么。
105
+ - 大输入框:回填下一条任务;**Enter 提交,Shift+Enter 换行**。
106
+ - 状态栏倒计时显示还会等多久;归零未回填则本轮自动结束。
107
+ - 下方是该主题的任务表,状态分「待领取 / 已领取」,可删除。
108
+ - 提交后显示「已回填给 agent,正在返回」即说明 wait 已取走、窗口可关闭;agent
109
+ 做完会再开下一个弹窗。
110
+
111
+ ## 六、多主题并存
112
+
113
+ - 每个工作目录一个主题;常驻面板「本地任务」页把所有主题并排展示,每张卡显示
114
+ 目录、当前现状、等待状态/倒计时、待领取数。
115
+ - 用户在任意主题卡上都能**分别回填**,也可趁 agent 在忙给几个目录提前排队。
116
+ - agent 在哪个目录跑 wait(或 `--cwd` 指哪个目录),就取哪个目录队列的任务,
117
+ 互不串。
118
+
119
+ ## 七、删除主题与批量删除
120
+
121
+ 主题(工作目录)可整体删除,不必逐条删任务:
122
+
123
+ - **单个删除**:
124
+ - 面板:主题卡片右下角「删除主题」;
125
+ - CLI:`nx-kv localtask topic remove --cwd "<工作目录>"`(或 `--key <key>`)。
126
+ - **批量删除**:
127
+ - 面板:勾选卡片左上角(或总览标题处「全选」)→「批量删除 (N)」;
128
+ - CLI:`nx-kv localtask topic remove-many --keys "<key,key>"`,也可
129
+ `--cwds "<dir,dir>"`,或 `--all` 清空。
130
+ - **安全保护**:等待中的主题默认不删(删了阻塞的 agent 只能等到超时);界面会
131
+ 二次确认,命令需加 `--force` 才连等待中一起删,agent 随后超时收尾。
132
+ - **返回**:单个 `{status:"removed",topic}`;批量 `{count,removed,skipped}`,
133
+ skipped 列出被跳过的「等待中 / 不存在」主题。
134
+
135
+ ## 八、常见错误与避坑
136
+
137
+ | 错误做法 | 后果 | 正确做法 |
138
+ | --- | --- | --- |
139
+ | 做完当前任务直接收尾、不调 wait | 强制循环被打破:队列里排着的任务(kv-submit 委托、用户提前排队的)没人领,用户喂的任务石沉大海 | 每一步的结束动作都必须是 wait;只有 timeout 才结束本轮 |
140
+ | wait 后又安排别的工具/推理 | wait 阻塞,后续动作被卡住、顺序全乱 | wait 严格放在最后一步,之后只处理返回 |
141
+ | 工具超时没设 / 设成 120000 | CLI 默认阻塞 180 秒,宿主会在阻塞中途杀掉调用 | 工具超时设 `300000` ms;用更长的 `--timeout` 时同步放大 |
142
+ | wait 不带 / 空着 --summary | 无法展示现状,命令报错退出 | 每次 wait 都写一句现状总结 |
143
+ | 刻意维持现状措辞逐字一致 | 白费功夫——现状只是展示,不影响领取 | 每次如实写当前现状即可 |
144
+ | 在错误的目录调 wait(没传 --cwd) | 取到别的目录队列的任务,或开出一个没人看的弹窗 | 确认进程 cwd 或显式 `--cwd "<目标目录>"` |
145
+ | 把 timeout 当报错、反复重试 | 用户想停却被反复弹窗 | timeout 即收尾结束本轮 |
146
+ | 读 `task.id` 当任务内容 | id 只是编号,内容在 `task.text` | 一律读 `task.text` |
147
+ | 同时开多个 wait(同一目录主题) | 只有一个收到回填,另一个等到超时 | 同一目录同一时刻只调一个 wait |
148
+ | 没用 --json | 输出人读文本,宿主难以可靠解析 | agent 调用一律加 `--json` |
149
+
150
+ ## 九、与常驻面板配合 / 排障
151
+
152
+ - 已 `nx-kv serve` 打开面板时,直接在「本地任务」页点选主题回填;正在阻塞的
153
+ wait 通过轮询(约每 0.5 秒)取走,不依赖弹窗本身。
154
+ - 弹窗没自动打开时:终端输出里有本次地址(`http://127.0.0.1:<端口>/`),手动
155
+ 复制到浏览器打开;该窗口只在本次 wait 期间有效。
156
+ - wait 结束(拿到任务或超时)后弹窗服务器随即关闭,窗口里再提交会失败——下一
157
+ 条等 agent 下一次 wait 打开新窗口再给,或直接在常驻面板回填。
@@ -48,7 +48,7 @@
48
48
 
49
49
  - **动手前必须调用** `superpowers:writing-plans`
50
50
  - 时机:**Step 7 确认主题意图之后、Step 8 开始解决之前**
51
- - 把已确认的意图作为 spec 交给它,产出实现计划(存 `docs/superpowers/plans/YYYY-MM-DD-<feature>-indent.md`),**按计划**再解决
51
+ - 把已确认的意图作为 spec 交给它,产出实现计划(计划存放到哪、怎么命名,由它自己的规范决定),**按计划**再解决
52
52
  - 任务拆分、写法、执行交接都以 `superpowers:writing-plans` 为准,本文件不重复
53
53
 
54
54
  ## 核心流程(8 步)
@@ -114,8 +114,8 @@ nx-kv todo list --status open --topic <topic> --json
114
114
 
115
115
  ### Step 6 — 生成意图文档
116
116
 
117
- 把聚类结果写成结构化文档(如 `.claude/repo/_self/<主题>-意图.md` 或
118
- `docs/superpowers/specs/` 下的设计文档):
117
+ 把聚类结果写成结构化文档,落到**当前项目**的文档目录(如 `docs/<主题>-意图.md`;
118
+ 项目已有设计文档 / spec 目录就放那里):
119
119
 
120
120
  - 每个主题:任务列表、目标、上下文/提示词摘要、边界、验收标准
121
121
  - 文档既是 Step 7 确认的载体,也是后续执行的依据
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nx-kv",
3
- "version": "0.7.0",
4
- "description": "nx-kv — KV 清单管理中枢 + 对话内实时任务接力:serve 驱动 CLI 与 Web 面板,同源同构,附带可安装 skill 供 agent 领取任务与阻塞接收用户输入",
3
+ "version": "0.8.1",
4
+ "description": "nx-kv — KV 清单管理中枢(远程)+ 本机本地任务队列(localtask):serve 驱动 CLI 与 Web 面板,同源同构,附带可安装 skill 供 agent 提交 / 领取 / 阻塞接收任务",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "nx-kv": "bin/cli.mjs"
package/src/index.js CHANGED
@@ -4,7 +4,7 @@ export * as config from './core/config.js';
4
4
  export * as kvapi from './core/kvapi.js';
5
5
  export * as errors from './core/errors.js';
6
6
  export * as todo from './modules/todo/service.js';
7
- export * as sessiontask from './modules/sessiontask/service.js';
7
+ export * as localtask from './modules/localtask/service.js';
8
8
 
9
9
  export { ACTIONS, MODULES } from './runtime/registry.js';
10
10
  export { startServer } from './runtime/server.js';
@@ -1,14 +1,16 @@
1
- // 实时任务输入模块:在一个对话里通过工具调用向 agent 实时回填任务,替代原始输入框。
1
+ // 本地任务(localtask)模块:本机按「工作目录(cwd)」划分的任务队列——提交(add)、
2
+ // 领取(wait)、查看/编辑/删除/调序(list/get/update/remove/reorder)与主题管理。
3
+ // 阻塞等待只是当前的一个子功能:agent 在一个对话里需要用户继续给任务时,不必结束
4
+ // 本轮、等用户去原始输入框打字,而是把
5
+ // `localtask wait --cwd "<工作目录>" --summary "<当前现状>"` 作为本轮**最后一步**调用:
6
+ // 命令阻塞、自动打开浏览器弹窗,用户回填后解除阻塞、把新任务作为结果返回,agent 接着干。
2
7
  //
3
- // 声明了 `resource: 'sessiontask'`,故 CRUD 五操作齐备且两端可调用
4
- // (tests/unit/registry.test.mjs 据此断言)。核心动作是纯 CLI 的
5
- // `sessiontask wait --cwd "<工作目录>" --summary "<当前现状>"`——agent 把它放在本轮
6
- // **最后一步**调用:阻塞、自动打开浏览器弹窗,用户回填后解除阻塞,把新任务返回给
7
- // agent。工作目录即主题、主题拥有任务队列:同目录共用队列、不同目录互不串,任务可
8
- // 在主题队列里堆积。
8
+ // 声明了 `resource: 'localtask'`,故 CRUD 五操作齐备且两端可调用
9
+ // (tests/unit/registry.test.mjs 据此断言)。工作目录即主题、主题拥有任务队列:
10
+ // 同目录共用队列、不同目录互不串,任务可在主题队列里堆积。
9
11
  //
10
- // 与 todo 的区别:todo 管 KV 后端上的清单(需登录);sessiontask 是**本机**
11
- // 实时人机接力,数据存本地文件,不依赖后端登录态。
12
+ // 与 todo 的区别:todo 管 KV 后端上的清单(需登录);localtask 是**本机**
13
+ // 任务队列,数据存本地文件,不依赖后端登录态。
12
14
  import * as service from './service.js';
13
15
 
14
16
  const CWD = { type: 'string', hint: '工作目录(作为主题;默认当前目录)' };
@@ -51,7 +53,8 @@ function renderAdd(d) {
51
53
  // wait 的人读输出:成功给任务,超时明确提示「本轮结束」
52
54
  function renderWait(r) {
53
55
  if (r.status === 'timeout') {
54
- return `等待超时(${Math.round(r.timeoutMs / 1000)} 秒未回填):主题「${r.cwd}」现状「${r.summary}」结束本轮。`;
56
+ const tail = r.cancelled ? '(用户在面板点了「关闭」)' : '';
57
+ return `等待超时(${Math.round(r.timeoutMs / 1000)} 秒未回填)${tail}:主题「${r.cwd}」现状「${r.summary}」结束本轮。`;
55
58
  }
56
59
  const origin = r.queued ? '排队任务' : '实时回填';
57
60
  return (
@@ -60,6 +63,13 @@ function renderWait(r) {
60
63
  );
61
64
  }
62
65
 
66
+ // cancel 的人读输出:等待中→已关闭(主题与任务保留);非等待中→无操作
67
+ function renderCancel(d) {
68
+ if (d.cancelled) return `已关闭主题「${d.name}」的等待:阻塞中的 wait 立即收尾,主题与任务保留。`;
69
+ if (d.reason === 'not_waiting') return `主题「${d.name || d.key}」不在等待中,无需关闭。`;
70
+ return '已关闭。';
71
+ }
72
+
63
73
  // 批量删除的人读输出:删除清单 + 跳过清单(等待中/不存在)
64
74
  function renderRemoveMany(d) {
65
75
  const lines = [d.all ? `清空:删除 ${d.count} 个主题` : `已删除 ${d.count} 个主题`];
@@ -76,37 +86,37 @@ function renderRemoveMany(d) {
76
86
  }
77
87
 
78
88
  export default {
79
- id: 'sessiontask',
80
- title: '实时任务(= Web「实时任务」页)',
89
+ id: 'localtask',
90
+ title: '本地任务(= Web「本地任务」页)',
81
91
  order: 20,
82
92
  view: () => import('./view.jsx'),
83
93
 
84
94
  // CRUD 资源声明:主题任务的增删查改齐备,CLI 与面板都能调用
85
- resource: 'sessiontask',
95
+ resource: 'localtask',
86
96
 
87
97
  actions: [
88
98
  // ─── CRUD 五操作 ───────────────────────────────────────────
89
99
  {
90
- id: 'sessiontask.list',
91
- cli: ['sessiontask', 'list'],
92
- http: ['GET', '/api/sessiontasks'],
100
+ id: 'localtask.list',
101
+ cli: ['localtask', 'list'],
102
+ http: ['GET', '/api/localtasks'],
93
103
  summary: '总览全部主题(工作目录、现状、是否等待中、待领取数)',
94
104
  run: () => service.listTasks(),
95
105
  render: renderOverview,
96
106
  },
97
107
  {
98
- id: 'sessiontask.get',
99
- cli: ['sessiontask', 'get'],
100
- http: ['GET', '/api/sessiontasks/item'],
108
+ id: 'localtask.get',
109
+ cli: ['localtask', 'get'],
110
+ http: ['GET', '/api/localtasks/item'],
101
111
  summary: '查看单条任务(按 --id,定位到 --cwd 主题)',
102
112
  flags: { id: { type: 'number', required: true, hint: '任务编号' }, cwd: CWD },
103
113
  run: (ctx) => service.getTask({ id: ctx.id, cwd: ctx.cwd }),
104
114
  render: renderTask,
105
115
  },
106
116
  {
107
- id: 'sessiontask.add',
108
- cli: ['sessiontask', 'add'],
109
- http: ['POST', '/api/sessiontasks'],
117
+ id: 'localtask.add',
118
+ cli: ['localtask', 'add'],
119
+ http: ['POST', '/api/localtasks'],
110
120
  summary: '向某主题(工作目录)的队列回填一条任务(有 wait 在等则即时投递,否则排队)',
111
121
  args: ['text'],
112
122
  flags: { cwd: CWD, summary: { type: 'string', hint: '顺带刷新该主题的当前现状(可选)' } },
@@ -114,9 +124,9 @@ export default {
114
124
  render: renderAdd,
115
125
  },
116
126
  {
117
- id: 'sessiontask.update',
118
- cli: ['sessiontask', 'update'],
119
- http: ['PATCH', '/api/sessiontasks/item'],
127
+ id: 'localtask.update',
128
+ cli: ['localtask', 'update'],
129
+ http: ['PATCH', '/api/localtasks/item'],
120
130
  summary: '编辑任务内容(按 --id,定位到 --cwd 主题)',
121
131
  flags: {
122
132
  id: { type: 'number', required: true, hint: '任务编号' },
@@ -127,9 +137,9 @@ export default {
127
137
  render: (d) => `已更新主题「${d.name}」#${d.task.id}: ${d.task.text}`,
128
138
  },
129
139
  {
130
- id: 'sessiontask.remove',
131
- cli: ['sessiontask', 'remove'],
132
- http: ['DELETE', '/api/sessiontasks/item'],
140
+ id: 'localtask.remove',
141
+ cli: ['localtask', 'remove'],
142
+ http: ['DELETE', '/api/localtasks/item'],
133
143
  summary: '删除一条任务(按 --id,定位到 --cwd 主题)',
134
144
  flags: { id: { type: 'number', required: true, hint: '任务编号' }, cwd: CWD },
135
145
  run: (ctx) => service.removeTask({ id: ctx.id, cwd: ctx.cwd }),
@@ -138,9 +148,9 @@ export default {
138
148
 
139
149
  // ─── 主题删除(单个 / 批量)──────────────────────────────
140
150
  {
141
- id: 'sessiontask.topic.remove',
142
- cli: ['sessiontask', 'topic', 'remove'],
143
- http: ['DELETE', '/api/sessiontasks/topic'],
151
+ id: 'localtask.topic.remove',
152
+ cli: ['localtask', 'topic', 'remove'],
153
+ http: ['DELETE', '/api/localtasks/topic'],
144
154
  summary: '删除整个主题(含全部任务;按 --cwd 或 --key,等待中需 --force)',
145
155
  flags: {
146
156
  cwd: { type: 'string', hint: '工作目录(与 --key 二选一)' },
@@ -152,9 +162,9 @@ export default {
152
162
  `已删除主题「${d.name}」(待领取/共 ${d.topic.pending}/${d.topic.total} 条任务一并删除)`,
153
163
  },
154
164
  {
155
- id: 'sessiontask.topic.removeMany',
156
- cli: ['sessiontask', 'topic', 'remove-many'],
157
- http: ['POST', '/api/sessiontasks/topics/remove'],
165
+ id: 'localtask.topic.removeMany',
166
+ cli: ['localtask', 'topic', 'remove-many'],
167
+ http: ['POST', '/api/localtasks/topics/remove'],
158
168
  summary: '批量删除主题(--keys/--cwds 逗号分隔,或 --all;等待中默认跳过,--force 连等待中删)',
159
169
  flags: {
160
170
  keys: { type: 'array', hint: '主题 key,逗号分隔' },
@@ -168,9 +178,9 @@ export default {
168
178
  },
169
179
 
170
180
  {
171
- id: 'sessiontask.config',
172
- cli: ['sessiontask', 'config'],
173
- http: ['PATCH', '/api/sessiontasks/setting'],
181
+ id: 'localtask.config',
182
+ cli: ['localtask', 'config'],
183
+ http: ['PATCH', '/api/localtasks/setting'],
174
184
  summary: '配置等待超时(秒;推荐 180=3 分钟,最大 600=10 分钟,超上限自动截断)',
175
185
  flags: { timeout: { type: 'number', required: true, hint: '超时秒数(1-600)' } },
176
186
  run: (ctx) => service.updateSettings({ timeoutSec: ctx.timeout }),
@@ -178,9 +188,9 @@ export default {
178
188
  },
179
189
 
180
190
  {
181
- id: 'sessiontask.prune',
182
- cli: ['sessiontask', 'prune'],
183
- http: ['POST', '/api/sessiontasks/prune'],
191
+ id: 'localtask.prune',
192
+ cli: ['localtask', 'prune'],
193
+ http: ['POST', '/api/localtasks/prune'],
184
194
  summary: '清理旧脏数据(旧模型无目录主题、空主题;等待中一律保留)',
185
195
  flags: {
186
196
  'all-finished': { type: 'boolean', hint: '连同已全部领取、无待领的主题一起清' },
@@ -195,9 +205,9 @@ export default {
195
205
  },
196
206
 
197
207
  {
198
- id: 'sessiontask.reorder',
199
- cli: ['sessiontask', 'reorder'],
200
- http: ['PATCH', '/api/sessiontasks/reorder'],
208
+ id: 'localtask.reorder',
209
+ cli: ['localtask', 'reorder'],
210
+ http: ['PATCH', '/api/localtasks/reorder'],
201
211
  summary: '调整待领取任务优先级(--id <编号> 移到 --before-id <编号> 前或 --after-id <编号> 后)',
202
212
  flags: {
203
213
  id: { type: 'number', required: true, hint: '要移动的任务编号' },
@@ -215,8 +225,8 @@ export default {
215
225
 
216
226
  // ─── 阻塞等待(纯 CLI,agent 本轮最后一步)────────────────
217
227
  {
218
- id: 'sessiontask.wait',
219
- cli: ['sessiontask', 'wait'],
228
+ id: 'localtask.wait',
229
+ cli: ['localtask', 'wait'],
220
230
  http: null,
221
231
  summary: '针对某主题(按 cwd 目录名识别)阻塞等待用户回填(默认 3 分钟,最大 10 分钟)',
222
232
  flags: {
@@ -234,5 +244,18 @@ export default {
234
244
  }),
235
245
  render: renderWait,
236
246
  },
247
+
248
+ {
249
+ id: 'localtask.cancel',
250
+ cli: ['localtask', 'cancel'],
251
+ http: ['POST', '/api/localtasks/cancel'],
252
+ summary: '关闭某主题的等待:阻塞中的 wait 立即收尾(与超时同一出口,带 cancelled 标记;不删主题与任务)',
253
+ flags: {
254
+ cwd: { type: 'string', hint: '工作目录(与 --key 二选一)' },
255
+ key: { type: 'string', hint: '主题 key(与 --cwd 二选一)' },
256
+ },
257
+ run: (ctx) => service.cancelWait({ cwd: ctx.cwd, key: ctx.key }),
258
+ render: renderCancel,
259
+ },
237
260
  ],
238
261
  };