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/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 chalk from "chalk";
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 ${chalk.bold.hex("#FF8C00")(dateLabel)}\uFF08${chalk.hex("#FF8C00")(relativeTime)}\uFF09 \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 ${chalk.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \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 chalk2 from "chalk";
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: chalk2.red,
653
- warn: chalk2.yellow,
654
- info: chalk2.cyan,
655
- debug: chalk2.gray
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] || chalk2.white;
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 `${chalk2.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
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 chalk3 from "chalk";
999
+ import chalk4 from "chalk";
944
1000
  import { createInterface } from "readline";
945
1001
  function printSuccess(message) {
946
- console.log(chalk3.green(message));
1002
+ console.log(chalk4.green(message));
947
1003
  }
948
1004
  function printError(message) {
949
- console.error(chalk3.red(`\u2717 ${message}`));
1005
+ console.error(chalk4.red(`\u2717 ${message}`));
950
1006
  }
951
1007
  function printWarning(message) {
952
- console.log(chalk3.yellow(`\u26A0 ${message}`));
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(chalk3.hex("#FF8C00")(message));
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 ${chalk3.red.bold(dangerousCommand)}\uFF0C${description}`);
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(chalk3.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
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(chalk3.green(`+${status.insertions}`));
993
- diffParts.push(chalk3.red(`-${status.deletions}`));
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(chalk3.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
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 chalk4 from "chalk";
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} ${chalk4.gray(TASK_STATUS_ICONS.PENDING)} ${chalk4.gray(TASK_STATUS_LABELS.PENDING)}`;
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 ? ` ${chalk4.dim(task.activity)}` : "";
1927
- return `${indexStr} ${pathStr} ${chalk4.cyan(spinnerChar)} ${chalk4.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk4.gray(elapsed)}${detail}`;
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 ? ` ${chalk4.dim(task.resultPreview)}` : "";
1933
- return `${indexStr} ${pathStr} ${chalk4.green(TASK_STATUS_ICONS.DONE)} ${chalk4.green(TASK_STATUS_LABELS.DONE)} ${chalk4.gray(duration)} ${chalk4.yellow(cost)}${preview}`;
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 ? ` ${chalk4.dim(task.resultPreview)}` : "";
1938
- return `${indexStr} ${pathStr} ${chalk4.red(TASK_STATUS_ICONS.FAILED)} ${chalk4.red(TASK_STATUS_LABELS.FAILED)} ${chalk4.gray(duration)}${preview}`;
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(chalk4.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
1949
- if (done > 0) parts.push(chalk4.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
1950
- if (failed > 0) parts.push(chalk4.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
1951
- if (pending > 0) parts.push(chalk4.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
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 chalk5 from "chalk";
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(` ${chalk5.bold(MESSAGES.DRY_RUN_TITLE)}`);
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(chalk5.gray(" \u2502 ")));
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(`${chalk5.yellow("\u26A0")} ${indexLabel} ${chalk5.yellow(branch)} ${chalk5.gray("\u2014")} ${chalk5.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
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(`${chalk5.green("\u2713")} ${indexLabel} ${chalk5.cyan(branch)}`);
2658
+ printInfo(`${chalk6.green("\u2713")} ${indexLabel} ${chalk6.cyan(branch)}`);
2603
2659
  }
2604
- printInfo(` ${chalk5.gray("\u8DEF\u5F84:")} ${worktreePath}`);
2660
+ printInfo(` ${chalk6.gray("\u8DEF\u5F84:")} ${worktreePath}`);
2605
2661
  if (!isInteractive) {
2606
- printInfo(` ${chalk5.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
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(chalk5.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
2668
+ printInfo(chalk6.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
2613
2669
  } else {
2614
- printInfo(chalk5.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
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 chalk6 from "chalk";
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 ? chalk6.green("true") : chalk6.yellow("false");
2732
+ return value ? chalk7.green("true") : chalk7.yellow("false");
2677
2733
  }
2678
- return chalk6.cyan(String(value));
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 chalk7 from "chalk";
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
- chalk7.red(currentVersion),
2801
- chalk7.green(latestVersion)
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(chalk7.cyan(updateCommand));
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 chalk8 from "chalk";
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 ? chalk8.hex("#FF8C00")(wt.path) : wt.path;
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(` ${chalk8.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
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 chalk9 from "chalk";
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 ? chalk9.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk9.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
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 chalk10 from "chalk";
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(` ${chalk10.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
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(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
3874
- printInfo(` \u5206\u652F: ${chalk10.bold(main2.branch)}`);
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: ${chalk10.green("\u2713 \u5E72\u51C0")}`);
4518
+ printInfo(` \u72B6\u6001: ${chalk12.green("\u2713 \u5E72\u51C0")}`);
3877
4519
  } else {
3878
- printInfo(` \u72B6\u6001: ${chalk10.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
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(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
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 = formatChangeStatusLabel(wt.changeStatus);
3895
- printInfo(` ${chalk10.bold("\u25CF")} ${chalk10.bold(wt.branch)} [${statusLabel}]`);
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(` ${chalk10.green(`+${wt.insertions}`)} ${chalk10.red(`-${wt.deletions}`)}`);
4539
+ printInfo(` ${chalk12.green(`+${wt.insertions}`)} ${chalk12.red(`-${wt.deletions}`)}`);
3898
4540
  }
3899
4541
  if (wt.commitsAhead > 0) {
3900
- printInfo(` ${chalk10.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
4542
+ printInfo(` ${chalk12.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3901
4543
  }
3902
4544
  if (wt.commitsBehind > 0) {
3903
- printInfo(` ${chalk10.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
4545
+ printInfo(` ${chalk12.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3904
4546
  } else {
3905
- printInfo(` ${chalk10.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
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(` ${chalk10.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
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(` ${chalk10.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
4558
+ printInfo(` ${chalk12.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3917
4559
  }
3918
4560
  } else {
3919
- printInfo(` ${chalk10.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
4561
+ printInfo(` ${chalk12.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3920
4562
  }
3921
4563
  printInfo("");
3922
4564
  }
3923
- function formatChangeStatusLabel(status) {
4565
+ function formatChangeStatusLabel2(status) {
3924
4566
  switch (status) {
3925
4567
  case "committed":
3926
- return chalk10.green(MESSAGES.STATUS_CHANGE_COMMITTED);
4568
+ return chalk12.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3927
4569
  case "uncommitted":
3928
- return chalk10.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
4570
+ return chalk12.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3929
4571
  case "conflict":
3930
- return chalk10.red(MESSAGES.STATUS_CHANGE_CONFLICT);
4572
+ return chalk12.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3931
4573
  case "clean":
3932
- return chalk10.gray(MESSAGES.STATUS_CHANGE_CLEAN);
4574
+ return chalk12.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3933
4575
  }
3934
4576
  }
3935
4577
  function printSnapshotsSection(snapshots) {
3936
- printInfo(` ${chalk10.bold("\u25C6")} ${chalk10.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
4578
+ printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
3937
4579
  if (snapshots.orphaned > 0) {
3938
- printInfo(` ${chalk10.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
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 chalk11 from "chalk";
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(` ${chalk11.bold(alias)} \u2192 ${chalk11.cyan(command)}`);
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 chalk12 from "chalk";
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(` ${chalk12.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
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 ${chalk12.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk12.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
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(` ${chalk12.bold("\u25CF")} ${chalk12.bold(project.name)}`);
4156
- printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk12.gray(activeLabel)} ${chalk12.gray(diskLabel)}`);
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(` ${chalk12.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
4803
+ printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
4162
4804
  printDoubleSeparator();
4163
4805
  printInfo("");
4164
- printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
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(` ${chalk12.bold("\u25CF")} ${chalk12.bold(wt.branch)}`);
4826
+ printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(wt.branch)}`);
4185
4827
  printInfo(` ${wt.path}`);
4186
- printInfo(` ${chalk12.gray(modifiedLabel)} ${chalk12.gray(diskLabel)}`);
4828
+ printInfo(` ${chalk14.gray(modifiedLabel)} ${chalk14.gray(diskLabel)}`);
4187
4829
  printInfo("");
4188
4830
  }
4189
4831