clawt 2.14.0 → 2.14.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.
@@ -7,7 +7,7 @@
7
7
  - 命令流程在 `5. 需求场景详细设计` 下,每个命令一个子章节(5.1-5.14)
8
8
  - run 命令对应 `5.2 批量创建 Worktree + 执行 Claude Code 任务`,流程按步骤编号描述
9
9
  - merge 命令对应 `5.6 合并验证过的分支`,-b 可选,支持模糊匹配(与 resume/validate 共享匹配逻辑),流程按步骤编号描述
10
- - config 命令对应 `5.10 查看和管理全局配置`,包含查看配置和 config reset 子命令两部分(使用 `####` 子标题区分)
10
+ - config 命令对应 `5.10 交互式查看和修改全局配置`,包含四个子章节:交互式修改(config / config set 无参数)、直接设置(config set key value)、获取(config get)、恢复默认(config reset),使用 `####` 子标题区分
11
11
  - resume 命令对应 `5.11 在已有 Worktree 中恢复会话`,统一使用多选交互(resolveTargetWorktrees),选 1 个当前终端恢复,选多个在独立终端 Tab 批量恢复(-b 可选)
12
12
  - validate 命令对应 `5.4 在主 Worktree 验证其他分支`,-b 可选,支持模糊匹配(与 resume 共享匹配逻辑)
13
13
  - sync 命令对应 `5.12 将主分支代码同步到目标 Worktree`,-b 可选,支持模糊匹配(与 resume/validate/merge 共享匹配逻辑)
@@ -72,9 +72,9 @@ run 命令有两种模式(自 claudeCodeCommand 特性后):
72
72
  - `formatDuration` 从 `src/utils/progress.ts` 移至 `src/utils/formatter.ts`
73
73
  - 进度面板每个任务行末尾显示 worktree 路径(终端可点击跳转)
74
74
 
75
- ## 命令清单(11 个)
75
+ ## 命令清单(12 个)
76
76
 
77
- `create`、`run`、`resume`、`list`、`remove`、`validate`、`merge`、`config`、`sync`、`reset`、`status`
77
+ `create`、`run`、`resume`、`list`、`remove`、`validate`、`merge`、`config`、`sync`、`reset`、`status`、`alias`
78
78
 
79
79
  Notes:
80
80
  - resume 和 run(交互式模式)共用 `launchInteractiveClaude()`,该函数从 run.ts 提取到 src/utils/claude.ts
@@ -91,6 +91,11 @@ Notes:
91
91
  - `promptMultiSelectBranches` 支持「全选」选项(顶部 [select-all]),通过扩展 MultiSelect 覆写 space() 实现全选 toggle
92
92
  - `SELECT_ALL_NAME` 和 `SELECT_ALL_LABEL` 常量定义在 `src/constants/prompt.ts`
93
93
  - `VALID_TERMINAL_APPS` 和 `ITERM2_APP_PATH` 常量定义在 `src/constants/terminal.ts`
94
+ - config 交互式修改:对象类型配置项(如 `aliases`)在 Enquirer.Select 列表中标灰不可选(`disabled: CONFIG_ALIAS_DISABLED_HINT`),提示用户通过专用命令管理
95
+ - `CONFIG_ALIAS_DISABLED_HINT` 常量定义在 `src/constants/messages/config.ts`,通过 `src/constants/messages/index.ts` 和 `src/constants/index.ts` 导出
96
+ - `parseConfigValue()` 和 `promptConfigValue()` 在 `src/utils/config-strategy.ts`,基于配置项类型和 `allowedValues` 自动分发值解析/提示策略
97
+ - `saveConfig()` 在 `src/utils/config.ts`,通用配置写入函数
98
+ - `ConfigItemDefinition` 支持可选 `allowedValues` 字段(`readonly string[]`),用于 string 类型枚举校验
94
99
 
95
100
  ## validate 快照机制
96
101
 
package/README.md CHANGED
@@ -143,11 +143,28 @@ clawt status --json # JSON 格式
143
143
  clawt reset
144
144
  ```
145
145
 
146
- ### `clawt config` — 查看配置
146
+ ### `clawt config` — 交互式查看和修改配置
147
147
 
148
148
  ```bash
