codeksei 0.1.0 → 0.1.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 (68) hide show
  1. package/LICENSE +661 -661
  2. package/README.en.md +109 -47
  3. package/README.md +79 -58
  4. package/bin/cyberboss.js +1 -1
  5. package/package.json +86 -86
  6. package/scripts/open_shared_wechat_thread.sh +77 -77
  7. package/scripts/open_wechat_thread.sh +108 -108
  8. package/scripts/shared-common.js +144 -144
  9. package/scripts/shared-open.js +14 -14
  10. package/scripts/shared-start.js +5 -5
  11. package/scripts/shared-status.js +27 -27
  12. package/scripts/show_shared_status.sh +45 -45
  13. package/scripts/start_shared_app_server.sh +52 -52
  14. package/scripts/start_shared_wechat.sh +94 -94
  15. package/scripts/timeline-screenshot.sh +14 -14
  16. package/src/adapters/channel/weixin/account-store.js +99 -99
  17. package/src/adapters/channel/weixin/api-v2.js +50 -50
  18. package/src/adapters/channel/weixin/api.js +169 -169
  19. package/src/adapters/channel/weixin/context-token-store.js +84 -84
  20. package/src/adapters/channel/weixin/index.js +618 -604
  21. package/src/adapters/channel/weixin/legacy.js +579 -566
  22. package/src/adapters/channel/weixin/media-mime.js +22 -22
  23. package/src/adapters/channel/weixin/media-receive.js +370 -370
  24. package/src/adapters/channel/weixin/media-send.js +102 -102
  25. package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
  26. package/src/adapters/channel/weixin/message-utils.js +199 -199
  27. package/src/adapters/channel/weixin/redact.js +41 -41
  28. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
  29. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
  30. package/src/adapters/runtime/codex/events.js +215 -215
  31. package/src/adapters/runtime/codex/index.js +109 -104
  32. package/src/adapters/runtime/codex/message-utils.js +95 -95
  33. package/src/adapters/runtime/codex/model-catalog.js +106 -106
  34. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
  35. package/src/adapters/runtime/codex/rpc-client.js +339 -339
  36. package/src/adapters/runtime/codex/session-store.js +286 -286
  37. package/src/app/channel-send-file-cli.js +57 -57
  38. package/src/app/diary-write-cli.js +236 -88
  39. package/src/app/note-sync-cli.js +2 -2
  40. package/src/app/reminder-write-cli.js +215 -210
  41. package/src/app/review-cli.js +7 -5
  42. package/src/app/system-checkin-poller.js +64 -64
  43. package/src/app/system-send-cli.js +129 -129
  44. package/src/app/timeline-event-cli.js +28 -25
  45. package/src/app/timeline-screenshot-cli.js +103 -100
  46. package/src/core/app.js +1763 -1763
  47. package/src/core/branding.js +2 -1
  48. package/src/core/command-registry.js +381 -369
  49. package/src/core/config.js +30 -14
  50. package/src/core/default-targets.js +163 -163
  51. package/src/core/durable-note-schema.js +9 -8
  52. package/src/core/instructions-template.js +17 -16
  53. package/src/core/note-sync.js +8 -7
  54. package/src/core/path-utils.js +54 -0
  55. package/src/core/project-radar.js +11 -10
  56. package/src/core/review.js +48 -50
  57. package/src/core/stream-delivery.js +1162 -983
  58. package/src/core/system-message-dispatcher.js +68 -68
  59. package/src/core/system-message-queue-store.js +128 -128
  60. package/src/core/thread-state-store.js +96 -96
  61. package/src/core/timeline-screenshot-queue-store.js +134 -134
  62. package/src/core/timezone.js +436 -0
  63. package/src/core/workspace-bootstrap.js +9 -1
  64. package/src/index.js +148 -146
  65. package/src/integrations/timeline/index.js +130 -74
  66. package/src/integrations/timeline/state-sync.js +240 -0
  67. package/templates/weixin-instructions.md +12 -38
  68. package/templates/weixin-operations.md +29 -31
