nx-kv 0.8.5 → 0.10.0

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 (41) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/README.md +82 -2
  3. package/assets/groups.json +4 -0
  4. package/assets/kv-timegoal/SKILL.md +160 -0
  5. package/assets/kv-timegoal/references/workflow.md +151 -0
  6. package/assets/nx-kv/SKILL.md +48 -0
  7. package/assets/nx-kv/references/todo-commands.md +52 -0
  8. package/package.json +1 -1
  9. package/src/core/attachstore.js +269 -0
  10. package/src/core/config.js +22 -0
  11. package/src/core/fileapi.js +243 -0
  12. package/src/core/kvapi.js +3 -1
  13. package/src/core/paths.js +9 -0
  14. package/src/modules/file/index.js +100 -0
  15. package/src/modules/file/service.js +214 -0
  16. package/src/modules/localtask/service.js +105 -22
  17. package/src/modules/localtask/view.jsx +454 -5
  18. package/src/modules/setting/index.js +33 -0
  19. package/src/modules/setting/service.js +48 -0
  20. package/src/modules/timegoal/index.js +157 -0
  21. package/src/modules/timegoal/service.js +313 -0
  22. package/src/modules/todo/attach.js +96 -0
  23. package/src/modules/todo/editor.jsx +229 -0
  24. package/src/modules/todo/index.js +72 -0
  25. package/src/modules/todo/markdown.jsx +53 -0
  26. package/src/modules/todo/service.js +171 -10
  27. package/src/modules/todo/upload.js +107 -0
  28. package/src/modules/todo/view.jsx +83 -10
  29. package/src/runtime/api.js +6 -1
  30. package/src/runtime/registry.js +4 -1
  31. package/src/web/frontend/style.css +133 -0
  32. package/src/web/public/assets/{CliHints-ZTpac1yV.js → CliHints-B98Zw4uK.js} +1 -1
  33. package/src/web/public/assets/{index-gJ1xxwt4.js → index-57QX-j8y.js} +2 -2
  34. package/src/web/public/assets/{index-CmCYxz0e.css → index-BDsJPdhM.css} +1 -1
  35. package/src/web/public/assets/view-BskvoBME.js +3 -0
  36. package/src/web/public/assets/{view-Cq4GElgD.js → view-BzY577lO.js} +1 -1
  37. package/src/web/public/assets/view-Cn7mYuiU.js +1 -0
  38. package/src/web/public/assets/{view-CYSIt3hy.js → view-_UA707kn.js} +1 -1
  39. package/src/web/public/index.html +2 -2
  40. package/src/web/public/assets/view-C9k3lhCV.js +0 -2
  41. package/src/web/public/assets/view-mzUKgI5p.js +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,178 @@
2
2
 
3
3
  本文件记录对外可见的变更。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ## [0.10.0] - 2026-10-20
