clawt 2.17.1 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ var LOGS_DIR = join(CLAWT_HOME, "logs");
15
15
  var WORKTREES_DIR = join(CLAWT_HOME, "worktrees");
16
16
  var VALIDATE_SNAPSHOTS_DIR = join(CLAWT_HOME, "validate-snapshots");
17
17
  var CLAUDE_PROJECTS_DIR = join(homedir(), ".claude", "projects");
18
+ var UPDATE_CHECK_PATH = join(CLAWT_HOME, "update-check.json");
18
19
 
19
20
  // src/constants/branch.ts
20
21
  var INVALID_BRANCH_CHARS = /[\/\\.\s~:*?[\]^]+/g;
@@ -372,6 +373,32 @@ var ALIAS_MESSAGES = {
372
373
  ALIAS_LIST_TITLE: "\u5F53\u524D\u522B\u540D\u5217\u8868\uFF1A"
373
374
  };
374
375
 
376
+ // src/constants/messages/projects.ts
377
+ var PROJECTS_MESSAGES = {
378
+ /** projects 命令全局概览标题 */
379
+ PROJECTS_OVERVIEW_TITLE: "\u9879\u76EE\u6982\u89C8",
380
+ /** projects 命令指定项目详情标题 */
381
+ PROJECTS_DETAIL_TITLE: (projectName) => `\u9879\u76EE\u8BE6\u60C5: ${projectName}`,
382
+ /** 无项目提示 */
383
+ PROJECTS_NO_PROJECTS: "(\u6682\u65E0\u9879\u76EE\uFF0Cworktrees \u76EE\u5F55\u4E3A\u7A7A)",
384
+ /** 项目不存在提示 */
385
+ PROJECTS_NOT_FOUND: (name) => `\u9879\u76EE ${name} \u4E0D\u5B58\u5728`,
386
+ /** worktree 数量标签 */
387
+ PROJECTS_WORKTREE_COUNT: (count) => `${count} \u4E2A worktree`,
388
+ /** 最近活跃时间标签 */
389
+ PROJECTS_LAST_ACTIVE: (relativeTime) => `\u6700\u8FD1\u6D3B\u8DC3: ${relativeTime}`,
390
+ /** 磁盘占用标签 */
391
+ PROJECTS_DISK_USAGE: (size) => `\u78C1\u76D8\u5360\u7528: ${size}`,
392
+ /** 总磁盘占用标签 */
393
+ PROJECTS_TOTAL_DISK_USAGE: (size) => `\u603B\u5360\u7528: ${size}`,
394
+ /** projects 详情无 worktree */
395
+ PROJECTS_DETAIL_NO_WORKTREES: "(\u8BE5\u9879\u76EE\u4E0B\u65E0 worktree)",
396
+ /** 路径标签 */
397
+ PROJECTS_PATH: (path2) => `\u8DEF\u5F84: ${path2}`,
398
+ /** 最后修改时间标签 */
399
+ PROJECTS_LAST_MODIFIED: (relativeTime) => `\u6700\u540E\u4FEE\u6539: ${relativeTime}`
400
+ };
401
+
375
402
  // src/constants/messages/completion.ts
