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
@@ -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
+ }