8
+
9
+ ### Added
10
+
11
+ - **新增 `timegoal` 模块:把"持续运行 N 小时"任务固化到 nx-kv。** 完全独立的命令族
12
+ / 独立 store / 独立循环链路——**与 localtask wait 没有任何关系**(不调用、不被调用、
13
+ store 也不共享),可以同时跑也可以分开跑。UI 上嵌在「本地任务」主题卡里只是为了查
14
+ 看便利(数据/逻辑完全独立——拔掉 localtask 本模块照样跑)。
15
+
16
+ - 命令族 `nx-kv timegoal <...>`:
17
+ - **`set`** 新建/替换本目录 timegoal(`--duration 60-86400`、`--prompt "<...>"`),
18
+ 重置起点;
19
+ - **`update`** 改提示词,可选**同时改时长**(`--duration`)——**保留起点**
20
+ (`deadline = startedAt + 新时长`,已用时间仍算数);agent 自我迭代提示词、
21
+ 用户 Web 端编辑都用这条,避免一次小改动把整套 3 小时清零;
22
+ - **`show`** 仅查询;**`check`** 时间运算 + **原文回显 prompt**
23
+ (让 agent 永远以 store 为准,避免长对话里漂移),到点返回 `reached:true`;
24
+ - **`clear`** 清空(无则 `no_goal`,面板按钮可无脑调用);
25
+ - **`list`** 总览所有有 timegoal 的目录。
26
+ - **典型流程**:开头 `set` 一次 → 每轮 `check` 拿 `prompt` 当本轮根指令干活 →
27
+ 调研发现 prompt 漏条件就 `update` 替换 → 到 `reached:true` 那轮立刻停手。
28
+ - 独立 store `~/.nx-kv/timegoals.json`(`NX_KV_TIMEGOAL_STORE` 可覆盖),
29
+ 与 localtask 的 `localtasks.json` 完全分离——两套循环并行不冲突。
30
+ - **UI 完整编辑入口**(`localtask/view.jsx`,任务看板):
31
+ - 页面顶部新增「时间目标」汇总卡:列出**所有目录**的 timegoal(含还没在
32
+ 看板里出现过的目录——CLI 先设、agent 后 wait),支持**新建**(填目录 +
33
+ 时长 + 提示词)、**编辑**(改时长/提示词;默认保留起点,勾「重置起点」
34
+ 才从头计)、**清空**;状态列显示 `剩 mm:ss` / `已到点`,每 1.5s 刷新。
35
+ - 主题卡里 timegoal 行**恒显示**:已设置 → 倒计时 + 查看/编辑/清空;
36
+ 未设置 → 「未设置时间目标 + 设置」(直接为该目录新建)。
37
+ - 时长输入带快捷按钮(30 分钟 / 1 / 2 / 3 / 8 小时),不用手打秒数。
38
+ - 编辑一律走 `PATCH /api/timegoals`(保留起点);只有勾「重置起点」才走
39
+ `POST`(= `set`,计时清零)。
40
+ - 模块边界:timegoal service 不读 localtask service;`topicKeyFor / stampNow
41
+ / parseStampMs` 是 4-6 行的纯工具,本地复制避免跨模块耦合;纯函数
42
+ `evalTimegoal(goal, nowMs)` 不读时钟、便于单测钉边界。
43
+ - 单测 `tests/unit/timegoal.test.mjs`:duration / prompt 边界、`evalTimegoal`
44
+ 四时段、set → show → check 链路、update 保留起点、不同 cwd 隔离、模拟到点、
45
+ store 损坏容忍、环境变量覆盖、prompt 含换行/Unicode 原样回显。
46
+ - 配套 skill `kv-timegoal`:`nx-kv skill install kv-timegoal`。`SKILL.md` 与
47
+ `references/workflow.md` 强调 timegoal 与 localtask wait 等其它命令族完全独立
48
+ ——skill 文字里不出现"`localtask wait` + `timegoal check`"的搭配示例。
49
+
50
+ ### Fixed
51
+
52
+ - **短时限等待被秒级截断「吃掉」:`waitingUntil` 只存到秒。** `stampNow` 是**截断**
53
+ 不是四舍五入——真实 deadline `S+1.97s` 存成 `"S+1"` 后解析回来是 `S+1.000`,比真实
54
+ 时刻早了近 1 秒。等待时长只有 1~2 秒时这点误差能吃掉**整个窗口**:wait 若恰好在
55
+ 某秒最后 ~40ms 启动,事件流 100ms 后轮询时判定「不在等待」→ `waiting` 事件永远推
56
+ 不出来(`tests/unit/localtask-sse.test.mjs` 由此带 ~4% 的 flake,实测 flake 率与
57
+ 40ms/1000ms 的理论值吻合)。
58
+ - **修法**:`setWaiting` 额外写毫秒精度的 `waitingUntilMs`(epoch ms);判定一律走
59
+ `deadlineMsOf()`(优先毫秒值,老数据回退解析字符串)——`isLiveWaiting` /
60
+ `watchEvents` / 面板倒计时全部改用它。`watchEvents` 判「新一轮等待」也从比较
61
+ 秒级字符串改成比较毫秒 deadline(同一秒内开始的两次等待字符串相同,靠它判会漏)。
62
+ - 真实影响:默认 180s 超时下截断误差无感(相对误差太小),但**判据正确性**不应
63
+ 依赖时长够长;面板倒计时也不再提前 1 秒归零。
64
+ - 回归测试 4 例:字符串已过期但毫秒值未到 → 仍算等待中;毫秒值也过期 → 不算;
65
+ 老数据无 `waitingUntilMs` 回退字符串;真实 wait 写入毫秒值且与字符串同秒。
66
+
67
+ - **「等待中」永远不结束:僵尸 `waiting` 标志残留。** 面板上某个主题永远显示
68
+ 「等待中 已等 760:35 · 剩 0:00」,点「关闭」也没反应。
69
+ - **根因**:`waiting` 标志由 wait 进程写入、结束时在 `finally` 里复位;但 wait
70
+ 被**强杀**(SIGKILL / 任务管理器杀 / 终端关掉 / 机器崩了)时 `finally` 不会执行,
71
+ 标志就永久留在 store 里。曾实测:gs-ac 的 wait 在 22:29 开始、应在 22:32 超时
72
+ 收尾,但进程没了 —— 于是标志卡住,用户 23:14 点「关闭」也**没有活进程**来消费
73
+ `cancelRequested`。
74
+ - **修复**:判定「是否真的在等」统一走新函数 `isLiveWaiting(t)` ——
75
+ `waiting` 为真**且**截止时刻还没过。截止已过 = 那个 wait 本来也该收尾了 =
76
+ 没人真在等。此前 `countWaitingTopics()` 与 `watchEvents()` 各有一份内联判断,
77
+ `topicView()` 却只看裸标志,于是面板看到的是假状态。
78
+ - 改动点:`topicView()` 对外把 `waiting` 抹成假值、另给 `staleWaiting` 标记;
79
+ `pruneTopics()` / `removeTopic()` / `removeTopics()` / `cancelWait()` /
80
+ `countWaitingTopics()` 全部改用 `isLiveWaiting`。
81
+ - **连带修好的三处卡死**:僵尸等待的主题过去永远躲过 `prune`(空主题也清不掉)、
82
+ 被「等待中不能删」挡住、也阻塞面板空闲自释放。
83
+ - 面板显示改为「**已结束(上次等待超时,进程已退出)**」,如实说明发生了什么,
84
+ 而不是让倒计时一直转。
85
+ - 单测 `tests/unit/localtask-stale.test.mjs`(7 例):`isLiveWaiting` 四个分支 +
86
+ 僵尸等待在 list / count / prune / remove / cancel 各路径的表现 + 正常等待不受影响。
87
+
88
+ - **`localtask wait` 会重复打开多个浏览器窗口**。根因是「有没有活标签」这条判据失效:
89
+ 服务端 `panelPresence()` 统计的是 `/api/localtasks/events` 上的活动 SSE 连接,
90
+ 但面板页改成 1.5s 轮询后**再也没有建立过 SSE**,presence 恒为 0 ——
91
+ 于是每次 wait 都以为没人在看、都开一个新窗口,等待期间还每 20s 再开一个。
92
+ - **面板补上常驻 SSE**(`localtask/view.jsx`):既是 presence 的信号,也顺手把
93
+ waiting / reminder / resolved 变成即时刷新,轮询退化为兜底。
94
+ - **wait 开窗加三道闸**:初次开页即计入冷却(页面加载到 SSE 连上之间有空窗,
95
+ 否则 5s 后第一次复查就会再开一个);单次探测为 0 不算标签被关,要**连续两次**
96
+ (≈10s)探不到才重开 —— 后台标签被冻结、SSE 自动重连、网络抖动都会短暂掉 0;
97
+ 一次 wait 最多开 3 个窗口,避免浏览器拦脚本时刷成窗口风暴。
98
+ - 单测 `tests/unit/localtask-window.test.mjs`:`shouldReopenWindow` 四个分支 +
99
+ `panelPresence` 的连接计数与断开归零。
100
+
101
+ ## [0.9.0] - 2026-10-16
102
+
103
+ ### Added
104
+
105
+ - **任务附件:正文里用标准 markdown 嵌图片与文档,并可同步到本地给 agent 直接读。**
106
+
107
+ - **嵌入语法就是标准 markdown**,不发明新方言(解析实现只有一处:
108
+ `src/modules/todo/attach.js`,Node 与浏览器共用):
109
+ `![说明](fileId)` 图片(面板内联预览)、`[文件名](fileId)` 其他文件(docx/pdf…)。
110
+ `fileId` 是后端图床的不透明 id(32 位 hex,原文件名带后缀时结尾接 `.<ext>`)。
111
+ **判定刻意收窄**:URL(含 `://`)、站点路径(`/…`)、相对路径(`./…`)一律按
112
+ 普通 markdown 原样保留 —— 否则任务里写条普通链接就会被当成附件,
113
+ 然后预览 404、同步白跑一趟网络。
114
+ - **新模块 `file`**:`file upload / list / get / remove / content / sync`。
115
+ 上传走后端 multipart(`POST /api/v1/files`,字段名 `file`);`file content` 在
116
+ HTTP 侧把后端出图内容原样透传(面板 `<img src="/api/file/content/<id>">` 用),
117
+ CLI 侧只打印地址与本地路径。
118
+ - **`file sync`:把附件同步到本机** —— 存在的唯一理由是让 agent **读本地文件而不是读网**。
119
+ 默认落到 `~/.nx-kv/attachments/<groupId|default>/`(`NX_KV_ATTACHMENTS` 可改根目录),
120
+ 同目录 `manifest.json` 记「fileId → 本地文件名 + 大小」,所以二次同步跳过未变更的。
121
+ 本地文件名优先取正文里的说明文字(`![设计稿.png](id)` → `设计稿.png`),否则退回
122
+ fileId 并按 Content-Type 补扩展名(没有扩展名,Windows 上双击打不开)。
123
+ `--ids` 只同步指定附件;`--prune` 清理本地已不在后端的(**仅在全量同步时生效**——
124
+ 带 `--ids` 再 prune 会把没列出来的附件一起删掉);`--dryRun` 只列不落盘。
125
+ - **`todo attach` / `todo attachments`**:把附件**追加**到任务正文;列出某条任务
126
+ 引用了哪些附件、本地是否已有。`todo attach` 会拒绝不像 fileId 的值(比如 URL)。
127
+ ⚠️ 追加会改写正文,而定位按正文内容(`--ref`)—— 连续操作要用**新的** text。
128
+ - **新模块 `setting` + 面板开关:「读任务时自动下载附件」。**
129
+ `nx-kv setting get` / `nx-kv setting set --autoSyncAttachments[=false]`
130
+ (面板在「清单」页工具栏)。开启后 `todo list` / `todo get` 会同步正文引用的附件,
131
+ 给每个任务附加 `attachments[]`(`id / kind / name / localPath / synced`,
132
+ **localPath 是本地绝对路径**),并用 `attachmentsSync` 汇报这一轮
133
+ `downloaded / skipped / failures`。**下载失败不阻断读取** —— 附件拿不到与
134
+ 清单读不到是两件事,失败如实进 `failures`,本地已有旧版本时继续用它。
135
+ 默认**关**:开启意味着每次 list/get 都触网,对「只想看一眼清单」是纯负担。
136
+ - **`attachments` 标注与开关无关**:关掉开关后不再同步,但「这条任务挂了附件」
137
+ 仍会出现在 `attachments[]` 里(`localPath` 反映本地已有则给路径)。
138
+ - 前端:`todo` 的编辑从单行 dialog 换成完整编辑器弹窗(`editor.jsx`)——
139
+ **粘贴(Ctrl+V)/ 拖入 / 选择文件** 三条路都能上传,自动插到光标处、实时预览;
140
+ 列表里图片以缩略图内联、其他文件显示为可点附件(`markdown.jsx`)。
141
+ - **Ctrl+V 直接粘截图**是主路径:截图工具不给文件名,`upload.js` 会替它起
142
+ `粘贴图片-<时间戳>.png`(这个名字会一路用下去:正文说明文字 + 落盘文件名,
143
+ 退化成 `image.png` 就再也认不出是哪张)。一次粘多张带序号,不重名。
144
+ - 拖拽挂在 **window** 上而不是编辑区上:往弹窗里丢文件、落点差一点也该接住,
145
+ 而不是让浏览器把文件打开;只在拖**文件**时介入(拖选中的文字不弹提示)。
146
+ - 批量上传**先收齐再一次性插入** —— 边传边插会跟光标位置打架
147
+ (插入后 DOM 里光标还没更新,第二个附件会算错落点、变成倒序)。
148
+ - 纯文本粘贴不拦截,交给浏览器默认行为插到光标处。
149
+ - 快捷键 <kbd>Ctrl</kbd>+<kbd>Enter</kbd> 保存;字符计数与插入语法提示常驻。
150
+ - 「快速新增」那一栏也能 Ctrl+V 贴图:贴张截图再回车,任务与附件一次成型。
151
+ - 「附件自动下载」做成胶囊开关(面板「清单」页),悬停显示附件会落到哪个目录。
152
+ - `core/fileapi.js`(后端文件客户端,与 kvapi 共用同一套信封判定)、
153
+ `core/attachstore.js`(本地镜像与台账)、`core/config.resolveGroupId`
154
+ (模块间共享的 group 回落规则 —— todo 读 KV 与 file 选分组/本地目录必须同一条,
155
+ 否则会出现「任务读 A 组、附件同步 B 组」这种不报错的错位)。
156
+ - 本机服务的请求体上限 4MB → 24MB:面板上传走 base64 JSON(膨胀 ~33%),
157
+ 10MB 的图会变成 ~13MB 的请求体。
158
+
159
+ ### Changed
160
+
161
+ - `core/kvapi.js` 的 `call` 改为导出,供 `core/fileapi.js` 复用(两处各写一遍
162
+ 信封判定迟早分叉)。
163
+ - `todo list` / `todo get` 的每个任务可能多出 `attachments[]`;开启自动下载时
164
+ 顶层多出 `attachmentsSync`。未开启时**没有** `attachmentsSync`,其余字段与旧版一致。
165
+
166
+ ### Tests
167
+
168
+ - `tests/fake-backend.mjs` 增加文件端点,并**手写 multipart 解析** ——
169
+ 用 JSON 假装上传就测不出「boundary 丢了」「字段名写错」。
170
+ 假后端刻意**不返回 originalName**,与真实后端一致:本地文件名只能自己解决。
171
+ - `tests/smoke.mjs` 新增 25 项:上传(图片/docx)、list/get/remove、content 直出字节、
172
+ 嵌入追加、`file sync` 落盘与二次跳过、开关打开后读任务自动下载并回传全路径、
173
+ 关掉后不再同步但标注仍在、非法 fileId 被拒。
174
+ - `tests/unit/attach.test.mjs` 新增 9 项:嵌入解析的边界(普通链接不许被误判、
175
+ 去重保序、说明文字取首次出现、往返一致)。
176
+
5
177
  ## [0.8.5] - 2026-10-13
