clawt 3.9.13 → 3.10.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/README.md +80 -1
- package/README.zh-CN.md +81 -0
- package/dist/index.js +1935 -592
- package/dist/postinstall.js +1626 -283
- package/docs/config-file.md +2 -0
- package/docs/config.md +2 -1
- package/docs/init.md +3 -2
- package/docs/project-config.md +10 -1
- package/docs/spec.md +69 -2
- package/package.json +1 -1
- package/src/commands/alias.ts +5 -4
- package/src/commands/completion.ts +2 -1
- package/src/commands/config.ts +25 -7
- package/src/commands/cover-validate.ts +3 -2
- package/src/commands/create.ts +8 -7
- package/src/commands/home.ts +2 -1
- package/src/commands/init.ts +13 -6
- package/src/commands/list.ts +6 -4
- package/src/commands/merge.ts +8 -7
- package/src/commands/projects.ts +5 -3
- package/src/commands/remove.ts +7 -6
- package/src/commands/reset.ts +3 -2
- package/src/commands/resume.ts +10 -7
- package/src/commands/run.ts +8 -7
- package/src/commands/status.ts +16 -11
- package/src/commands/sync.ts +4 -3
- package/src/commands/tasks.ts +8 -6
- package/src/commands/validate.ts +7 -6
- package/src/constants/ai-prompts.ts +11 -11
- package/src/constants/config.ts +30 -0
- package/src/constants/index.ts +3 -2
- package/src/constants/messages/alias.ts +44 -14
- package/src/constants/messages/cli-descriptions.ts +91 -0
- package/src/constants/messages/common.ts +221 -36
- package/src/constants/messages/completion.ts +43 -14
- package/src/constants/messages/config.ts +61 -18
- package/src/constants/messages/cover-validate.ts +43 -14
- package/src/constants/messages/create.ts +16 -5
- package/src/constants/messages/home.ts +19 -6
- package/src/constants/messages/index.ts +2 -0
- package/src/constants/messages/init.ts +45 -14
- package/src/constants/messages/interactive-panel.ts +183 -29
- package/src/constants/messages/merge.ts +140 -38
- package/src/constants/messages/post-create.ts +59 -19
- package/src/constants/messages/projects.ts +51 -14
- package/src/constants/messages/remove.ts +50 -15
- package/src/constants/messages/reset.ts +14 -4
- package/src/constants/messages/resume.ts +116 -19
- package/src/constants/messages/run.ts +165 -35
- package/src/constants/messages/status.ts +84 -23
- package/src/constants/messages/sync.ts +54 -17
- package/src/constants/messages/tasks.ts +21 -7
- package/src/constants/messages/update.ts +35 -11
- package/src/constants/messages/validate.ts +218 -57
- package/src/constants/progress.ts +17 -6
- package/src/constants/project-config.ts +17 -0
- package/src/constants/prompt.ts +18 -2
- package/src/constants/tasks-template.ts +56 -2
- package/src/hooks/post-create.ts +5 -2
- package/src/index.ts +6 -5
- package/src/types/config.ts +2 -0
- package/src/utils/alias.ts +2 -1
- package/src/utils/claude.ts +10 -9
- package/src/utils/config-strategy.ts +3 -3
- package/src/utils/dry-run.ts +2 -2
- package/src/utils/formatter.ts +18 -11
- package/src/utils/i18n.ts +63 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/interactive-panel-render.ts +6 -3
- package/src/utils/progress-render.ts +11 -9
- package/src/utils/prompt.ts +2 -1
- package/src/utils/task-executor.ts +10 -7
- package/src/utils/task-file.ts +2 -1
- package/src/utils/terminal.ts +9 -9
- package/src/utils/ui-prompts.ts +4 -3
- package/src/utils/update-checker.ts +1 -1
- package/src/utils/validate-branch.ts +16 -9
- package/src/utils/validate-core.ts +2 -1
- package/src/utils/validate-runner.ts +2 -2
- package/src/utils/worktree-matcher.ts +9 -7
- package/tests/unit/commands/alias.test.ts +4 -0
- package/tests/unit/commands/completion.test.ts +14 -0
- package/tests/unit/commands/config.test.ts +61 -28
- package/tests/unit/commands/cover-validate.test.ts +13 -2
- package/tests/unit/commands/init.test.ts +6 -2
- package/tests/unit/commands/merge.test.ts +14 -0
- package/tests/unit/commands/run.test.ts +17 -0
- package/tests/unit/commands/tasks.test.ts +39 -9
- package/tests/unit/constants/config.test.ts +16 -1
- package/tests/unit/constants/messages-post-create.test.ts +93 -1
- package/tests/unit/constants/messages.test.ts +85 -1
- package/tests/unit/hooks/post-create.test.ts +32 -0
- package/tests/unit/utils/alias.test.ts +14 -0
- package/tests/unit/utils/claude.test.ts +24 -4
- package/tests/unit/utils/config-strategy.test.ts +21 -0
- package/tests/unit/utils/conflict-resolver.test.ts +24 -4
- package/tests/unit/utils/formatter.test.ts +21 -0
- package/tests/unit/utils/i18n.test.ts +91 -0
- package/tests/unit/utils/progress.test.ts +39 -18
- package/tests/unit/utils/prompt.test.ts +25 -2
- package/tests/unit/utils/task-file.test.ts +73 -10
- package/tests/unit/utils/terminal-cmux.test.ts +19 -4
- package/tests/unit/utils/update-checker.test.ts +2 -0
- package/tests/unit/utils/validate-branch.test.ts +26 -1
- package/tests/unit/utils/validation.test.ts +2 -2
- package/tests/unit/utils/worktree-matcher.test.ts +2 -0
package/docs/config-file.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
```json
|
|
18
18
|
{
|
|
19
|
+
"language": "en",
|
|
19
20
|
"autoDeleteBranch": false,
|
|
20
21
|
"claudeCodeCommand": "claude",
|
|
21
22
|
"autoPullPush": false,
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
|
|
35
36
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
36
37
|
| ------------------ | --------- | --------- | -------------------------------------------------- |
|
|
38
|
+
| `language` | `'en' \| 'zh-CN'` | `"en"` | 界面语言:`en`(英文)、`zh-CN`(中文)。控制 CLI 输出、提示消息、命令描述等所有面向用户的文本语言。修改后无需重启,下次命令执行时自动生效 |
|
|
37
39
|
| `autoDeleteBranch` | `boolean` | `false` | 移除 worktree 时是否自动删除对应本地分支(无需每次确认);merge 成功后是否自动清理 worktree 和分支;run 任务被中断(Ctrl+C)后是否自动清理本次创建的 worktree 和分支 |
|
|
38
40
|
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks` 时和 `clawt resume` 在 worktree 中打开交互式界面。可被项目级配置 `claudeCodeCommand` 覆盖(优先级:项目级 > 全局级,详见 [project-config.md](./project-config.md)) |
|
|
39
41
|
| `autoPullPush` | `boolean` | `false` | merge 成功后是否自动执行 git pull 和 git push |
|
package/docs/config.md
CHANGED
|
@@ -26,7 +26,7 @@ clawt config reset
|
|
|
26
26
|
2. 列出所有配置项供用户选择(`Enquirer.Select`),每项显示:
|
|
27
27
|
- 配置项名称
|
|
28
28
|
- 当前值(布尔值绿色/黄色,字符串和数字青色)
|
|
29
|
-
- 配置项描述(暗淡色 dim
|
|
29
|
+
- 配置项描述(暗淡色 dim,根据当前 `language` 配置显示中文或英文描述,通过 `getI18nConfigDescriptions()` 获取)
|
|
30
30
|
- 对象类型配置项(如 `aliases`)标灰不可选,提示用户通过专用命令管理
|
|
31
31
|
3. 用户选择某个配置项后,根据值类型自动选择提示策略:
|
|
32
32
|
- **boolean 类型** → `Select`(true / false)
|
|
@@ -84,6 +84,7 @@ clawt config reset
|
|
|
84
84
|
**实现要点:**
|
|
85
85
|
|
|
86
86
|
- 配置项类型定义:`ConfigItemDefinition` 新增可选字段 `allowedValues`(`readonly string[]`),仅对 string 类型有效,用于枚举值校验和交互式 Select 提示
|
|
87
|
+
- 配置描述国际化:`getI18nConfigDescriptions()`(`src/constants/config.ts`)根据当前语言返回中文或英文的配置项描述映射,供 `interactiveConfigEditor` 在构建选择列表时使用
|
|
87
88
|
- 值解析与提示策略:`src/utils/config-strategy.ts` 中的 `parseConfigValue()`(CLI 字符串解析)和 `promptConfigValue()`(交互式提示),基于类型和 `allowedValues` 自动分发
|
|
88
89
|
- 交互式配置编辑:`handleInteractiveConfigSet` 调用通用的 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),传入 `CONFIG_DEFINITIONS` 和 `disabledKeys`(对象类型配置项禁用映射),不再在 config 命令中直接构建选择列表和调用 `promptConfigValue`
|
|
89
90
|
- `saveConfig(config)`:`src/utils/config.ts` 中的通用配置写入函数,将完整配置对象持久化到文件
|
package/docs/init.md
CHANGED
|
@@ -50,7 +50,7 @@ clawt init show --json
|
|
|
50
50
|
- 配置缺少 `clawtMainWorkBranch` 字段 → 抛出错误 `项目配置缺少主工作分支信息,请重新执行 clawt init 设置主工作分支`
|
|
51
51
|
- 配置存在且合法 → 继续
|
|
52
52
|
2.5. **`--json` 模式判断**:如果指定了 `--json` 选项,直接以 JSON 格式输出当前项目配置,跳过交互式流程并返回
|
|
53
|
-
3. **交互式配置编辑**:调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),基于 `PROJECT_CONFIG_DEFINITIONS` 构建配置项列表(详见 [project-config.md](./project-config.md)
|
|
53
|
+
3. **交互式配置编辑**:调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),基于 `PROJECT_CONFIG_DEFINITIONS` 构建配置项列表(详见 [project-config.md](./project-config.md))。配置项描述根据当前 `language` 配置自动选择中文或英文,通过 `getI18nProjectConfigDescriptions()` 获取国际化描述并替换 `PROJECT_CONFIG_DEFINITIONS` 中的 `description` 字段
|
|
54
54
|
- 列出所有项目配置项,显示名称、当前值和描述
|
|
55
55
|
- 用户选择配置项后,根据值类型自动选择输入方式(与全局配置的交互式编辑逻辑一致)
|
|
56
56
|
4. **持久化修改**:将修改后的值合并到当前配置,经 `normalizeProjectConfig` 归一化处理后写入配置文件(可选字段的空字符串会被移除,等同于未设置)
|
|
@@ -84,7 +84,8 @@ clawt init show --json
|
|
|
84
84
|
|
|
85
85
|
- `init show` 子命令从 JSON 展示改为交互式面板,调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`)实现通用交互式配置编辑
|
|
86
86
|
- 配置项定义来自 `PROJECT_CONFIG_DEFINITIONS`(`src/constants/project-config.ts`),详见 [项目级配置文档](./project-config.md)
|
|
87
|
-
-
|
|
87
|
+
- 国际化支持:`init show` 通过 `getI18nProjectConfigDescriptions()` 获取当前语言的配置项描述,替换 `PROJECT_CONFIG_DEFINITIONS` 中的 `description` 字段后再传入 `interactiveConfigEditor`,使交互式面板根据 `language` 配置显示中文或英文描述
|
|
88
|
+
- 消息常量:`MESSAGES.INIT_SELECT_PROMPT`(选择配置项提示语)、`MESSAGES.INIT_SET_SUCCESS`(修改成功提示),定义在 `src/constants/messages/init.ts`(已双语化,根据 `language` 配置自动选择语言)
|
|
88
89
|
- `handleInitShow` 使用 `normalizeProjectConfig` 对修改后的配置进行归一化处理:可选字段(如 `validateRunCommand`、`postCreate`、`claudeCodeCommand`)设为空字符串时自动移除该键,避免 JSON 文件中出现冗余的 `"field": ""` 条目
|
|
89
90
|
|
|
90
91
|
---
|
package/docs/project-config.md
CHANGED
|
@@ -61,6 +61,9 @@ export const PROJECT_DEFAULT_CONFIG: Required<ProjectConfig> = deriveDefaultConf
|
|
|
61
61
|
|
|
62
62
|
/** 项目配置项描述映射(从 PROJECT_CONFIG_DEFINITIONS 自动派生) */
|
|
63
63
|
export const PROJECT_CONFIG_DESCRIPTIONS: Record<keyof Required<ProjectConfig>, string> = deriveConfigDescriptions(PROJECT_CONFIG_DEFINITIONS);
|
|
64
|
+
|
|
65
|
+
/** 获取国际化项目配置项描述映射(根据当前语言返回中文或英文描述) */
|
|
66
|
+
export function getI18nProjectConfigDescriptions(): Record<keyof Required<ProjectConfig>, string>;
|
|
64
67
|
```
|
|
65
68
|
|
|
66
69
|
#### 相关类型定义
|
|
@@ -109,6 +112,12 @@ export type ProjectConfigDefinitions = {
|
|
|
109
112
|
| `resolveClaudeCodeCommand` | `() => string` | 解析当前项目生效的 Claude Code 启动指令,优先级:项目级配置 > 全局配置 |
|
|
110
113
|
| `normalizeProjectConfig` | `(config: ProjectConfig, key: string, value: unknown) => ProjectConfig` | 归一化项目配置:可选字段的空字符串等同于未设置,从对象中删除该键以保持 JSON 文件整洁 |
|
|
111
114
|
|
|
115
|
+
**国际化函数**(位于 `src/constants/project-config.ts`):
|
|
116
|
+
|
|
117
|
+
| 函数 | 签名 | 说明 |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| `getI18nProjectConfigDescriptions` | `() => Record<keyof Required<ProjectConfig>, string>` | 获取国际化项目配置项描述映射,根据当前语言返回中文或英文描述 |
|
|
120
|
+
|
|
112
121
|
#### 设置方式
|
|
113
122
|
|
|
114
123
|
通过 `clawt init` 命令设置(详见 [init.md](./init.md)):
|
|
@@ -124,7 +133,7 @@ clawt init -b <branchName>
|
|
|
124
133
|
clawt init show
|
|
125
134
|
```
|
|
126
135
|
|
|
127
|
-
`init show` 子命令调用通用的 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),基于 `PROJECT_CONFIG_DEFINITIONS`
|
|
136
|
+
`init show` 子命令调用通用的 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),基于 `PROJECT_CONFIG_DEFINITIONS` 构建配置项列表,提供交互式面板供用户查看和修改所有项目配置项。配置项描述通过 `getI18nProjectConfigDescriptions()` 获取国际化版本,根据当前 `language` 配置自动显示中文或英文。
|
|
128
137
|
|
|
129
138
|
#### 前置校验
|
|
130
139
|
|
package/docs/spec.md
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
- [2. 核心概念](#2-核心概念)
|
|
12
12
|
- [2.5 验证分支](#25-验证分支)
|
|
13
13
|
- [2.6 项目级配置](#26-项目级配置)(详见 [project-config.md](./project-config.md))
|
|
14
|
-
- [2.7
|
|
14
|
+
- [2.7 国际化(i18n)](#27-国际化i18n)
|
|
15
|
+
- [2.8 通用交互式配置编辑器](#28-通用交互式配置编辑器)
|
|
15
16
|
- [3. 全局目录结构](#3-全局目录结构)
|
|
16
17
|
- [4. 命令总览](#4-命令总览)
|
|
17
18
|
- [5. 需求场景详细设计](#5-需求场景详细设计)
|
|
@@ -206,7 +207,49 @@ export const VALIDATE_BRANCH_PREFIX = 'clawt-validate-';
|
|
|
206
207
|
|
|
207
208
|
详细的配置项列表、类型定义、工具函数和设置方式见 [项目级配置文档](./project-config.md)。
|
|
208
209
|
|
|
209
|
-
### 2.7
|
|
210
|
+
### 2.7 国际化(i18n)
|
|
211
|
+
|
|
212
|
+
Clawt 支持中英双语界面,通过全局配置 `language` 字段控制输出语言。
|
|
213
|
+
|
|
214
|
+
**核心机制:**
|
|
215
|
+
|
|
216
|
+
- **语言配置**:`ClawtConfig.language`(`'en'` | `'zh-CN'`),默认 `'en'`
|
|
217
|
+
- **语言获取**:`getCurrentLanguage()`(`src/utils/i18n.ts`),优先使用内存缓存,缓存不存在时从配置文件读取,读取失败默认 `'en'`
|
|
218
|
+
- **消息国际化**:所有消息常量(`src/constants/messages/` 下)从单语字符串改为 `{ en, 'zh-CN' }` 双语映射,运行时通过 `createMessages()` 根据当前语言选择对应文本。导出的消息对象类型与原单语版本保持一致,消费方无需改动
|
|
219
|
+
- **配置描述国际化**:`getI18nConfigDescriptions()` 和 `getI18nProjectConfigDescriptions()` 分别提供全局配置和项目配置项描述的国际化版本,供交互式配置编辑器使用
|
|
220
|
+
- **CLI 描述国际化**:`src/constants/messages/cli-descriptions.ts` 集中管理 Commander.js 的命令描述和选项文本(双语映射),通过 `CLI_DESCRIPTIONS` 导出当前语言的描述对象
|
|
221
|
+
|
|
222
|
+
**i18n 工具函数**(`src/utils/i18n.ts`):
|
|
223
|
+
|
|
224
|
+
| 函数 | 签名 | 说明 |
|
|
225
|
+
| --- | --- | --- |
|
|
226
|
+
| `getCurrentLanguage` | `() => Language` | 获取当前语言(优先缓存 → 配置文件 → 默认 `'en'`) |
|
|
227
|
+
| `setCurrentLanguage` | `(lang: Language) => void` | 设置当前语言(用于测试和 CLI 初始化) |
|
|
228
|
+
| `resetLanguageCache` | `() => void` | 重置语言缓存(配置变更后调用,使下次读取时重新加载) |
|
|
229
|
+
| `createMessages` | `<T>(i18nMap: T) => { [K in keyof T]: ExtractLang<T[K]> }` | 从双语映射创建当前语言的消息对象 |
|
|
230
|
+
|
|
231
|
+
**双语映射模式:**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// 定义双语消息映射
|
|
235
|
+
const MESSAGES_I18N = {
|
|
236
|
+
NOT_MAIN_WORKTREE: {
|
|
237
|
+
en: 'Please run clawt in the root directory of the main worktree',
|
|
238
|
+
'zh-CN': '请在主 worktree 的根目录下执行 clawt',
|
|
239
|
+
},
|
|
240
|
+
BRANCH_EXISTS: {
|
|
241
|
+
en: (name: string) => `Branch ${name} already exists`,
|
|
242
|
+
'zh-CN': (name: string) => `分支 ${name} 已存在`,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// 运行时根据语言选择
|
|
247
|
+
export const MESSAGES = createMessages(MESSAGES_I18N);
|
|
248
|
+
// MESSAGES.NOT_MAIN_WORKTREE → string(当前语言的文本)
|
|
249
|
+
// MESSAGES.BRANCH_EXISTS → (name: string) => string(当前语言的函数)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 2.8 通用交互式配置编辑器
|
|
210
253
|
|
|
211
254
|
`interactiveConfigEditor`(`src/utils/config-strategy.ts`)是一个通用的交互式配置编辑函数,供全局配置(`config` 命令)和项目级配置(`init show` 子命令)复用。
|
|
212
255
|
|
|
@@ -357,6 +400,30 @@ async function interactiveConfigEditor<T extends object>(
|
|
|
357
400
|
|
|
358
401
|
## 6. 验证架构规则
|
|
359
402
|
|
|
403
|
+
### 6.0 消息常量双语化
|
|
404
|
+
|
|
405
|
+
`src/constants/messages/` 下所有消息常量已支持中英双语。每个消息常量文件的结构为:
|
|
406
|
+
|
|
407
|
+
1. 定义双语映射对象(`*_I18N`),每个消息条目为 `{ en: ..., 'zh-CN': ... }` 结构
|
|
408
|
+
2. 通过 `createMessages()` 运行时根据当前语言选择对应文本,导出的消息对象类型与原单语版本一致
|
|
409
|
+
3. 消费方(各命令模块)无需改动,直接使用导出的消息对象即可
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// 定义双语映射
|
|
413
|
+
const MESSAGES_I18N = {
|
|
414
|
+
SOME_MESSAGE: {
|
|
415
|
+
en: 'English text',
|
|
416
|
+
'zh-CN': '中文文本',
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// 运行时导出
|
|
421
|
+
export const MESSAGES = createMessages(MESSAGES_I18N);
|
|
422
|
+
// MESSAGES.SOME_MESSAGE → 'English text' 或 '中文文本'(取决于 language 配置)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
已双语化的消息常量文件包括:`common.ts`、`cli-descriptions.ts`、`init.ts`、`create.ts`、`run.ts`、`validate.ts`、`merge.ts`、`remove.ts`、`resume.ts`、`sync.ts`、`reset.ts`、`status.ts`、`alias.ts`、`completion.ts`、`home.ts`、`cover-validate.ts`、`projects.ts`、`tasks.ts`、`interactive-panel.ts`、`post-create.ts`、`config.ts`。
|
|
426
|
+
|
|
360
427
|
以下规则适用于验证分支架构的所有实现工作:
|
|
361
428
|
|
|
362
429
|
1. **不兼容旧版本**:本次重构不考虑旧版本数据、旧版本创建的 worktree 或旧版本配置的兼容性。所有命令均假定验证分支和项目级配置已按新架构存在。用户需删除旧 worktree 后重新创建。
|
package/package.json
CHANGED
package/src/commands/alias.ts
CHANGED
|
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import { MESSAGES } from '../constants/index.js';
|
|
4
4
|
import { logger } from '../logger/index.js';
|
|
5
5
|
import { loadConfig, writeConfig, printInfo, printSuccess, printError, printSeparator } from '../utils/index.js';
|
|
6
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* 从 Commander 实例动态获取所有已注册的命令名
|
|
@@ -104,28 +105,28 @@ function handleAliasRemove(alias: string): void {
|
|
|
104
105
|
export function registerAliasCommand(program: Command): void {
|
|
105
106
|
const aliasCmd = program
|
|
106
107
|
.command('alias')
|
|
107
|
-
.description('管理命令别名(列出 / 设置 / 移除)')
|
|
108
|
+
.description(getCurrentLanguage() === 'en' ? 'Manage command aliases (list / set / remove)' : '管理命令别名(列出 / 设置 / 移除)')
|
|
108
109
|
.action(() => {
|
|
109
110
|
handleAliasList();
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
aliasCmd
|
|
113
114
|
.command('list')
|
|
114
|
-
.description('列出所有别名')
|
|
115
|
+
.description(getCurrentLanguage() === 'en' ? 'List all aliases' : '列出所有别名')
|
|
115
116
|
.action(() => {
|
|
116
117
|
handleAliasList();
|
|
117
118
|
});
|
|
118
119
|
|
|
119
120
|
aliasCmd
|
|
120
121
|
.command('set <alias> <command>')
|
|
121
|
-
.description('设置命令别名')
|
|
122
|
+
.description(getCurrentLanguage() === 'en' ? 'Set a command alias' : '设置命令别名')
|
|
122
123
|
.action((alias: string, command: string) => {
|
|
123
124
|
handleAliasSet(program, alias, command);
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
aliasCmd
|
|
127
128
|
.command('remove <alias>')
|
|
128
|
-
.description('移除命令别名')
|
|
129
|
+
.description(getCurrentLanguage() === 'en' ? 'Remove a command alias' : '移除命令别名')
|
|
129
130
|
.action((alias: string) => {
|
|
130
131
|
handleAliasRemove(alias);
|
|
131
132
|
});
|
|
@@ -7,6 +7,7 @@ import { MESSAGES } from '../constants/messages/index.js';
|
|
|
7
7
|
import { printSuccess, printInfo, printWarning, printError } from '../utils/index.js';
|
|
8
8
|
import { getBashScript, getZshScript } from '../utils/completion-scripts.js';
|
|
9
9
|
import { generateCompletions } from '../utils/completion-engine.js';
|
|
10
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 向文件中追加内容(如果不存在),或者创建新文件
|
|
@@ -89,7 +90,7 @@ export function registerCompletionCommand(program: Command): void {
|
|
|
89
90
|
completionCommand
|
|
90
91
|
.command('_complete [args...]')
|
|
91
92
|
.allowUnknownOption()
|
|
92
|
-
.description('内部使用的动态补全方法,不对外公开')
|
|
93
|
+
.description(getCurrentLanguage() === 'en' ? 'Internal dynamic completion method, not for public use' : '内部使用的动态补全方法,不对外公开')
|
|
93
94
|
.action((args: string[]) => {
|
|
94
95
|
// args 中包含了: shell, cword, ...words
|
|
95
96
|
if (!args || args.length < 2) return;
|
package/src/commands/config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
-
import { DEFAULT_CONFIG, CONFIG_DEFINITIONS, MESSAGES, CONFIG_ALIAS_DISABLED_HINT } from '../constants/index.js';
|
|
2
|
+
import { DEFAULT_CONFIG, CONFIG_DEFINITIONS, MESSAGES, CONFIG_ALIAS_DISABLED_HINT, getI18nConfigDescriptions } from '../constants/index.js';
|
|
3
3
|
import { logger } from '../logger/index.js';
|
|
4
4
|
import {
|
|
5
5
|
loadConfig,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
parseConfigValue,
|
|
15
15
|
interactiveConfigEditor,
|
|
16
16
|
} from '../utils/index.js';
|
|
17
|
+
import { getCurrentLanguage, resetLanguageCache } from '../utils/i18n.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* 注册 config 命令组:查看和管理全局配置
|
|
@@ -22,28 +23,28 @@ import {
|
|
|
22
23
|
export function registerConfigCommand(program: Command): void {
|
|
23
24
|
const configCmd = program
|
|
24
25
|
.command('config')
|
|
25
|
-
.description('交互式查看和修改全局配置')
|
|
26
|
+
.description(getCurrentLanguage() === 'en' ? 'Interactively view and modify global configuration' : '交互式查看和修改全局配置')
|
|
26
27
|
.action(async () => {
|
|
27
28
|
await handleConfigSet();
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
configCmd
|
|
31
32
|
.command('reset')
|
|
32
|
-
.description('将配置恢复为默认值')
|
|
33
|
+
.description(getCurrentLanguage() === 'en' ? 'Reset configuration to default values' : '将配置恢复为默认值')
|
|
33
34
|
.action(async () => {
|
|
34
35
|
await handleConfigReset();
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
configCmd
|
|
38
39
|
.command('set [key] [value]')
|
|
39
|
-
.description('修改配置项(无参数进入交互式配置)')
|
|
40
|
+
.description(getCurrentLanguage() === 'en' ? 'Modify configuration (enter interactive mode without arguments)' : '修改配置项(无参数进入交互式配置)')
|
|
40
41
|
.action(async (key?: string, value?: string) => {
|
|
41
42
|
await handleConfigSet(key, value);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
configCmd
|
|
45
46
|
.command('get <key>')
|
|
46
|
-
.description('获取单个配置项的值')
|
|
47
|
+
.description(getCurrentLanguage() === 'en' ? 'Get the value of a single configuration item' : '获取单个配置项的值')
|
|
47
48
|
.action((key: string) => {
|
|
48
49
|
handleConfigGet(key);
|
|
49
50
|
});
|
|
@@ -57,7 +58,7 @@ async function handleConfigReset(): Promise<void> {
|
|
|
57
58
|
|
|
58
59
|
const confirmed = await confirmDestructiveAction(
|
|
59
60
|
'config reset',
|
|
60
|
-
|
|
61
|
+
MESSAGES.CONFIG_RESET_WARNING,
|
|
61
62
|
);
|
|
62
63
|
|
|
63
64
|
if (!confirmed) {
|
|
@@ -66,6 +67,7 @@ async function handleConfigReset(): Promise<void> {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
writeDefaultConfig();
|
|
70
|
+
resetLanguageCache();
|
|
69
71
|
printSuccess(MESSAGES.CONFIG_RESET_SUCCESS);
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -105,6 +107,11 @@ async function handleConfigSet(key?: string, value?: string): Promise<void> {
|
|
|
105
107
|
(config as unknown as Record<string, unknown>)[key] = result.value;
|
|
106
108
|
saveConfig(config);
|
|
107
109
|
|
|
110
|
+
// 语言配置变更时刷新缓存,使后续消息立即使用新语言
|
|
111
|
+
if (key === 'language') {
|
|
112
|
+
resetLanguageCache();
|
|
113
|
+
}
|
|
114
|
+
|
|
108
115
|
logger.info(`config set 命令执行,设置 ${key} = ${String(result.value)}`);
|
|
109
116
|
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(key, String(result.value)));
|
|
110
117
|
}
|
|
@@ -125,7 +132,13 @@ async function handleInteractiveConfigSet(): Promise<void> {
|
|
|
125
132
|
}
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
|
|
135
|
+
// 使用 i18n 后的描述替换 CONFIG_DEFINITIONS 中的中文 description
|
|
136
|
+
const i18nDescriptions = getI18nConfigDescriptions();
|
|
137
|
+
const i18nDefinitions = Object.fromEntries(
|
|
138
|
+
Object.entries(CONFIG_DEFINITIONS).map(([k, def]) => [k, { ...def, description: i18nDescriptions[k as keyof typeof i18nDescriptions] }]),
|
|
139
|
+
) as typeof CONFIG_DEFINITIONS;
|
|
140
|
+
|
|
141
|
+
const { key, newValue } = await interactiveConfigEditor(config, i18nDefinitions, {
|
|
129
142
|
disabledKeys,
|
|
130
143
|
});
|
|
131
144
|
|
|
@@ -133,6 +146,11 @@ async function handleInteractiveConfigSet(): Promise<void> {
|
|
|
133
146
|
(config as unknown as Record<string, unknown>)[key as string] = newValue;
|
|
134
147
|
saveConfig(config);
|
|
135
148
|
|
|
149
|
+
// 语言配置变更时刷新缓存,使后续消息立即使用新语言
|
|
150
|
+
if (key === 'language') {
|
|
151
|
+
resetLanguageCache();
|
|
152
|
+
}
|
|
153
|
+
|
|
136
154
|
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(key as string, String(newValue)));
|
|
137
155
|
}
|
|
138
156
|
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
isWorkingDirClean,
|
|
23
23
|
confirmAction,
|
|
24
24
|
} from '../utils/index.js';
|
|
25
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* 注册 cover-validate 命令:将验证分支上的修改覆盖回目标 worktree
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
export function registerCoverValidateCommand(program: Command): void {
|
|
31
32
|
program
|
|
32
33
|
.command('cover')
|
|
33
|
-
.description('将验证分支上的修改覆盖回目标 worktree(自动推导目标分支)')
|
|
34
|
+
.description(getCurrentLanguage() === 'en' ? 'Overwrite changes from the validation branch back to the target worktree (auto-detect target branch)' : '将验证分支上的修改覆盖回目标 worktree(自动推导目标分支)')
|
|
34
35
|
.action(async () => {
|
|
35
36
|
await handleCoverValidate();
|
|
36
37
|
});
|
|
@@ -113,7 +114,7 @@ async function handleCoverValidate(): Promise<void> {
|
|
|
113
114
|
// 步骤 3.5:工作区干净时提示确认,避免误操作
|
|
114
115
|
if (isWorkingDirClean(mainWorktreePath)) {
|
|
115
116
|
printInfo(MESSAGES.COVER_VALIDATE_WORKING_DIR_CLEAN);
|
|
116
|
-
const confirmed = await confirmAction('是否继续执行覆盖?');
|
|
117
|
+
const confirmed = await confirmAction(getCurrentLanguage() === 'en' ? 'Proceed with cover?' : '是否继续执行覆盖?');
|
|
117
118
|
if (!confirmed) return;
|
|
118
119
|
}
|
|
119
120
|
|
package/src/commands/create.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
printSeparator,
|
|
14
14
|
runPostCreateHooks,
|
|
15
15
|
} from '../utils/index.js';
|
|
16
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* 注册 create 命令:批量创建 worktree 及对应分支(含验证分支)
|
|
@@ -21,10 +22,10 @@ import {
|
|
|
21
22
|
export function registerCreateCommand(program: Command): void {
|
|
22
23
|
program
|
|
23
24
|
.command('create')
|
|
24
|
-
.description('批量创建 worktree 及对应分支(含验证分支)')
|
|
25
|
-
.requiredOption('-b, --branch <branchName>', '分支名')
|
|
26
|
-
.option('-n, --number <count>', '创建数量', '1')
|
|
27
|
-
.option('--post-create', '执行 postCreate hook(默认开启,--no-post-create 跳过)', true)
|
|
25
|
+
.description(getCurrentLanguage() === 'en' ? 'Batch create worktrees and corresponding branches (including validation branches)' : '批量创建 worktree 及对应分支(含验证分支)')
|
|
26
|
+
.requiredOption('-b, --branch <branchName>', getCurrentLanguage() === 'en' ? 'Branch name' : '分支名')
|
|
27
|
+
.option('-n, --number <count>', getCurrentLanguage() === 'en' ? 'Number of worktrees to create' : '创建数量', '1')
|
|
28
|
+
.option('--post-create', getCurrentLanguage() === 'en' ? 'Execute postCreate hook (enabled by default, use --no-post-create to skip)' : '执行 postCreate hook(默认开启,--no-post-create 跳过)', true)
|
|
28
29
|
.action(async (options: CreateOptions) => {
|
|
29
30
|
await handleCreate(options);
|
|
30
31
|
});
|
|
@@ -58,10 +59,10 @@ async function handleCreate(options: CreateOptions): Promise<void> {
|
|
|
58
59
|
printInfo('');
|
|
59
60
|
|
|
60
61
|
worktrees.forEach((wt, index) => {
|
|
61
|
-
printInfo(`目录路径${index + 1}:`);
|
|
62
|
+
printInfo(getCurrentLanguage() === 'en' ? `Directory path ${index + 1}:` : `目录路径${index + 1}:`);
|
|
62
63
|
printInfo(` ${wt.path}`);
|
|
63
|
-
printInfo(` 分支名: ${wt.branch}`);
|
|
64
|
-
printInfo(` 验证分支: ${getValidateBranchName(wt.branch)}`);
|
|
64
|
+
printInfo(getCurrentLanguage() === 'en' ? ` Branch: ${wt.branch}` : ` 分支名: ${wt.branch}`);
|
|
65
|
+
printInfo(getCurrentLanguage() === 'en' ? ` Validation branch: ${getValidateBranchName(wt.branch)}` : ` 验证分支: ${getValidateBranchName(wt.branch)}`);
|
|
65
66
|
printSeparator();
|
|
66
67
|
});
|
|
67
68
|
}
|
package/src/commands/home.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
printHint,
|
|
14
14
|
printError,
|
|
15
15
|
} from '../utils/index.js';
|
|
16
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* 注册 home 命令:快速切换回主工作分支
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
export function registerHomeCommand(program: Command): void {
|
|
22
23
|
program
|
|
23
24
|
.command('home')
|
|
24
|
-
.description('切换回主工作分支')
|
|
25
|
+
.description(getCurrentLanguage() === 'en' ? 'Switch back to the main work branch' : '切换回主工作分支')
|
|
25
26
|
.action(async () => {
|
|
26
27
|
await handleHome();
|
|
27
28
|
});
|
package/src/commands/init.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { Command as Cmd } from 'commander';
|
|
3
3
|
import { logger } from '../logger/index.js';
|
|
4
|
-
import { MESSAGES, PROJECT_CONFIG_DEFINITIONS } from '../constants/index.js';
|
|
4
|
+
import { MESSAGES, PROJECT_CONFIG_DEFINITIONS, getI18nProjectConfigDescriptions } from '../constants/index.js';
|
|
5
5
|
import type { InitOptions, InitShowOptions, ProjectConfig } from '../types/index.js';
|
|
6
6
|
import {
|
|
7
7
|
runPreChecks,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
safeStringify,
|
|
16
16
|
normalizeProjectConfig,
|
|
17
17
|
} from '../utils/index.js';
|
|
18
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* 注册 init 命令:初始化项目级配置,设置主工作分支
|
|
@@ -23,8 +24,8 @@ import {
|
|
|
23
24
|
export function registerInitCommand(program: Command): void {
|
|
24
25
|
const initCmd = program
|
|
25
26
|
.command('init')
|
|
26
|
-
.description('初始化项目级配置,设置主工作分支')
|
|
27
|
-
.option('-b, --branch <branchName>', '指定主工作分支名(默认使用当前分支)')
|
|
27
|
+
.description(getCurrentLanguage() === 'en' ? 'Initialize project configuration and set the main work branch' : '初始化项目级配置,设置主工作分支')
|
|
28
|
+
.option('-b, --branch <branchName>', getCurrentLanguage() === 'en' ? 'Specify the main work branch name (defaults to current branch)' : '指定主工作分支名(默认使用当前分支)')
|
|
28
29
|
.action(async (options: InitOptions) => {
|
|
29
30
|
await handleInit(options);
|
|
30
31
|
});
|
|
@@ -32,8 +33,8 @@ export function registerInitCommand(program: Command): void {
|
|
|
32
33
|
// 注册 show 子命令:交互式查看和修改项目配置(支持 --json 格式输出)
|
|
33
34
|
initCmd.addCommand(
|
|
34
35
|
new Cmd('show')
|
|
35
|
-
.description('交互式查看和修改项目配置(支持 --json 格式输出)')
|
|
36
|
-
.option('--json', '以 JSON 格式输出')
|
|
36
|
+
.description(getCurrentLanguage() === 'en' ? 'Interactively view and modify project configuration (supports --json output)' : '交互式查看和修改项目配置(支持 --json 格式输出)')
|
|
37
|
+
.option('--json', getCurrentLanguage() === 'en' ? 'Output in JSON format' : '以 JSON 格式输出')
|
|
37
38
|
.action(async (options: InitShowOptions) => {
|
|
38
39
|
await handleInitShow(options);
|
|
39
40
|
}),
|
|
@@ -56,9 +57,15 @@ async function handleInitShow(options: InitShowOptions): Promise<void> {
|
|
|
56
57
|
|
|
57
58
|
logger.info('init show 命令执行,进入交互式项目配置');
|
|
58
59
|
|
|
60
|
+
// 使用 i18n 后的描述替换 PROJECT_CONFIG_DEFINITIONS 中的中文 description
|
|
61
|
+
const i18nDescriptions = getI18nProjectConfigDescriptions();
|
|
62
|
+
const i18nDefinitions = Object.fromEntries(
|
|
63
|
+
Object.entries(PROJECT_CONFIG_DEFINITIONS).map(([k, def]) => [k, { ...def, description: i18nDescriptions[k as keyof typeof i18nDescriptions] }]),
|
|
64
|
+
) as typeof PROJECT_CONFIG_DEFINITIONS;
|
|
65
|
+
|
|
59
66
|
const { key, newValue } = await interactiveConfigEditor(
|
|
60
67
|
config as Required<ProjectConfig>,
|
|
61
|
-
|
|
68
|
+
i18nDefinitions,
|
|
62
69
|
{ selectPrompt: MESSAGES.INIT_SELECT_PROMPT },
|
|
63
70
|
);
|
|
64
71
|
|
package/src/commands/list.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
isWorktreeIdle,
|
|
13
13
|
printInfo,
|
|
14
14
|
} from '../utils/index.js';
|
|
15
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
15
16
|
// getWorktreeStatus 和 formatWorktreeStatus 仅在文本模式下使用
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -21,8 +22,8 @@ import {
|
|
|
21
22
|
export function registerListCommand(program: Command): void {
|
|
22
23
|
program
|
|
23
24
|
.command('list')
|
|
24
|
-
.description('列出当前项目所有 worktree(支持 --json 格式输出)')
|
|
25
|
-
.option('--json', '以 JSON 格式输出')
|
|
25
|
+
.description(getCurrentLanguage() === 'en' ? 'List all worktrees for the current project (supports --json output)' : '列出当前项目所有 worktree(支持 --json 格式输出)')
|
|
26
|
+
.option('--json', getCurrentLanguage() === 'en' ? 'Output in JSON format' : '以 JSON 格式输出')
|
|
26
27
|
.action(async (options: ListOptions) => {
|
|
27
28
|
await handleList(options);
|
|
28
29
|
});
|
|
@@ -72,7 +73,8 @@ function printListAsJson(projectName: string, worktrees: import('../types/index.
|
|
|
72
73
|
* @param {import('../types/index.js').WorktreeInfo[]} worktrees - worktree 列表
|
|
73
74
|
*/
|
|
74
75
|
function printListAsText(projectName: string, worktrees: import('../types/index.js').WorktreeInfo[]): void {
|
|
75
|
-
|
|
76
|
+
const lang = getCurrentLanguage();
|
|
77
|
+
printInfo(`${lang === 'en' ? 'Current project' : '当前项目'}: ${projectName}\n`);
|
|
76
78
|
|
|
77
79
|
if (worktrees.length === 0) {
|
|
78
80
|
printInfo(` ${MESSAGES.NO_WORKTREES}`);
|
|
@@ -95,6 +97,6 @@ function printListAsText(projectName: string, worktrees: import('../types/index.
|
|
|
95
97
|
|
|
96
98
|
printInfo('');
|
|
97
99
|
}
|
|
98
|
-
printInfo(
|
|
100
|
+
printInfo(`${worktrees.length} ${lang === 'en' ? 'worktree(s)' : '个 worktree'}`);
|
|
99
101
|
}
|
|
100
102
|
}
|
package/src/commands/merge.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
isNonInteractive,
|
|
40
40
|
} from '../utils/index.js';
|
|
41
41
|
import type { WorktreeResolveMessages } from '../utils/index.js';
|
|
42
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* 注册 merge 命令:合并验证过的分支到主 worktree
|
|
@@ -47,10 +48,10 @@ import type { WorktreeResolveMessages } from '../utils/index.js';
|
|
|
47
48
|
export function registerMergeCommand(program: Command): void {
|
|
48
49
|
program
|
|
49
50
|
.command('merge')
|
|
50
|
-
.description('合并某个已验证的 worktree 分支到主 worktree')
|
|
51
|
-
.option('-b, --branch <branchName>', '要合并的分支名(支持模糊匹配,不传则列出所有分支供选择)')
|
|
52
|
-
.option('-m, --message <commitMessage>', '提交信息(目标 worktree 工作区有修改时必填)')
|
|
53
|
-
.option('--auto', '遇到冲突直接调用 AI 解决,不再询问')
|
|
51
|
+
.description(getCurrentLanguage() === 'en' ? 'Merge a validated worktree branch into the main worktree' : '合并某个已验证的 worktree 分支到主 worktree')
|
|
52
|
+
.option('-b, --branch <branchName>', getCurrentLanguage() === 'en' ? 'Branch name to merge (supports fuzzy match, lists all branches if not provided)' : '要合并的分支名(支持模糊匹配,不传则列出所有分支供选择)')
|
|
53
|
+
.option('-m, --message <commitMessage>', getCurrentLanguage() === 'en' ? 'Commit message (required when target worktree has uncommitted changes)' : '提交信息(目标 worktree 工作区有修改时必填)')
|
|
54
|
+
.option('--auto', getCurrentLanguage() === 'en' ? 'Resolve conflicts with AI automatically without prompting' : '遇到冲突直接调用 AI 解决,不再询问')
|
|
54
55
|
.action(async (options: MergeOptions) => {
|
|
55
56
|
await handleMerge(options);
|
|
56
57
|
});
|
|
@@ -141,11 +142,11 @@ async function handleSquashIfNeeded(
|
|
|
141
142
|
async function shouldCleanupAfterMerge(branchName: string): Promise<boolean> {
|
|
142
143
|
const autoDelete = getConfigValue('autoDeleteBranch');
|
|
143
144
|
if (autoDelete) {
|
|
144
|
-
printInfo(
|
|
145
|
+
printInfo(MESSAGES.AUTO_DELETE_CONFIGURED(branchName));
|
|
145
146
|
return true;
|
|
146
147
|
}
|
|
147
148
|
// 非交互模式下自动删除
|
|
148
|
-
return confirmAction(
|
|
149
|
+
return confirmAction(MESSAGES.CONFIRM_DELETE_WORKTREE_BRANCH(branchName), true);
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
/**
|
|
@@ -260,7 +261,7 @@ async function handleMerge(options: MergeOptions): Promise<void> {
|
|
|
260
261
|
return;
|
|
261
262
|
}
|
|
262
263
|
} else {
|
|
263
|
-
printInfo('已跳过自动 pull/push,请手动执行 git pull && git push');
|
|
264
|
+
printInfo(getCurrentLanguage() === 'en' ? 'Auto pull/push skipped, please run git pull && git push manually' : '已跳过自动 pull/push,请手动执行 git pull && git push');
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
// 步骤 8:输出成功提示(根据是否有 message 选择对应模板)
|
package/src/commands/projects.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
printSeparator,
|
|
22
22
|
printError,
|
|
23
23
|
} from '../utils/index.js';
|
|
24
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* 注册 projects 命令:统一管理多个项目的 worktree 状态
|
|
@@ -29,8 +30,8 @@ import {
|
|
|
29
30
|
export function registerProjectsCommand(program: Command): void {
|
|
30
31
|
program
|
|
31
32
|
.command('projects [name]')
|
|
32
|
-
.description('展示所有项目的 worktree 概览,或查看指定项目的 worktree 详情')
|
|
33
|
-
.option('--json', '以 JSON 格式输出')
|
|
33
|
+
.description(getCurrentLanguage() === 'en' ? 'Show worktree overview across projects, or view details for a specific project' : '展示所有项目的 worktree 概览,或查看指定项目的 worktree 详情')
|
|
34
|
+
.option('--json', getCurrentLanguage() === 'en' ? 'Output in JSON format' : '以 JSON 格式输出')
|
|
34
35
|
.action((name: string | undefined, options: { json?: boolean }) => {
|
|
35
36
|
handleProjects({ name, json: options.json });
|
|
36
37
|
});
|
|
@@ -258,7 +259,8 @@ function printProjectsOverviewAsText(result: ProjectsOverviewResult): void {
|
|
|
258
259
|
|
|
259
260
|
printSeparator();
|
|
260
261
|
printInfo('');
|
|
261
|
-
|
|
262
|
+
const lang = getCurrentLanguage();
|
|
263
|
+
printInfo(` ${chalk.bold(String(result.totalProjects))} ${lang === 'en' ? 'project(s)' : '个项目'} ${chalk.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
|
|
262
264
|
printInfo('');
|
|
263
265
|
printDoubleSeparator();
|
|
264
266
|
}
|
package/src/commands/remove.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
getCurrentBranch,
|
|
27
27
|
} from '../utils/index.js';
|
|
28
28
|
import type { WorktreeMultiResolveMessages } from '../utils/index.js';
|
|
29
|
+
import { getCurrentLanguage } from '../utils/i18n.js';
|
|
29
30
|
|
|
30
31
|
/** remove 命令的分支解析消息配置 */
|
|
31
32
|
const REMOVE_RESOLVE_MESSAGES: WorktreeMultiResolveMessages = {
|
|
@@ -42,9 +43,9 @@ const REMOVE_RESOLVE_MESSAGES: WorktreeMultiResolveMessages = {
|
|
|
42
43
|
export function registerRemoveCommand(program: Command): void {
|
|
43
44
|
program
|
|
44
45
|
.command('remove')
|
|
45
|
-
.description('移除 worktree(支持模糊匹配/多选/全部)')
|
|
46
|
-
.option('--all', '移除当前项目下所有 worktree')
|
|
47
|
-
.option('-b, --branch <branchName>', '指定分支名(支持模糊匹配,不传则列出所有分支)')
|
|
46
|
+
.description(getCurrentLanguage() === 'en' ? 'Remove worktrees (supports fuzzy match / multi-select / all)' : '移除 worktree(支持模糊匹配/多选/全部)')
|
|
47
|
+
.option('--all', getCurrentLanguage() === 'en' ? 'Remove all worktrees in the current project' : '移除当前项目下所有 worktree')
|
|
48
|
+
.option('-b, --branch <branchName>', getCurrentLanguage() === 'en' ? 'Specify branch name (supports fuzzy match, lists all branches if not provided)' : '指定分支名(支持模糊匹配,不传则列出所有分支)')
|
|
48
49
|
.action(async (options: RemoveOptions) => {
|
|
49
50
|
await handleRemove(options);
|
|
50
51
|
});
|
|
@@ -89,9 +90,9 @@ async function handleRemove(options: RemoveOptions): Promise<void> {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
// 列出即将移除的 worktree
|
|
92
|
-
printInfo('即将移除以下 worktree 及本地分支:\n');
|
|
93
|
+
printInfo(getCurrentLanguage() === 'en' ? 'The following worktrees and local branches will be removed:\n' : '即将移除以下 worktree 及本地分支:\n');
|
|
93
94
|
worktreesToRemove.forEach((wt, index) => {
|
|
94
|
-
printInfo(` ${index + 1}. ${wt.path} → 分支: ${wt.branch} 验证分支: ${getValidateBranchName(wt.branch)}`);
|
|
95
|
+
printInfo(getCurrentLanguage() === 'en' ? ` ${index + 1}. ${wt.path} → branch: ${wt.branch} validate branch: ${getValidateBranchName(wt.branch)}` : ` ${index + 1}. ${wt.path} → 分支: ${wt.branch} 验证分支: ${getValidateBranchName(wt.branch)}`);
|
|
95
96
|
});
|
|
96
97
|
printInfo('');
|
|
97
98
|
|
|
@@ -141,7 +142,7 @@ async function handleRemove(options: RemoveOptions): Promise<void> {
|
|
|
141
142
|
if (failures.length > 0) {
|
|
142
143
|
printError(MESSAGES.REMOVE_PARTIAL_FAILURE(failures));
|
|
143
144
|
throw new ClawtError(
|
|
144
|
-
`${failures.length} 个 worktree 移除失败`,
|
|
145
|
+
getCurrentLanguage() === 'en' ? `${failures.length} worktree removal(s) failed` : `${failures.length} 个 worktree 移除失败`,
|
|
145
146
|
);
|
|
146
147
|
}
|
|
147
148
|
}
|