@@ -1,93 +1,93 @@
1
- const COMMAND_GROUPS = [
2
- {
3
- id: "lifecycle",
4
- label: "启动与诊断",
5
- actions: [
6
- {
7
- action: "app.login",
8
- summary: "发起微信扫码登录并保存账号",
9
- terminal: ["login"],
10
- weixin: [],
11
- status: "active",
12
- },
13
- {
14
- action: "app.accounts",
15
- summary: "查看本地已保存账号",
16
- terminal: ["accounts"],
17
- weixin: [],
18
- status: "active",
19
- },
20
- {
21
- action: "app.start",
22
- summary: "启动当前 channel/runtime 主循环",
23
- terminal: ["start"],
24
- weixin: [],
25
- status: "active",
26
- },
27
- {
28
- action: "app.shared_start",
29
- summary: "启动共享 app-server 与共享微信桥接",
30
- terminal: ["shared start"],
31
- weixin: [],
32
- status: "active",
33
- },
34
- {
35
- action: "app.shared_open",
36
- summary: "接入当前微信绑定的共享线程",
37
- terminal: ["shared open"],
38
- weixin: [],
39
- status: "active",
40
- },
41
- {
42
- action: "app.shared_status",
43
- summary: "查看共享 app-server 与共享桥接状态",
44
- terminal: ["shared status"],
45
- weixin: [],
46
- status: "active",
47
- },
48
- {
49
- action: "app.doctor",
50
- summary: "打印当前配置、边界和线程状态",
51
- terminal: ["doctor"],
52
- weixin: [],
53
- status: "active",
54
- },
55
- {
56
- action: "system.send",
57
- summary: "向内部系统队列写入一条不可见触发消息",
58
- terminal: ["system send"],
59
- terminalGroup: "system",
60
- weixin: [],
61
- status: "active",
62
- },
63
- {
64
- action: "system.checkin_poller",
65
- summary: "按随机间隔写入主动 check-in 触发",
66
- terminal: ["system checkin-poller"],
67
- terminalGroup: "system",
68
- weixin: [],
69
- status: "active",
70
- },
71
- ],
72
- },
73
- {
74
- id: "workspace",
75
- label: "项目与线程",
76
- actions: [
77
- {
78
- action: "workspace.bind",
79
- summary: "绑定当前聊天使用的项目目录",
80
- terminal: [],
81
- weixin: ["/bind"],
82
- status: "active",
83
- },
84
- {
85
- action: "workspace.status",
86
- summary: "查看当前项目、线程、模型与上下文使用情况",
87
- terminal: [],
88
- weixin: ["/status"],
89
- status: "active",
90
- },
1
+ const COMMAND_GROUPS = [
2
+ {
3
+ id: "lifecycle",
4
+ label: "启动与诊断",
5
+ actions: [
6
+ {
7
+ action: "app.login",
8
+ summary: "发起微信扫码登录并保存账号",
9
+ terminal: ["login"],
10
+ weixin: [],
11
+ status: "active",
12
+ },
13
+ {
14
+ action: "app.accounts",
15
+ summary: "查看本地已保存账号",
16
+ terminal: ["accounts"],
17
+ weixin: [],
18
+ status: "active",
19
+ },
20
+ {
21
+ action: "app.start",
22
+ summary: "启动当前 channel/runtime 主循环",
23
+ terminal: ["start"],
24
+ weixin: [],
25
+ status: "active",
26
+ },
27
+ {
28
+ action: "app.shared_start",
29
+ summary: "启动共享 app-server 与共享微信桥接",
30
+ terminal: ["shared start"],
31
+ weixin: [],
32
+ status: "active",
33
+ },
34
+ {
35
+ action: "app.shared_open",
36
+ summary: "接入当前微信绑定的共享线程",
37
+ terminal: ["shared open"],
38
+ weixin: [],
39
+ status: "active",
40
+ },
41
+ {
42
+ action: "app.shared_status",
43
+ summary: "查看共享 app-server 与共享桥接状态",
44
+ terminal: ["shared status"],
45
+ weixin: [],
46
+ status: "active",
47
+ },
48
+ {
49
+ action: "app.doctor",
50
+ summary: "打印当前配置、边界和线程状态",
51
+ terminal: ["doctor"],
52
+ weixin: [],
53
+ status: "active",
54
+ },
55
+ {
56
+ action: "system.send",
57
+ summary: "向内部系统队列写入一条不可见触发消息",
58
+ terminal: ["system send"],
59
+ terminalGroup: "system",
60
+ weixin: [],
61
+ status: "active",
62
+ },
63
+ {
64
+ action: "system.checkin_poller",
65
+ summary: "按随机间隔写入主动 check-in 触发",
66
+ terminal: ["system checkin-poller"],
67
+ terminalGroup: "system",
68
+ weixin: [],
69
+ status: "active",
70
+ },
71
+ ],
72
+ },
73
+ {
74
+ id: "workspace",
75
+ label: "项目与线程",
76
+ actions: [
77
+ {
78
+ action: "workspace.bind",
79
+ summary: "绑定当前聊天使用的项目目录",
80
+ terminal: [],
81
+ weixin: ["/bind"],
82
+ status: "active",
83
+ },
84
+ {
85
+ action: "workspace.status",
86
+ summary: "查看当前项目、线程、模型与上下文使用情况",
87
+ terminal: [],
88
+ weixin: ["/status"],
89
+ status: "active",
90
+ },
91
91
  {
92
92
  action: "thread.new",
93
93
  summary: "切到新线程草稿,并在下一条消息前重建当前 workspace 上下文",
@@ -109,39 +109,39 @@ const COMMAND_GROUPS = [
109
109
  weixin: ["/switch <threadId>"],
110
110
  status: "active",
111
111
  },
112
- {
113
- action: "thread.stop",
114
- summary: "停止当前线程中的运行",
115
- terminal: [],
116
- weixin: ["/stop"],
117
- status: "active",
118
- },
119
- ],
120
- },
112
+ {
113
+ action: "thread.stop",
114
+ summary: "停止当前线程中的运行",
115
+ terminal: [],
116
+ weixin: ["/stop"],
117
+ status: "active",
118
+ },
119
+ ],
120
+ },
121
121
  {
122
122
  id: "approval",
123
123
  label: "授权与控制",
124
- actions: [
125
- {
126
- action: "approval.accept_once",
127
- summary: "允许当前待处理的授权请求一次",
128
- terminal: [],
129
- weixin: ["/yes"],
130
- status: "active",
131
- },
132
- {
133
- action: "approval.accept_workspace",
134
- summary: "在当前项目内持续允许同前缀命令",
135
- terminal: [],
136
- weixin: ["/always"],
137
- status: "active",
138
- },
139
- {
140
- action: "approval.reject_once",
141
- summary: "拒绝当前待处理的授权请求",
142
- terminal: [],
143
- weixin: ["/no"],
144
- status: "active",
124
+ actions: [
125
+ {
126
+ action: "approval.accept_once",
127
+ summary: "允许当前待处理的授权请求一次",
128
+ terminal: [],
129
+ weixin: ["/yes"],
130
+ status: "active",
131
+ },
132
+ {
133
+ action: "approval.accept_workspace",
134
+ summary: "在当前项目内持续允许同前缀命令",
135
+ terminal: [],
136
+ weixin: ["/always"],
137
+ status: "active",
138
+ },
139
+ {
140
+ action: "approval.reject_once",
141
+ summary: "拒绝当前待处理的授权请求",
142
+ terminal: [],
143
+ weixin: ["/no"],
144
+ status: "active",
145
145
  },
146
146
  ],
147
147
  },
@@ -210,29 +210,29 @@ const COMMAND_GROUPS = [
210
210
  {
211
211
  id: "capabilities",
212
212
  label: "能力集成",
213
- actions: [
214
- {
215
- action: "model.inspect",
216
- summary: "查看当前模型",
217
- terminal: [],
218
- weixin: ["/model"],
219
- status: "active",
220
- },
221
- {
222
- action: "model.select",
223
- summary: "切换到指定模型",
224
- terminal: [],
225
- weixin: ["/model <id>"],
226
- status: "active",
227
- },
228
- {
229
- action: "channel.send_file",
230
- summary: "将文件作为附件发送回当前聊天",
231
- terminal: ["channel send-file"],
232
- terminalGroup: "channel",
233
- weixin: [],
234
- status: "active",
235
- },
213
+ actions: [
214
+ {
215
+ action: "model.inspect",
216
+ summary: "查看当前模型",
217
+ terminal: [],
218
+ weixin: ["/model"],
219
+ status: "active",
220
+ },
221
+ {
222
+ action: "model.select",
223
+ summary: "切换到指定模型",
224
+ terminal: [],
225
+ weixin: ["/model <id>"],
226
+ status: "active",
227
+ },
228
+ {
229
+ action: "channel.send_file",
230
+ summary: "将文件作为附件发送回当前聊天",
231
+ terminal: ["channel send-file"],
232
+ terminalGroup: "channel",
233
+ weixin: [],
234
+ status: "active",
235
+ },
236
236
  {
237
237
  action: "timeline.event",
238
238
  summary: "按单个时间块写入时间轴,不必手写 JSON",
@@ -243,7 +243,7 @@ const COMMAND_GROUPS = [
243
243
  },
244
244
  {
245
245
  action: "timeline.write",
246
- summary: "将当前上下文写入时间轴",
246
+ summary: "按批量或原始 JSON 写入时间轴事件(低层入口)",
247
247
  terminal: ["timeline write"],
248
248
  terminalGroup: "timeline",
249
249
  weixin: [],
@@ -278,68 +278,68 @@ const COMMAND_GROUPS = [
278
278
  summary: "构建时间轴静态页面",
279
279
  terminal: ["timeline build"],
280
280
  terminalGroup: "timeline",
281
- weixin: [],
282
- status: "active",
283
- },
284
- {
285
- action: "timeline.serve",
286
- summary: "启动时间轴静态页面服务",
287
- terminal: ["timeline serve"],
288
- terminalGroup: "timeline",
289
- weixin: [],
290
- status: "active",
291
- },
292
- {
293
- action: "timeline.dev",
294
- summary: "启动时间轴热更新开发服务",
295
- terminal: ["timeline dev"],
296
- terminalGroup: "timeline",
297
- weixin: [],
298
- status: "active",
299
- },
300
- {
301
- action: "timeline.screenshot",
302
- summary: "截图时间轴页面",
303
- terminal: ["timeline screenshot"],
304
- terminalGroup: "timeline",
305
- weixin: [],
306
- status: "active",
307
- },
308
- {
309
- action: "reminder.create",
310
- summary: "创建提醒并交给调度层处理",
311
- terminal: ["reminder write"],
312
- terminalGroup: "reminder",
313
- weixin: [],
314
- status: "active",
315
- },
316
- {
317
- action: "diary.append",
318
- summary: "追加一条日记记录",
319
- terminal: ["diary write"],
320
- terminalGroup: "diary",
321
- weixin: [],
322
- status: "active",
323
- },
324
- {
325
- action: "app.help",
326
- summary: "查看当前通道可用命令",
327
- terminal: ["help"],
328
- weixin: ["/help"],
329
- status: "active",
330
- },
331
- ],
332
- },
333
- ];
334
-
335
- function listCommandGroups() {
336
- return COMMAND_GROUPS.map((group) => ({
337
- ...group,
338
- actions: group.actions.map((action) => ({ ...action })),
339
- }));
340
- }
341
-
342
- function buildTerminalHelpText() {
281
+ weixin: [],
282
+ status: "active",
283
+ },
284
+ {
285
+ action: "timeline.serve",
286
+ summary: "启动时间轴静态页面服务",
287
+ terminal: ["timeline serve"],
288
+ terminalGroup: "timeline",
289
+ weixin: [],
290
+ status: "active",
291
+ },
292
+ {
293
+ action: "timeline.dev",
294
+ summary: "启动时间轴热更新开发服务",
295
+ terminal: ["timeline dev"],
296
+ terminalGroup: "timeline",
297
+ weixin: [],
298
+ status: "active",
299
+ },
300
+ {
301
+ action: "timeline.screenshot",
302
+ summary: "截图时间轴页面",
303
+ terminal: ["timeline screenshot"],
304
+ terminalGroup: "timeline",
305
+ weixin: [],
306
+ status: "active",
307
+ },
308
+ {
309
+ action: "reminder.create",
310
+ summary: "创建提醒并交给调度层处理",
311
+ terminal: ["reminder write"],
312
+ terminalGroup: "reminder",
313
+ weixin: [],
314
+ status: "active",
315
+ },
316
+ {
317
+ action: "diary.append",
318
+ summary: "追加一条日记记录",
319
+ terminal: ["diary write"],
320
+ terminalGroup: "diary",
321
+ weixin: [],
322
+ status: "active",
323
+ },
324
+ {
325
+ action: "app.help",
326
+ summary: "查看当前通道可用命令",
327
+ terminal: ["help"],
328
+ weixin: ["/help"],
329
+ status: "active",
330
+ },
331
+ ],
332
+ },
333
+ ];
334
+
335
+ function listCommandGroups() {
336
+ return COMMAND_GROUPS.map((group) => ({
337
+ ...group,
338
+ actions: group.actions.map((action) => ({ ...action })),
339
+ }));
340
+ }
341
+
342
+ function buildTerminalHelpText() {
343
343
  const lines = [
344
344
  "用法: npm run <script>",
345
345
  "",
@@ -350,129 +350,134 @@ function buildTerminalHelpText() {
350
350
  " npm run background:install Windows 安装开机自启和周期巡检",
351
351
  " npm run background:uninstall Windows 卸载开机自启和周期巡检",
352
352
  ];
353
-
354
- for (const group of COMMAND_GROUPS) {
355
- const activeActions = group.actions.filter((action) => action.status === "active" && action.terminal.length);
356
- if (!activeActions.length) {
357
- continue;
358
- }
359
- lines.push(`- ${group.label}`);
360
- for (const action of activeActions) {
361
- lines.push(` ${formatTerminalExamples(action)} ${action.summary}`);
362
- }
363
- }
364
-
365
- const plannedGroups = collectPlannedTerminalGroups();
366
- if (plannedGroups.length) {
367
- lines.push("");
368
- lines.push("规划中的终端子命令:");
369
- for (const group of plannedGroups) {
370
- lines.push(`- ${group.name}`);
371
- for (const action of group.actions) {
372
- lines.push(` ${action.terminal.join(", ")} ${action.summary}`);
373
- }
374
- }
375
- }
376
-
377
- lines.push("");
378
- lines.push("微信命令映射与后续能力动作请看 README / docs。");
379
- return lines.join("\n");
380
- }
381
-
382
- function buildWeixinHelpText() {
383
- const lines = ["当前可用命令:"];
384
- for (const group of COMMAND_GROUPS) {
385
- const activeActions = group.actions.filter((action) => action.status === "active" && action.weixin.length);
386
- if (!activeActions.length) {
387
- continue;
388
- }
389
- lines.push("");
390
- lines.push(`${group.label}:`);
391
- for (const action of activeActions) {
392
- lines.push(`- ${action.weixin.join(", ")} ${action.summary}`);
393
- }
394
- }
395
- return lines.join("\n");
396
- }
397
-
398
- function buildTerminalTopicHelp(topic) {
399
- const normalizedTopic = normalizeTopic(topic);
400
- const actions = COMMAND_GROUPS
401
- .flatMap((group) => group.actions)
402
- .filter((action) => normalizeTopic(action.terminalGroup) === normalizedTopic && action.terminal.length);
403
-
404
- if (!actions.length) {
405
- return "";
406
- }
407
-
408
- const hasPlannedOnly = actions.every((action) => action.status === "planned");
409
- const lines = [
410
- `用法: ${buildTopicUsage(normalizedTopic)}`,
411
- "",
412
- hasPlannedOnly
413
- ? `当前 ${normalizedTopic} 命令仍在接入中,计划中的子命令:`
414
- : `当前 ${normalizedTopic} 命令:`,
415
- ];
416
- for (const action of actions) {
417
- lines.push(`- ${formatTerminalExamples(action)} ${action.summary}`);
418
- }
419
- return lines.join("\n");
420
- }
421
-
422
- function isPlannedTerminalTopic(topic) {
423
- const normalizedTopic = normalizeTopic(topic);
424
- return COMMAND_GROUPS
425
- .flatMap((group) => group.actions)
426
- .some((action) => normalizeTopic(action.terminalGroup) === normalizedTopic && action.terminal.length);
427
- }
428
-
429
- function collectPlannedTerminalGroups() {
430
- const grouped = new Map();
431
- for (const action of COMMAND_GROUPS.flatMap((group) => group.actions)) {
432
- if (!action.terminal.length || !action.terminalGroup || action.status !== "planned") {
433
- continue;
434
- }
435
- const key = action.terminalGroup;
436
- if (!grouped.has(key)) {
437
- grouped.set(key, { name: key, actions: [] });
438
- }
439
- grouped.get(key).actions.push(action);
440
- }
441
- return Array.from(grouped.values());
442
- }
443
-
444
- function normalizeTopic(value) {
445
- return typeof value === "string" ? value.trim().toLowerCase() : "";
446
- }
447
-
448
- module.exports = {
449
- buildTerminalHelpText,
450
- buildTerminalTopicHelp,
451
- buildWeixinHelpText,
452
- isPlannedTerminalTopic,
453
- listCommandGroups,
454
- };
455
-
456
- function formatTerminalExamples(action) {
457
- const terminal = Array.isArray(action?.terminal) ? action.terminal : [];
458
- if (!terminal.length) {
459
- return "";
460
- }
461
- return terminal.map((commandText) => toNpmRunExample(commandText)).join(", ");
462
- }
463
-
464
- function buildTopicUsage(topic) {
465
- switch (topic) {
466
- case "reminder":
467
- return [
468
- "npm run reminder:write -- <args>",
469
- "",
470
- "参数:",
471
- " --delay 30s|10m|1h30m|2d4h",
472
- " --at 2026-04-07T21:30+08:00 | 2026-04-07 21:30",
473
- " --text \"提醒内容\"",
474
- " --user <wechatUserId> 可选",
475
- ].join("\n");
353
+
354
+ for (const group of COMMAND_GROUPS) {
355
+ const activeActions = group.actions.filter((action) => action.status === "active" && action.terminal.length);
356
+ if (!activeActions.length) {
357
+ continue;
358
+ }
359
+ lines.push(`- ${group.label}`);
360
+ for (const action of activeActions) {
361
+ lines.push(` ${formatTerminalExamples(action)} ${action.summary}`);
362
+ }
363
+ }
364
+
365
+ const plannedGroups = collectPlannedTerminalGroups();
366
+ if (plannedGroups.length) {
367
+ lines.push("");
368
+ lines.push("规划中的终端子命令:");
369
+ for (const group of plannedGroups) {
370
+ lines.push(`- ${group.name}`);
371
+ for (const action of group.actions) {
372
+ lines.push(` ${action.terminal.join(", ")} ${action.summary}`);
373
+ }
374
+ }
375
+ }
376
+
377
+ lines.push("");
378
+ lines.push("微信命令映射与后续能力动作请看 README / docs。");
379
+ return lines.join("\n");
380
+ }
381
+
382
+ function buildWeixinHelpText() {
383
+ const lines = ["当前可用命令:"];
384
+ for (const group of COMMAND_GROUPS) {
385
+ const activeActions = group.actions.filter((action) => action.status === "active" && action.weixin.length);
386
+ if (!activeActions.length) {
387
+ continue;
388
+ }
389
+ lines.push("");
390
+ lines.push(`${group.label}:`);
391
+ for (const action of activeActions) {
392
+ lines.push(`- ${action.weixin.join(", ")} ${action.summary}`);
393
+ }
394
+ }
395
+ return lines.join("\n");
396
+ }
397
+
398
+ function buildTerminalTopicHelp(topic) {
399
+ const normalizedTopic = normalizeTopic(topic);
400
+ const actions = COMMAND_GROUPS
401
+ .flatMap((group) => group.actions)
402
+ .filter((action) => normalizeTopic(action.terminalGroup) === normalizedTopic && action.terminal.length);
403
+
404
+ if (!actions.length) {
405
+ return "";
406
+ }
407
+
408
+ const hasPlannedOnly = actions.every((action) => action.status === "planned");
409
+ const lines = [
410
+ `用法: ${buildTopicUsage(normalizedTopic)}`,
411
+ "",
412
+ hasPlannedOnly
413
+ ? `当前 ${normalizedTopic} 命令仍在接入中,计划中的子命令:`
414
+ : `当前 ${normalizedTopic} 命令:`,
415
+ ];
416
+ for (const action of actions) {
417
+ lines.push(`- ${formatTerminalExamples(action)} ${action.summary}`);
418
+ }
419
+ return lines.join("\n");
420
+ }
421
+
422
+ function isPlannedTerminalTopic(topic) {
423
+ const normalizedTopic = normalizeTopic(topic);
424
+ return COMMAND_GROUPS
425
+ .flatMap((group) => group.actions)
426
+ .some((action) => normalizeTopic(action.terminalGroup) === normalizedTopic && action.terminal.length);
427
+ }
428
+
429
+ function collectPlannedTerminalGroups() {
430
+ const grouped = new Map();
431
+ for (const action of COMMAND_GROUPS.flatMap((group) => group.actions)) {
432
+ if (!action.terminal.length || !action.terminalGroup || action.status !== "planned") {
433
+ continue;
434
+ }
435
+ const key = action.terminalGroup;
436
+ if (!grouped.has(key)) {
437
+ grouped.set(key, { name: key, actions: [] });
438
+ }
439
+ grouped.get(key).actions.push(action);
440
+ }
441
+ return Array.from(grouped.values());
442
+ }
443
+
444
+ function normalizeTopic(value) {
445
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
446
+ }
447
+
448
+ module.exports = {
449
+ buildTerminalHelpText,
450
+ buildTerminalTopicHelp,
451
+ buildWeixinHelpText,
452
+ isPlannedTerminalTopic,
453
+ listCommandGroups,
454
+ };
455
+
456
+ function formatTerminalExamples(action) {
457
+ const terminal = Array.isArray(action?.terminal) ? action.terminal : [];
458
+ if (!terminal.length) {
459
+ return "";
460
+ }
461
+ return terminal.map((commandText) => toNpmRunExample(commandText)).join(", ");
462
+ }
463
+
464
+ function buildTopicUsage(topic) {
465
+ switch (topic) {
466
+ case "reminder":
467
+ return [
468
+ "npm run reminder:write -- <args>",
469
+ "",
470
+ "参数:",
471
+ " --delay 30s|10m|1h30m|2d4h",
472
+ " --at 2026-04-07T21:30-04:00 | 2026-04-07 21:30",
473
+ " --text \"提醒内容\"",
474
+ " --user <senderId> 可选;必须是桥实际观测到的 sender id",
475
+ "",
476
+ "补充:",
477
+ " 先用 npm run accounts 看可用 sender id;不要填昵称或自己猜的微信号",
478
+ " 当前选中的 sender id 必须已经有可用的 context_token;否则命令会直接失败",
479
+ " 不带 offset 的本地时间按当前 runtime timezone 解释;显式偏移时间戳按原值保留",
480
+ ].join("\n");
476
481
  case "diary":
477
482
  return [
478
483
  "npm run diary:write -- <args>",
@@ -484,29 +489,36 @@ function buildTopicUsage(topic) {
484
489
  " --timeline-text \"...\" 只和 --section todo --state done 一起用;同一切换点会同步写入“时间线事实”",
485
490
  " --title \"标题\" 默认主要给 supplement 用;其他 section 会和正文合成单行内容",
486
491
  " --date YYYY-MM-DD 决定写入哪个日记文件",
487
- " --time HH:mm 可选,覆盖条目时间",
492
+ " --time HH:mm 可选,覆盖条目时间;todo open 时也会把它记成这条 live block 的开始时间",
488
493
  "",
489
494
  "示例:",
490
495
  " npm run diary:write -- --section todo --state open --text \"把药单发给 Alex\"",
491
496
  " npm run diary:write -- --section todo --state done --text \"收住 Codeksei 微信回复问题\" --timeline-text \"22:39-23:04 连续压测 Codeksei 微信回复链路;这条问题今晚可以先收尾。\"",
492
497
  " npm run diary:write -- --section timeline --text \"17:30-17:58 把药单发出去了\"",
498
+ "",
499
+ "说明:",
500
+ " open loop / 明确待跟进 -> todo;事后完成块 -> timeline;灵感碎片 -> fragment;解释判断 -> supplement;收口带走 -> summary",
501
+ " 如果 todo done 省略 --timeline-text,会优先复用同一 Todo 已捕获的开始时间来补 HH:mm-HH:mm 硬事实;只有找不到开始时间时才退回成单点时间。",
502
+ " 不要为了记一条已完成事实而先补造一个 Todo 再立刻 done。",
503
+ ].join("\n");
504
+ case "channel":
505
+ return [
506
+ "npm run channel:send-file -- --path /绝对路径 [--user <wechatUserId>]",
507
+ "",
508
+ "参数:",
509
+ " --path /绝对路径 要发回当前微信聊天的本地文件",
510
+ " --user <wechatUserId> 可选,覆盖默认接收用户",
493
511
  ].join("\n");
494
- case "channel":
495
- return [
496
- "npm run channel:send-file -- --path /绝对路径 [--user <wechatUserId>]",
497
- "",
498
- "参数:",
499
- " --path /绝对路径 要发回当前微信聊天的本地文件",
500
- " --user <wechatUserId> 可选,覆盖默认接收用户",
501
- ].join("\n");
502
- case "system":
503
- return "npm run system:send -- <args> / npm run system:checkin";
512
+ case "system":
513
+ return "npm run system:send -- <args> / npm run system:checkin";
504
514
  case "timeline":
505
515
  return [
506
516
  "npm run timeline:event -- <args> / npm run timeline:write -- <args> / npm run timeline:read -- <args> / npm run timeline:categories / npm run timeline:proposals -- <args> / npm run timeline:build / npm run timeline:serve / npm run timeline:dev / npm run timeline:screenshot -- --send",
507
517
  "",
508
518
  "补充:",
509
519
  " 单条事件优先用 npm run timeline:event -- --date YYYY-MM-DD --start HH:mm --end HH:mm --title \"...\" ...,避免手写 JSON",
520
+ " 如果必须用 timeline:write --stdin,传完整 JSON 对象 {\"events\":[...]},不要传裸数组",
521
+ " 不带 offset 的本地时间按当前 timezone 解释;如果 timeline state 已声明非 legacy timezone,会优先沿用它",
510
522
  " timeline 查分类先用 npm run timeline:categories;改已有日程前先用 npm run timeline:read -- --date YYYY-MM-DD",
511
523
  " timeline 截图稳定入口是 npm run timeline:screenshot -- --send,它会把任务交给当前微信桥执行",
512
524
  ].join("\n");
@@ -546,31 +558,31 @@ function buildTopicUsage(topic) {
546
558
  return "npm run <script>";
547
559
  }
548
560
  }
549
-
550
- function toNpmRunExample(commandText) {
551
- const normalized = typeof commandText === "string" ? commandText.trim() : "";
552
- switch (normalized) {
553
- case "login":
554
- case "accounts":
555
- case "start":
556
- case "shared start":
557
- case "shared open":
558
- case "shared status":
559
- case "doctor":
560
- case "help":
561
- return `npm run ${normalized.replace(" ", ":")}`;
562
- case "start --checkin":
563
- return "npm run start:checkin";
564
- case "reminder write":
565
- return "npm run reminder:write -- <args>";
566
- case "diary write":
567
- return "npm run diary:write -- <args>";
568
- case "channel send-file":
569
- return "npm run channel:send-file -- --path /绝对路径";
570
- case "system send":
571
- return "npm run system:send -- <args>";
572
- case "system checkin-poller":
573
- return "npm run system:checkin";
561
+
562
+ function toNpmRunExample(commandText) {
563
+ const normalized = typeof commandText === "string" ? commandText.trim() : "";
564
+ switch (normalized) {
565
+ case "login":
566
+ case "accounts":
567
+ case "start":
568
+ case "shared start":
569
+ case "shared open":
570
+ case "shared status":
571
+ case "doctor":
572
+ case "help":
573
+ return `npm run ${normalized.replace(" ", ":")}`;
574
+ case "start --checkin":
575
+ return "npm run start:checkin";
576
+ case "reminder write":
577
+ return "npm run reminder:write -- <args>";
578
+ case "diary write":
579
+ return "npm run diary:write -- <args>";
580
+ case "channel send-file":
581
+ return "npm run channel:send-file -- --path /绝对路径";
582
+ case "system send":
583
+ return "npm run system:send -- <args>";
584
+ case "system checkin-poller":
585
+ return "npm run system:checkin";
574
586
  case "timeline write":
575
587
  return "npm run timeline:write -- <args>";
576
588
  case "timeline event":
@@ -583,10 +595,10 @@ function toNpmRunExample(commandText) {
583
595
  return "npm run timeline:proposals -- <args>";
584
596
  case "timeline build":
585
597
  return "npm run timeline:build";
586
- case "timeline serve":
587
- return "npm run timeline:serve";
588
- case "timeline dev":
589
- return "npm run timeline:dev";
598
+ case "timeline serve":
599
+ return "npm run timeline:serve";
600
+ case "timeline dev":
601
+ return "npm run timeline:dev";
590
602
  case "timeline screenshot":
591
603
  return "npm run timeline:screenshot -- --send";
592
604
  case "project radar":