6
178
 
7
179
  ### Added
package/README.md CHANGED
@@ -6,6 +6,10 @@ KV 清单(todo)管理中枢。一个 `npx` 命令起一个 Web 面板,同
6
6
  对接 GoFrame KV 后端(默认 `http://47.110.80.47:8988`),
7
7
  数据落在**四把 key** 上:`todo:open` / `todo:done` / `todo:freeze` / `todo:topics`。
8
8
 
9
+ 任务正文还能直接**嵌图片与文档**(标准 markdown:`![说明](fileId)` / `[文件名](fileId)`)——
10
+ 附件存在后端图床的同一用户空间里,既能同步到本地给 agent 直接读(`nx-kv file sync`),
11
+ 也能在读任务时自动下载并把本地绝对路径一并交出去。
12
+
9
13
  ## 快速开始
10
14
 
11
15
  ```bash
@@ -66,6 +70,47 @@ nx-kv group members 24 # 成员
66
70
  完整命令表由 action 声明自动生成:`nx-kv help`。
67
71
  命令与 HTTP 端点互相可查:`nx-kv routes`,反查用 `nx-kv routes --http "POST /api/todo"`。
68
72
 
73
+ ## 附件(图片 / 文档)
74
+
75
+ 任务正文直接用**标准 markdown** 引用附件,附件本体存在后端图床的同一用户空间里:
76
+
77
+ ```
78
+ 修一下首页 logo ![设计稿](3f2a1b…9c) # 图片:面板内联预览
79
+ 补充材料见 [需求说明.docx](b71e…04.docx) # 其他文件:点开即下载/预览
80
+ ```
81
+
82
+ 括号里是后端返回的 `fileId`(32 位 hex,原文件名带后缀时会接 `.<ext>`)。
83
+ URL 与相对路径不算附件,按普通 markdown 原样保留(不会误判)。
84
+
85
+ ```bash
86
+ nx-kv file upload --path "D:\设计\首页.png" # 上传到当前用户空间
87
+ nx-kv todo attach --ref "修一下首页 logo" --path "D:\设计\首页.png" # 上传并嵌进正文
88
+ nx-kv todo attachments --ref "修一下首页 logo" # 这条任务引用了哪些附件
89
+
90
+ nx-kv file sync # 把附件同步到 ~/.nx-kv/attachments/<工作空间>/
91
+ nx-kv file sync --ids a,b # 只同步指定附件
92
+ nx-kv file sync --prune # 顺带清掉本地已不在后端的(仅全量同步时生效)
93
+ ```
94
+
95
+ 面板里编辑任务时,**Ctrl+V 直接粘截图**、把文件拖进来、或点「选择文件」——
96
+ 都会自动上传并插到光标处,右侧实时预览;列表里图片显示为缩略图、其他文件是可点附件。
97
+ 「快速新增」那一栏也支持粘贴图片。
98
+
99
+ **给 agent 的关键开关**:开启「读任务时自动下载」后,`todo list` / `todo get`
100
+ 的每个任务会多出 `attachments[]`,里面是**本地绝对路径**,另有 `attachmentsSync`
101
+ 汇报下载/跳过/失败。读本地文件因此不再受网络与跨域影响:
102
+
103
+ ```bash
104
+ nx-kv setting set --autoSyncAttachments # 开
105
+ nx-kv setting set --autoSyncAttachments=false # 关(默认)
106
+ ```
107
+
108
+ 面板里对应「清单」页工具栏的「附件自动下载」勾选。本地文件名优先取正文里的说明文字
109
+ (`![设计稿.png](id)` → `设计稿.png`),否则退回 fileId 并补上扩展名;
110
+ 同目录 `manifest.json` 记「fileId → 文件名 + 大小」,所以二次同步会跳过未变更的。
111
+
112
+ 单独的命令参考见 `nx-kv help file` 与 skill 的 `todo-commands`。
113
+
69
114
  ## 本地任务(localtask,不需登录)
70
115
 
71
116
  本机任务队列,按**工作目录**划分(目录名即主题):提交(`add`)、领取(`wait`)、
@@ -96,6 +141,39 @@ nx-kv skill install kv-submit # 提交侧:总结上下文、提炼需求
96
141
  nx-kv skill install kv-localget # 领取侧:主动领取排给本目录的任务
97
142
  ```
