clawt 1.1.0 → 1.2.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/.claude/agent-memory/docs-sync-updater/MEMORY.md +10 -3
- package/CLAUDE.md +10 -3
- package/README.md +19 -2
- package/dist/index.js +56 -29
- package/docs/spec.md +35 -2
- package/package.json +1 -1
- package/src/commands/config.ts +7 -2
- package/src/commands/resume.ts +60 -0
- package/src/commands/run.ts +2 -37
- package/src/index.ts +2 -0
- package/src/types/command.ts +6 -0
- package/src/types/index.ts +1 -1
- package/src/utils/claude.ts +41 -0
- package/src/utils/index.ts +1 -0
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
### docs/spec.md
|
|
6
6
|
- 完整的软件规格说明,包含 7 大章节
|
|
7
|
-
- 命令流程在 `5. 需求场景详细设计` 下,每个命令一个子章节(5.1-5.
|
|
7
|
+
- 命令流程在 `5. 需求场景详细设计` 下,每个命令一个子章节(5.1-5.11)
|
|
8
8
|
- run 命令对应 `5.2 批量创建 Worktree + 执行 Claude Code 任务`,流程按步骤编号描述
|
|
9
9
|
- merge 命令对应 `5.6 合并验证过的分支`,流程按步骤编号描述
|
|
10
10
|
- config 命令对应 `5.10 查看全局配置`,只读展示配置
|
|
11
|
+
- resume 命令对应 `5.11 在已有 Worktree 中恢复会话`,查找已有 worktree 并启动交互式界面
|
|
11
12
|
- 配置项说明在 `5.7 默认配置文件` 章节的表格中
|
|
12
13
|
- 更新模式:新增步骤时追加编号,配置项影响范围变化时更新说明列
|
|
13
14
|
|
|
14
15
|
### CLAUDE.md
|
|
15
16
|
- 面向 Claude Code 的项目架构指引,精简扼要
|
|
16
17
|
- run 命令流程在 `核心流程(run 命令)` 章节,编号列表描述
|
|
18
|
+
- resume 命令流程在独立的 `### resume 命令流程` 章节,编号列表描述
|
|
17
19
|
- merge 和 run 中断清理在 `validate + merge 工作流` 章节,一行式描述用箭头连接流程
|
|
18
20
|
- utils 目录描述用括号内逗号分隔列举功能模块
|
|
19
21
|
- 更新模式:编号列表追加步骤,箭头链追加阶段,括号内追加关键词
|
|
@@ -30,6 +32,7 @@
|
|
|
30
32
|
- run 的中断清理在所有子进程退出后执行
|
|
31
33
|
- 文档中文风格,技术术语保留英文(worktree, merge, branch, SIGINT 等)
|
|
32
34
|
- cleanupWorktrees 是 merge 和 run 共用的公共清理函数(在 src/utils/worktree.ts)
|
|
35
|
+
- `launchInteractiveClaude` 是 run(交互式模式)和 resume 共用的公共函数(在 src/utils/claude.ts)
|
|
33
36
|
- killAllChildProcesses 是 run 专用的子进程终止函数(在 src/utils/shell.ts)
|
|
34
37
|
|
|
35
38
|
## 配置项同步检查点
|
|
@@ -55,6 +58,10 @@ run 命令有两种模式(自 claudeCodeCommand 特性后):
|
|
|
55
58
|
- 传 `--tasks`:并行任务模式(多 worktree + `executeClaudeTask` + spawnProcess)
|
|
56
59
|
- CLAUDE.md 中的核心流程按模式分段描述
|
|
57
60
|
|
|
58
|
-
## 命令清单(
|
|
61
|
+
## 命令清单(8 个)
|
|
59
62
|
|
|
60
|
-
`create`、`run`、`list`、`remove`、`validate`、`merge`、`config`
|
|
63
|
+
`create`、`run`、`resume`、`list`、`remove`、`validate`、`merge`、`config`
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
- resume 和 run(交互式模式)共用 `launchInteractiveClaude()`,该函数从 run.ts 提取到 src/utils/claude.ts
|
|
67
|
+
- `claudeCodeCommand` 配置项同时影响 run 交互式模式和 resume 命令
|
package/CLAUDE.md
CHANGED
|
@@ -24,7 +24,7 @@ npm i -g . # 本地全局安装进行测试
|
|
|
24
24
|
|
|
25
25
|
每个命令为独立文件 `src/commands/<name>.ts`,导出 `registerXxxCommand(program)` 函数,在 `src/index.ts` 中统一注册到 Commander。命令内部逻辑封装在对应的 `handleXxx` 函数中。
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
八个命令:`create`、`run`、`resume`、`list`、`remove`、`validate`、`merge`、`config`。
|
|
28
28
|
|
|
29
29
|
### 核心流程(run 命令)
|
|
30
30
|
|
|
@@ -46,6 +46,13 @@ run 命令有两种模式:
|
|
|
46
46
|
5. 每个任务完成时实时输出通知,全部完成后输出汇总
|
|
47
47
|
6. SIGINT(Ctrl+C)中断处理:`killAllChildProcesses()` 终止所有子进程 → 等待退出 → `handleInterruptCleanup()` 根据 `autoDeleteBranch` 配置自动或交互式清理 worktree 和分支
|
|
48
48
|
|
|
49
|
+
### resume 命令流程
|
|
50
|
+
|
|
51
|
+
1. `validateMainWorktree()` 确认在主 worktree 根目录
|
|
52
|
+
2. `validateClaudeCodeInstalled()` 确认 claude CLI 可用
|
|
53
|
+
3. `findWorktreeByBranch()` 在当前项目的 worktree 列表中按分支名查找已有 worktree
|
|
54
|
+
4. `launchInteractiveClaude()` 在目标 worktree 中启动 Claude Code 交互式界面
|
|
55
|
+
|
|
49
56
|
### validate + merge 工作流
|
|
50
57
|
|
|
51
58
|
- `validate`:将目标 worktree 的变更通过 git stash 迁移到主 worktree,便于在主 worktree 中测试
|
|
@@ -55,7 +62,7 @@ run 命令有两种模式:
|
|
|
55
62
|
### 目录层级
|
|
56
63
|
|
|
57
64
|
- `src/commands/` — 各命令的注册与处理逻辑
|
|
58
|
-
- `src/utils/` — 工具函数(git 操作、shell 执行与子进程管理、分支名处理、worktree
|
|
65
|
+
- `src/utils/` — 工具函数(git 操作、shell 执行与子进程管理、分支名处理、worktree 管理与批量清理、配置、格式化输出、交互式输入、Claude Code 交互式启动)
|
|
59
66
|
- `src/constants/` — 常量定义(路径、退出码、消息模板、分支规则、配置默认值、终端控制序列)
|
|
60
67
|
- `src/types/` — TypeScript 类型定义
|
|
61
68
|
- `src/errors/` — 自定义 `ClawtError` 错误类(携带退出码)
|
|
@@ -65,7 +72,7 @@ run 命令有两种模式:
|
|
|
65
72
|
|
|
66
73
|
- 所有命令执行前都会调用 `validateMainWorktree()` 确保在主 worktree 根目录(`git rev-parse --git-common-dir === ".git"`)
|
|
67
74
|
- Worktree 统一存放在 `~/.clawt/worktrees/<projectName>/` 下
|
|
68
|
-
- 全局配置文件 `~/.clawt/config.json`,postinstall 时自动创建/合并,包含 `autoDeleteBranch`(是否自动删除分支)、`claudeCodeCommand`(Claude Code CLI
|
|
75
|
+
- 全局配置文件 `~/.clawt/config.json`,postinstall 时自动创建/合并,包含 `autoDeleteBranch`(是否自动删除分支)、`claudeCodeCommand`(Claude Code CLI 启动指令,用于 `run` 和 `resume` 的交互式界面)、`autoPullPush`(merge 后是否自动 pull/push)三个配置项。配置项以 `CONFIG_DEFINITIONS` 为单一数据源,`DEFAULT_CONFIG` 和 `CONFIG_DESCRIPTIONS` 均从中派生
|
|
69
76
|
- shell 命令执行有同步(`execCommand` → `execSync`)和异步(`spawnProcess` → `spawn`)两种方式
|
|
70
77
|
- 项目为纯 ESM(`"type": "module"`),模块导入需带 `.js` 后缀
|
|
71
78
|
- 分支名特殊字符会被 `sanitizeBranchName()` 自动清理
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm i -g clawt
|
|
|
12
12
|
|
|
13
13
|
- Node.js >= 18
|
|
14
14
|
- Git >= 2.15
|
|
15
|
-
- Claude Code CLI
|
|
15
|
+
- Claude Code CLI(`clawt run` 和 `clawt resume` 需要)
|
|
16
16
|
|
|
17
17
|
## 使用前提
|
|
18
18
|
|
|
@@ -73,6 +73,23 @@ clawt run -b feature-scheme \
|
|
|
73
73
|
clawt run -b feature-login
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### `clawt resume` — 在已有 worktree 中恢复 Claude Code 会话
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
clawt resume -b <branchName>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| 参数 | 必填 | 说明 |
|
|
83
|
+
| ---- | ---- | ---- |
|
|
84
|
+
| `-b` | 是 | 要恢复的分支名 |
|
|
85
|
+
|
|
86
|
+
在之前通过 `clawt run` 或 `clawt create` 创建的 worktree 中重新打开 Claude Code 交互式界面,继续之前的工作。启动命令由配置项 `claudeCodeCommand` 指定(默认 `claude`)。
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# 在已有 worktree 中恢复会话
|
|
90
|
+
clawt resume -b feature-login
|
|
91
|
+
```
|
|
92
|
+
|
|
76
93
|
### `clawt validate` — 在主 worktree 验证分支变更
|
|
77
94
|
|
|
78
95
|
```bash
|
|
@@ -162,7 +179,7 @@ clawt config
|
|
|
162
179
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
163
180
|
| ------ | ---- | ------ | ---- |
|
|
164
181
|
| `autoDeleteBranch` | `boolean` | `false` | 移除 worktree 时自动删除对应本地分支;merge 成功后自动清理 worktree 和分支;run 中断后自动清理本次创建的 worktree 和分支 |
|
|
165
|
-
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks`
|
|
182
|
+
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks` 时和 `clawt resume` 在 worktree 中打开交互式界面 |
|
|
166
183
|
| `autoPullPush` | `boolean` | `false` | merge 成功后是否自动执行 git pull 和 git push |
|
|
167
184
|
|
|
168
185
|
## 分支名规则
|
package/dist/index.js
CHANGED
|
@@ -537,6 +537,34 @@ function ensureClawtDirs() {
|
|
|
537
537
|
// src/utils/prompt.ts
|
|
538
538
|
import Enquirer from "enquirer";
|
|
539
539
|
|
|
540
|
+
// src/utils/claude.ts
|
|
541
|
+
import { spawnSync } from "child_process";
|
|
542
|
+
function launchInteractiveClaude(worktree) {
|
|
543
|
+
const commandStr = getConfigValue("claudeCodeCommand");
|
|
544
|
+
const parts = commandStr.split(/\s+/).filter(Boolean);
|
|
545
|
+
const cmd = parts[0];
|
|
546
|
+
const args = [
|
|
547
|
+
...parts.slice(1),
|
|
548
|
+
"--append-system-prompt",
|
|
549
|
+
APPEND_SYSTEM_PROMPT
|
|
550
|
+
];
|
|
551
|
+
printInfo(`\u6B63\u5728 worktree \u4E2D\u542F\u52A8 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762...`);
|
|
552
|
+
printInfo(` \u5206\u652F: ${worktree.branch}`);
|
|
553
|
+
printInfo(` \u8DEF\u5F84: ${worktree.path}`);
|
|
554
|
+
printInfo(` \u6307\u4EE4: ${commandStr}`);
|
|
555
|
+
printInfo("");
|
|
556
|
+
const result = spawnSync(cmd, args, {
|
|
557
|
+
cwd: worktree.path,
|
|
558
|
+
stdio: "inherit"
|
|
559
|
+
});
|
|
560
|
+
if (result.error) {
|
|
561
|
+
throw new ClawtError(`\u542F\u52A8 Claude Code \u5931\u8D25: ${result.error.message}`);
|
|
562
|
+
}
|
|
563
|
+
if (result.status !== null && result.status !== 0) {
|
|
564
|
+
printWarning(`Claude Code \u9000\u51FA\u7801: ${result.status}`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
540
568
|
// src/commands/list.ts
|
|
541
569
|
import chalk2 from "chalk";
|
|
542
570
|
function registerListCommand(program2) {
|
|
@@ -665,37 +693,11 @@ async function handleRemove(options) {
|
|
|
665
693
|
}
|
|
666
694
|
|
|
667
695
|
// src/commands/run.ts
|
|
668
|
-
import { spawnSync } from "child_process";
|
|
669
696
|
function registerRunCommand(program2) {
|
|
670
697
|
program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree \u5E76\u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1").requiredOption("-b, --branch <branchName>", "\u5206\u652F\u540D").option("--tasks <task...>", "\u4EFB\u52A1\u5217\u8868\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09\uFF0C\u4E0D\u4F20\u5219\u5728 worktree \u4E2D\u6253\u5F00 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762").action(async (options) => {
|
|
671
698
|
await handleRun(options);
|
|
672
699
|
});
|
|
673
700
|
}
|
|
674
|
-
function launchInteractiveClaude(worktree) {
|
|
675
|
-
const commandStr = getConfigValue("claudeCodeCommand");
|
|
676
|
-
const parts = commandStr.split(/\s+/).filter(Boolean);
|
|
677
|
-
const cmd = parts[0];
|
|
678
|
-
const args = [
|
|
679
|
-
...parts.slice(1),
|
|
680
|
-
"--append-system-prompt",
|
|
681
|
-
APPEND_SYSTEM_PROMPT
|
|
682
|
-
];
|
|
683
|
-
printInfo(`\u6B63\u5728 worktree \u4E2D\u542F\u52A8 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762...`);
|
|
684
|
-
printInfo(` \u5206\u652F: ${worktree.branch}`);
|
|
685
|
-
printInfo(` \u8DEF\u5F84: ${worktree.path}`);
|
|
686
|
-
printInfo(` \u6307\u4EE4: ${commandStr}`);
|
|
687
|
-
printInfo("");
|
|
688
|
-
const result = spawnSync(cmd, args, {
|
|
689
|
-
cwd: worktree.path,
|
|
690
|
-
stdio: "inherit"
|
|
691
|
-
});
|
|
692
|
-
if (result.error) {
|
|
693
|
-
throw new ClawtError(`\u542F\u52A8 Claude Code \u5931\u8D25: ${result.error.message}`);
|
|
694
|
-
}
|
|
695
|
-
if (result.status !== null && result.status !== 0) {
|
|
696
|
-
printWarning(`Claude Code \u9000\u51FA\u7801: ${result.status}`);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
701
|
function executeClaudeTask(worktree, task) {
|
|
700
702
|
const child = spawnProcess(
|
|
701
703
|
"claude",
|
|
@@ -856,6 +858,28 @@ async function handleRun(options) {
|
|
|
856
858
|
printTaskSummary(summary);
|
|
857
859
|
}
|
|
858
860
|
|
|
861
|
+
// src/commands/resume.ts
|
|
862
|
+
function registerResumeCommand(program2) {
|
|
863
|
+
program2.command("resume").description("\u5728\u5DF2\u6709 worktree \u4E2D\u6062\u590D Claude Code \u4EA4\u4E92\u5F0F\u4F1A\u8BDD").requiredOption("-b, --branch <branchName>", "\u8981\u6062\u590D\u7684\u5206\u652F\u540D").action(async (options) => {
|
|
864
|
+
await handleResume(options);
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
function findWorktreeByBranch(branchName) {
|
|
868
|
+
const worktrees = getProjectWorktrees();
|
|
869
|
+
const matched = worktrees.find((wt) => wt.branch === branchName);
|
|
870
|
+
if (!matched) {
|
|
871
|
+
throw new ClawtError(MESSAGES.WORKTREE_NOT_FOUND(branchName));
|
|
872
|
+
}
|
|
873
|
+
return matched;
|
|
874
|
+
}
|
|
875
|
+
async function handleResume(options) {
|
|
876
|
+
validateMainWorktree();
|
|
877
|
+
validateClaudeCodeInstalled();
|
|
878
|
+
logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}`);
|
|
879
|
+
const worktree = findWorktreeByBranch(options.branch);
|
|
880
|
+
launchInteractiveClaude(worktree);
|
|
881
|
+
}
|
|
882
|
+
|
|
859
883
|
// src/commands/validate.ts
|
|
860
884
|
import { join as join4 } from "path";
|
|
861
885
|
import { existsSync as existsSync5 } from "fs";
|
|
@@ -1017,13 +1041,15 @@ ${chalk3.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
|
|
|
1017
1041
|
`);
|
|
1018
1042
|
printSeparator();
|
|
1019
1043
|
const keys = Object.keys(DEFAULT_CONFIG);
|
|
1020
|
-
for (
|
|
1044
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1045
|
+
const key = keys[i];
|
|
1021
1046
|
const value = config[key];
|
|
1022
1047
|
const description = CONFIG_DESCRIPTIONS[key];
|
|
1023
1048
|
const formattedValue = formatConfigValue(value);
|
|
1049
|
+
if (i === 0) printInfo("");
|
|
1024
1050
|
printInfo(` ${chalk3.bold(key)}: ${formattedValue}`);
|
|
1025
|
-
printInfo(` ${chalk3.dim(description)}
|
|
1026
|
-
|
|
1051
|
+
printInfo(` ${chalk3.dim(description)}`);
|
|
1052
|
+
printInfo("");
|
|
1027
1053
|
}
|
|
1028
1054
|
printSeparator();
|
|
1029
1055
|
}
|
|
@@ -1044,6 +1070,7 @@ registerListCommand(program);
|
|
|
1044
1070
|
registerCreateCommand(program);
|
|
1045
1071
|
registerRemoveCommand(program);
|
|
1046
1072
|
registerRunCommand(program);
|
|
1073
|
+
registerResumeCommand(program);
|
|
1047
1074
|
registerValidateCommand(program);
|
|
1048
1075
|
registerMergeCommand(program);
|
|
1049
1076
|
registerConfigCommand(program);
|
package/docs/spec.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- [5.8 获取当前项目所有 Worktree](#58-获取当前项目所有-worktree)
|
|
23
23
|
- [5.9 日志系统](#59-日志系统)
|
|
24
24
|
- [5.10 查看全局配置](#510-查看全局配置)
|
|
25
|
+
- [5.11 在已有 Worktree 中恢复会话](#511-在已有-worktree-中恢复会话)
|
|
25
26
|
- [6. 错误处理规范](#6-错误处理规范)
|
|
26
27
|
- [7. 非功能性需求](#7-非功能性需求)
|
|
27
28
|
|
|
@@ -159,6 +160,7 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
|
|
|
159
160
|
| `clawt remove` | 移除 worktree(支持单个/批量/全部) | 5.5 |
|
|
160
161
|
| `clawt list` | 列出当前项目所有 worktree | 5.8 |
|
|
161
162
|
| `clawt config` | 查看全局配置 | 5.10 |
|
|
163
|
+
| `clawt resume` | 在已有 worktree 中恢复 Claude Code 交互式会话 | 5.11 |
|
|
162
164
|
|
|
163
165
|
所有命令执行前,都必须先执行**主 worktree 校验**(见 [2.1](#21-主-worktree-的定义与定位规则))。
|
|
164
166
|
|
|
@@ -592,7 +594,7 @@ clawt merge -b <branchName> [-m <commitMessage>]
|
|
|
592
594
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
593
595
|
| ------------------ | --------- | --------- | -------------------------------------------------- |
|
|
594
596
|
| `autoDeleteBranch` | `boolean` | `false` | 移除 worktree 时是否自动删除对应本地分支(无需每次确认);merge 成功后是否自动清理 worktree 和分支;run 任务被中断(Ctrl+C)后是否自动清理本次创建的 worktree 和分支 |
|
|
595
|
-
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks`
|
|
597
|
+
| `claudeCodeCommand` | `string` | `"claude"` | Claude Code CLI 启动指令,用于 `clawt run` 不传 `--tasks` 时和 `clawt resume` 在 worktree 中打开交互式界面 |
|
|
596
598
|
| `autoPullPush` | `boolean` | `false` | merge 成功后是否自动执行 git pull 和 git push |
|
|
597
599
|
|
|
598
600
|
---
|
|
@@ -706,6 +708,37 @@ clawt config
|
|
|
706
708
|
|
|
707
709
|
---
|
|
708
710
|
|
|
711
|
+
### 5.11 在已有 Worktree 中恢复会话
|
|
712
|
+
|
|
713
|
+
**命令:**
|
|
714
|
+
|
|
715
|
+
```bash
|
|
716
|
+
clawt resume -b <branchName>
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
**参数:**
|
|
720
|
+
|
|
721
|
+
| 参数 | 必填 | 说明 |
|
|
722
|
+
| ---- | ---- | ----------------------------------------------------- |
|
|
723
|
+
| `-b` | 是 | 要恢复的分支名(对应已有 worktree 的分支) |
|
|
724
|
+
|
|
725
|
+
**使用场景:**
|
|
726
|
+
|
|
727
|
+
当用户之前通过 `clawt run` 或 `clawt create` 创建了 worktree 但会话已结束,希望在该 worktree 中重新打开 Claude Code 交互式界面继续工作。
|
|
728
|
+
|
|
729
|
+
**运行流程:**
|
|
730
|
+
|
|
731
|
+
1. **主 worktree 校验** (2.1)
|
|
732
|
+
2. **Claude Code CLI 校验**:确认 `claude` CLI 可用
|
|
733
|
+
3. **查找目标 worktree**:在当前项目的 worktree 列表中按分支名查找匹配的 worktree
|
|
734
|
+
- 未找到 → 报错退出
|
|
735
|
+
- 找到 → 继续
|
|
736
|
+
4. **启动 Claude Code 交互式界面**:通过 `launchInteractiveClaude()` 在目标 worktree 中启动 Claude Code CLI 交互式界面(使用 `spawnSync` + `inherit stdio`)
|
|
737
|
+
|
|
738
|
+
启动命令通过配置项 `claudeCodeCommand`(默认值 `claude`)指定,与 `clawt run` 不传 `--tasks` 时的交互式界面行为一致。
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
709
742
|
## 6. 错误处理规范
|
|
710
743
|
|
|
711
744
|
### 6.1 通用错误处理
|
|
@@ -714,7 +747,7 @@ clawt config
|
|
|
714
747
|
| --------------------------------- | ---------------------------------------------------------- |
|
|
715
748
|
| 不在主 worktree 根目录执行 | 输出错误提示,退出 (exit code 1) |
|
|
716
749
|
| Git 未安装 | 输出错误提示,退出 (exit code 1) |
|
|
717
|
-
| Claude Code CLI 未安装 | 输出错误提示,退出 (exit code 1)
|
|
750
|
+
| Claude Code CLI 未安装 | 输出错误提示,退出 (exit code 1)(`clawt run` 和 `clawt resume` 时检测) |
|
|
718
751
|
| 分支已存在 | 输出错误提示,退出 (exit code 1) |
|
|
719
752
|
| Worktree 路径已存在 | 输出错误提示,退出 (exit code 1) |
|
|
720
753
|
| Git 命令执行失败 | 捕获 stderr,记录日志,输出错误提示,退出 (exit code 1) |
|
package/package.json
CHANGED
package/src/commands/config.ts
CHANGED
|
@@ -31,13 +31,18 @@ function handleConfig(): void {
|
|
|
31
31
|
|
|
32
32
|
const keys = Object.keys(DEFAULT_CONFIG) as Array<keyof ClawtConfig>;
|
|
33
33
|
|
|
34
|
-
for (
|
|
34
|
+
for (let i = 0; i < keys.length; i++) {
|
|
35
|
+
const key = keys[i];
|
|
35
36
|
const value = config[key];
|
|
36
37
|
const description = CONFIG_DESCRIPTIONS[key];
|
|
37
38
|
const formattedValue = formatConfigValue(value);
|
|
38
39
|
|
|
40
|
+
// 第一个配置项前增加空行,与下横线前的空行对称
|
|
41
|
+
if (i === 0) printInfo('');
|
|
39
42
|
printInfo(` ${chalk.bold(key)}: ${formattedValue}`);
|
|
40
|
-
printInfo(` ${chalk.dim(description)}
|
|
43
|
+
printInfo(` ${chalk.dim(description)}`);
|
|
44
|
+
// 配置项之间及最后一项与下横线之间保持统一空行间距
|
|
45
|
+
printInfo('');
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
printSeparator();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { logger } from '../logger/index.js';
|
|
3
|
+
import { ClawtError } from '../errors/index.js';
|
|
4
|
+
import { MESSAGES } from '../constants/index.js';
|
|
5
|
+
import type { ResumeOptions, WorktreeInfo } from '../types/index.js';
|
|
6
|
+
import {
|
|
7
|
+
validateMainWorktree,
|
|
8
|
+
validateClaudeCodeInstalled,
|
|
9
|
+
getProjectWorktrees,
|
|
10
|
+
launchInteractiveClaude,
|
|
11
|
+
} from '../utils/index.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 注册 resume 命令:在已有 worktree 中恢复 Claude Code 会话
|
|
15
|
+
* @param {Command} program - Commander 实例
|
|
16
|
+
*/
|
|
17
|
+
export function registerResumeCommand(program: Command): void {
|
|
18
|
+
program
|
|
19
|
+
.command('resume')
|
|
20
|
+
.description('在已有 worktree 中恢复 Claude Code 交互式会话')
|
|
21
|
+
.requiredOption('-b, --branch <branchName>', '要恢复的分支名')
|
|
22
|
+
.action(async (options: ResumeOptions) => {
|
|
23
|
+
await handleResume(options);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 在已有 worktree 列表中按分支名查找对应的 worktree
|
|
29
|
+
* @param {string} branchName - 目标分支名
|
|
30
|
+
* @returns {WorktreeInfo} 匹配的 worktree 信息
|
|
31
|
+
* @throws {ClawtError} 当找不到匹配的 worktree 时抛出
|
|
32
|
+
*/
|
|
33
|
+
function findWorktreeByBranch(branchName: string): WorktreeInfo {
|
|
34
|
+
const worktrees = getProjectWorktrees();
|
|
35
|
+
const matched = worktrees.find((wt) => wt.branch === branchName);
|
|
36
|
+
|
|
37
|
+
if (!matched) {
|
|
38
|
+
throw new ClawtError(MESSAGES.WORKTREE_NOT_FOUND(branchName));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return matched;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 执行 resume 命令的核心逻辑
|
|
46
|
+
* 查找已有 worktree 并恢复 Claude Code 会话
|
|
47
|
+
* @param {ResumeOptions} options - 命令选项
|
|
48
|
+
*/
|
|
49
|
+
async function handleResume(options: ResumeOptions): Promise<void> {
|
|
50
|
+
validateMainWorktree();
|
|
51
|
+
validateClaudeCodeInstalled();
|
|
52
|
+
|
|
53
|
+
logger.info(`resume 命令执行,分支: ${options.branch}`);
|
|
54
|
+
|
|
55
|
+
// 查找目标 worktree
|
|
56
|
+
const worktree = findWorktreeByBranch(options.branch);
|
|
57
|
+
|
|
58
|
+
// 启动 Claude Code 交互式界面
|
|
59
|
+
launchInteractiveClaude(worktree);
|
|
60
|
+
}
|
package/src/commands/run.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import type { ChildProcess } from 'node:child_process';
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
3
|
import { logger } from '../logger/index.js';
|
|
5
4
|
import { ClawtError } from '../errors/index.js';
|
|
6
|
-
import { MESSAGES
|
|
5
|
+
import { MESSAGES } from '../constants/index.js';
|
|
7
6
|
import type { RunOptions, ClaudeCodeResult, TaskResult, TaskSummary } from '../types/index.js';
|
|
8
7
|
import {
|
|
9
8
|
validateMainWorktree,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
printSeparator,
|
|
21
20
|
printDoubleSeparator,
|
|
22
21
|
confirmAction,
|
|
22
|
+
launchInteractiveClaude,
|
|
23
23
|
} from '../utils/index.js';
|
|
24
24
|
import type { WorktreeInfo } from '../types/index.js';
|
|
25
25
|
|
|
@@ -38,41 +38,6 @@ export function registerRunCommand(program: Command): void {
|
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* 在指定 worktree 中启动 Claude Code CLI 交互式界面
|
|
43
|
-
* 使用 spawnSync + inherit stdio,让用户直接与 Claude Code 交互
|
|
44
|
-
* @param {WorktreeInfo} worktree - worktree 信息
|
|
45
|
-
*/
|
|
46
|
-
function launchInteractiveClaude(worktree: WorktreeInfo): void {
|
|
47
|
-
const commandStr = getConfigValue('claudeCodeCommand');
|
|
48
|
-
const parts = commandStr.split(/\s+/).filter(Boolean);
|
|
49
|
-
const cmd = parts[0];
|
|
50
|
-
const args = [
|
|
51
|
-
...parts.slice(1),
|
|
52
|
-
'--append-system-prompt',
|
|
53
|
-
APPEND_SYSTEM_PROMPT,
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
printInfo(`正在 worktree 中启动 Claude Code 交互式界面...`);
|
|
57
|
-
printInfo(` 分支: ${worktree.branch}`);
|
|
58
|
-
printInfo(` 路径: ${worktree.path}`);
|
|
59
|
-
printInfo(` 指令: ${commandStr}`);
|
|
60
|
-
printInfo('');
|
|
61
|
-
|
|
62
|
-
const result = spawnSync(cmd, args, {
|
|
63
|
-
cwd: worktree.path,
|
|
64
|
-
stdio: 'inherit',
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (result.error) {
|
|
68
|
-
throw new ClawtError(`启动 Claude Code 失败: ${result.error.message}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (result.status !== null && result.status !== 0) {
|
|
72
|
-
printWarning(`Claude Code 退出码: ${result.status}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
41
|
/** executeClaudeTask 的返回结构,包含子进程引用和结果 Promise */
|
|
77
42
|
interface ClaudeTaskHandle {
|
|
78
43
|
/** 子进程实例,用于在中断时终止 */
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { registerListCommand } from './commands/list.js';
|
|
|
8
8
|
import { registerCreateCommand } from './commands/create.js';
|
|
9
9
|
import { registerRemoveCommand } from './commands/remove.js';
|
|
10
10
|
import { registerRunCommand } from './commands/run.js';
|
|
11
|
+
import { registerResumeCommand } from './commands/resume.js';
|
|
11
12
|
import { registerValidateCommand } from './commands/validate.js';
|
|
12
13
|
import { registerMergeCommand } from './commands/merge.js';
|
|
13
14
|
import { registerConfigCommand } from './commands/config.js';
|
|
@@ -31,6 +32,7 @@ registerListCommand(program);
|
|
|
31
32
|
registerCreateCommand(program);
|
|
32
33
|
registerRemoveCommand(program);
|
|
33
34
|
registerRunCommand(program);
|
|
35
|
+
registerResumeCommand(program);
|
|
34
36
|
registerValidateCommand(program);
|
|
35
37
|
registerMergeCommand(program);
|
|
36
38
|
registerConfigCommand(program);
|
package/src/types/command.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ClawtConfig, ConfigItemDefinition, ConfigDefinitions } from './config.js';
|
|
2
|
-
export type { CreateOptions, RunOptions, ValidateOptions, MergeOptions, RemoveOptions } from './command.js';
|
|
2
|
+
export type { CreateOptions, RunOptions, ValidateOptions, MergeOptions, RemoveOptions, ResumeOptions } from './command.js';
|
|
3
3
|
export type { WorktreeInfo, WorktreeStatus } from './worktree.js';
|
|
4
4
|
export type { ClaudeCodeResult } from './claudeCode.js';
|
|
5
5
|
export type { TaskResult, TaskSummary } from './taskResult.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { ClawtError } from '../errors/index.js';
|
|
3
|
+
import { APPEND_SYSTEM_PROMPT } from '../constants/index.js';
|
|
4
|
+
import { getConfigValue } from './config.js';
|
|
5
|
+
import { printInfo, printWarning } from './formatter.js';
|
|
6
|
+
import type { WorktreeInfo } from '../types/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 在指定 worktree 中启动 Claude Code CLI 交互式界面
|
|
10
|
+
* 使用 spawnSync + inherit stdio,让用户直接与 Claude Code 交互
|
|
11
|
+
* @param {WorktreeInfo} worktree - worktree 信息
|
|
12
|
+
*/
|
|
13
|
+
export function launchInteractiveClaude(worktree: WorktreeInfo): void {
|
|
14
|
+
const commandStr = getConfigValue('claudeCodeCommand');
|
|
15
|
+
const parts = commandStr.split(/\s+/).filter(Boolean);
|
|
16
|
+
const cmd = parts[0];
|
|
17
|
+
const args = [
|
|
18
|
+
...parts.slice(1),
|
|
19
|
+
'--append-system-prompt',
|
|
20
|
+
APPEND_SYSTEM_PROMPT,
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
printInfo(`正在 worktree 中启动 Claude Code 交互式界面...`);
|
|
24
|
+
printInfo(` 分支: ${worktree.branch}`);
|
|
25
|
+
printInfo(` 路径: ${worktree.path}`);
|
|
26
|
+
printInfo(` 指令: ${commandStr}`);
|
|
27
|
+
printInfo('');
|
|
28
|
+
|
|
29
|
+
const result = spawnSync(cmd, args, {
|
|
30
|
+
cwd: worktree.path,
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (result.error) {
|
|
35
|
+
throw new ClawtError(`启动 Claude Code 失败: ${result.error.message}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (result.status !== null && result.status !== 0) {
|
|
39
|
+
printWarning(`Claude Code 退出码: ${result.status}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -35,3 +35,4 @@ export { loadConfig, getConfigValue, ensureClawtDirs } from './config.js';
|
|
|
35
35
|
export { printSuccess, printError, printWarning, printInfo, printSeparator, printDoubleSeparator, confirmAction, formatWorktreeStatus } from './formatter.js';
|
|
36
36
|
export { ensureDir, removeEmptyDir } from './fs.js';
|
|
37
37
|
export { multilineInput } from './prompt.js';
|
|
38
|
+
export { launchInteractiveClaude } from './claude.js';
|