clawt 2.12.1 → 2.14.0
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 +28 -0
- package/dist/index.js +314 -58
- package/dist/postinstall.js +49 -3
- package/docs/spec.md +111 -1
- package/package.json +1 -1
- package/src/commands/alias.ts +132 -0
- package/src/commands/config.ts +116 -40
- package/src/constants/config.ts +6 -0
- package/src/constants/index.ts +1 -1
- package/src/constants/messages/alias.ts +22 -0
- package/src/constants/messages/config.ts +22 -0
- package/src/constants/messages/index.ts +2 -0
- package/src/index.ts +7 -1
- package/src/types/config.ts +4 -0
- package/src/utils/alias.ts +20 -0
- package/src/utils/config-strategy.ts +196 -0
- package/src/utils/config.ts +17 -1
- package/src/utils/index.ts +3 -1
- package/tests/unit/commands/alias.test.ts +184 -0
- package/tests/unit/commands/config.test.ts +324 -24
- package/tests/unit/constants/config.test.ts +25 -1
- package/tests/unit/constants/messages.test.ts +28 -0
- package/tests/unit/utils/alias.test.ts +51 -0
- package/tests/unit/utils/config-strategy.test.ts +217 -0
- package/tests/unit/utils/config.test.ts +25 -1
package/docs/spec.md
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
- [5.12 将主分支代码同步到目标 Worktree](#512-将主分支代码同步到目标-worktree)
|
|
27
27
|
- [5.13 重置主 Worktree 工作区和暂存区](#513-重置主-worktree-工作区和暂存区)
|
|
28
28
|
- [5.14 项目全局状态总览](#514-项目全局状态总览)
|
|
29
|
+
- [5.15 命令别名管理](#515-命令别名管理)
|
|
29
30
|
- [6. 错误处理规范](#6-错误处理规范)
|
|
30
31
|
- [7. 非功能性需求](#7-非功能性需求)
|
|
31
32
|
- [7.1 性能](#71-性能)
|
|
@@ -179,6 +180,7 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
|
|
|
179
180
|
| `clawt sync` | 将主分支最新代码同步到目标 worktree | 5.12 |
|
|
180
181
|
| `clawt reset` | 重置主 worktree 工作区和暂存区 | 5.13 |
|
|
181
182
|
| `clawt status` | 显示项目全局状态总览(支持 `--json` 格式输出) | 5.14 |
|
|
183
|
+
| `clawt alias` | 管理命令别名(列出 / 设置 / 移除) | 5.15 |
|
|
182
184
|
|
|
183
185
|
**全局选项:**
|
|
184
186
|
|
|
@@ -842,7 +844,8 @@ clawt merge [-m <commitMessage>]
|
|
|
842
844
|
"autoPullPush": false,
|
|
843
845
|
"confirmDestructiveOps": true,
|
|
844
846
|
"maxConcurrency": 0,
|
|
845
|
-
"terminalApp": "auto"
|
|
847
|
+
"terminalApp": "auto",
|
|
848
|
+
"aliases": {}
|
|
846
849
|
}
|
|
847
850
|
```
|
|
848
851
|
|
|
@@ -856,6 +859,7 @@ clawt merge [-m <commitMessage>]
|
|
|
856
859
|
| `confirmDestructiveOps` | `boolean` | `true` | 执行破坏性操作(reset、validate --clean、config reset)前是否提示确认 |
|
|
857
860
|
| `maxConcurrency` | `number` | `0` | run 命令默认最大并发数,`0` 表示不限制 |
|
|
858
861
|
| `terminalApp` | `string` | `"auto"` | 批量 resume 使用的终端应用:`auto`(自动检测)、`iterm2`、`terminal`(macOS) |
|
|
862
|
+
| `aliases` | `Record<string, string>` | `{}` | 命令别名映射,键为别名,值为目标内置命令名 |
|
|
859
863
|
|
|
860
864
|
---
|
|
861
865
|
|
|
@@ -1350,6 +1354,112 @@ clawt status [--json]
|
|
|
1350
1354
|
|
|
1351
1355
|
---
|
|
1352
1356
|
|
|
1357
|
+
### 5.15 命令别名管理
|
|
1358
|
+
|
|
1359
|
+
**命令:**
|
|
1360
|
+
|
|
1361
|
+
```bash
|
|
1362
|
+
# 列出所有命令别名
|
|
1363
|
+
clawt alias
|
|
1364
|
+
clawt alias list
|
|
1365
|
+
|
|
1366
|
+
# 设置命令别名
|
|
1367
|
+
clawt alias set <alias> <command>
|
|
1368
|
+
|
|
1369
|
+
# 移除命令别名
|
|
1370
|
+
clawt alias remove <alias>
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
**子命令:**
|
|
1374
|
+
|
|
1375
|
+
| 子命令 | 说明 |
|
|
1376
|
+
| ------ | ---- |
|
|
1377
|
+
| `clawt alias` / `clawt alias list` | 列出所有已配置的命令别名 |
|
|
1378
|
+
| `clawt alias set <alias> <command>` | 设置命令别名,将 `<alias>` 映射到 `<command>` |
|
|
1379
|
+
| `clawt alias remove <alias>` | 移除指定的命令别名 |
|
|
1380
|
+
|
|
1381
|
+
**参数:**
|
|
1382
|
+
|
|
1383
|
+
| 参数 | 必填 | 说明 |
|
|
1384
|
+
| ---- | ---- | ---- |
|
|
1385
|
+
| `<alias>` | 是(set / remove) | 别名名称 |
|
|
1386
|
+
| `<command>` | 是(set) | 目标内置命令名 |
|
|
1387
|
+
|
|
1388
|
+
**约束规则:**
|
|
1389
|
+
|
|
1390
|
+
1. **别名不能覆盖内置命令名**:别名不能与已注册的内置命令同名(`list`、`create`、`remove`、`run`、`resume`、`validate`、`merge`、`config`、`sync`、`reset`、`status`、`alias`)。如果用户尝试设置与内置命令同名的别名,报错退出
|
|
1391
|
+
2. **目标必须是内置命令**:别名的目标(`<command>`)必须是已注册的内置命令名。如果指定了不存在的目标命令,报错退出
|
|
1392
|
+
3. **参数透传**:通过别名调用时,所有选项和参数会完全透传给目标命令,行为与直接调用目标命令完全一致
|
|
1393
|
+
|
|
1394
|
+
**持久化:**
|
|
1395
|
+
|
|
1396
|
+
别名配置存储在 `~/.clawt/config.json` 的 `aliases` 字段中(类型 `Record<string, string>`,默认 `{}`)。
|
|
1397
|
+
|
|
1398
|
+
**运行流程:**
|
|
1399
|
+
|
|
1400
|
+
#### `alias list`(默认)
|
|
1401
|
+
|
|
1402
|
+
1. 读取配置文件中的 `aliases` 字段
|
|
1403
|
+
2. 如果没有配置任何别名,输出提示 `当前没有配置任何命令别名`
|
|
1404
|
+
3. 如果有别名,逐行输出所有别名映射
|
|
1405
|
+
|
|
1406
|
+
**输出格式:**
|
|
1407
|
+
|
|
1408
|
+
```
|
|
1409
|
+
命令别名列表:
|
|
1410
|
+
|
|
1411
|
+
l → list
|
|
1412
|
+
r → run
|
|
1413
|
+
v → validate
|
|
1414
|
+
```
|
|
1415
|
+
|
|
1416
|
+
#### `alias set <alias> <command>`
|
|
1417
|
+
|
|
1418
|
+
1. **校验别名不与内置命令冲突**:检查 `<alias>` 是否为内置命令名,是则报错退出
|
|
1419
|
+
2. **校验目标命令存在**:检查 `<command>` 是否为已注册的内置命令名,不是则报错退出
|
|
1420
|
+
3. 将别名写入配置文件的 `aliases` 字段(如果别名已存在,覆盖旧值)
|
|
1421
|
+
4. 输出成功提示
|
|
1422
|
+
|
|
1423
|
+
**输出格式:**
|
|
1424
|
+
|
|
1425
|
+
```
|
|
1426
|
+
✓ 已设置别名: l → list
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
#### `alias remove <alias>`
|
|
1430
|
+
|
|
1431
|
+
1. 读取配置文件中的 `aliases` 字段
|
|
1432
|
+
2. 检查指定的别名是否存在,不存在则报错退出
|
|
1433
|
+
3. 从 `aliases` 中删除该别名并写入配置文件
|
|
1434
|
+
4. 输出成功提示
|
|
1435
|
+
|
|
1436
|
+
**输出格式:**
|
|
1437
|
+
|
|
1438
|
+
```
|
|
1439
|
+
✓ 已移除别名: l
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
**别名使用示例:**
|
|
1443
|
+
|
|
1444
|
+
```bash
|
|
1445
|
+
# 设置别名
|
|
1446
|
+
clawt alias set l list
|
|
1447
|
+
clawt alias set r run
|
|
1448
|
+
clawt alias set v validate
|
|
1449
|
+
|
|
1450
|
+
# 使用别名(等同于对应的完整命令)
|
|
1451
|
+
clawt l # 等同于 clawt list
|
|
1452
|
+
clawt r task.md # 等同于 clawt run task.md
|
|
1453
|
+
|
|
1454
|
+
# 查看所有别名
|
|
1455
|
+
clawt alias list
|
|
1456
|
+
|
|
1457
|
+
# 移除别名
|
|
1458
|
+
clawt alias remove l
|
|
1459
|
+
```
|
|
1460
|
+
|
|
1461
|
+
---
|
|
1462
|
+
|
|
1353
1463
|
## 6. 错误处理规范
|
|
1354
1464
|
|
|
1355
1465
|
### 6.1 通用错误处理
|
package/package.json
CHANGED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { MESSAGES } from '../constants/index.js';
|
|
4
|
+
import { logger } from '../logger/index.js';
|
|
5
|
+
import { loadConfig, writeConfig, printInfo, printSuccess, printError, printSeparator } from '../utils/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 从 Commander 实例动态获取所有已注册的命令名
|
|
9
|
+
* @param {Command} program - Commander 实例
|
|
10
|
+
* @returns {string[]} 已注册的命令名列表
|
|
11
|
+
*/
|
|
12
|
+
function getRegisteredCommandNames(program: Command): string[] {
|
|
13
|
+
return program.commands.map((cmd) => cmd.name());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 校验别名名称是否与已注册命令冲突
|
|
18
|
+
* @param {Command} program - Commander 实例
|
|
19
|
+
* @param {string} alias - 别名
|
|
20
|
+
* @returns {boolean} 是否冲突
|
|
21
|
+
*/
|
|
22
|
+
function isBuiltinCommand(program: Command, alias: string): boolean {
|
|
23
|
+
return getRegisteredCommandNames(program).includes(alias);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 列出所有已配置的别名
|
|
28
|
+
*/
|
|
29
|
+
function handleAliasList(): void {
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
const { aliases } = config;
|
|
32
|
+
const entries = Object.entries(aliases);
|
|
33
|
+
|
|
34
|
+
logger.debug('alias list 命令执行,展示别名列表');
|
|
35
|
+
|
|
36
|
+
if (entries.length === 0) {
|
|
37
|
+
printInfo(MESSAGES.ALIAS_LIST_EMPTY);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
printInfo(`\n${MESSAGES.ALIAS_LIST_TITLE}\n`);
|
|
42
|
+
printSeparator();
|
|
43
|
+
|
|
44
|
+
for (const [alias, command] of entries) {
|
|
45
|
+
printInfo(` ${chalk.bold(alias)} → ${chalk.cyan(command)}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
printInfo('');
|
|
49
|
+
printSeparator();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 设置命令别名
|
|
54
|
+
* @param {Command} program - Commander 实例
|
|
55
|
+
* @param {string} alias - 别名
|
|
56
|
+
* @param {string} command - 目标命令名
|
|
57
|
+
*/
|
|
58
|
+
function handleAliasSet(program: Command, alias: string, command: string): void {
|
|
59
|
+
logger.debug(`alias set 命令执行,别名: ${alias},目标: ${command}`);
|
|
60
|
+
|
|
61
|
+
// 校验别名不能与内置命令冲突
|
|
62
|
+
if (isBuiltinCommand(program, alias)) {
|
|
63
|
+
printError(MESSAGES.ALIAS_CONFLICTS_BUILTIN(alias));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 校验目标命令必须是已注册的内置命令
|
|
68
|
+
if (!isBuiltinCommand(program, command)) {
|
|
69
|
+
printError(MESSAGES.ALIAS_TARGET_NOT_FOUND(command));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const config = loadConfig();
|
|
74
|
+
config.aliases[alias] = command;
|
|
75
|
+
writeConfig(config);
|
|
76
|
+
|
|
77
|
+
printSuccess(MESSAGES.ALIAS_SET_SUCCESS(alias, command));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 移除命令别名
|
|
82
|
+
* @param {string} alias - 要移除的别名
|
|
83
|
+
*/
|
|
84
|
+
function handleAliasRemove(alias: string): void {
|
|
85
|
+
logger.debug(`alias remove 命令执行,别名: ${alias}`);
|
|
86
|
+
|
|
87
|
+
const config = loadConfig();
|
|
88
|
+
|
|
89
|
+
if (!(alias in config.aliases)) {
|
|
90
|
+
printError(MESSAGES.ALIAS_NOT_FOUND(alias));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
delete config.aliases[alias];
|
|
95
|
+
writeConfig(config);
|
|
96
|
+
|
|
97
|
+
printSuccess(MESSAGES.ALIAS_REMOVE_SUCCESS(alias));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 注册 alias 命令组:管理命令别名
|
|
102
|
+
* @param {Command} program - Commander 实例
|
|
103
|
+
*/
|
|
104
|
+
export function registerAliasCommand(program: Command): void {
|
|
105
|
+
const aliasCmd = program
|
|
106
|
+
.command('alias')
|
|
107
|
+
.description('管理命令别名')
|
|
108
|
+
.action(() => {
|
|
109
|
+
handleAliasList();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
aliasCmd
|
|
113
|
+
.command('list')
|
|
114
|
+
.description('列出所有别名')
|
|
115
|
+
.action(() => {
|
|
116
|
+
handleAliasList();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
aliasCmd
|
|
120
|
+
.command('set <alias> <command>')
|
|
121
|
+
.description('设置命令别名')
|
|
122
|
+
.action((alias: string, command: string) => {
|
|
123
|
+
handleAliasSet(program, alias, command);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
aliasCmd
|
|
127
|
+
.command('remove <alias>')
|
|
128
|
+
.description('移除命令别名')
|
|
129
|
+
.action((alias: string) => {
|
|
130
|
+
handleAliasRemove(alias);
|
|
131
|
+
});
|
|
132
|
+
}
|
package/src/commands/config.ts
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import Enquirer from 'enquirer';
|
|
4
|
+
import { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, MESSAGES } from '../constants/index.js';
|
|
4
5
|
import { logger } from '../logger/index.js';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
loadConfig,
|
|
8
|
+
saveConfig,
|
|
9
|
+
writeDefaultConfig,
|
|
10
|
+
printInfo,
|
|
11
|
+
printSuccess,
|
|
12
|
+
printError,
|
|
13
|
+
confirmDestructiveAction,
|
|
14
|
+
isValidConfigKey,
|
|
15
|
+
getValidConfigKeys,
|
|
16
|
+
parseConfigValue,
|
|
17
|
+
promptConfigValue,
|
|
18
|
+
formatConfigValue,
|
|
19
|
+
} from '../utils/index.js';
|
|
6
20
|
import type { ClawtConfig } from '../types/index.js';
|
|
7
21
|
|
|
8
22
|
/**
|
|
@@ -12,9 +26,9 @@ import type { ClawtConfig } from '../types/index.js';
|
|
|
12
26
|
export function registerConfigCommand(program: Command): void {
|
|
13
27
|
const configCmd = program
|
|
14
28
|
.command('config')
|
|
15
|
-
.description('
|
|
16
|
-
.action(() => {
|
|
17
|
-
|
|
29
|
+
.description('交互式查看和修改全局配置')
|
|
30
|
+
.action(async () => {
|
|
31
|
+
await handleConfigSet();
|
|
18
32
|
});
|
|
19
33
|
|
|
20
34
|
configCmd
|
|
@@ -23,36 +37,20 @@ export function registerConfigCommand(program: Command): void {
|
|
|
23
37
|
.action(async () => {
|
|
24
38
|
await handleConfigReset();
|
|
25
39
|
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* 执行 config 命令的核心逻辑,读取并展示配置列表
|
|
30
|
-
*/
|
|
31
|
-
function handleConfig(): void {
|
|
32
|
-
const config = loadConfig();
|
|
33
|
-
|
|
34
|
-
logger.info('config 命令执行,展示全局配置');
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const key = keys[i];
|
|
43
|
-
const value = config[key];
|
|
44
|
-
const description = CONFIG_DESCRIPTIONS[key];
|
|
45
|
-
const formattedValue = formatConfigValue(value);
|
|
46
|
-
|
|
47
|
-
// 第一个配置项前增加空行,与下横线前的空行对称
|
|
48
|
-
if (i === 0) printInfo('');
|
|
49
|
-
printInfo(` ${chalk.bold(key)}: ${formattedValue}`);
|
|
50
|
-
printInfo(` ${chalk.dim(description)}`);
|
|
51
|
-
// 配置项之间及最后一项与下横线之间保持统一空行间距
|
|
52
|
-
printInfo('');
|
|
53
|
-
}
|
|
41
|
+
configCmd
|
|
42
|
+
.command('set [key] [value]')
|
|
43
|
+
.description('修改配置项(无参数进入交互式配置)')
|
|
44
|
+
.action(async (key?: string, value?: string) => {
|
|
45
|
+
await handleConfigSet(key, value);
|
|
46
|
+
});
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
configCmd
|
|
49
|
+
.command('get <key>')
|
|
50
|
+
.description('获取单个配置项的值')
|
|
51
|
+
.action((key: string) => {
|
|
52
|
+
handleConfigGet(key);
|
|
53
|
+
});
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
/**
|
|
@@ -76,13 +74,91 @@ async function handleConfigReset(): Promise<void> {
|
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
/**
|
|
79
|
-
*
|
|
80
|
-
* @param {
|
|
81
|
-
* @
|
|
77
|
+
* 执行 config set 子命令:直接设置或交互式配置
|
|
78
|
+
* @param {string} [key] - 配置项名称(可选,无参数进入交互式)
|
|
79
|
+
* @param {string} [value] - 配置值(可选)
|
|
82
80
|
*/
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
async function handleConfigSet(key?: string, value?: string): Promise<void> {
|
|
82
|
+
// 无参数进入交互式配置
|
|
83
|
+
if (!key) {
|
|
84
|
+
await handleInteractiveConfigSet();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 校验 key 有效性
|
|
89
|
+
if (!isValidConfigKey(key)) {
|
|
90
|
+
printError(MESSAGES.CONFIG_INVALID_KEY(key, getValidConfigKeys()));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 有 key 无 value 时提示缺少参数
|
|
95
|
+
if (value === undefined) {
|
|
96
|
+
printError(MESSAGES.CONFIG_MISSING_VALUE(key));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 解析并校验值
|
|
101
|
+
const result = parseConfigValue(key, value);
|
|
102
|
+
if (!result.success) {
|
|
103
|
+
printError(result.error);
|
|
104
|
+
return;
|
|
86
105
|
}
|
|
87
|
-
|
|
106
|
+
|
|
107
|
+
// 加载、修改、持久化
|
|
108
|
+
const config = loadConfig();
|
|
109
|
+
(config as unknown as Record<string, unknown>)[key] = result.value;
|
|
110
|
+
saveConfig(config);
|
|
111
|
+
|
|
112
|
+
logger.info(`config set 命令执行,设置 ${key} = ${String(result.value)}`);
|
|
113
|
+
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(key, String(result.value)));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 交互式配置修改:列出所有配置项供用户选择并修改
|
|
118
|
+
*/
|
|
119
|
+
async function handleInteractiveConfigSet(): Promise<void> {
|
|
120
|
+
const config = loadConfig();
|
|
121
|
+
const keys = Object.keys(DEFAULT_CONFIG) as Array<keyof ClawtConfig>;
|
|
122
|
+
|
|
123
|
+
logger.info('config set 命令执行,进入交互式配置');
|
|
124
|
+
|
|
125
|
+
// 构建选择列表,显示配置项名称、当前值和描述
|
|
126
|
+
const choices = keys.map((k) => ({
|
|
127
|
+
name: k,
|
|
128
|
+
message: `${k}: ${formatConfigValue(config[k])} ${chalk.dim(`— ${CONFIG_DESCRIPTIONS[k]}`)}`,
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
// @ts-expect-error enquirer 类型声明未导出 Select 类,但运行时存在
|
|
132
|
+
const selectedKey: keyof ClawtConfig = await new Enquirer.Select({
|
|
133
|
+
message: MESSAGES.CONFIG_SELECT_PROMPT,
|
|
134
|
+
choices,
|
|
135
|
+
}).run();
|
|
136
|
+
|
|
137
|
+
// 根据类型和 allowedValues 自动选择提示策略
|
|
138
|
+
const currentValue = config[selectedKey];
|
|
139
|
+
const newValue = await promptConfigValue(selectedKey, currentValue);
|
|
140
|
+
|
|
141
|
+
// 持久化并提示成功
|
|
142
|
+
(config as unknown as Record<string, unknown>)[selectedKey] = newValue;
|
|
143
|
+
saveConfig(config);
|
|
144
|
+
|
|
145
|
+
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(selectedKey, String(newValue)));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 执行 config get 子命令:获取单个配置项的值
|
|
150
|
+
* @param {string} key - 配置项名称
|
|
151
|
+
*/
|
|
152
|
+
function handleConfigGet(key: string): void {
|
|
153
|
+
// 校验 key 有效性
|
|
154
|
+
if (!isValidConfigKey(key)) {
|
|
155
|
+
printError(MESSAGES.CONFIG_INVALID_KEY(key, getValidConfigKeys()));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const config = loadConfig();
|
|
160
|
+
const value = config[key];
|
|
161
|
+
|
|
162
|
+
logger.info(`config get 命令执行,获取 ${key} = ${String(value)}`);
|
|
163
|
+
printInfo(MESSAGES.CONFIG_GET_VALUE(key, String(value)));
|
|
88
164
|
}
|
package/src/constants/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ClawtConfig, ConfigDefinitions } from '../types/index.js';
|
|
2
|
+
import { VALID_TERMINAL_APPS } from './terminal.js';
|
|
2
3
|
|
|
3
4
|
/** Claude Code 系统约束提示,禁止代码执行完成后构建项目验证 */
|
|
4
5
|
export const APPEND_SYSTEM_PROMPT =
|
|
@@ -32,6 +33,11 @@ export const CONFIG_DEFINITIONS: ConfigDefinitions = {
|
|
|
32
33
|
terminalApp: {
|
|
33
34
|
defaultValue: 'auto',
|
|
34
35
|
description: '批量 resume 使用的终端应用:auto(自动检测)、iterm2、terminal(macOS)',
|
|
36
|
+
allowedValues: VALID_TERMINAL_APPS,
|
|
37
|
+
},
|
|
38
|
+
aliases: {
|
|
39
|
+
defaultValue: {} as Record<string, string>,
|
|
40
|
+
description: '命令别名映射(通过 clawt alias 命令管理)',
|
|
35
41
|
},
|
|
36
42
|
};
|
|
37
43
|
|
package/src/constants/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { INVALID_BRANCH_CHARS } from './branch.js';
|
|
|
3
3
|
export { MESSAGES } from './messages/index.js';
|
|
4
4
|
export { EXIT_CODES } from './exitCodes.js';
|
|
5
5
|
export { ENABLE_BRACKETED_PASTE, DISABLE_BRACKETED_PASTE, PASTE_THRESHOLD_MS, VALID_TERMINAL_APPS, ITERM2_APP_PATH } from './terminal.js';
|
|
6
|
-
export { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, APPEND_SYSTEM_PROMPT } from './config.js';
|
|
6
|
+
export { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, CONFIG_DEFINITIONS, APPEND_SYSTEM_PROMPT } from './config.js';
|
|
7
7
|
export { AUTO_SAVE_COMMIT_MESSAGE } from './git.js';
|
|
8
8
|
export { DEBUG_LOG_PREFIX, DEBUG_TIMESTAMP_FORMAT } from './logger.js';
|
|
9
9
|
export {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** alias 命令专属提示消息 */
|
|
2
|
+
export const ALIAS_MESSAGES = {
|
|
3
|
+
/** 别名列表为空 */
|
|
4
|
+
ALIAS_LIST_EMPTY: '(无别名)',
|
|
5
|
+
/** 别名设置成功 */
|
|
6
|
+
ALIAS_SET_SUCCESS: (alias: string, command: string) =>
|
|
7
|
+
`✓ 已设置别名: ${alias} → ${command}`,
|
|
8
|
+
/** 别名移除成功 */
|
|
9
|
+
ALIAS_REMOVE_SUCCESS: (alias: string) =>
|
|
10
|
+
`✓ 已移除别名: ${alias}`,
|
|
11
|
+
/** 别名不存在 */
|
|
12
|
+
ALIAS_NOT_FOUND: (alias: string) =>
|
|
13
|
+
`别名 "${alias}" 不存在`,
|
|
14
|
+
/** 别名与内置命令冲突 */
|
|
15
|
+
ALIAS_CONFLICTS_BUILTIN: (alias: string) =>
|
|
16
|
+
`别名 "${alias}" 与内置命令冲突,不允许覆盖内置命令`,
|
|
17
|
+
/** 目标命令不存在 */
|
|
18
|
+
ALIAS_TARGET_NOT_FOUND: (command: string) =>
|
|
19
|
+
`目标命令 "${command}" 不存在,请指定已注册的内置命令名`,
|
|
20
|
+
/** 别名列表标题 */
|
|
21
|
+
ALIAS_LIST_TITLE: '当前别名列表:',
|
|
22
|
+
} as const;
|
|
@@ -2,4 +2,26 @@
|
|
|
2
2
|
export const CONFIG_CMD_MESSAGES = {
|
|
3
3
|
/** 配置已恢复为默认值 */
|
|
4
4
|
CONFIG_RESET_SUCCESS: '✓ 配置已恢复为默认值',
|
|
5
|
+
/** 配置项设置成功 */
|
|
6
|
+
CONFIG_SET_SUCCESS: (key: string, value: string) => `✓ ${key} 已设置为 ${value}`,
|
|
7
|
+
/** 获取配置值显示 */
|
|
8
|
+
CONFIG_GET_VALUE: (key: string, value: string) => `${key} = ${value}`,
|
|
9
|
+
/** 无效配置项名称 */
|
|
10
|
+
CONFIG_INVALID_KEY: (key: string, validKeys: string[]) =>
|
|
11
|
+
`无效的配置项: ${key}\n可用的配置项: ${validKeys.join(', ')}`,
|
|
12
|
+
/** 布尔类型值无效 */
|
|
13
|
+
CONFIG_INVALID_BOOLEAN: (key: string) =>
|
|
14
|
+
`配置项 ${key} 为布尔类型,仅接受 true 或 false`,
|
|
15
|
+
/** 数字类型值无效 */
|
|
16
|
+
CONFIG_INVALID_NUMBER: (key: string) =>
|
|
17
|
+
`配置项 ${key} 为数字类型,请输入有效的数字`,
|
|
18
|
+
/** 枚举类型配置项值无效(通用版) */
|
|
19
|
+
CONFIG_INVALID_ENUM: (key: string, validValues: readonly string[]) =>
|
|
20
|
+
`配置项 ${key} 仅接受以下值: ${validValues.join(', ')}`,
|
|
21
|
+
/** 交互式选择配置项提示 */
|
|
22
|
+
CONFIG_SELECT_PROMPT: '选择要修改的配置项',
|
|
23
|
+
/** 交互式输入新值提示 */
|
|
24
|
+
CONFIG_INPUT_PROMPT: (key: string) => `输入 ${key} 的新值`,
|
|
25
|
+
/** 缺少 value 参数提示 */
|
|
26
|
+
CONFIG_MISSING_VALUE: (key: string) => `缺少配置值,用法: clawt config set ${key} <value>`,
|
|
5
27
|
} as const;
|
|
@@ -9,6 +9,7 @@ import { REMOVE_MESSAGES } from './remove.js';
|
|
|
9
9
|
import { RESET_MESSAGES } from './reset.js';
|
|
10
10
|
import { CONFIG_CMD_MESSAGES } from './config.js';
|
|
11
11
|
import { STATUS_MESSAGES } from './status.js';
|
|
12
|
+
import { ALIAS_MESSAGES } from './alias.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* 提示消息模板
|
|
@@ -26,4 +27,5 @@ export const MESSAGES = {
|
|
|
26
27
|
...RESET_MESSAGES,
|
|
27
28
|
...CONFIG_CMD_MESSAGES,
|
|
28
29
|
...STATUS_MESSAGES,
|
|
30
|
+
...ALIAS_MESSAGES,
|
|
29
31
|
} as const;
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { ClawtError } from './errors/index.js';
|
|
4
4
|
import { logger, enableConsoleTransport } from './logger/index.js';
|
|
5
5
|
import { EXIT_CODES } from './constants/index.js';
|
|
6
|
-
import { printError, ensureClawtDirs } from './utils/index.js';
|
|
6
|
+
import { printError, ensureClawtDirs, loadConfig, applyAliases } from './utils/index.js';
|
|
7
7
|
import { registerListCommand } from './commands/list.js';
|
|
8
8
|
import { registerCreateCommand } from './commands/create.js';
|
|
9
9
|
import { registerRemoveCommand } from './commands/remove.js';
|
|
@@ -15,6 +15,7 @@ import { registerConfigCommand } from './commands/config.js';
|
|
|
15
15
|
import { registerSyncCommand } from './commands/sync.js';
|
|
16
16
|
import { registerResetCommand } from './commands/reset.js';
|
|
17
17
|
import { registerStatusCommand } from './commands/status.js';
|
|
18
|
+
import { registerAliasCommand } from './commands/alias.js';
|
|
18
19
|
|
|
19
20
|
// 从 package.json 读取版本号,避免硬编码
|
|
20
21
|
const require = createRequire(import.meta.url);
|
|
@@ -50,6 +51,11 @@ registerConfigCommand(program);
|
|
|
50
51
|
registerSyncCommand(program);
|
|
51
52
|
registerResetCommand(program);
|
|
52
53
|
registerStatusCommand(program);
|
|
54
|
+
registerAliasCommand(program);
|
|
55
|
+
|
|
56
|
+
// 加载配置并应用命令别名
|
|
57
|
+
const config = loadConfig();
|
|
58
|
+
applyAliases(program, config.aliases);
|
|
53
59
|
|
|
54
60
|
// 全局未捕获异常处理
|
|
55
61
|
process.on('uncaughtException', (error) => {
|
package/src/types/config.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface ClawtConfig {
|
|
|
12
12
|
maxConcurrency: number;
|
|
13
13
|
/** 批量 resume 使用的终端应用:'auto'(自动检测)、'iterm2'、'terminal'(macOS) */
|
|
14
14
|
terminalApp: string;
|
|
15
|
+
/** 命令别名映射,键为别名,值为目标内置命令名 */
|
|
16
|
+
aliases: Record<string, string>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/** 单个配置项的完整定义(默认值 + 描述) */
|
|
@@ -20,6 +22,8 @@ export interface ConfigItemDefinition<T> {
|
|
|
20
22
|
defaultValue: T;
|
|
21
23
|
/** 配置项描述,用于 config 命令展示 */
|
|
22
24
|
description: string;
|
|
25
|
+
/** 可选:允许的枚举值列表,仅对 string 类型有效 */
|
|
26
|
+
allowedValues?: T extends string ? readonly string[] : never;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
/** 所有配置项的完整定义映射 */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { ClawtConfig } from '../types/index.js';
|
|
3
|
+
import { logger } from '../logger/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 根据配置中的别名映射,为已注册的命令添加 Commander.js 别名
|
|
7
|
+
* @param {Command} program - Commander 实例
|
|
8
|
+
* @param {ClawtConfig['aliases']} aliases - 别名映射
|
|
9
|
+
*/
|
|
10
|
+
export function applyAliases(program: Command, aliases: ClawtConfig['aliases']): void {
|
|
11
|
+
for (const [alias, commandName] of Object.entries(aliases)) {
|
|
12
|
+
const targetCmd = program.commands.find((cmd) => cmd.name() === commandName);
|
|
13
|
+
if (targetCmd) {
|
|
14
|
+
targetCmd.alias(alias);
|
|
15
|
+
logger.debug(`已注册别名: ${alias} → ${commandName}`);
|
|
16
|
+
} else {
|
|
17
|
+
logger.warn(`别名 "${alias}" 的目标命令 "${commandName}" 不存在,已跳过`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|