98
143
 
144
+ ## 时间目标(timegoal,不需登录)
145
+
146
+ 把「AI 持续运行 N 小时」这类长任务**固化到工具里**,而不是散落在外部脚本:
147
+
148
+ 每个**工作目录**绑定一个 timegoal = 「持续秒数 + 一段完整全局提示词」。
149
+
150
+ ```bash
151
+ # 开头设一次:3 小时 + 完整提示词(提示词会被原文固化到 store)
152
+ nx-kv timegoal set --cwd "$PWD" --duration 10800 --prompt "<完整全局提示词>" --json
153
+
154
+ # 每轮调一次:算出还剩多少时间,并把提示词【原文】回显给你
155
+ nx-kv timegoal check --cwd "$PWD" --json
156
+ # → { reached:false, remainingSec:10780, prompt:"<完整提示词原文>", deadline, ... }
157
+
158
+ # 提示词要改(agent 自我迭代 / 用户在面板上改)——只换提示词,保住计时起点
159
+ nx-kv timegoal update --cwd "$PWD" --prompt "<新版>" [--duration <秒>] --json
160
+
161
+ # 其余:show(仅查)/ clear(清空)/ list(总览所有目录)
162
+ ```
163
+
164
+ **为什么需要它**:长对话里 agent 一定会把自己手里的提示词改写、漂移、漏条件。
165
+ `check` 每次把 store 里的**原文**回显出来,agent 就没有"凭记忆发挥"的空间。
166
+ 到点时 `reached:true`,agent 据此停手、收尾。
167
+
168
+ **完全独立的模块**:独立 store(`~/.nx-kv/timegoals.json`)、独立命令族、
169
+ 独立循环——不调用 `localtask wait`、不被它调用,两者可以同时跑。
170
+ 面板上嵌在「本地任务」页(顶部「时间目标」汇总卡 + 每个主题卡里一行),
171
+ 纯粹是为了查看与编辑方便。
172
+
173
+ ```bash
174
+ nx-kv skill install kv-timegoal # 持续 N 小时任务的时序与提示词模板
175
+ ```
176
+
99
177
  ## ⚠️ 定位一律按内容,不按 id
