@chatcode/chatcode-cli-test 1.0.46 → 3.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write apps/cli/README.md
5
+ README.md: 3ff830cff37e8ab848a82c05982997c513b29b08
6
+ README.zh.md: b3077be13064bf50e47eefb641f4739c664a0008
package/README.md CHANGED
@@ -1,157 +1,63 @@
1
- # ChatCode-cli使用手册
1
+ # `@chatcode/chatcode-cli`
2
2
 
3
- ChatCode-cli ChatCode官方推出的命令行工具,让你可以在终端中直接与 ChatCode 进行交互,完成代码编写、调试、重构等任务。
3
+ English | [中文](README.zh.md)
4
4
 
5
- ## 安装教程
5
+ `chatcode-cli` is the formal launcher and `cco` is its short form; both commands share one parser and start the TUI/CLI profile when invoked without arguments. The hidden `dsh` bin remains a compatibility alias. Profiles are ordered stacks of plugin-bundle patch layers under the user's own overrides. SDK and ACP are profiles, not separate public bins. The Python runtime wheel packages the same launcher; the SDK defaults to `sdk`, and the minimal example selects `sdk-minimal`. [`src/args.ts`](src/args.ts) owns the command grammar, and [`src/bin.ts`](src/bin.ts) loads only the selected runner. Invalid commands, options from another mode, and fatal configuration or boot failures exit nonzero.
6
6
 
7
- ### 1. 一键安装
7
+ ## Entry modes
8
8
 
9
- 如果本机缺少 Node.js,安装脚本会从国内 Node.js 镜像安装 Node.js,然后在安装 ChatCode CLI 与 OpenSpec 时为对应 npm 命令临时指定国内 registry。
9
+ | Command | Purpose |
10
+ |---|---|
11
+ | `chatcode-cli` / `cco` | Start the default TUI/CLI profile. |
12
+ | `chatcode-cli --profile <name>` | Boot the named profile under `$CHATCODE_CLI_HOME/profiles/<name>`. |
13
+ | `chatcode-cli --profile <name> --from-default-profile <template>` | Create a new custom profile from a shipped template, then boot it. |
14
+ | `chatcode-cli --profile acp` | Serve automation clients over ACP stdio until disconnect. |
15
+ | `chatcode-cli --profile headless "job"` | Run one fresh persisted session, print the final answer, and exit. |
16
+ | `chatcode-cli --profile sdk` | Serve SDK clients over JSON-RPC stdio until shutdown or disconnect. |
17
+ | `chatcode-cli --profile sdk-minimal` | Serve SDK clients with the standalone minimal agent tree. |
18
+ | `chatcode-cli web` / `cco web` | Boot the Web profile. |
19
+ | `chatcode-cli plugin --profile <name> <pnpm args>` | Manage a profile's plugins by forwarding to pnpm in the profile directory. |
20
+ | `chatcode-cli migrate --dry-run` | Print the copy-only migration plan from legacy user homes. |
21
+ | `chatcode-cli migrate --apply` | Revalidate and apply that plan without overwriting destination data. |
10
22
 
11
- 如果本机已有 Node.js `>= 20`,脚本会直接复用,不会更新。检测到低于 `20` Node.js 时,脚本会先询问是否升级;拒绝升级将退出安装。
23
+ The invoking directory is the default workspace root. The `web`, `headless`, `sdk`, `sdk-minimal`, and `acp` profiles auto-initialize on first use from shipped templates. Create another profile at an unused, non-shipped name with `--from-default-profile`, or initialize a base-backed profile through `chatcode-cli plugin`. The `desktop` name is reserved for the Electron-owned profile, so the CLI rejects boot, config-dump, and plugin-management requests for it.
12
24
 
13
- 安装 ChatCode CLI 前,脚本会通过 `--registry=https://registry.npmmirror.com` 检查 `@chatcode/chatcode-cli` 是否为最新版本。如果已安装版本不是最新版本,会提示当前版本和最新版本,并询问是否更新;拒绝更新时会继续保留当前已安装版本。
25
+ ## App arguments
14
26
 
15
- Windows 会检查 Git for Windows Git Bash。建议使用 Git `>= 2.21.0`;检测到更低版本时会显示 warning,但不会阻断安装。
16
-
17
- 如果本机已安装 nvm,脚本会优先通过 nvm 安装和切换 Node.js。脚本不会持久写入 npm prefix,也不会设置与 nvm 冲突的 `NPM_CONFIG_PREFIX`。
18
-
19
- 安装完成后,脚本会同步安装 `@fission-ai/openspec@latest`,在 `~/.chatcode-cli/openspec-workspace` 中执行 OpenSpec 初始化,并把当前 OpenSpec 版本生成的 `/opsx:*` 命令和 skills 同步到 `~/.chatcode-cli`,安装后可直接使用 `chatcode-cli` 和 `openspec`。
20
-
21
- macOS/Linux:
27
+ The launcher parses only its own flags and hands everything after them to the booted profile, where any injected app plugin may parse the shared immutable snapshot ([`dsh-cmdline`](../../packages/boot/cmdline/README.md)). The first token the launcher does not recognize starts the app's arguments:
22
28
 
