clawt 2.7.4 → 2.8.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.
@@ -16,8 +16,10 @@
16
16
 
17
17
  ### README.md
18
18
  - 面向用户的使用文档
19
+ - 全局选项在 `## 全局选项` 章节(位于 `## 使用前提` 和 `## 命令` 之间)
19
20
  - 每个命令一个 `###` 小节,含命令格式、参数表格、简要说明、示例
20
21
  - 配置文件说明在 `## 配置文件` 章节
22
+ - 日志说明在 `## 日志` 章节(文档末尾)
21
23
  - 更新模式:更新命令说明段落,配置项表格
22
24
 
23
25
  ## 关键约定
@@ -90,3 +92,12 @@ Notes:
90
92
  - `removeSnapshot()` 同时清理 `.tree` 和 `.head` 文件
91
93
  - merge 成功后自动清理对应快照;merge 时主 worktree 脏 + 存在快照会输出警告提示
92
94
  - docs/spec.md 中 validate 章节(5.4)按 `--clean 模式`、`首次 validate`、`增量 validate` 三段描述
95
+
96
+ ## 全局选项
97
+
98
+ - `--debug` 全局选项在 `src/index.ts` 通过 Commander.js `.option()` + `preAction` 钩子实现
99
+ - `enableConsoleTransport()` 在 `src/logger/index.ts`,幂等地向 winston 添加 Console transport
100
+ - 调试相关常量在 `src/constants/logger.ts`:`DEBUG_LOG_PREFIX`、`DEBUG_TIMESTAMP_FORMAT`
101
+ - docs/spec.md 中 `--debug` 说明位于 `5.9 日志系统` 章节下的 `#### --debug 控制台调试输出` 子章节
102
+ - docs/spec.md 中 `4. 命令总览` 的命令表格后有 `**全局选项:**` 表格
103
+ - README.md 中 `## 全局选项` 章节(在 `## 使用前提` 和 `## 命令` 之间)
package/README.md CHANGED
@@ -5,6 +5,10 @@
5
5
  ## 安装
6
6
 
7
7
  ```bash
8
+ # 推荐
9
+ pnpm add -g clawt
10
+
11
+ # 或使用 npm
8
12
  npm i -g clawt
9
13
  ```
10
14
 
@@ -18,6 +22,19 @@ npm i -g clawt
18
22
 
19
23
  所有命令**必须在主 worktree 的仓库根目录**下执行(即包含 `.git` 目录的原始仓库)。在子 worktree 或子目录中执行会被拒绝。
20
24
 
25
+ ## 全局选项
26
+
27
+ | 选项 | 说明 |
28
+ | ---- | ---- |
29
+ | `--debug` | 输出详细调试信息到终端,实时显示带颜色和时间戳的日志 |
30
+
31
+ `--debug` 可与任意子命令组合使用:
32
+
33
+ ```bash
34
+ clawt run -b feature-login --debug
35
+ clawt validate -b scheme --debug
36
+ ```
37
+
21
38
  ## 命令
22
39
 
23
40
  ### `clawt create` — 批量创建 worktree
@@ -313,4 +330,4 @@ feature/a.b → feature-a-b
313
330
 
314
331
  ## 日志
315
332
 
316
- 日志保存在 `~/.clawt/logs/` 目录,按日期滚动,保留 30 天。
333
+ 日志保存在 `~/.clawt/logs/` 目录,按日期滚动,保留 30 天。使用 `--debug` 全局选项可在终端实时查看调试日志。
package/dist/index.js CHANGED
@@ -221,6 +221,9 @@ var CONFIG_DESCRIPTIONS = deriveConfigDescriptions(CONFIG_DEFINITIONS);
221
221
  // src/constants/git.ts
222
222
  var AUTO_SAVE_COMMIT_MESSAGE = "chore: auto-save before sync";
223
223
 
224
+ // src/constants/logger.ts
225
+ var DEBUG_TIMESTAMP_FORMAT = "HH:mm:ss.SSS";
226
+
224
227
  // src/errors/index.ts