100
178
 
101
179
  待办的 id **非常容易重复**,两条独立的原因:
@@ -156,14 +234,15 @@ nx-kv todo get --ref "旧记录" --topic fr # 或按主题收窄
156
234
  所以「Web 上能做的,CLI 都能做」是结构保证而非口头约定。
157
235
 
158
236
  ```
159
- src/core/ 零业务语义:kvapi(后端客户端) / config(本机配置) / errors / fstree
160
- src/modules/ 功能域:system / auth / group / localtask / todo / bundled
237
+ src/core/ 零业务语义:kvapi / fileapi(后端客户端) / attachstore(附件本地镜像) / config / errors / fstree
238
+ src/modules/ 功能域:system / setting / auth / group / localtask / timegoal / todo / file / bundled
161
239
  src/runtime/ 装配层:registry / spec / cli / api / server
162
240
  src/web/frontend/ React 壳
163
241
  assets/nx-kv/ 内置 skill:KV 清单操作手册(远程)
164
242
  assets/kv-waittask/ 内置 skill:对话内本地任务接力(等待侧)
165
243
  assets/kv-submit/ 内置 skill:需求提炼与委托提交(提交侧)
166
244
  assets/kv-localget/ 内置 skill:领取本目录本地任务(领取侧)
245
+ assets/kv-timegoal/ 内置 skill:持续 N 小时任务的时间目标与提示词回显
167
246
  ```
168
247
 
169
248
  分层由 `eslint.config.js` 的 `no-restricted-imports` 强制;
@@ -176,6 +255,7 @@ nx-kv skill install # 装到 ~/.claude/skills/nx-kv(KV 清单操作手册
176
255
  nx-kv skill install kv-waittask # 本地任务接力:最后一步阻塞等用户喂任务
177
256
  nx-kv skill install kv-submit # 提交侧:总结上下文、提炼需求,委托别的 agent
178
257
  nx-kv skill install kv-localget # 领取侧:主动领取排给本目录的任务
258
+ nx-kv skill install kv-timegoal # 持续 N 小时任务:每轮回显提示词原文、到点自动停手
179
259
  nx-kv skill list # 列出可装 skill、默认安装项与可装 group
180
260
  nx-kv skill groups # 列出 group → 包含哪些 skill
181
261
  nx-kv skill install --group=<key> # 一键装整组(与 <name> 二选一)
@@ -17,6 +17,10 @@
17
17
  "kv-localget": {
18
18
  "skills": ["kv-localget"],
19
19
  "summary": "领取排给本目录的本地任务 + 队列管理(领取侧)"
20
+ },
21
+ "kv-timegoal": {
22
+ "skills": ["kv-timegoal"],
23
+ "summary": "把「持续运行 N 小时」任务固化到 nx-kv:每轮回显原文提示词防偏移 + 时间到点自动停手"
20
24
  }
21
25
  }
