chatccc 0.2.251 → 0.2.253

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 (277) hide show
  1. package/README.md +1 -1
  2. package/bin/cccagent.mjs +12 -3
  3. package/bin/chatccc.mjs +15 -6
  4. package/deepccc-agent/package.json +1 -1
  5. package/dist/deepccc-agent/src/cli.js +634 -0
  6. package/dist/deepccc-agent/src/config.js +76 -0
  7. package/dist/deepccc-agent/src/context.js +348 -0
  8. package/dist/deepccc-agent/src/file-log.js +34 -0
  9. package/dist/deepccc-agent/src/file-tools.js +1208 -0
  10. package/dist/deepccc-agent/src/index.js +571 -0
  11. package/dist/deepccc-agent/src/permissions.js +170 -0
  12. package/dist/deepccc-agent/src/privacy.js +124 -0
  13. package/dist/deepccc-agent/src/proc-tree-kill.js +60 -0
  14. package/dist/deepccc-agent/src/progress/cards-helpers.js +70 -0
  15. package/dist/deepccc-agent/src/progress/reducer.js +102 -0
  16. package/dist/deepccc-agent/src/progress/terminal-renderer.js +264 -0
  17. package/dist/deepccc-agent/src/progress/view.js +30 -0
  18. package/dist/deepccc-agent/src/raw-stream-log.js +106 -0
  19. package/dist/deepccc-agent/src/session-search.js +276 -0
  20. package/dist/deepccc-agent/src/session-select.js +23 -0
  21. package/dist/deepccc-agent/src/sigint.js +26 -0
  22. package/dist/deepccc-agent/src/skills.js +178 -0
  23. package/dist/deepccc-agent/src/web-tools.js +246 -0
  24. package/dist/src/adapters/adapter-interface.js +19 -0
  25. package/dist/src/adapters/ccc-adapter.js +112 -0
  26. package/dist/src/adapters/claude-adapter.js +497 -0
  27. package/dist/src/adapters/claude-session-meta-store.js +92 -0
  28. package/dist/src/adapters/codex-adapter.js +279 -0
  29. package/dist/src/adapters/codex-session-meta-store.js +94 -0
  30. package/dist/src/adapters/cursor-adapter.js +491 -0
  31. package/dist/src/adapters/cursor-session-meta-store.js +116 -0
  32. package/dist/src/adapters/jsonl-stream.js +104 -0
  33. package/{src/adapters/proc-tree-kill.ts → dist/src/adapters/proc-tree-kill.js} +94 -97
  34. package/dist/src/adapters/raw-stream-log.js +106 -0
  35. package/dist/src/adapters/resource-monitor.js +113 -0
  36. package/dist/src/agent-activity.js +133 -0
  37. package/dist/src/agent-delegate-task-rpc.js +129 -0
  38. package/dist/src/agent-delegate-task.js +48 -0
  39. package/dist/src/agent-file-rpc.js +152 -0
  40. package/dist/src/agent-image-rpc.js +148 -0
  41. package/dist/src/agent-platform-routing.js +13 -0
  42. package/dist/src/agent-reload-config-rpc.js +23 -0
  43. package/dist/src/agent-rpc-body.js +87 -0
  44. package/dist/src/agent-stop-stuck.js +110 -0
  45. package/dist/src/card-action-routing.js +7 -0
  46. package/dist/src/card-plain-text.js +101 -0
  47. package/dist/src/cardkit.js +158 -0
  48. package/dist/src/cards.js +573 -0
  49. package/dist/src/chatgpt-subscription-rpc.js +18 -0
  50. package/dist/src/chatgpt-subscription.js +199 -0
  51. package/dist/src/chrome-devtools-guard.js +238 -0
  52. package/dist/src/claude-sdk-installer.js +249 -0
  53. package/dist/src/codex-reset-actions.js +143 -0
  54. package/dist/src/config-utils.js +149 -0
  55. package/dist/src/config.js +804 -0
  56. package/dist/src/cursor-usage.js +77 -0
  57. package/dist/src/exit-banner.js +28 -0
  58. package/dist/src/feishu-api.js +1404 -0
  59. package/dist/src/feishu-message-ingress.js +137 -0
  60. package/dist/src/feishu-platform.js +97 -0
  61. package/dist/src/format-message.js +252 -0
  62. package/dist/src/git-command.js +155 -0
  63. package/dist/src/im-skills.js +121 -0
  64. package/dist/src/index.js +833 -0
  65. package/dist/src/litellm-proxy.js +300 -0
  66. package/dist/src/orchestrator.js +2078 -0
  67. package/dist/src/package-root.js +26 -0
  68. package/dist/src/platform-adapter.js +7 -0
  69. package/dist/src/platform-startup.js +6 -0
  70. package/dist/src/privacy.js +100 -0
  71. package/dist/src/progress/reducer.js +102 -0
  72. package/dist/src/progress/terminal-renderer.js +264 -0
  73. package/dist/src/progress/view.js +30 -0
  74. package/dist/src/response-stall.js +14 -0
  75. package/dist/src/runtime-entry.js +13 -0
  76. package/dist/src/runtime-reload.js +19 -0
  77. package/dist/src/session-chat-binding.js +183 -0
  78. package/dist/src/session-name.js +7 -0
  79. package/dist/src/session.js +2144 -0
  80. package/dist/src/shared-prefix.js +16 -0
  81. package/dist/src/shared.js +493 -0
  82. package/dist/src/sim-agent.js +105 -0
  83. package/dist/src/sim-platform.js +142 -0
  84. package/dist/src/sim-store.js +231 -0
  85. package/dist/src/simplify.js +99 -0
  86. package/dist/src/startup-lifecycle.js +209 -0
  87. package/dist/src/stream-state.js +141 -0
  88. package/dist/src/terminal-error.js +100 -0
  89. package/dist/src/trace.js +50 -0
  90. package/dist/src/turn-cards.js +92 -0
  91. package/dist/src/update-command-guard.js +114 -0
  92. package/{src/web-ui.ts → dist/src/web-ui.js} +749 -823
  93. package/dist/src/wechat-platform.js +545 -0
  94. package/package.json +7 -5
  95. package/deepccc-agent/LICENSE +0 -201
  96. package/deepccc-agent/bin/deepccc.mjs +0 -26
  97. package/deepccc-agent/docs/cache-hit-rate-1.jpg +0 -0
  98. package/deepccc-agent/docs/cache-hit-rate-2.jpg +0 -0
  99. package/deepccc-agent/package-lock.json +0 -2027
  100. package/deepccc-agent/src/__tests__/chat-session.test.ts +0 -852
  101. package/deepccc-agent/src/__tests__/cli-json.test.ts +0 -49
  102. package/deepccc-agent/src/__tests__/config.test.ts +0 -34
  103. package/deepccc-agent/src/__tests__/context.test.ts +0 -341
  104. package/deepccc-agent/src/__tests__/file-tools.test.ts +0 -240
  105. package/deepccc-agent/src/__tests__/permissions.test.ts +0 -199
  106. package/deepccc-agent/src/__tests__/privacy.test.ts +0 -318
  107. package/deepccc-agent/src/__tests__/progress-reducer.test.ts +0 -121
  108. package/deepccc-agent/src/__tests__/session-search.test.ts +0 -262
  109. package/deepccc-agent/src/__tests__/session-select.test.ts +0 -116
  110. package/deepccc-agent/src/__tests__/sigint.test.ts +0 -56
  111. package/deepccc-agent/src/__tests__/skills.test.ts +0 -284
  112. package/deepccc-agent/src/__tests__/terminal-renderer.test.ts +0 -247
  113. package/deepccc-agent/src/__tests__/web-tools.test.ts +0 -220
  114. package/deepccc-agent/src/cli.ts +0 -682
  115. package/deepccc-agent/src/config.ts +0 -101
  116. package/deepccc-agent/src/context.ts +0 -465
  117. package/deepccc-agent/src/file-log.ts +0 -38
  118. package/deepccc-agent/src/file-tools.ts +0 -1493
  119. package/deepccc-agent/src/index.ts +0 -676
  120. package/deepccc-agent/src/permissions.ts +0 -226
  121. package/deepccc-agent/src/privacy.ts +0 -141
  122. package/deepccc-agent/src/proc-tree-kill.ts +0 -61
  123. package/deepccc-agent/src/progress/cards-helpers.ts +0 -76
  124. package/deepccc-agent/src/progress/reducer.ts +0 -113
  125. package/deepccc-agent/src/progress/terminal-renderer.ts +0 -294
  126. package/deepccc-agent/src/progress/view.ts +0 -77
  127. package/deepccc-agent/src/raw-stream-log.ts +0 -124
  128. package/deepccc-agent/src/session-search.ts +0 -370
  129. package/deepccc-agent/src/session-select.ts +0 -48
  130. package/deepccc-agent/src/sigint.ts +0 -50
  131. package/deepccc-agent/src/skills.ts +0 -205
  132. package/deepccc-agent/src/web-tools.ts +0 -313
  133. package/deepccc-agent/tsconfig.build.json +0 -13
  134. package/deepccc-agent/tsconfig.json +0 -13
  135. package/deepccc-agent/vitest.config.ts +0 -7
  136. package/src/__tests__/adapter-interface.test.ts +0 -152
  137. package/src/__tests__/agent-activity.test.ts +0 -86
  138. package/src/__tests__/agent-delegate-task-rpc.test.ts +0 -165
  139. package/src/__tests__/agent-image-rpc.test.ts +0 -34
  140. package/src/__tests__/agent-platform-routing.test.ts +0 -26
  141. package/src/__tests__/agent-reload-config-rpc.test.ts +0 -99
  142. package/src/__tests__/agent-rpc-body.test.ts +0 -42
  143. package/src/__tests__/builtin-chat-session.test.ts +0 -532
  144. package/src/__tests__/builtin-cli-json.test.ts +0 -39
  145. package/src/__tests__/builtin-config.test.ts +0 -26
  146. package/src/__tests__/builtin-context.test.ts +0 -319
  147. package/src/__tests__/builtin-file-tools.test.ts +0 -240
  148. package/src/__tests__/builtin-permissions.test.ts +0 -219
  149. package/src/__tests__/builtin-session-search.test.ts +0 -262
  150. package/src/__tests__/builtin-session-select.test.ts +0 -116
  151. package/src/__tests__/builtin-sigint.test.ts +0 -56
  152. package/src/__tests__/builtin-skills.test.ts +0 -284
  153. package/src/__tests__/builtin-web-tools.test.ts +0 -220
  154. package/src/__tests__/card-action-routing.test.ts +0 -18
  155. package/src/__tests__/card-plain-text.test.ts +0 -45
  156. package/src/__tests__/cardkit.test.ts +0 -60
  157. package/src/__tests__/cards.test.ts +0 -607
  158. package/src/__tests__/ccc-adapter.test.ts +0 -194
  159. package/src/__tests__/chatgpt-subscription-rpc.test.ts +0 -89
  160. package/src/__tests__/chatgpt-subscription.test.ts +0 -135
  161. package/src/__tests__/chrome-devtools-guard.test.ts +0 -165
  162. package/src/__tests__/claude-adapter.test.ts +0 -614
  163. package/src/__tests__/claude-raw-stream-log.test.ts +0 -96
  164. package/src/__tests__/claude-sdk-installer.test.ts +0 -285
  165. package/src/__tests__/codex-adapter.test.ts +0 -331
  166. package/src/__tests__/codex-raw-stream-log.test.ts +0 -170
  167. package/src/__tests__/codex-reset-actions.test.ts +0 -146
  168. package/src/__tests__/config-reload.test.ts +0 -284
  169. package/src/__tests__/config-sample.test.ts +0 -97
  170. package/src/__tests__/config-utils.test.ts +0 -40
  171. package/src/__tests__/config.test.ts +0 -395
  172. package/src/__tests__/crash-logging.test.ts +0 -360
  173. package/src/__tests__/cursor-adapter.test.ts +0 -890
  174. package/src/__tests__/cursor-session-meta-store.test.ts +0 -212
  175. package/src/__tests__/feishu-api.test.ts +0 -60
  176. package/src/__tests__/feishu-avatar.test.ts +0 -504
  177. package/src/__tests__/feishu-message-ingress.test.ts +0 -138
  178. package/src/__tests__/feishu-platform.test.ts +0 -75
  179. package/src/__tests__/fixtures/codex_simple_text.jsonl +0 -4
  180. package/src/__tests__/fixtures/codex_with_tool.jsonl +0 -6
  181. package/src/__tests__/fixtures/cursor_partial_only.jsonl +0 -5
  182. package/src/__tests__/fixtures/cursor_partial_with_final.jsonl +0 -13
  183. package/src/__tests__/fixtures/cursor_with_tool_call.jsonl +0 -12
  184. package/src/__tests__/format-message.test.ts +0 -316
  185. package/src/__tests__/git-command.test.ts +0 -288
  186. package/src/__tests__/im-skills.test.ts +0 -125
  187. package/src/__tests__/jsonl-stream.test.ts +0 -79
  188. package/src/__tests__/orchestrator.test.ts +0 -1268
  189. package/src/__tests__/package-files.test.ts +0 -24
  190. package/src/__tests__/platform-startup.test.ts +0 -19
  191. package/src/__tests__/privacy.test.ts +0 -198
  192. package/src/__tests__/proc-tree-kill.test.ts +0 -108
  193. package/src/__tests__/progress-reducer.test.ts +0 -121
  194. package/src/__tests__/raw-stream-log.test.ts +0 -106
  195. package/src/__tests__/response-stall.test.ts +0 -49
  196. package/src/__tests__/restart.test.ts +0 -232
  197. package/src/__tests__/session-ccc-config.test.ts +0 -66
  198. package/src/__tests__/session.test.ts +0 -3004
  199. package/src/__tests__/shared-prefix.test.ts +0 -36
  200. package/src/__tests__/sim-agent.test.ts +0 -174
  201. package/src/__tests__/sim-platform.test.ts +0 -93
  202. package/src/__tests__/sim-store.test.ts +0 -214
  203. package/src/__tests__/simplify.test.ts +0 -283
  204. package/src/__tests__/startup-lifecycle.test.ts +0 -231
  205. package/src/__tests__/stop-session.test.ts +0 -162
  206. package/src/__tests__/stream-state.test.ts +0 -164
  207. package/src/__tests__/terminal-error.test.ts +0 -54
  208. package/src/__tests__/terminal-renderer.test.ts +0 -247
  209. package/src/__tests__/update-command-guard.test.ts +0 -144
  210. package/src/__tests__/web-ui.test.ts +0 -438
  211. package/src/__tests__/wechat-platform.test.ts +0 -111
  212. package/src/adapters/adapter-interface.ts +0 -217
  213. package/src/adapters/ccc-adapter.ts +0 -150
  214. package/src/adapters/claude-adapter.ts +0 -673
  215. package/src/adapters/claude-session-meta-store.ts +0 -120
  216. package/src/adapters/codex-adapter.ts +0 -426
  217. package/src/adapters/codex-session-meta-store.ts +0 -131
  218. package/src/adapters/cursor-adapter.ts +0 -681
  219. package/src/adapters/cursor-session-meta-store.ts +0 -154
  220. package/src/adapters/jsonl-stream.ts +0 -157
  221. package/src/adapters/raw-stream-log.ts +0 -124
  222. package/src/adapters/resource-monitor.ts +0 -141
  223. package/src/agent-activity.ts +0 -175
  224. package/src/agent-delegate-task-rpc.ts +0 -153
  225. package/src/agent-delegate-task.ts +0 -91
  226. package/src/agent-file-rpc.ts +0 -172
  227. package/src/agent-image-rpc.ts +0 -168
  228. package/src/agent-platform-routing.ts +0 -28
  229. package/src/agent-reload-config-rpc.ts +0 -34
  230. package/src/agent-rpc-body.ts +0 -92
  231. package/src/agent-stop-stuck.ts +0 -129
  232. package/src/card-action-routing.ts +0 -14
  233. package/src/card-plain-text.ts +0 -108
  234. package/src/cardkit.ts +0 -179
  235. package/src/cards.ts +0 -684
  236. package/src/chatgpt-subscription-rpc.ts +0 -27
  237. package/src/chatgpt-subscription.ts +0 -299
  238. package/src/chrome-devtools-guard.ts +0 -318
  239. package/src/claude-sdk-installer.ts +0 -324
  240. package/src/codex-reset-actions.ts +0 -184
  241. package/src/config-utils.ts +0 -211
  242. package/src/config.ts +0 -1063
  243. package/src/cursor-usage.ts +0 -128
  244. package/src/exit-banner.ts +0 -33
  245. package/src/feishu-api.ts +0 -1616
  246. package/src/feishu-message-ingress.ts +0 -195
  247. package/src/feishu-platform.ts +0 -159
  248. package/src/format-message.ts +0 -293
  249. package/src/git-command.ts +0 -202
  250. package/src/im-skills.ts +0 -149
  251. package/src/index.ts +0 -1089
  252. package/src/litellm-proxy.ts +0 -374
  253. package/src/orchestrator.ts +0 -2543
  254. package/src/platform-adapter.ts +0 -70
  255. package/src/platform-startup.ts +0 -16
  256. package/src/privacy.ts +0 -118
  257. package/src/progress/reducer.ts +0 -113
  258. package/src/progress/terminal-renderer.ts +0 -294
  259. package/src/progress/view.ts +0 -77
  260. package/src/response-stall.ts +0 -28
  261. package/src/runtime-reload.ts +0 -34
  262. package/src/session-chat-binding.ts +0 -292
  263. package/src/session-name.ts +0 -8
  264. package/src/session.ts +0 -2659
  265. package/src/shared-prefix.ts +0 -29
  266. package/src/shared.ts +0 -552
  267. package/src/sim-agent.ts +0 -167
  268. package/src/sim-platform.ts +0 -177
  269. package/src/sim-store.ts +0 -317
  270. package/src/simplify.ts +0 -120
  271. package/src/startup-lifecycle.ts +0 -250
  272. package/src/stream-state.ts +0 -177
  273. package/src/terminal-error.ts +0 -129
  274. package/src/trace.ts +0 -51
  275. package/src/turn-cards.ts +0 -118
  276. package/src/update-command-guard.ts +0 -165
  277. package/src/wechat-platform.ts +0 -680
