clawt 2.13.0 → 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/dist/index.js +201 -63
- package/dist/postinstall.js +25 -2
- package/package.json +1 -1
- package/src/commands/config.ts +115 -48
- package/src/constants/config.ts +2 -0
- package/src/constants/index.ts +1 -1
- package/src/constants/messages/config.ts +22 -0
- package/src/types/config.ts +2 -0
- package/src/utils/config-strategy.ts +196 -0
- package/src/utils/config.ts +8 -0
- package/src/utils/index.ts +2 -1
- package/tests/unit/commands/config.test.ts +324 -24
- package/tests/unit/constants/config.test.ts +24 -1
- package/tests/unit/utils/config-strategy.test.ts +217 -0
- package/tests/unit/utils/config.test.ts +13 -1
package/dist/index.js
CHANGED
|
@@ -251,7 +251,26 @@ var RESET_MESSAGES = {
|
|
|
251
251
|
// src/constants/messages/config.ts
|
|
252
252
|
var CONFIG_CMD_MESSAGES = {
|
|
253
253
|
/** 配置已恢复为默认值 */
|
|
254
|
-
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C"
|
|
254
|
+
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C",
|
|
255
|
+
/** 配置项设置成功 */
|
|
256
|
+
CONFIG_SET_SUCCESS: (key, value) => `\u2713 ${key} \u5DF2\u8BBE\u7F6E\u4E3A ${value}`,
|
|
257
|
+
/** 获取配置值显示 */
|
|
258
|
+
CONFIG_GET_VALUE: (key, value) => `${key} = ${value}`,
|
|
259
|
+
/** 无效配置项名称 */
|
|
260
|
+
CONFIG_INVALID_KEY: (key, validKeys) => `\u65E0\u6548\u7684\u914D\u7F6E\u9879: ${key}
|
|
261
|
+
\u53EF\u7528\u7684\u914D\u7F6E\u9879: ${validKeys.join(", ")}`,
|
|
262
|
+
/** 布尔类型值无效 */
|
|
263
|
+
CONFIG_INVALID_BOOLEAN: (key) => `\u914D\u7F6E\u9879 ${key} \u4E3A\u5E03\u5C14\u7C7B\u578B\uFF0C\u4EC5\u63A5\u53D7 true \u6216 false`,
|
|
264
|
+
/** 数字类型值无效 */
|
|
265
|
+
CONFIG_INVALID_NUMBER: (key) => `\u914D\u7F6E\u9879 ${key} \u4E3A\u6570\u5B57\u7C7B\u578B\uFF0C\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57`,
|
|
266
|
+
/** 枚举类型配置项值无效(通用版) */
|
|
267
|
+
CONFIG_INVALID_ENUM: (key, validValues) => `\u914D\u7F6E\u9879 ${key} \u4EC5\u63A5\u53D7\u4EE5\u4E0B\u503C: ${validValues.join(", ")}`,
|
|
268
|
+
/** 交互式选择配置项提示 */
|
|
269
|
+
CONFIG_SELECT_PROMPT: "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u9879",
|
|
270
|
+
/** 交互式输入新值提示 */
|
|
271
|
+
CONFIG_INPUT_PROMPT: (key) => `\u8F93\u5165 ${key} \u7684\u65B0\u503C`,
|
|
272
|
+
/** 缺少 value 参数提示 */
|
|
273
|
+
CONFIG_MISSING_VALUE: (key) => `\u7F3A\u5C11\u914D\u7F6E\u503C\uFF0C\u7528\u6CD5: clawt config set ${key} <value>`
|
|
255
274
|
};
|
|
256
275
|
|
|
257
276
|
// src/constants/messages/status.ts
|
|
@@ -353,7 +372,8 @@ var CONFIG_DEFINITIONS = {
|
|
|
353
372
|
},
|
|
354
373
|
terminalApp: {
|
|
355
374
|
defaultValue: "auto",
|
|
356
|
-
description: "\u6279\u91CF resume \u4F7F\u7528\u7684\u7EC8\u7AEF\u5E94\u7528\uFF1Aauto\uFF08\u81EA\u52A8\u68C0\u6D4B\uFF09\u3001iterm2\u3001terminal\uFF08macOS\uFF09"
|
|
375
|
+
description: "\u6279\u91CF resume \u4F7F\u7528\u7684\u7EC8\u7AEF\u5E94\u7528\uFF1Aauto\uFF08\u81EA\u52A8\u68C0\u6D4B\uFF09\u3001iterm2\u3001terminal\uFF08macOS\uFF09",
|
|
376
|
+
allowedValues: VALID_TERMINAL_APPS
|
|
357
377
|
},
|
|
358
378
|
aliases: {
|
|
359
379
|
defaultValue: {},
|
|
@@ -936,6 +956,9 @@ function writeConfig(config2) {
|
|
|
936
956
|
function writeDefaultConfig() {
|
|
937
957
|
writeConfig(DEFAULT_CONFIG);
|
|
938
958
|
}
|
|
959
|
+
function saveConfig(config2) {
|
|
960
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2), "utf-8");
|
|
961
|
+
}
|
|
939
962
|
function getConfigValue(key) {
|
|
940
963
|
const config2 = loadConfig();
|
|
941
964
|
return config2[key];
|
|
@@ -1731,8 +1754,98 @@ function applyAliases(program2, aliases) {
|
|
|
1731
1754
|
}
|
|
1732
1755
|
}
|
|
1733
1756
|
|
|
1734
|
-
// src/
|
|
1757
|
+
// src/utils/config-strategy.ts
|
|
1735
1758
|
import chalk4 from "chalk";
|
|
1759
|
+
import Enquirer3 from "enquirer";
|
|
1760
|
+
function isValidConfigKey(key) {
|
|
1761
|
+
return key in DEFAULT_CONFIG;
|
|
1762
|
+
}
|
|
1763
|
+
function getValidConfigKeys() {
|
|
1764
|
+
return Object.keys(DEFAULT_CONFIG);
|
|
1765
|
+
}
|
|
1766
|
+
function parseConfigValue(key, rawValue) {
|
|
1767
|
+
const expectedType = typeof DEFAULT_CONFIG[key];
|
|
1768
|
+
if (expectedType === "boolean") {
|
|
1769
|
+
if (rawValue === "true") return { success: true, value: true };
|
|
1770
|
+
if (rawValue === "false") return { success: true, value: false };
|
|
1771
|
+
return { success: false, error: MESSAGES.CONFIG_INVALID_BOOLEAN(key) };
|
|
1772
|
+
}
|
|
1773
|
+
if (expectedType === "number") {
|
|
1774
|
+
const num = Number(rawValue);
|
|
1775
|
+
if (Number.isNaN(num)) {
|
|
1776
|
+
return { success: false, error: MESSAGES.CONFIG_INVALID_NUMBER(key) };
|
|
1777
|
+
}
|
|
1778
|
+
return { success: true, value: num };
|
|
1779
|
+
}
|
|
1780
|
+
const definition = CONFIG_DEFINITIONS[key];
|
|
1781
|
+
if (definition.allowedValues && !definition.allowedValues.includes(rawValue)) {
|
|
1782
|
+
return { success: false, error: MESSAGES.CONFIG_INVALID_ENUM(key, definition.allowedValues) };
|
|
1783
|
+
}
|
|
1784
|
+
return { success: true, value: rawValue };
|
|
1785
|
+
}
|
|
1786
|
+
async function promptConfigValue(key, currentValue) {
|
|
1787
|
+
const expectedType = typeof currentValue;
|
|
1788
|
+
if (expectedType === "boolean") {
|
|
1789
|
+
return promptBooleanValue(key, currentValue);
|
|
1790
|
+
}
|
|
1791
|
+
if (expectedType === "number") {
|
|
1792
|
+
return promptNumberValue(key, currentValue);
|
|
1793
|
+
}
|
|
1794
|
+
const definition = CONFIG_DEFINITIONS[key];
|
|
1795
|
+
if (definition.allowedValues) {
|
|
1796
|
+
return promptEnumValue(key, currentValue, definition.allowedValues);
|
|
1797
|
+
}
|
|
1798
|
+
return promptStringValue(key, currentValue);
|
|
1799
|
+
}
|
|
1800
|
+
function formatConfigValue(value) {
|
|
1801
|
+
if (typeof value === "boolean") {
|
|
1802
|
+
return value ? chalk4.green("true") : chalk4.yellow("false");
|
|
1803
|
+
}
|
|
1804
|
+
return chalk4.cyan(String(value));
|
|
1805
|
+
}
|
|
1806
|
+
async function promptBooleanValue(key, currentValue) {
|
|
1807
|
+
const choices = [
|
|
1808
|
+
{ name: "true", message: "true" },
|
|
1809
|
+
{ name: "false", message: "false" }
|
|
1810
|
+
];
|
|
1811
|
+
const selected = await new Enquirer3.Select({
|
|
1812
|
+
message: MESSAGES.CONFIG_INPUT_PROMPT(key),
|
|
1813
|
+
choices,
|
|
1814
|
+
initial: currentValue ? 0 : 1
|
|
1815
|
+
}).run();
|
|
1816
|
+
return selected === "true";
|
|
1817
|
+
}
|
|
1818
|
+
async function promptNumberValue(key, currentValue) {
|
|
1819
|
+
const input = await new Enquirer3.Input({
|
|
1820
|
+
message: MESSAGES.CONFIG_INPUT_PROMPT(key),
|
|
1821
|
+
initial: String(currentValue),
|
|
1822
|
+
validate: (val) => {
|
|
1823
|
+
if (Number.isNaN(Number(val))) return "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57";
|
|
1824
|
+
return true;
|
|
1825
|
+
}
|
|
1826
|
+
}).run();
|
|
1827
|
+
return Number(input);
|
|
1828
|
+
}
|
|
1829
|
+
async function promptEnumValue(key, currentValue, allowedValues) {
|
|
1830
|
+
const choices = allowedValues.map((v) => ({
|
|
1831
|
+
name: v,
|
|
1832
|
+
message: v
|
|
1833
|
+
}));
|
|
1834
|
+
return await new Enquirer3.Select({
|
|
1835
|
+
message: MESSAGES.CONFIG_INPUT_PROMPT(key),
|
|
1836
|
+
choices,
|
|
1837
|
+
initial: allowedValues.indexOf(currentValue)
|
|
1838
|
+
}).run();
|
|
1839
|
+
}
|
|
1840
|
+
async function promptStringValue(key, currentValue) {
|
|
1841
|
+
return await new Enquirer3.Input({
|
|
1842
|
+
message: MESSAGES.CONFIG_INPUT_PROMPT(key),
|
|
1843
|
+
initial: currentValue
|
|
1844
|
+
}).run();
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// src/commands/list.ts
|
|
1848
|
+
import chalk5 from "chalk";
|
|
1736
1849
|
function registerListCommand(program2) {
|
|
1737
1850
|
program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
|
|
1738
1851
|
handleList(options);
|
|
@@ -1769,12 +1882,12 @@ function printListAsText(projectName, worktrees) {
|
|
|
1769
1882
|
for (const wt of worktrees) {
|
|
1770
1883
|
const status = getWorktreeStatus(wt);
|
|
1771
1884
|
const isIdle = status ? isWorktreeIdle(status) : false;
|
|
1772
|
-
const pathDisplay = isIdle ?
|
|
1885
|
+
const pathDisplay = isIdle ? chalk5.hex("#FF8C00")(wt.path) : wt.path;
|
|
1773
1886
|
printInfo(` ${pathDisplay} [${wt.branch}]`);
|
|
1774
1887
|
if (status) {
|
|
1775
1888
|
printInfo(` ${formatWorktreeStatus(status)}`);
|
|
1776
1889
|
} else {
|
|
1777
|
-
printInfo(` ${
|
|
1890
|
+
printInfo(` ${chalk5.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
|
|
1778
1891
|
}
|
|
1779
1892
|
printInfo("");
|
|
1780
1893
|
}
|
|
@@ -1997,7 +2110,7 @@ async function handleBatchResume(worktrees) {
|
|
|
1997
2110
|
}
|
|
1998
2111
|
|
|
1999
2112
|
// src/commands/validate.ts
|
|
2000
|
-
import
|
|
2113
|
+
import Enquirer4 from "enquirer";
|
|
2001
2114
|
var VALIDATE_RESOLVE_MESSAGES = {
|
|
2002
2115
|
noWorktrees: MESSAGES.VALIDATE_NO_WORKTREES,
|
|
2003
2116
|
selectBranch: MESSAGES.VALIDATE_SELECT_BRANCH,
|
|
@@ -2011,7 +2124,7 @@ function registerValidateCommand(program2) {
|
|
|
2011
2124
|
}
|
|
2012
2125
|
async function handleDirtyMainWorktree(mainWorktreePath) {
|
|
2013
2126
|
printWarning("\u4E3B worktree \u5F53\u524D\u5206\u652F\u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n");
|
|
2014
|
-
const choice = await new
|
|
2127
|
+
const choice = await new Enquirer4.Select({
|
|
2015
2128
|
message: "\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
|
|
2016
2129
|
choices: [
|
|
2017
2130
|
{
|
|
@@ -2299,34 +2412,21 @@ async function handleMerge(options) {
|
|
|
2299
2412
|
}
|
|
2300
2413
|
|
|
2301
2414
|
// src/commands/config.ts
|
|
2302
|
-
import
|
|
2415
|
+
import chalk6 from "chalk";
|
|
2416
|
+
import Enquirer5 from "enquirer";
|
|
2303
2417
|
function registerConfigCommand(program2) {
|
|
2304
|
-
const configCmd = program2.command("config").description("\u67E5\u770B\u548C\
|
|
2305
|
-
|
|
2418
|
+
const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
|
|
2419
|
+
await handleConfigSet();
|
|
2306
2420
|
});
|
|
2307
2421
|
configCmd.command("reset").description("\u5C06\u914D\u7F6E\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C").action(async () => {
|
|
2308
2422
|
await handleConfigReset();
|
|
2309
2423
|
});
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
`);
|
|
2317
|
-
printSeparator();
|
|
2318
|
-
const keys = Object.keys(DEFAULT_CONFIG);
|
|
2319
|
-
for (let i = 0; i < keys.length; i++) {
|
|
2320
|
-
const key = keys[i];
|
|
2321
|
-
const value = config2[key];
|
|
2322
|
-
const description = CONFIG_DESCRIPTIONS[key];
|
|
2323
|
-
const formattedValue = formatConfigValue(value);
|
|
2324
|
-
if (i === 0) printInfo("");
|
|
2325
|
-
printInfo(` ${chalk5.bold(key)}: ${formattedValue}`);
|
|
2326
|
-
printInfo(` ${chalk5.dim(description)}`);
|
|
2327
|
-
printInfo("");
|
|
2328
|
-
}
|
|
2329
|
-
printSeparator();
|
|
2424
|
+
configCmd.command("set [key] [value]").description("\u4FEE\u6539\u914D\u7F6E\u9879\uFF08\u65E0\u53C2\u6570\u8FDB\u5165\u4EA4\u4E92\u5F0F\u914D\u7F6E\uFF09").action(async (key, value) => {
|
|
2425
|
+
await handleConfigSet(key, value);
|
|
2426
|
+
});
|
|
2427
|
+
configCmd.command("get <key>").description("\u83B7\u53D6\u5355\u4E2A\u914D\u7F6E\u9879\u7684\u503C").action((key) => {
|
|
2428
|
+
handleConfigGet(key);
|
|
2429
|
+
});
|
|
2330
2430
|
}
|
|
2331
2431
|
async function handleConfigReset() {
|
|
2332
2432
|
logger.info("config reset \u547D\u4EE4\u6267\u884C\uFF0C\u6062\u590D\u9ED8\u8BA4\u914D\u7F6E");
|
|
@@ -2341,19 +2441,57 @@ async function handleConfigReset() {
|
|
|
2341
2441
|
writeDefaultConfig();
|
|
2342
2442
|
printSuccess(MESSAGES.CONFIG_RESET_SUCCESS);
|
|
2343
2443
|
}
|
|
2344
|
-
function
|
|
2345
|
-
if (
|
|
2346
|
-
|
|
2444
|
+
async function handleConfigSet(key, value) {
|
|
2445
|
+
if (!key) {
|
|
2446
|
+
await handleInteractiveConfigSet();
|
|
2447
|
+
return;
|
|
2347
2448
|
}
|
|
2348
|
-
if (
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
return
|
|
2449
|
+
if (!isValidConfigKey(key)) {
|
|
2450
|
+
printError(MESSAGES.CONFIG_INVALID_KEY(key, getValidConfigKeys()));
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
if (value === void 0) {
|
|
2454
|
+
printError(MESSAGES.CONFIG_MISSING_VALUE(key));
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
const result = parseConfigValue(key, value);
|
|
2458
|
+
if (!result.success) {
|
|
2459
|
+
printError(result.error);
|
|
2460
|
+
return;
|
|
2355
2461
|
}
|
|
2356
|
-
|
|
2462
|
+
const config2 = loadConfig();
|
|
2463
|
+
config2[key] = result.value;
|
|
2464
|
+
saveConfig(config2);
|
|
2465
|
+
logger.info(`config set \u547D\u4EE4\u6267\u884C\uFF0C\u8BBE\u7F6E ${key} = ${String(result.value)}`);
|
|
2466
|
+
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(key, String(result.value)));
|
|
2467
|
+
}
|
|
2468
|
+
async function handleInteractiveConfigSet() {
|
|
2469
|
+
const config2 = loadConfig();
|
|
2470
|
+
const keys = Object.keys(DEFAULT_CONFIG);
|
|
2471
|
+
logger.info("config set \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u914D\u7F6E");
|
|
2472
|
+
const choices = keys.map((k) => ({
|
|
2473
|
+
name: k,
|
|
2474
|
+
message: `${k}: ${formatConfigValue(config2[k])} ${chalk6.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`
|
|
2475
|
+
}));
|
|
2476
|
+
const selectedKey = await new Enquirer5.Select({
|
|
2477
|
+
message: MESSAGES.CONFIG_SELECT_PROMPT,
|
|
2478
|
+
choices
|
|
2479
|
+
}).run();
|
|
2480
|
+
const currentValue = config2[selectedKey];
|
|
2481
|
+
const newValue = await promptConfigValue(selectedKey, currentValue);
|
|
2482
|
+
config2[selectedKey] = newValue;
|
|
2483
|
+
saveConfig(config2);
|
|
2484
|
+
printSuccess(MESSAGES.CONFIG_SET_SUCCESS(selectedKey, String(newValue)));
|
|
2485
|
+
}
|
|
2486
|
+
function handleConfigGet(key) {
|
|
2487
|
+
if (!isValidConfigKey(key)) {
|
|
2488
|
+
printError(MESSAGES.CONFIG_INVALID_KEY(key, getValidConfigKeys()));
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
const config2 = loadConfig();
|
|
2492
|
+
const value = config2[key];
|
|
2493
|
+
logger.info(`config get \u547D\u4EE4\u6267\u884C\uFF0C\u83B7\u53D6 ${key} = ${String(value)}`);
|
|
2494
|
+
printInfo(MESSAGES.CONFIG_GET_VALUE(key, String(value)));
|
|
2357
2495
|
}
|
|
2358
2496
|
|
|
2359
2497
|
// src/commands/sync.ts
|
|
@@ -2440,7 +2578,7 @@ async function handleReset() {
|
|
|
2440
2578
|
}
|
|
2441
2579
|
|
|
2442
2580
|
// src/commands/status.ts
|
|
2443
|
-
import
|
|
2581
|
+
import chalk7 from "chalk";
|
|
2444
2582
|
function registerStatusCommand(program2) {
|
|
2445
2583
|
program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
|
|
2446
2584
|
handleStatus(options);
|
|
@@ -2536,7 +2674,7 @@ function printStatusAsJson(result) {
|
|
|
2536
2674
|
}
|
|
2537
2675
|
function printStatusAsText(result) {
|
|
2538
2676
|
printDoubleSeparator();
|
|
2539
|
-
printInfo(` ${
|
|
2677
|
+
printInfo(` ${chalk7.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
|
|
2540
2678
|
printDoubleSeparator();
|
|
2541
2679
|
printInfo("");
|
|
2542
2680
|
printMainSection(result.main);
|
|
@@ -2549,17 +2687,17 @@ function printStatusAsText(result) {
|
|
|
2549
2687
|
printDoubleSeparator();
|
|
2550
2688
|
}
|
|
2551
2689
|
function printMainSection(main) {
|
|
2552
|
-
printInfo(` ${
|
|
2553
|
-
printInfo(` \u5206\u652F: ${
|
|
2690
|
+
printInfo(` ${chalk7.bold("\u25C6")} ${chalk7.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
|
|
2691
|
+
printInfo(` \u5206\u652F: ${chalk7.bold(main.branch)}`);
|
|
2554
2692
|
if (main.isClean) {
|
|
2555
|
-
printInfo(` \u72B6\u6001: ${
|
|
2693
|
+
printInfo(` \u72B6\u6001: ${chalk7.green("\u2713 \u5E72\u51C0")}`);
|
|
2556
2694
|
} else {
|
|
2557
|
-
printInfo(` \u72B6\u6001: ${
|
|
2695
|
+
printInfo(` \u72B6\u6001: ${chalk7.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
|
|
2558
2696
|
}
|
|
2559
2697
|
printInfo("");
|
|
2560
2698
|
}
|
|
2561
2699
|
function printWorktreesSection(worktrees, total) {
|
|
2562
|
-
printInfo(` ${
|
|
2700
|
+
printInfo(` ${chalk7.bold("\u25C6")} ${chalk7.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
|
|
2563
2701
|
printInfo("");
|
|
2564
2702
|
if (worktrees.length === 0) {
|
|
2565
2703
|
printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
|
|
@@ -2571,39 +2709,39 @@ function printWorktreesSection(worktrees, total) {
|
|
|
2571
2709
|
}
|
|
2572
2710
|
function printWorktreeItem(wt) {
|
|
2573
2711
|
const statusLabel = formatChangeStatusLabel(wt.changeStatus);
|
|
2574
|
-
printInfo(` ${
|
|
2712
|
+
printInfo(` ${chalk7.bold("\u25CF")} ${chalk7.bold(wt.branch)} [${statusLabel}]`);
|
|
2575
2713
|
const parts = [];
|
|
2576
2714
|
if (wt.insertions > 0 || wt.deletions > 0) {
|
|
2577
|
-
parts.push(`${
|
|
2715
|
+
parts.push(`${chalk7.green(`+${wt.insertions}`)} ${chalk7.red(`-${wt.deletions}`)}`);
|
|
2578
2716
|
}
|
|
2579
2717
|
if (wt.commitsAhead > 0) {
|
|
2580
|
-
parts.push(
|
|
2718
|
+
parts.push(chalk7.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
|
|
2581
2719
|
}
|
|
2582
2720
|
if (wt.commitsBehind > 0) {
|
|
2583
|
-
parts.push(
|
|
2721
|
+
parts.push(chalk7.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
|
|
2584
2722
|
} else {
|
|
2585
|
-
parts.push(
|
|
2723
|
+
parts.push(chalk7.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
|
|
2586
2724
|
}
|
|
2587
2725
|
printInfo(` ${parts.join(" ")}`);
|
|
2588
2726
|
if (wt.hasSnapshot) {
|
|
2589
|
-
printInfo(` ${
|
|
2727
|
+
printInfo(` ${chalk7.blue("\u6709 validate \u5FEB\u7167")}`);
|
|
2590
2728
|
}
|
|
2591
2729
|
printInfo("");
|
|
2592
2730
|
}
|
|
2593
2731
|
function formatChangeStatusLabel(status) {
|
|
2594
2732
|
switch (status) {
|
|
2595
2733
|
case "committed":
|
|
2596
|
-
return
|
|
2734
|
+
return chalk7.green(MESSAGES.STATUS_CHANGE_COMMITTED);
|
|
2597
2735
|
case "uncommitted":
|
|
2598
|
-
return
|
|
2736
|
+
return chalk7.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
|
|
2599
2737
|
case "conflict":
|
|
2600
|
-
return
|
|
2738
|
+
return chalk7.red(MESSAGES.STATUS_CHANGE_CONFLICT);
|
|
2601
2739
|
case "clean":
|
|
2602
|
-
return
|
|
2740
|
+
return chalk7.gray(MESSAGES.STATUS_CHANGE_CLEAN);
|
|
2603
2741
|
}
|
|
2604
2742
|
}
|
|
2605
2743
|
function printSnapshotsSection(snapshots) {
|
|
2606
|
-
printInfo(` ${
|
|
2744
|
+
printInfo(` ${chalk7.bold("\u25C6")} ${chalk7.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
|
|
2607
2745
|
printInfo("");
|
|
2608
2746
|
if (snapshots.length === 0) {
|
|
2609
2747
|
printInfo(` ${MESSAGES.STATUS_NO_SNAPSHOTS}`);
|
|
@@ -2611,15 +2749,15 @@ function printSnapshotsSection(snapshots) {
|
|
|
2611
2749
|
return;
|
|
2612
2750
|
}
|
|
2613
2751
|
for (const snap of snapshots) {
|
|
2614
|
-
const orphanLabel = snap.worktreeExists ? "" : ` ${
|
|
2615
|
-
const icon = snap.worktreeExists ?
|
|
2752
|
+
const orphanLabel = snap.worktreeExists ? "" : ` ${chalk7.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
|
|
2753
|
+
const icon = snap.worktreeExists ? chalk7.blue("\u25CF") : chalk7.yellow("\u26A0");
|
|
2616
2754
|
printInfo(` ${icon} ${snap.branch}${orphanLabel}`);
|
|
2617
2755
|
}
|
|
2618
2756
|
printInfo("");
|
|
2619
2757
|
}
|
|
2620
2758
|
|
|
2621
2759
|
// src/commands/alias.ts
|
|
2622
|
-
import
|
|
2760
|
+
import chalk8 from "chalk";
|
|
2623
2761
|
function getRegisteredCommandNames(program2) {
|
|
2624
2762
|
return program2.commands.map((cmd) => cmd.name());
|
|
2625
2763
|
}
|
|
@@ -2640,7 +2778,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
|
|
|
2640
2778
|
`);
|
|
2641
2779
|
printSeparator();
|
|
2642
2780
|
for (const [alias, command] of entries) {
|
|
2643
|
-
printInfo(` ${
|
|
2781
|
+
printInfo(` ${chalk8.bold(alias)} \u2192 ${chalk8.cyan(command)}`);
|
|
2644
2782
|
}
|
|
2645
2783
|
printInfo("");
|
|
2646
2784
|
printSeparator();
|
package/dist/postinstall.js
CHANGED
|
@@ -243,7 +243,26 @@ var RESET_MESSAGES = {
|
|
|
243
243
|
// src/constants/messages/config.ts
|
|
244
244
|
var CONFIG_CMD_MESSAGES = {
|
|
245
245
|
/** 配置已恢复为默认值 */
|
|
246
|
-
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C"
|
|
246
|
+
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C",
|
|
247
|
+
/** 配置项设置成功 */
|
|
248
|
+
CONFIG_SET_SUCCESS: (key, value) => `\u2713 ${key} \u5DF2\u8BBE\u7F6E\u4E3A ${value}`,
|
|
249
|
+
/** 获取配置值显示 */
|
|
250
|
+
CONFIG_GET_VALUE: (key, value) => `${key} = ${value}`,
|
|
251
|
+
/** 无效配置项名称 */
|
|
252
|
+
CONFIG_INVALID_KEY: (key, validKeys) => `\u65E0\u6548\u7684\u914D\u7F6E\u9879: ${key}
|
|
253
|
+
\u53EF\u7528\u7684\u914D\u7F6E\u9879: ${validKeys.join(", ")}`,
|
|
254
|
+
/** 布尔类型值无效 */
|
|
255
|
+
CONFIG_INVALID_BOOLEAN: (key) => `\u914D\u7F6E\u9879 ${key} \u4E3A\u5E03\u5C14\u7C7B\u578B\uFF0C\u4EC5\u63A5\u53D7 true \u6216 false`,
|
|
256
|
+
/** 数字类型值无效 */
|
|
257
|
+
CONFIG_INVALID_NUMBER: (key) => `\u914D\u7F6E\u9879 ${key} \u4E3A\u6570\u5B57\u7C7B\u578B\uFF0C\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57`,
|
|
258
|
+
/** 枚举类型配置项值无效(通用版) */
|
|
259
|
+
CONFIG_INVALID_ENUM: (key, validValues) => `\u914D\u7F6E\u9879 ${key} \u4EC5\u63A5\u53D7\u4EE5\u4E0B\u503C: ${validValues.join(", ")}`,
|
|
260
|
+
/** 交互式选择配置项提示 */
|
|
261
|
+
CONFIG_SELECT_PROMPT: "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u9879",
|
|
262
|
+
/** 交互式输入新值提示 */
|
|
263
|
+
CONFIG_INPUT_PROMPT: (key) => `\u8F93\u5165 ${key} \u7684\u65B0\u503C`,
|
|
264
|
+
/** 缺少 value 参数提示 */
|
|
265
|
+
CONFIG_MISSING_VALUE: (key) => `\u7F3A\u5C11\u914D\u7F6E\u503C\uFF0C\u7528\u6CD5: clawt config set ${key} <value>`
|
|
247
266
|
};
|
|
248
267
|
|
|
249
268
|
// src/constants/messages/status.ts
|
|
@@ -306,6 +325,9 @@ var MESSAGES = {
|
|
|
306
325
|
...ALIAS_MESSAGES
|
|
307
326
|
};
|
|
308
327
|
|
|
328
|
+
// src/constants/terminal.ts
|
|
329
|
+
var VALID_TERMINAL_APPS = ["auto", "iterm2", "terminal"];
|
|
330
|
+
|
|
309
331
|
// src/constants/config.ts
|
|
310
332
|
var CONFIG_DEFINITIONS = {
|
|
311
333
|
autoDeleteBranch: {
|
|
@@ -330,7 +352,8 @@ var CONFIG_DEFINITIONS = {
|
|
|
330
352
|
},
|
|
331
353
|
terminalApp: {
|
|
332
354
|
defaultValue: "auto",
|
|
333
|
-
description: "\u6279\u91CF resume \u4F7F\u7528\u7684\u7EC8\u7AEF\u5E94\u7528\uFF1Aauto\uFF08\u81EA\u52A8\u68C0\u6D4B\uFF09\u3001iterm2\u3001terminal\uFF08macOS\uFF09"
|
|
355
|
+
description: "\u6279\u91CF resume \u4F7F\u7528\u7684\u7EC8\u7AEF\u5E94\u7528\uFF1Aauto\uFF08\u81EA\u52A8\u68C0\u6D4B\uFF09\u3001iterm2\u3001terminal\uFF08macOS\uFF09",
|
|
356
|
+
allowedValues: VALID_TERMINAL_APPS
|
|
334
357
|
},
|
|
335
358
|
aliases: {
|
|
336
359
|
defaultValue: {},
|
package/package.json
CHANGED
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
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
for (let i = 0; i < keys.length; i++) {
|
|
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,22 +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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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;
|
|
95
157
|
}
|
|
96
|
-
|
|
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)));
|
|
97
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,7 @@ 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,
|
|
35
37
|
},
|
|
36
38
|
aliases: {
|
|
37
39
|
defaultValue: {} as Record<string, string>,
|