22
26
  }
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: kv-timegoal
3
+ description: 把"AI 持续运行 N 小时(如 3 小时)"任务固化到 nx-kv 里——用 CLI 设置时间目标 + 完整全局提示词,每轮自动回显提示词原文避免长对话里 agent 漂移(防偏移),到点返回 reached:true,agent 据此停手收尾。timegoal 是独立模块(独立 store、独立循环、独立命令族),与 localtask wait 等任何其它机制都没有关系;UI 上嵌入在「本地任务」主题卡里只是为了方便查看,逻辑上完全不依赖。agent 可通过 `timegoal update` 自己迭代提示词、用户也可在 web 端动态编辑(都不重置计时器)。典型用法:设置 3 小时"调研开源 Redis 差距并逼近生产级数据库"任务;每轮调 subagent 找差距、写 CHANGELOG、调 update 改 prompt,调 check 看是否到点。触发词:timegoal、time goal、时间目标、3 小时、两小时、持续运行、定时任务、跑一晚上、强制 N 小时、N 小时任务、self-improve prompt、自我迭代提示词、agent 改自己的 prompt、到点自动结束、到点停手、循环任务、跑完为止、UX prompt 防漂移、防偏移。
4
+ ---
5
+
6
+ # kv-timegoal — 把"持续 N 小时任务"固化到 nx-kv
7
+
8
+ 一个带"到期硬出口"的循环:开头一次性 `set`,**完整全局提示词固化进 store**;
9
+ 每轮调 `check`,**拿到的 `prompt` 字段就是这轮的根指令**(永远从 store 原文回显,
10
+ 不让 agent 在长对话里漂移);`reached:true` 那轮**自动停手**,写最终总结退出。
11
+
12
+ ## 怎么用
13
+
14
+ ### 1. 开头 set 一次
15
+
16
+ ```bash
17
+ nx-kv timegoal set --cwd "$PWD" \
18
+ --duration 10800 \
19
+ --prompt "<完整全局提示词>" --json
20
+ ```
21
+
22
+ - `--duration` 持续秒数,**60-86400**(1 分钟到 24 小时)。例:3 小时 = `10800`
23
+ - `--prompt` 完整全局提示词,≤20000 字
24
+ - `--cwd` 绑定的目录(缺省用当前目录);**一个目录一个 timegoal**
25
+
26
+ 返回:
27
+
28
+ ```jsonc
29
+ {
30
+ "status": "ok",
31
+ "replaced": false, // 之前没设过;已有则 true(会重置起点)
32
+ "goal": {
33
+ "durationSec": 10800,
34
+ "startedAt": "2026-10-19 20:31:12",
35
+ "deadline": "2026-10-19 23:31:12", // = startedAt + duration
36
+ "prompt": "<完整提示词原文>" // 已固化
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 2. 每轮循环
42
+
43
+ ```
44
+ 每轮开始:
45
+ nx-kv timegoal check --cwd "$PWD" --json
46
+ → { remainingSec, prompt, ... }
47
+ → 把 prompt 字段当作本轮根指令(不要从对话历史里重新拼)
48
+
49
+ 干活(按 prompt 的指令:调 subagent、写代码、改 CHANGELOG 等)
50
+
51
+ 写一行本轮总结到 .timegoal-log.md(或 git CHANGELOG.md)
52
+
53
+ 如本轮调研发现 prompt 漏条件:
54
+ nx-kv timegoal update --cwd "$PWD" --prompt "<新版>" --json
55
+ (只换 prompt,截止时间不动)
56
+
57
+ 回到本轮开头
58
+
59
+ 拿到的 {reached: true} 那一轮:
60
+ 写完本轮总结 → 不再开新轮 → 退出本循环
61
+ ```
62
+
63
+ ### 3. check 的返回形状
64
+
65
+ ```jsonc
66
+ {
67
+ "hasGoal": true,
68
+ "reached": false, // true 即停手信号
69
+ "elapsedSec": 120, // 已用秒
70
+ "remainingSec": 10680, // 还剩秒
71
+ "startedAt": "2026-10-19 20:31:12",
72
+ "deadline": "2026-10-19 23:31:12",
73
+ "now": "2026-10-19 20:33:12",
74
+ "prompt": "<完整提示词原文>", // ← 这才是该轮的根指令
75
+ "cwd": "/path/to/workdir",
76
+ "key": "<目录 key>",
77
+ "name": "<目录名>"
78
+ }
79
+ ```
80
+
81
+ 边界:
82
+
83
+ - 没设过 timegoal → `{hasGoal:false, reached:false, prompt:""}`(需先 `set`)
84
+ - 到点后 `reached:true`,goal 对象仍在;可继续 `check` 看 prompt,直到 `clear`
85
+
86
+ ## 四条硬规则
87
+
88
+ 1. **一次 `set`,整个循环只看 `check` 的返回。** 不要再从对话历史里拼"原始 prompt"
89
+ —— 长对话里 agent 一定会自己改写、漂移、漏条件。**永远把 `check` 返回的
90
+ `prompt` 字段当作本轮根指令**。
91
+ 2. **想改 prompt 必须用 `timegoal update`,不要 `set`。** `set` 会**重置起点**
92
+ (一次小迭代把 3 小时清零!);`update` 保留起点——只换 prompt,或顺带
93
+ `--duration` 调整时长(deadline 相应平移,已用时间仍算数)。
94
+ 3. **`reached:true` 立刻停手。** 干完手头这步、最后写一遍最终总结到
95
+ `.timegoal-log.md`(或 git CHANGELOG.md),**不再开新轮**;这次循环结束。
96
+ 4. **每轮结尾必写一行总结**到 `<workdir>/.timegoal-log.md`(或 git CHANGELOG),
97
+ 格式自定,目的是"失败也能复盘":时间 / 干了什么 / 下一步计划。
98
+
99
+ ## 完整命令清单
100
+
101
+ ```bash
102
+ nx-kv timegoal set --cwd <dir> --duration <秒> --prompt "<...>" # 设/换(新起点)
103
+ nx-kv timegoal update --cwd <dir> --prompt "<新版>" [--duration <秒>] # 改提示词/时长(保起点)
104
+ nx-kv timegoal show --cwd <dir> # 仅查(不算时间)
105
+ nx-kv timegoal check --cwd <dir> # 算时间 + 回显 prompt
106
+ nx-kv timegoal clear --cwd <dir> # 清空
107
+ nx-kv timegoal list # 所有有 timegoal 的目录
108
+ ```
109
+
110
+ 全部命令都支持 `--json`(推荐 agent 用)。
111
+
112
+ `update` 与 `set` 的区别:
113
+
114
+ | | `set` | `update` |
115
+ | --- | --- | --- |
116
+ | 起点 `startedAt` | **重置**(计时从头算) | **保留**(已用时间仍算数) |
117
+ | 时长 | 必填 | 可选(省略=不变) |
118
+ | prompt | 必填 | 必填 |
119
+ | 用途 | 开一局新的 | 迭代提示词 / 调整时长 |
120
+
121
+ `update` 改时长时:`deadline = startedAt + 新时长`。
122
+
123
+ ## Web 端操作
124
+
125
+ 面板「本地任务」页有两处入口(都是**纯 UI 便利**,数据与逻辑仍属 timegoal 模块):
126
+
127
+ **1. 顶部「时间目标」汇总卡** —— 列出所有目录的 timegoal(包括还没在看板里出现过
128
+ 的目录):
129
+
130
+ - **新建**:填目录 + 时长 + 提示词 → 创建
131
+ - **编辑**:改时长 / 提示词;默认**保留计时起点**,勾「重置起点」才从头计
132
+ - **清空**:删除该目录的 timegoal
133
+ - 状态列显示 `剩 mm:ss` 或 `已到点`,每 1.5s 自动刷新
134
+
135
+ **2. 主题卡里的 timegoal 行**:
136
+
137
+ - 已设置 → `时间目标剩余 mm:ss` / `已到点(用时 ...)` +「查看提示词 / 编辑 / 清空」
138
+ - 未设置 → 「未设置时间目标」+「设置」(为该目录新建)
139
+
140
+ 时长输入带快捷按钮(30 分钟 / 1 / 2 / 3 / 8 小时)。
141
+
142
+ ## 与其它机制的关系
143
+
144
+ **没有依赖关系。** timegoal 只做两件事:算时间、回显提示词原文。
145
+
146
+ - 不调用 `localtask wait`:等不到任务是别的事,与是否到点无关
147
+ - 不阻塞任何东西:可以同时跑人机接力、派任务给别的 agent、外部 cron
148
+ - 不需要登录 KV 后端:纯本机
149
+
150
+ 你可以把它当成一个独立计时器 + 便签本:`set` 把便签写进文件,`check` 读出来
151
+ 顺便告诉你还剩多少时间。
152
+
153
+ ## 安装
154
+
155
+ ```bash
156
+ nx-kv skill install kv-timegoal
157
+ ```
158
+
159
+ 要详细时序与一份可抄走的 prompt 模板("调研开源 Redis 差距并逼近生产级数据库"
160
+ 那种 3 小时任务的范例),读 `references/workflow.md`(按需加载,不查时序不必读)。
@@ -0,0 +1,151 @@
1
+ # kv-timegoal · 时序与完整示例
2
+
3
+ 按需加载:只是想看命令的话回到 [[kv-timegoal|SKILL.md]],不必读这份。
4
+ 下面给一份能复刻的"3 小时调研 Redis 差距并自迭代实现生产级数据库"任务
5
+ 完整时序,含可直接抄走的 prompt 模板。
6
+
7
+ ---
8
+
9
+ ## 完整时序(一次 timegoal 循环长什么样)
10
+
11
+ ```
12
+ ┌──────────────────┐
13
+ │ ~/.nx-kv/ │
14
+ │ timegoals.json │
15
+ │ (独立存储) │
16
+ └──────────────────┘
17
+ ▲
18
+ │ set/update/check/clear
19
+ │ 都只读写这一个文件
20
+ │
21
+ ┌─────────┐ ▼
22
+ │ user │ cli: nx-kv timegoal <...> ──────►─┐
23
+ │ (人) │ │
24
+ │ │ web: 时间目标 主题卡里内嵌显示 │ ┌──────────────┐
25
+ │ │ (逻辑上无关) │ │ nx-kv │
26
+ └─────────┘ │ │ timegoal │
27
+ │ │ module │
28
+ │ │ (独立模块) │
29
+ └──────┬───────┘
30
+ │
31
+ ┌─────────────┐ ▼
32
+ │ agent │ cli: nx-kv timegoal set/update/check (事实源)
33
+ │ (LLM) │ ★ agent 唯一读取指令来源 = check 返回的 prompt 字段
34
+ └──────┬──────┘
35
+ │
36
+ │ 每一轮:
37
+ ▼
38
+ ┌───────────────────────────────────────────────────────┐
39
+ │ 1. nx-kv timegoal check --cwd "$PWD" --json │
40
+ │ ↓ 拿到 {remainingSec, prompt} │
41
+ │ 2. 把 prompt 字段当本轮根指令 │
42
+ │ 3. 干活(按 prompt 指令): │
43
+ │ · 调 subagent 调研差距、产出最小补丁 + 测试 │
44
+ │ · 把本轮成果追加到 CHANGELOG.md 与 .timegoal-log.md │
45
+ │ 4. 如本轮调研发现 prompt 漏条件: │
46
+ │ nx-kv timegoal update --prompt "<V2>" --json │
47
+ │ (**只换 prompt、截止时间不动**) │
48
+ │ 5. 回到第 1 步 │
49
+ └───────────────────────────────────────────────────────┘
50
+ │
51
+ ▼
52
+ ┌───────────────────────────────────────────────────────┐
53
+ │ 拿到的 {reached: true} 那一轮: │
54
+ │ · 写完本轮总结 │
55
+ │ · 不再开新轮、不再 check(除非写最终总结查 prompt) │
56
+ │ · 退出本循环 │
57
+ └───────────────────────────────────────────────────────┘
58
+ ```
59
+
60
+ **关键**:timegoal 循环里**只有 `timegoal` 命令族**,没有 `localtask wait`、
61
+ 没有别的 nx-kv 命令。可以把它单独跑、可以并行跑——互不感知。
62
+
63
+ ## 完整可抄走的 prompt 模板("调研 Redis 差距并逼近生产级数据库")
64
+
65
+ ```text
66
+ 你是「Redis-compatible 生产级数据库」的迭代研发 agent;目标是用 N 小时
67
+ (到 set 的 deadline 为止)持续调研当前项目与开源 Redis 的能力差距,
68
+ 逐轮修复差距、把改进写到 CHANGELOG.md,并自我迭代本 prompt 让下一轮更高效。
69
+
70
+ 每轮你必须做:
71
+ 1. 调 subagent(general-purpose)对比「当前项目 + 上一轮改进」与「开源
72
+ Redis 最新稳定版」的能力清单,输出 1-3 个本轮最高 ROI 的差距修复项。
73
+ 2. 对每项写出最小可工作补丁 + 测试,写完跑测试;失败的子任务要在下一轮
74
+ 优先重试。
75
+ 3. 把本轮成果追加一行到 CHANGELOG.md(格式:「- [YYYY-MM-DD HH:mm] 干了什么」)。
76
+ 4. 把本轮总结写到 .timegoal-log.md 的同一行尾(每轮一行)。
77
+ 5. 如果 subagent 给出"prompt 优化建议":调 `nx-kv timegoal update --cwd "$PWD"
78
+ --prompt "<新版 V(N+1)>"` 把下一轮的 prompt 替换掉。**不要调 set**——
79
+ set 会把已用时间清零。
80
+
81
+ 硬性约束:
82
+ - 严禁超过本轮剩余时间预算(remainingSec < 30 时立刻停手、写总结、不开新轮)
83
+ - 严禁自己改 deadline;想缩短就 `timegoal clear` 再 `timegoal set`
84
+ - 每轮开始时先 `nx-kv timegoal check --cwd "$PWD" --json`,把返回里的
85
+ `prompt` 字段当作本轮根指令;**不要从对话历史里重新拼**——以 store 为准
86
+ - 写代码必须随附测试;测试失败的修补不计入本轮成果
87
+ ```
88
+
89
+ 把这个 prompt 一次性 `timegoal set` 进 store,agent 之后每一轮照着干到点为止。
90
+
91
+ ## 实际跑(手工模拟)
92
+
93
+ ```bash
94
+ WORKDIR=/path/to/your-project
95
+ cd "$WORKDIR"
96
+
97
+ # 1. set
98
+ nx-kv timegoal set --cwd "$PWD" --duration 10800 \
99
+ --prompt "..." --json
100
+
101
+ # 2. 立刻 check:reached:false
102
+ nx-kv timegoal check --cwd "$PWD" --json
103
+
104
+ # 3. sleep 6s
105
+ sleep 6
106
+
107
+ # 4. 再 check:注意 elapsedSec 增加
108
+ nx-kv timegoal check --cwd "$PWD" --json
109
+
110
+ # 5. update:只换 prompt,deadline 不动
111
+ nx-kv timegoal update --cwd "$PWD" --prompt "V2 改得更简洁" --json
112
+
113
+ # 6. check:prompt 已是 V2,deadline 仍是 1 小时后
114
+ nx-kv timegoal check --cwd "$PWD" --json
115
+
116
+ # 7. 等到点(或手动改 store)后:reached:true
117
+ # (实操:把 ~/.nx-kv/timegoals.json 里 startedAt 改成 4 小时前)
118
+
119
+ # 8. clear
120
+ nx-kv timegoal clear --cwd "$PWD" --json
121
+
122
+ # 全程不曾、也不需调过 localtask / todo / 等任何其它命令族
123
+ ```
124
+
125
+ ## 常见问题
126
+
127
+ | 现象 | 原因 / 做法 |
128
+ | --- | --- |
129
+ | `reached` 没翻 | 检查客户端时区或 set 时是否给了 `--duration` 过短(默认下限 60s) |
130
+ | update 失败"主题「X」尚未设置 timegoal" | 先 `set` 再 `update`;`update` 不创建 |
131
+ | clear 返回"无需清空" | 本来就没设过;这是 info 不是 error |
132
+ | 想换目录 | clear 旧的 → set 新的;目录(cwd)是绑定的 |
133
+ | 想中途换 task queue | **与 timegoal 无关**——本模块不参与队列,那是其它事 |
134
+ | 想同时用 localtask 等用户 | **可以同时跑**——两套独立循环、各管各的;timegoal 只关心自身 deadline |
135
+
136
+ ## timegoal 的运行边界
137
+
138
+ ```
139
+ timegoal 循环里只有 timegoal 命令族——没有别的 nx-kv 命令参与。
140
+
141
+ 跑 100 轮 timegoal 不需要任何其它机制配合;
142
+ 反过来,跑别的东西也不需要 timegoal 配合。
143
+
144
+ · timegoal 只做两件事:算时间、回显提示词原文
145
+ · 不阻塞任何东西——可以同时跑人机接力、派任务给别的 agent、外部 cron
146
+ · 不需要登录 KV 后端——纯本机
147
+ · 数据落在本机文件(timegoals.json),可由环境变量 NX_KV_TIMEGOAL_STORE 改路径
148
+ ```
149
+
150
+ **唯一的交互面**是 CLI 与 web 主题卡上的三个按钮(查看 / 编辑 / 清空),
151
+ 没有任何隐式联动。