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.
@@ -4,16 +4,18 @@
4
4
 
5
5
  ### docs/spec.md
6
6
  - 完整的软件规格说明,包含 7 大章节
7
- - 命令流程在 `5. 需求场景详细设计` 下,每个命令一个子章节(5.1-5.10
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
- ## 命令清单(7 个)
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
- 七个命令:`create`、`run`、`list`、`remove`、`validate`、`merge`、`config`。
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 启动指令)、`autoPullPush`(merge 后是否自动 pull/push)三个配置项。配置项以 `CONFIG_DEFINITIONS` 为单一数据源,`DEFAULT_CONFIG` 和 `CONFIG_DESCRIPTIONS` 均从中派生
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(仅 `clawt run` 需要)
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` 时在 worktree 中打开交互式界面 |
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 (const key of keys) {
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` 时在 worktree 中打开交互式界面 |
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)(仅 `clawt run` 时检测) |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawt",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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 (const key of keys) {
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)}\n`);
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
+ }
@@ -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, APPEND_SYSTEM_PROMPT } from '../constants/index.js';
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);
@@ -37,3 +37,9 @@ export interface RemoveOptions {
37
37
  /** 指定索引 */
38
38
  index?: number;
39
39
  }
40
+
41
+ /** resume 命令选项 */
42
+ export interface ResumeOptions {
43
+ /** 要恢复的分支名 */
44
+ branch: string;
45
+ }
@@ -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
+ }
@@ -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';