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
@@ -0,0 +1,634 @@
1
+ /**
2
+ * DeepCCC terminal REPL and JSONL streaming entrypoint — 同步自 ChatCCC
3
+ *
4
+ * Usage:
5
+ * node bin/deepccc.mjs
6
+ * node bin/deepccc.mjs --model deepseek-v4-pro
7
+ * node bin/deepccc.mjs --stream-json --prompt "hello"
8
+ *
9
+ * 交互模式(TTY)下,单轮回复渲染为固定"过程区块":状态行 + 折叠工具行 +
10
+ * 原地更新正文,不再滚屏刷 JSON;完成/停止/异常后定型留在屏幕上。
11
+ * 非 TTY(管道/CI)或 --plain 回退为纯文本流式输出;--stream-json 机器接口不变。
12
+ */
13
+ import * as readline from "node:readline";
14
+ import process from "node:process";
15
+ import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ import { join, resolve as resolvePath } from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+ import { listBuiltinContextSessions } from "./context.js";
20
+ import { resolveBuiltinSession } from "./session-select.js";
21
+ import { createCtrlCState } from "./sigint.js";
22
+ import { buildSkillTemplate } from "./skills.js";
23
+ import { reduceProgress } from "./progress/reducer.js";
24
+ import { TerminalProgressRenderer } from "./progress/terminal-renderer.js";
25
+ import { progressView } from "./progress/view.js";
26
+ import { defaultLogDir, setupFileLogging } from "./file-log.js";
27
+ function parsePositiveIntegerOption(name, value) {
28
+ const parsed = Number(value);
29
+ if (!Number.isInteger(parsed) || parsed <= 0) {
30
+ throw new Error(`${name} must be a positive integer`);
31
+ }
32
+ return parsed;
33
+ }
34
+ function parseProviderOption(value) {
35
+ const normalized = value.trim().toLowerCase();
36
+ if (normalized === "openai" || normalized === "anthropic")
37
+ return normalized;
38
+ throw new Error(`--provider must be "openai" or "anthropic", received: ${value}`);
39
+ }
40
+ function parseArgs(argv = process.argv.slice(2)) {
41
+ const config = {};
42
+ const options = {};
43
+ let listSessions = false;
44
+ let resume;
45
+ let help = false;
46
+ let streamJson = false;
47
+ let prompt = null;
48
+ let plain = false;
49
+ for (let i = 0; i < argv.length; i++) {
50
+ const arg = argv[i];
51
+ const next = argv[i + 1];
52
+ if (arg === "--provider" && next !== undefined) {
53
+ config.provider = parseProviderOption(next);
54
+ i++;
55
+ }
56
+ else if (arg === "--model" && next !== undefined) {
57
+ config.model = next;
58
+ i++;
59
+ }
60
+ else if (arg === "--effort" && next !== undefined) {
61
+ config.effort = next;
62
+ i++;
63
+ }
64
+ else if (arg === "--base-url" && next !== undefined) {
65
+ config.baseURL = next;
66
+ i++;
67
+ }
68
+ else if (arg === "--api-key" && next !== undefined) {
69
+ config.apiKey = next;
70
+ i++;
71
+ }
72
+ else if (arg === "--cwd" && next !== undefined) {
73
+ options.cwd = next;
74
+ i++;
75
+ }
76
+ else if (arg === "--max-steps" && next !== undefined) {
77
+ options.maxSteps = parsePositiveIntegerOption("--max-steps", next);
78
+ i++;
79
+ }
80
+ else if (arg === "--resume") {
81
+ if (next !== undefined && !next.startsWith("--")) {
82
+ resume = next;
83
+ i++;
84
+ }
85
+ else {
86
+ resume = true;
87
+ }
88
+ }
89
+ else if (arg === "--list-sessions") {
90
+ listSessions = true;
91
+ }
92
+ else if (arg === "--stream-json") {
93
+ streamJson = true;
94
+ }
95
+ else if (arg === "--prompt" && next !== undefined) {
96
+ prompt = next;
97
+ i++;
98
+ }
99
+ else if (arg === "--plain") {
100
+ plain = true;
101
+ }
102
+ else if (arg === "--dangerously-bypass-permissions") {
103
+ // 仅保留一个 bypass 入口(对齐 chatccc 调 claude/codex 的 bypass 模式)
104
+ options.permissionMode = "bypass";
105
+ }
106
+ else if (arg === "--help" || arg === "-h") {
107
+ help = true;
108
+ }
109
+ }
110
+ return { config, options, listSessions, resume, help, streamJson, prompt, plain };
111
+ }
112
+ async function loadRuntime() {
113
+ const [{ ChatSession }, { config: appConfig }] = await Promise.all([
114
+ import("./index.js"),
115
+ import("./config.js"),
116
+ ]);
117
+ return { ChatSession, appConfig };
118
+ }
119
+ function printHelp(appConfig) {
120
+ console.log([
121
+ "DeepCCC terminal agent",
122
+ "",
123
+ "Usage: deepccc [options]",
124
+ "",
125
+ "Options:",
126
+ ` --provider <name> API protocol: openai or anthropic (current default ${appConfig.provider})`,
127
+ ` --model <name> Model name (current default ${appConfig.model})`,
128
+ ` --effort <level> Reasoning effort: none/minimal/low/medium/high/xhigh/max (overrides config.effort)`,
129
+ ` --base-url <url> Provider API base URL (current default ${appConfig.baseURL})`,
130
+ " --api-key <key> API key",
131
+ " --cwd <path> Working directory",
132
+ " --max-steps <n> Optional tool-step limit. Omit for no step limit",
133
+ " --resume [id] Resume latest cwd session, or the explicit session id",
134
+ " --list-sessions List saved sessions and exit",
135
+ " --stream-json One-shot mode: write JSONL events to stdout",
136
+ " --prompt <text> Prompt text for --stream-json",
137
+ " --plain Force plain streaming output (no progress block renderer)",
138
+ " --dangerously-bypass-permissions Skip all permission prompts (aligns with chatccc's bypass mode)",
139
+ " --help, -h Show help",
140
+ "",
141
+ "Permissions:",
142
+ " Default mode asks before high-risk commands (rm -rf, git push --force, ...).",
143
+ " Answer y = allow once, a = allow always (saved to ~/.deepccc/allow.json),",
144
+ " n = deny, g = allow all for this session. Read-only tools and normal file",
145
+ " edits never prompt. In non-interactive mode (--stream-json) high-risk",
146
+ " commands are denied unless --dangerously-bypass-permissions is passed.",
147
+ "",
148
+ "Default config source:",
149
+ " ~/.deepccc/config.json, DEEPCCC_* environment variables, or DEEPSEEK_* aliases",
150
+ "",
151
+ ].join("\n"));
152
+ }
153
+ function formatTime(ms) {
154
+ return new Date(ms).toLocaleString();
155
+ }
156
+ function printSessions(streamJson = false) {
157
+ const sessions = listBuiltinContextSessions();
158
+ if (streamJson) {
159
+ writeJsonLine({
160
+ type: "sessions",
161
+ sessions: sessions.map((session) => ({
162
+ session_id: session.sessionId,
163
+ turns: session.totalMessages,
164
+ compacted_messages: session.compactedMessages,
165
+ has_summary: session.hasSummary,
166
+ updated_at: session.updatedAt,
167
+ cwd: session.cwd,
168
+ })),
169
+ });
170
+ return;
171
+ }
172
+ if (sessions.length === 0) {
173
+ console.log("No saved DeepCCC sessions");
174
+ return;
175
+ }
176
+ for (const session of sessions) {
177
+ const summary = session.hasSummary ? " summary=yes" : "";
178
+ const cwd = session.cwd ? ` cwd=${session.cwd}` : "";
179
+ console.log(`${session.sessionId} turns=${session.totalMessages} compacted=${session.compactedMessages}${summary} updated=${formatTime(session.updatedAt)}${cwd}`);
180
+ }
181
+ }
182
+ function stringifyConsoleArg(value) {
183
+ if (typeof value === "string")
184
+ return value;
185
+ if (value instanceof Error)
186
+ return value.stack ?? value.message;
187
+ try {
188
+ return JSON.stringify(value);
189
+ }
190
+ catch {
191
+ return String(value);
192
+ }
193
+ }
194
+ function redirectConsoleLogsToStderr() {
195
+ const write = (...args) => {
196
+ process.stderr.write(`${args.map(stringifyConsoleArg).join(" ")}\n`);
197
+ };
198
+ console.log = write;
199
+ console.info = write;
200
+ console.warn = write;
201
+ }
202
+ function writeJsonLine(event) {
203
+ process.stdout.write(`${JSON.stringify(event)}\n`);
204
+ }
205
+ function streamJsonEvent(event) {
206
+ if (event.type === "status") {
207
+ writeJsonLine({
208
+ type: "status",
209
+ phase: event.phase,
210
+ });
211
+ }
212
+ else if (event.type === "text") {
213
+ writeJsonLine({
214
+ type: "text_delta",
215
+ text: event.text,
216
+ accumulated: event.accumulated,
217
+ });
218
+ }
219
+ else if (event.type === "compact") {
220
+ writeJsonLine({
221
+ type: "compact",
222
+ compacted_messages: event.compactedMessages,
223
+ });
224
+ }
225
+ else if (event.type === "tool_use") {
226
+ writeJsonLine({
227
+ type: "tool_call",
228
+ id: event.id,
229
+ name: event.name,
230
+ input: event.input,
231
+ });
232
+ }
233
+ else if (event.type === "tool_result") {
234
+ writeJsonLine({
235
+ type: "tool_result",
236
+ tool_call_id: event.tool_use_id,
237
+ name: event.name,
238
+ content: event.content,
239
+ is_error: event.is_error,
240
+ });
241
+ }
242
+ else if (event.type === "done") {
243
+ writeJsonLine({
244
+ type: "done",
245
+ text: event.text,
246
+ });
247
+ }
248
+ else if (event.type === "error") {
249
+ writeJsonLine({
250
+ type: "error",
251
+ message: event.message,
252
+ });
253
+ }
254
+ }
255
+ async function readPromptFromStdin() {
256
+ const chunks = [];
257
+ for await (const chunk of process.stdin) {
258
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
259
+ }
260
+ return Buffer.concat(chunks).toString("utf8");
261
+ }
262
+ async function runStreamJson(args) {
263
+ redirectConsoleLogsToStderr();
264
+ if (args.listSessions) {
265
+ printSessions(true);
266
+ return 0;
267
+ }
268
+ const prompt = args.prompt ?? (!process.stdin.isTTY ? await readPromptFromStdin() : "");
269
+ if (!prompt.trim()) {
270
+ writeJsonLine({ type: "error", message: "--stream-json requires --prompt <text> or stdin input" });
271
+ return 1;
272
+ }
273
+ let runtime;
274
+ try {
275
+ runtime = await loadRuntime();
276
+ }
277
+ catch (err) {
278
+ writeJsonLine({ type: "error", message: err.message });
279
+ return 1;
280
+ }
281
+ const cwd = resolvePath(args.options.cwd ?? process.cwd());
282
+ let resolvedSession;
283
+ try {
284
+ resolvedSession = resolveBuiltinSession({ cwd, resume: args.resume });
285
+ }
286
+ catch (err) {
287
+ writeJsonLine({ type: "error", message: err.message });
288
+ return 1;
289
+ }
290
+ let session;
291
+ try {
292
+ session = new runtime.ChatSession(args.config, {
293
+ ...args.options,
294
+ cwd,
295
+ persist: true,
296
+ sessionId: resolvedSession.sessionId,
297
+ });
298
+ }
299
+ catch (err) {
300
+ writeJsonLine({ type: "error", message: err.message });
301
+ return 1;
302
+ }
303
+ writeJsonLine({
304
+ type: "start",
305
+ session_id: resolvedSession.sessionId,
306
+ mode: resolvedSession.mode,
307
+ cwd,
308
+ model: args.config.model ?? runtime.appConfig.model,
309
+ });
310
+ try {
311
+ for await (const event of session.chat(prompt)) {
312
+ streamJsonEvent(event);
313
+ }
314
+ return 0;
315
+ }
316
+ catch (err) {
317
+ writeJsonLine({ type: "error", message: err.message });
318
+ return 1;
319
+ }
320
+ }
321
+ const C = {
322
+ reset: "\x1b[0m",
323
+ dim: "\x1b[2m",
324
+ green: "\x1b[32m",
325
+ yellow: "\x1b[33m",
326
+ };
327
+ /**
328
+ * 交互模式下渲染器独占终端 stdout:普通日志只写日志文件、不回显到终端,
329
+ * 避免生成过程中任何 console 输出混入过程区块、破坏行数计数(导致重绘
330
+ * 上移不足、把上方历史内容"吃掉")。错误提示(console.error)保留回显。
331
+ */
332
+ function muteConsoleLogToFile(logPath) {
333
+ const writeFile = (level, args) => {
334
+ try {
335
+ const text = args
336
+ .map((a) => typeof a === "string" ? a
337
+ : a instanceof Error ? (a.stack ?? a.message)
338
+ : JSON.stringify(a))
339
+ .join(" ");
340
+ appendFileSync(logPath, `[${new Date().toISOString()}] [${level}] ${text}\n`, "utf8");
341
+ }
342
+ catch {
343
+ // 日志系统自身失败不影响主流程
344
+ }
345
+ };
346
+ console.log = (...args) => writeFile("LOG", args);
347
+ console.info = (...args) => writeFile("INFO", args);
348
+ console.warn = (...args) => writeFile("WARN", args);
349
+ }
350
+ async function runRepl(args) {
351
+ const { ChatSession, appConfig } = await loadRuntime();
352
+ const cwd = resolvePath(args.options.cwd ?? process.cwd());
353
+ let resolvedSession;
354
+ try {
355
+ resolvedSession = resolveBuiltinSession({ cwd, resume: args.resume });
356
+ }
357
+ catch (err) {
358
+ console.error(`${C.yellow}${err.message}${C.reset}`);
359
+ process.exit(1);
360
+ }
361
+ console.log(`${C.dim}DeepCCC agent${C.reset}`);
362
+ console.log(`${C.dim}Provider: ${args.config.provider ?? appConfig.provider}${C.reset}`);
363
+ console.log(`${C.dim}Model: ${args.config.model ?? appConfig.model}${C.reset}`);
364
+ console.log(`${C.dim}Directory: ${cwd}${C.reset}`);
365
+ console.log(`${C.dim}Session: ${resolvedSession.sessionId} (${resolvedSession.mode === "new" ? "new" : "resumed"})${C.reset}`);
366
+ console.log(`${C.dim}Type a message to chat. Double Ctrl+C interrupts generation or exits. Type exit to quit.${C.reset}`);
367
+ console.log("");
368
+ // 交互渲染模式下 console 输出只写日志文件、不回显到终端(渲染器独占 stdout,
369
+ // 避免生成中日志混入区块破坏行数)。--plain 无渲染器,不需要静音。
370
+ if (process.stdout.isTTY === true && !args.plain) {
371
+ muteConsoleLogToFile(setupFileLogging(defaultLogDir(), "index").logPath);
372
+ }
373
+ const rl = readline.createInterface({
374
+ input: process.stdin,
375
+ output: process.stdout,
376
+ prompt: `${C.green}>${C.reset} `,
377
+ });
378
+ // 权限交互:暂停过程区块渲染(恢复光标)→ readline 提问 → 恢复渲染。
379
+ // activeRenderer/activeView 由每轮 line 处理器维护,resolver 只在工具
380
+ // 执行期间被调用,此时当前轮的区块正处于运行中。
381
+ let activeRenderer = null;
382
+ let activeView = null;
383
+ const permissionResolver = async (request) => {
384
+ const renderer = activeRenderer;
385
+ const view = activeView;
386
+ if (renderer)
387
+ renderer.dispose();
388
+ process.stdout.write(`\n${C.yellow}⚠️ 高危操作需要确认${C.reset}\n`);
389
+ process.stdout.write(`${C.dim}${request.detail}${C.reset}\n`);
390
+ const answer = await new Promise((resolve) => {
391
+ rl.question(`${C.green}允许一次(y) / 永远允许(a) / 拒绝(n) / 本会话允许所有(g) >${C.reset} `, resolve);
392
+ });
393
+ if (renderer && view)
394
+ renderer.begin(view);
395
+ const v = answer.trim().toLowerCase();
396
+ if (v === "y")
397
+ return "allow";
398
+ if (v === "a")
399
+ return "allow-always";
400
+ if (v === "g")
401
+ return "allow-session";
402
+ return "deny";
403
+ };
404
+ let session;
405
+ try {
406
+ session = new ChatSession(args.config, {
407
+ ...args.options,
408
+ cwd,
409
+ persist: true,
410
+ sessionId: resolvedSession.sessionId,
411
+ permissionResolver,
412
+ });
413
+ }
414
+ catch (err) {
415
+ console.error(`${C.yellow}${err.message}${C.reset}`);
416
+ process.exit(1);
417
+ }
418
+ let currentAbort = null;
419
+ const ctrlCState = createCtrlCState();
420
+ rl.prompt();
421
+ rl.on("line", async (line) => {
422
+ ctrlCState.reset();
423
+ const input = line.trim();
424
+ if (!input) {
425
+ rl.prompt();
426
+ return;
427
+ }
428
+ if (input === "exit") {
429
+ process.stdout.write(`${C.dim}bye${C.reset}\n`);
430
+ rl.close();
431
+ return;
432
+ }
433
+ if (input === "/clear") {
434
+ session.reset();
435
+ process.stdout.write(`${C.dim}session cleared${C.reset}\n`);
436
+ rl.prompt();
437
+ return;
438
+ }
439
+ if (input === "/history") {
440
+ process.stdout.write(`${C.dim}${session.turnCount} conversation turns${C.reset}\n`);
441
+ rl.prompt();
442
+ return;
443
+ }
444
+ if (input === "/sessions") {
445
+ printSessions();
446
+ rl.prompt();
447
+ return;
448
+ }
449
+ currentAbort = new AbortController();
450
+ const signal = currentAbort.signal;
451
+ // TTY 下用"过程区块"(飞书过程卡片的终端形态):固定区域原地更新、
452
+ // 工具调用折叠为单行;非 TTY(管道/CI)或 --plain 回退为纯文本流式输出。
453
+ const useTerminalBlock = process.stdout.isTTY === true && !args.plain;
454
+ const renderer = useTerminalBlock ? new TerminalProgressRenderer() : null;
455
+ let view = null;
456
+ if (renderer) {
457
+ view = progressView({ headerTitle: "生成中..." });
458
+ activeRenderer = renderer;
459
+ activeView = view;
460
+ // 先回行首换行再 begin:让过程区块从输入行下方开始,避免首帧 \r\x1b[2K
461
+ // 清掉用户刚输入的问题行(历史文本不被刷掉)。\r\n 兼容 readline
462
+ // 行提交后光标仍停在输入行行尾的情况。
463
+ process.stdout.write("\r\n");
464
+ renderer.begin(view);
465
+ }
466
+ let rendererEnded = false;
467
+ try {
468
+ let lastAccumulated = "";
469
+ for await (const event of session.chat(input, signal)) {
470
+ if (renderer && view) {
471
+ view = reduceProgress(view, event);
472
+ if (event.type === "text" || event.type === "compact" || event.type === "status") {
473
+ renderer.render(view);
474
+ }
475
+ else {
476
+ renderer.flush();
477
+ }
478
+ }
479
+ else if (event.type === "status") {
480
+ console.log(`${C.dim}[${event.phase === "compacting" ? "compacting context" : "generating reply"}]${C.reset}`);
481
+ }
482
+ else if (event.type === "text") {
483
+ const newText = event.accumulated.slice(lastAccumulated.length);
484
+ process.stdout.write(newText);
485
+ lastAccumulated = event.accumulated;
486
+ }
487
+ else if (event.type === "done") {
488
+ if (lastAccumulated)
489
+ console.log("");
490
+ console.log(`${C.dim}[done]${C.reset}`);
491
+ }
492
+ else if (event.type === "compact") {
493
+ console.log(`${C.dim}[context compacted: ${event.compactedMessages} old messages]${C.reset}`);
494
+ }
495
+ else if (event.type === "tool_use") {
496
+ console.log(`\n${C.dim}[tool] ${event.name} ${stringifyConsoleArg(event.input)}${C.reset}`);
497
+ }
498
+ else if (event.type === "tool_result") {
499
+ const status = event.is_error ? "error" : "ok";
500
+ console.log(`${C.dim}[tool result] ${event.name ?? event.tool_use_id} ${status}${C.reset}`);
501
+ }
502
+ else if (event.type === "error") {
503
+ console.log(`\n${C.yellow}[error] ${event.message}${C.reset}`);
504
+ }
505
+ }
506
+ }
507
+ catch (err) {
508
+ if (renderer && view) {
509
+ const aborted = err instanceof Error && err.name === "AbortError";
510
+ view = progressView({ ...view, status: aborted ? "stopped" : "error", showStop: false });
511
+ renderer.end(view);
512
+ rendererEnded = true;
513
+ process.stdout.write("\n");
514
+ }
515
+ console.error(`\n${C.yellow}[error] ${err.message}${C.reset}`);
516
+ }
517
+ finally {
518
+ activeRenderer = null;
519
+ activeView = null;
520
+ if (renderer && view && !rendererEnded) {
521
+ // 定型终态区块(完成/已停止/异常结束)留在屏幕上,恢复光标
522
+ renderer.end(view);
523
+ process.stdout.write("\n");
524
+ }
525
+ currentAbort = null;
526
+ ctrlCState.reset();
527
+ }
528
+ rl.prompt();
529
+ });
530
+ rl.on("SIGINT", () => {
531
+ const action = ctrlCState.press(currentAbort !== null);
532
+ if (action === "exit") {
533
+ console.error(`\n${C.dim}bye${C.reset}`);
534
+ rl.close();
535
+ return;
536
+ }
537
+ if (action === "interrupt") {
538
+ console.error(`\n${C.yellow}[interrupting...]${C.reset}`);
539
+ currentAbort?.abort();
540
+ currentAbort = null;
541
+ return;
542
+ }
543
+ if (action === "arm-interrupt") {
544
+ console.error(`\n${C.dim}Press Ctrl+C again to interrupt current response${C.reset}`);
545
+ return;
546
+ }
547
+ if (action === "arm-exit") {
548
+ console.error(`\n${C.dim}Press Ctrl+C again to exit, or type exit${C.reset}`);
549
+ rl.prompt();
550
+ }
551
+ });
552
+ rl.on("close", () => {
553
+ process.stdout.write("\n");
554
+ process.exit(0);
555
+ });
556
+ }
557
+ /**
558
+ * skill create 子命令:deepccc skill create <name> [--scope global|project] [--description "..."]
559
+ * 默认创建为全局技能(~/.deepccc/skills/<name>/SKILL.md,Codex 结构);
560
+ * --scope project 创建为项目技能(<cwd>/.deepccc/skills/<name>/SKILL.md)。
561
+ * 新技能在下一次对话自动生效(技能索引每次 chat() 前重扫)。
562
+ */
563
+ function runSkillCreate(argv) {
564
+ const positional = argv.filter((a) => !a.startsWith("--"));
565
+ const name = positional[0];
566
+ if (!name) {
567
+ console.error("usage: deepccc skill create <name> [--scope global|project] [--description \"...\"]");
568
+ process.exit(1);
569
+ }
570
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {
571
+ console.error(`invalid skill name: ${name}(允许字母/数字/._-,不能以 . 或 - 开头)`);
572
+ process.exit(1);
573
+ }
574
+ const scopeIdx = argv.indexOf("--scope");
575
+ const scope = scopeIdx !== -1 && argv[scopeIdx + 1] === "project" ? "project" : "global";
576
+ const descIdx = argv.indexOf("--description");
577
+ const description = descIdx !== -1 ? (argv[descIdx + 1] ?? "") : "";
578
+ const base = scope === "project"
579
+ ? join(process.cwd(), ".deepccc", "skills")
580
+ : join(homedir(), ".deepccc", "skills");
581
+ const skillPath = join(base, name, "SKILL.md");
582
+ if (existsSync(skillPath)) {
583
+ console.error(`skill already exists: ${skillPath}`);
584
+ process.exit(1);
585
+ }
586
+ try {
587
+ mkdirSync(join(base, name), { recursive: true });
588
+ writeFileSync(skillPath, buildSkillTemplate(name, description), "utf8");
589
+ }
590
+ catch (err) {
591
+ console.error(`failed to create skill: ${err.message}`);
592
+ process.exit(1);
593
+ }
594
+ console.log(`created skill: ${skillPath}`);
595
+ console.log(`scope: ${scope === "project" ? "project(仅当前项目生效)" : "global(所有项目生效)"}`);
596
+ if (description)
597
+ console.log(`description: ${description}`);
598
+ console.log("hot reload: 下一次对话自动生效,无需重启");
599
+ }
600
+ async function main() {
601
+ // skill create 子命令:deepccc skill create <name> [--scope global|project] [--description "..."]
602
+ // 默认创建在全局 ~/.deepccc/skills(Codex 目录结构),--scope project 创建到 <cwd>/.deepccc/skills。
603
+ if (process.argv[2] === "skill" && process.argv[3] === "create") {
604
+ runSkillCreate(process.argv.slice(4));
605
+ return;
606
+ }
607
+ const args = parseArgs();
608
+ if (args.streamJson) {
609
+ const code = await runStreamJson(args);
610
+ process.exitCode = code;
611
+ return;
612
+ }
613
+ if (args.help) {
614
+ const { appConfig } = await loadRuntime();
615
+ printHelp(appConfig);
616
+ return;
617
+ }
618
+ if (args.listSessions) {
619
+ printSessions();
620
+ return;
621
+ }
622
+ await runRepl(args);
623
+ }
624
+ function isDirectCliInvocation() {
625
+ const current = resolvePath(fileURLToPath(import.meta.url));
626
+ const invoked = process.argv[1] ? resolvePath(process.argv[1]) : "";
627
+ return current === invoked;
628
+ }
629
+ if (isDirectCliInvocation()) {
630
+ main().catch((err) => {
631
+ console.error(`${C.yellow}startup failed: ${err.message}${C.reset}`);
632
+ process.exit(1);
633
+ });
634
+ }