376
403
  var COMPLETION_MESSAGES = {
377
404
  /** completion 命令的主描述 */
@@ -394,6 +421,19 @@ var COMPLETION_MESSAGES = {
394
421
  COMPLETION_INSTALL_WRITE_ERROR: (filePath) => `\u65E0\u6CD5\u5199\u5165\u6587\u4EF6 ${filePath}\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u6743\u9650\u6216\u624B\u52A8\u914D\u7F6E\u3002`
395
422
  };
396
423
 
424
+ // src/constants/messages/update.ts
425
+ var UPDATE_COMMANDS = {
426
+ npm: "npm i -g clawt",
427
+ pnpm: "pnpm add -g clawt",
428
+ yarn: "yarn global add clawt"
429
+ };
430
+ var UPDATE_MESSAGES = {
431
+ /** 版本更新提示 */
432
+ UPDATE_AVAILABLE: (currentVersion, latestVersion) => `clawt \u6709\u65B0\u7248\u672C\u53EF\u7528: ${currentVersion} \u2192 ${latestVersion}`,
433
+ /** 更新操作提示 */
434
+ UPDATE_HINT: (command) => `\u6267\u884C ${command} \u8FDB\u884C\u66F4\u65B0`
435
+ };
436
+
397
437
  // src/constants/messages/index.ts
398
438
  var MESSAGES = {
399
439
  ...COMMON_MESSAGES,
@@ -408,6 +448,7 @@ var MESSAGES = {
408
448
  ...CONFIG_CMD_MESSAGES,
409
449
  ...STATUS_MESSAGES,
410
450
  ...ALIAS_MESSAGES,
451
+ ...PROJECTS_MESSAGES,
411
452
  ...COMPLETION_MESSAGES
412
453
  };
413
454
 
@@ -456,6 +497,10 @@ var CONFIG_DEFINITIONS = {
456
497
  aliases: {
457
498
  defaultValue: {},
458
499
  description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
500
+ },
501
+ autoUpdate: {
502
+ defaultValue: true,
503
+ description: "\u662F\u5426\u542F\u7528\u81EA\u52A8\u66F4\u65B0\u68C0\u67E5\uFF08\u6BCF 24 \u5C0F\u65F6\u68C0\u67E5\u4E00\u6B21 npm registry\uFF09"
459
504
  }
460
505
  };
461
506
  function deriveDefaultConfig(definitions) {
@@ -479,6 +524,12 @@ var AUTO_SAVE_COMMIT_MESSAGE = "chore: auto-save before sync";
479
524
  // src/constants/logger.ts
480
525
  var DEBUG_TIMESTAMP_FORMAT = "HH:mm:ss.SSS";
481
526
 
527
+ // src/constants/update.ts
528
+ var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
529
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/clawt/latest";
530
+ var NPM_REGISTRY_TIMEOUT_MS = 5e3;
531
+ var PACKAGE_NAME = "clawt";
532
+
482
533
  // src/constants/progress.ts
483
534
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
484
535
  var SPINNER_INTERVAL_MS = 100;
@@ -943,6 +994,32 @@ function formatRelativeTime(isoDateString) {
943
994
  const years = Math.floor(diffDays / 365);
944
995
  return `${years} \u5E74\u524D`;
945
996
  }
997
+ function formatDiskSize(bytes) {
998
+ const KB = 1024;
999
+ const MB = KB * 1024;
1000
+ const GB = MB * 1024;
1001
+ if (bytes >= GB) {
1002
+ return `${(bytes / GB).toFixed(1)} GB`;
1003
+ }
1004
+ if (bytes >= MB) {
1005
+ return `${(bytes / MB).toFixed(1)} MB`;
1006
+ }
1007
+ if (bytes >= KB) {
1008
+ return `${(bytes / KB).toFixed(1)} KB`;
1009
+ }
1010
+ return `${bytes} B`;
1011
+ }
1012
+ function formatLocalISOString(date) {
1013
+ const tzOffsetMs = date.getTimezoneOffset() * 60 * 1e3;
1014
+ const localDate = new Date(date.getTime() - tzOffsetMs);
1015
+ const iso = localDate.toISOString().slice(0, -1);
1016
+ const totalMinutes = -date.getTimezoneOffset();
1017
+ const sign = totalMinutes >= 0 ? "+" : "-";
1018
+ const absMinutes = Math.abs(totalMinutes);
1019
+ const hours = String(Math.floor(absMinutes / 60)).padStart(2, "0");
1020
+ const minutes = String(absMinutes % 60).padStart(2, "0");
1021
+ return `${iso}${sign}${hours}:${minutes}`;
1022
+ }
946
1023
 
947
1024
  // src/utils/branch.ts
948
1025
  function sanitizeBranchName(branchName) {
@@ -993,11 +1070,12 @@ function validateClaudeCodeInstalled() {
993
1070
  }
994
1071
 
995
1072
  // src/utils/worktree.ts
996
- import { join as join2 } from "path";
1073
+ import { join as join3 } from "path";
997
1074
  import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
998
1075
 
999
1076
  // src/utils/fs.ts
1000
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmdirSync } from "fs";
1077
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmdirSync, statSync } from "fs";
1078
+ import { join as join2 } from "path";
1001
1079
  function ensureDir(dirPath) {
1002
1080
  if (!existsSync2(dirPath)) {
1003
1081
  mkdirSync2(dirPath, { recursive: true });
@@ -1014,11 +1092,30 @@ function removeEmptyDir(dirPath) {
1014
1092
  }
1015
1093
  return false;
1016
1094
  }
1095
+ function calculateDirSize(dirPath) {
1096
+ let totalSize = 0;
1097
+ try {
1098
+ const entries = readdirSync(dirPath, { withFileTypes: true });
1099
+ for (const entry of entries) {
1100
+ const fullPath = join2(dirPath, entry.name);
1101
+ try {
1102
+ if (entry.isDirectory()) {
1103
+ totalSize += calculateDirSize(fullPath);
1104
+ } else if (entry.isFile()) {
1105
+ totalSize += statSync(fullPath).size;
1106
+ }
1107
+ } catch {
1108
+ }
1109
+ }
1110
+ } catch {
1111
+ }
1112
+ return totalSize;
1113
+ }
1017
1114
 
1018
1115
  // src/utils/worktree.ts
1019
1116
  function getProjectWorktreeDir() {
1020
1117
  const projectName = getProjectName();
1021
- return join2(WORKTREES_DIR, projectName);
1118
+ return join3(WORKTREES_DIR, projectName);
1022
1119
  }
1023
1120
  function createWorktrees(branchName, count) {
1024
1121
  const sanitized = sanitizeBranchName(branchName);
@@ -1028,7 +1125,7 @@ function createWorktrees(branchName, count) {
1028
1125
  ensureDir(projectDir);
1029
1126
  const results = [];
1030
1127
  for (const name of branchNames) {
1031
- const worktreePath = join2(projectDir, name);
1128
+ const worktreePath = join3(projectDir, name);
1032
1129
  createWorktree(name, worktreePath);
1033
1130
  results.push({ path: worktreePath, branch: name });
1034
1131
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
@@ -1041,7 +1138,7 @@ function createWorktreesByBranches(branchNames) {
1041
1138
  ensureDir(projectDir);
1042
1139
  const results = [];
1043
1140
  for (const name of branchNames) {
1044
- const worktreePath = join2(projectDir, name);
1141
+ const worktreePath = join3(projectDir, name);
1045
1142
  createWorktree(name, worktreePath);
1046
1143
  results.push({ path: worktreePath, branch: name });
1047
1144
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
@@ -1063,7 +1160,7 @@ function getProjectWorktrees() {
1063
1160
  if (!entry.isDirectory()) {
1064
1161
  continue;
1065
1162
  }
1066
- const fullPath = join2(projectDir, entry.name);
1163
+ const fullPath = join3(projectDir, entry.name);
1067
1164
  if (registeredPaths.has(fullPath)) {
1068
1165
  worktrees.push({
1069
1166
  path: fullPath,
@@ -1149,7 +1246,7 @@ import Enquirer from "enquirer";
1149
1246
  // src/utils/claude.ts
1150
1247
  import { spawnSync as spawnSync2 } from "child_process";
1151
1248
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
1152
- import { join as join3 } from "path";
1249
+ import { join as join4 } from "path";
1153
1250
 
1154
1251
  // src/utils/terminal.ts
1155
1252
  import { execFileSync as execFileSync2 } from "child_process";
@@ -1228,7 +1325,7 @@ function encodeClaudeProjectPath(absolutePath) {
1228
1325
  }
1229
1326
  function hasClaudeSessionHistory(worktreePath) {
1230
1327
  const encodedName = encodeClaudeProjectPath(worktreePath);
1231
- const projectDir = join3(CLAUDE_PROJECTS_DIR, encodedName);
1328
+ const projectDir = join4(CLAUDE_PROJECTS_DIR, encodedName);
1232
1329
  if (!existsSync6(projectDir)) {
1233
1330
  return false;
1234
1331
  }
@@ -1285,13 +1382,13 @@ function launchInteractiveClaudeInNewTerminal(worktree, hasPreviousSession) {
1285
1382
  }
1286
1383
 
1287
1384
  // src/utils/validate-snapshot.ts
1288
- import { join as join4 } from "path";
1289
- import { existsSync as existsSync7, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync, readdirSync as readdirSync4, rmdirSync as rmdirSync2, statSync } from "fs";
1385
+ import { join as join5 } from "path";
1386
+ import { existsSync as existsSync7, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync, readdirSync as readdirSync4, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
1290
1387
  function getSnapshotPath(projectName, branchName) {
1291
- return join4(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
1388
+ return join5(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
1292
1389
  }
1293
1390
  function getSnapshotHeadPath(projectName, branchName) {
1294
- return join4(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
1391
+ return join5(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
1295
1392
  }
1296
1393
  function hasSnapshot(projectName, branchName) {
1297
1394
  return existsSync7(getSnapshotPath(projectName, branchName));
@@ -1299,7 +1396,7 @@ function hasSnapshot(projectName, branchName) {
1299
1396
  function getSnapshotModifiedTime(projectName, branchName) {
1300
1397
  const snapshotPath = getSnapshotPath(projectName, branchName);
1301
1398
  if (!existsSync7(snapshotPath)) return null;
1302
- const stat = statSync(snapshotPath);
1399
+ const stat = statSync2(snapshotPath);
1303
1400
  return stat.mtime.toISOString();
1304
1401
  }
1305
1402
  function readSnapshot(projectName, branchName) {
@@ -1313,7 +1410,7 @@ function readSnapshot(projectName, branchName) {
1313
1410
  function writeSnapshot(projectName, branchName, treeHash, headCommitHash) {
1314
1411
  const snapshotPath = getSnapshotPath(projectName, branchName);
1315
1412
  const headPath = getSnapshotHeadPath(projectName, branchName);
1316
- const snapshotDir = join4(VALIDATE_SNAPSHOTS_DIR, projectName);
1413
+ const snapshotDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1317
1414
  ensureDir(snapshotDir);
1318
1415
  writeFileSync2(snapshotPath, treeHash, "utf-8");
1319
1416
  writeFileSync2(headPath, headCommitHash, "utf-8");
@@ -1332,7 +1429,7 @@ function removeSnapshot(projectName, branchName) {
1332
1429
  }
1333
1430
  }
1334
1431
  function getProjectSnapshotBranches(projectName) {
1335
- const projectDir = join4(VALIDATE_SNAPSHOTS_DIR, projectName);
1432
+ const projectDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1336
1433
  if (!existsSync7(projectDir)) {
1337
1434
  return [];
1338
1435
  }
@@ -1340,13 +1437,13 @@ function getProjectSnapshotBranches(projectName) {
1340
1437
  return files.filter((f) => f.endsWith(".tree")).map((f) => f.replace(/\.tree$/, ""));
1341
1438
  }
1342
1439
  function removeProjectSnapshots(projectName) {
1343
- const projectDir = join4(VALIDATE_SNAPSHOTS_DIR, projectName);
1440
+ const projectDir = join5(VALIDATE_SNAPSHOTS_DIR, projectName);
1344
1441
  if (!existsSync7(projectDir)) {
1345
1442
  return;
1346
1443
  }
1347
1444
  const files = readdirSync4(projectDir);
1348
1445
  for (const file of files) {
1349
- unlinkSync(join4(projectDir, file));
1446
+ unlinkSync(join5(projectDir, file));
1350
1447
  }
1351
1448
  try {
1352
1449
  rmdirSync2(projectDir);
@@ -2145,7 +2242,7 @@ async function executeBatchTasks(worktrees, tasks, concurrency) {
2145
2242
 
2146
2243
  // src/utils/dry-run.ts
2147
2244
  import chalk4 from "chalk";
2148
- import { join as join5 } from "path";
2245
+ import { join as join6 } from "path";
2149
2246
  var DRY_RUN_TASK_DESC_MAX_LENGTH = 80;
2150
2247
  function truncateTaskDesc(task) {
2151
2248
  const oneLine = task.replace(/\n/g, " ").trim();
@@ -2173,7 +2270,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
2173
2270
  let hasConflict = false;
2174
2271
  for (let i = 0; i < branchNames.length; i++) {
2175
2272
  const branch = branchNames[i];
2176
- const worktreePath = join5(projectDir, branch);
2273
+ const worktreePath = join6(projectDir, branch);
2177
2274
  const exists = checkBranchExists(branch);
2178
2275
  if (exists) hasConflict = true;
2179
2276
  const indexLabel = `[${i + 1}/${branchNames.length}]`;
@@ -2299,8 +2396,139 @@ async function promptStringValue(key, currentValue) {
2299
2396
  }).run();
2300
2397
  }
2301
2398
 
2302
- // src/commands/list.ts
2399
+ // src/utils/update-checker.ts
2400
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2401
+ import { execSync as execSync3 } from "child_process";
2402
+ import { request } from "https";
2303
2403
  import chalk6 from "chalk";
2404
+ import stringWidth2 from "string-width";
2405
+ function readUpdateCache() {
2406
+ try {
2407
+ const raw = readFileSync4(UPDATE_CHECK_PATH, "utf-8");
2408
+ return JSON.parse(raw);
2409
+ } catch {
2410
+ return null;
2411
+ }
2412
+ }
2413
+ function writeUpdateCache(cache) {
2414
+ try {
2415
+ writeFileSync3(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
2416
+ } catch {
2417
+ }
2418
+ }
2419
+ function isCacheExpired(cache, currentVersion) {
2420
+ if (cache.currentVersion !== currentVersion) {
2421
+ return true;
2422
+ }
2423
+ return Date.now() - cache.lastCheck > UPDATE_CHECK_INTERVAL_MS;
2424
+ }
2425
+ function isNewerVersion(latest, current) {
2426
+ const latestParts = latest.split(".").map(Number);
2427
+ const currentParts = current.split(".").map(Number);
2428
+ for (let i = 0; i < 3; i++) {
2429
+ const l = latestParts[i] || 0;
2430
+ const c = currentParts[i] || 0;
2431
+ if (l > c) return true;
2432
+ if (l < c) return false;
2433
+ }
2434
+ return false;
2435
+ }
2436
+ function fetchLatestVersion() {
2437
+ return new Promise((resolve3) => {
2438
+ const req = request(NPM_REGISTRY_URL, { timeout: NPM_REGISTRY_TIMEOUT_MS }, (res) => {
2439
+ let data = "";
2440
+ res.on("data", (chunk) => {
2441
+ data += chunk.toString();
2442
+ });
2443
+ res.on("end", () => {
2444
+ try {
2445
+ const parsed = JSON.parse(data);
2446
+ resolve3(parsed.version ?? null);
2447
+ } catch {
2448
+ resolve3(null);
2449
+ }
2450
+ });
2451
+ });
2452
+ req.on("error", () => resolve3(null));
2453
+ req.on("timeout", () => {
2454
+ req.destroy();
2455
+ resolve3(null);
2456
+ });
2457
+ req.end();
2458
+ });
2459
+ }
2460
+ function detectPackageManager() {
2461
+ const checks = [
2462
+ { name: "pnpm", command: `pnpm list -g --depth=0 ${PACKAGE_NAME}` },
2463
+ { name: "yarn", command: `yarn global list --depth=0 2>/dev/null` }
2464
+ ];
2465
+ for (const { name, command } of checks) {
2466
+ try {
2467
+ const output = execSync3(command, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
2468
+ if (output.includes(PACKAGE_NAME)) {
2469
+ return name;
2470
+ }
2471
+ } catch {
2472
+ }
2473
+ }
2474
+ return "npm";
2475
+ }
2476
+ function printUpdateNotification(currentVersion, latestVersion) {
2477
+ const updateText = UPDATE_MESSAGES.UPDATE_AVAILABLE(
2478
+ chalk6.red(currentVersion),
2479
+ chalk6.green(latestVersion)
2480
+ );
2481
+ const pm = detectPackageManager();
2482
+ const updateCommand = UPDATE_COMMANDS[pm] || UPDATE_COMMANDS.npm;
2483
+ const commandText = UPDATE_MESSAGES.UPDATE_HINT(chalk6.cyan(updateCommand));
2484
+ const updateTextWidth = stringWidth2(updateText);
2485
+ const commandTextWidth = stringWidth2(commandText);
2486
+ const innerWidth = Math.max(updateTextWidth, commandTextWidth) + 4;
2487
+ const padLine = (text) => {
2488
+ const textWidth = stringWidth2(text);
2489
+ const leftPad = Math.floor((innerWidth - textWidth) / 2);
2490
+ const rightPad = innerWidth - textWidth - leftPad;
2491
+ return `\u2502${" ".repeat(leftPad)}${text}${" ".repeat(rightPad)}\u2502`;
2492
+ };
2493
+ const top = `\u256D${"\u2500".repeat(innerWidth)}\u256E`;
2494
+ const bottom = `\u2570${"\u2500".repeat(innerWidth)}\u256F`;
2495
+ const emptyLine = `\u2502${" ".repeat(innerWidth)}\u2502`;
2496
+ console.log();
2497
+ console.log(top);
2498
+ console.log(emptyLine);
2499
+ console.log(padLine(updateText));
2500
+ console.log(padLine(commandText));
2501
+ console.log(emptyLine);
2502
+ console.log(bottom);
2503
+ console.log();
2504
+ }
2505
+ async function checkForUpdates(currentVersion) {
2506
+ try {
2507
+ const cache = readUpdateCache();
2508
+ if (cache && !isCacheExpired(cache, currentVersion)) {
2509
+ if (isNewerVersion(cache.latestVersion, currentVersion)) {
2510
+ printUpdateNotification(currentVersion, cache.latestVersion);
2511
+ }
2512
+ return;
2513
+ }
2514
+ const latestVersion = await fetchLatestVersion();
2515
+ if (!latestVersion) {
2516
+ return;
2517
+ }
2518
+ writeUpdateCache({
2519
+ lastCheck: Date.now(),
2520
+ latestVersion,
2521
+ currentVersion
2522
+ });
2523
+ if (isNewerVersion(latestVersion, currentVersion)) {
2524
+ printUpdateNotification(currentVersion, latestVersion);
2525
+ }
2526
+ } catch {
2527
+ }
2528
+ }
2529
+
2530
+ // src/commands/list.ts
2531
+ import chalk7 from "chalk";
2304
2532
  function registerListCommand(program2) {
2305
2533
  program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
2306
2534
  handleList(options);
@@ -2337,12 +2565,12 @@ function printListAsText(projectName, worktrees) {
2337
2565
  for (const wt of worktrees) {
2338
2566
  const status = getWorktreeStatus(wt);
2339
2567
  const isIdle = status ? isWorktreeIdle(status) : false;
2340
- const pathDisplay = isIdle ? chalk6.hex("#FF8C00")(wt.path) : wt.path;
2568
+ const pathDisplay = isIdle ? chalk7.hex("#FF8C00")(wt.path) : wt.path;
2341
2569
  printInfo(` ${pathDisplay} [${wt.branch}]`);
2342
2570
  if (status) {
2343
2571
  printInfo(` ${formatWorktreeStatus(status)}`);
2344
2572
  } else {
2345
- printInfo(` ${chalk6.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
2573
+ printInfo(` ${chalk7.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
2346
2574
  }
2347
2575
  printInfo("");
2348
2576
  }
@@ -3025,7 +3253,7 @@ async function handleMerge(options) {
3025
3253
  }
3026
3254
 
3027
3255
  // src/commands/config.ts
3028
- import chalk7 from "chalk";
3256
+ import chalk8 from "chalk";
3029
3257
  import Enquirer5 from "enquirer";
3030
3258
  function registerConfigCommand(program2) {
3031
3259
  const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
@@ -3086,7 +3314,7 @@ async function handleInteractiveConfigSet() {
3086
3314
  const isObject = typeof DEFAULT_CONFIG[k] === "object";
3087
3315
  return {
3088
3316
  name: k,
3089
- message: `${k}: ${isObject ? chalk7.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk7.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
3317
+ message: `${k}: ${isObject ? chalk8.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk8.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
3090
3318
  ...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
3091
3319
  };
3092
3320
  });
@@ -3141,7 +3369,7 @@ async function handleReset() {
3141
3369
  }
3142
3370
 
3143
3371
  // src/commands/status.ts
3144
- import chalk8 from "chalk";
3372
+ import chalk9 from "chalk";
3145
3373
  function registerStatusCommand(program2) {
3146
3374
  program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
3147
3375
  handleStatus(options);
@@ -3161,7 +3389,7 @@ function collectStatus() {
3161
3389
  const projectName = getProjectName();
3162
3390
  const currentBranch = getCurrentBranch();
3163
3391
  const isClean = isWorkingDirClean();
3164
- const main = {
3392
+ const main2 = {
3165
3393
  branch: currentBranch,
3166
3394
  isClean,
3167
3395
  projectName
@@ -3170,7 +3398,7 @@ function collectStatus() {
3170
3398
  const worktreeStatuses = worktrees.map((wt) => collectWorktreeDetailedStatus(wt, projectName));
3171
3399
  const snapshots = collectSnapshots(projectName, worktrees);
3172
3400
  return {
3173
- main,
3401
+ main: main2,
3174
3402
  worktrees: worktreeStatuses,
3175
3403
  snapshots,
3176
3404
  totalWorktrees: worktrees.length
@@ -3254,7 +3482,7 @@ function printStatusAsJson(result) {
3254
3482
  }
3255
3483
  function printStatusAsText(result) {
3256
3484
  printDoubleSeparator();
3257
- printInfo(` ${chalk8.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
3485
+ printInfo(` ${chalk9.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
3258
3486
  printDoubleSeparator();
3259
3487
  printInfo("");
3260
3488
  printMainSection(result.main);
@@ -3266,18 +3494,18 @@ function printStatusAsText(result) {
3266
3494
  printSnapshotsSection(result.snapshots);
3267
3495
  printDoubleSeparator();
3268
3496
  }
3269
- function printMainSection(main) {
3270
- printInfo(` ${chalk8.bold("\u25C6")} ${chalk8.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
3271
- printInfo(` \u5206\u652F: ${chalk8.bold(main.branch)}`);
3272
- if (main.isClean) {
3273
- printInfo(` \u72B6\u6001: ${chalk8.green("\u2713 \u5E72\u51C0")}`);
3497
+ function printMainSection(main2) {
3498
+ printInfo(` ${chalk9.bold("\u25C6")} ${chalk9.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
3499
+ printInfo(` \u5206\u652F: ${chalk9.bold(main2.branch)}`);
3500
+ if (main2.isClean) {
3501
+ printInfo(` \u72B6\u6001: ${chalk9.green("\u2713 \u5E72\u51C0")}`);
3274
3502
  } else {
3275
- printInfo(` \u72B6\u6001: ${chalk8.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
3503
+ printInfo(` \u72B6\u6001: ${chalk9.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
3276
3504
  }
3277
3505
  printInfo("");
3278
3506
  }
3279
3507
  function printWorktreesSection(worktrees, total) {
3280
- printInfo(` ${chalk8.bold("\u25C6")} ${chalk8.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
3508
+ printInfo(` ${chalk9.bold("\u25C6")} ${chalk9.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
3281
3509
  printInfo("");
3282
3510
  if (worktrees.length === 0) {
3283
3511
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -3289,56 +3517,56 @@ function printWorktreesSection(worktrees, total) {
3289
3517
  }
3290
3518
  function printWorktreeItem(wt) {
3291
3519
  const statusLabel = formatChangeStatusLabel(wt.changeStatus);
3292
- printInfo(` ${chalk8.bold("\u25CF")} ${chalk8.bold(wt.branch)} [${statusLabel}]`);
3520
+ printInfo(` ${chalk9.bold("\u25CF")} ${chalk9.bold(wt.branch)} [${statusLabel}]`);
3293
3521
  if (wt.insertions > 0 || wt.deletions > 0) {
3294
- printInfo(` ${chalk8.green(`+${wt.insertions}`)} ${chalk8.red(`-${wt.deletions}`)}`);
3522
+ printInfo(` ${chalk9.green(`+${wt.insertions}`)} ${chalk9.red(`-${wt.deletions}`)}`);
3295
3523
  }
3296
3524
  if (wt.commitsAhead > 0) {
3297
- printInfo(` ${chalk8.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3525
+ printInfo(` ${chalk9.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
3298
3526
  }
3299
3527
  if (wt.commitsBehind > 0) {
3300
- printInfo(` ${chalk8.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3528
+ printInfo(` ${chalk9.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
3301
3529
  } else {
3302
- printInfo(` ${chalk8.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
3530
+ printInfo(` ${chalk9.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
3303
3531
  }
3304
3532
  if (wt.createdAt) {
3305
3533
  const relativeTime = formatRelativeTime(wt.createdAt);
3306
3534
  if (relativeTime) {
3307
- printInfo(` ${chalk8.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
3535
+ printInfo(` ${chalk9.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
3308
3536
  }
3309
3537
  }
3310
3538
  if (wt.snapshotTime) {
3311
3539
  const relativeTime = formatRelativeTime(wt.snapshotTime);
3312
3540
  if (relativeTime) {
3313
- printInfo(` ${chalk8.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3541
+ printInfo(` ${chalk9.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
3314
3542
  }
3315
3543
  } else {
3316
- printInfo(` ${chalk8.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3544
+ printInfo(` ${chalk9.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
3317
3545
  }
3318
3546
  printInfo("");
3319
3547
  }
3320
3548
  function formatChangeStatusLabel(status) {
3321
3549
  switch (status) {
3322
3550
  case "committed":
3323
- return chalk8.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3551
+ return chalk9.green(MESSAGES.STATUS_CHANGE_COMMITTED);
3324
3552
  case "uncommitted":
3325
- return chalk8.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3553
+ return chalk9.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
3326
3554
  case "conflict":
3327
- return chalk8.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3555
+ return chalk9.red(MESSAGES.STATUS_CHANGE_CONFLICT);
3328
3556
  case "clean":
3329
- return chalk8.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3557
+ return chalk9.gray(MESSAGES.STATUS_CHANGE_CLEAN);
3330
3558
  }
3331
3559
  }
3332
3560
  function printSnapshotsSection(snapshots) {
3333
- printInfo(` ${chalk8.bold("\u25C6")} ${chalk8.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
3561
+ printInfo(` ${chalk9.bold("\u25C6")} ${chalk9.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
3334
3562
  if (snapshots.orphaned > 0) {
3335
- printInfo(` ${chalk8.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
3563
+ printInfo(` ${chalk9.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
3336
3564
  }
3337
3565
  printInfo("");
3338
3566
  }
3339
3567
 
3340
3568
  // src/commands/alias.ts
3341
- import chalk9 from "chalk";
3569
+ import chalk10 from "chalk";
3342
3570
  function getRegisteredCommandNames(program2) {
3343
3571
  return program2.commands.map((cmd) => cmd.name());
3344
3572
  }
@@ -3359,7 +3587,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
3359
3587
  `);
3360
3588
  printSeparator();
3361
3589
  for (const [alias, command] of entries) {
3362
- printInfo(` ${chalk9.bold(alias)} \u2192 ${chalk9.cyan(command)}`);
3590
+ printInfo(` ${chalk10.bold(alias)} \u2192 ${chalk10.cyan(command)}`);
3363
3591
  }
3364
3592
  printInfo("");
3365
3593
  printSeparator();
@@ -3405,8 +3633,187 @@ function registerAliasCommand(program2) {
3405
3633
  });
3406
3634
  }
3407
3635
 
3636
+ // src/commands/projects.ts
3637
+ import { existsSync as existsSync9, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
3638
+ import { join as join7 } from "path";
3639
+ import chalk11 from "chalk";
3640
+ function registerProjectsCommand(program2) {
3641
+ 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) => {
3642
+ handleProjects({ name, json: options.json });
3643
+ });
3644
+ }
3645
+ function handleProjects(options) {
3646
+ if (options.name) {
3647
+ handleProjectDetail(options.name, options.json);
3648
+ } else {
3649
+ handleProjectsOverview(options.json);
3650
+ }
3651
+ }
3652
+ function handleProjectsOverview(json) {
3653
+ const result = collectProjectsOverview();
3654
+ logger.info(`projects \u547D\u4EE4\u6267\u884C\uFF0C\u5171 ${result.totalProjects} \u4E2A\u9879\u76EE`);
3655
+ if (json) {
3656
+ console.log(JSON.stringify(result, null, 2));
3657
+ return;
3658
+ }
3659
+ printProjectsOverviewAsText(result);
3660
+ }
3661
+ function handleProjectDetail(name, json) {
3662
+ const projectDir = join7(WORKTREES_DIR, name);
3663
+ if (!existsSync9(projectDir)) {
3664
+ printError(MESSAGES.PROJECTS_NOT_FOUND(name));
3665
+ process.exit(1);
3666
+ }
3667
+ const result = collectProjectDetail(name, projectDir);
3668
+ logger.info(`projects \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${name}\uFF0C\u5171 ${result.worktrees.length} \u4E2A worktree`);
3669
+ if (json) {
3670
+ console.log(JSON.stringify(result, null, 2));
3671
+ return;
3672
+ }
3673
+ printProjectDetailAsText(result);
3674
+ }
3675
+ function collectProjectsOverview() {
3676
+ if (!existsSync9(WORKTREES_DIR)) {
3677
+ return { projects: [], totalProjects: 0, totalDiskUsage: 0 };
3678
+ }
3679
+ const entries = readdirSync5(WORKTREES_DIR, { withFileTypes: true });
3680
+ const projects = [];
3681
+ for (const entry of entries) {
3682
+ if (!entry.isDirectory()) {
3683
+ continue;
3684
+ }
3685
+ const projectDir = join7(WORKTREES_DIR, entry.name);
3686
+ const overview = collectSingleProjectOverview(entry.name, projectDir);
3687
+ projects.push(overview);
3688
+ }
3689
+ sortByLastActiveTimeDesc(projects);
3690
+ const totalDiskUsage = projects.reduce((sum, p) => sum + p.diskUsage, 0);
3691
+ return {
3692
+ projects,
3693
+ totalProjects: projects.length,
3694
+ totalDiskUsage
3695
+ };
3696
+ }
3697
+ function collectSingleProjectOverview(name, projectDir) {
3698
+ const subEntries = readdirSync5(projectDir, { withFileTypes: true });
3699
+ const worktreeDirs = subEntries.filter((e) => e.isDirectory());
3700
+ const worktreeCount = worktreeDirs.length;
3701
+ const diskUsage = calculateDirSize(projectDir);
3702
+ const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join7(projectDir, e.name)));
3703
+ return {
3704
+ name,
3705
+ worktreeCount,
3706
+ lastActiveTime,
3707
+ diskUsage
3708
+ };
3709
+ }
3710
+ function collectProjectDetail(name, projectDir) {
3711
+ const subEntries = readdirSync5(projectDir, { withFileTypes: true });
3712
+ const worktrees = [];
3713
+ for (const entry of subEntries) {
3714
+ if (!entry.isDirectory()) {
3715
+ continue;
3716
+ }
3717
+ const wtPath = join7(projectDir, entry.name);
3718
+ const detail = collectSingleWorktreeDetail(entry.name, wtPath);
3719
+ worktrees.push(detail);
3720
+ }
3721
+ worktrees.sort((a, b) => new Date(b.lastModifiedTime).getTime() - new Date(a.lastModifiedTime).getTime());
3722
+ const totalDiskUsage = worktrees.reduce((sum, wt) => sum + wt.diskUsage, 0);
3723
+ return {
3724
+ name,
3725
+ projectDir,
3726
+ worktrees,
3727
+ totalDiskUsage
3728
+ };
3729
+ }
3730
+ function collectSingleWorktreeDetail(branch, wtPath) {
3731
+ const stat = statSync3(wtPath);
3732
+ const diskUsage = calculateDirSize(wtPath);
3733
+ return {
3734
+ branch,
3735
+ path: wtPath,
3736
+ lastModifiedTime: formatLocalISOString(stat.mtime),
3737
+ diskUsage
3738
+ };
3739
+ }
3740
+ function resolveProjectLastActiveTime(projectDir, worktreePaths) {
3741
+ let latestTime = statSync3(projectDir).mtime;
3742
+ for (const wtPath of worktreePaths) {
3743
+ try {
3744
+ const wtStat = statSync3(wtPath);
3745
+ if (wtStat.mtime > latestTime) {
3746
+ latestTime = wtStat.mtime;
3747
+ }
3748
+ } catch {
3749
+ }
3750
+ }
3751
+ return formatLocalISOString(latestTime);
3752
+ }
3753
+ function sortByLastActiveTimeDesc(projects) {
3754
+ projects.sort((a, b) => new Date(b.lastActiveTime).getTime() - new Date(a.lastActiveTime).getTime());
3755
+ }
3756
+ function printProjectsOverviewAsText(result) {
3757
+ printDoubleSeparator();
3758
+ printInfo(` ${chalk11.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
3759
+ printDoubleSeparator();
3760
+ printInfo("");
3761
+ if (result.projects.length === 0) {
3762
+ printInfo(` ${MESSAGES.PROJECTS_NO_PROJECTS}`);
3763
+ printInfo("");
3764
+ printDoubleSeparator();
3765
+ return;
3766
+ }
3767
+ for (const project of result.projects) {
3768
+ printProjectOverviewItem(project);
3769
+ }
3770
+ printSeparator();
3771
+ printInfo("");
3772
+ printInfo(` \u5171 ${chalk11.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk11.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
3773
+ printInfo("");
3774
+ printDoubleSeparator();
3775
+ }
3776
+ function printProjectOverviewItem(project) {
3777
+ const relativeTime = formatRelativeTime(project.lastActiveTime);
3778
+ const activeLabel = relativeTime ? MESSAGES.PROJECTS_LAST_ACTIVE(relativeTime) : "";
3779
+ const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(project.diskUsage));
3780
+ printInfo(` ${chalk11.bold("\u25CF")} ${chalk11.bold(project.name)}`);
3781
+ printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk11.gray(activeLabel)} ${chalk11.gray(diskLabel)}`);
3782
+ printInfo("");
3783
+ }
3784
+ function printProjectDetailAsText(result) {
3785
+ printDoubleSeparator();
3786
+ printInfo(` ${chalk11.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
3787
+ printDoubleSeparator();
3788
+ printInfo("");
3789
+ printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
3790
+ printInfo(` ${MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage))}`);
3791
+ printInfo("");
3792
+ printSeparator();
3793
+ printInfo("");
3794
+ if (result.worktrees.length === 0) {
3795
+ printInfo(` ${MESSAGES.PROJECTS_DETAIL_NO_WORKTREES}`);
3796
+ printInfo("");
3797
+ printDoubleSeparator();
3798
+ return;
3799
+ }
3800
+ for (const wt of result.worktrees) {
3801
+ printWorktreeDetailItem(wt);
3802
+ }
3803
+ printDoubleSeparator();
3804
+ }
3805
+ function printWorktreeDetailItem(wt) {
3806
+ const relativeTime = formatRelativeTime(wt.lastModifiedTime);
3807
+ const modifiedLabel = relativeTime ? MESSAGES.PROJECTS_LAST_MODIFIED(relativeTime) : "";
3808
+ const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(wt.diskUsage));
3809
+ printInfo(` ${chalk11.bold("\u25CF")} ${chalk11.bold(wt.branch)}`);
3810
+ printInfo(` ${wt.path}`);
3811
+ printInfo(` ${chalk11.gray(modifiedLabel)} ${chalk11.gray(diskLabel)}`);
3812
+ printInfo("");
3813
+ }
3814
+
3408
3815
  // src/commands/completion.ts
3409
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync10 } from "fs";
3816
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync11 } from "fs";
3410
3817
  import { resolve as resolve2 } from "path";
3411
3818
  import { homedir as homedir2 } from "os";
3412
3819
 
@@ -3457,25 +3864,25 @@ compdef _clawt_completion clawt
3457
3864
  }
3458
3865
 
3459
3866
  // src/utils/completion-engine.ts
3460
- import { existsSync as existsSync9, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
3461
- import { join as join6, dirname, basename as basename2 } from "path";
3867
+ import { existsSync as existsSync10, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
3868
+ import { join as join8, dirname, basename as basename2 } from "path";
3462
3869
  function completeFilePath(partial) {
3463
3870
  const cwd = process.cwd();
3464
3871
  const hasDir = partial.includes("/");
3465
- const searchDir = hasDir ? join6(cwd, dirname(partial)) : cwd;
3872
+ const searchDir = hasDir ? join8(cwd, dirname(partial)) : cwd;
3466
3873
  const prefix = hasDir ? basename2(partial) : partial;
3467
- if (!existsSync9(searchDir)) {
3874
+ if (!existsSync10(searchDir)) {
3468
3875
  return [];
3469
3876
  }
3470
- const entries = readdirSync5(searchDir);
3877
+ const entries = readdirSync6(searchDir);
3471
3878
  const results = [];
3472
3879
  const dirPrefix = hasDir ? dirname(partial) + "/" : "";
3473
3880
  for (const entry of entries) {
3474
3881
  if (!entry.startsWith(prefix)) continue;
3475
3882
  if (entry.startsWith(".")) continue;
3476
- const fullPath = join6(searchDir, entry);
3883
+ const fullPath = join8(searchDir, entry);
3477
3884
  try {
3478
- const stat = statSync2(fullPath);
3885
+ const stat = statSync4(fullPath);
3479
3886
  if (stat.isDirectory()) {
3480
3887
  results.push(dirPrefix + entry + "/");
3481
3888
  } else if (stat.isFile()) {
@@ -3562,15 +3969,15 @@ function generateCompletions(program2, args) {
3562
3969
 
3563
3970
  // src/commands/completion.ts
3564
3971
  function appendToFile(filePath, content) {
3565
- if (existsSync10(filePath)) {
3566
- const current = readFileSync4(filePath, "utf-8");
3972
+ if (existsSync11(filePath)) {
3973
+ const current = readFileSync5(filePath, "utf-8");
3567
3974
  if (current.includes("clawt completion")) {
3568
3975
  printInfo(MESSAGES.COMPLETION_INSTALL_EXISTS + ": " + filePath);
3569
3976
  return;
3570
3977
  }
3571
3978
  content = current + content;
3572
3979
  }
3573
- writeFileSync3(filePath, content, "utf-8");
3980
+ writeFileSync4(filePath, content, "utf-8");
3574
3981
  printSuccess(MESSAGES.COMPLETION_INSTALL_SUCCESS + ": " + filePath);
3575
3982
  printInfo(MESSAGES.COMPLETION_INSTALL_RESTART(filePath));
3576
3983
  }
@@ -3640,6 +4047,7 @@ registerSyncCommand(program);
3640
4047
  registerResetCommand(program);
3641
4048
  registerStatusCommand(program);
3642
4049
  registerAliasCommand(program);
4050
+ registerProjectsCommand(program);
3643
4051
  registerCompletionCommand(program);
3644
4052
  var config = loadConfig();
3645
4053
  applyAliases(program, config.aliases);
@@ -3665,4 +4073,10 @@ process.on("unhandledRejection", (reason) => {
3665
4073
  logger.error(`\u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD: ${error.message}`);
3666
4074
  process.exit(EXIT_CODES.ERROR);
3667
4075
  });
3668
- program.parse(process.argv);
4076
+ async function main() {
4077
+ await program.parseAsync(process.argv);
4078
+ if (config.autoUpdate) {
4079
+ await checkForUpdates(version);
4080
+ }
4081
+ }
4082
+ main();