149
- clawt config # 查看当前配置
150
- clawt config reset # 恢复默认配置
149
+ clawt config # 交互式修改配置(选择配置项并修改值)
150
+ clawt config set <key> <value> # 直接设置某个配置项
151
+ clawt config get <key> # 获取某个配置项的值
152
+ clawt config reset # 恢复默认配置
153
+ ```
154
+
155
+ **使用示例:**
156
+
157
+ ```bash
158
+ # 交互式修改(列出所有配置项,方向键选择,根据类型自动提示)
159
+ clawt config
160
+
161
+ # 直接设置
162
+ clawt config set autoDeleteBranch true
163
+ clawt config set maxConcurrency 4
164
+ clawt config set terminalApp iterm2
165
+
166
+ # 查看某项配置
167
+ clawt config get maxConcurrency
151
168
  ```
152
169
 
153
170
  ### `clawt alias` — 管理命令别名
package/dist/index.js CHANGED
@@ -249,6 +249,7 @@ var RESET_MESSAGES = {
249
249
  };
250
250
 
251
251
  // src/constants/messages/config.ts
252
+ var CONFIG_ALIAS_DISABLED_HINT = "(\u901A\u8FC7 clawt alias \u547D\u4EE4\u7BA1\u7406)";
252
253
  var CONFIG_CMD_MESSAGES = {
253
254
  /** 配置已恢复为默认值 */
254
255
  CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C",
@@ -377,7 +378,7 @@ var CONFIG_DEFINITIONS = {
377
378
  },
378
379
  aliases: {
379
380
  defaultValue: {},
380
- description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04\uFF08\u901A\u8FC7 clawt alias \u547D\u4EE4\u7BA1\u7406\uFF09"
381
+ description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
381
382
  }
382
383
  };
383
384
  function deriveDefaultConfig(definitions) {
@@ -2469,10 +2470,14 @@ async function handleInteractiveConfigSet() {
2469
2470
  const config2 = loadConfig();
2470
2471
  const keys = Object.keys(DEFAULT_CONFIG);
2471
2472
  logger.info("config set \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u914D\u7F6E");
2472
- const choices = keys.map((k) => ({
2473
- name: k,
2474
- message: `${k}: ${formatConfigValue(config2[k])} ${chalk6.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`
2475
- }));
2473
+ const choices = keys.map((k) => {
2474
+ const isObject = typeof DEFAULT_CONFIG[k] === "object";
2475
+ return {
2476
+ name: k,
2477
+ message: `${k}: ${isObject ? chalk6.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk6.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
2478
+ ...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
2479
+ };
2480
+ });
2476
2481
  const selectedKey = await new Enquirer5.Select({
2477
2482
  message: MESSAGES.CONFIG_SELECT_PROMPT,
2478
2483
  choices
@@ -357,7 +357,7 @@ var CONFIG_DEFINITIONS = {
357
357
  },
358
358
  aliases: {
359
359
  defaultValue: {},
360
- description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04\uFF08\u901A\u8FC7 clawt alias \u547D\u4EE4\u7BA1\u7406\uFF09"
360
+ description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
361
361
  }
362
362
  };
363
363
  function deriveDefaultConfig(definitions) {
package/docs/spec.md CHANGED
@@ -21,7 +21,7 @@
21
21
  - [5.7 默认配置文件](#57-默认配置文件)
22
22
  - [5.8 获取当前项目所有 Worktree](#58-获取当前项目所有-worktree)
23
23
  - [5.9 日志系统](#59-日志系统)
24
- - [5.10 查看和管理全局配置](#510-查看和管理全局配置)
24
+ - [5.10 交互式查看和修改全局配置](#510-交互式查看和修改全局配置)
25
25
  - [5.11 在已有 Worktree 中恢复会话](#511-在已有-worktree-中恢复会话)
26
26
  - [5.12 将主分支代码同步到目标 Worktree](#512-将主分支代码同步到目标-worktree)
27
27
  - [5.13 重置主 Worktree 工作区和暂存区](#513-重置主-worktree-工作区和暂存区)
@@ -174,7 +174,9 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
174
174
  | `clawt merge` | 合并某个已验证的 worktree 分支到主 worktree | 5.6 |
175
175
  | `clawt remove` | 移除 worktree(支持模糊匹配/多选/全部) | 5.5 |
176
176
  | `clawt list` | 列出当前项目所有 worktree(支持 `--json` 格式输出) | 5.8 |
177
- | `clawt config` | 查看全局配置 | 5.10 |
177
+ | `clawt config` | 交互式查看和修改全局配置(等同于 `config set`) | 5.10 |
178
+ | `clawt config set` | 修改配置项(无参数进入交互式,有参数直接设置) | 5.10 |
179
+ | `clawt config get` | 获取单个配置项的值 | 5.10 |
178
180
  | `clawt config reset` | 将配置恢复为默认值 | 5.10 |
179
181
  | `clawt resume` | 在已有 worktree 中恢复 Claude Code 会话(支持多选批量恢复) | 5.11 |
180
182
  | `clawt sync` | 将主分支最新代码同步到目标 worktree | 5.12 |
@@ -1000,51 +1002,82 @@ clawt validate -b feature-scheme --debug
1000
1002
 
1001
1003
  ---
1002
1004
 
1003
- ### 5.10 查看和管理全局配置
1005
+ ### 5.10 交互式查看和修改全局配置
1004
1006
 
1005
1007
  **命令:**
1006
1008
 
1007
1009
  ```bash
1008
- # 查看全局配置
1010
+ # 交互式修改配置(等同于 config set 无参数)
1009
1011
  clawt config
1010
1012
 
1013
+ # 修改配置项(无参数进入交互式,有参数直接设置)
1014
+ clawt config set [key] [value]
1015
+
1016
+ # 获取单个配置项的值
1017
+ clawt config get <key>
1018
+
1011
1019
  # 将配置恢复为默认值
1012
1020
  clawt config reset
1013
1021
  ```
1014
1022
 
1015
- #### 查看配置
1023
+ #### 交互式修改配置(`config` / `config set`)
1024
+
1025
+ 直接执行 `clawt config` 或 `clawt config set`(不带参数)进入交互式配置修改模式。
1016
1026
 
1017
1027
  **运行流程:**
1018
1028
 
1019
1029
  1. 读取全局配置文件 `~/.clawt/config.json`
1020
- 2. 遍历所有配置项(以 `CONFIG_DEFINITIONS` 为单一数据源),逐项展示:
1021
- - 配置项名称(粗体)
1030
+ 2. 列出所有配置项供用户选择(`Enquirer.Select`),每项显示:
1031
+ - 配置项名称
1022
1032
  - 当前值(布尔值绿色/黄色,字符串青色)
1023
1033
  - 配置项描述(灰色)
1024
- 3. 输出配置文件路径,提示用户可直接编辑
1034
+ - 对象类型配置项(如 `aliases`)标灰不可选,提示用户通过专用命令管理
1035
+ 3. 用户选择某个配置项后,根据值类型自动选择提示策略:
1036
+ - **boolean 类型** → `Select`(true / false)
1037
+ - **number 类型** → `Input`(带数字校验)
1038
+ - **string 类型 + 有 `allowedValues`** → `Select`(枚举列表)
1039
+ - **string 类型 + 无 `allowedValues`** → `Input`(自由输入)
1040
+ 4. 将修改后的配置持久化到配置文件
1041
+ 5. 输出成功提示:`✓ <key> 已设置为 <value>`
1025
1042
 
1026
- **输出格式:**
1043
+ #### 直接设置配置项(`config set <key> <value>`)
1027
1044
 
1028
- ```
1029
- 配置文件路径: ~/.clawt/config.json
1030
- ────────────────────────────────────────
1031
- autoDeleteBranch: false
1032
- 移除 worktree 时是否自动删除对应本地分支
1045
+ 当带参数执行 `clawt config set <key> <value>` 时,直接修改指定配置项。
1033
1046
 
1034
- claudeCodeCommand: claude
1035
- Claude Code CLI 启动指令
1047
+ **参数:**
1036
1048
 
1037
- autoPullPush: false
1038
- merge 成功后是否自动执行 git pull git push
1049
+ | 参数 | 必填 | 说明 |
1050
+ | ---- | ---- | ---- |
1051
+ | `key` | 否 | 配置项名称(不传则进入交互式模式) |
1052
+ | `value` | 否 | 配置值(传了 `key` 时必填) |
1039
1053
 
1040
- confirmDestructiveOps: true
1041
- 执行破坏性操作(reset、validate --clean)前是否提示确认
1054
+ **运行流程:**
1042
1055
 
1043
- ────────────────────────────────────────
1056
+ 1. 校验 `key` 是否为有效的配置项名称(基于 `DEFAULT_CONFIG` 的键列表),无效则输出错误及可用配置项列表
1057
+ 2. 校验 `value` 是否缺失,缺失则提示用法:`clawt config set <key> <value>`
1058
+ 3. 根据目标配置项的类型解析并校验值:
1059
+ - **boolean** → 仅接受 `true` 或 `false`
1060
+ - **number** → `Number()` 解析,`NaN` 报错
1061
+ - **string + 有 `allowedValues`** → 校验值是否在枚举列表中
1062
+ - **string + 无 `allowedValues`** → 无额外校验
1063
+ 4. 加载配置、修改目标项、持久化
1064
+ 5. 输出成功提示:`✓ <key> 已设置为 <value>`
1044
1065
 
1045
- ```
1066
+ #### 获取单个配置项(`config get <key>`)
1046
1067
 
1047
- #### 恢复默认配置
1068
+ **参数:**
1069
+
1070
+ | 参数 | 必填 | 说明 |
1071
+ | ---- | ---- | ---- |
1072
+ | `key` | 是 | 配置项名称 |
1073
+
1074
+ **运行流程:**
1075
+
1076
+ 1. 校验 `key` 是否为有效的配置项名称,无效则输出错误及可用配置项列表
1077
+ 2. 读取配置文件,获取目标配置项的值
1078
+ 3. 输出:`<key> = <value>`
1079
+
1080
+ #### 恢复默认配置(`config reset`)
1048
1081
 
1049
1082
  **运行流程:**
1050
1083
 
@@ -1052,6 +1085,13 @@ clawt config reset
1052
1085
  2. 将默认配置写入 `~/.clawt/config.json`(覆盖现有配置文件)
1053
1086
  3. 输出成功提示:`✓ 配置已恢复为默认值`
1054
1087
 
1088
+ **实现要点:**
1089
+
1090
+ - 配置项类型定义:`ConfigItemDefinition` 新增可选字段 `allowedValues`(`readonly string[]`),仅对 string 类型有效,用于枚举值校验和交互式 Select 提示
1091
+ - 值解析与提示策略:`src/utils/config-strategy.ts` 中的 `parseConfigValue()`(CLI 字符串解析)和 `promptConfigValue()`(交互式提示),基于类型和 `allowedValues` 自动分发
1092
+ - `saveConfig(config)`:`src/utils/config.ts` 中新增的通用配置写入函数,将完整配置对象持久化到文件
1093
+ - `formatConfigValue(value)`:支持 boolean、string、number、对象类型(如 `aliases`,按键值对逐行展示)的格式化显示
1094
+
1055
1095
  ---
1056
1096
 
1057
1097
  ### 5.11 在已有 Worktree 中恢复会话
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawt",
3
- "version": "2.14.0",
3
+ "version": "2.14.1",
4
4
  "description": "本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,7 +1,7 @@
1
1
  import type { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import Enquirer from 'enquirer';
4
- import { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, MESSAGES } from '../constants/index.js';
4
+ import { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, MESSAGES, CONFIG_ALIAS_DISABLED_HINT } from '../constants/index.js';
5
5
  import { logger } from '../logger/index.js';
6
6
  import {
7
7
  loadConfig,
@@ -123,10 +123,15 @@ async function handleInteractiveConfigSet(): Promise<void> {
123
123
  logger.info('config set 命令执行,进入交互式配置');
124
124
 
125
125
  // 构建选择列表,显示配置项名称、当前值和描述
126
- const choices = keys.map((k) => ({
127
- name: k,
128
- message: `${k}: ${formatConfigValue(config[k])} ${chalk.dim(`— ${CONFIG_DESCRIPTIONS[k]}`)}`,
129
- }));
126
+ // 对象类型配置项(如 aliases)标灰不可选,提示用户通过专用命令管理
127
+ const choices = keys.map((k) => {
128
+ const isObject = typeof DEFAULT_CONFIG[k] === 'object';
129
+ return {
130
+ name: k,
131
+ message: `${k}: ${isObject ? chalk.dim(JSON.stringify(config[k])) : formatConfigValue(config[k])} ${chalk.dim(`— ${CONFIG_DESCRIPTIONS[k]}`)}`,
132
+ ...(isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }),
133
+ };
134
+ });
130
135
 
131
136
  // @ts-expect-error enquirer 类型声明未导出 Select 类,但运行时存在
132
137
  const selectedKey: keyof ClawtConfig = await new Enquirer.Select({
@@ -37,7 +37,7 @@ export const CONFIG_DEFINITIONS: ConfigDefinitions = {
37
37
  },
38
38
  aliases: {
39
39
  defaultValue: {} as Record<string, string>,
40
- description: '命令别名映射(通过 clawt alias 命令管理)',
40
+ description: '命令别名映射',
41
41
  },
42
42
  };
43
43
 
@@ -1,6 +1,7 @@
1
1
  export { CLAWT_HOME, CONFIG_PATH, LOGS_DIR, WORKTREES_DIR, VALIDATE_SNAPSHOTS_DIR, CLAUDE_PROJECTS_DIR } from './paths.js';
2
2
  export { INVALID_BRANCH_CHARS } from './branch.js';
3
3
  export { MESSAGES } from './messages/index.js';
4
+ export { CONFIG_ALIAS_DISABLED_HINT } from './messages/index.js';
4
5
  export { EXIT_CODES } from './exitCodes.js';
5
6
  export { ENABLE_BRACKETED_PASTE, DISABLE_BRACKETED_PASTE, PASTE_THRESHOLD_MS, VALID_TERMINAL_APPS, ITERM2_APP_PATH } from './terminal.js';
6
7
  export { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, CONFIG_DEFINITIONS, APPEND_SYSTEM_PROMPT } from './config.js';
@@ -1,3 +1,6 @@
1
+ /** 对象类型配置项禁用提示(如 aliases 需通过专用命令管理) */
2
+ export const CONFIG_ALIAS_DISABLED_HINT = '(通过 clawt alias 命令管理)';
3
+
1
4
  /** config 命令专属提示消息 */
2
5
  export const CONFIG_CMD_MESSAGES = {
3
6
  /** 配置已恢复为默认值 */
@@ -7,7 +7,9 @@ import { SYNC_MESSAGES } from './sync.js';
7
7
  import { RESUME_MESSAGES } from './resume.js';
8
8
  import { REMOVE_MESSAGES } from './remove.js';
9
9
  import { RESET_MESSAGES } from './reset.js';
10
- import { CONFIG_CMD_MESSAGES } from './config.js';
10
+ import { CONFIG_CMD_MESSAGES, CONFIG_ALIAS_DISABLED_HINT } from './config.js';
11
+
12
+ export { CONFIG_ALIAS_DISABLED_HINT };
11
13
  import { STATUS_MESSAGES } from './status.js';
12
14
  import { ALIAS_MESSAGES } from './alias.js';
13
15
 
@@ -2,15 +2,17 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { Command } from 'commander';
3
3
 
4
4
  // mock enquirer(必须在所有 import 之前)
5
- const { mockSelectRun, mockInputRun } = vi.hoisted(() => {
5
+ const { mockSelectRun, mockInputRun, mockSelectConstructorArgs } = vi.hoisted(() => {
6
6
  const mockSelectRun = vi.fn();
7
7
  const mockInputRun = vi.fn();
8
- return { mockSelectRun, mockInputRun };
8
+ /** 用于捕获 Select 构造时传入的参数 */
9
+ const mockSelectConstructorArgs: unknown[] = [];
10
+ return { mockSelectRun, mockInputRun, mockSelectConstructorArgs };
9
11
  });
10
12
 
11
13
  vi.mock('enquirer', () => ({
12
14
  default: {
13
- Select: function MockSelect() { return { run: mockSelectRun }; },
15
+ Select: function MockSelect(opts: unknown) { mockSelectConstructorArgs.push(opts); return { run: mockSelectRun }; },
14
16
  Input: function MockInput() { return { run: mockInputRun }; },
15
17
  },
16
18
  }));
@@ -48,6 +50,7 @@ vi.mock('../../../src/constants/index.js', () => ({
48
50
  confirmDestructiveOps: true,
49
51
  maxConcurrency: 0,
50
52
  terminalApp: 'auto',
53
+ aliases: {},
51
54
  },
52
55
  CONFIG_DESCRIPTIONS: {
53
56
  autoDeleteBranch: '自动删除分支',
@@ -56,6 +59,7 @@ vi.mock('../../../src/constants/index.js', () => ({
56
59
  confirmDestructiveOps: '破坏性操作确认',
57
60
  maxConcurrency: '最大并发数',
58
61
  terminalApp: '终端应用',
62
+ aliases: '命令别名映射',
59
63
  },
60
64
  CONFIG_DEFINITIONS: {
61
65
  autoDeleteBranch: { defaultValue: false, description: '自动删除分支' },
@@ -64,7 +68,9 @@ vi.mock('../../../src/constants/index.js', () => ({
64
68
  confirmDestructiveOps: { defaultValue: true, description: '破坏性操作确认' },
65
69
  maxConcurrency: { defaultValue: 0, description: '最大并发数' },
66
70
  terminalApp: { defaultValue: 'auto', description: '终端应用', allowedValues: ['auto', 'iterm2', 'terminal'] },
71
+ aliases: { defaultValue: {}, description: '命令别名映射' },
67
72
  },
73
+ CONFIG_ALIAS_DISABLED_HINT: '(通过 clawt alias 命令管理)',
68
74
  MESSAGES: {
69
75
  CONFIG_RESET_SUCCESS: '配置已恢复为默认值',
70
76
  DESTRUCTIVE_OP_CANCELLED: '已取消操作',
@@ -104,6 +110,7 @@ function createMockConfig() {
104
110
  confirmDestructiveOps: true,
105
111
  maxConcurrency: 0,
106
112
  terminalApp: 'auto',
113
+ aliases: {},
107
114
  };
108
115
  }
109
116
 
@@ -117,6 +124,7 @@ beforeEach(() => {
117
124
  mockedConfirmDestructiveAction.mockReset();
118
125
  mockSelectRun.mockReset();
119
126
  mockInputRun.mockReset();
127
+ mockSelectConstructorArgs.length = 0;
120
128
  });
121
129
 
122
130
  describe('registerConfigCommand', () => {
@@ -374,6 +382,29 @@ describe('handleConfigSet — 交互模式', () => {
374
382
  );
375
383
  expect(mockedPrintSuccess).toHaveBeenCalled();
376
384
  });
385
+
386
+ it('aliases 选项带 disabled 属性且不可选', async () => {
387
+ mockedLoadConfig.mockReturnValue(createMockConfig());
388
+ // 选择一个普通配置项完成交互流程
389
+ mockSelectRun.mockResolvedValueOnce('autoDeleteBranch');
390
+ mockSelectRun.mockResolvedValueOnce('true');
391
+
392
+ const program = new Command();
393
+ program.exitOverride();
394
+ registerConfigCommand(program);
395
+ await program.parseAsync(['config', 'set'], { from: 'user' });
396
+
397
+ // 捕获第一次 Select 构造参数(配置项选择列表)
398
+ const selectOpts = mockSelectConstructorArgs[0] as { choices: Array<{ name: string; disabled?: string }> };
399
+ const aliasesChoice = selectOpts.choices.find((c) => c.name === 'aliases');
400
+ expect(aliasesChoice).toBeDefined();
401
+ expect(aliasesChoice!.disabled).toBe('(通过 clawt alias 命令管理)');
402
+
403
+ // 普通配置项不应有 disabled 属性
404
+ const normalChoice = selectOpts.choices.find((c) => c.name === 'autoDeleteBranch');
405
+ expect(normalChoice).toBeDefined();
406
+ expect(normalChoice!.disabled).toBeUndefined();
407
+ });
377
408
  });
378
409
 
379
410
  describe('handleConfigGet', () => {