nx-kv 0.1.1 → 0.2.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.
- package/CHANGELOG.md +100 -48
- package/README.md +38 -22
- package/assets/nx-kv/SKILL.md +21 -18
- package/assets/nx-kv/references/agent-workflow.md +9 -7
- package/assets/nx-kv/references/todo-commands.md +44 -20
- package/package.json +1 -1
- package/src/core/paths.js +1 -1
- package/src/modules/todo/index.js +288 -280
- package/src/modules/todo/service.js +61 -47
- package/src/modules/todo/view.jsx +285 -280
- package/src/runtime/cli.js +300 -299
- package/src/runtime/registry.js +11 -0
- package/src/runtime/spec.js +34 -8
- package/src/web/frontend/App.jsx +1 -1
- package/src/web/frontend/index.html +4 -0
- package/src/web/frontend/style.css +2 -1
- package/src/web/public/assets/{CliHints-3H3KOoDZ.js → CliHints-Bc3Me3vO.js} +1 -1
- package/src/web/public/assets/index-CmCYxz0e.css +1 -0
- package/src/web/public/assets/{index-jhTtYCb9.js → index-Dv9g9jmR.js} +2 -2
- package/src/web/public/assets/{view-CrL-DSJu.js → view-B8AnDK0i.js} +1 -1
- package/src/web/public/assets/{view-Du7Wv_-h.js → view-BKTVRs9U.js} +1 -1
- package/src/web/public/assets/view-Cdi5TUV1.js +2 -0
- package/src/web/public/index.html +6 -2
- package/src/web/public/assets/index-BnWex75m.css +0 -1
- package/src/web/public/assets/view-S0N5g6v1.js +0 -2
|
@@ -1,280 +1,288 @@
|
|
|
1
|
-
// 清单模块:四把 key(open / done / freeze / topics)上的任务管理。
|
|
2
|
-
//
|
|
3
|
-
// 声明了 `resource: 'todo'`,因此必须齐备 CRUD 五操作且两端可调用 ——
|
|
4
|
-
// tests/unit/registry.test.mjs 会据此断言。非 CRUD 的动作(完成/冻结/归档/topic/prompt)
|
|
5
|
-
// 用动作动词命名,不受五动词约束,但同样两端可达。
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
},
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
1
|
+
// 清单模块:四把 key(open / done / freeze / topics)上的任务管理。
|
|
2
|
+
//
|
|
3
|
+
// 声明了 `resource: 'todo'`,因此必须齐备 CRUD 五操作且两端可调用 ——
|
|
4
|
+
// tests/unit/registry.test.mjs 会据此断言。非 CRUD 的动作(完成/冻结/归档/topic/prompt)
|
|
5
|
+
// 用动作动词命名,不受五动词约束,但同样两端可达。
|
|
6
|
+
//
|
|
7
|
+
// ⚠️ 定位一律用**内容**(`--ref`),不要用 id。
|
|
8
|
+
//
|
|
9
|
+
// id 只是给人看的元数据:分配只扫「待办 + 冻结」,所以它会被复用;
|
|
10
|
+
// 同一主题里也本来就有内容重复的任务。按 id 定位迟早改错数据。
|
|
11
|
+
// 详见 service.js 的「定位」一节。
|
|
12
|
+
import * as service from './service.js';
|
|
13
|
+
|
|
14
|
+
// 所有动作都接受 --group 覆盖当前工作空间;默认取本机配置里的当前值
|
|
15
|
+
const GROUP = { type: 'number', hint: '工作空间 id(默认取当前)' };
|
|
16
|
+
|
|
17
|
+
// 完成时间显示:只取到分钟,表格里塞不下完整时间戳
|
|
18
|
+
const shortTime = (s) => (s ? String(s).slice(0, 16).replace('T', ' ') : '');
|
|
19
|
+
|
|
20
|
+
function renderTaskList(d) {
|
|
21
|
+
const groups = [
|
|
22
|
+
['open', '待办'],
|
|
23
|
+
['done', '已完成'],
|
|
24
|
+
['freeze', '冻结'],
|
|
25
|
+
].filter(([k]) => d[k]);
|
|
26
|
+
|
|
27
|
+
const lines = [];
|
|
28
|
+
if (d.topic) lines.push(`topic=${d.topic}`);
|
|
29
|
+
for (const [key, label] of groups) {
|
|
30
|
+
const items = d[key];
|
|
31
|
+
if (!items) continue;
|
|
32
|
+
if (!items.length) {
|
|
33
|
+
lines.push(`${label}: (空)`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
lines.push(`${label}(${items.length}):`);
|
|
37
|
+
const w = Math.max(...items.map((t) => String(t.id).length));
|
|
38
|
+
for (const t of items) {
|
|
39
|
+
const mark = key === 'done' ? `✓ ${shortTime(t.doneAt)}` : key === 'freeze' ? `❄ ${shortTime(t.frozenAt)}` : '';
|
|
40
|
+
// id 只作展示位(对齐列的宽度由它决定),**操作时不要用它定位** ——
|
|
41
|
+
// 定位一律用 --ref <内容>,见文件头的说明。
|
|
42
|
+
lines.push(` ${String(t.id).padStart(w)} [${t.topic}] ${t.text}${mark ? ' ' + mark : ''}`);
|
|
43
|
+
if (t.note) lines.push(` ${' '.repeat(w)} note: ${t.note.slice(0, 100)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return lines.join('\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const renderTask = (t) =>
|
|
50
|
+
[
|
|
51
|
+
`[${t.topic}] ${t.text}`,
|
|
52
|
+
` 状态: ${t.doneAt ? '已完成' : t.frozenAt ? '冻结' : '待办'}${t.bucket ? `(在 ${t.bucket})` : ''}`,
|
|
53
|
+
` 创建: ${t.createdAt}`,
|
|
54
|
+
t.doneAt ? ` 完成: ${t.doneAt}` : '',
|
|
55
|
+
t.frozenAt ? ` 冻结: ${t.frozenAt}` : '',
|
|
56
|
+
t.note ? ` 备注: ${t.note}` : '',
|
|
57
|
+
]
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join('\n');
|
|
60
|
+
|
|
61
|
+
export default {
|
|
62
|
+
id: 'todo',
|
|
63
|
+
title: '清单(= Web「清单」页)',
|
|
64
|
+
order: 30,
|
|
65
|
+
view: () => import('./view.jsx'),
|
|
66
|
+
|
|
67
|
+
// CRUD 资源声明:任务的增删查改必须齐备,且 CLI 与面板都能调用
|
|
68
|
+
resource: 'todo',
|
|
69
|
+
|
|
70
|
+
actions: [
|
|
71
|
+
// ─── CRUD 五操作 ───────────────────────────────────────────
|
|
72
|
+
{
|
|
73
|
+
id: 'todo.list',
|
|
74
|
+
cli: ['todo', 'list'],
|
|
75
|
+
http: ['GET', '/api/todo'],
|
|
76
|
+
summary: '列出任务(--topic / --status 过滤;默认三个桶全列)',
|
|
77
|
+
flags: {
|
|
78
|
+
topic: { type: 'string', hint: '只看某个主题' },
|
|
79
|
+
status: { type: 'string', enum: ['open', 'done', 'freeze', 'all'], default: 'all' },
|
|
80
|
+
group: GROUP,
|
|
81
|
+
},
|
|
82
|
+
run: (ctx) => service.listTodos({ topic: ctx.topic, status: ctx.status, groupId: ctx.group }),
|
|
83
|
+
render: renderTaskList,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'todo.get',
|
|
87
|
+
cli: ['todo', 'get'],
|
|
88
|
+
http: ['GET', '/api/todo/item'],
|
|
89
|
+
summary: '查看单个任务(按内容定位;自动在待办/已完成/冻结里找)',
|
|
90
|
+
flags: {
|
|
91
|
+
ref: { type: 'string', required: true, hint: '任务内容(整串)' },
|
|
92
|
+
topic: { type: 'string', hint: '消歧:同内容有多条时用主题收窄' },
|
|
93
|
+
pick: { type: 'number', hint: '消歧:同内容多条时按编号选(配合报错里的列表)' },
|
|
94
|
+
group: GROUP,
|
|
95
|
+
},
|
|
96
|
+
run: (ctx) => service.getTodo(ctx.ref, { topic: ctx.topic, pick: ctx.pick, groupId: ctx.group }),
|
|
97
|
+
render: renderTask,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'todo.add',
|
|
101
|
+
cli: ['todo', 'add'],
|
|
102
|
+
http: ['POST', '/api/todo'],
|
|
103
|
+
summary: '新增任务到待办(topic 不在快捷列表时自动补上)',
|
|
104
|
+
args: ['text'],
|
|
105
|
+
flags: { topic: { type: 'string', required: true, hint: '主题,路由维度' }, group: GROUP },
|
|
106
|
+
run: (ctx) => service.addTodo({ topic: ctx.topic, text: ctx.text, groupId: ctx.group }),
|
|
107
|
+
render: (d) =>
|
|
108
|
+
`已加入待办: [${d.task.topic}] ${d.task.text}` +
|
|
109
|
+
(d.topicAdded ? `\n(已把 ${d.task.topic} 加入快捷 topic)` : ''),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'todo.update',
|
|
113
|
+
cli: ['todo', 'update'],
|
|
114
|
+
http: ['PATCH', '/api/todo/item'],
|
|
115
|
+
summary: '编辑任务(按内容定位,只改传入的字段)',
|
|
116
|
+
flags: {
|
|
117
|
+
ref: { type: 'string', required: true, hint: '要改的那条的内容(整串)' },
|
|
118
|
+
topic: { type: 'string', hint: '改成这个主题' },
|
|
119
|
+
text: { type: 'string', hint: '改成这个内容' },
|
|
120
|
+
note: { type: 'string' },
|
|
121
|
+
matchTopic: { type: 'string', hint: '消歧:同内容有多条时用主题收窄' },
|
|
122
|
+
pick: { type: 'number', hint: '消歧:同内容多条时按编号选(配合报错里的列表)' },
|
|
123
|
+
group: GROUP,
|
|
124
|
+
},
|
|
125
|
+
run: (ctx) =>
|
|
126
|
+
service.updateTodo(ctx.ref, {
|
|
127
|
+
topic: ctx.topic,
|
|
128
|
+
text: ctx.text,
|
|
129
|
+
note: ctx.note,
|
|
130
|
+
matchTopic: ctx.matchTopic,
|
|
131
|
+
pick: ctx.pick,
|
|
132
|
+
groupId: ctx.group,
|
|
133
|
+
}),
|
|
134
|
+
render: (d) => `已更新(在 ${d.bucket}): [${d.task.topic}] ${d.task.text}`,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'todo.remove',
|
|
138
|
+
cli: ['todo', 'remove'],
|
|
139
|
+
http: ['DELETE', '/api/todo/item'],
|
|
140
|
+
summary: '删除任务(按内容定位,只删命中的那一条)',
|
|
141
|
+
flags: {
|
|
142
|
+
ref: { type: 'string', required: true, hint: '要删的那条的内容(整串)' },
|
|
143
|
+
topic: { type: 'string', hint: '消歧:同内容有多条时用主题收窄' },
|
|
144
|
+
pick: { type: 'number', hint: '消歧:同内容多条时按编号选(配合报错里的列表)' },
|
|
145
|
+
group: GROUP,
|
|
146
|
+
},
|
|
147
|
+
run: (ctx) => service.removeTodo(ctx.ref, { topic: ctx.topic, pick: ctx.pick, groupId: ctx.group }),
|
|
148
|
+
render: (d) => `已删除 [${d.removed.topic}] ${d.removed.text}`,
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// ─── 状态流转 ──────────────────────────────────────────────
|
|
152
|
+
{
|
|
153
|
+
id: 'todo.done',
|
|
154
|
+
cli: ['todo', 'done'],
|
|
155
|
+
http: ['POST', '/api/todo/done'],
|
|
156
|
+
summary: '标记完成(--ref 定位;--result 写进 note 作为完成结果)',
|
|
157
|
+
flags: {
|
|
158
|
+
ref: { type: 'string', required: true, hint: '要完成的那条的内容(整串)' },
|
|
159
|
+
result: { type: 'string', hint: '完成结果摘要' },
|
|
160
|
+
topic: { type: 'string', hint: '消歧:同内容有多条时用主题收窄' },
|
|
161
|
+
pick: { type: 'number', hint: '消歧:同内容多条时按编号选(配合报错里的列表)' },
|
|
162
|
+
group: GROUP,
|
|
163
|
+
},
|
|
164
|
+
run: (ctx) =>
|
|
165
|
+
service.doneTodo(ctx.ref, { result: ctx.result, topic: ctx.topic, pick: ctx.pick, groupId: ctx.group }),
|
|
166
|
+
render: (d) => `已完成 [${d.task.topic}] ${d.task.text}\n ${d.task.doneAt}`,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'todo.freeze',
|
|
170
|
+
cli: ['todo', 'freeze'],
|
|
171
|
+
http: ['POST', '/api/todo/freeze'],
|
|
172
|
+
summary: '冻结任务(按内容定位;id 保留,解冻时校验冲突)',
|
|
173
|
+
flags: {
|
|
174
|
+
ref: { type: 'string', required: true, hint: '要冻结的那条的内容(整串)' },
|
|
175
|
+
topic: { type: 'string', hint: '消歧:同内容有多条时用主题收窄' },
|
|
176
|
+
pick: { type: 'number', hint: '消歧:同内容多条时按编号选(配合报错里的列表)' },
|
|
177
|
+
group: GROUP,
|
|
178
|
+
},
|
|
179
|
+
run: (ctx) => service.freezeTodo(ctx.ref, { topic: ctx.topic, pick: ctx.pick, groupId: ctx.group }),
|
|
180
|
+
render: (d) => `已冻结 [${d.task.topic}] ${d.task.text}`,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'todo.unfreeze',
|
|
184
|
+
cli: ['todo', 'unfreeze'],
|
|
185
|
+
http: ['POST', '/api/todo/unfreeze'],
|
|
186
|
+
summary: '解冻回待办(按内容定位;id 撞车时自动换新 id)',
|
|
187
|
+
flags: {
|
|
188
|
+
ref: { type: 'string', required: true, hint: '要解冻的那条的内容(整串)' },
|
|
189
|
+
topic: { type: 'string', hint: '消歧:同内容有多条时用主题收窄' },
|
|
190
|
+
pick: { type: 'number', hint: '消歧:同内容多条时按编号选(配合报错里的列表)' },
|
|
191
|
+
group: GROUP,
|
|
192
|
+
},
|
|
193
|
+
run: (ctx) => service.unfreezeTodo(ctx.ref, { topic: ctx.topic, pick: ctx.pick, groupId: ctx.group }),
|
|
194
|
+
render: (d) =>
|
|
195
|
+
`已解冻 [${d.task.topic}] ${d.task.text}` +
|
|
196
|
+
(d.reIded ? '\n(原 id 与现有待办冲突,已分配新 id)' : ''),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'todo.archive',
|
|
200
|
+
cli: ['todo', 'archive'],
|
|
201
|
+
http: ['POST', '/api/todo/archive'],
|
|
202
|
+
summary: '把已完成里较旧的条目归档到冷 key(默认 30 天前)',
|
|
203
|
+
flags: {
|
|
204
|
+
before: { type: 'string', hint: '截止日期,如 2026-08-01' },
|
|
205
|
+
group: GROUP,
|
|
206
|
+
},
|
|
207
|
+
run: (ctx) => service.archiveDone({ before: ctx.before, groupId: ctx.group }),
|
|
208
|
+
render: (d) =>
|
|
209
|
+
d.moved
|
|
210
|
+
? `已归档 ${d.moved} 条 -> ${d.coldKey}(已完成剩 ${d.remaining} 条,冷 key 共 ${d.total} 条)`
|
|
211
|
+
: '没有需要归档的条目',
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// ─── 快捷 topic(动作集合,非 CRUD)───────────────────────
|
|
215
|
+
{
|
|
216
|
+
id: 'topic.list',
|
|
217
|
+
cli: ['topic', 'list'],
|
|
218
|
+
http: ['GET', '/api/topics'],
|
|
219
|
+
summary: '快捷主题列表(含各状态任务数)',
|
|
220
|
+
flags: { group: GROUP },
|
|
221
|
+
run: (ctx) => service.listTopics({ groupId: ctx.group }),
|
|
222
|
+
render: (list) => {
|
|
223
|
+
if (!list.length) return '(暂无快捷主题)';
|
|
224
|
+
const w = Math.max(...list.map((t) => t.name.length));
|
|
225
|
+
return list
|
|
226
|
+
.map((t) => `${t.name.padEnd(w)} 待办 ${t.open} 完成 ${t.done} 冻结 ${t.freeze}`)
|
|
227
|
+
.join('\n');
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: 'topic.add',
|
|
232
|
+
cli: ['topic', 'add'],
|
|
233
|
+
http: ['POST', '/api/topics'],
|
|
234
|
+
summary: '加入快捷主题',
|
|
235
|
+
args: ['name'],
|
|
236
|
+
flags: { group: GROUP },
|
|
237
|
+
run: (ctx) => service.addTopic(ctx.name, { groupId: ctx.group }),
|
|
238
|
+
render: (d) => (d.added ? `已加入快捷主题: ${d.name}` : `${d.name} 已在快捷列表中`),
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: 'topic.remove',
|
|
242
|
+
cli: ['topic', 'remove'],
|
|
243
|
+
http: ['DELETE', '/api/topics'],
|
|
244
|
+
summary: '移出快捷主题(不动已有任务;仍被任务使用时会在结果里提示)',
|
|
245
|
+
args: ['name'],
|
|
246
|
+
flags: { group: GROUP },
|
|
247
|
+
run: (ctx) => service.removeTopic(ctx.name, { groupId: ctx.group }),
|
|
248
|
+
render: (d) => {
|
|
249
|
+
if (!d.removed) return `${d.name} 本就不在快捷列表中`;
|
|
250
|
+
return d.inUse
|
|
251
|
+
? `已移出快捷主题: ${d.name}\n注意: 仍有 ${d.inUse} 条任务在用它,主题名不会消失,只是不再出现在候选里`
|
|
252
|
+
: `已移出快捷主题: ${d.name}`;
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// ─── 主题提示词(给 agent 的上下文)───────────────────────
|
|
257
|
+
{
|
|
258
|
+
id: 'prompt.get',
|
|
259
|
+
cli: ['prompt', 'get'],
|
|
260
|
+
http: ['GET', '/api/prompts/:topic'],
|
|
261
|
+
summary: '读取某主题的上下文提示词',
|
|
262
|
+
args: ['topic'],
|
|
263
|
+
flags: { group: GROUP },
|
|
264
|
+
run: (ctx) => service.getPrompt(ctx.topic, { groupId: ctx.group }),
|
|
265
|
+
render: (d) => (d.hasPrompt ? d.prompt : `(${d.topic} 没有配置提示词)`),
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
id: 'prompt.set',
|
|
269
|
+
cli: ['prompt', 'set'],
|
|
270
|
+
http: ['POST', '/api/prompts/:topic'],
|
|
271
|
+
summary: '写入某主题的上下文提示词(覆盖)',
|
|
272
|
+
args: ['topic', 'text'],
|
|
273
|
+
flags: { group: GROUP },
|
|
274
|
+
run: (ctx) => service.setPrompt(ctx.topic, ctx.text, { groupId: ctx.group }),
|
|
275
|
+
render: (d) => `已写入 ${d.topic} 的提示词(${d.bytes} 字节)`,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'prompt.remove',
|
|
279
|
+
cli: ['prompt', 'remove'],
|
|
280
|
+
http: ['DELETE', '/api/prompts/:topic'],
|
|
281
|
+
summary: '删除某主题的提示词',
|
|
282
|
+
args: ['topic'],
|
|
283
|
+
flags: { group: GROUP },
|
|
284
|
+
run: (ctx) => service.removePrompt(ctx.topic, { groupId: ctx.group }),
|
|
285
|
+
render: (d) => (d.removed ? `已删除 ${d.topic} 的提示词` : `${d.topic} 本就没有提示词`),
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
};
|