225
228
  var ClawtError = class extends Error {
226
229
  /** 退出码 */
@@ -239,6 +242,7 @@ var ClawtError = class extends Error {
239
242
  // src/logger/index.ts
240
243
  import winston from "winston";
241
244
  import DailyRotateFile from "winston-daily-rotate-file";
245
+ import chalk from "chalk";
242
246
  import { existsSync, mkdirSync } from "fs";
243
247
  if (!existsSync(LOGS_DIR)) {
244
248
  mkdirSync(LOGS_DIR, { recursive: true });
@@ -262,6 +266,35 @@ var logger = winston.createLogger({
262
266
  ),
263
267
  transports: [dailyRotateTransport]
264
268
  });
269
+ var LEVEL_COLORS = {
270
+ error: chalk.red,
271
+ warn: chalk.yellow,
272
+ info: chalk.cyan,
273
+ debug: chalk.gray
274
+ };
275
+ function colorizeLevel(level) {
276
+ const colorFn = LEVEL_COLORS[level] || chalk.white;
277
+ return colorFn(level.toUpperCase().padEnd(5));
278
+ }
279
+ function enableConsoleTransport() {
280
+ const hasConsole = logger.transports.some(
281
+ (t) => t instanceof winston.transports.Console
282
+ );
283
+ if (hasConsole) {
284
+ return;
285
+ }
286
+ const consoleFormat = winston.format.printf(({ level, message, timestamp }) => {
287
+ return `${chalk.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
288
+ });
289
+ const consoleTransport = new winston.transports.Console({
290
+ level: "debug",
291
+ format: winston.format.combine(
292
+ winston.format.timestamp({ format: DEBUG_TIMESTAMP_FORMAT }),
293
+ consoleFormat
294
+ )
295
+ });
296
+ logger.add(consoleTransport);
297
+ }
265
298
 
266
299
  // src/utils/shell.ts
267
300
  import { execSync, execFileSync, spawn } from "child_process";
@@ -467,16 +500,16 @@ function gitApplyCachedCheck(patchContent, cwd) {
467
500
  }
468
501
 
469
502
  // src/utils/formatter.ts
470
- import chalk from "chalk";
503
+ import chalk2 from "chalk";
471
504
  import { createInterface } from "readline";
472
505
  function printSuccess(message) {
473
- console.log(chalk.green(message));
506
+ console.log(chalk2.green(message));
474
507
  }
475
508
  function printError(message) {
476
- console.error(chalk.red(`\u2717 ${message}`));
509
+ console.error(chalk2.red(`\u2717 ${message}`));
477
510
  }
478
511
  function printWarning(message) {
479
- console.log(chalk.yellow(`\u26A0 ${message}`));
512
+ console.log(chalk2.yellow(`\u26A0 ${message}`));
480
513
  }
481
514
  function printInfo(message) {
482
515
  console.log(message);
@@ -500,7 +533,7 @@ function confirmAction(question) {
500
533
  });
501
534
  }
502
535
  function confirmDestructiveAction(dangerousCommand, description) {
503
- printWarning(`\u5373\u5C06\u6267\u884C ${chalk.red.bold(dangerousCommand)}\uFF0C${description}`);
536
+ printWarning(`\u5373\u5C06\u6267\u884C ${chalk2.red.bold(dangerousCommand)}\uFF0C${description}`);
504
537
  return confirmAction("\u662F\u5426\u7EE7\u7EED\uFF1F");
505
538
  }
506
539
  function isWorktreeIdle(status) {
@@ -508,17 +541,17 @@ function isWorktreeIdle(status) {
508
541
  }
509
542
  function formatWorktreeStatus(status) {
510
543
  const parts = [];
511
- parts.push(chalk.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
544
+ parts.push(chalk2.yellow(`${status.commitCount} \u4E2A\u63D0\u4EA4`));
512
545
  if (status.insertions === 0 && status.deletions === 0) {
513
546
  parts.push("\u65E0\u53D8\u66F4");
514
547
  } else {
515
548
  const diffParts = [];
516
- diffParts.push(chalk.green(`+${status.insertions}`));
517
- diffParts.push(chalk.red(`-${status.deletions}`));
549
+ diffParts.push(chalk2.green(`+${status.insertions}`));
550
+ diffParts.push(chalk2.red(`-${status.deletions}`));
518
551
  parts.push(diffParts.join(" "));
519
552
  }
520
553
  if (status.hasDirtyFiles) {
521
- parts.push(chalk.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
554
+ parts.push(chalk2.gray("(\u672A\u63D0\u4EA4\u4FEE\u6539)"));
522
555
  }
523
556
  return parts.join(" ");
524
557
  }
@@ -826,7 +859,7 @@ async function resolveTargetWorktree(worktrees, messages, branchName) {
826
859
  }
827
860
 
828
861
  // src/commands/list.ts
829
- import chalk2 from "chalk";
862
+ import chalk3 from "chalk";
830
863
  function registerListCommand(program2) {
831
864
  program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
832
865
  handleList(options);
@@ -863,12 +896,12 @@ function printListAsText(projectName, worktrees) {
863
896
  for (const wt of worktrees) {
864
897
  const status = getWorktreeStatus(wt);
865
898
  const isIdle = status ? isWorktreeIdle(status) : false;
866
- const pathDisplay = isIdle ? chalk2.hex("#FF8C00")(wt.path) : wt.path;
899
+ const pathDisplay = isIdle ? chalk3.hex("#FF8C00")(wt.path) : wt.path;
867
900
  printInfo(` ${pathDisplay} [${wt.branch}]`);
868
901
  if (status) {
869
902
  printInfo(` ${formatWorktreeStatus(status)}`);
870
903
  } else {
871
- printInfo(` ${chalk2.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
904
+ printInfo(` ${chalk3.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
872
905
  }
873
906
  printInfo("");
874
907
  }
@@ -1467,7 +1500,7 @@ async function handleMerge(options) {
1467
1500
  }
1468
1501
 
1469
1502
  // src/commands/config.ts
1470
- import chalk3 from "chalk";
1503
+ import chalk4 from "chalk";
1471
1504
  function registerConfigCommand(program2) {
1472
1505
  const configCmd = program2.command("config").description("\u67E5\u770B\u548C\u7BA1\u7406\u5168\u5C40\u914D\u7F6E").action(() => {
1473
1506
  handleConfig();
@@ -1480,7 +1513,7 @@ function handleConfig() {
1480
1513
  const config = loadConfig();
1481
1514
  logger.info("config \u547D\u4EE4\u6267\u884C\uFF0C\u5C55\u793A\u5168\u5C40\u914D\u7F6E");
1482
1515
  printInfo(`
1483
- ${chalk3.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1516
+ ${chalk4.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1484
1517
  `);
1485
1518
  printSeparator();
1486
1519
  const keys = Object.keys(DEFAULT_CONFIG);
@@ -1490,8 +1523,8 @@ ${chalk3.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1490
1523
  const description = CONFIG_DESCRIPTIONS[key];
1491
1524
  const formattedValue = formatConfigValue(value);
1492
1525
  if (i === 0) printInfo("");
1493
- printInfo(` ${chalk3.bold(key)}: ${formattedValue}`);
1494
- printInfo(` ${chalk3.dim(description)}`);
1526
+ printInfo(` ${chalk4.bold(key)}: ${formattedValue}`);
1527
+ printInfo(` ${chalk4.dim(description)}`);
1495
1528
  printInfo("");
1496
1529
  }
1497
1530
  printSeparator();
@@ -1511,9 +1544,9 @@ async function handleConfigReset() {
1511
1544
  }
1512
1545
  function formatConfigValue(value) {
1513
1546
  if (typeof value === "boolean") {
1514
- return value ? chalk3.green("true") : chalk3.yellow("false");
1547
+ return value ? chalk4.green("true") : chalk4.yellow("false");
1515
1548
  }
1516
- return chalk3.cyan(String(value));
1549
+ return chalk4.cyan(String(value));
1517
1550
  }
1518
1551
 
1519
1552
  // src/commands/sync.ts
@@ -1604,7 +1637,12 @@ var require2 = createRequire(import.meta.url);
1604
1637
  var { version } = require2("../package.json");
1605
1638
  ensureClawtDirs();
1606
1639
  var program = new Command();
1607
- program.name("clawt").description("\u672C\u5730\u5E76\u884C\u6267\u884C\u591A\u4E2AClaude Code Agent\u4EFB\u52A1\uFF0C\u878D\u5408 Git Worktree \u4E0E Claude Code CLI \u7684\u547D\u4EE4\u884C\u5DE5\u5177").version(version);
1640
+ program.name("clawt").description("\u672C\u5730\u5E76\u884C\u6267\u884C\u591A\u4E2AClaude Code Agent\u4EFB\u52A1\uFF0C\u878D\u5408 Git Worktree \u4E0E Claude Code CLI \u7684\u547D\u4EE4\u884C\u5DE5\u5177").version(version).option("--debug", "\u8F93\u51FA\u8BE6\u7EC6\u8C03\u8BD5\u4FE1\u606F\u5230\u7EC8\u7AEF");
1641
+ program.hook("preAction", (thisCommand) => {
1642
+ if (thisCommand.opts().debug) {
1643
+ enableConsoleTransport();
1644
+ }
1645
+ });
1608
1646
  registerListCommand(program);
1609
1647
  registerCreateCommand(program);
1610
1648
  registerRemoveCommand(program);
package/docs/spec.md CHANGED
@@ -36,12 +36,12 @@
36
36
  | -------- | ----------------------------- |
37
37
  | 运行时 | Node.js >= 18 |
38
38
  | 语言 | TypeScript |
39
- | 包管理 | npm |
39
+ | 包管理 | pnpm |
40
40
  | CLI 框架 | Commander.js |
41
41
  | 日志库 | winston (按日期滚动文件) |
42
42
  | 交互式 | enquirer (选项选择/确认对话) |
43
43
  | 构建 | tsup / tsc |
44
- | 分发 | npm 全局安装 (`npm i -g clawt`) |
44
+ | 分发 | pnpm 全局安装 (`pnpm add -g clawt`) |
45
45
 
46
46
  ---
47
47
 
@@ -173,6 +173,12 @@ git show-ref --verify refs/heads/<branchName> 2>/dev/null
173
173
  | `clawt sync` | 将主分支最新代码同步到目标 worktree | 5.12 |
174
174
  | `clawt reset` | 重置主 worktree 工作区和暂存区 | 5.13 |
175
175
 
176
+ **全局选项:**
177
+
178
+ | 选项 | 说明 |
179
+ | --------- | ---------------------------------------- |
180
+ | `--debug` | 输出详细调试信息到终端(启用 Console transport) |
181
+
176
182
  所有命令执行前,都必须先执行**主 worktree 校验**(见 [2.1](#21-主-worktree-的定义与定位规则))。
177
183
 
178
184
  ---
@@ -748,7 +754,7 @@ clawt merge [-m <commitMessage>]
748
754
 
749
755
  **路径:** `~/.clawt/config.json`
750
756
 
751
- **生成时机:** npm 全局安装后自动生成(通过 npm 的 `postinstall` 脚本)。
757
+ **生成时机:** 全局安装后自动生成(通过 `postinstall` 脚本)。
752
758
 
753
759
  **升级策略:** 配置文件已存在时,执行增量合并而非简单跳过:
754
760
 
@@ -879,6 +885,42 @@ clawt list [--json]
879
885
  - 日志文件保留 30 天
880
886
  - 单个日志文件最大 10MB
881
887
 
888
+ #### `--debug` 控制台调试输出
889
+
890
+ 通过全局选项 `--debug` 可将调试日志实时输出到终端,方便排查问题。
891
+
892
+ **实现机制:**
893
+
894
+ - 在 Commander.js 的 `preAction` 钩子中检测 `--debug` 选项,按需调用 `enableConsoleTransport()` 函数
895
+ - `enableConsoleTransport()` 动态向 winston 实例添加 `Console` transport(level 为 `debug`),该函数幂等,多次调用不会重复添加 transport
896
+ - 相关常量定义在 `src/constants/logger.ts`:
897
+ - `DEBUG_LOG_PREFIX`:控制台调试输出的日志前缀标识
898
+ - `DEBUG_TIMESTAMP_FORMAT`:时间戳格式(`HH:mm:ss.SSS`,精简,不含日期)
899
+
900
+ **控制台日志格式:**
901
+
902
+ ```
903
+ HH:mm:ss.SSS LEVEL 消息内容
904
+ ```
905
+
906
+ **日志级别颜色映射:**
907
+
908
+ | 级别 | 颜色 |
909
+ | ------- | ------ |
910
+ | `error` | 红色 |
911
+ | `warn` | 黄色 |
912
+ | `info` | 青色 |
913
+ | `debug` | 灰色 |
914
+
915
+ **使用示例:**
916
+
917
+ ```bash
918
+ clawt run -b feature-login --debug
919
+ clawt validate -b feature-scheme --debug
920
+ ```
921
+
922
+ > **注意:** `--debug` 选项不影响文件日志(file transport),文件日志始终按原有策略写入。控制台输出仅在传入 `--debug` 时启用。
923
+
882
924
  ---
883
925
 
884
926
  ### 5.10 查看和管理全局配置
package/package.json CHANGED
@@ -1,18 +1,12 @@
1
1
  {
2
2
  "name": "clawt",
3
- "version": "2.7.4",
3
+ "version": "2.8.1",
4
4
  "description": "本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "clawt": "dist/index.js"
9
9
  },
10
- "scripts": {
11
- "build": "tsup",
12
- "dev": "tsup --watch",
13
- "postinstall": "node dist/postinstall.js",
14
- "release": "bash scripts/release.sh"
15
- },
16
10
  "keywords": [
17
11
  "claude",
18
12
  "worktree",
@@ -35,5 +29,11 @@
35
29
  },
36
30
  "engines": {
37
31
  "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "dev": "tsup --watch",
36
+ "postinstall": "node dist/postinstall.js",
37
+ "release": "bash scripts/release.sh"
38
38
  }
39
- }
39
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * postinstall 脚本:npm 全局安装后初始化 ~/.clawt/ 目录
2
+ * postinstall 脚本:全局安装后初始化 ~/.clawt/ 目录
3
3
  */
4
4
 
5
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  # ============================================================
3
3
  # release.sh - Clawt 自动发布脚本
4
- # 功能: 构建 → 交互式选择版本级别 → 更新 package.json → 提交 → 打 tag → push → npm publish
4
+ # 功能: 构建 → 交互式选择版本级别 → 更新 package.json → 提交 → 打 tag → push → pnpm publish
5
5
  # 用法: bash scripts/release.sh
6
6
  # ============================================================
7
7
 
@@ -138,7 +138,7 @@ echo ""
138
138
 
139
139
  TAG="v${NEW_VERSION}"
140
140
 
141
- # 回滚函数:npm publish 失败时撤销 commit、tag、push
141
+ # 回滚函数:pnpm publish 失败时撤销 commit、tag、push
142
142
  # 参数 $1: 是否已推送到远程("pushed" 表示已推送)
143
143
  rollback() {
144
144
  local pushed="${1:-}"
@@ -166,7 +166,7 @@ rollback() {
166
166
  # ────────────────────────────────────────
167
167
 
168
168
  print_step "构建项目..."
169
- npm run build
169
+ pnpm build
170
170
  print_success "构建完成"
171
171
 
172
172
  # ────────────────────────────────────────
@@ -174,8 +174,8 @@ print_success "构建完成"
174
174
  # ────────────────────────────────────────
175
175
 
176
176
  print_step "更新版本号: ${CURRENT_VERSION} → ${NEW_VERSION}"
177
- # 使用 npm version 更新版本号,--no-git-tag-version 避免 npm 自动打 tag
178
- npm version "$NEW_VERSION" --no-git-tag-version > /dev/null
177
+ # 使用 pnpm version 更新版本号,--no-git-tag-version 避免自动打 tag
178
+ pnpm version "$NEW_VERSION" --no-git-tag-version > /dev/null
179
179
  print_success "版本号已更新"
180
180
 
181
181
  # ────────────────────────────────────────
@@ -183,7 +183,7 @@ print_success "版本号已更新"
183
183
  # ────────────────────────────────────────
184
184
 
185
185
  print_step "提交版本变更..."
186
- git add package.json package-lock.json 2>/dev/null || git add package.json
186
+ git add package.json pnpm-lock.yaml 2>/dev/null || git add package.json
187
187
  git commit -m "build: bump version to ${NEW_VERSION}"
188
188
  print_success "版本变更已提交"
189
189
 
@@ -197,14 +197,14 @@ print_success "tag 已创建: ${TAG}"
197
197
 
198
198
  # ────────────────────────────────────────
199
199
  # 步骤 5: 发布到 npm(先发布,成功后再推送 git)
200
- # 调整顺序:npm publish 放在 git push 之前
200
+ # 调整顺序:pnpm publish 放在 git push 之前
201
201
  # 如果 publish 失败,只需回滚本地 commit 和 tag,无需处理远程
202
202
  # ────────────────────────────────────────
203
203
 
204
204
  print_step "发布到 npm..."
205
- # 临时关闭 set -e,手动捕获 npm publish 的退出码
205
+ # 临时关闭 set -e,手动捕获 pnpm publish 的退出码
206
206
  set +e
207
- NPM_OUTPUT=$(npm publish --access public --registry https://registry.npmjs.org/ 2>&1)
207
+ NPM_OUTPUT=$(pnpm publish --access public --registry https://registry.npmjs.org/ --no-git-checks 2>&1)
208
208
  NPM_EXIT_CODE=$?
209
209
  set -e
210
210
 
@@ -5,3 +5,4 @@ export { EXIT_CODES } from './exitCodes.js';
5
5
  export { ENABLE_BRACKETED_PASTE, DISABLE_BRACKETED_PASTE, PASTE_THRESHOLD_MS } from './terminal.js';
6
6
  export { DEFAULT_CONFIG, CONFIG_DESCRIPTIONS, APPEND_SYSTEM_PROMPT } from './config.js';
7
7
  export { AUTO_SAVE_COMMIT_MESSAGE } from './git.js';
8
+ export { DEBUG_LOG_PREFIX, DEBUG_TIMESTAMP_FORMAT } from './logger.js';
@@ -0,0 +1,5 @@
1
+ /** 控制台调试输出的日志前缀标识 */
2
+ export const DEBUG_LOG_PREFIX = '[DEBUG]';
3
+
4
+ /** 控制台调试日志的时间戳格式(精简,不含日期) */
5
+ export const DEBUG_TIMESTAMP_FORMAT = 'HH:mm:ss.SSS';
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { Command } from 'commander';
3
3
  import { ClawtError } from './errors/index.js';
4
- import { logger } from './logger/index.js';
4
+ import { logger, enableConsoleTransport } from './logger/index.js';
5
5
  import { EXIT_CODES } from './constants/index.js';
6
6
  import { printError, ensureClawtDirs } from './utils/index.js';
7
7
  import { registerListCommand } from './commands/list.js';
@@ -27,7 +27,15 @@ const program = new Command();
27
27
  program
28
28
  .name('clawt')
29
29
  .description('本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具')
30
- .version(version);
30
+ .version(version)
31
+ .option('--debug', '输出详细调试信息到终端');
32
+
33
+ // 在子命令 action 执行前检查 --debug 选项,按需启用控制台日志
34
+ program.hook('preAction', (thisCommand) => {
35
+ if (thisCommand.opts().debug) {
36
+ enableConsoleTransport();
37
+ }
38
+ });
31
39
 
32
40
  // 注册所有命令
33
41
  registerListCommand(program);
@@ -1,6 +1,7 @@
1
1
  import winston from 'winston';
2
2
  import DailyRotateFile from 'winston-daily-rotate-file';
3
- import { LOGS_DIR } from '../constants/index.js';
3
+ import chalk from 'chalk';
4
+ import { LOGS_DIR, DEBUG_TIMESTAMP_FORMAT } from '../constants/index.js';
4
5
  import { existsSync, mkdirSync } from 'node:fs';
5
6
 
6
7
  // 确保日志目录存在
@@ -32,3 +33,51 @@ export const logger = winston.createLogger({
32
33
  ),
33
34
  transports: [dailyRotateTransport],
34
35
  });
36
+
37
+ /** 日志级别颜色映射 */
38
+ const LEVEL_COLORS: Record<string, (text: string) => string> = {
39
+ error: chalk.red,
40
+ warn: chalk.yellow,
41
+ info: chalk.cyan,
42
+ debug: chalk.gray,
43
+ };
44
+
45
+ /**
46
+ * 为日志级别添加对应颜色
47
+ * @param {string} level - 日志级别
48
+ * @returns {string} 带颜色的日志级别字符串
49
+ */
50
+ function colorizeLevel(level: string): string {
51
+ const colorFn = LEVEL_COLORS[level] || chalk.white;
52
+ return colorFn(level.toUpperCase().padEnd(5));
53
+ }
54
+
55
+ /**
56
+ * 启用控制台日志输出(--debug 模式)
57
+ * 动态向 winston 实例添加 Console transport,输出带颜色和时间戳的调试信息
58
+ * 该函数幂等,多次调用不会重复添加 transport
59
+ */
60
+ export function enableConsoleTransport(): void {
61
+ // 幂等保护:检查是否已添加过 Console transport
62
+ const hasConsole = logger.transports.some(
63
+ (t) => t instanceof winston.transports.Console,
64
+ );
65
+ if (hasConsole) {
66
+ return;
67
+ }
68
+
69
+ /** --debug 模式的控制台日志格式:精简时间戳 + 带颜色级别 + 消息 */
70
+ const consoleFormat = winston.format.printf(({ level, message, timestamp }) => {
71
+ return `${chalk.gray(timestamp)} ${colorizeLevel(level)} ${message}`;
72
+ });
73
+
74
+ const consoleTransport = new winston.transports.Console({
75
+ level: 'debug',
76
+ format: winston.format.combine(
77
+ winston.format.timestamp({ format: DEBUG_TIMESTAMP_FORMAT }),
78
+ consoleFormat,
79
+ ),
80
+ });
81
+
82
+ logger.add(consoleTransport);
83
+ }