chatccc 0.2.252 → 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 (280) 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/os-prompts/darwin.md +8 -8
  5. package/deepccc-agent/os-prompts/linux.md +8 -8
  6. package/deepccc-agent/os-prompts/win32.md +11 -11
  7. package/deepccc-agent/package.json +65 -65
  8. package/dist/deepccc-agent/src/cli.js +634 -0
  9. package/dist/deepccc-agent/src/config.js +76 -0
  10. package/dist/deepccc-agent/src/context.js +348 -0
  11. package/dist/deepccc-agent/src/file-log.js +34 -0
  12. package/dist/deepccc-agent/src/file-tools.js +1208 -0
  13. package/dist/deepccc-agent/src/index.js +571 -0
  14. package/dist/deepccc-agent/src/permissions.js +170 -0
  15. package/dist/deepccc-agent/src/privacy.js +124 -0
  16. package/dist/deepccc-agent/src/proc-tree-kill.js +60 -0
  17. package/dist/deepccc-agent/src/progress/cards-helpers.js +70 -0
  18. package/dist/deepccc-agent/src/progress/reducer.js +102 -0
  19. package/dist/deepccc-agent/src/progress/terminal-renderer.js +264 -0
  20. package/dist/deepccc-agent/src/progress/view.js +30 -0
  21. package/dist/deepccc-agent/src/raw-stream-log.js +106 -0
  22. package/dist/deepccc-agent/src/session-search.js +276 -0
  23. package/dist/deepccc-agent/src/session-select.js +23 -0
  24. package/dist/deepccc-agent/src/sigint.js +26 -0
  25. package/dist/deepccc-agent/src/skills.js +178 -0
  26. package/dist/deepccc-agent/src/web-tools.js +246 -0
  27. package/dist/src/adapters/adapter-interface.js +19 -0
  28. package/dist/src/adapters/ccc-adapter.js +112 -0
  29. package/dist/src/adapters/claude-adapter.js +497 -0
  30. package/dist/src/adapters/claude-session-meta-store.js +92 -0
  31. package/dist/src/adapters/codex-adapter.js +279 -0
  32. package/dist/src/adapters/codex-session-meta-store.js +94 -0
  33. package/dist/src/adapters/cursor-adapter.js +491 -0
  34. package/dist/src/adapters/cursor-session-meta-store.js +116 -0
  35. package/dist/src/adapters/jsonl-stream.js +104 -0
  36. package/{src/adapters/proc-tree-kill.ts → dist/src/adapters/proc-tree-kill.js} +94 -97
  37. package/dist/src/adapters/raw-stream-log.js +106 -0
  38. package/dist/src/adapters/resource-monitor.js +113 -0
  39. package/dist/src/agent-activity.js +133 -0
  40. package/dist/src/agent-delegate-task-rpc.js +129 -0
  41. package/dist/src/agent-delegate-task.js +48 -0
  42. package/dist/src/agent-file-rpc.js +152 -0
  43. package/dist/src/agent-image-rpc.js +148 -0
  44. package/dist/src/agent-platform-routing.js +13 -0
  45. package/dist/src/agent-reload-config-rpc.js +23 -0
  46. package/dist/src/agent-rpc-body.js +87 -0
  47. package/dist/src/agent-stop-stuck.js +110 -0
  48. package/dist/src/card-action-routing.js +7 -0
  49. package/dist/src/card-plain-text.js +101 -0
  50. package/dist/src/cardkit.js +158 -0
  51. package/dist/src/cards.js +573 -0
  52. package/dist/src/chatgpt-subscription-rpc.js +18 -0
  53. package/dist/src/chatgpt-subscription.js +199 -0
  54. package/dist/src/chrome-devtools-guard.js +238 -0
  55. package/dist/src/claude-sdk-installer.js +249 -0
  56. package/dist/src/codex-reset-actions.js +143 -0
  57. package/dist/src/config-utils.js +149 -0
  58. package/dist/src/config.js +804 -0
  59. package/dist/src/cursor-usage.js +77 -0
  60. package/dist/src/exit-banner.js +28 -0
  61. package/dist/src/feishu-api.js +1404 -0
  62. package/dist/src/feishu-message-ingress.js +137 -0
  63. package/dist/src/feishu-platform.js +97 -0
  64. package/dist/src/format-message.js +252 -0
  65. package/dist/src/git-command.js +155 -0
  66. package/dist/src/im-skills.js +121 -0
  67. package/dist/src/index.js +833 -0
  68. package/dist/src/litellm-proxy.js +300 -0
  69. package/dist/src/orchestrator.js +2078 -0
  70. package/dist/src/package-root.js +26 -0
  71. package/dist/src/platform-adapter.js +7 -0
  72. package/dist/src/platform-startup.js +6 -0
  73. package/dist/src/privacy.js +100 -0
  74. package/dist/src/progress/reducer.js +102 -0
  75. package/dist/src/progress/terminal-renderer.js +264 -0
  76. package/dist/src/progress/view.js +30 -0
  77. package/dist/src/response-stall.js +14 -0
  78. package/dist/src/runtime-entry.js +13 -0
  79. package/dist/src/runtime-reload.js +19 -0
  80. package/dist/src/session-chat-binding.js +183 -0
  81. package/dist/src/session-name.js +7 -0
  82. package/dist/src/session.js +2144 -0
  83. package/dist/src/shared-prefix.js +16 -0
  84. package/dist/src/shared.js +493 -0
  85. package/dist/src/sim-agent.js +105 -0
  86. package/dist/src/sim-platform.js +142 -0
  87. package/dist/src/sim-store.js +231 -0
  88. package/dist/src/simplify.js +99 -0
  89. package/dist/src/startup-lifecycle.js +209 -0
  90. package/dist/src/stream-state.js +141 -0
  91. package/dist/src/terminal-error.js +100 -0
  92. package/dist/src/trace.js +50 -0
  93. package/dist/src/turn-cards.js +92 -0
  94. package/dist/src/update-command-guard.js +114 -0
  95. package/{src/web-ui.ts → dist/src/web-ui.js} +749 -823
  96. package/dist/src/wechat-platform.js +545 -0
  97. package/package.json +76 -74
  98. package/deepccc-agent/LICENSE +0 -201
  99. package/deepccc-agent/bin/deepccc.mjs +0 -26
  100. package/deepccc-agent/docs/cache-hit-rate-1.jpg +0 -0
  101. package/deepccc-agent/docs/cache-hit-rate-2.jpg +0 -0
  102. package/deepccc-agent/package-lock.json +0 -2027
  103. package/deepccc-agent/src/__tests__/chat-session.test.ts +0 -877
  104. package/deepccc-agent/src/__tests__/cli-json.test.ts +0 -49
  105. package/deepccc-agent/src/__tests__/config.test.ts +0 -34
  106. package/deepccc-agent/src/__tests__/context.test.ts +0 -341
  107. package/deepccc-agent/src/__tests__/file-tools.test.ts +0 -240
  108. package/deepccc-agent/src/__tests__/permissions.test.ts +0 -199
  109. package/deepccc-agent/src/__tests__/privacy.test.ts +0 -318
  110. package/deepccc-agent/src/__tests__/progress-reducer.test.ts +0 -121
  111. package/deepccc-agent/src/__tests__/session-search.test.ts +0 -262
  112. package/deepccc-agent/src/__tests__/session-select.test.ts +0 -116
  113. package/deepccc-agent/src/__tests__/sigint.test.ts +0 -56
  114. package/deepccc-agent/src/__tests__/skills.test.ts +0 -284
  115. package/deepccc-agent/src/__tests__/terminal-renderer.test.ts +0 -247
  116. package/deepccc-agent/src/__tests__/web-tools.test.ts +0 -220
  117. package/deepccc-agent/src/cli.ts +0 -682
  118. package/deepccc-agent/src/config.ts +0 -101
  119. package/deepccc-agent/src/context.ts +0 -465
  120. package/deepccc-agent/src/file-log.ts +0 -38
  121. package/deepccc-agent/src/file-tools.ts +0 -1493
  122. package/deepccc-agent/src/index.ts +0 -710
  123. package/deepccc-agent/src/permissions.ts +0 -226
  124. package/deepccc-agent/src/privacy.ts +0 -141
  125. package/deepccc-agent/src/proc-tree-kill.ts +0 -61
  126. package/deepccc-agent/src/progress/cards-helpers.ts +0 -76
  127. package/deepccc-agent/src/progress/reducer.ts +0 -113
  128. package/deepccc-agent/src/progress/terminal-renderer.ts +0 -294
  129. package/deepccc-agent/src/progress/view.ts +0 -77
  130. package/deepccc-agent/src/raw-stream-log.ts +0 -124
  131. package/deepccc-agent/src/session-search.ts +0 -370
  132. package/deepccc-agent/src/session-select.ts +0 -48
  133. package/deepccc-agent/src/sigint.ts +0 -50
  134. package/deepccc-agent/src/skills.ts +0 -205
  135. package/deepccc-agent/src/web-tools.ts +0 -313
  136. package/deepccc-agent/tsconfig.build.json +0 -13
  137. package/deepccc-agent/tsconfig.json +0 -13
  138. package/deepccc-agent/vitest.config.ts +0 -7
  139. package/src/__tests__/adapter-interface.test.ts +0 -152
  140. package/src/__tests__/agent-activity.test.ts +0 -86
  141. package/src/__tests__/agent-delegate-task-rpc.test.ts +0 -165
  142. package/src/__tests__/agent-image-rpc.test.ts +0 -34
  143. package/src/__tests__/agent-platform-routing.test.ts +0 -26
  144. package/src/__tests__/agent-reload-config-rpc.test.ts +0 -99
  145. package/src/__tests__/agent-rpc-body.test.ts +0 -42
  146. package/src/__tests__/builtin-chat-session.test.ts +0 -532
  147. package/src/__tests__/builtin-cli-json.test.ts +0 -39
  148. package/src/__tests__/builtin-config.test.ts +0 -26
  149. package/src/__tests__/builtin-context.test.ts +0 -319
  150. package/src/__tests__/builtin-file-tools.test.ts +0 -240
  151. package/src/__tests__/builtin-permissions.test.ts +0 -219
  152. package/src/__tests__/builtin-session-search.test.ts +0 -262
  153. package/src/__tests__/builtin-session-select.test.ts +0 -116
  154. package/src/__tests__/builtin-sigint.test.ts +0 -56
  155. package/src/__tests__/builtin-skills.test.ts +0 -284
  156. package/src/__tests__/builtin-web-tools.test.ts +0 -220
  157. package/src/__tests__/card-action-routing.test.ts +0 -18
  158. package/src/__tests__/card-plain-text.test.ts +0 -45
  159. package/src/__tests__/cardkit.test.ts +0 -60
  160. package/src/__tests__/cards.test.ts +0 -607
  161. package/src/__tests__/ccc-adapter.test.ts +0 -194
  162. package/src/__tests__/chatgpt-subscription-rpc.test.ts +0 -89
  163. package/src/__tests__/chatgpt-subscription.test.ts +0 -135
  164. package/src/__tests__/chrome-devtools-guard.test.ts +0 -165
  165. package/src/__tests__/claude-adapter.test.ts +0 -614
  166. package/src/__tests__/claude-raw-stream-log.test.ts +0 -96
  167. package/src/__tests__/claude-sdk-installer.test.ts +0 -285
  168. package/src/__tests__/codex-adapter.test.ts +0 -331
  169. package/src/__tests__/codex-raw-stream-log.test.ts +0 -170
  170. package/src/__tests__/codex-reset-actions.test.ts +0 -146
  171. package/src/__tests__/config-reload.test.ts +0 -284
  172. package/src/__tests__/config-sample.test.ts +0 -97
  173. package/src/__tests__/config-utils.test.ts +0 -40
  174. package/src/__tests__/config.test.ts +0 -395
  175. package/src/__tests__/crash-logging.test.ts +0 -360
  176. package/src/__tests__/cursor-adapter.test.ts +0 -890
  177. package/src/__tests__/cursor-session-meta-store.test.ts +0 -212
  178. package/src/__tests__/feishu-api.test.ts +0 -60
  179. package/src/__tests__/feishu-avatar.test.ts +0 -504
  180. package/src/__tests__/feishu-message-ingress.test.ts +0 -138
  181. package/src/__tests__/feishu-platform.test.ts +0 -75
  182. package/src/__tests__/fixtures/codex_simple_text.jsonl +0 -4
  183. package/src/__tests__/fixtures/codex_with_tool.jsonl +0 -6
  184. package/src/__tests__/fixtures/cursor_partial_only.jsonl +0 -5
  185. package/src/__tests__/fixtures/cursor_partial_with_final.jsonl +0 -13
  186. package/src/__tests__/fixtures/cursor_with_tool_call.jsonl +0 -12
  187. package/src/__tests__/format-message.test.ts +0 -316
  188. package/src/__tests__/git-command.test.ts +0 -288
  189. package/src/__tests__/im-skills.test.ts +0 -125
  190. package/src/__tests__/jsonl-stream.test.ts +0 -79
  191. package/src/__tests__/orchestrator.test.ts +0 -1268
  192. package/src/__tests__/package-files.test.ts +0 -24
  193. package/src/__tests__/platform-startup.test.ts +0 -19
  194. package/src/__tests__/privacy.test.ts +0 -198
  195. package/src/__tests__/proc-tree-kill.test.ts +0 -108
  196. package/src/__tests__/progress-reducer.test.ts +0 -121
  197. package/src/__tests__/raw-stream-log.test.ts +0 -106
  198. package/src/__tests__/response-stall.test.ts +0 -49
  199. package/src/__tests__/restart.test.ts +0 -232
  200. package/src/__tests__/session-ccc-config.test.ts +0 -66
  201. package/src/__tests__/session.test.ts +0 -3004
  202. package/src/__tests__/shared-prefix.test.ts +0 -36
  203. package/src/__tests__/sim-agent.test.ts +0 -174
  204. package/src/__tests__/sim-platform.test.ts +0 -93
  205. package/src/__tests__/sim-store.test.ts +0 -214
  206. package/src/__tests__/simplify.test.ts +0 -283
  207. package/src/__tests__/startup-lifecycle.test.ts +0 -231
  208. package/src/__tests__/stop-session.test.ts +0 -162
  209. package/src/__tests__/stream-state.test.ts +0 -164
  210. package/src/__tests__/terminal-error.test.ts +0 -54
  211. package/src/__tests__/terminal-renderer.test.ts +0 -247
  212. package/src/__tests__/update-command-guard.test.ts +0 -144
  213. package/src/__tests__/web-ui.test.ts +0 -438
  214. package/src/__tests__/wechat-platform.test.ts +0 -111
  215. package/src/adapters/adapter-interface.ts +0 -217
  216. package/src/adapters/ccc-adapter.ts +0 -150
  217. package/src/adapters/claude-adapter.ts +0 -673
  218. package/src/adapters/claude-session-meta-store.ts +0 -120
  219. package/src/adapters/codex-adapter.ts +0 -426
  220. package/src/adapters/codex-session-meta-store.ts +0 -131
  221. package/src/adapters/cursor-adapter.ts +0 -681
  222. package/src/adapters/cursor-session-meta-store.ts +0 -154
  223. package/src/adapters/jsonl-stream.ts +0 -157
  224. package/src/adapters/raw-stream-log.ts +0 -124
  225. package/src/adapters/resource-monitor.ts +0 -141
  226. package/src/agent-activity.ts +0 -175
  227. package/src/agent-delegate-task-rpc.ts +0 -153
  228. package/src/agent-delegate-task.ts +0 -91
  229. package/src/agent-file-rpc.ts +0 -172
  230. package/src/agent-image-rpc.ts +0 -168
  231. package/src/agent-platform-routing.ts +0 -28
  232. package/src/agent-reload-config-rpc.ts +0 -34
  233. package/src/agent-rpc-body.ts +0 -92
  234. package/src/agent-stop-stuck.ts +0 -129
  235. package/src/card-action-routing.ts +0 -14
  236. package/src/card-plain-text.ts +0 -108
  237. package/src/cardkit.ts +0 -179
  238. package/src/cards.ts +0 -684
  239. package/src/chatgpt-subscription-rpc.ts +0 -27
  240. package/src/chatgpt-subscription.ts +0 -299
  241. package/src/chrome-devtools-guard.ts +0 -318
  242. package/src/claude-sdk-installer.ts +0 -324
  243. package/src/codex-reset-actions.ts +0 -184
  244. package/src/config-utils.ts +0 -211
  245. package/src/config.ts +0 -1063
  246. package/src/cursor-usage.ts +0 -128
  247. package/src/exit-banner.ts +0 -33
  248. package/src/feishu-api.ts +0 -1616
  249. package/src/feishu-message-ingress.ts +0 -195
  250. package/src/feishu-platform.ts +0 -159
  251. package/src/format-message.ts +0 -293
  252. package/src/git-command.ts +0 -202
  253. package/src/im-skills.ts +0 -149
  254. package/src/index.ts +0 -1089
  255. package/src/litellm-proxy.ts +0 -374
  256. package/src/orchestrator.ts +0 -2543
  257. package/src/platform-adapter.ts +0 -70
  258. package/src/platform-startup.ts +0 -16
  259. package/src/privacy.ts +0 -118
  260. package/src/progress/reducer.ts +0 -113
  261. package/src/progress/terminal-renderer.ts +0 -294
  262. package/src/progress/view.ts +0 -77
  263. package/src/response-stall.ts +0 -28
  264. package/src/runtime-reload.ts +0 -34
  265. package/src/session-chat-binding.ts +0 -292
  266. package/src/session-name.ts +0 -8
  267. package/src/session.ts +0 -2659
  268. package/src/shared-prefix.ts +0 -29
  269. package/src/shared.ts +0 -552
  270. package/src/sim-agent.ts +0 -167
  271. package/src/sim-platform.ts +0 -177
  272. package/src/sim-store.ts +0 -317
  273. package/src/simplify.ts +0 -120
  274. package/src/startup-lifecycle.ts +0 -250
  275. package/src/stream-state.ts +0 -177
  276. package/src/terminal-error.ts +0 -129
  277. package/src/trace.ts +0 -51
  278. package/src/turn-cards.ts +0 -118
  279. package/src/update-command-guard.ts +0 -165
  280. 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
-