package/src/cards.ts DELETED
@@ -1,684 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Button helpers
3
- // ---------------------------------------------------------------------------
4
-
5
- import { ABD_HELP_LINE } from "./shared-prefix.ts";
6
- import type { ProgressView } from "./progress/view.ts";
7
-
8
- export interface ButtonDef {
9
- text: string;
10
- value: string;
11
- type?: "primary" | "default" | "danger";
12
- }
13
-
14
- export function buildButtons(buttons: ButtonDef[]): object {
15
- return {
16
- tag: "action",
17
- actions: buttons.map((b) => ({
18
- tag: "button",
19
- text: { tag: "plain_text", content: b.text },
20
- type: b.type ?? "primary",
21
- value: b.value,
22
- })),
23
- };
24
- }
25
-
26
- // ---------------------------------------------------------------------------
27
- // Content helpers
28
- // ---------------------------------------------------------------------------
29
-
30
- // 检测 markdown 代码块是否未闭合(``` 出现奇数次)
31
- export function isCodeBlockOpen(text: string): boolean {
32
- const matches = text.match(/```/g);
33
- return matches ? matches.length % 2 !== 0 : false;
34
- }
35
-
36
- export function truncateContent(text: string, maxLines = 20, maxChars = 8000): string {
37
- const lines = text.split("\n");
38
- // 跳过开头空行
39
- let startIdx = 0;
40
- while (startIdx < lines.length && lines[startIdx].trim() === "") {
41
- startIdx++;
42
- }
43
- const effectiveLines = lines.slice(startIdx);
44
- let displayText: string;
45
- if (effectiveLines.length > maxLines) {
46
- const firstLine = effectiveLines[0];
47
- const lastLines = effectiveLines.slice(-(maxLines - 1)).join("\n");
48
- displayText = firstLine + "\n...\n" + lastLines;
49
- } else {
50
- displayText = text;
51
- }
52
-
53
- // 截断后如果代码块未闭合,补上闭合标记,避免后续追加内容时误入代码块
54
- if (isCodeBlockOpen(displayText)) {
55
- displayText += "\n```";
56
- }
57
-
58
- return displayText;
59
- }
60
-
61
- const TOOL_EMOJI_MAP: Record<string, string> = {
62
- Read: "\u{1F4D6}", // 📖
63
- Write: "\u{270D}\u{FE0F}", // ✍️
64
- Edit: "\u{270F}\u{FE0F}", // ✏️
65
- Grep: "\u{1F50E}", // 🔎
66
- Glob: "\u{1F4C2}", // 📂
67
- Bash: "\u{1F5A5}\u{FE0F}", // 🖥️
68
- WebSearch: "\u{1F310}", // 🌐
69
- WebFetch: "\u{1F4E5}", // 📥
70
- TodoWrite: "\u{2705}", // ✅
71
- Agent: "\u{1F916}", // 🤖
72
- NotebookEdit: "\u{1F4D3}", // 📓
73
- AskUserQuestion: "\u{2753}",// ❓
74
- // CCC 内置 Agent 下划线命名(getToolEmoji 会先把下划线转驼峰再查表,这里保留蛇形条目以便直接命中)
75
- read_file: "\u{1F4D6}", // 📖
76
- list_dir: "\u{1F4C2}", // 📂
77
- search_code: "\u{1F50E}", // 🔎
78
- run_command: "\u{1F5A5}\u{FE0F}", // 🖥️
79
- edit_file: "\u{270F}\u{FE0F}", // ✏️
80
- create_file: "\u{270D}\u{FE0F}", // ✍️
81
- delete_file: "\u{1F5D1}\u{FE0F}", // 🗑️
82
- move_file: "\u{1F4E6}", // 📦
83
- apply_patch: "\u{1F4CB}", // 📋
84
- };
85
-
86
- export function getToolEmoji(name: string): string {
87
- return TOOL_EMOJI_MAP[name] ?? TOOL_EMOJI_MAP[normalizeToolName(name)] ?? "\u{1F527}"; // 🔧
88
- }
89
-
90
- /** 把下划线命名转成驼峰(read_file → ReadFile),用于兼容两种命名风格 */
91
- export function normalizeToolName(name: string): string {
92
- return name
93
- .split("_")
94
- .filter((part) => part.length > 0)
95
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
96
- .join("");
97
- }
98
-
99
- // ---------------------------------------------------------------------------
100
- // Card builders
101
- // ---------------------------------------------------------------------------
102
-
103
- // CardKit schema 2.0 进度卡片(带停止按钮,支持流式更新)
104
- // 输入是平台无关的 ProgressView(与终端过程区块共享同一视图模型):
105
- // - view.text → main_content(正文)
106
- // - view.showStop → 是否展示状态/停止按钮
107
- // - view.headerTitle → 卡片头部标题(生成中阶段的动态标题)
108
- // - view.headerTemplate → 卡片头部颜色模板
109
- export function buildProgressCard(view: ProgressView): string {
110
- const showStop = view.showStop;
111
- const headerTitle = view.headerTitle;
112
- const headerTemplate = view.headerTemplate;
113
- const elements: object[] = [
114
- { tag: "markdown", content: truncateContent(view.text), element_id: "main_content" },
115
- ];
116
- if (showStop) {
117
- elements.push({ tag: "hr" });
118
- elements.push({
119
- tag: "button",
120
- text: { tag: "plain_text", content: "查看状态(/state)" },
121
- type: "default",
122
- value: { action: "state" },
123
- element_id: "action_state",
124
- });
125
- elements.push({
126
- tag: "button",
127
- text: { tag: "plain_text", content: "停止生成(/stop)" },
128
- type: "danger",
129
- value: { action: "stop" },
130
- element_id: "action_stop",
131
- });
132
- }
133
- return JSON.stringify({
134
- schema: "2.0",
135
- config: {
136
- update_multi: true,
137
- streaming_mode: false,
138
- },
139
- header: {
140
- template: headerTemplate,
141
- title: { tag: "plain_text", content: headerTitle },
142
- },
143
- body: {
144
- direction: "vertical",
145
- elements,
146
- },
147
- });
148
- }
149
-
150
- export function buildHelpCard(
151
- userText: string,
152
- opts: { greeting?: string; defaultToolLabel?: string } = {},
153
- ): string {
154
- const greeting = opts.greeting ?? `你发送了: ${userText}`;
155
- const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
156
- const lines = [
157
- `发送 **/new** 创建新会话(使用 /cd 设置的目录,默认 ${defaultToolLabel})`,
158
- "发送 **/new claude** 创建新 Claude 会话",
159
- "发送 **/new cursor** 创建新 Cursor 会话",
160
- "发送 **/new codex** 创建新 Codex 会话",
161
- "发送 **/new ccc** 创建新 CCC Agent 会话",
162
- "发送 **/newh** 重置当前会话(沿用当前工作目录,不切换)",
163
- "发送 **/plan** 以规划模式提问(只读,不执行写操作)",
164
- "发送 **/ask** 以问答模式提问(只读,不执行写操作)",
165
- "发送 **/usage** 查看当前 Agent 的用量或余额",
166
- "发送 **/restart** 重启 ChatCCC 进程",
167
- "发送 **/update** 更新并重启(仅 npm 全局安装可用)",
168
- ABD_HELP_LINE,
169
- ].join("\n");
170
- return JSON.stringify({
171
- config: { wide_screen_mode: true },
172
- header: { template: "blue", title: { content: "ChatCCC", tag: "plain_text" } },
173
- elements: [
174
- { tag: "div", text: { tag: "lark_md", content: greeting } },
175
- { tag: "div", text: { tag: "lark_md", content: lines } },
176
- buildButtons([
177
- { text: `新建默认会话(/new,${defaultToolLabel})`, value: JSON.stringify({ cmd: "new" }), type: "primary" },
178
- { text: "新建 Claude 会话(/new claude)", value: JSON.stringify({ cmd: "new claude" }), type: "primary" },
179
- { text: "新建 Cursor 会话(/new cursor)", value: JSON.stringify({ cmd: "new cursor" }), type: "primary" },
180
- { text: "新建 Codex 会话(/new codex)", value: JSON.stringify({ cmd: "new codex" }), type: "primary" },
181
- { text: "新建 CCC Agent 会话(/new ccc)", value: JSON.stringify({ cmd: "new ccc" }), type: "primary" },
182
- { text: "重启 ChatCCC(/restart)", value: JSON.stringify({ cmd: "restart" }), type: "danger" },
183
- { text: "更新并重启(/update)", value: JSON.stringify({ cmd: "update" }), type: "danger" },
184
- { text: "切换工作路径(/cd)", value: JSON.stringify({ cmd: "cd" }), type: "default" },
185
- ]),
186
- ],
187
- });
188
- }
189
-
190
- // 工作路径内容(/cd 命令统一使用,返回 markdown 内容字符串)
191
- export function buildCdContent(
192
- dirPath: string,
193
- entries: { name: string; isDir: boolean }[],
194
- isUpdate: boolean,
195
- currentCwd?: string,
196
- maxFiles = 100
197
- ): string {
198
- const display = entries.slice(0, maxFiles);
199
- const overflow = entries.length > maxFiles ? `\n...(共 ${entries.length} 个条目,仅显示前 ${maxFiles} 个)` : "";
200
- const listing = display.map(e => e.isDir ? `📁 ${e.name}/` : `📄 ${e.name}`).join("\n");
201
-
202
- const currentLine = currentCwd
203
- ? `**当前会话工作路径:** \`${currentCwd}\``
204
- : "";
205
-
206
- const statusLine = isUpdate
207
- ? `**后续新建会话默认工作路径(已切换):** \`${dirPath}\``
208
- : `**后续新建会话默认工作路径:** \`${dirPath}\``;
209
-
210
- const lines: string[] = [];
211
- if (currentLine) lines.push(currentLine, "");
212
- lines.push(
213
- statusLine,
214
- ``,
215
- `此路径仅影响**本会话中新建**的会话工作路径。`,
216
- ``,
217
- `---`,
218
- `**目录内容** (最多 ${maxFiles} 个):`,
219
- listing,
220
- overflow,
221
- );
222
-
223
- return lines.join("\n");
224
- }
225
-
226
- // 工作路径卡片(/cd 无参数时使用,含最近使用路径按钮)
227
- //
228
- // 必须使用 v1 卡片格式(无 schema 字段、elements 放顶层)。原因:
229
- // 此卡片通过 `/im/v1/messages?msg_type=interactive` 端点直接发出,content
230
- // 字段就是卡片 JSON。该端点不接受 schema 2.0 的原始 JSON 作为 content
231
- // (schema 2.0 卡片必须先通过 CardKit /cardkit/v1/cards 创建得到 card_id,
232
- // 再以 `{type:"card", data:{card_id}}` 包装发出,参见 sendCardKitMessage)。
233
- // 之前用过 schema 2.0 + body.elements 的结构,飞书会静默拒绝该消息,
234
- // 导致 /cd 无任何回复——故此处与 buildSessionsCard/buildStatusCard 保持
235
- // 同一 v1 结构。
236
- export function buildCdCard(
237
- dirPath: string,
238
- entries: { name: string; isDir: boolean }[],
239
- recentDirs: string[],
240
- sessionCwd?: string,
241
- maxFiles = 100,
242
- ): string {
243
- const display = entries.slice(0, maxFiles);
244
- const overflow = entries.length > maxFiles ? `\n...(共 ${entries.length} 个条目,仅显示前 ${maxFiles} 个)` : "";
245
- const listing = display.map(e => e.isDir ? `📁 ${e.name}/` : `📄 ${e.name}`).join("\n");
246
-
247
- const elements: object[] = [];
248
-
249
- if (sessionCwd) {
250
- elements.push({
251
- tag: "div",
252
- text: { tag: "lark_md", content: `**当前会话工作路径:** \`${sessionCwd}\`` },
253
- });
254
- }
255
- elements.push({
256
- tag: "div",
257
- text: { tag: "lark_md", content: `**后续新建会话默认工作路径:** \`${dirPath}\`` },
258
- });
259
-
260
- if (recentDirs.length > 0) {
261
- elements.push({ tag: "hr" });
262
- elements.push({
263
- tag: "div",
264
- text: { tag: "lark_md", content: "**最近使用过的路径:**" },
265
- });
266
- elements.push({
267
- tag: "action",
268
- actions: recentDirs.map(d => {
269
- const label = d.length > 36 ? `...${d.slice(-33)}` : d;
270
- return {
271
- tag: "button",
272
- text: { tag: "plain_text", content: label },
273
- type: "default",
274
- value: { action: "cd", path: d },
275
- };
276
- }),
277
- });
278
- }
279
-
280
- elements.push({ tag: "hr" });
281
- elements.push({
282
- tag: "div",
283
- text: {
284
- tag: "lark_md",
285
- content: [
286
- `**目录内容** (最多 ${maxFiles} 个):`,
287
- listing,
288
- overflow,
289
- ].join("\n"),
290
- },
291
- });
292
-
293
- return JSON.stringify({
294
- config: { wide_screen_mode: true },
295
- header: {
296
- template: "blue",
297
- title: { tag: "plain_text", content: "工作路径" },
298
- },
299
- elements,
300
- });
301
- }
302
-
303
- function sessionToolLabel(tool: string): string {
304
- if (tool === "cursor") return "Cursor";
305
- if (tool === "codex") return "Codex";
306
- if (tool === "ccc") return "CCC Agent";
307
- return "Claude Code";
308
- }
309
-
310
- function pushSessionGroup(
311
- lines: string[],
312
- title: string,
313
- sessions: Array<{
314
- sessionId: string;
315
- chatName: string;
316
- chatId: string;
317
- active: boolean;
318
- turnCount: number;
319
- elapsedSeconds: number | null;
320
- model: string;
321
- tool: string;
322
- }>,
323
- formatSession: (session: {
324
- sessionId: string;
325
- chatName: string;
326
- chatId: string;
327
- active: boolean;
328
- turnCount: number;
329
- elapsedSeconds: number | null;
330
- model: string;
331
- tool: string;
332
- }, index: number) => string,
333
- index: { value: number },
334
- ): void {
335
- if (sessions.length === 0) return;
336
- if (index.value > 0) lines.push("", `**${title}:**`, "");
337
- else lines.push(`**${title}:**`, "");
338
- for (const session of sessions) {
339
- lines.push(formatSession(session, index.value++));
340
- }
341
- }
342
-
343
- // 所有会话列表卡片(Claude Code 优先,然后 Cursor)
344
- export function buildSessionsCard(sessions: Array<{
345
- sessionId: string;
346
- chatName: string;
347
- chatId: string;
348
- chatType?: string;
349
- active: boolean;
350
- turnCount: number;
351
- elapsedSeconds: number | null;
352
- model: string;
353
- tool: string;
354
- }>, opts: { defaultToolLabel?: string; fixedPrivateSession?: boolean } = {}): string {
355
- const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
356
- const fixedPrivateSession = opts.fixedPrivateSession ?? false;
357
- // 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
358
- const claudeCodeSessions = sessions.filter(s => s.tool !== "cursor" && s.tool !== "codex" && s.tool !== "ccc");
359
- const cursorSessions = sessions.filter(s => s.tool === "cursor");
360
- const codexSessions = sessions.filter(s => s.tool === "codex");
361
- const cccSessions = sessions.filter(s => s.tool === "ccc");
362
- const hasClaudeCode = claudeCodeSessions.length > 0;
363
- const hasCursor = cursorSessions.length > 0;
364
- const hasCodex = codexSessions.length > 0;
365
- const hasCcc = cccSessions.length > 0;
366
-
367
- if (sessions.length === 0) {
368
- return JSON.stringify({
369
- config: { wide_screen_mode: true },
370
- header: { template: "blue", title: { content: "所有会话", tag: "plain_text" } },
371
- elements: [
372
- { tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
373
- ? `当前没有会话记录。\n\n直接发送普通消息即可创建飞书私聊专属 ${defaultToolLabel} 会话;默认 Agent 变化后,下一条普通消息会创建对应 Agent 的新空会话。发送 **/new**、**/new claude**、**/new cursor**、**/new codex** 或 **/new ccc** 会另外创建会话群。`
374
- : `当前没有会话记录。\n\n使用 **/new**(默认 ${defaultToolLabel})、**/new claude**、**/new cursor**、**/new codex** 或 **/new ccc** 创建新会话。\n创建后可在任意会话群内发送 **/sessions** 查看列表,用 **/session 数字** 切换会话。` } },
375
- { tag: "hr" },
376
- { tag: "action", actions: [{ tag: "button", text: { tag: "plain_text", content: "收起" }, type: "default", value: { action: "close" } }] },
377
- ],
378
- });
379
- }
380
-
381
- const formatSession = (s: typeof sessions[0], i: number) => {
382
- const status = s.active ? "🟢 活跃" : "⚪ 空闲";
383
- const shortId = s.sessionId.length > 16 ? s.sessionId.slice(0, 16) + "..." : s.sessionId;
384
- let extra = "";
385
- if (s.active && s.elapsedSeconds !== null) {
386
- const mins = Math.floor(s.elapsedSeconds / 60);
387
- const secs = s.elapsedSeconds % 60;
388
- extra = ` | 本轮: ${mins}分${secs}秒`;
389
- }
390
- const toolLabel = sessionToolLabel(s.tool);
391
- const namePart = s.chatName ? `**${s.chatName}** ` : "";
392
- const chatTag = !s.chatId
393
- ? " (chat id缺失)"
394
- : s.chatType === "p2p"
395
- ? " (私聊)"
396
- : s.chatType === "group" || (s.chatType == null && s.chatId.startsWith("oc_"))
397
- ? " (群聊)"
398
- : "";
399
- return `**${i + 1}.** ${namePart}${chatTag} \`${shortId}\` ${status} | 工具: ${toolLabel} | 轮数: ${s.turnCount} | ${s.model}${extra}`;
400
- };
401
-
402
- const lines: string[] = [`共 **${sessions.length}** 个会话:`, ""];
403
- const idx = { value: 0 };
404
- if (hasClaudeCode) pushSessionGroup(lines, "Claude Code 会话", claudeCodeSessions, formatSession, idx);
405
- if (hasCursor) pushSessionGroup(lines, "Cursor 会话", cursorSessions, formatSession, idx);
406
- if (hasCodex) pushSessionGroup(lines, "Codex 会话", codexSessions, formatSession, idx);
407
- if (hasCcc) pushSessionGroup(lines, "CCC Agent 会话", cccSessions, formatSession, idx);
408
-
409
- return JSON.stringify({
410
- config: { wide_screen_mode: true },
411
- header: { template: "blue", title: { content: "所有会话", tag: "plain_text" } },
412
- elements: [
413
- { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
414
- { tag: "hr" },
415
- { tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
416
- ? "当前飞书私聊使用专属会话;默认 Agent 变化后,下一条普通消息会自动创建对应 Agent 的新空会话。发送 **/newh** 可在私聊中原地重置。群聊会话请回到对应群聊继续,私聊不支持 **/session** 切换。"
417
- : "在会话群内发送 **/newh** 可重置当前会话(创建新 Session,保留工作目录和群聊)。\n发送 **/session 数字**(如 `/session 1`)可将当前群聊切换到列表中对应编号的会话。" } },
418
- { tag: "hr" },
419
- {
420
- tag: "action",
421
- actions: [{
422
- tag: "button",
423
- text: { tag: "plain_text", content: "收起" },
424
- type: "default",
425
- value: { action: "close" },
426
- }],
427
- },
428
- ],
429
- });
430
- }
431
-
432
- // 队列缓存卡片(消息已进入缓存队列,带取消按钮)
433
- export function buildQueuedCard(text: string): string {
434
- const preview = text.length > 100 ? text.slice(0, 100) + "…" : text;
435
- return JSON.stringify({
436
- config: { wide_screen_mode: true },
437
- header: { template: "blue", title: { content: "消息已进入缓存队列", tag: "plain_text" } },
438
- elements: [
439
- { tag: "div", text: { tag: "lark_md", content: `当前会话正在生成中,你的消息已进入缓存队列,生成完成后会立即处理。\n\n> ${preview}` } },
440
- { tag: "hr" },
441
- {
442
- tag: "action",
443
- actions: [{
444
- tag: "button",
445
- text: { tag: "plain_text", content: "取消缓存(/cancel)" },
446
- type: "danger",
447
- value: { action: "cancel" },
448
- }],
449
- },
450
- ],
451
- });
452
- }
453
-
454
- // 队列满卡片(提示 /stop 或 /cancel)
455
- export function buildQueueFullCard(): string {
456
- return JSON.stringify({
457
- config: { wide_screen_mode: true },
458
- header: { template: "yellow", title: { content: "消息队列已满", tag: "plain_text" } },
459
- elements: [
460
- { tag: "div", text: { tag: "lark_md", content: "当前缓存队列中已有消息等待处理,请等待或发送指令:\n- **/stop** — 停止当前生成\n- **/cancel** — 取消队列中的消息" } },
461
- { tag: "hr" },
462
- {
463
- tag: "action",
464
- actions: [
465
- { tag: "button", text: { tag: "plain_text", content: "取消缓存(/cancel)" }, type: "danger", value: { action: "cancel" } },
466
- { tag: "button", text: { tag: "plain_text", content: "停止生成(/stop)" }, type: "default", value: { action: "stop" } },
467
- ],
468
- },
469
- ],
470
- });
471
- }
472
-
473
- // 状态卡片(带关闭按钮)
474
- export function buildStatusCard(statusText: string, template = "blue"): string {
475
- return JSON.stringify({
476
- config: { wide_screen_mode: true },
477
- header: { template, title: { content: "会话状态", tag: "plain_text" } },
478
- elements: [
479
- { tag: "div", text: { tag: "lark_md", content: statusText } },
480
- { tag: "hr" },
481
- {
482
- tag: "action",
483
- actions: [{
484
- tag: "button",
485
- text: { tag: "plain_text", content: "收起" },
486
- type: "default",
487
- value: { action: "close" },
488
- }],
489
- },
490
- ],
491
- });
492
- }
493
-
494
- export function buildCodexUsageCard(content: string, resetCreditsAvailable: number | null): string {
495
- const elements: object[] = [
496
- { tag: "div", text: { tag: "lark_md", content } },
497
- ];
498
- if (resetCreditsAvailable !== null && resetCreditsAvailable > 0) {
499
- elements.push({ tag: "hr" });
500
- elements.push({
501
- tag: "action",
502
- actions: [{
503
- tag: "button",
504
- text: { tag: "plain_text", content: "发起重置" },
505
- type: "primary",
506
- value: { action: "codex_reset_request", availableCount: resetCreditsAvailable },
507
- }],
508
- });
509
- }
510
-
511
- return JSON.stringify({
512
- config: { wide_screen_mode: true },
513
- header: { template: "blue", title: { content: "Codex Usage", tag: "plain_text" } },
514
- elements,
515
- });
516
- }
517
-
518
- export function buildCodexResetConfirmCard(params: {
519
- availableCount: number;
520
- parentMessageId: string;
521
- requestId: string;
522
- }): string {
523
- const valueBase = {
524
- parentMessageId: params.parentMessageId,
525
- requestId: params.requestId,
526
- };
527
- return JSON.stringify({
528
- config: { wide_screen_mode: true },
529
- header: { template: "yellow", title: { content: "确认 Codex 主动重置", tag: "plain_text" } },
530
- elements: [
531
- {
532
- tag: "div",
533
- text: {
534
- tag: "lark_md",
535
- content: `当前可用主动重置次数:**${params.availableCount}**。\n\n确认后会消耗 1 次主动重置,并重置当前可重置的 Codex 用量窗口。`,
536
- },
537
- },
538
- { tag: "hr" },
539
- {
540
- tag: "action",
541
- actions: [
542
- {
543
- tag: "button",
544
- text: { tag: "plain_text", content: "是,发起重置" },
545
- type: "danger",
546
- value: { action: "codex_reset_confirm", decision: "yes", ...valueBase },
547
- },
548
- {
549
- tag: "button",
550
- text: { tag: "plain_text", content: "否" },
551
- type: "default",
552
- value: { action: "codex_reset_confirm", decision: "no", ...valueBase },
553
- },
554
- ],
555
- },
556
- ],
557
- });
558
- }
559
-
560
- /** 模型切换卡片(/model 命令,飞书专用,含切换按钮) */
561
- export function buildModelCard(
562
- currentModel: string,
563
- models: string[],
564
- tool?: string,
565
- ): string {
566
- const toolLabel = tool ? ` (${tool})` : "";
567
- const currentLine = currentModel
568
- ? `**当前模型:** \`${currentModel}\``
569
- : "**当前模型:** 未指定";
570
-
571
- const lines: string[] = [currentLine];
572
- if (models.length > 0) {
573
- lines.push("", "**可切换模型:**");
574
- for (const m of models) {
575
- lines.push(`- \`${m}\``);
576
- }
577
- lines.push("", "点击按钮切换模型,或输入 `/model clear` 恢复默认");
578
- } else {
579
- lines.push("", "没有可切换的模型。请在 config.json 中配置模型字段。");
580
- }
581
-
582
- if (tool === "codex") {
583
- lines.push("输入 `/fast` 查看或切换当前会话的 Fast 模式");
584
- }
585
-
586
- const buttons: ButtonDef[] = [];
587
- for (const m of models.slice(0, 20)) {
588
- const shortName = m.includes("/") ? m.slice(m.lastIndexOf("/") + 1) : m;
589
- buttons.push({
590
- text: `/model ${shortName}`,
591
- value: JSON.stringify({ cmd: `/model ${m}` }),
592
- type: "primary",
593
- });
594
- }
595
-
596
- return JSON.stringify({
597
- config: { wide_screen_mode: true },
598
- header: { template: "blue", title: { content: `模型切换${toolLabel}`, tag: "plain_text" } },
599
- elements: [
600
- { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
601
- { tag: "hr" },
602
- buildButtons(buttons),
603
- ],
604
- });
605
- }
606
-
607
- export function buildFastModeCard(enabled: boolean): string {
608
- const mode = enabled ? "ON (Fast)" : "OFF (Standard)";
609
- return JSON.stringify({
610
- config: { wide_screen_mode: true },
611
- header: {
612
- template: enabled ? "green" : "blue",
613
- title: { content: "Codex Fast 模式", tag: "plain_text" },
614
- },
615
- elements: [
616
- {
617
- tag: "div",
618
- text: {
619
- tag: "lark_md",
620
- content: [
621
- `**当前模式:** ${mode}`,
622
- "",
623
- "切换将在下一条消息生效,当前生成不中断。",
624
- ].join("\n"),
625
- },
626
- },
627
- { tag: "hr" },
628
- buildButtons([
629
- {
630
- text: "ON",
631
- value: JSON.stringify({ cmd: "/fast on" }),
632
- type: enabled ? "primary" : "default",
633
- },
634
- {
635
- text: "OFF",
636
- value: JSON.stringify({ cmd: "/fast off" }),
637
- type: enabled ? "default" : "primary",
638
- },
639
- ]),
640
- ],
641
- });
642
- }
643
-
644
- export function buildEffortCard(
645
- currentEffort: string,
646
- efforts: string[],
647
- tool?: string,
648
- ): string {
649
- const toolLabel = tool ? ` (${tool})` : "";
650
- const currentLine = currentEffort
651
- ? `**当前 effort:** \`${currentEffort}\``
652
- : "**当前 effort:** 未指定";
653
-
654
- const lines: string[] = [currentLine];
655
- if (efforts.length > 0) {
656
- lines.push("", "**可切换 effort**");
657
- for (const effort of efforts) {
658
- lines.push(`- \`${effort}\``);
659
- }
660
- lines.push("", "点击按钮切换 effort,或输入 `/effort clear` 恢复默认");
661
- } else {
662
- lines.push("", "当前 agent 不支持 effort 切换。");
663
- }
664
-
665
- const buttons: ButtonDef[] = [];
666
- for (const effort of efforts.slice(0, 20)) {
667
- buttons.push({
668
- text: `/effort ${effort}`,
669
- value: JSON.stringify({ cmd: `/effort ${effort}` }),
670
- type: "primary",
671
- });
672
- }
673
-
674
- return JSON.stringify({
675
- config: { wide_screen_mode: true },
676
- header: { template: "blue", title: { content: `Effort 切换${toolLabel}`, tag: "plain_text" } },
677
- elements: [
678
- { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
679
- { tag: "hr" },
680
- buildButtons(buttons),
681
- ],
682
- });
683
- }
684
-