23
29
  ```sh
24
- curl -fsSL "http://gitlab.tianti.tg.unicom.local/kubernetesAMP/visualDesignPlatform/chatcode-cli/-/raw/dev_v1alpha/scripts/install/install.sh" | sh
30
+ chatcode-cli --profile web --port 8080 # --port belongs to the web app
31
+ cco --resume <id> # --resume belongs to the default terminal app
32
+ chatcode-cli --profile headless "run the tests"
33
+ chatcode-cli --profile web --help # the web app's flags, not the launcher's
34
+ chatcode-cli --help # the launcher's own help
25
35
  ```
26
36
 
27
- Windows PowerShell:
37
+ <a id="profiles"></a>
38
+ ## Profiles
28
39
 
29
- ```powershell
30
- irm "http://gitlab.tianti.tg.unicom.local/kubernetesAMP/visualDesignPlatform/chatcode-cli/-/raw/dev_v1alpha/scripts/install/install-windows.ps1" | iex
31
- ```
40
+ A profile directory holds a `package.json` (out-of-tree plugin dependencies plus the profile manifest `dsh.profile` with its ordered `bundles` list) and a `cordis.patch.yml` (the user's own patch layer). `dsh-hmr`, when enabled in YAML, watches the profile manifest and both profile and home patch files, then recomposes all layers through one serialized reload. Without HMR, changes apply on restart. Edits arriving during watcher registration use the same nonfatal reload reporting as later edits. [Plugin Manager](../../packages/boot/plugin-manager/README.md) shares package operations and the profile write lock with `chatcode-cli plugin`; package updates retain disabled bundle selections. CLI package commands inherit authentication variables and terminal descriptors, including interactive build approval; service calls retain their scrubbed environment and captured diagnostics.
32
41
 
33
- 安装前可以审阅 `scripts/install/install.sh`、`scripts/install/install-macos.sh`、`scripts/install/install-linux.sh` `scripts/install/install-windows.ps1`。
42
+ The tree composes over an empty root:
43
+ - each bundle's patch in `dsh.profile.bundles` order
44
+ - then the profile's `cordis.patch.yml`, then the home-level `$CHATCODE_CLI_HOME/cordis.patch.yml`
45
+ - then `--patch` overlays
34
46
 
35
- 验证安装成功:
36
-
37
- ```
38
- node --version
39
- chatcode-cli --version
40
- openspec --version
41
- ```
47
+ Bundles named in `dsh.profile.bundles` resolve from the ChatCode CLI installation first (`@deepseek-ai/dsh-base`, `@deepseek-ai/dsh-web-app`, `@deepseek-ai/dsh-headless`, `@deepseek-ai/dsh-sdk-app`, `@deepseek-ai/dsh-sdk-minimal`, `@deepseek-ai/dsh-acp-app`), then from the profile's own `node_modules`, where pnpm installs DSH-compatible plugins.
42
48
 
43
- Windows 安装后如果新打开标签页仍提示 `node` `chatcode-cli` 不存在,请关闭所有 Windows Terminal 窗口后重新打开 PowerShell。
49
+ Use `--dump-default-config` and `--dump-config` to inspect the composed tree without booting it.
44
50
 
45
- ### 2. 已有 Node.js 时手动安装
51
+ The [CLI behavior reference](reference/README.md) owns exact layer precedence, flags, shutdown behavior, deployment defaults, and source execution. The [startup and reload failure table](../../packages/boot/app-boot/README.md#startup-and-reload-failures) compares optional and required plugin failures with configuration HMR.
46
52
 
47
- 方式1:外部安装
53
+ ## Optional overlays
48
54
 
49
- ```bash
50
- npm install -g @chatcode/chatcode-cli@latest
51
- ```
52
-
53
- 方式2:内部仓库安装
54
-
55
- ```bash
56
- npm install -g @chatcode/chatcode-cli@latest --registry http://ccp.tianti.tg.unicom.local/artifactory/api/npm/ckp-npm-virtual/
57
- ```
58
-
59
- ### 3. 配置ChatCode CLI设置
60
-
61
- 创建或编辑 `~/.chatcode-cli/settings.json`,添加以下配置,注意需要配置您自己的 OPENAI 地址 URL、密钥 APIKEY 和使用的模型名。
62
-
63
- ```json
64
- {
65
- "env": {
66
- "OPENAI_API_KEY": "[您的OPENAI密钥APIKEY]",
67
- "OPENAI_BASE_URL": "[您的OPENAI地址URL]",
68
- "CHATCODE_CLI_MAX_OUTPUT_TOKENS": "[按照您模型的最大token数]",
69
- "CHATCODE_CLI_DISABLE_NONESSENTIAL_TRAFFIC": "1",
70
- "OPENAI_MODEL": "[您的模型名]"
71
- },
72
- "permissions": {
73
- "allow": [],
74
- "deny": []
75
- },
76
- "model": "[您的模型名]"
77
- }
78
- ```
55
+ `config/examples/` ships opt-in overlays for GitHub review webhooks, session-local Schedule, memory MCP servers, and runtime Cordis tools. They are never part of a default profile; the [user guides](../../docs/user/guide/index.md) and [developer practice guides](../../docs/user/develop/practice/index.md) own setup and safety instructions.
79
56
 
80
- DeepSeek V4 users can configure external OpenAI-compatible models with `"model": "deepseek-v4-pro"` or `"model": "deepseek-v4-flash"`. These model names now use the same generic OpenAI-compatible path as other external models; the DeepSeek V4-specific adapter code is retained in the source tree but disabled at the entry point. The built-in ChatCode default remains `DeepSeek-V3`.
81
-
82
- ### 4. 启动ChatCode CLI
83
-
84
- ```bash
85
- chatcode-cli
86
- ```
87
-
88
- ### 5. 首次启动配置
89
-
90
- 首次启动需要配置信任当前文件夹,直接回车即可。
91
-
92
-
93
-
94
- ## 卸载和重新安装
95
-
96
- 如果遇到问题需要重新安装 ChatCode CLI:
97
-
98
- ```bash
99
- # 卸载
100
- npm uninstall -g @chatcode/chatcode-cli
101
-
102
- # 重新安装
103
- # 方式1:外部安装
104
- npm install -g @chatcode/chatcode-cli@latest
105
- # 方式2:内部仓库安装
106
- npm install -g @chatcode/chatcode-cli@latest --registry http://ccp.tianti.tg.unicom.local/artifactory/api/npm/ckp-npm-virtual/
107
- ```
57
+ ## Development
108
58
 
59
+ Production runs require built package and frontend artifacts. From the repository root, run `pnpm run build` separately, then use `pnpm cco <args...>` to run the TypeScript entry and forward every argument; the [source-execution reference](reference/README.md#source-execution) owns the module-resolution contract.
109
60
 
61
+ The `@chatcode/chatcode-cli/profile-boot` export provides the shared profile lifecycle to the Desktop host. A resolved application profile supplies its own installation anchor for runtime package resolution while retaining the ChatCode CLI home patch, proxy environment, telemetry switch, patch reload, and bounded shutdown.
110
62
 
111
- ## 常用内置命令
112
-
113
- ChatCode CLI 中可用的内置命令的参考。
114
-
115
- 在 ChatCode CLI 中输入 `/` 可以查看所有可用命令,或输入 `/` 后跟任何字母来筛选。ChatCode CLI 还包括捆绑的 skills,如 `/simplify`、`/batch`、`/debug` 和 `/loop`,当您输入 `/` 时会与内置命令一起显示。
116
-
117
- 部分常用命令如下表,在下表中,`<arg>` 表示必需的参数,`[arg]` 表示可选参数。
118
-
119
- | 命令 | 用途 |
120
- | :----------------------------------- | :----------------------------------------------------------- |
121
- | `/add-dir <path>` | 向当前会话添加新的工作目录 |
122
- | `/agents` | 管理 agent 配置 |
123
- | `/btw <question>` | 提出快速[附加问题],无需添加到对话中 |
124
- | `/clear` | 清除对话历史记录并释放上下文。别名:`/reset`、`/new` |
125
- | `/color [color|default]` | 为当前会话设置提示栏颜色。可用颜色:`red`、`blue`、`green`、`yellow`、`purple`、`orange`、`pink`、`cyan`。使用 `default` 重置 |
126
- | `/compact [instructions]` | 压缩对话,可选择性地提供焦点说明 |
127
- | `/config` | 打开[设置]界面以调整主题、模型、[输出样式]和其他偏好设置。别名:`/settings` |
128
- | `/context` | 将当前上下文使用情况可视化为彩色网格。显示上下文密集型工具、内存膨胀和容量警告的优化建议 |
129
- | `/copy [N]` | 将最后一个助手响应复制到剪贴板。传递数字 `N` 以复制第 N 个最新响应:`/copy 2` 复制倒数第二个。当存在代码块时,显示交互式选择器以选择单个块或完整响应。在选择器中按 `w` 将选择内容写入文件而不是剪贴板,这在 SSH 上很有用 |
130
- | `/cost` | 显示令牌使用统计信息。 |
131
- | `/diff` | 打开交互式差异查看器,显示未提交的更改和每轮差异。使用左/右箭头在当前 git 差异和单个轮次之间切换,使用上/下浏览文件 |
132
- | `/doctor` | 诊断并验证您的安装和设置 |
133
- | `/effort [low|medium|high|max|auto]` | 设置模型工作量级别。`low`、`medium` 和 `high` 在会话间保持。`max` 仅适用于当前会话,需要支持的模型。`auto` 重置为模型默认值。不带参数时,显示当前级别。立即生效,无需等待当前响应完成 |
134
- | `/exit` | 退出 CLI。别名:`/quit` |
135
- | `/export [filename]` | 将当前对话导出为纯文本。使用文件名时,直接写入该文件。不使用文件名时,打开对话框以复制到剪贴板或保存到文件 |
136
- | `/fast [on|off]` | 切换快速模式开启或关闭 |
137
- | `/branch [name]` | 在此点创建当前对话的分支。别名:`/fork` |
138
- | `/help` | 显示帮助和可用命令 |
139
- | `/hooks` | 查看工具事件的 hook 配置 |
140
- | `/init` | 使用 `AGENTS.md` 指南初始化项目。 |
141
- | `/insights` | 生成报告,分析您的 ChatCode 会话,包括项目领域、交互模式和摩擦点 |
142
- | `/keybindings` | 打开或创建您的快捷键配置文件 |
143
- | `/login` | 登录到您的 账户(暂未支持) |
144
- | `/logout` | 从您的 账户登出(暂未支持) |
145
- | `/model [model]` | 选择或更改 AI 模型。对于支持的模型,使用左/右箭头[调整工作量级别]。更改立即生效,无需等待当前响应完成 |
146
- | `/plan [description]` | 直接从提示进入计划模式。传递可选描述以进入计划模式并立即开始该任务,例如 `/plan fix the auth bug` |
147
- | `/plugin` | 管理 ChatCode Plugins |
148
- | `/rename [name]` | 重命名当前会话并在提示栏上显示名称。不使用名称时,从对话历史记录自动生成一个 |
149
- | `/resume [session]` | 按 ID 或名称恢复对话,或打开会话选择器。别名:`/continue` |
150
- | `/rewind` | 将对话和/或代码倒回到上一个点,或从选定的消息进行总结。别名:`/checkpoint` |
151
- | `/security-review` | 分析当前分支上的待处理更改以查找安全漏洞。审查 git 差异并识别注入、身份验证问题和数据泄露等风险 |
152
- | `/skills` | 列出可用的 skills |
153
- | `/stats` | 可视化每日使用情况、会话历史记录、连续记录和模型偏好 |
154
- | `/status` | 打开设置界面(状态选项卡),显示版本、模型、账户和连接性。在 ChatCode响应时工作,无需等待当前响应完成 |
155
- | `/tasks` | 列出并管理后台任务 |
156
- | `/theme` | 更改颜色主题。包括浅色和深色变体、色盲友好(道尔顿化)主题和使用您终端颜色调色板的 ANSI 主题 |
157
- | `/vim` | 在 Vim 和普通编辑模式之间切换 |
63
+ The [Web failure matrix](tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts) runs the built CLI through startup failures and native configuration HMR with `awaitWriteFinish` enabled in `test:expected`. It verifies authenticated HTTP responses, diagnostics, recovery, process exits, and disposal without model API calls; the [startup acceptance](tests/profiles/web/tests/web-best-effort-startup.expected.e2e.ts) also covers the shipped required Web dependencies and port conflicts.
package/README.zh.md ADDED
@@ -0,0 +1,63 @@
1
+ # `@chatcode/chatcode-cli`
2
+
3
+ [English](README.md) | 中文
4
+
5
+ `chatcode-cli` 是正式启动命令,`cco` 是其简写;两者共用同一解析器,且无参数时都启动 TUI/CLI profile。隐藏的 `dsh` bin 仅作为兼容别名保留。profile 由多个插件组合包 patch 层按顺序叠加而成,其上再应用用户自己的覆盖配置。SDK 与 ACP(Agent Client Protocol)都是 profile,而不是独立的公开可执行命令。Python 运行时 wheel 包中也包含同一个启动器;SDK 默认使用 `sdk`,极简示例选择 `sdk-minimal`。[`src/args.ts`](src/args.ts) 负责命令语法,[`src/bin.ts`](src/bin.ts) 只加载选中的运行器。无效命令、来自其他模式的选项,以及致命的配置或启动错误都会以非零状态退出。
6
+
7
+ ## 入口模式
8
+
9
+ | 命令 | 用途 |
10
+ |---|---|
11
+ | `chatcode-cli` / `cco` | 启动默认 TUI/CLI profile。 |
12
+ | `chatcode-cli --profile <name>` | 启动位于 `$CHATCODE_CLI_HOME/profiles/<name>` 的指定 profile。 |
13
+ | `chatcode-cli --profile <name> --from-default-profile <template>` | 从随附模板创建新的自定义 profile,然后启动它。 |
14
+ | `chatcode-cli --profile acp` | 通过 ACP stdio 为自动化客户端提供服务,直至断开连接。 |
15
+ | `chatcode-cli --profile headless "job"` | 运行一个全新的持久化会话,打印最终答案并退出。 |
16
+ | `chatcode-cli --profile sdk` | 通过 JSON-RPC stdio 为 SDK 客户端提供服务,直至关闭或断开连接。 |
17
+ | `chatcode-cli --profile sdk-minimal` | 以独立极简 agent(智能体)配置树为 SDK 客户端提供服务。 |
18
+ | `chatcode-cli web` / `cco web` | 启动 Web profile。 |
19
+ | `chatcode-cli plugin --profile <name> <pnpm args>` | 通过在 profile 目录中转发给 pnpm 来管理该 profile 的插件。 |
20
+ | `chatcode-cli migrate --dry-run` | 输出旧版用户目录的只复制迁移计划。 |
21
+ | `chatcode-cli migrate --apply` | 重新校验并应用计划,绝不覆盖目标数据。 |
22
+
23
+ 运行命令时所在的目录将作为默认 workspace 根目录。`web`、`headless`、`sdk`、`sdk-minimal` 和 `acp` profile 在首次使用时会从随附模板自动初始化。使用 `--from-default-profile` 可以基于这些模板之一,在尚未使用的非内置名称处创建其他 profile;通过 `chatcode-cli plugin` 则可以初始化一个以 base 为基础的 profile。`desktop` 名称保留给 Electron 持有的 profile,因此 CLI(命令行界面)会拒绝针对它的启动、配置 dump 和插件管理请求。
24
+
25
+ ## 应用参数
26
+
27
+ 启动器只解析自身的 flag,并将其后的所有内容交给已启动的 profile;注入该 profile 的任意应用插件都可以解析这份共享的不可变快照([`dsh-cmdline`](../../packages/boot/cmdline/README.zh.md))。启动器无法识别的第一个 token 标志着应用参数的开始:
28
+
29
+ ```sh
30
+ chatcode-cli --profile web --port 8080 # --port belongs to the web app
31
+ cco --resume <id> # --resume belongs to the default terminal app
32
+ chatcode-cli --profile headless "run the tests"
33
+ chatcode-cli --profile web --help # the web app's flags, not the launcher's
34
+ chatcode-cli --help # the launcher's own help
35
+ ```
36
+
37
+ <a id="profiles"></a>
38
+ ## Profile
39
+
40
+ profile 目录包含一个 `package.json`,其中记录树外插件依赖,以及 profile manifest(元数据清单)`dsh.profile`、其中按顺序排列的 `bundles` 列表;还包含一个 `cordis.patch.yml`,其中保存用户自己的 patch 层。在 YAML 中启用的 `dsh-hmr` 监视 profile manifest、profile 与 home 级 patch 文件,再通过统一串行重载重新组合所有层。未启用 HMR 时,更改在重启后生效。监听器注册期间发生的编辑与后续编辑使用相同的非致命重载错误报告。[插件管理器](../../packages/boot/plugin-manager/README.zh.md) 与 `chatcode-cli plugin` 共享包操作和 profile 写锁;更新依赖会保留已停用的组合包选择。CLI 包操作继承认证环境和终端描述符,支持交互式构建批准;service 调用保留清理后的环境并捕获诊断。
41
+
42
+ 配置树以空根为起点,依次叠加以下配置层:
43
+ - `dsh.profile.bundles` 中各组合包的 patch
44
+ - profile 自身的 `cordis.patch.yml`,然后是 home 级的 `$CHATCODE_CLI_HOME/cordis.patch.yml`
45
+ - `--patch` 指定的覆盖层
46
+
47
+ `dsh.profile.bundles` 中列出的组合包先从 ChatCode CLI 安装目录解析(`@deepseek-ai/dsh-base`、`@deepseek-ai/dsh-web-app`、`@deepseek-ai/dsh-headless`、`@deepseek-ai/dsh-sdk-app`、`@deepseek-ai/dsh-sdk-minimal`、`@deepseek-ai/dsh-acp-app`),再从 profile 自身的 `node_modules` 解析;pnpm 会将 DSH 兼容插件安装到该目录。
48
+
49
+ 使用 `--dump-default-config` 和 `--dump-config` 可在不启动的情况下检查组合后的配置树。
50
+
51
+ 层的确切优先级、flag、关闭行为、部署默认值和源码执行方式,以 [CLI 行为参考](reference/README.zh.md)为准。[启动与重载失败表](../../packages/boot/app-boot/README.zh.md#startup-and-reload-failures)对比 optional、required 插件启动失败与配置 HMR 的行为。
52
+
53
+ ## 可选覆盖层
54
+
55
+ `config/examples/` 交付 GitHub 评审 webhook、会话内 Schedule、记忆 MCP 服务器与运行时 Cordis 工具的可选覆盖层。它们绝不属于默认 profile;设置与安全说明由[用户指南](../../docs/user/guide/index.zh.md)和[开发实战指南](../../docs/user/develop/practice/index.zh.md)负责。
56
+
57
+ ## 开发
58
+
59
+ 生产运行需要已构建的包与前端产物。请在仓库根目录单独运行 `pnpm run build`,然后使用 `pnpm cco <args...>` 运行 TypeScript 入口并转发所有参数;模块解析约定以[源码执行参考](reference/README.zh.md#source-execution)为准。
60
+
61
+ `@chatcode/chatcode-cli/profile-boot` 导出向 Desktop Host 提供共享 profile 生命周期。已解析的应用 profile 为运行时包解析指定自己的安装锚点,同时沿用 ChatCode CLI home patch、代理环境、遥测开关、patch 热重载和有界关闭。
62
+
63
+ [Web 失败矩阵](tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts)在 `test:expected` 中通过构建后的 CLI 验证启动失败与启用 `awaitWriteFinish` 的原生配置 HMR。它不调用模型 API,而是检查经过认证的 HTTP 响应、诊断、恢复、进程退出与 dispose;[启动验收测试](tests/profiles/web/tests/web-best-effort-startup.expected.e2e.ts)还覆盖随附 Web 的必需依赖与端口冲突。
package/lib/bin.js ADDED
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { StartupError, loadLayeredEnv } from "@deepseek-ai/dsh-app-boot";
5
+ import { resolveChatCodeCliHome } from "@deepseek-ai/dsh-home-paths";
6
+ import { Command, CommanderError, InvalidArgumentError } from "commander";
7
+ import { randomUUID } from "node:crypto";
8
+ import { mkdir, writeFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { inspect } from "node:util";
11
+ //#region lib/types/args.js
12
+ /**
13
+ * Commander adapter for the ChatCode CLI launcher.
14
+ *
15
+ * The launcher parses only what it owns — which profile to boot, which extra
16
+ * patch overlays to apply, and the config dumps — and hands **everything after
17
+ * its own flags** to the booted tree verbatim, where injected app plugins parse
18
+ * their own flag families and print their own `--help` (see
19
+ * `@deepseek-ai/dsh-cmdline`). Launcher flags therefore come first: the first
20
+ * token this parser does not recognize starts the inner arguments, so
21
+ * `chatcode-cli --profile cli --resume abc` boots the cli profile with
22
+ * `--resume abc`, and `chatcode-cli --profile web -h` prints the web app's
23
+ * help, not this one's.
24
+ *
25
+ * `chatcode-cli <name>` abbreviates `chatcode-cli --profile <name>`; `plugin`
26
+ * manages a profile's plugin dependencies by forwarding to pnpm. A bare
27
+ * public launcher boots `$CHATCODE_CLI_DEFAULT_PROFILE`, then legacy
28
+ * `$DSH_DEFAULT_PROFILE`, else the builtin `cli` profile.
29
+ * @module @chatcode/chatcode-cli/args
30
+ */
31
+ /** Environment variables controlling the bare-launcher default profile. */
32
+ const DEFAULT_PROFILE_ENV = "CHATCODE_CLI_DEFAULT_PROFILE";
33
+ const LEGACY_DEFAULT_PROFILE_ENV = "DSH_DEFAULT_PROFILE";
34
+ /** Resolve the bare-launcher profile with canonical-before-legacy precedence. */
35
+ function resolveDefaultProfile(env = process.env) {
36
+ const canonical = env[DEFAULT_PROFILE_ENV]?.trim();
37
+ if (canonical !== void 0 && canonical !== "") return canonical;
38
+ const legacy = env[LEGACY_DEFAULT_PROFILE_ENV]?.trim();
39
+ return legacy !== void 0 && legacy !== "" ? legacy : "cli";
40
+ }
41
+ /**
42
+ * Repeatable single-value collector: `--patch a.yml --patch b.yml`. Never
43
+ * variadic — a variadic `--patch` would swallow the inner arguments.
44
+ */
45
+ const collect = (value, previous = []) => [...previous, value];
46
+ function selectProfile(value, previous) {
47
+ if (previous !== void 0) throw new InvalidArgumentError("select a profile only once");
48
+ return value;
49
+ }
50
+ function rejectElectronProfile(program, profile) {
51
+ if (profile.toLowerCase() === "desktop") program.error("error: profile \"desktop\" is managed exclusively by the Electron application");
52
+ }
53
+ /** The launcher's own help text; each app prints its own. */
54
+ const HELP_EXAMPLES = `
55
+ Examples:
56
+ chatcode-cli boot the default CLI profile (short: cco)
57
+ chatcode-cli web boot the web profile (short: cco web)
58
+ chatcode-cli rescue --from-default-profile web
59
+ create rescue from the shipped web template, then boot it
60
+ chatcode-cli headless "run the tests" answer one task, print the result, and exit
61
+ chatcode-cli cli --patch ./extra.yml boot the CLI profile with one extra overlay
62
+ chatcode-cli cli --resume <session> arguments after launcher flags reach the app
63
+ chatcode-cli web --help the web app's own flags and help
64
+ chatcode-cli plugin --profile cli add <package>
65
+ install a plugin into the CLI profile
66
+ chatcode-cli migrate --dry-run print a safe ~/.dsh migration plan
67
+ chatcode-cli migrate --apply apply the reviewed copy-only plan
68
+
69
+ The bare-command default comes from ${DEFAULT_PROFILE_ENV} when set.
70
+ ${LEGACY_DEFAULT_PROFILE_ENV} is accepted only when the canonical variable is unset.
71
+ `;
72
+ /**
73
+ * Resolve a boot or dump invocation from the launcher flags and the leftover
74
+ * inner arguments.
75
+ * @param program - the command whose options were parsed.
76
+ * @param profile - the profile these flags boot.
77
+ * @param options - the launcher flags commander collected.
78
+ * @param args - the leftover arguments, in argv order.
79
+ * @returns the resolved invocation.
80
+ */
81
+ function resolveBoot(program, profile, options, args) {
82
+ const patches = options.patch ?? [];
83
+ if (patches.includes("")) program.error("error: --patch needs a path");
84
+ if (options.fromDefaultProfile === "") program.error("error: --from-default-profile needs a name");
85
+ if (options.dumpConfig !== true && options.dumpDefaultConfig !== true) return {
86
+ mode: "profile",
87
+ profile,
88
+ fromDefaultProfile: options.fromDefaultProfile,
89
+ patches,
90
+ args
91
+ };
92
+ if (options.dumpConfig === true && options.dumpDefaultConfig === true) program.error("error: --dump-config and --dump-default-config are mutually exclusive");
93
+ if (args.length > 0) program.error(`error: config dumps take no app arguments, got ${args.map((argument) => JSON.stringify(argument)).join(" ")}`);
94
+ const defaultOnly = options.dumpDefaultConfig === true;
95
+ if (defaultOnly && patches.length > 0) program.error("error: --dump-default-config prints the bundle layers and takes no --patch");
96
+ return {
97
+ mode: "dump-config",
98
+ profile,
99
+ fromDefaultProfile: options.fromDefaultProfile,
100
+ defaultOnly,
101
+ patches
102
+ };
103
+ }
104
+ /**
105
+ * Resolve argv into one invocation, or print and exit for help, version, or an
106
+ * error.
107
+ * @param argv - arguments after the Node binary and script.
108
+ * @param version - version string printed by `--version`.
109
+ * @returns the resolved invocation.
110
+ */
111
+ function parseChatCodeCliArgs(argv, version) {
112
+ const first = argv[0];
113
+ let resolved;
114
+ const program = new Command();
115
+ program.name("chatcode-cli").version(version, "-V, --version", "output the version number").usage("[--profile] [name] [options] [app-args...]\n chatcode-cli plugin --profile <name> <pnpm-args...>\n chatcode-cli migrate [--dry-run|--apply] [--json]").description("ChatCode CLI: start a profile-backed coding agent. A bare command starts the TUI/CLI profile.").addHelpText("after", HELP_EXAMPLES).exitOverride().helpOption(false).helpCommand(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the booted profile's app (see: chatcode-cli --profile <name> --help)").option("--profile <name>", `the profile under $CHATCODE_CLI_HOME/profiles to boot (default: $${DEFAULT_PROFILE_ENV} or cli)`, selectProfile).option("--from-default-profile <name>", "initialize a new custom profile from a shipped profile template").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed profile tree and exit").option("--dump-default-config", "print the profile tree without its user layer or --patch overlays and exit").action((args, options) => {
116
+ if (options.profile === void 0) {
117
+ if (args.some((argument) => argument === "-h" || argument === "--help")) program.help();
118
+ if (options.fromDefaultProfile !== void 0) program.error("error: --from-default-profile requires --profile <name>");
119
+ options.profile = resolveDefaultProfile();
120
+ }
121
+ const profile = options.profile;
122
+ if (profile === "") program.error("error: --profile needs a name");
123
+ rejectElectronProfile(program, profile);
124
+ resolved = resolveBoot(program, profile, options, args);
125
+ });
126
+ if (first === "plugin") {
127
+ const plugin = program.command("plugin").description("manage a profile's plugins by forwarding the remaining arguments to pnpm in the profile directory");
128
+ plugin.requiredOption("--profile <name>", "the profile whose plugins to manage (initialized on first use)", selectProfile).allowUnknownOption().argument("[args...]", "pnpm arguments, forwarded verbatim (add <pkg>, remove <pkg>, why <pkg>, ...)").action((args, options) => {
129
+ if (options.profile === "") program.error("error: --profile needs a name");
130
+ rejectElectronProfile(plugin, options.profile);
131
+ if (args.length === 0) program.error("error: plugin needs pnpm arguments to forward (e.g. add <package>)");
132
+ resolved = {
133
+ mode: "plugin",
134
+ profile: options.profile,
135
+ args
136
+ };
137
+ });
138
+ }
139
+ if (first === "migrate") {
140
+ const migrate = program.command("migrate").description("plan or apply non-destructive migration from legacy Harness and TUI homes");
141
+ migrate.option("--dry-run", "print the migration plan without writing (the default)").option("--apply", "apply the reviewed copy-only migration plan").option("--json", "print machine-readable JSON").action((options) => {
142
+ if (options.apply === true && options.dryRun === true) migrate.error("error: --apply and --dry-run are mutually exclusive");
143
+ resolved = {
144
+ mode: "migrate",
145
+ apply: options.apply === true,
146
+ json: options.json === true
147
+ };
148
+ });
149
+ }
150
+ try {
151
+ const expanded = first !== void 0 && !first.startsWith("-") && first !== "plugin" && first !== "migrate" ? ["--profile", ...argv] : argv;
152
+ program.parse(expanded, { from: "user" });
153
+ } catch (error) {
154
+ return process.exit(error instanceof CommanderError ? error.exitCode : 1);
155
+ }
156
+ /* v8 ignore next -- an action resolves or Commander throws */
157
+ if (resolved === void 0) throw new Error("ChatCode CLI: no invocation resolved");
158
+ return resolved;
159
+ }
160
+ //#endregion
161
+ //#region lib/types/startup-diagnostics.js
162
+ /** Save original startup diagnostics while keeping the terminal report concise. */
163
+ /** Wait for stderr to finish the write before the failed process exits. */
164
+ function writeStderr(text) {
165
+ return new Promise((resolve, reject) => {
166
+ process.stderr.write(text, (error) => {
167
+ if (error) reject(error);
168
+ else resolve();
169
+ });
170
+ });
171
+ }
172
+ /**
173
+ * Print the startup summary and save a private, uniquely named report under DSH_HOME/logs.
174
+ * Failed writes print the complete report to stderr instead of claiming a saved path.
175
+ * @param error - startup audit failure retaining plugin metadata and original errors.
176
+ * @param context - resolved Harness home, application version, and selected profile.
177
+ * @param write - terminal output sink; awaited before returning, defaults to stderr.
178
+ * @returns after saving or printing the report and completing terminal writes.
179
+ */
180
+ async function reportStartupFailure(error, context, write = writeStderr) {
181
+ const now = (/* @__PURE__ */ new Date()).toISOString();
182
+ const report = "WARNING: Raw diagnostics may contain configuration or credential values from plugin errors. Review before sharing.\n\n" + inspect({
183
+ timestamp: now,
184
+ dshVersion: context.version,
185
+ nodeVersion: process.version,
186
+ platform: process.platform,
187
+ arch: process.arch,
188
+ profile: context.profile,
189
+ error
190
+ }, {
191
+ depth: null,
192
+ maxArrayLength: null,
193
+ maxStringLength: null,
194
+ showHidden: true,
195
+ customInspect: false,
196
+ getters: false,
197
+ colors: false
198
+ }) + "\n";
199
+ await write(`${error.message}\n`);
200
+ const logDir = join(context.home, "logs");
201
+ const logPath = join(logDir, `startup-${now.replaceAll(":", "-")}-${randomUUID()}.log`);
202
+ try {
203
+ await mkdir(logDir, {
204
+ recursive: true,
205
+ mode: 448
206
+ });
207
+ await writeFile(logPath, report, {
208
+ flag: "wx",
209
+ mode: 384
210
+ });
211
+ } catch (writeError) {
212
+ await write(`\ndsh: warning: could not write startup diagnostics: ${String(writeError)}\nFull diagnostics:\n${report}`);
213
+ return;
214
+ }
215
+ await write(`\nFull diagnostics: ${logPath}\n`);
216
+ }
217
+ //#endregion
218
+ //#region lib/types/bin.js
219
+ /**
220
+ * Command-line entry for ChatCode CLI and its compatibility aliases.
221
+ * @module @chatcode/chatcode-cli/bin
222
+ */
223
+ /* v8 ignore file -- built-bin acceptance exercises this self-executing dispatch. */
224
+ function readVersion() {
225
+ const manifest = JSON.parse(readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf8"));
226
+ return typeof manifest.version === "string" ? manifest.version : "0.0.0";
227
+ }
228
+ /**
229
+ * Set the canonical process identity shared by all launcher aliases.
230
+ */
231
+ function initializeProcessIdentity() {
232
+ process.title = "chatcode-cli";
233
+ }
234
+ /**
235
+ * Run the public ChatCode CLI command-line interface.
236
+ * @returns a promise that settles when the selected command mode finishes.
237
+ */
238
+ async function runCli() {
239
+ initializeProcessIdentity();
240
+ const version = readVersion();
241
+ const invocation = parseChatCodeCliArgs(process.argv.slice(2), version);
242
+ switch (invocation.mode) {
243
+ case "profile": {
244
+ const legacyHome = process.env.DSH_HOME?.trim();
245
+ if (process.env.CHATCODE_CLI_HOME?.trim() || legacyHome === void 0 || legacyHome === "") {
246
+ const { detectPendingHomeMigrations, resolveHomeMigrationRoots } = await import("./home-migration-BejD6LQn.js");
247
+ if ((await detectPendingHomeMigrations(resolveHomeMigrationRoots())).length > 0) process.stderr.write("ChatCode CLI detected legacy user data. Preview the non-destructive migration with \"chatcode-cli migrate --dry-run\"; nothing is migrated automatically.\n");
248
+ }
249
+ const { runProfile } = await import("./profile-boot.js");
250
+ try {
251
+ await runProfile({
252
+ environment: loadLayeredEnv("ChatCode CLI"),
253
+ profile: invocation.profile,
254
+ fromDefaultProfile: invocation.fromDefaultProfile,
255
+ patchFiles: invocation.patches,
256
+ args: invocation.args
257
+ });
258
+ } catch (error) {
259
+ if (!(error instanceof StartupError)) throw error;
260
+ await reportStartupFailure(error, {
261
+ home: resolveChatCodeCliHome(),
262
+ version,
263
+ profile: invocation.profile
264
+ });
265
+ process.exit(1);
266
+ }
267
+ break;
268
+ }
269
+ case "migrate": {
270
+ const { applyHomeMigration, formatHomeMigrationPlan, planHomeMigration, resolveHomeMigrationRoots } = await import("./home-migration-BejD6LQn.js");
271
+ const roots = resolveHomeMigrationRoots();
272
+ const migrations = [[roots.source, roots.destination], [roots.tuiSource, roots.tuiDestination]];
273
+ if (!invocation.apply) {
274
+ const plans = await Promise.all(migrations.map(([source, destination]) => planHomeMigration(source, destination)));
275
+ process.stdout.write(invocation.json ? `${JSON.stringify({ plans }, null, 2)}\n` : `${plans.map(formatHomeMigrationPlan).join("\n\n")}\n`);
276
+ break;
277
+ }
278
+ const results = [];
279
+ for (const [source, destination] of migrations) {
280
+ const plan = await planHomeMigration(source, destination);
281
+ process.stderr.write(`${formatHomeMigrationPlan(plan)}\n\n`);
282
+ results.push(await applyHomeMigration(plan));
283
+ }
284
+ process.stdout.write(invocation.json ? `${JSON.stringify({ results }, null, 2)}\n` : `Applied: copied ${results.reduce((sum, result) => sum + result.copied.length, 0)} files; skipped ${results.reduce((sum, result) => sum + result.skipped.length, 0)}.\nMigration records: ${results.flatMap((result) => result.recordPath ?? []).join(", ") || "not written"}\n`);
285
+ break;
286
+ }
287
+ case "plugin": {
288
+ const { runPlugin } = await import("./plugin-DrWxOiv2.js");
289
+ process.exit(await runPlugin(invocation.profile, invocation.args));
290
+ break;
291
+ }
292
+ case "dump-config": {
293
+ const { runDumpConfig } = await import("./dump-config-Bn-CzGIR.js");
294
+ runDumpConfig(invocation.profile, invocation.defaultOnly, invocation.patches, invocation.fromDefaultProfile);
295
+ break;
296
+ }
297
+ default: throw new Error(`ChatCode CLI: unhandled invocation mode ${JSON.stringify(invocation)}`);
298
+ }
299
+ }
300
+ if (import.meta.main) await runCli();
301
+ //#endregion
302
+ export { initializeProcessIdentity, runCli };