clawt 3.1.1 → 3.1.3
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 +5 -4
- package/dist/index.js +139 -114
- package/dist/postinstall.js +35 -1
- package/docs/config-file.md +2 -0
- package/docs/config.md +2 -1
- package/docs/init.md +16 -7
- package/docs/project-config.md +132 -0
- package/docs/resume.md +4 -2
- package/docs/spec.md +31 -22
- package/docs/validate.md +4 -3
- package/package.json +1 -1
- package/src/commands/config.ts +14 -28
- package/src/commands/init.ts +23 -12
- package/src/commands/resume.ts +12 -3
- package/src/commands/validate.ts +17 -3
- package/src/constants/config.ts +4 -0
- package/src/constants/index.ts +1 -0
- package/src/constants/messages/init.ts +4 -0
- package/src/constants/project-config.ts +46 -0
- package/src/constants/terminal.ts +1 -0
- package/src/types/config.ts +2 -0
- package/src/types/index.ts +1 -1
- package/src/types/projectConfig.ts +17 -0
- package/src/utils/config-strategy.ts +68 -20
- package/src/utils/index.ts +2 -2
- package/src/utils/project-config.ts +9 -0
- package/tests/unit/commands/config.test.ts +1 -0
- package/tests/unit/commands/init.test.ts +41 -6
- package/tests/unit/commands/resume.test.ts +113 -0
- package/tests/unit/commands/validate.test.ts +63 -0
- package/tests/unit/constants/config.test.ts +1 -0
- package/tests/unit/utils/config-strategy.test.ts +77 -1
- package/tests/unit/utils/project-config.test.ts +32 -0
package/README.md
CHANGED
|
@@ -43,10 +43,10 @@ clawt merge -b branch-1 -m "feat: 实现xxx功能"
|
|
|
43
43
|
```bash
|
|
44
44
|
clawt init # 以当前分支作为主工作分支进行初始化
|
|
45
45
|
clawt init -b <branch> # 指定主工作分支名
|
|
46
|
-
clawt init show #
|
|
46
|
+
clawt init show # 交互式查看和修改项目配置
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
设置项目的主工作分支。重复执行会更新主工作分支配置。`init show` 提供交互式面板,可查看和修改项目配置项(如 validate 成功后自动执行的命令)。
|
|
50
50
|
|
|
51
51
|
### `clawt run` — 创建 worktree 并执行任务
|
|
52
52
|
|
|
@@ -113,7 +113,7 @@ clawt resume -b <branch> # 指定分支
|
|
|
113
113
|
clawt resume # 交互式多选(按创建日期分组)
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
不传 `-b` 时,分支列表按创建日期分组显示,支持全局全选和按组全选。选 1
|
|
116
|
+
不传 `-b` 时,分支列表按创建日期分组显示,支持全局全选和按组全选。选 1 个默认在新终端 Tab 中恢复(设置 `resumeInPlace: true` 可改为在当前终端就地恢复),选多个自动在独立终端 Tab 中批量恢复(仅 macOS)。
|
|
117
117
|
|
|
118
118
|
如果目标 worktree 存在历史会话,会自动继续上次对话(`--continue`)。
|
|
119
119
|
|
|
@@ -140,7 +140,7 @@ clawt validate -b <branch> -r "pnpm test & pnpm build" # 并行执行多个命
|
|
|
140
140
|
|
|
141
141
|
当 patch apply 失败(目标分支与主分支差异过大)时,会自动询问是否执行 `sync` 同步主分支到目标 worktree,无需手动操作。
|
|
142
142
|
|
|
143
|
-
`-r, --run` 选项可在 validate 成功后自动在主 worktree 中执行指定命令(如测试、构建等),命令执行失败不影响 validate
|
|
143
|
+
`-r, --run` 选项可在 validate 成功后自动在主 worktree 中执行指定命令(如测试、构建等),命令执行失败不影响 validate 结果。不传 `-r` 时会自动从项目配置的 `validateRunCommand` 读取(可通过 `clawt init show` 设置)。支持用 `&` 分隔多个命令并行执行:
|
|
144
144
|
|
|
145
145
|
| 用法 | 行为 |
|
|
146
146
|
| ---- | ---- |
|
|
@@ -290,6 +290,7 @@ clawt alias remove l
|
|
|
290
290
|
| `confirmDestructiveOps` | `true` | 破坏性操作前确认 |
|
|
291
291
|
| `maxConcurrency` | `0` | run 命令最大并发数,`0` 为不限制 |
|
|
292
292
|
| `terminalApp` | `"auto"` | 批量 resume 使用的终端:`auto` / `iterm2` / `terminal` |
|
|
293
|
+
| `resumeInPlace` | `false` | resume 单选时在当前终端就地恢复,`false` 则在新 Tab 中打开 |
|
|
293
294
|
| `aliases` | `{}` | 命令别名映射(如 `{"l": "list", "r": "run"}`) |
|
|
294
295
|
| `autoUpdate` | `true` | 自动检查新版本(每 24 小时检查一次 npm registry) |
|
|
295
296
|
|
package/dist/index.js
CHANGED
|
@@ -460,7 +460,11 @@ var INIT_MESSAGES = {
|
|
|
460
460
|
/** 项目未初始化(requireProjectConfig 使用) */
|
|
461
461
|
PROJECT_NOT_INITIALIZED: "\u9879\u76EE\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
|
|
462
462
|
/** 项目配置缺少 clawtMainWorkBranch 字段 */
|
|
463
|
-
PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F"
|
|
463
|
+
PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
|
|
464
|
+
/** init show 交互式面板选择配置项提示 */
|
|
465
|
+
INIT_SELECT_PROMPT: "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u9879\u76EE\u914D\u7F6E\u9879",
|
|
466
|
+
/** init show 交互式面板配置项修改成功 */
|
|
467
|
+
INIT_SET_SUCCESS: (key, value) => `\u2713 \u9879\u76EE\u914D\u7F6E ${key} \u5DF2\u8BBE\u7F6E\u4E3A ${value}`
|
|
464
468
|
};
|
|
465
469
|
|
|
466
470
|
// src/constants/messages/interactive-panel.ts
|
|
@@ -580,6 +584,10 @@ var CONFIG_DEFINITIONS = {
|
|
|
580
584
|
description: "\u6279\u91CF resume \u4F7F\u7528\u7684\u7EC8\u7AEF\u5E94\u7528\uFF1Aauto\uFF08\u81EA\u52A8\u68C0\u6D4B\uFF09\u3001iterm2\u3001terminal\uFF08macOS\uFF09",
|
|
581
585
|
allowedValues: VALID_TERMINAL_APPS
|
|
582
586
|
},
|
|
587
|
+
resumeInPlace: {
|
|
588
|
+
defaultValue: false,
|
|
589
|
+
description: "resume \u5355\u9009\u65F6\u662F\u5426\u5728\u5F53\u524D\u7EC8\u7AEF\u5C31\u5730\u6253\u5F00\uFF0Cfalse \u5219\u901A\u8FC7 terminalApp \u5728\u65B0 Tab \u4E2D\u6253\u5F00"
|
|
590
|
+
},
|
|
583
591
|
aliases: {
|
|
584
592
|
defaultValue: {},
|
|
585
593
|
description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
|
|
@@ -604,6 +612,32 @@ function deriveConfigDescriptions(definitions) {
|
|
|
604
612
|
var DEFAULT_CONFIG = deriveDefaultConfig(CONFIG_DEFINITIONS);
|
|
605
613
|
var CONFIG_DESCRIPTIONS = deriveConfigDescriptions(CONFIG_DEFINITIONS);
|
|
606
614
|
|
|
615
|
+
// src/constants/project-config.ts
|
|
616
|
+
var PROJECT_CONFIG_DEFINITIONS = {
|
|
617
|
+
clawtMainWorkBranch: {
|
|
618
|
+
defaultValue: "",
|
|
619
|
+
description: "\u4E3B worktree \u7684\u5DE5\u4F5C\u5206\u652F\u540D"
|
|
620
|
+
},
|
|
621
|
+
validateRunCommand: {
|
|
622
|
+
defaultValue: void 0,
|
|
623
|
+
description: "validate \u6210\u529F\u540E\u81EA\u52A8\u6267\u884C\u7684\u547D\u4EE4\uFF08-r \u7684\u9ED8\u8BA4\u503C\uFF09"
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
function deriveDefaultConfig2(definitions) {
|
|
627
|
+
const entries = Object.entries(definitions).map(
|
|
628
|
+
([key, def]) => [key, def.defaultValue]
|
|
629
|
+
);
|
|
630
|
+
return Object.fromEntries(entries);
|
|
631
|
+
}
|
|
632
|
+
function deriveConfigDescriptions2(definitions) {
|
|
633
|
+
const entries = Object.entries(definitions).map(
|
|
634
|
+
([key, def]) => [key, def.description]
|
|
635
|
+
);
|
|
636
|
+
return Object.fromEntries(entries);
|
|
637
|
+
}
|
|
638
|
+
var PROJECT_DEFAULT_CONFIG = deriveDefaultConfig2(PROJECT_CONFIG_DEFINITIONS);
|
|
639
|
+
var PROJECT_CONFIG_DESCRIPTIONS = deriveConfigDescriptions2(PROJECT_CONFIG_DEFINITIONS);
|
|
640
|
+
|
|
607
641
|
// src/constants/git.ts
|
|
608
642
|
var AUTO_SAVE_COMMIT_MESSAGE = "chore: auto-save before sync";
|
|
609
643
|
|
|
@@ -1255,6 +1289,10 @@ function getMainWorkBranch() {
|
|
|
1255
1289
|
const config2 = requireProjectConfig();
|
|
1256
1290
|
return config2.clawtMainWorkBranch;
|
|
1257
1291
|
}
|
|
1292
|
+
function getValidateRunCommand() {
|
|
1293
|
+
const config2 = loadProjectConfig();
|
|
1294
|
+
return config2?.validateRunCommand || void 0;
|
|
1295
|
+
}
|
|
1258
1296
|
|
|
1259
1297
|
// src/utils/validate-branch.ts
|
|
1260
1298
|
function getValidateBranchName(branchName) {
|
|
@@ -2713,7 +2751,7 @@ function parseConfigValue(key, rawValue) {
|
|
|
2713
2751
|
}
|
|
2714
2752
|
return { success: true, value: rawValue };
|
|
2715
2753
|
}
|
|
2716
|
-
async function promptConfigValue(key, currentValue) {
|
|
2754
|
+
async function promptConfigValue(key, currentValue, allowedValues) {
|
|
2717
2755
|
const expectedType = typeof currentValue;
|
|
2718
2756
|
if (expectedType === "boolean") {
|
|
2719
2757
|
return promptBooleanValue(key, currentValue);
|
|
@@ -2721,18 +2759,43 @@ async function promptConfigValue(key, currentValue) {
|
|
|
2721
2759
|
if (expectedType === "number") {
|
|
2722
2760
|
return promptNumberValue(key, currentValue);
|
|
2723
2761
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
return promptEnumValue(key, currentValue, definition.allowedValues);
|
|
2762
|
+
if (allowedValues) {
|
|
2763
|
+
return promptEnumValue(key, currentValue, allowedValues);
|
|
2727
2764
|
}
|
|
2728
2765
|
return promptStringValue(key, currentValue);
|
|
2729
2766
|
}
|
|
2730
2767
|
function formatConfigValue(value) {
|
|
2768
|
+
if (value === void 0 || value === null) {
|
|
2769
|
+
return chalk7.dim("(\u672A\u8BBE\u7F6E)");
|
|
2770
|
+
}
|
|
2731
2771
|
if (typeof value === "boolean") {
|
|
2732
2772
|
return value ? chalk7.green("true") : chalk7.yellow("false");
|
|
2733
2773
|
}
|
|
2734
2774
|
return chalk7.cyan(String(value));
|
|
2735
2775
|
}
|
|
2776
|
+
async function interactiveConfigEditor(config2, definitions, options) {
|
|
2777
|
+
const keys = Object.keys(definitions);
|
|
2778
|
+
const disabledKeys = options?.disabledKeys ?? {};
|
|
2779
|
+
const configRecord = config2;
|
|
2780
|
+
const choices = keys.map((k) => {
|
|
2781
|
+
const isDisabled = k in disabledKeys;
|
|
2782
|
+
const value = configRecord[k];
|
|
2783
|
+
const isObject = typeof value === "object" && value !== null;
|
|
2784
|
+
return {
|
|
2785
|
+
name: k,
|
|
2786
|
+
message: `${k}: ${isObject || isDisabled ? chalk7.dim(isObject ? JSON.stringify(value) : String(value ?? "")) : formatConfigValue(value)} ${chalk7.dim(`\u2014 ${definitions[k].description}`)}`,
|
|
2787
|
+
...isDisabled && { disabled: disabledKeys[k] }
|
|
2788
|
+
};
|
|
2789
|
+
});
|
|
2790
|
+
const selectedKey = await new Enquirer4.Select({
|
|
2791
|
+
message: options?.selectPrompt ?? MESSAGES.CONFIG_SELECT_PROMPT,
|
|
2792
|
+
choices
|
|
2793
|
+
}).run();
|
|
2794
|
+
const currentValue = configRecord[selectedKey];
|
|
2795
|
+
const definition = definitions[selectedKey];
|
|
2796
|
+
const newValue = await promptConfigValue(selectedKey, currentValue, definition.allowedValues);
|
|
2797
|
+
return { key: selectedKey, newValue };
|
|
2798
|
+
}
|
|
2736
2799
|
async function promptBooleanValue(key, currentValue) {
|
|
2737
2800
|
const choices = [
|
|
2738
2801
|
{ name: "true", message: "true" },
|
|
@@ -2770,7 +2833,7 @@ async function promptEnumValue(key, currentValue, allowedValues) {
|
|
|
2770
2833
|
async function promptStringValue(key, currentValue) {
|
|
2771
2834
|
return await new Enquirer4.Input({
|
|
2772
2835
|
message: MESSAGES.CONFIG_INPUT_PROMPT(key),
|
|
2773
|
-
initial: currentValue
|
|
2836
|
+
initial: currentValue || ""
|
|
2774
2837
|
}).run();
|
|
2775
2838
|
}
|
|
2776
2839
|
|
|
@@ -2905,56 +2968,6 @@ async function checkForUpdates(currentVersion) {
|
|
|
2905
2968
|
}
|
|
2906
2969
|
}
|
|
2907
2970
|
|
|
2908
|
-
// src/utils/json.ts
|
|
2909
|
-
function primitiveToString(value) {
|
|
2910
|
-
if (value === void 0) {
|
|
2911
|
-
return "undefined";
|
|
2912
|
-
}
|
|
2913
|
-
if (value === null) {
|
|
2914
|
-
return "null";
|
|
2915
|
-
}
|
|
2916
|
-
if (typeof value === "symbol") {
|
|
2917
|
-
return value.toString();
|
|
2918
|
-
}
|
|
2919
|
-
if (typeof value === "function") {
|
|
2920
|
-
return `[Function: ${value.name || "anonymous"}]`;
|
|
2921
|
-
}
|
|
2922
|
-
return String(value);
|
|
2923
|
-
}
|
|
2924
|
-
function safeStringify(value, indent = 2) {
|
|
2925
|
-
if (value === null || typeof value !== "object") {
|
|
2926
|
-
return primitiveToString(value);
|
|
2927
|
-
}
|
|
2928
|
-
try {
|
|
2929
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
2930
|
-
return JSON.stringify(
|
|
2931
|
-
value,
|
|
2932
|
-
(_key, val) => {
|
|
2933
|
-
if (typeof val === "bigint") {
|
|
2934
|
-
return val.toString();
|
|
2935
|
-
}
|
|
2936
|
-
if (typeof val === "undefined" || typeof val === "function" || typeof val === "symbol") {
|
|
2937
|
-
return primitiveToString(val);
|
|
2938
|
-
}
|
|
2939
|
-
if (typeof val === "object" && val !== null) {
|
|
2940
|
-
if (seen.has(val)) {
|
|
2941
|
-
return "[Circular]";
|
|
2942
|
-
}
|
|
2943
|
-
seen.add(val);
|
|
2944
|
-
}
|
|
2945
|
-
return val;
|
|
2946
|
-
},
|
|
2947
|
-
indent
|
|
2948
|
-
);
|
|
2949
|
-
} catch {
|
|
2950
|
-
try {
|
|
2951
|
-
return JSON.stringify(String(value), null, indent);
|
|
2952
|
-
} catch {
|
|
2953
|
-
return "[Unserializable]";
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
2971
|
// src/utils/interactive-panel.ts
|
|
2959
2972
|
import { createInterface as createInterface2 } from "readline";
|
|
2960
2973
|
|
|
@@ -3805,7 +3818,13 @@ async function handleResume(options) {
|
|
|
3805
3818
|
return;
|
|
3806
3819
|
}
|
|
3807
3820
|
if (targetWorktrees.length === 1) {
|
|
3808
|
-
|
|
3821
|
+
const inPlace = getConfigValue("resumeInPlace");
|
|
3822
|
+
if (inPlace) {
|
|
3823
|
+
launchInteractiveClaude(targetWorktrees[0], { autoContinue: true });
|
|
3824
|
+
} else {
|
|
3825
|
+
const hasPreviousSession = hasClaudeSessionHistory(targetWorktrees[0].path);
|
|
3826
|
+
launchInteractiveClaudeInNewTerminal(targetWorktrees[0], hasPreviousSession);
|
|
3827
|
+
}
|
|
3809
3828
|
} else {
|
|
3810
3829
|
await handleBatchResume(targetWorktrees);
|
|
3811
3830
|
}
|
|
@@ -4106,6 +4125,12 @@ async function executeRunCommand(command, mainWorktreePath) {
|
|
|
4106
4125
|
await executeParallelCommands(commands, mainWorktreePath);
|
|
4107
4126
|
}
|
|
4108
4127
|
}
|
|
4128
|
+
function resolveRunCommand(optionRun) {
|
|
4129
|
+
if (optionRun) {
|
|
4130
|
+
return optionRun;
|
|
4131
|
+
}
|
|
4132
|
+
return getValidateRunCommand();
|
|
4133
|
+
}
|
|
4109
4134
|
async function handleValidate(options) {
|
|
4110
4135
|
if (options.clean) {
|
|
4111
4136
|
await handleValidateClean(options);
|
|
@@ -4138,8 +4163,9 @@ async function handleValidate(options) {
|
|
|
4138
4163
|
}
|
|
4139
4164
|
await handleFirstValidate(targetWorktreePath, mainWorktreePath, projectName, branchName, hasUncommitted);
|
|
4140
4165
|
}
|
|
4141
|
-
|
|
4142
|
-
|
|
4166
|
+
const runCommand = resolveRunCommand(options.run);
|
|
4167
|
+
if (runCommand) {
|
|
4168
|
+
await executeRunCommand(runCommand, mainWorktreePath);
|
|
4143
4169
|
}
|
|
4144
4170
|
}
|
|
4145
4171
|
|
|
@@ -4264,8 +4290,6 @@ async function handleMerge(options) {
|
|
|
4264
4290
|
}
|
|
4265
4291
|
|
|
4266
4292
|
// src/commands/config.ts
|
|
4267
|
-
import chalk11 from "chalk";
|
|
4268
|
-
import Enquirer5 from "enquirer";
|
|
4269
4293
|
function registerConfigCommand(program2) {
|
|
4270
4294
|
const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
|
|
4271
4295
|
await handleConfigSet();
|
|
@@ -4319,25 +4343,19 @@ async function handleConfigSet(key, value) {
|
|
|
4319
4343
|
}
|
|
4320
4344
|
async function handleInteractiveConfigSet() {
|
|
4321
4345
|
const config2 = loadConfig();
|
|
4322
|
-
const keys = Object.keys(DEFAULT_CONFIG);
|
|
4323
4346
|
logger.info("config set \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u914D\u7F6E");
|
|
4324
|
-
const
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4347
|
+
const disabledKeys = {};
|
|
4348
|
+
for (const k of Object.keys(DEFAULT_CONFIG)) {
|
|
4349
|
+
if (typeof DEFAULT_CONFIG[k] === "object") {
|
|
4350
|
+
disabledKeys[k] = CONFIG_ALIAS_DISABLED_HINT;
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
const { key, newValue } = await interactiveConfigEditor(config2, CONFIG_DEFINITIONS, {
|
|
4354
|
+
disabledKeys
|
|
4331
4355
|
});
|
|
4332
|
-
|
|
4333
|
-
message: MESSAGES.CONFIG_SELECT_PROMPT,
|
|
4334
|
-
choices
|
|
4335
|
-
}).run();
|
|
4336
|
-
const currentValue = config2[selectedKey];
|
|
4337
|
-
const newValue = await promptConfigValue(selectedKey, currentValue);
|
|
4338
|
-
config2[selectedKey] = newValue;
|
|
4356
|
+
config2[key] = newValue;
|
|
4339
4357
|
saveConfig(config2);
|
|
4340
|
-
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(
|
|
4358
|
+
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(key, String(newValue)));
|
|
4341
4359
|
}
|
|
4342
4360
|
function handleConfigGet(key) {
|
|
4343
4361
|
if (!isValidConfigKey(key)) {
|
|
@@ -4381,7 +4399,7 @@ async function handleReset() {
|
|
|
4381
4399
|
}
|
|
4382
4400
|
|
|
4383
4401
|
// src/commands/status.ts
|
|
4384
|
-
import
|
|
4402
|
+
import chalk11 from "chalk";
|
|
4385
4403
|
function registerStatusCommand(program2) {
|
|
4386
4404
|
program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9762\u677F\u6A21\u5F0F").action(async (options) => {
|
|
4387
4405
|
await handleStatus(options);
|
|
@@ -4499,7 +4517,7 @@ function printStatusAsJson(result) {
|
|
|
4499
4517
|
}
|
|
4500
4518
|
function printStatusAsText(result) {
|
|
4501
4519
|
printDoubleSeparator();
|
|
4502
|
-
printInfo(` ${
|
|
4520
|
+
printInfo(` ${chalk11.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
|
|
4503
4521
|
printDoubleSeparator();
|
|
4504
4522
|
printInfo("");
|
|
4505
4523
|
printMainSection(result.main);
|
|
@@ -4512,17 +4530,17 @@ function printStatusAsText(result) {
|
|
|
4512
4530
|
printDoubleSeparator();
|
|
4513
4531
|
}
|
|
4514
4532
|
function printMainSection(main2) {
|
|
4515
|
-
printInfo(` ${
|
|
4516
|
-
printInfo(` \u5206\u652F: ${
|
|
4533
|
+
printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
|
|
4534
|
+
printInfo(` \u5206\u652F: ${chalk11.bold(main2.branch)}`);
|
|
4517
4535
|
if (main2.isClean) {
|
|
4518
|
-
printInfo(` \u72B6\u6001: ${
|
|
4536
|
+
printInfo(` \u72B6\u6001: ${chalk11.green("\u2713 \u5E72\u51C0")}`);
|
|
4519
4537
|
} else {
|
|
4520
|
-
printInfo(` \u72B6\u6001: ${
|
|
4538
|
+
printInfo(` \u72B6\u6001: ${chalk11.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
|
|
4521
4539
|
}
|
|
4522
4540
|
printInfo("");
|
|
4523
4541
|
}
|
|
4524
4542
|
function printWorktreesSection(worktrees, total) {
|
|
4525
|
-
printInfo(` ${
|
|
4543
|
+
printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
|
|
4526
4544
|
printInfo("");
|
|
4527
4545
|
if (worktrees.length === 0) {
|
|
4528
4546
|
printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
|
|
@@ -4534,56 +4552,56 @@ function printWorktreesSection(worktrees, total) {
|
|
|
4534
4552
|
}
|
|
4535
4553
|
function printWorktreeItem(wt) {
|
|
4536
4554
|
const statusLabel = formatChangeStatusLabel2(wt.changeStatus);
|
|
4537
|
-
printInfo(` ${
|
|
4555
|
+
printInfo(` ${chalk11.bold("\u25CF")} ${chalk11.bold(wt.branch)} [${statusLabel}]`);
|
|
4538
4556
|
if (wt.insertions > 0 || wt.deletions > 0) {
|
|
4539
|
-
printInfo(` ${
|
|
4557
|
+
printInfo(` ${chalk11.green(`+${wt.insertions}`)} ${chalk11.red(`-${wt.deletions}`)}`);
|
|
4540
4558
|
}
|
|
4541
4559
|
if (wt.commitsAhead > 0) {
|
|
4542
|
-
printInfo(` ${
|
|
4560
|
+
printInfo(` ${chalk11.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
|
|
4543
4561
|
}
|
|
4544
4562
|
if (wt.commitsBehind > 0) {
|
|
4545
|
-
printInfo(` ${
|
|
4563
|
+
printInfo(` ${chalk11.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
|
|
4546
4564
|
} else {
|
|
4547
|
-
printInfo(` ${
|
|
4565
|
+
printInfo(` ${chalk11.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
|
|
4548
4566
|
}
|
|
4549
4567
|
if (wt.createdAt) {
|
|
4550
4568
|
const relativeTime = formatRelativeTime(wt.createdAt);
|
|
4551
4569
|
if (relativeTime) {
|
|
4552
|
-
printInfo(` ${
|
|
4570
|
+
printInfo(` ${chalk11.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
|
|
4553
4571
|
}
|
|
4554
4572
|
}
|
|
4555
4573
|
if (wt.snapshotTime) {
|
|
4556
4574
|
const relativeTime = formatRelativeTime(wt.snapshotTime);
|
|
4557
4575
|
if (relativeTime) {
|
|
4558
|
-
printInfo(` ${
|
|
4576
|
+
printInfo(` ${chalk11.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
|
|
4559
4577
|
}
|
|
4560
4578
|
} else {
|
|
4561
|
-
printInfo(` ${
|
|
4579
|
+
printInfo(` ${chalk11.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
|
|
4562
4580
|
}
|
|
4563
4581
|
printInfo("");
|
|
4564
4582
|
}
|
|
4565
4583
|
function formatChangeStatusLabel2(status) {
|
|
4566
4584
|
switch (status) {
|
|
4567
4585
|
case "committed":
|
|
4568
|
-
return
|
|
4586
|
+
return chalk11.green(MESSAGES.STATUS_CHANGE_COMMITTED);
|
|
4569
4587
|
case "uncommitted":
|
|
4570
|
-
return
|
|
4588
|
+
return chalk11.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
|
|
4571
4589
|
case "conflict":
|
|
4572
|
-
return
|
|
4590
|
+
return chalk11.red(MESSAGES.STATUS_CHANGE_CONFLICT);
|
|
4573
4591
|
case "clean":
|
|
4574
|
-
return
|
|
4592
|
+
return chalk11.gray(MESSAGES.STATUS_CHANGE_CLEAN);
|
|
4575
4593
|
}
|
|
4576
4594
|
}
|
|
4577
4595
|
function printSnapshotsSection(snapshots) {
|
|
4578
|
-
printInfo(` ${
|
|
4596
|
+
printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
|
|
4579
4597
|
if (snapshots.orphaned > 0) {
|
|
4580
|
-
printInfo(` ${
|
|
4598
|
+
printInfo(` ${chalk11.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
|
|
4581
4599
|
}
|
|
4582
4600
|
printInfo("");
|
|
4583
4601
|
}
|
|
4584
4602
|
|
|
4585
4603
|
// src/commands/alias.ts
|
|
4586
|
-
import
|
|
4604
|
+
import chalk12 from "chalk";
|
|
4587
4605
|
function getRegisteredCommandNames(program2) {
|
|
4588
4606
|
return program2.commands.map((cmd) => cmd.name());
|
|
4589
4607
|
}
|
|
@@ -4604,7 +4622,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
|
|
|
4604
4622
|
`);
|
|
4605
4623
|
printSeparator();
|
|
4606
4624
|
for (const [alias, command] of entries) {
|
|
4607
|
-
printInfo(` ${
|
|
4625
|
+
printInfo(` ${chalk12.bold(alias)} \u2192 ${chalk12.cyan(command)}`);
|
|
4608
4626
|
}
|
|
4609
4627
|
printInfo("");
|
|
4610
4628
|
printSeparator();
|
|
@@ -4653,7 +4671,7 @@ function registerAliasCommand(program2) {
|
|
|
4653
4671
|
// src/commands/projects.ts
|
|
4654
4672
|
import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
4655
4673
|
import { join as join8 } from "path";
|
|
4656
|
-
import
|
|
4674
|
+
import chalk13 from "chalk";
|
|
4657
4675
|
function registerProjectsCommand(program2) {
|
|
4658
4676
|
program2.command("projects [name]").description("\u5C55\u793A\u6240\u6709\u9879\u76EE\u7684 worktree \u6982\u89C8\uFF0C\u6216\u67E5\u770B\u6307\u5B9A\u9879\u76EE\u7684 worktree \u8BE6\u60C5").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((name, options) => {
|
|
4659
4677
|
handleProjects({ name, json: options.json });
|
|
@@ -4772,7 +4790,7 @@ function sortByLastActiveTimeDesc(projects) {
|
|
|
4772
4790
|
}
|
|
4773
4791
|
function printProjectsOverviewAsText(result) {
|
|
4774
4792
|
printDoubleSeparator();
|
|
4775
|
-
printInfo(` ${
|
|
4793
|
+
printInfo(` ${chalk13.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
|
|
4776
4794
|
printDoubleSeparator();
|
|
4777
4795
|
printInfo("");
|
|
4778
4796
|
if (result.projects.length === 0) {
|
|
@@ -4786,7 +4804,7 @@ function printProjectsOverviewAsText(result) {
|
|
|
4786
4804
|
}
|
|
4787
4805
|
printSeparator();
|
|
4788
4806
|
printInfo("");
|
|
4789
|
-
printInfo(` \u5171 ${
|
|
4807
|
+
printInfo(` \u5171 ${chalk13.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk13.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
|
|
4790
4808
|
printInfo("");
|
|
4791
4809
|
printDoubleSeparator();
|
|
4792
4810
|
}
|
|
@@ -4794,16 +4812,16 @@ function printProjectOverviewItem(project) {
|
|
|
4794
4812
|
const relativeTime = formatRelativeTime(project.lastActiveTime);
|
|
4795
4813
|
const activeLabel = relativeTime ? MESSAGES.PROJECTS_LAST_ACTIVE(relativeTime) : "";
|
|
4796
4814
|
const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(project.diskUsage));
|
|
4797
|
-
printInfo(` ${
|
|
4798
|
-
printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${
|
|
4815
|
+
printInfo(` ${chalk13.bold("\u25CF")} ${chalk13.bold(project.name)}`);
|
|
4816
|
+
printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk13.gray(activeLabel)} ${chalk13.gray(diskLabel)}`);
|
|
4799
4817
|
printInfo("");
|
|
4800
4818
|
}
|
|
4801
4819
|
function printProjectDetailAsText(result) {
|
|
4802
4820
|
printDoubleSeparator();
|
|
4803
|
-
printInfo(` ${
|
|
4821
|
+
printInfo(` ${chalk13.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
|
|
4804
4822
|
printDoubleSeparator();
|
|
4805
4823
|
printInfo("");
|
|
4806
|
-
printInfo(` ${
|
|
4824
|
+
printInfo(` ${chalk13.bold("\u25C6")} ${chalk13.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
|
|
4807
4825
|
printInfo(` ${MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage))}`);
|
|
4808
4826
|
printInfo("");
|
|
4809
4827
|
printSeparator();
|
|
@@ -4823,9 +4841,9 @@ function printWorktreeDetailItem(wt) {
|
|
|
4823
4841
|
const relativeTime = formatRelativeTime(wt.lastModifiedTime);
|
|
4824
4842
|
const modifiedLabel = relativeTime ? MESSAGES.PROJECTS_LAST_MODIFIED(relativeTime) : "";
|
|
4825
4843
|
const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(wt.diskUsage));
|
|
4826
|
-
printInfo(` ${
|
|
4844
|
+
printInfo(` ${chalk13.bold("\u25CF")} ${chalk13.bold(wt.branch)}`);
|
|
4827
4845
|
printInfo(` ${wt.path}`);
|
|
4828
|
-
printInfo(` ${
|
|
4846
|
+
printInfo(` ${chalk13.gray(modifiedLabel)} ${chalk13.gray(diskLabel)}`);
|
|
4829
4847
|
printInfo("");
|
|
4830
4848
|
}
|
|
4831
4849
|
|
|
@@ -5048,16 +5066,23 @@ function registerInitCommand(program2) {
|
|
|
5048
5066
|
await handleInit(options);
|
|
5049
5067
|
});
|
|
5050
5068
|
initCmd.addCommand(
|
|
5051
|
-
new Cmd("show").description("\
|
|
5052
|
-
handleInitShow();
|
|
5069
|
+
new Cmd("show").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u9879\u76EE\u914D\u7F6E").action(async () => {
|
|
5070
|
+
await handleInitShow();
|
|
5053
5071
|
})
|
|
5054
5072
|
);
|
|
5055
5073
|
}
|
|
5056
|
-
function handleInitShow() {
|
|
5074
|
+
async function handleInitShow() {
|
|
5057
5075
|
validateMainWorktree();
|
|
5058
5076
|
const config2 = requireProjectConfig();
|
|
5059
|
-
|
|
5060
|
-
|
|
5077
|
+
logger.info("init show \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u9879\u76EE\u914D\u7F6E");
|
|
5078
|
+
const { key, newValue } = await interactiveConfigEditor(
|
|
5079
|
+
config2,
|
|
5080
|
+
PROJECT_CONFIG_DEFINITIONS,
|
|
5081
|
+
{ selectPrompt: MESSAGES.INIT_SELECT_PROMPT }
|
|
5082
|
+
);
|
|
5083
|
+
const updatedConfig = { ...config2, [key]: newValue };
|
|
5084
|
+
saveProjectConfig(updatedConfig);
|
|
5085
|
+
printSuccess(MESSAGES.INIT_SET_SUCCESS(key, String(newValue)));
|
|
5061
5086
|
}
|
|
5062
5087
|
async function handleInit(options) {
|
|
5063
5088
|
validateMainWorktree();
|
package/dist/postinstall.js
CHANGED
|
@@ -437,7 +437,11 @@ var INIT_MESSAGES = {
|
|
|
437
437
|
/** 项目未初始化(requireProjectConfig 使用) */
|
|
438
438
|
PROJECT_NOT_INITIALIZED: "\u9879\u76EE\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
|
|
439
439
|
/** 项目配置缺少 clawtMainWorkBranch 字段 */
|
|
440
|
-
PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F"
|
|
440
|
+
PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
|
|
441
|
+
/** init show 交互式面板选择配置项提示 */
|
|
442
|
+
INIT_SELECT_PROMPT: "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u9879\u76EE\u914D\u7F6E\u9879",
|
|
443
|
+
/** init show 交互式面板配置项修改成功 */
|
|
444
|
+
INIT_SET_SUCCESS: (key, value) => `\u2713 \u9879\u76EE\u914D\u7F6E ${key} \u5DF2\u8BBE\u7F6E\u4E3A ${value}`
|
|
441
445
|
};
|
|
442
446
|
|
|
443
447
|
// src/constants/messages/interactive-panel.ts
|
|
@@ -525,6 +529,10 @@ var CONFIG_DEFINITIONS = {
|
|
|
525
529
|
description: "\u6279\u91CF resume \u4F7F\u7528\u7684\u7EC8\u7AEF\u5E94\u7528\uFF1Aauto\uFF08\u81EA\u52A8\u68C0\u6D4B\uFF09\u3001iterm2\u3001terminal\uFF08macOS\uFF09",
|
|
526
530
|
allowedValues: VALID_TERMINAL_APPS
|
|
527
531
|
},
|
|
532
|
+
resumeInPlace: {
|
|
533
|
+
defaultValue: false,
|
|
534
|
+
description: "resume \u5355\u9009\u65F6\u662F\u5426\u5728\u5F53\u524D\u7EC8\u7AEF\u5C31\u5730\u6253\u5F00\uFF0Cfalse \u5219\u901A\u8FC7 terminalApp \u5728\u65B0 Tab \u4E2D\u6253\u5F00"
|
|
535
|
+
},
|
|
528
536
|
aliases: {
|
|
529
537
|
defaultValue: {},
|
|
530
538
|
description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
|
|
@@ -549,6 +557,32 @@ function deriveConfigDescriptions(definitions) {
|
|
|
549
557
|
var DEFAULT_CONFIG = deriveDefaultConfig(CONFIG_DEFINITIONS);
|
|
550
558
|
var CONFIG_DESCRIPTIONS = deriveConfigDescriptions(CONFIG_DEFINITIONS);
|
|
551
559
|
|
|
560
|
+
// src/constants/project-config.ts
|
|
561
|
+
var PROJECT_CONFIG_DEFINITIONS = {
|
|
562
|
+
clawtMainWorkBranch: {
|
|
563
|
+
defaultValue: "",
|
|
564
|
+
description: "\u4E3B worktree \u7684\u5DE5\u4F5C\u5206\u652F\u540D"
|
|
565
|
+
},
|
|
566
|
+
validateRunCommand: {
|
|
567
|
+
defaultValue: void 0,
|
|
568
|
+
description: "validate \u6210\u529F\u540E\u81EA\u52A8\u6267\u884C\u7684\u547D\u4EE4\uFF08-r \u7684\u9ED8\u8BA4\u503C\uFF09"
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
function deriveDefaultConfig2(definitions) {
|
|
572
|
+
const entries = Object.entries(definitions).map(
|
|
573
|
+
([key, def]) => [key, def.defaultValue]
|
|
574
|
+
);
|
|
575
|
+
return Object.fromEntries(entries);
|
|
576
|
+
}
|
|
577
|
+
function deriveConfigDescriptions2(definitions) {
|
|
578
|
+
const entries = Object.entries(definitions).map(
|
|
579
|
+
([key, def]) => [key, def.description]
|
|
580
|
+
);
|
|
581
|
+
return Object.fromEntries(entries);
|
|
582
|
+
}
|
|
583
|
+
var PROJECT_DEFAULT_CONFIG = deriveDefaultConfig2(PROJECT_CONFIG_DEFINITIONS);
|
|
584
|
+
var PROJECT_CONFIG_DESCRIPTIONS = deriveConfigDescriptions2(PROJECT_CONFIG_DEFINITIONS);
|
|
585
|
+
|
|
552
586
|
// src/constants/update.ts
|
|
553
587
|
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
554
588
|
|
package/docs/config-file.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"confirmDestructiveOps": true,
|
|
23
23
|
"maxConcurrency": 0,
|
|
24
24
|
"terminalApp": "auto",
|
|
25
|
+
"resumeInPlace": false,
|
|
25
26
|
"aliases": {},
|
|
26
27
|
"autoUpdate": true
|
|
27
28
|
}
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
| `confirmDestructiveOps` | `boolean` | `true` | 执行破坏性操作(reset、validate --clean)前是否提示确认 |
|
|
38
39
|
| `maxConcurrency` | `number` | `0` | run 命令默认最大并发数,`0` 表示不限制 |
|
|
39
40
|
| `terminalApp` | `string` | `"auto"` | 批量 resume 使用的终端应用:`auto`(自动检测)、`iterm2`、`terminal`(macOS) |
|
|
41
|
+
| `resumeInPlace` | `boolean` | `false` | resume 单选时是否在当前终端就地打开,`false` 则通过 `terminalApp` 在新 Tab 中打开 |
|
|
40
42
|
| `aliases` | `Record<string, string>` | `{}` | 命令别名映射,键为别名,值为目标内置命令名 |
|
|
41
43
|
| `autoUpdate` | `boolean` | `true` | 是否启用自动更新检查(每 24 小时通过 npm registry 检查一次新版本) |
|
|
42
44
|
|
package/docs/config.md
CHANGED
|
@@ -85,7 +85,8 @@ clawt config reset
|
|
|
85
85
|
|
|
86
86
|
- 配置项类型定义:`ConfigItemDefinition` 新增可选字段 `allowedValues`(`readonly string[]`),仅对 string 类型有效,用于枚举值校验和交互式 Select 提示
|
|
87
87
|
- 值解析与提示策略:`src/utils/config-strategy.ts` 中的 `parseConfigValue()`(CLI 字符串解析)和 `promptConfigValue()`(交互式提示),基于类型和 `allowedValues` 自动分发
|
|
88
|
+
- 交互式配置编辑:`handleInteractiveConfigSet` 调用通用的 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),传入 `CONFIG_DEFINITIONS` 和 `disabledKeys`(对象类型配置项禁用映射),不再在 config 命令中直接构建选择列表和调用 `promptConfigValue`
|
|
88
89
|
- `saveConfig(config)`:`src/utils/config.ts` 中新增的通用配置写入函数,将完整配置对象持久化到文件
|
|
89
|
-
- `formatConfigValue(value)`:支持 boolean(绿色/黄色)和 string/number
|
|
90
|
+
- `formatConfigValue(value)`:支持 boolean(绿色/黄色)和 string/number(青色)的格式化显示。`undefined` / `null` 值显示为暗淡色的 `(未设置)`。对象类型配置项(如 `aliases`)在交互式列表中通过 `JSON.stringify` 以暗淡色显示
|
|
90
91
|
|
|
91
92
|
---
|
package/docs/init.md
CHANGED
|
@@ -18,11 +18,11 @@ clawt init show
|
|
|
18
18
|
| 参数/子命令 | 必填 | 说明 |
|
|
19
19
|
| --- | --- | --- |
|
|
20
20
|
| `-b` | 否 | 指定主工作分支名。不传则使用当前分支 |
|
|
21
|
-
| `show` | 否 |
|
|
21
|
+
| `show` | 否 | 交互式查看和修改项目配置 |
|
|
22
22
|
|
|
23
23
|
**功能说明:**
|
|
24
24
|
|
|
25
|
-
初始化项目级配置,将指定分支记录为该项目的主工作分支(`clawtMainWorkBranch`)。该配置用于 `create` / `run`
|
|
25
|
+
初始化项目级配置,将指定分支记录为该项目的主工作分支(`clawtMainWorkBranch`)。该配置用于 `create` / `run` 时检测当前分支是否为主工作分支,并在偏离时提醒用户。`init show` 子命令提供交互式面板,可查看和修改所有项目配置项(如 `validateRunCommand`)。项目级配置的完整说明见 [project-config.md](./project-config.md)。
|
|
26
26
|
|
|
27
27
|
**运行流程(设置模式):**
|
|
28
28
|
|
|
@@ -40,7 +40,12 @@ clawt init show
|
|
|
40
40
|
1. **主 worktree 校验** (2.1)
|
|
41
41
|
2. **读取项目级配置**:读取 `~/.clawt/projects/<projectName>/config.json`
|
|
42
42
|
- 配置不存在 → 抛出错误 `项目尚未初始化,请先执行 clawt init 设置主工作分支`
|
|
43
|
-
- 配置存在 →
|
|
43
|
+
- 配置存在 → 进入交互式面板
|
|
44
|
+
3. **交互式配置编辑**:调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),基于 `PROJECT_CONFIG_DEFINITIONS` 构建配置项列表(详见 [project-config.md](./project-config.md))
|
|
45
|
+
- 列出所有项目配置项,显示名称、当前值和描述
|
|
46
|
+
- 用户选择配置项后,根据值类型自动选择输入方式(与全局配置的交互式编辑逻辑一致)
|
|
47
|
+
4. **持久化修改**:将修改后的值合并到当前配置并写入配置文件
|
|
48
|
+
5. **输出成功提示**:`✓ 项目配置 <key> 已设置为 <value>`
|
|
44
49
|
|
|
45
50
|
**输出格式:**
|
|
46
51
|
|
|
@@ -51,10 +56,8 @@ clawt init show
|
|
|
51
56
|
# 更新已有配置
|
|
52
57
|
✓ 已将主工作分支从 develop 更新为 main
|
|
53
58
|
|
|
54
|
-
# show
|
|
55
|
-
|
|
56
|
-
"clawtMainWorkBranch": "main"
|
|
57
|
-
}
|
|
59
|
+
# show 交互式修改成功
|
|
60
|
+
✓ 项目配置 validateRunCommand 已设置为 npm test
|
|
58
61
|
|
|
59
62
|
# show 未初始化(抛出错误)
|
|
60
63
|
项目尚未初始化,请先执行 clawt init 设置主工作分支
|
|
@@ -62,4 +65,10 @@ clawt init show
|
|
|
62
65
|
|
|
63
66
|
**重复执行:** 支持重复执行,后一次覆盖前一次的配置。
|
|
64
67
|
|
|
68
|
+
**实现要点:**
|
|
69
|
+
|
|
70
|
+
- `init show` 子命令从 JSON 展示改为交互式面板,调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`)实现通用交互式配置编辑
|
|
71
|
+
- 配置项定义来自 `PROJECT_CONFIG_DEFINITIONS`(`src/constants/project-config.ts`),详见 [项目级配置文档](./project-config.md)
|
|
72
|
+
- 消息常量:`MESSAGES.INIT_SELECT_PROMPT`(选择配置项提示语)、`MESSAGES.INIT_SET_SUCCESS`(修改成功提示),定义在 `src/constants/messages/init.ts`
|
|
73
|
+
|
|
65
74
|
---
|