clawt 3.0.0 → 3.1.1
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/agents/docs-sync-updater.md +29 -11
- package/README.md +9 -30
- package/dist/index.js +734 -92
- package/dist/postinstall.js +38 -2
- package/docs/spec.md +2 -2
- package/docs/status.md +147 -4
- package/package.json +1 -1
- package/src/commands/remove.ts +2 -3
- package/src/commands/status.ts +13 -4
- package/src/commands/sync.ts +0 -2
- package/src/constants/index.ts +12 -0
- package/src/constants/interactive-panel.ts +44 -0
- package/src/constants/messages/index.ts +2 -0
- package/src/constants/messages/interactive-panel.ts +61 -0
- package/src/types/command.ts +2 -0
- package/src/utils/index.ts +4 -1
- package/src/utils/interactive-panel-render.ts +315 -0
- package/src/utils/interactive-panel.ts +590 -0
- package/src/utils/worktree-matcher.ts +2 -2
- package/tests/unit/commands/remove.test.ts +0 -1
- package/tests/unit/commands/sync.test.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -463,6 +463,62 @@ var INIT_MESSAGES = {
|
|
|
463
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
464
|
};
|
|
465
465
|
|
|
466
|
+
// src/constants/messages/interactive-panel.ts
|
|
467
|
+
import chalk from "chalk";
|
|
468
|
+
|
|
469
|
+
// src/constants/interactive-panel.ts
|
|
470
|
+
var PANEL_REFRESH_INTERVAL_MS = 5e3;
|
|
471
|
+
var PANEL_COUNTDOWN_INTERVAL_MS = 1e3;
|
|
472
|
+
var SELECTED_INDICATOR = "\u25B6";
|
|
473
|
+
var UNSELECTED_INDICATOR = " ";
|
|
474
|
+
var KEY_ARROW_UP = "\x1B[A";
|
|
475
|
+
var KEY_ARROW_DOWN = "\x1B[B";
|
|
476
|
+
var KEY_CTRL_C = 3;
|
|
477
|
+
var PANEL_SHORTCUT_KEYS = {
|
|
478
|
+
/** 验证 */
|
|
479
|
+
VALIDATE: "v",
|
|
480
|
+
/** 合并 */
|
|
481
|
+
MERGE: "m",
|
|
482
|
+
/** 删除 */
|
|
483
|
+
DELETE: "d",
|
|
484
|
+
/** 恢复 */
|
|
485
|
+
RESUME: "r",
|
|
486
|
+
/** 同步 */
|
|
487
|
+
SYNC: "s",
|
|
488
|
+
/** 手动刷新 */
|
|
489
|
+
REFRESH: "f",
|
|
490
|
+
/** 退出 */
|
|
491
|
+
QUIT: "q"
|
|
492
|
+
};
|
|
493
|
+
var PANEL_DATE_SEPARATOR_PREFIX = "\u2550\u2550\u2550\u2550";
|
|
494
|
+
var PANEL_FIXED_ROWS = 4;
|
|
495
|
+
|
|
496
|
+
// src/constants/messages/interactive-panel.ts
|
|
497
|
+
var SHORTCUT_LABELS = {
|
|
498
|
+
VALIDATE: "\u9A8C\u8BC1",
|
|
499
|
+
MERGE: "\u5408\u5E76",
|
|
500
|
+
DELETE: "\u5220\u9664",
|
|
501
|
+
RESUME: "\u6062\u590D",
|
|
502
|
+
SYNC: "\u540C\u6B65",
|
|
503
|
+
REFRESH: "\u5237\u65B0",
|
|
504
|
+
QUIT: "\u9000\u51FA"
|
|
505
|
+
};
|
|
506
|
+
var PANEL_FOOTER_SHORTCUTS = Object.entries(SHORTCUT_LABELS).map(([key, label]) => `[${chalk.cyan(PANEL_SHORTCUT_KEYS[key])}]${label}`).join(" ");
|
|
507
|
+
var PANEL_FOOTER_COUNTDOWN = (seconds) => chalk.gray(`(${seconds}s \u540E\u5237\u65B0)`);
|
|
508
|
+
var PANEL_OVERFLOW_DOWN_HINT = chalk.gray("\u2193 \u66F4\u591A worktree...");
|
|
509
|
+
var PANEL_OVERFLOW_UP_HINT = chalk.gray("\u2191 \u66F4\u591A worktree...");
|
|
510
|
+
var PANEL_SNAPSHOT_SUMMARY = (total, orphaned) => {
|
|
511
|
+
const base = `\u5FEB\u7167: ${total} \u4E2A`;
|
|
512
|
+
if (orphaned > 0) {
|
|
513
|
+
return `${base}\uFF08${chalk.yellow(`${orphaned} \u4E2A\u5B64\u7ACB`)}\uFF09`;
|
|
514
|
+
}
|
|
515
|
+
return base;
|
|
516
|
+
};
|
|
517
|
+
var PANEL_NO_WORKTREES = "(\u65E0\u6D3B\u8DC3 worktree)";
|
|
518
|
+
var PANEL_PRESS_ENTER_TO_RETURN = chalk.gray("\n\u6309 Enter \u8FD4\u56DE\u9762\u677F...");
|
|
519
|
+
var PANEL_NOT_TTY = "\u4EA4\u4E92\u5F0F\u9762\u677F\u9700\u8981 TTY \u7EC8\u7AEF\u73AF\u5883\uFF0C\u8BF7\u76F4\u63A5\u5728\u7EC8\u7AEF\u4E2D\u8FD0\u884C clawt status -i";
|
|
520
|
+
var PANEL_TITLE = (projectName) => chalk.bold.cyan(`\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`);
|
|
521
|
+
|
|
466
522
|
// src/constants/messages/index.ts
|
|
467
523
|
var MESSAGES = {
|
|
468
524
|
...COMMON_MESSAGES,
|
|
@@ -597,14 +653,14 @@ var CLEAR_SCREEN = "\x1B[2J";
|
|
|
597
653
|
var CURSOR_HOME = "\x1B[H";
|
|
598
654
|
|
|
599
655
|
// src/constants/prompt.ts
|
|
600
|
-
import
|
|
656
|
+
import chalk2 from "chalk";
|
|
601
657
|
var SELECT_ALL_NAME = "__select_all__";
|
|
602
658
|
var SELECT_ALL_LABEL = "[select-all]";
|
|
603
659
|
var GROUP_SELECT_ALL_PREFIX = "__group_select_all_";
|
|
604
660
|
var GROUP_SELECT_ALL_LABEL = (dateLabel) => `[select-all: ${dateLabel}]`;
|
|
605
|
-
var GROUP_SEPARATOR_LABEL = (dateLabel, relativeTime) => `\u2550\u2550\u2550\u2550 ${
|
|
661
|
+
var GROUP_SEPARATOR_LABEL = (dateLabel, relativeTime) => `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")(dateLabel)}\uFF08${chalk2.hex("#FF8C00")(relativeTime)}\uFF09 \u2550\u2550\u2550\u2550`;
|
|
606
662
|
var UNKNOWN_DATE_GROUP = "\u672A\u77E5\u65E5\u671F";
|
|
607
|
-
var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${
|
|
663
|
+
var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
|
|
608
664
|
|
|
609
665
|
// src/errors/index.ts
|
|
610
666
|
var ClawtError = class extends Error {
|
|
@@ -624,7 +680,7 @@ var ClawtError = class extends Error {
|
|
|
624
680
|
// src/logger/index.ts
|
|
625
681
|
import winston from "winston";
|
|
626
682
|
import DailyRotateFile from "winston-daily-rotate-file";
|
|
627
|
-
import
|
|
683
|
+
import chalk3 from "chalk";
|
|
628
684
|
import { existsSync, mkdirSync } from "fs";
|
|
629
685
|
if (!existsSync(LOGS_DIR)) {
|
|
630
686
|
mkdirSync(LOGS_DIR, { recursive: true });
|
|
@@ -649,13 +705,13 @@ var logger = winston.createLogger({
|
|
|
649
705
|
transports: [dailyRotateTransport]
|
|
650
706
|
});
|
|
651
707
|
var LEVEL_COLORS = {
|
|
652
|
-
error:
|
|
653
|
-
warn:
|
|
654
|
-
info:
|
|
655
|
-
debug:
|
|
708
|
+
error: chalk3.red,
|
|
709
|
+
warn: chalk3.yellow,
|
|
710
|
+
info: chalk3.cyan,
|
|
711
|
+
debug: chalk3.gray
|
|
656
712
|
};
|
|
657
713
|
function colorizeLevel(level) {
|
|
658
|
-
const colorFn = LEVEL_COLORS[level] ||
|
|
714
|
+
const colorFn = LEVEL_COLORS[level] || chalk3.white;
|
|
659
715
|
return colorFn(level.toUpperCase().padEnd(5));
|
|
660
716
|
}
|
|
661
717
|
function enableConsoleTransport() {
|
|
@@ -666,7 +722,7 @@ function enableConsoleTransport() {
|
|
|
666
722
|
return;
|
|
667
723
|
}
|
|
668
724
|
const consoleFormat = winston.format.printf(({ level, message, timestamp }) => {
|
|
669
|
-
return `${
|
|
725
|
+
return `${chalk3.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
|
|
670
726
|
});
|
|
671
727
|
const consoleTransport = new winston.transports.Console({
|
|
672
728
|
level: "debug",
|
|
@@ -940,22 +996,22 @@ function createBranch(branchName, cwd) {
|
|
|
940
996
|
}
|
|
941
997
|
|
|
942
998
|
// src/utils/formatter.ts
|
|
943
|
-
import
|
|
999
|
+
import chalk4 from "chalk";
|
|
944
1000
|
import { createInterface } from "readline";
|
|
945
1001
|
function printSuccess(message) {
|
|
946
|
-
console.log(
|
|
1002
|
+
console.log(chalk4.green(message));
|
|
947
1003
|
}
|
|
948
1004
|
function printError(message) {
|
|
949
|
-
console.error(
|
|
1005
|
+
console.error(chalk4.red(`\u2717 ${message}`));
|
|
950
1006
|
}
|
|
951
1007
|
function printWarning(message) {
|
|
952
|
-
console.log(
|
|
1008
|
+
console.log(chalk4.yellow(`\u26A0 ${message}`));
|
|
953
1009
|
}
|
|
954
1010
|
function printInfo(message) {
|
|
955
1011
|
console.log(message);
|
|
956
1012
|
}
|
|
957
1013
|
function printHint(message) {
|
|
958
|
-
console.log(
|
|
1014
|
+
console.log(chalk4.hex("#FF8C00")(message));
|
|
959
1015
|
}
|
|
960
1016
|
function printSeparator() {
|
|
961
1017
|
console.log(MESSAGES.SEPARATOR);
|
|
@@ -976,7 +1032,7 @@ function confirmAction(question) {
|
|
|
976
1032
|
});
|
|
977
1033
|
}
|
|
978
1034
|
function confirmDestructiveAction(dangerousCommand, description) {
|
|
979
|
-
printWarning(`\u5373\u5C06\u6267\u884C ${
|
|
1035
|
+
printWarning(`\u5373\u5C06\u6267\u884C ${chalk4.red.bold(dangerousCommand)}\uFF0C${description}`);
|
|
980
1036
|
return confirmAction("\u662F\u5426\u7EE7\u7EED\uFF1F");
|
|
981
1037
|
}
|
|
982
1038
|
function isWorktreeIdle(status) {
|
|
@@ -984,17 +1040,17 @@ function isWorktreeIdle(status) {
|
|
|
984
1040
|
}
|
|
985
1041
|
function formatWorktreeStatus(status) {
|
|
986
1042
|
const parts = [];
|
|
987
|
-
parts.push(
|
|
1043
|
+
parts.push(chalk4.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
|
|
988
1044
|
if (status.insertions === 0 && status.deletions === 0) {
|
|
989
1045
|
parts.push("\u65E0\u53D8\u66F4");
|
|
990
1046
|
} else {
|
|
991
1047
|
const diffParts = [];
|
|
992
|
-
diffParts.push(
|
|
993
|
-
diffParts.push(
|
|
1048
|
+
diffParts.push(chalk4.green(`+${status.insertions}`));
|
|
1049
|
+
diffParts.push(chalk4.red(`-${status.deletions}`));
|
|
994
1050
|
parts.push(diffParts.join(" "));
|
|
995
1051
|
}
|
|
996
1052
|
if (status.hasDirtyFiles) {
|
|
997
|
-
parts.push(
|
|
1053
|
+
parts.push(chalk4.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
|
|
998
1054
|
}
|
|
999
1055
|
return parts.join(" ");
|
|
1000
1056
|
}
|
|
@@ -1882,7 +1938,7 @@ async function promptGroupedMultiSelectBranches(worktrees, message) {
|
|
|
1882
1938
|
}
|
|
1883
1939
|
|
|
1884
1940
|
// src/utils/progress-render.ts
|
|
1885
|
-
import
|
|
1941
|
+
import chalk5 from "chalk";
|
|
1886
1942
|
import stringWidth from "string-width";
|
|
1887
1943
|
var ANSI_RESET = "\x1B[0m";
|
|
1888
1944
|
function truncateToTerminalWidth(text, maxWidth) {
|
|
@@ -1919,23 +1975,23 @@ function renderTaskLine(task, total, maxPathWidth, spinnerChar) {
|
|
|
1919
1975
|
const pathStr = task.path.padEnd(maxPathWidth);
|
|
1920
1976
|
switch (task.status) {
|
|
1921
1977
|
case "pending": {
|
|
1922
|
-
return `${indexStr} ${pathStr} ${
|
|
1978
|
+
return `${indexStr} ${pathStr} ${chalk5.gray(TASK_STATUS_ICONS.PENDING)} ${chalk5.gray(TASK_STATUS_LABELS.PENDING)}`;
|
|
1923
1979
|
}
|
|
1924
1980
|
case "running": {
|
|
1925
1981
|
const elapsed = formatDuration(Date.now() - task.startedAt);
|
|
1926
|
-
const detail = task.activity ? ` ${
|
|
1927
|
-
return `${indexStr} ${pathStr} ${
|
|
1982
|
+
const detail = task.activity ? ` ${chalk5.dim(task.activity)}` : "";
|
|
1983
|
+
return `${indexStr} ${pathStr} ${chalk5.cyan(spinnerChar)} ${chalk5.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk5.gray(elapsed)}${detail}`;
|
|
1928
1984
|
}
|
|
1929
1985
|
case "done": {
|
|
1930
1986
|
const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
|
|
1931
1987
|
const cost = task.costUsd != null ? `$${task.costUsd.toFixed(2)}` : "";
|
|
1932
|
-
const preview = task.resultPreview ? ` ${
|
|
1933
|
-
return `${indexStr} ${pathStr} ${
|
|
1988
|
+
const preview = task.resultPreview ? ` ${chalk5.dim(task.resultPreview)}` : "";
|
|
1989
|
+
return `${indexStr} ${pathStr} ${chalk5.green(TASK_STATUS_ICONS.DONE)} ${chalk5.green(TASK_STATUS_LABELS.DONE)} ${chalk5.gray(duration)} ${chalk5.yellow(cost)}${preview}`;
|
|
1934
1990
|
}
|
|
1935
1991
|
case "failed": {
|
|
1936
1992
|
const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
|
|
1937
|
-
const preview = task.resultPreview ? ` ${
|
|
1938
|
-
return `${indexStr} ${pathStr} ${
|
|
1993
|
+
const preview = task.resultPreview ? ` ${chalk5.dim(task.resultPreview)}` : "";
|
|
1994
|
+
return `${indexStr} ${pathStr} ${chalk5.red(TASK_STATUS_ICONS.FAILED)} ${chalk5.red(TASK_STATUS_LABELS.FAILED)} ${chalk5.gray(duration)}${preview}`;
|
|
1939
1995
|
}
|
|
1940
1996
|
}
|
|
1941
1997
|
}
|
|
@@ -1945,10 +2001,10 @@ function renderSummaryLine(tasks, total) {
|
|
|
1945
2001
|
const failed = tasks.filter((t) => t.status === "failed").length;
|
|
1946
2002
|
const pending = tasks.filter((t) => t.status === "pending").length;
|
|
1947
2003
|
const parts = [];
|
|
1948
|
-
if (running > 0) parts.push(
|
|
1949
|
-
if (done > 0) parts.push(
|
|
1950
|
-
if (failed > 0) parts.push(
|
|
1951
|
-
if (pending > 0) parts.push(
|
|
2004
|
+
if (running > 0) parts.push(chalk5.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
|
|
2005
|
+
if (done > 0) parts.push(chalk5.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
|
|
2006
|
+
if (failed > 0) parts.push(chalk5.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
|
|
2007
|
+
if (pending > 0) parts.push(chalk5.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
|
|
1952
2008
|
return `[${parts.join(", ")}]`;
|
|
1953
2009
|
}
|
|
1954
2010
|
|
|
@@ -2563,7 +2619,7 @@ async function executeBatchTasks(worktrees, tasks, concurrency) {
|
|
|
2563
2619
|
}
|
|
2564
2620
|
|
|
2565
2621
|
// src/utils/dry-run.ts
|
|
2566
|
-
import
|
|
2622
|
+
import chalk6 from "chalk";
|
|
2567
2623
|
import { join as join7 } from "path";
|
|
2568
2624
|
var DRY_RUN_TASK_DESC_MAX_LENGTH = 80;
|
|
2569
2625
|
function truncateTaskDesc(task) {
|
|
@@ -2577,7 +2633,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
|
|
|
2577
2633
|
const projectDir = getProjectWorktreeDir();
|
|
2578
2634
|
const isInteractive = tasks.length === 0;
|
|
2579
2635
|
printDoubleSeparator();
|
|
2580
|
-
printInfo(` ${
|
|
2636
|
+
printInfo(` ${chalk6.bold(MESSAGES.DRY_RUN_TITLE)}`);
|
|
2581
2637
|
printDoubleSeparator();
|
|
2582
2638
|
const summaryParts = [
|
|
2583
2639
|
MESSAGES.DRY_RUN_TASK_COUNT(branchNames.length),
|
|
@@ -2587,7 +2643,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
|
|
|
2587
2643
|
if (isInteractive) {
|
|
2588
2644
|
summaryParts.push(MESSAGES.DRY_RUN_INTERACTIVE_MODE);
|
|
2589
2645
|
}
|
|
2590
|
-
printInfo(summaryParts.join(
|
|
2646
|
+
printInfo(summaryParts.join(chalk6.gray(" \u2502 ")));
|
|
2591
2647
|
printSeparator();
|
|
2592
2648
|
let hasConflict = false;
|
|
2593
2649
|
for (let i = 0; i < branchNames.length; i++) {
|
|
@@ -2597,21 +2653,21 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
|
|
|
2597
2653
|
if (exists) hasConflict = true;
|
|
2598
2654
|
const indexLabel = `[${i + 1}/${branchNames.length}]`;
|
|
2599
2655
|
if (exists) {
|
|
2600
|
-
printInfo(`${
|
|
2656
|
+
printInfo(`${chalk6.yellow("\u26A0")} ${indexLabel} ${chalk6.yellow(branch)} ${chalk6.gray("\u2014")} ${chalk6.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
|
|
2601
2657
|
} else {
|
|
2602
|
-
printInfo(`${
|
|
2658
|
+
printInfo(`${chalk6.green("\u2713")} ${indexLabel} ${chalk6.cyan(branch)}`);
|
|
2603
2659
|
}
|
|
2604
|
-
printInfo(` ${
|
|
2660
|
+
printInfo(` ${chalk6.gray("\u8DEF\u5F84:")} ${worktreePath}`);
|
|
2605
2661
|
if (!isInteractive) {
|
|
2606
|
-
printInfo(` ${
|
|
2662
|
+
printInfo(` ${chalk6.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
|
|
2607
2663
|
}
|
|
2608
2664
|
printInfo("");
|
|
2609
2665
|
}
|
|
2610
2666
|
printDoubleSeparator();
|
|
2611
2667
|
if (hasConflict) {
|
|
2612
|
-
printInfo(
|
|
2668
|
+
printInfo(chalk6.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
|
|
2613
2669
|
} else {
|
|
2614
|
-
printInfo(
|
|
2670
|
+
printInfo(chalk6.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
|
|
2615
2671
|
}
|
|
2616
2672
|
}
|
|
2617
2673
|
|
|
@@ -2629,7 +2685,7 @@ function applyAliases(program2, aliases) {
|
|
|
2629
2685
|
}
|
|
2630
2686
|
|
|
2631
2687
|
// src/utils/config-strategy.ts
|
|
2632
|
-
import
|
|
2688
|
+
import chalk7 from "chalk";
|
|
2633
2689
|
import Enquirer4 from "enquirer";
|
|
2634
2690
|
function isValidConfigKey(key) {
|
|
2635
2691
|
return key in DEFAULT_CONFIG;
|
|
@@ -2673,9 +2729,9 @@ async function promptConfigValue(key, currentValue) {
|
|
|
2673
2729
|
}
|
|
2674
2730
|
function formatConfigValue(value) {
|
|
2675
2731
|
if (typeof value === "boolean") {
|
|
2676
|
-
return value ?
|
|
2732
|
+
return value ? chalk7.green("true") : chalk7.yellow("false");
|
|
2677
2733
|
}
|
|
2678
|
-
return
|
|
2734
|
+
return chalk7.cyan(String(value));
|
|
2679
2735
|
}
|
|
2680
2736
|
async function promptBooleanValue(key, currentValue) {
|
|
2681
2737
|
const choices = [
|
|
@@ -2722,7 +2778,7 @@ async function promptStringValue(key, currentValue) {
|
|
|
2722
2778
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
2723
2779
|
import { execSync as execSync3 } from "child_process";
|
|
2724
2780
|
import { request } from "https";
|
|
2725
|
-
import
|
|
2781
|
+
import chalk8 from "chalk";
|
|
2726
2782
|
import stringWidth2 from "string-width";
|
|
2727
2783
|
function readUpdateCache() {
|
|
2728
2784
|
try {
|
|
@@ -2797,12 +2853,12 @@ function detectPackageManager() {
|
|
|
2797
2853
|
}
|
|
2798
2854
|
function printUpdateNotification(currentVersion, latestVersion) {
|
|
2799
2855
|
const updateText = UPDATE_MESSAGES.UPDATE_AVAILABLE(
|
|
2800
|
-
|
|
2801
|
-
|
|
2856
|
+
chalk8.red(currentVersion),
|
|
2857
|
+
chalk8.green(latestVersion)
|
|
2802
2858
|
);
|
|
2803
2859
|
const pm = detectPackageManager();
|
|
2804
2860
|
const updateCommand = UPDATE_COMMANDS[pm] || UPDATE_COMMANDS.npm;
|
|
2805
|
-
const commandText = UPDATE_MESSAGES.UPDATE_HINT(
|
|
2861
|
+
const commandText = UPDATE_MESSAGES.UPDATE_HINT(chalk8.cyan(updateCommand));
|
|
2806
2862
|
const updateTextWidth = stringWidth2(updateText);
|
|
2807
2863
|
const commandTextWidth = stringWidth2(commandText);
|
|
2808
2864
|
const innerWidth = Math.max(updateTextWidth, commandTextWidth) + 4;
|
|
@@ -2899,8 +2955,590 @@ function safeStringify(value, indent = 2) {
|
|
|
2899
2955
|
}
|
|
2900
2956
|
}
|
|
2901
2957
|
|
|
2958
|
+
// src/utils/interactive-panel.ts
|
|
2959
|
+
import { createInterface as createInterface2 } from "readline";
|
|
2960
|
+
|
|
2961
|
+
// src/utils/interactive-panel-render.ts
|
|
2962
|
+
import chalk9 from "chalk";
|
|
2963
|
+
import stringWidth3 from "string-width";
|
|
2964
|
+
function buildSeparatorWithHint(cols, hint) {
|
|
2965
|
+
const maxWidth = Math.min(cols, 60);
|
|
2966
|
+
if (!hint) {
|
|
2967
|
+
return chalk9.gray("\u2500".repeat(maxWidth));
|
|
2968
|
+
}
|
|
2969
|
+
const hintWidth = stringWidth3(hint);
|
|
2970
|
+
const remaining = Math.max(maxWidth - 2 - hintWidth, 0);
|
|
2971
|
+
const leftLen = Math.floor(remaining / 2);
|
|
2972
|
+
const rightLen = remaining - leftLen;
|
|
2973
|
+
return `${chalk9.gray("\u2500".repeat(leftLen))} ${hint} ${chalk9.gray("\u2500".repeat(rightLen))}`;
|
|
2974
|
+
}
|
|
2975
|
+
function buildPanelFrame(statusResult, selectedIndex, scrollOffset, rows, cols, countdown) {
|
|
2976
|
+
const lines = [];
|
|
2977
|
+
lines.push(PANEL_TITLE(statusResult.main.projectName));
|
|
2978
|
+
lines.push(renderSnapshotSummary(statusResult.snapshots.total, statusResult.snapshots.orphaned));
|
|
2979
|
+
const visibleRows = calculateVisibleRows(rows);
|
|
2980
|
+
if (statusResult.worktrees.length === 0) {
|
|
2981
|
+
lines.push(buildSeparatorWithHint(cols, ""));
|
|
2982
|
+
lines.push(PANEL_NO_WORKTREES);
|
|
2983
|
+
lines.push(buildSeparatorWithHint(cols, ""));
|
|
2984
|
+
} else {
|
|
2985
|
+
const panelLines = buildGroupedWorktreeLines(statusResult.worktrees, selectedIndex);
|
|
2986
|
+
const hasOverflowUp = scrollOffset > 0;
|
|
2987
|
+
const hasOverflowDown = scrollOffset + visibleRows < panelLines.length;
|
|
2988
|
+
lines.push(buildSeparatorWithHint(cols, hasOverflowUp ? PANEL_OVERFLOW_UP_HINT : ""));
|
|
2989
|
+
const visibleLines = panelLines.slice(scrollOffset, scrollOffset + visibleRows);
|
|
2990
|
+
for (const pl of visibleLines) {
|
|
2991
|
+
lines.push(pl.text);
|
|
2992
|
+
}
|
|
2993
|
+
lines.push(buildSeparatorWithHint(cols, hasOverflowDown ? PANEL_OVERFLOW_DOWN_HINT : ""));
|
|
2994
|
+
}
|
|
2995
|
+
lines.push(renderFooter(countdown));
|
|
2996
|
+
return lines;
|
|
2997
|
+
}
|
|
2998
|
+
function buildDisplayOrder(worktrees) {
|
|
2999
|
+
const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch }));
|
|
3000
|
+
const groups = groupWorktreesByDate(worktreeInfos);
|
|
3001
|
+
const branchIndexMap = /* @__PURE__ */ new Map();
|
|
3002
|
+
for (let i = 0; i < worktrees.length; i++) {
|
|
3003
|
+
branchIndexMap.set(worktrees[i].branch, i);
|
|
3004
|
+
}
|
|
3005
|
+
const displayOrder = [];
|
|
3006
|
+
for (const [, groupWorktrees] of groups) {
|
|
3007
|
+
for (const gwt of groupWorktrees) {
|
|
3008
|
+
displayOrder.push(branchIndexMap.get(gwt.branch));
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
return displayOrder;
|
|
3012
|
+
}
|
|
3013
|
+
function buildGroupedWorktreeLines(worktrees, selectedIndex) {
|
|
3014
|
+
const panelLines = [];
|
|
3015
|
+
const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch }));
|
|
3016
|
+
const groups = groupWorktreesByDate(worktreeInfos);
|
|
3017
|
+
const branchIndexMap = /* @__PURE__ */ new Map();
|
|
3018
|
+
for (let i = 0; i < worktrees.length; i++) {
|
|
3019
|
+
branchIndexMap.set(worktrees[i].branch, i);
|
|
3020
|
+
}
|
|
3021
|
+
for (const [dateKey, groupWorktrees] of groups) {
|
|
3022
|
+
panelLines.push({
|
|
3023
|
+
type: "separator",
|
|
3024
|
+
text: renderDateSeparator(dateKey)
|
|
3025
|
+
});
|
|
3026
|
+
panelLines.push({
|
|
3027
|
+
type: "separator",
|
|
3028
|
+
text: ""
|
|
3029
|
+
});
|
|
3030
|
+
for (const gwt of groupWorktrees) {
|
|
3031
|
+
const wtIndex = branchIndexMap.get(gwt.branch);
|
|
3032
|
+
const wt = worktrees[wtIndex];
|
|
3033
|
+
const isSelected = wtIndex === selectedIndex;
|
|
3034
|
+
const blockLines = renderWorktreeBlock(wt, isSelected);
|
|
3035
|
+
for (const line of blockLines) {
|
|
3036
|
+
panelLines.push({
|
|
3037
|
+
type: "worktree-content",
|
|
3038
|
+
text: line,
|
|
3039
|
+
worktreeIndex: wtIndex
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
return panelLines;
|
|
3045
|
+
}
|
|
3046
|
+
function renderDateSeparator(dateKey) {
|
|
3047
|
+
const leftPad = " ";
|
|
3048
|
+
if (dateKey === UNKNOWN_DATE_GROUP) {
|
|
3049
|
+
return `${leftPad}${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)} ${chalk9.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} ${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)}`;
|
|
3050
|
+
}
|
|
3051
|
+
const relativeDate = formatRelativeDate(dateKey);
|
|
3052
|
+
return `${leftPad}${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)} ${chalk9.bold.hex("#FF8C00")(dateKey)}${chalk9.hex("#FF8C00")(`\uFF08${relativeDate}\uFF09`)} ${chalk9.gray(PANEL_DATE_SEPARATOR_PREFIX)}`;
|
|
3053
|
+
}
|
|
3054
|
+
function renderWorktreeBlock(wt, isSelected) {
|
|
3055
|
+
const lines = [];
|
|
3056
|
+
const indicator = isSelected ? chalk9.cyan(SELECTED_INDICATOR) : UNSELECTED_INDICATOR;
|
|
3057
|
+
const branchStyle = isSelected ? chalk9.bold.cyan : chalk9.bold;
|
|
3058
|
+
const statusLabel = formatChangeStatusLabel(wt.changeStatus);
|
|
3059
|
+
lines.push(`${indicator} ${branchStyle(wt.branch)} [${statusLabel}]`);
|
|
3060
|
+
const indent = " ";
|
|
3061
|
+
if (wt.insertions > 0 || wt.deletions > 0) {
|
|
3062
|
+
lines.push(`${indent}${chalk9.green(`+${wt.insertions}`)} ${chalk9.red(`-${wt.deletions}`)}`);
|
|
3063
|
+
}
|
|
3064
|
+
if (wt.commitsAhead > 0) {
|
|
3065
|
+
lines.push(`${indent}${chalk9.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
|
|
3066
|
+
}
|
|
3067
|
+
if (wt.commitsBehind > 0) {
|
|
3068
|
+
lines.push(`${indent}${chalk9.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
|
|
3069
|
+
} else {
|
|
3070
|
+
lines.push(`${indent}${chalk9.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
|
|
3071
|
+
}
|
|
3072
|
+
if (wt.createdAt) {
|
|
3073
|
+
const relativeTime = formatRelativeTime(wt.createdAt);
|
|
3074
|
+
if (relativeTime) {
|
|
3075
|
+
lines.push(`${indent}${chalk9.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
if (wt.snapshotTime) {
|
|
3079
|
+
const relativeTime = formatRelativeTime(wt.snapshotTime);
|
|
3080
|
+
if (relativeTime) {
|
|
3081
|
+
lines.push(`${indent}${chalk9.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
|
|
3082
|
+
}
|
|
3083
|
+
} else {
|
|
3084
|
+
lines.push(`${indent}${chalk9.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
|
|
3085
|
+
}
|
|
3086
|
+
lines.push("");
|
|
3087
|
+
return lines;
|
|
3088
|
+
}
|
|
3089
|
+
function formatChangeStatusLabel(status) {
|
|
3090
|
+
switch (status) {
|
|
3091
|
+
case "committed":
|
|
3092
|
+
return chalk9.green(MESSAGES.STATUS_CHANGE_COMMITTED);
|
|
3093
|
+
case "uncommitted":
|
|
3094
|
+
return chalk9.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
|
|
3095
|
+
case "conflict":
|
|
3096
|
+
return chalk9.red(MESSAGES.STATUS_CHANGE_CONFLICT);
|
|
3097
|
+
case "clean":
|
|
3098
|
+
return chalk9.gray(MESSAGES.STATUS_CHANGE_CLEAN);
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
function renderSnapshotSummary(total, orphaned) {
|
|
3102
|
+
return PANEL_SNAPSHOT_SUMMARY(total, orphaned);
|
|
3103
|
+
}
|
|
3104
|
+
function renderFooter(countdown) {
|
|
3105
|
+
return `${PANEL_FOOTER_SHORTCUTS} ${PANEL_FOOTER_COUNTDOWN(countdown)}`;
|
|
3106
|
+
}
|
|
3107
|
+
function calculateVisibleRows(terminalRows) {
|
|
3108
|
+
const fixedRows = PANEL_FIXED_ROWS + 1;
|
|
3109
|
+
return Math.max(terminalRows - fixedRows, 3);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// src/utils/interactive-panel.ts
|
|
3113
|
+
var InteractivePanel = class {
|
|
3114
|
+
/** 当前状态数据 */
|
|
3115
|
+
statusResult;
|
|
3116
|
+
/** 当前选中的显示位置索引(对应 displayOrder 数组的下标) */
|
|
3117
|
+
selectedDisplayIndex;
|
|
3118
|
+
/** 显示顺序到原始索引的映射(按日期分组后的排列顺序) */
|
|
3119
|
+
displayOrder;
|
|
3120
|
+
/** 滚动偏移(基于行数) */
|
|
3121
|
+
scrollOffset;
|
|
3122
|
+
/** 数据刷新定时器引用 */
|
|
3123
|
+
refreshTimer;
|
|
3124
|
+
/** 倒计时定时器引用 */
|
|
3125
|
+
countdownTimer;
|
|
3126
|
+
/** 刷新倒计时剩余秒数 */
|
|
3127
|
+
refreshCountdown;
|
|
3128
|
+
/** 是否已停止 */
|
|
3129
|
+
stopped;
|
|
3130
|
+
/** 是否为 TTY 环境 */
|
|
3131
|
+
isTTY;
|
|
3132
|
+
/** resize 事件处理器引用 */
|
|
3133
|
+
resizeHandler;
|
|
3134
|
+
/** exit 兜底处理器 */
|
|
3135
|
+
exitHandler;
|
|
3136
|
+
/** stdin 数据处理器引用(用于清理) */
|
|
3137
|
+
stdinDataHandler;
|
|
3138
|
+
/** 操作锁(防止操作期间响应按键) */
|
|
3139
|
+
isOperating;
|
|
3140
|
+
/** Promise resolve 函数(stop 时调用以完成 start 返回的 Promise) */
|
|
3141
|
+
resolveStart;
|
|
3142
|
+
/** 数据收集函数引用 */
|
|
3143
|
+
collectStatusFn;
|
|
3144
|
+
/**
|
|
3145
|
+
* 创建交互式面板
|
|
3146
|
+
* @param {() => StatusResult} collectStatusFn - 数据收集函数
|
|
3147
|
+
*/
|
|
3148
|
+
constructor(collectStatusFn) {
|
|
3149
|
+
this.statusResult = null;
|
|
3150
|
+
this.selectedDisplayIndex = 0;
|
|
3151
|
+
this.displayOrder = [];
|
|
3152
|
+
this.scrollOffset = 0;
|
|
3153
|
+
this.refreshTimer = null;
|
|
3154
|
+
this.countdownTimer = null;
|
|
3155
|
+
this.refreshCountdown = PANEL_REFRESH_INTERVAL_MS / 1e3;
|
|
3156
|
+
this.stopped = false;
|
|
3157
|
+
this.isTTY = !!process.stdout.isTTY;
|
|
3158
|
+
this.resizeHandler = null;
|
|
3159
|
+
this.exitHandler = null;
|
|
3160
|
+
this.stdinDataHandler = null;
|
|
3161
|
+
this.isOperating = false;
|
|
3162
|
+
this.resolveStart = null;
|
|
3163
|
+
this.collectStatusFn = collectStatusFn;
|
|
3164
|
+
}
|
|
3165
|
+
/**
|
|
3166
|
+
* 启动交互式面板
|
|
3167
|
+
* 非 TTY 时打印提示并退出
|
|
3168
|
+
* @returns {Promise<void>} 面板关闭时 resolve
|
|
3169
|
+
*/
|
|
3170
|
+
start() {
|
|
3171
|
+
if (!this.isTTY) {
|
|
3172
|
+
console.log(PANEL_NOT_TTY);
|
|
3173
|
+
return Promise.resolve();
|
|
3174
|
+
}
|
|
3175
|
+
return new Promise((resolve3) => {
|
|
3176
|
+
this.resolveStart = resolve3;
|
|
3177
|
+
this.statusResult = this.collectStatusFn();
|
|
3178
|
+
this.displayOrder = buildDisplayOrder(this.statusResult.worktrees);
|
|
3179
|
+
this.initTerminal();
|
|
3180
|
+
this.startKeyboardListener();
|
|
3181
|
+
this.startAutoRefresh();
|
|
3182
|
+
this.render();
|
|
3183
|
+
});
|
|
3184
|
+
}
|
|
3185
|
+
/**
|
|
3186
|
+
* 停止面板,恢复终端状态
|
|
3187
|
+
*/
|
|
3188
|
+
stop() {
|
|
3189
|
+
if (this.stopped) return;
|
|
3190
|
+
this.stopped = true;
|
|
3191
|
+
this.clearTimers();
|
|
3192
|
+
this.stopKeyboardListener();
|
|
3193
|
+
this.restoreTerminal();
|
|
3194
|
+
if (this.resizeHandler) {
|
|
3195
|
+
process.stdout.removeListener("resize", this.resizeHandler);
|
|
3196
|
+
this.resizeHandler = null;
|
|
3197
|
+
}
|
|
3198
|
+
if (this.exitHandler) {
|
|
3199
|
+
process.removeListener("exit", this.exitHandler);
|
|
3200
|
+
this.exitHandler = null;
|
|
3201
|
+
}
|
|
3202
|
+
if (this.resolveStart) {
|
|
3203
|
+
this.resolveStart();
|
|
3204
|
+
this.resolveStart = null;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
/**
|
|
3208
|
+
* 初始化终端:进入备选屏幕、隐藏光标、禁用行换行
|
|
3209
|
+
*/
|
|
3210
|
+
initTerminal() {
|
|
3211
|
+
process.stdout.write(ALT_SCREEN_ENTER);
|
|
3212
|
+
process.stdout.write(CURSOR_HIDE);
|
|
3213
|
+
process.stdout.write(LINE_WRAP_DISABLE);
|
|
3214
|
+
this.resizeHandler = () => {
|
|
3215
|
+
if (!this.stopped && !this.isOperating) {
|
|
3216
|
+
this.render();
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
process.stdout.on("resize", this.resizeHandler);
|
|
3220
|
+
this.exitHandler = () => {
|
|
3221
|
+
process.stdout.write(LINE_WRAP_ENABLE);
|
|
3222
|
+
process.stdout.write(CURSOR_SHOW);
|
|
3223
|
+
process.stdout.write(ALT_SCREEN_LEAVE);
|
|
3224
|
+
};
|
|
3225
|
+
process.on("exit", this.exitHandler);
|
|
3226
|
+
}
|
|
3227
|
+
/**
|
|
3228
|
+
* 恢复终端:启用行换行、显示光标、退出备选屏幕
|
|
3229
|
+
*/
|
|
3230
|
+
restoreTerminal() {
|
|
3231
|
+
process.stdout.write(LINE_WRAP_ENABLE);
|
|
3232
|
+
process.stdout.write(CURSOR_SHOW);
|
|
3233
|
+
process.stdout.write(ALT_SCREEN_LEAVE);
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* 启动键盘监听
|
|
3237
|
+
* 将 stdin 设为 raw 模式以捕获每个按键
|
|
3238
|
+
*/
|
|
3239
|
+
startKeyboardListener() {
|
|
3240
|
+
if (process.stdin.isTTY) {
|
|
3241
|
+
process.stdin.setRawMode(true);
|
|
3242
|
+
}
|
|
3243
|
+
process.stdin.resume();
|
|
3244
|
+
this.stdinDataHandler = (data) => {
|
|
3245
|
+
this.handleKeypress(data);
|
|
3246
|
+
};
|
|
3247
|
+
process.stdin.on("data", this.stdinDataHandler);
|
|
3248
|
+
}
|
|
3249
|
+
/**
|
|
3250
|
+
* 停止键盘监听,恢复 stdin 状态
|
|
3251
|
+
*/
|
|
3252
|
+
stopKeyboardListener() {
|
|
3253
|
+
if (this.stdinDataHandler) {
|
|
3254
|
+
process.stdin.removeListener("data", this.stdinDataHandler);
|
|
3255
|
+
this.stdinDataHandler = null;
|
|
3256
|
+
}
|
|
3257
|
+
if (process.stdin.isTTY) {
|
|
3258
|
+
process.stdin.setRawMode(false);
|
|
3259
|
+
}
|
|
3260
|
+
process.stdin.pause();
|
|
3261
|
+
}
|
|
3262
|
+
/**
|
|
3263
|
+
* 处理键盘输入
|
|
3264
|
+
* @param {Buffer} data - 按键数据
|
|
3265
|
+
*/
|
|
3266
|
+
handleKeypress(data) {
|
|
3267
|
+
if (this.isOperating) return;
|
|
3268
|
+
const str = data.toString();
|
|
3269
|
+
if (data[0] === KEY_CTRL_C) {
|
|
3270
|
+
this.stop();
|
|
3271
|
+
return;
|
|
3272
|
+
}
|
|
3273
|
+
if (str === KEY_ARROW_UP) {
|
|
3274
|
+
this.navigateUp();
|
|
3275
|
+
return;
|
|
3276
|
+
}
|
|
3277
|
+
if (str === KEY_ARROW_DOWN) {
|
|
3278
|
+
this.navigateDown();
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
const key = str.toLowerCase();
|
|
3282
|
+
if (key === PANEL_SHORTCUT_KEYS.QUIT) {
|
|
3283
|
+
this.stop();
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
if (key === PANEL_SHORTCUT_KEYS.REFRESH) {
|
|
3287
|
+
this.refreshData();
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3290
|
+
if (key === PANEL_SHORTCUT_KEYS.VALIDATE) {
|
|
3291
|
+
this.executeOperation(() => this.handleValidate());
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
if (key === PANEL_SHORTCUT_KEYS.MERGE) {
|
|
3295
|
+
this.executeOperation(() => this.handleMerge());
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
if (key === PANEL_SHORTCUT_KEYS.DELETE) {
|
|
3299
|
+
this.executeOperation(() => this.handleDelete());
|
|
3300
|
+
return;
|
|
3301
|
+
}
|
|
3302
|
+
if (key === PANEL_SHORTCUT_KEYS.RESUME) {
|
|
3303
|
+
this.executeOperation(() => this.handleResume());
|
|
3304
|
+
return;
|
|
3305
|
+
}
|
|
3306
|
+
if (key === PANEL_SHORTCUT_KEYS.SYNC) {
|
|
3307
|
+
this.executeOperation(() => this.handleSync());
|
|
3308
|
+
return;
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
/**
|
|
3312
|
+
* 向上导航,选中显示顺序中的上一个 worktree
|
|
3313
|
+
*/
|
|
3314
|
+
navigateUp() {
|
|
3315
|
+
if (!this.statusResult || this.displayOrder.length === 0) return;
|
|
3316
|
+
if (this.selectedDisplayIndex > 0) {
|
|
3317
|
+
this.selectedDisplayIndex--;
|
|
3318
|
+
this.adjustScrollForSelection();
|
|
3319
|
+
this.render();
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
/**
|
|
3323
|
+
* 向下导航,选中显示顺序中的下一个 worktree
|
|
3324
|
+
*/
|
|
3325
|
+
navigateDown() {
|
|
3326
|
+
if (!this.statusResult || this.displayOrder.length === 0) return;
|
|
3327
|
+
if (this.selectedDisplayIndex < this.displayOrder.length - 1) {
|
|
3328
|
+
this.selectedDisplayIndex++;
|
|
3329
|
+
this.adjustScrollForSelection();
|
|
3330
|
+
this.render();
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
/**
|
|
3334
|
+
* 获取当前选中的原始 worktree 索引
|
|
3335
|
+
* @returns {number} 原始 worktrees 数组中的索引
|
|
3336
|
+
*/
|
|
3337
|
+
getSelectedOriginalIndex() {
|
|
3338
|
+
return this.displayOrder[this.selectedDisplayIndex];
|
|
3339
|
+
}
|
|
3340
|
+
/**
|
|
3341
|
+
* 调整滚动偏移以确保选中项在可见区域内
|
|
3342
|
+
*/
|
|
3343
|
+
adjustScrollForSelection() {
|
|
3344
|
+
if (!this.statusResult || this.displayOrder.length === 0) return;
|
|
3345
|
+
const originalIndex = this.getSelectedOriginalIndex();
|
|
3346
|
+
const rows = process.stdout.rows || 24;
|
|
3347
|
+
const visibleRows = calculateVisibleRows(rows);
|
|
3348
|
+
const panelLines = buildGroupedWorktreeLines(this.statusResult.worktrees, originalIndex);
|
|
3349
|
+
let firstLine = -1;
|
|
3350
|
+
let lastLine = -1;
|
|
3351
|
+
for (let i = 0; i < panelLines.length; i++) {
|
|
3352
|
+
if (panelLines[i].worktreeIndex === originalIndex) {
|
|
3353
|
+
if (firstLine === -1) firstLine = i;
|
|
3354
|
+
lastLine = i;
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
if (firstLine === -1) return;
|
|
3358
|
+
let groupStart = firstLine;
|
|
3359
|
+
while (groupStart > 0 && panelLines[groupStart - 1].type === "separator") {
|
|
3360
|
+
groupStart--;
|
|
3361
|
+
}
|
|
3362
|
+
if (groupStart < this.scrollOffset) {
|
|
3363
|
+
this.scrollOffset = groupStart;
|
|
3364
|
+
}
|
|
3365
|
+
if (lastLine >= this.scrollOffset + visibleRows) {
|
|
3366
|
+
this.scrollOffset = lastLine - visibleRows + 1;
|
|
3367
|
+
}
|
|
3368
|
+
if (this.scrollOffset > groupStart) {
|
|
3369
|
+
this.scrollOffset = groupStart;
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
/**
|
|
3373
|
+
* 启动自动刷新:数据刷新定时器 + 倒计时定时器
|
|
3374
|
+
*/
|
|
3375
|
+
startAutoRefresh() {
|
|
3376
|
+
this.refreshCountdown = PANEL_REFRESH_INTERVAL_MS / 1e3;
|
|
3377
|
+
this.refreshTimer = setInterval(() => {
|
|
3378
|
+
this.refreshData();
|
|
3379
|
+
}, PANEL_REFRESH_INTERVAL_MS);
|
|
3380
|
+
this.countdownTimer = setInterval(() => {
|
|
3381
|
+
if (this.refreshCountdown > 0) {
|
|
3382
|
+
this.refreshCountdown--;
|
|
3383
|
+
}
|
|
3384
|
+
this.render();
|
|
3385
|
+
}, PANEL_COUNTDOWN_INTERVAL_MS);
|
|
3386
|
+
if (this.refreshTimer.unref) this.refreshTimer.unref();
|
|
3387
|
+
if (this.countdownTimer.unref) this.countdownTimer.unref();
|
|
3388
|
+
}
|
|
3389
|
+
/**
|
|
3390
|
+
* 清除所有定时器
|
|
3391
|
+
*/
|
|
3392
|
+
clearTimers() {
|
|
3393
|
+
if (this.refreshTimer) {
|
|
3394
|
+
clearInterval(this.refreshTimer);
|
|
3395
|
+
this.refreshTimer = null;
|
|
3396
|
+
}
|
|
3397
|
+
if (this.countdownTimer) {
|
|
3398
|
+
clearInterval(this.countdownTimer);
|
|
3399
|
+
this.countdownTimer = null;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
/**
|
|
3403
|
+
* 刷新数据:记录当前选中分支 → 重新收集 → 恢复选中位置 → 重置倒计时 → 重绘
|
|
3404
|
+
*/
|
|
3405
|
+
refreshData() {
|
|
3406
|
+
if (this.stopped || this.isOperating) return;
|
|
3407
|
+
const originalIndex = this.displayOrder[this.selectedDisplayIndex];
|
|
3408
|
+
const previousBranch = this.statusResult?.worktrees[originalIndex]?.branch;
|
|
3409
|
+
this.statusResult = this.collectStatusFn();
|
|
3410
|
+
this.displayOrder = buildDisplayOrder(this.statusResult.worktrees);
|
|
3411
|
+
this.restoreSelection(previousBranch);
|
|
3412
|
+
this.refreshCountdown = PANEL_REFRESH_INTERVAL_MS / 1e3;
|
|
3413
|
+
this.render();
|
|
3414
|
+
}
|
|
3415
|
+
/**
|
|
3416
|
+
* 按分支名恢复选中位置(基于显示顺序)
|
|
3417
|
+
* @param {string | undefined} previousBranch - 之前选中的分支名
|
|
3418
|
+
*/
|
|
3419
|
+
restoreSelection(previousBranch) {
|
|
3420
|
+
if (!this.statusResult || !previousBranch || this.displayOrder.length === 0) {
|
|
3421
|
+
this.selectedDisplayIndex = 0;
|
|
3422
|
+
return;
|
|
3423
|
+
}
|
|
3424
|
+
const newDisplayIndex = this.displayOrder.findIndex(
|
|
3425
|
+
(origIdx) => this.statusResult.worktrees[origIdx]?.branch === previousBranch
|
|
3426
|
+
);
|
|
3427
|
+
if (newDisplayIndex >= 0) {
|
|
3428
|
+
this.selectedDisplayIndex = newDisplayIndex;
|
|
3429
|
+
} else {
|
|
3430
|
+
this.selectedDisplayIndex = Math.min(this.selectedDisplayIndex, Math.max(0, this.displayOrder.length - 1));
|
|
3431
|
+
}
|
|
3432
|
+
this.adjustScrollForSelection();
|
|
3433
|
+
}
|
|
3434
|
+
/**
|
|
3435
|
+
* 渲染一帧面板内容
|
|
3436
|
+
* 使用同步输出防止闪烁
|
|
3437
|
+
*/
|
|
3438
|
+
render() {
|
|
3439
|
+
if (this.stopped || this.isOperating || !this.statusResult) return;
|
|
3440
|
+
const cols = process.stdout.columns || DEFAULT_TERMINAL_COLUMNS;
|
|
3441
|
+
const rows = process.stdout.rows || 24;
|
|
3442
|
+
const frameLines = buildPanelFrame(
|
|
3443
|
+
this.statusResult,
|
|
3444
|
+
this.getSelectedOriginalIndex(),
|
|
3445
|
+
this.scrollOffset,
|
|
3446
|
+
rows,
|
|
3447
|
+
cols,
|
|
3448
|
+
this.refreshCountdown
|
|
3449
|
+
);
|
|
3450
|
+
process.stdout.write(SYNC_OUTPUT_START);
|
|
3451
|
+
process.stdout.write(CLEAR_SCREEN);
|
|
3452
|
+
process.stdout.write(CURSOR_HOME);
|
|
3453
|
+
for (let i = 0; i < frameLines.length; i++) {
|
|
3454
|
+
const suffix = i < frameLines.length - 1 ? "\n" : "";
|
|
3455
|
+
process.stdout.write(`${truncateToTerminalWidth(frameLines[i], cols)}${suffix}`);
|
|
3456
|
+
}
|
|
3457
|
+
process.stdout.write(SYNC_OUTPUT_END);
|
|
3458
|
+
}
|
|
3459
|
+
/**
|
|
3460
|
+
* 执行操作:暂停面板 → 恢复终端 → 执行命令 → 等待回车 → 恢复面板
|
|
3461
|
+
* @param {() => void} action - 要执行的操作
|
|
3462
|
+
*/
|
|
3463
|
+
async executeOperation(action) {
|
|
3464
|
+
if (!this.statusResult || this.displayOrder.length === 0) return;
|
|
3465
|
+
this.isOperating = true;
|
|
3466
|
+
this.clearTimers();
|
|
3467
|
+
this.restoreTerminal();
|
|
3468
|
+
this.stopKeyboardListener();
|
|
3469
|
+
action();
|
|
3470
|
+
console.log(PANEL_PRESS_ENTER_TO_RETURN);
|
|
3471
|
+
await this.waitForEnter();
|
|
3472
|
+
this.initTerminal();
|
|
3473
|
+
this.startKeyboardListener();
|
|
3474
|
+
this.isOperating = false;
|
|
3475
|
+
this.refreshData();
|
|
3476
|
+
this.startAutoRefresh();
|
|
3477
|
+
this.render();
|
|
3478
|
+
}
|
|
3479
|
+
/**
|
|
3480
|
+
* 获取当前选中的分支名
|
|
3481
|
+
* @returns {string} 当前选中的分支名
|
|
3482
|
+
*/
|
|
3483
|
+
getSelectedBranch() {
|
|
3484
|
+
const originalIndex = this.getSelectedOriginalIndex();
|
|
3485
|
+
return this.statusResult.worktrees[originalIndex].branch;
|
|
3486
|
+
}
|
|
3487
|
+
/**
|
|
3488
|
+
* 执行验证操作
|
|
3489
|
+
*/
|
|
3490
|
+
handleValidate() {
|
|
3491
|
+
const branch = this.getSelectedBranch();
|
|
3492
|
+
runCommandInherited(`clawt validate -b ${branch}`);
|
|
3493
|
+
}
|
|
3494
|
+
/**
|
|
3495
|
+
* 执行合并操作
|
|
3496
|
+
*/
|
|
3497
|
+
handleMerge() {
|
|
3498
|
+
const branch = this.getSelectedBranch();
|
|
3499
|
+
runCommandInherited(`clawt merge -b ${branch}`);
|
|
3500
|
+
}
|
|
3501
|
+
/**
|
|
3502
|
+
* 执行删除操作
|
|
3503
|
+
*/
|
|
3504
|
+
handleDelete() {
|
|
3505
|
+
const branch = this.getSelectedBranch();
|
|
3506
|
+
runCommandInherited(`clawt remove -b ${branch}`);
|
|
3507
|
+
}
|
|
3508
|
+
/**
|
|
3509
|
+
* 执行恢复操作
|
|
3510
|
+
*/
|
|
3511
|
+
handleResume() {
|
|
3512
|
+
const branch = this.getSelectedBranch();
|
|
3513
|
+
runCommandInherited(`clawt resume -b ${branch}`);
|
|
3514
|
+
}
|
|
3515
|
+
/**
|
|
3516
|
+
* 执行同步操作
|
|
3517
|
+
*/
|
|
3518
|
+
handleSync() {
|
|
3519
|
+
const branch = this.getSelectedBranch();
|
|
3520
|
+
runCommandInherited(`clawt sync -b ${branch}`);
|
|
3521
|
+
}
|
|
3522
|
+
/**
|
|
3523
|
+
* 等待用户按回车键
|
|
3524
|
+
* @returns {Promise<void>} 用户按回车时 resolve
|
|
3525
|
+
*/
|
|
3526
|
+
waitForEnter() {
|
|
3527
|
+
return new Promise((resolve3) => {
|
|
3528
|
+
const rl = createInterface2({
|
|
3529
|
+
input: process.stdin,
|
|
3530
|
+
output: process.stdout
|
|
3531
|
+
});
|
|
3532
|
+
rl.once("line", () => {
|
|
3533
|
+
rl.close();
|
|
3534
|
+
resolve3();
|
|
3535
|
+
});
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
};
|
|
3539
|
+
|
|
2902
3540
|
// src/commands/list.ts
|
|
2903
|
-
import
|
|
3541
|
+
import chalk10 from "chalk";
|
|
2904
3542
|
function registerListCommand(program2) {
|
|
2905
3543
|
program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
|
|
2906
3544
|
handleList(options);
|
|
@@ -2937,12 +3575,12 @@ function printListAsText(projectName, worktrees) {
|
|
|
2937
3575
|
for (const wt of worktrees) {
|
|
2938
3576
|
const status = getWorktreeStatus(wt);
|
|
2939
3577
|
const isIdle = status ? isWorktreeIdle(status) : false;
|
|
2940
|
-
const pathDisplay = isIdle ?
|
|
3578
|
+
const pathDisplay = isIdle ? chalk10.hex("#FF8C00")(wt.path) : wt.path;
|
|
2941
3579
|
printInfo(` ${pathDisplay} [${wt.branch}]`);
|
|
2942
3580
|
if (status) {
|
|
2943
3581
|
printInfo(` ${formatWorktreeStatus(status)}`);
|
|
2944
3582
|
} else {
|
|
2945
|
-
printInfo(` ${
|
|
3583
|
+
printInfo(` ${chalk10.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
|
|
2946
3584
|
}
|
|
2947
3585
|
printInfo("");
|
|
2948
3586
|
}
|
|
@@ -2993,6 +3631,7 @@ function registerRemoveCommand(program2) {
|
|
|
2993
3631
|
}
|
|
2994
3632
|
async function handleRemove(options) {
|
|
2995
3633
|
validateMainWorktree();
|
|
3634
|
+
requireProjectConfig();
|
|
2996
3635
|
const projectName = getProjectName();
|
|
2997
3636
|
logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
|
|
2998
3637
|
const allWorktrees = getProjectWorktrees();
|
|
@@ -3022,7 +3661,6 @@ async function handleRemove(options) {
|
|
|
3022
3661
|
const failures = [];
|
|
3023
3662
|
for (const wt of worktreesToRemove) {
|
|
3024
3663
|
try {
|
|
3025
|
-
await ensureOnMainWorkBranch();
|
|
3026
3664
|
removeWorktreeByPath(wt.path);
|
|
3027
3665
|
if (shouldDeleteBranch) {
|
|
3028
3666
|
deleteBranch(wt.branch);
|
|
@@ -3255,7 +3893,6 @@ async function executeSyncForBranch(targetWorktreePath, branch) {
|
|
|
3255
3893
|
async function handleSync(options) {
|
|
3256
3894
|
validateMainWorktree();
|
|
3257
3895
|
requireProjectConfig();
|
|
3258
|
-
await ensureOnMainWorkBranch();
|
|
3259
3896
|
logger.info(`sync \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
|
|
3260
3897
|
const worktrees = getProjectWorktrees();
|
|
3261
3898
|
const worktree = await resolveTargetWorktree(worktrees, SYNC_RESOLVE_MESSAGES, options.branch);
|
|
@@ -3627,7 +4264,7 @@ async function handleMerge(options) {
|
|
|
3627
4264
|
}
|
|
3628
4265
|
|
|
3629
4266
|
// src/commands/config.ts
|
|
3630
|
-
import
|
|
4267
|
+
import chalk11 from "chalk";
|
|
3631
4268
|
import Enquirer5 from "enquirer";
|
|
3632
4269
|
function registerConfigCommand(program2) {
|
|
3633
4270
|
const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
|
|
@@ -3688,7 +4325,7 @@ async function handleInteractiveConfigSet() {
|
|
|
3688
4325
|
const isObject = typeof DEFAULT_CONFIG[k] === "object";
|
|
3689
4326
|
return {
|
|
3690
4327
|
name: k,
|
|
3691
|
-
message: `${k}: ${isObject ?
|
|
4328
|
+
message: `${k}: ${isObject ? chalk11.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk11.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
|
|
3692
4329
|
...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
|
|
3693
4330
|
};
|
|
3694
4331
|
});
|
|
@@ -3744,14 +4381,19 @@ async function handleReset() {
|
|
|
3744
4381
|
}
|
|
3745
4382
|
|
|
3746
4383
|
// src/commands/status.ts
|
|
3747
|
-
import
|
|
4384
|
+
import chalk12 from "chalk";
|
|
3748
4385
|
function registerStatusCommand(program2) {
|
|
3749
|
-
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").action((options) => {
|
|
3750
|
-
handleStatus(options);
|
|
4386
|
+
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
|
+
await handleStatus(options);
|
|
3751
4388
|
});
|
|
3752
4389
|
}
|
|
3753
|
-
function handleStatus(options) {
|
|
4390
|
+
async function handleStatus(options) {
|
|
3754
4391
|
validateMainWorktree();
|
|
4392
|
+
if (options.interactive) {
|
|
4393
|
+
const panel = new InteractivePanel(collectStatus);
|
|
4394
|
+
await panel.start();
|
|
4395
|
+
return;
|
|
4396
|
+
}
|
|
3755
4397
|
const statusResult = collectStatus();
|
|
3756
4398
|
logger.info(`status \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${statusResult.main.projectName}\uFF0C\u5171 ${statusResult.totalWorktrees} \u4E2A worktree`);
|
|
3757
4399
|
if (options.json) {
|
|
@@ -3857,7 +4499,7 @@ function printStatusAsJson(result) {
|
|
|
3857
4499
|
}
|
|
3858
4500
|
function printStatusAsText(result) {
|
|
3859
4501
|
printDoubleSeparator();
|
|
3860
|
-
printInfo(` ${
|
|
4502
|
+
printInfo(` ${chalk12.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
|
|
3861
4503
|
printDoubleSeparator();
|
|
3862
4504
|
printInfo("");
|
|
3863
4505
|
printMainSection(result.main);
|
|
@@ -3870,17 +4512,17 @@ function printStatusAsText(result) {
|
|
|
3870
4512
|
printDoubleSeparator();
|
|
3871
4513
|
}
|
|
3872
4514
|
function printMainSection(main2) {
|
|
3873
|
-
printInfo(` ${
|
|
3874
|
-
printInfo(` \u5206\u652F: ${
|
|
4515
|
+
printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
|
|
4516
|
+
printInfo(` \u5206\u652F: ${chalk12.bold(main2.branch)}`);
|
|
3875
4517
|
if (main2.isClean) {
|
|
3876
|
-
printInfo(` \u72B6\u6001: ${
|
|
4518
|
+
printInfo(` \u72B6\u6001: ${chalk12.green("\u2713 \u5E72\u51C0")}`);
|
|
3877
4519
|
} else {
|
|
3878
|
-
printInfo(` \u72B6\u6001: ${
|
|
4520
|
+
printInfo(` \u72B6\u6001: ${chalk12.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
|
|
3879
4521
|
}
|
|
3880
4522
|
printInfo("");
|
|
3881
4523
|
}
|
|
3882
4524
|
function printWorktreesSection(worktrees, total) {
|
|
3883
|
-
printInfo(` ${
|
|
4525
|
+
printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
|
|
3884
4526
|
printInfo("");
|
|
3885
4527
|
if (worktrees.length === 0) {
|
|
3886
4528
|
printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
|
|
@@ -3891,57 +4533,57 @@ function printWorktreesSection(worktrees, total) {
|
|
|
3891
4533
|
}
|
|
3892
4534
|
}
|
|
3893
4535
|
function printWorktreeItem(wt) {
|
|
3894
|
-
const statusLabel =
|
|
3895
|
-
printInfo(` ${
|
|
4536
|
+
const statusLabel = formatChangeStatusLabel2(wt.changeStatus);
|
|
4537
|
+
printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(wt.branch)} [${statusLabel}]`);
|
|
3896
4538
|
if (wt.insertions > 0 || wt.deletions > 0) {
|
|
3897
|
-
printInfo(` ${
|
|
4539
|
+
printInfo(` ${chalk12.green(`+${wt.insertions}`)} ${chalk12.red(`-${wt.deletions}`)}`);
|
|
3898
4540
|
}
|
|
3899
4541
|
if (wt.commitsAhead > 0) {
|
|
3900
|
-
printInfo(` ${
|
|
4542
|
+
printInfo(` ${chalk12.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
|
|
3901
4543
|
}
|
|
3902
4544
|
if (wt.commitsBehind > 0) {
|
|
3903
|
-
printInfo(` ${
|
|
4545
|
+
printInfo(` ${chalk12.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
|
|
3904
4546
|
} else {
|
|
3905
|
-
printInfo(` ${
|
|
4547
|
+
printInfo(` ${chalk12.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
|
|
3906
4548
|
}
|
|
3907
4549
|
if (wt.createdAt) {
|
|
3908
4550
|
const relativeTime = formatRelativeTime(wt.createdAt);
|
|
3909
4551
|
if (relativeTime) {
|
|
3910
|
-
printInfo(` ${
|
|
4552
|
+
printInfo(` ${chalk12.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
|
|
3911
4553
|
}
|
|
3912
4554
|
}
|
|
3913
4555
|
if (wt.snapshotTime) {
|
|
3914
4556
|
const relativeTime = formatRelativeTime(wt.snapshotTime);
|
|
3915
4557
|
if (relativeTime) {
|
|
3916
|
-
printInfo(` ${
|
|
4558
|
+
printInfo(` ${chalk12.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
|
|
3917
4559
|
}
|
|
3918
4560
|
} else {
|
|
3919
|
-
printInfo(` ${
|
|
4561
|
+
printInfo(` ${chalk12.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
|
|
3920
4562
|
}
|
|
3921
4563
|
printInfo("");
|
|
3922
4564
|
}
|
|
3923
|
-
function
|
|
4565
|
+
function formatChangeStatusLabel2(status) {
|
|
3924
4566
|
switch (status) {
|
|
3925
4567
|
case "committed":
|
|
3926
|
-
return
|
|
4568
|
+
return chalk12.green(MESSAGES.STATUS_CHANGE_COMMITTED);
|
|
3927
4569
|
case "uncommitted":
|
|
3928
|
-
return
|
|
4570
|
+
return chalk12.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
|
|
3929
4571
|
case "conflict":
|
|
3930
|
-
return
|
|
4572
|
+
return chalk12.red(MESSAGES.STATUS_CHANGE_CONFLICT);
|
|
3931
4573
|
case "clean":
|
|
3932
|
-
return
|
|
4574
|
+
return chalk12.gray(MESSAGES.STATUS_CHANGE_CLEAN);
|
|
3933
4575
|
}
|
|
3934
4576
|
}
|
|
3935
4577
|
function printSnapshotsSection(snapshots) {
|
|
3936
|
-
printInfo(` ${
|
|
4578
|
+
printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
|
|
3937
4579
|
if (snapshots.orphaned > 0) {
|
|
3938
|
-
printInfo(` ${
|
|
4580
|
+
printInfo(` ${chalk12.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
|
|
3939
4581
|
}
|
|
3940
4582
|
printInfo("");
|
|
3941
4583
|
}
|
|
3942
4584
|
|
|
3943
4585
|
// src/commands/alias.ts
|
|
3944
|
-
import
|
|
4586
|
+
import chalk13 from "chalk";
|
|
3945
4587
|
function getRegisteredCommandNames(program2) {
|
|
3946
4588
|
return program2.commands.map((cmd) => cmd.name());
|
|
3947
4589
|
}
|
|
@@ -3962,7 +4604,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
|
|
|
3962
4604
|
`);
|
|
3963
4605
|
printSeparator();
|
|
3964
4606
|
for (const [alias, command] of entries) {
|
|
3965
|
-
printInfo(` ${
|
|
4607
|
+
printInfo(` ${chalk13.bold(alias)} \u2192 ${chalk13.cyan(command)}`);
|
|
3966
4608
|
}
|
|
3967
4609
|
printInfo("");
|
|
3968
4610
|
printSeparator();
|
|
@@ -4011,7 +4653,7 @@ function registerAliasCommand(program2) {
|
|
|
4011
4653
|
// src/commands/projects.ts
|
|
4012
4654
|
import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
4013
4655
|
import { join as join8 } from "path";
|
|
4014
|
-
import
|
|
4656
|
+
import chalk14 from "chalk";
|
|
4015
4657
|
function registerProjectsCommand(program2) {
|
|
4016
4658
|
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) => {
|
|
4017
4659
|
handleProjects({ name, json: options.json });
|
|
@@ -4130,7 +4772,7 @@ function sortByLastActiveTimeDesc(projects) {
|
|
|
4130
4772
|
}
|
|
4131
4773
|
function printProjectsOverviewAsText(result) {
|
|
4132
4774
|
printDoubleSeparator();
|
|
4133
|
-
printInfo(` ${
|
|
4775
|
+
printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
|
|
4134
4776
|
printDoubleSeparator();
|
|
4135
4777
|
printInfo("");
|
|
4136
4778
|
if (result.projects.length === 0) {
|
|
@@ -4144,7 +4786,7 @@ function printProjectsOverviewAsText(result) {
|
|
|
4144
4786
|
}
|
|
4145
4787
|
printSeparator();
|
|
4146
4788
|
printInfo("");
|
|
4147
|
-
printInfo(` \u5171 ${
|
|
4789
|
+
printInfo(` \u5171 ${chalk14.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk14.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
|
|
4148
4790
|
printInfo("");
|
|
4149
4791
|
printDoubleSeparator();
|
|
4150
4792
|
}
|
|
@@ -4152,16 +4794,16 @@ function printProjectOverviewItem(project) {
|
|
|
4152
4794
|
const relativeTime = formatRelativeTime(project.lastActiveTime);
|
|
4153
4795
|
const activeLabel = relativeTime ? MESSAGES.PROJECTS_LAST_ACTIVE(relativeTime) : "";
|
|
4154
4796
|
const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(project.diskUsage));
|
|
4155
|
-
printInfo(` ${
|
|
4156
|
-
printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${
|
|
4797
|
+
printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(project.name)}`);
|
|
4798
|
+
printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk14.gray(activeLabel)} ${chalk14.gray(diskLabel)}`);
|
|
4157
4799
|
printInfo("");
|
|
4158
4800
|
}
|
|
4159
4801
|
function printProjectDetailAsText(result) {
|
|
4160
4802
|
printDoubleSeparator();
|
|
4161
|
-
printInfo(` ${
|
|
4803
|
+
printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
|
|
4162
4804
|
printDoubleSeparator();
|
|
4163
4805
|
printInfo("");
|
|
4164
|
-
printInfo(` ${
|
|
4806
|
+
printInfo(` ${chalk14.bold("\u25C6")} ${chalk14.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
|
|
4165
4807
|
printInfo(` ${MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage))}`);
|
|
4166
4808
|
printInfo("");
|
|
4167
4809
|
printSeparator();
|
|
@@ -4181,9 +4823,9 @@ function printWorktreeDetailItem(wt) {
|
|
|
4181
4823
|
const relativeTime = formatRelativeTime(wt.lastModifiedTime);
|
|
4182
4824
|
const modifiedLabel = relativeTime ? MESSAGES.PROJECTS_LAST_MODIFIED(relativeTime) : "";
|
|
4183
4825
|
const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(wt.diskUsage));
|
|
4184
|
-
printInfo(` ${
|
|
4826
|
+
printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(wt.branch)}`);
|
|
4185
4827
|
printInfo(` ${wt.path}`);
|
|
4186
|
-
printInfo(` ${
|
|
4828
|
+
printInfo(` ${chalk14.gray(modifiedLabel)} ${chalk14.gray(diskLabel)}`);
|
|
4187
4829
|
printInfo("");
|
|
4188
4830
|
}
|
|
4189
4831
|
|