chatccc 0.2.252 → 0.2.254
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.
- package/README.md +10 -1
- package/bin/cccagent.mjs +12 -3
- package/bin/chatccc.mjs +15 -6
- package/config.sample.json +2 -1
- package/deepccc-agent/README.md +40 -35
- package/deepccc-agent/os-prompts/darwin.md +8 -8
- package/deepccc-agent/os-prompts/linux.md +8 -8
- package/deepccc-agent/os-prompts/win32.md +11 -11
- package/deepccc-agent/package.json +65 -65
- package/dist/deepccc-agent/src/cli.js +634 -0
- package/dist/deepccc-agent/src/config.js +81 -0
- package/dist/deepccc-agent/src/context.js +348 -0
- package/dist/deepccc-agent/src/file-log.js +34 -0
- package/dist/deepccc-agent/src/file-tools.js +1208 -0
- package/dist/deepccc-agent/src/index.js +601 -0
- package/dist/deepccc-agent/src/permissions.js +170 -0
- package/dist/deepccc-agent/src/privacy.js +124 -0
- package/dist/deepccc-agent/src/proc-tree-kill.js +60 -0
- package/dist/deepccc-agent/src/progress/cards-helpers.js +70 -0
- package/dist/deepccc-agent/src/progress/reducer.js +102 -0
- package/dist/deepccc-agent/src/progress/terminal-renderer.js +264 -0
- package/dist/deepccc-agent/src/progress/view.js +30 -0
- package/dist/deepccc-agent/src/raw-stream-log.js +106 -0
- package/dist/deepccc-agent/src/session-search.js +276 -0
- package/dist/deepccc-agent/src/session-select.js +23 -0
- package/dist/deepccc-agent/src/sigint.js +26 -0
- package/dist/deepccc-agent/src/skills.js +178 -0
- package/dist/deepccc-agent/src/web-tools.js +246 -0
- package/dist/src/adapters/adapter-interface.js +19 -0
- package/dist/src/adapters/ccc-adapter.js +112 -0
- package/dist/src/adapters/claude-adapter.js +497 -0
- package/dist/src/adapters/claude-session-meta-store.js +92 -0
- package/dist/src/adapters/codex-adapter.js +279 -0
- package/dist/src/adapters/codex-session-meta-store.js +94 -0
- package/dist/src/adapters/cursor-adapter.js +491 -0
- package/dist/src/adapters/cursor-session-meta-store.js +116 -0
- package/dist/src/adapters/jsonl-stream.js +104 -0
- package/{src/adapters/proc-tree-kill.ts → dist/src/adapters/proc-tree-kill.js} +94 -97
- package/dist/src/adapters/raw-stream-log.js +106 -0
- package/dist/src/adapters/resource-monitor.js +113 -0
- package/dist/src/agent-activity.js +133 -0
- package/dist/src/agent-delegate-task-rpc.js +129 -0
- package/dist/src/agent-delegate-task.js +48 -0
- package/dist/src/agent-file-rpc.js +152 -0
- package/dist/src/agent-image-rpc.js +148 -0
- package/dist/src/agent-platform-routing.js +13 -0
- package/dist/src/agent-reload-config-rpc.js +23 -0
- package/dist/src/agent-rpc-body.js +87 -0
- package/dist/src/agent-stop-stuck.js +110 -0
- package/dist/src/agent-team/application/board-service.js +142 -0
- package/dist/src/agent-team/application/main-agent-service.js +151 -0
- package/dist/src/agent-team/domain/board.js +69 -0
- package/dist/src/agent-team/http/board-routes.js +213 -0
- package/dist/src/agent-team/infrastructure/filesystem-browser.js +112 -0
- package/dist/src/agent-team/infrastructure/main-agent-session-runtime.js +30 -0
- package/dist/src/agent-team/main-agent-bootstrap.js +15 -0
- package/dist/src/agent-team/repositories/board-repository.js +10 -0
- package/dist/src/agent-team/repositories/feishu-p2p-contact-store.js +64 -0
- package/dist/src/agent-team/repositories/json-board-repository.js +209 -0
- package/dist/src/agent-team/repositories/main-agent-binding-repository.js +84 -0
- package/dist/src/agent-team/sync/board-sync-adapter.js +1 -0
- package/dist/src/agent-team/web/agent-team-page.js +219 -0
- package/dist/src/agent-tool.js +10 -0
- package/dist/src/card-action-routing.js +7 -0
- package/dist/src/card-plain-text.js +101 -0
- package/dist/src/cardkit.js +158 -0
- package/dist/src/cards.js +573 -0
- package/dist/src/chatgpt-subscription-rpc.js +18 -0
- package/dist/src/chatgpt-subscription.js +199 -0
- package/dist/src/chrome-devtools-guard.js +238 -0
- package/dist/src/claude-sdk-installer.js +249 -0
- package/dist/src/codex-reset-actions.js +143 -0
- package/dist/src/config-utils.js +149 -0
- package/dist/src/config.js +808 -0
- package/dist/src/cursor-usage.js +77 -0
- package/dist/src/exit-banner.js +28 -0
- package/dist/src/feishu-api.js +1404 -0
- package/dist/src/feishu-message-ingress.js +137 -0
- package/dist/src/feishu-platform.js +97 -0
- package/dist/src/format-message.js +252 -0
- package/dist/src/git-command.js +155 -0
- package/dist/src/im-skills.js +121 -0
- package/dist/src/index.js +835 -0
- package/dist/src/litellm-proxy.js +300 -0
- package/dist/src/orchestrator.js +2096 -0
- package/dist/src/package-root.js +26 -0
- package/dist/src/platform-adapter.js +7 -0
- package/dist/src/platform-startup.js +6 -0
- package/dist/src/privacy.js +100 -0
- package/dist/src/progress/reducer.js +102 -0
- package/dist/src/progress/terminal-renderer.js +264 -0
- package/dist/src/progress/view.js +30 -0
- package/dist/src/response-stall.js +14 -0
- package/dist/src/runtime-entry.js +13 -0
- package/dist/src/runtime-reload.js +19 -0
- package/dist/src/session-chat-binding.js +183 -0
- package/dist/src/session-name.js +7 -0
- package/dist/src/session.js +2147 -0
- package/dist/src/shared-prefix.js +16 -0
- package/dist/src/shared.js +493 -0
- package/dist/src/sim-agent.js +105 -0
- package/dist/src/sim-platform.js +142 -0
- package/dist/src/sim-store.js +231 -0
- package/dist/src/simplify.js +99 -0
- package/dist/src/startup-lifecycle.js +209 -0
- package/dist/src/stream-state.js +141 -0
- package/dist/src/terminal-error.js +100 -0
- package/dist/src/trace.js +50 -0
- package/dist/src/turn-cards.js +92 -0
- package/dist/src/update-command-guard.js +114 -0
- package/{src/web-ui.ts → dist/src/web-ui.js} +753 -839
- package/dist/src/wechat-platform.js +545 -0
- package/package.json +76 -74
- package/deepccc-agent/LICENSE +0 -201
- package/deepccc-agent/bin/deepccc.mjs +0 -26
- package/deepccc-agent/docs/cache-hit-rate-1.jpg +0 -0
- package/deepccc-agent/docs/cache-hit-rate-2.jpg +0 -0
- package/deepccc-agent/package-lock.json +0 -2027
- package/deepccc-agent/src/__tests__/chat-session.test.ts +0 -877
- package/deepccc-agent/src/__tests__/cli-json.test.ts +0 -49
- package/deepccc-agent/src/__tests__/config.test.ts +0 -34
- package/deepccc-agent/src/__tests__/context.test.ts +0 -341
- package/deepccc-agent/src/__tests__/file-tools.test.ts +0 -240
- package/deepccc-agent/src/__tests__/permissions.test.ts +0 -199
- package/deepccc-agent/src/__tests__/privacy.test.ts +0 -318
- package/deepccc-agent/src/__tests__/progress-reducer.test.ts +0 -121
- package/deepccc-agent/src/__tests__/session-search.test.ts +0 -262
- package/deepccc-agent/src/__tests__/session-select.test.ts +0 -116
- package/deepccc-agent/src/__tests__/sigint.test.ts +0 -56
- package/deepccc-agent/src/__tests__/skills.test.ts +0 -284
- package/deepccc-agent/src/__tests__/terminal-renderer.test.ts +0 -247
- package/deepccc-agent/src/__tests__/web-tools.test.ts +0 -220
- package/deepccc-agent/src/cli.ts +0 -682
- package/deepccc-agent/src/config.ts +0 -101
- package/deepccc-agent/src/context.ts +0 -465
- package/deepccc-agent/src/file-log.ts +0 -38
- package/deepccc-agent/src/file-tools.ts +0 -1493
- package/deepccc-agent/src/index.ts +0 -710
- package/deepccc-agent/src/permissions.ts +0 -226
- package/deepccc-agent/src/privacy.ts +0 -141
- package/deepccc-agent/src/proc-tree-kill.ts +0 -61
- package/deepccc-agent/src/progress/cards-helpers.ts +0 -76
- package/deepccc-agent/src/progress/reducer.ts +0 -113
- package/deepccc-agent/src/progress/terminal-renderer.ts +0 -294
- package/deepccc-agent/src/progress/view.ts +0 -77
- package/deepccc-agent/src/raw-stream-log.ts +0 -124
- package/deepccc-agent/src/session-search.ts +0 -370
- package/deepccc-agent/src/session-select.ts +0 -48
- package/deepccc-agent/src/sigint.ts +0 -50
- package/deepccc-agent/src/skills.ts +0 -205
- package/deepccc-agent/src/web-tools.ts +0 -313
- package/deepccc-agent/tsconfig.build.json +0 -13
- package/deepccc-agent/tsconfig.json +0 -13
- package/deepccc-agent/vitest.config.ts +0 -7
- package/src/__tests__/adapter-interface.test.ts +0 -152
- package/src/__tests__/agent-activity.test.ts +0 -86
- package/src/__tests__/agent-delegate-task-rpc.test.ts +0 -165
- package/src/__tests__/agent-image-rpc.test.ts +0 -34
- package/src/__tests__/agent-platform-routing.test.ts +0 -26
- package/src/__tests__/agent-reload-config-rpc.test.ts +0 -99
- package/src/__tests__/agent-rpc-body.test.ts +0 -42
- package/src/__tests__/builtin-chat-session.test.ts +0 -532
- package/src/__tests__/builtin-cli-json.test.ts +0 -39
- package/src/__tests__/builtin-config.test.ts +0 -26
- package/src/__tests__/builtin-context.test.ts +0 -319
- package/src/__tests__/builtin-file-tools.test.ts +0 -240
- package/src/__tests__/builtin-permissions.test.ts +0 -219
- package/src/__tests__/builtin-session-search.test.ts +0 -262
- package/src/__tests__/builtin-session-select.test.ts +0 -116
- package/src/__tests__/builtin-sigint.test.ts +0 -56
- package/src/__tests__/builtin-skills.test.ts +0 -284
- package/src/__tests__/builtin-web-tools.test.ts +0 -220
- package/src/__tests__/card-action-routing.test.ts +0 -18
- package/src/__tests__/card-plain-text.test.ts +0 -45
- package/src/__tests__/cardkit.test.ts +0 -60
- package/src/__tests__/cards.test.ts +0 -607
- package/src/__tests__/ccc-adapter.test.ts +0 -194
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +0 -89
- package/src/__tests__/chatgpt-subscription.test.ts +0 -135
- package/src/__tests__/chrome-devtools-guard.test.ts +0 -165
- package/src/__tests__/claude-adapter.test.ts +0 -614
- package/src/__tests__/claude-raw-stream-log.test.ts +0 -96
- package/src/__tests__/claude-sdk-installer.test.ts +0 -285
- package/src/__tests__/codex-adapter.test.ts +0 -331
- package/src/__tests__/codex-raw-stream-log.test.ts +0 -170
- package/src/__tests__/codex-reset-actions.test.ts +0 -146
- package/src/__tests__/config-reload.test.ts +0 -284
- package/src/__tests__/config-sample.test.ts +0 -97
- package/src/__tests__/config-utils.test.ts +0 -40
- package/src/__tests__/config.test.ts +0 -395
- package/src/__tests__/crash-logging.test.ts +0 -360
- package/src/__tests__/cursor-adapter.test.ts +0 -890
- package/src/__tests__/cursor-session-meta-store.test.ts +0 -212
- package/src/__tests__/feishu-api.test.ts +0 -60
- package/src/__tests__/feishu-avatar.test.ts +0 -504
- package/src/__tests__/feishu-message-ingress.test.ts +0 -138
- package/src/__tests__/feishu-platform.test.ts +0 -75
- package/src/__tests__/fixtures/codex_simple_text.jsonl +0 -4
- package/src/__tests__/fixtures/codex_with_tool.jsonl +0 -6
- package/src/__tests__/fixtures/cursor_partial_only.jsonl +0 -5
- package/src/__tests__/fixtures/cursor_partial_with_final.jsonl +0 -13
- package/src/__tests__/fixtures/cursor_with_tool_call.jsonl +0 -12
- package/src/__tests__/format-message.test.ts +0 -316
- package/src/__tests__/git-command.test.ts +0 -288
- package/src/__tests__/im-skills.test.ts +0 -125
- package/src/__tests__/jsonl-stream.test.ts +0 -79
- package/src/__tests__/orchestrator.test.ts +0 -1268
- package/src/__tests__/package-files.test.ts +0 -24
- package/src/__tests__/platform-startup.test.ts +0 -19
- package/src/__tests__/privacy.test.ts +0 -198
- package/src/__tests__/proc-tree-kill.test.ts +0 -108
- package/src/__tests__/progress-reducer.test.ts +0 -121
- package/src/__tests__/raw-stream-log.test.ts +0 -106
- package/src/__tests__/response-stall.test.ts +0 -49
- package/src/__tests__/restart.test.ts +0 -232
- package/src/__tests__/session-ccc-config.test.ts +0 -66
- package/src/__tests__/session.test.ts +0 -3004
- package/src/__tests__/shared-prefix.test.ts +0 -36
- package/src/__tests__/sim-agent.test.ts +0 -174
- package/src/__tests__/sim-platform.test.ts +0 -93
- package/src/__tests__/sim-store.test.ts +0 -214
- package/src/__tests__/simplify.test.ts +0 -283
- package/src/__tests__/startup-lifecycle.test.ts +0 -231
- package/src/__tests__/stop-session.test.ts +0 -162
- package/src/__tests__/stream-state.test.ts +0 -164
- package/src/__tests__/terminal-error.test.ts +0 -54
- package/src/__tests__/terminal-renderer.test.ts +0 -247
- package/src/__tests__/update-command-guard.test.ts +0 -144
- package/src/__tests__/web-ui.test.ts +0 -438
- package/src/__tests__/wechat-platform.test.ts +0 -111
- package/src/adapters/adapter-interface.ts +0 -217
- package/src/adapters/ccc-adapter.ts +0 -150
- package/src/adapters/claude-adapter.ts +0 -673
- package/src/adapters/claude-session-meta-store.ts +0 -120
- package/src/adapters/codex-adapter.ts +0 -426
- package/src/adapters/codex-session-meta-store.ts +0 -131
- package/src/adapters/cursor-adapter.ts +0 -681
- package/src/adapters/cursor-session-meta-store.ts +0 -154
- package/src/adapters/jsonl-stream.ts +0 -157
- package/src/adapters/raw-stream-log.ts +0 -124
- package/src/adapters/resource-monitor.ts +0 -141
- package/src/agent-activity.ts +0 -175
- package/src/agent-delegate-task-rpc.ts +0 -153
- package/src/agent-delegate-task.ts +0 -91
- package/src/agent-file-rpc.ts +0 -172
- package/src/agent-image-rpc.ts +0 -168
- package/src/agent-platform-routing.ts +0 -28
- package/src/agent-reload-config-rpc.ts +0 -34
- package/src/agent-rpc-body.ts +0 -92
- package/src/agent-stop-stuck.ts +0 -129
- package/src/card-action-routing.ts +0 -14
- package/src/card-plain-text.ts +0 -108
- package/src/cardkit.ts +0 -179
- package/src/cards.ts +0 -684
- package/src/chatgpt-subscription-rpc.ts +0 -27
- package/src/chatgpt-subscription.ts +0 -299
- package/src/chrome-devtools-guard.ts +0 -318
- package/src/claude-sdk-installer.ts +0 -324
- package/src/codex-reset-actions.ts +0 -184
- package/src/config-utils.ts +0 -211
- package/src/config.ts +0 -1063
- package/src/cursor-usage.ts +0 -128
- package/src/exit-banner.ts +0 -33
- package/src/feishu-api.ts +0 -1616
- package/src/feishu-message-ingress.ts +0 -195
- package/src/feishu-platform.ts +0 -159
- package/src/format-message.ts +0 -293
- package/src/git-command.ts +0 -202
- package/src/im-skills.ts +0 -149
- package/src/index.ts +0 -1089
- package/src/litellm-proxy.ts +0 -374
- package/src/orchestrator.ts +0 -2543
- package/src/platform-adapter.ts +0 -70
- package/src/platform-startup.ts +0 -16
- package/src/privacy.ts +0 -118
- package/src/progress/reducer.ts +0 -113
- package/src/progress/terminal-renderer.ts +0 -294
- package/src/progress/view.ts +0 -77
- package/src/response-stall.ts +0 -28
- package/src/runtime-reload.ts +0 -34
- package/src/session-chat-binding.ts +0 -292
- package/src/session-name.ts +0 -8
- package/src/session.ts +0 -2659
- package/src/shared-prefix.ts +0 -29
- package/src/shared.ts +0 -552
- package/src/sim-agent.ts +0 -167
- package/src/sim-platform.ts +0 -177
- package/src/sim-store.ts +0 -317
- package/src/simplify.ts +0 -120
- package/src/startup-lifecycle.ts +0 -250
- package/src/stream-state.ts +0 -177
- package/src/terminal-error.ts +0 -129
- package/src/trace.ts +0 -51
- package/src/turn-cards.ts +0 -118
- package/src/update-command-guard.ts +0 -165
- 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
|
+
}
|