@hangox/pm-cli 0.0.1 → 0.2.2

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
@@ -334,51 +334,210 @@ function validateCredentials(creds, requiredFields = ["token", "host", "project"
334
334
 
335
335
  // src/utils/output.ts
336
336
  import { writeFileSync as writeFileSync2 } from "fs";
337
+
338
+ // src/utils/key-translator.ts
339
+ var KEY_MAP = {
340
+ // 问题 (Issue) 相关字段
341
+ id: "\u7F16\u53F7",
342
+ subject: "\u6807\u9898",
343
+ description: "\u63CF\u8FF0",
344
+ status: "\u72B6\u6001",
345
+ tracker: "\u8DDF\u8E2A\u5668",
346
+ priority: "\u4F18\u5148\u7EA7",
347
+ author: "\u521B\u5EFA\u8005",
348
+ assigned_to: "\u8D1F\u8D23\u4EBA",
349
+ project: "\u9879\u76EE",
350
+ parent: "\u7236\u5355",
351
+ parent_id: "\u7236\u5355\u7F16\u53F7",
352
+ parent_issue_id: "\u7236\u5355\u7F16\u53F7",
353
+ root_id: "\u6839\u5355\u7F16\u53F7",
354
+ children: "\u5B50\u5355\u5217\u8868",
355
+ level: "\u5C42\u7EA7",
356
+ fixed_version: "\u76EE\u6807\u7248\u672C",
357
+ version: "\u7248\u672C",
358
+ start_date: "\u5F00\u59CB\u65E5\u671F",
359
+ due_date: "\u622A\u6B62\u65E5\u671F",
360
+ estimated_hours: "\u9884\u4F30\u5DE5\u65F6",
361
+ spent_hours: "\u5DF2\u7528\u5DE5\u65F6",
362
+ done_ratio: "\u5B8C\u6210\u5EA6",
363
+ created_on: "\u521B\u5EFA\u65F6\u95F4",
364
+ updated_on: "\u66F4\u65B0\u65F6\u95F4",
365
+ closed_on: "\u5173\u95ED\u65F6\u95F4",
366
+ relations: "\u5173\u8054\u95EE\u9898",
367
+ custom_fields: "\u81EA\u5B9A\u4E49\u5B57\u6BB5",
368
+ attachments: "\u9644\u4EF6",
369
+ journals: "\u5386\u53F2\u8BB0\u5F55",
370
+ watchers: "\u8DDF\u8E2A\u8005",
371
+ watcher_user_ids: "\u8DDF\u8E2A\u8005\u7F16\u53F7",
372
+ // 通用对象字段
373
+ name: "\u540D\u79F0",
374
+ value: "\u503C",
375
+ mail: "\u90AE\u7BB1",
376
+ login: "\u767B\u5F55\u540D",
377
+ firstname: "\u540D",
378
+ lastname: "\u59D3",
379
+ identifier: "\u6807\u8BC6\u7B26",
380
+ is_public: "\u662F\u5426\u516C\u5F00",
381
+ // 工时 (TimeEntry) 相关字段
382
+ hours: "\u5DE5\u65F6",
383
+ spent_on: "\u5DE5\u65F6\u65E5\u671F",
384
+ activity: "\u6D3B\u52A8\u7C7B\u578B",
385
+ activity_id: "\u6D3B\u52A8\u7C7B\u578B\u7F16\u53F7",
386
+ comments: "\u5907\u6CE8",
387
+ time_entries: "\u5DE5\u65F6\u6761\u76EE",
388
+ time_entry: "\u5DE5\u65F6\u6761\u76EE",
389
+ total_count: "\u603B\u6570",
390
+ total_hours: "\u603B\u5DE5\u65F6",
391
+ // 用户 (User) 相关字段
392
+ user: "\u7528\u6237",
393
+ user_id: "\u7528\u6237\u7F16\u53F7",
394
+ author_mail: "\u4F5C\u8005\u90AE\u7BB1",
395
+ assigned_to_mail: "\u8D1F\u8D23\u4EBA\u90AE\u7BB1",
396
+ assigned_to_id: "\u8D1F\u8D23\u4EBA\u7F16\u53F7",
397
+ follows: "\u8DDF\u8FDBQA",
398
+ last_login_on: "\u6700\u540E\u767B\u5F55\u65F6\u95F4",
399
+ // API 响应字段
400
+ success: "\u6210\u529F",
401
+ data: "\u6570\u636E",
402
+ error: "\u9519\u8BEF",
403
+ message: "\u6D88\u606F",
404
+ msg: "\u6D88\u606F",
405
+ api_error_msg: "\u63A5\u53E3\u9519\u8BEF",
406
+ post_error_msg: "\u8BF7\u6C42\u9519\u8BEF",
407
+ output: "\u8F93\u51FA\u8DEF\u5F84",
408
+ issues: "\u95EE\u9898\u5217\u8868",
409
+ list: "\u5217\u8868",
410
+ total: "\u603B\u6570",
411
+ // 查询/统计相关字段
412
+ offset: "\u504F\u79FB\u91CF",
413
+ limit: "\u9650\u5236\u6570",
414
+ page: "\u9875\u7801",
415
+ per_page: "\u6BCF\u9875\u6570\u91CF",
416
+ period: "\u65F6\u95F4\u6BB5",
417
+ targetHours: "\u76EE\u6807\u5DE5\u65F6",
418
+ remainingHours: "\u5269\u4F59\u5DE5\u65F6",
419
+ isFilled: "\u662F\u5426\u586B\u6EE1",
420
+ projectBreakdown: "\u9879\u76EE\u5206\u5E03",
421
+ project_summary: "\u9879\u76EE\u6C47\u603B",
422
+ // 版本 (Version) 相关字段
423
+ effective_date: "\u6709\u6548\u65E5\u671F",
424
+ sharing: "\u5171\u4EAB\u8303\u56F4",
425
+ wiki_page_title: "Wiki\u9875\u9762",
426
+ // 自定义字段相关
427
+ custom_field: "\u81EA\u5B9A\u4E49\u5B57\u6BB5",
428
+ identify: "\u6807\u8BC6",
429
+ // 选项/配置相关
430
+ options: "\u9009\u9879",
431
+ activities: "\u6D3B\u52A8\u7C7B\u578B\u5217\u8868",
432
+ trackers: "\u8DDF\u8E2A\u5668\u5217\u8868",
433
+ statuses: "\u72B6\u6001\u5217\u8868",
434
+ priorities: "\u4F18\u5148\u7EA7\u5217\u8868",
435
+ versions: "\u7248\u672C\u5217\u8868",
436
+ users: "\u7528\u6237\u5217\u8868",
437
+ projects: "\u9879\u76EE\u5217\u8868",
438
+ // 其他字段
439
+ notes: "\u8BF4\u660E",
440
+ is_pending_version: "\u662F\u5426\u5F85\u5B9A\u7248\u672C",
441
+ count: "\u6570\u91CF",
442
+ category: "\u5206\u7C7B",
443
+ category_id: "\u5206\u7C7B\u7F16\u53F7"
444
+ };
445
+ var keyMappingEnabled = true;
446
+ function setKeyMappingEnabled(enabled) {
447
+ keyMappingEnabled = enabled;
448
+ }
449
+ function isKeyMappingEnabled() {
450
+ return keyMappingEnabled;
451
+ }
452
+ function collectKeysRecursive(obj, keys) {
453
+ if (obj === null || obj === void 0) return;
454
+ if (typeof obj !== "object") return;
455
+ if (Array.isArray(obj)) {
456
+ for (const item of obj) {
457
+ collectKeysRecursive(item, keys);
458
+ }
459
+ return;
460
+ }
461
+ for (const [key, value] of Object.entries(obj)) {
462
+ keys.add(key);
463
+ collectKeysRecursive(value, keys);
464
+ }
465
+ }
466
+ function collectUsedKeys(obj) {
467
+ const keys = /* @__PURE__ */ new Set();
468
+ collectKeysRecursive(obj, keys);
469
+ return Array.from(keys);
470
+ }
471
+ function generateKeyMapping(obj) {
472
+ const usedKeys = collectUsedKeys(obj);
473
+ const mapping = {};
474
+ for (const key of usedKeys) {
475
+ if (KEY_MAP[key]) {
476
+ mapping[key] = KEY_MAP[key];
477
+ }
478
+ }
479
+ return mapping;
480
+ }
481
+
482
+ // src/utils/output.ts
337
483
  function generateOutputPath(command, identifier) {
338
484
  const now = /* @__PURE__ */ new Date();
339
485
  const timestamp = now.getFullYear().toString() + (now.getMonth() + 1).toString().padStart(2, "0") + now.getDate().toString().padStart(2, "0") + now.getHours().toString().padStart(2, "0") + now.getMinutes().toString().padStart(2, "0") + now.getSeconds().toString().padStart(2, "0");
340
486
  return `/tmp/pm-cli_${command}_${identifier}_${timestamp}.json`;
341
487
  }
342
- function outputToFile(data, filePath) {
488
+ function addKeyMappingToResult(result) {
489
+ if (!isKeyMappingEnabled()) {
490
+ return result;
491
+ }
492
+ const mapping = generateKeyMapping(result);
493
+ return {
494
+ ...result,
495
+ _key_mapping: mapping
496
+ };
497
+ }
498
+ function outputToFile(data, filePath, pretty = false) {
343
499
  const result = {
344
500
  success: true,
345
501
  data
346
502
  };
347
- writeFileSync2(filePath, JSON.stringify(result, null, 2), "utf-8");
503
+ const finalResult = addKeyMappingToResult(result);
504
+ writeFileSync2(filePath, JSON.stringify(finalResult, null, pretty ? 2 : 0), "utf-8");
348
505
  return filePath;
349
506
  }
350
507
  function smartOutput(data, options) {
351
508
  if (options.stdout) {
352
- outputSuccess(data);
509
+ outputSuccess(data, options.pretty);
353
510
  return void 0;
354
511
  }
355
512
  const filePath = options.output || generateOutputPath(options.command, options.identifier);
356
- outputToFile(data, filePath);
357
- console.log(JSON.stringify({ success: true, output: filePath }, null, 2));
513
+ outputToFile(data, filePath, options.pretty);
514
+ console.log(JSON.stringify({ success: true, output: filePath }, null, options.pretty ? 2 : 0));
358
515
  return filePath;
359
516
  }
360
- function outputSuccess(data) {
517
+ function outputSuccess(data, pretty = false) {
361
518
  const result = {
362
519
  success: true,
363
520
  data
364
521
  };
365
- console.log(JSON.stringify(result, null, 2));
522
+ const finalResult = addKeyMappingToResult(result);
523
+ console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0));
366
524
  }
367
- function outputError(error) {
525
+ function outputError(error, pretty = false) {
368
526
  const result = {
369
527
  success: false,
370
528
  error
371
529
  };
372
- console.log(JSON.stringify(result, null, 2));
530
+ const finalResult = addKeyMappingToResult(result);
531
+ console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0));
373
532
  }
374
533
 
375
534
  // src/commands/test.ts
376
535
  function createTestCommand() {
377
- const testCmd = new Command("test").description("\u6D4B\u8BD5\u7F51\u6613\u6613\u534F\u4F5C\u8FDE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
536
+ const testCmd = new Command("test").description("\u6D4B\u8BD5\u7F51\u6613\u6613\u534F\u4F5C\u8FDE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
378
537
  const creds = resolveCredentials(options);
379
538
  const validation = validateCredentials(creds);
380
539
  if (!validation.valid) {
381
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
540
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
382
541
  process.exit(1);
383
542
  }
384
543
  const result = await userService.testConnection(
@@ -392,9 +551,9 @@ function createTestCommand() {
392
551
  host: creds.host,
393
552
  project: creds.project,
394
553
  data: result.data
395
- });
554
+ }, options.pretty);
396
555
  } else {
397
- outputError(result.message || result.msg || result.api_error_msg || "\u8FDE\u63A5\u5931\u8D25");
556
+ outputError(result.message || result.msg || result.api_error_msg || "\u8FDE\u63A5\u5931\u8D25", options.pretty);
398
557
  process.exit(1);
399
558
  }
400
559
  });
@@ -405,40 +564,40 @@ function createTestCommand() {
405
564
  import { Command as Command2 } from "commander";
406
565
  function createConfigCommand() {
407
566
  const configCmd = new Command2("config").description("\u914D\u7F6E\u7BA1\u7406");
408
- configCmd.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((key, value, options) => {
567
+ configCmd.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((key, value, options) => {
409
568
  setConfigValue(key, value, options.config);
410
569
  outputSuccess({
411
570
  message: `\u914D\u7F6E\u9879 ${key} \u5DF2\u8BBE\u7F6E`,
412
571
  key,
413
572
  value,
414
573
  configPath: getConfigPath(options.config)
415
- });
574
+ }, options.pretty);
416
575
  });
417
- configCmd.command("get <key>").description("\u83B7\u53D6\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((key, options) => {
576
+ configCmd.command("get <key>").description("\u83B7\u53D6\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((key, options) => {
418
577
  const value = getConfigValue(key, options.config);
419
578
  if (value !== void 0) {
420
- outputSuccess({ key, value });
579
+ outputSuccess({ key, value }, options.pretty);
421
580
  } else {
422
- outputError(`\u914D\u7F6E\u9879 ${key} \u4E0D\u5B58\u5728`);
581
+ outputError(`\u914D\u7F6E\u9879 ${key} \u4E0D\u5B58\u5728`, options.pretty);
423
582
  process.exit(1);
424
583
  }
425
584
  });
426
- configCmd.command("list").description("\u5217\u51FA\u6240\u6709\u914D\u7F6E").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((options) => {
585
+ configCmd.command("list").description("\u5217\u51FA\u6240\u6709\u914D\u7F6E").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((options) => {
427
586
  const config = readConfig(options.config);
428
587
  outputSuccess({
429
588
  configPath: getConfigPath(options.config),
430
589
  config
431
- });
590
+ }, options.pretty);
432
591
  });
433
592
  const profileCmd = new Command2("profile").description("Profile \u7BA1\u7406");
434
- profileCmd.command("add <name>").description("\u6DFB\u52A0 profile").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--token <token>", "API Token").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(
593
+ profileCmd.command("add <name>").description("\u6DFB\u52A0 profile").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--token <token>", "API Token").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(
435
594
  (name, options) => {
436
595
  const profile = {};
437
596
  if (options.host) profile.host = options.host;
438
597
  if (options.project) profile.project = options.project;
439
598
  if (options.token) profile.token = options.token;
440
599
  if (Object.keys(profile).length === 0) {
441
- outputError("\u8BF7\u81F3\u5C11\u6307\u5B9A\u4E00\u4E2A\u914D\u7F6E\u9879 (--host, --project, --token)");
600
+ outputError("\u8BF7\u81F3\u5C11\u6307\u5B9A\u4E00\u4E2A\u914D\u7F6E\u9879 (--host, --project, --token)", options.pretty);
442
601
  process.exit(1);
443
602
  }
444
603
  setProfile(name, profile, options.config);
@@ -446,19 +605,19 @@ function createConfigCommand() {
446
605
  message: `Profile ${name} \u5DF2\u6DFB\u52A0`,
447
606
  name,
448
607
  profile
449
- });
608
+ }, options.pretty);
450
609
  }
451
610
  );
452
- profileCmd.command("remove <name>").description("\u5220\u9664 profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((name, options) => {
611
+ profileCmd.command("remove <name>").description("\u5220\u9664 profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((name, options) => {
453
612
  const removed = removeProfile(name, options.config);
454
613
  if (removed) {
455
- outputSuccess({ message: `Profile ${name} \u5DF2\u5220\u9664`, name });
614
+ outputSuccess({ message: `Profile ${name} \u5DF2\u5220\u9664`, name }, options.pretty);
456
615
  } else {
457
- outputError(`Profile ${name} \u4E0D\u5B58\u5728`);
616
+ outputError(`Profile ${name} \u4E0D\u5B58\u5728`, options.pretty);
458
617
  process.exit(1);
459
618
  }
460
619
  });
461
- profileCmd.command("list").description("\u5217\u51FA\u6240\u6709 profiles").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((options) => {
620
+ profileCmd.command("list").description("\u5217\u51FA\u6240\u6709 profiles").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((options) => {
462
621
  const profiles = listProfiles(options.config);
463
622
  const profileDetails = {};
464
623
  for (const name of profiles) {
@@ -467,7 +626,7 @@ function createConfigCommand() {
467
626
  outputSuccess({
468
627
  profiles: profileDetails,
469
628
  count: profiles.length
470
- });
629
+ }, options.pretty);
471
630
  });
472
631
  configCmd.addCommand(profileCmd);
473
632
  return configCmd;
@@ -477,6 +636,9 @@ function createConfigCommand() {
477
636
  import { Command as Command3 } from "commander";
478
637
 
479
638
  // src/services/issue-service.ts
639
+ function sleep(ms) {
640
+ return new Promise((resolve) => setTimeout(resolve, ms));
641
+ }
480
642
  var IssueService = class {
481
643
  /**
482
644
  * 获取问题详情
@@ -560,9 +722,15 @@ var IssueService = class {
560
722
  return await apiClient.post("filter_query_v6", params);
561
723
  }
562
724
  /**
563
- * 递归获取问题及其子单
725
+ * 递归获取问题及其子单(包含完整详情)
564
726
  * @param depth 递归深度,默认 10
565
727
  * @param currentLevel 当前层级(内部使用)
728
+ *
729
+ * 实现逻辑:
730
+ * 1. 调用 getIssue(includeChildren=true) 获取当前问题详情
731
+ * 2. API 返回的 children 字段只包含简略信息(status 是字符串,缺少 assigned_to 等)
732
+ * 3. 从 children 中提取子单 ID,递归调用 getIssue 获取每个子单的完整详情
733
+ * 4. 用完整详情替换原始的简略 children
566
734
  */
567
735
  async getIssueWithChildren(token, host, project, issueId, depth = 10, currentLevel2 = 0) {
568
736
  logger_default.info("\u9012\u5F52\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5", { host, project, issueId, depth, currentLevel: currentLevel2 });
@@ -575,15 +743,16 @@ var IssueService = class {
575
743
  if (currentLevel2 >= depth) {
576
744
  return { success: true, data: issue };
577
745
  }
578
- const childrenResult = await this.getDirectChildren(token, host, project, issueId);
579
- if (!childrenResult.success || !childrenResult.data) {
746
+ const rawChildren = issue.children;
747
+ if (!rawChildren || rawChildren.length === 0) {
580
748
  return { success: true, data: issue };
581
749
  }
582
- const childrenIds = childrenResult.data;
583
- if (childrenIds.length === 0) {
750
+ const directChildrenIds = rawChildren.filter((child) => child.id).map((child) => child.id);
751
+ if (directChildrenIds.length === 0) {
584
752
  return { success: true, data: issue };
585
753
  }
586
- const childrenPromises = childrenIds.map(
754
+ logger_default.info("\u83B7\u53D6\u5B50\u5355\u5B8C\u6574\u8BE6\u60C5", { parentId: issueId, childCount: directChildrenIds.length, level: currentLevel2 });
755
+ const childrenPromises = directChildrenIds.map(
587
756
  (childId) => this.getIssueWithChildren(token, host, project, childId, depth, currentLevel2 + 1)
588
757
  );
589
758
  const childrenResults = await Promise.all(childrenPromises);
@@ -672,6 +841,285 @@ var IssueService = class {
672
841
  }
673
842
  return result;
674
843
  }
844
+ /**
845
+ * 批量获取多个问题详情
846
+ * @param issueIds 问题 ID 数组
847
+ * @param options 可选参数
848
+ * @returns 返回所有问题的详情数组(包含成功和失败的结果)
849
+ */
850
+ async getMultipleIssues(token, host, project, issueIds, options) {
851
+ const { includeChildren = false, includeRelations = false, depth = 0 } = options || {};
852
+ logger_default.info("\u6279\u91CF\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5", { host, project, count: issueIds.length, issueIds });
853
+ const results = await Promise.all(
854
+ issueIds.map(async (issueId) => {
855
+ try {
856
+ let result;
857
+ if (includeChildren && depth > 0) {
858
+ result = await this.getIssueWithChildren(token, host, project, issueId, depth);
859
+ } else {
860
+ result = await this.getIssue(token, host, project, issueId, includeChildren, includeRelations);
861
+ }
862
+ if (result.success && result.data) {
863
+ return { id: issueId, success: true, data: result.data };
864
+ } else {
865
+ return {
866
+ id: issueId,
867
+ success: false,
868
+ error: result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u5931\u8D25"
869
+ };
870
+ }
871
+ } catch (error) {
872
+ return {
873
+ id: issueId,
874
+ success: false,
875
+ error: error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"
876
+ };
877
+ }
878
+ })
879
+ );
880
+ const successCount = results.filter((r) => r.success).length;
881
+ const failCount = results.filter((r) => !r.success).length;
882
+ logger_default.info("\u6279\u91CF\u83B7\u53D6\u5B8C\u6210", { total: issueIds.length, success: successCount, fail: failCount });
883
+ return {
884
+ success: true,
885
+ data: results
886
+ };
887
+ }
888
+ /**
889
+ * 同步子单:从源父单复制子单到目标父单
890
+ * @param sourceParentId 源父单 ID
891
+ * @param targetParentId 目标父单 ID
892
+ * @param assignedToMail 新子单的指派人邮箱
893
+ * @param options 选项
894
+ */
895
+ async syncChildIssues(token, host, project, sourceParentId, targetParentId, assignedToMail, options) {
896
+ const { dryRun = false, depth = 10, skipExisting = true } = options || {};
897
+ logger_default.info("\u5F00\u59CB\u540C\u6B65\u5B50\u5355", {
898
+ sourceParentId,
899
+ targetParentId,
900
+ assignedToMail,
901
+ dryRun,
902
+ depth,
903
+ skipExisting
904
+ });
905
+ const result = {
906
+ totalCreated: 0,
907
+ totalSkipped: 0,
908
+ totalFailed: 0,
909
+ created: [],
910
+ skipped: [],
911
+ failed: []
912
+ };
913
+ try {
914
+ logger_default.info("\u6B63\u5728\u83B7\u53D6\u6E90\u7236\u5355\u7684\u5B50\u5355\u6811\u5F62\u7ED3\u6784...");
915
+ const sourceResult = await this.getIssueWithChildren(token, host, project, sourceParentId, depth);
916
+ if (!sourceResult.success || !sourceResult.data) {
917
+ return {
918
+ success: false,
919
+ message: `\u83B7\u53D6\u6E90\u7236\u5355 #${sourceParentId} \u5931\u8D25: ${sourceResult.message || "\u672A\u77E5\u9519\u8BEF"}`
920
+ };
921
+ }
922
+ const sourceIssue = sourceResult.data;
923
+ logger_default.info("\u6B63\u5728\u83B7\u53D6\u76EE\u6807\u7236\u5355\u4FE1\u606F...");
924
+ const targetResult = await this.getIssue(token, host, project, targetParentId, false, false);
925
+ if (!targetResult.success || !targetResult.data) {
926
+ return {
927
+ success: false,
928
+ message: `\u83B7\u53D6\u76EE\u6807\u7236\u5355 #${targetParentId} \u5931\u8D25: ${targetResult.message || "\u672A\u77E5\u9519\u8BEF"}`
929
+ };
930
+ }
931
+ const targetParentInfo = targetResult.data;
932
+ let existingTaskNames = /* @__PURE__ */ new Set();
933
+ if (skipExisting) {
934
+ logger_default.info("\u6B63\u5728\u83B7\u53D6\u76EE\u6807\u7236\u5355\u5DF2\u6709\u7684\u5B50\u5355...");
935
+ const existingResult = await this.getIssueWithChildren(token, host, project, targetParentId, depth);
936
+ if (existingResult.success && existingResult.data) {
937
+ existingTaskNames = this.collectAllTaskNames(existingResult.data);
938
+ logger_default.info(`\u76EE\u6807\u7236\u5355\u5DF2\u6709 ${existingTaskNames.size} \u4E2A\u5B50\u5355`);
939
+ }
940
+ }
941
+ logger_default.info("\u5F00\u59CB\u9012\u5F52\u540C\u6B65\u5B50\u5355...");
942
+ await this.syncChildrenRecursive(
943
+ token,
944
+ host,
945
+ project,
946
+ sourceIssue,
947
+ targetParentId,
948
+ targetParentInfo,
949
+ existingTaskNames,
950
+ assignedToMail,
951
+ dryRun,
952
+ result
953
+ );
954
+ logger_default.info("\u540C\u6B65\u5B8C\u6210", {
955
+ totalCreated: result.totalCreated,
956
+ totalSkipped: result.totalSkipped,
957
+ totalFailed: result.totalFailed
958
+ });
959
+ return { success: true, data: result };
960
+ } catch (error) {
961
+ logger_default.error("\u540C\u6B65\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF", error);
962
+ return {
963
+ success: false,
964
+ message: `\u540C\u6B65\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
965
+ };
966
+ }
967
+ }
968
+ /**
969
+ * 收集所有任务名称(递归)
970
+ */
971
+ collectAllTaskNames(issue) {
972
+ const names = /* @__PURE__ */ new Set();
973
+ if (issue.subject?.trim()) {
974
+ names.add(issue.subject.trim());
975
+ }
976
+ for (const child of issue.children || []) {
977
+ const childNames = this.collectAllTaskNames(child);
978
+ childNames.forEach((name) => names.add(name));
979
+ }
980
+ return names;
981
+ }
982
+ /**
983
+ * 递归同步子单
984
+ */
985
+ async syncChildrenRecursive(token, host, project, sourceNode, targetParentId, targetParentInfo, existingTaskNames, assignedToMail, dryRun, result) {
986
+ for (const child of sourceNode.children || []) {
987
+ const taskName = child.subject?.trim() || "\u672A\u547D\u540D\u4EFB\u52A1";
988
+ const sourceId = child.id;
989
+ if (existingTaskNames.has(taskName)) {
990
+ logger_default.info(`\u23ED\uFE0F \u8DF3\u8FC7\u5DF2\u5B58\u5728\u7684\u4EFB\u52A1: ${taskName}`);
991
+ result.totalSkipped++;
992
+ result.skipped.push({
993
+ sourceId,
994
+ subject: taskName,
995
+ reason: "\u76EE\u6807\u7236\u5355\u4E0B\u5DF2\u5B58\u5728\u540C\u540D\u4EFB\u52A1"
996
+ });
997
+ continue;
998
+ }
999
+ const isLeafNode = !child.children || child.children.length === 0;
1000
+ const createParams = {
1001
+ token,
1002
+ host,
1003
+ project,
1004
+ parent_issue_id: targetParentId,
1005
+ subject: taskName,
1006
+ // 继承自目标父单
1007
+ tracker: targetParentInfo.tracker?.name,
1008
+ status: "\u65B0\u5EFA",
1009
+ // 覆盖指派人为"我"
1010
+ assigned_to_mail: assignedToMail,
1011
+ // 只有叶子节点才设置工时
1012
+ estimated_hours: isLeafNode ? child.estimated_hours : void 0,
1013
+ // 保留原任务的优先级
1014
+ priority_id: child.priority?.id
1015
+ };
1016
+ const targetWithVersion = targetParentInfo;
1017
+ if (targetWithVersion.fixed_version?.name) {
1018
+ createParams.version = targetWithVersion.fixed_version.name;
1019
+ }
1020
+ const targetWithCustomFields = targetParentInfo;
1021
+ if (targetWithCustomFields.custom_fields && targetWithCustomFields.custom_fields.length > 0) {
1022
+ const customFieldMap = {};
1023
+ const followsMails = [];
1024
+ for (const field of targetWithCustomFields.custom_fields) {
1025
+ if (field.value !== null && field.value !== void 0 && field.value !== "") {
1026
+ if (field.identify === "IssuesQCFollow") {
1027
+ const followsValue = field.value;
1028
+ if (Array.isArray(followsValue)) {
1029
+ for (const item of followsValue) {
1030
+ if (item.user?.mail) {
1031
+ followsMails.push(item.user.mail);
1032
+ }
1033
+ }
1034
+ }
1035
+ } else {
1036
+ customFieldMap[field.id] = field.value;
1037
+ }
1038
+ }
1039
+ }
1040
+ if (Object.keys(customFieldMap).length > 0) {
1041
+ createParams.custom_field = JSON.stringify(customFieldMap);
1042
+ }
1043
+ if (followsMails.length > 0) {
1044
+ createParams.follows = followsMails;
1045
+ }
1046
+ }
1047
+ logger_default.info(`\u6B63\u5728\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`, { sourceId, targetParentId, isLeafNode });
1048
+ if (dryRun) {
1049
+ logger_default.info(`[\u6A21\u62DF] \u5C06\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`);
1050
+ result.totalCreated++;
1051
+ result.created.push({
1052
+ sourceId,
1053
+ newId: 0,
1054
+ // 模拟模式没有真实 ID
1055
+ subject: taskName,
1056
+ parentId: targetParentId
1057
+ });
1058
+ if (child.children && child.children.length > 0) {
1059
+ await this.syncChildrenRecursive(
1060
+ token,
1061
+ host,
1062
+ project,
1063
+ child,
1064
+ 0,
1065
+ // 模拟模式下没有真实的新 ID
1066
+ targetParentInfo,
1067
+ existingTaskNames,
1068
+ assignedToMail,
1069
+ dryRun,
1070
+ result
1071
+ );
1072
+ }
1073
+ } else {
1074
+ try {
1075
+ const createResult = await this.createIssue(createParams);
1076
+ if (createResult.success && createResult.data) {
1077
+ const newId = createResult.data.id;
1078
+ logger_default.info(`\u2705 \u6210\u529F\u521B\u5EFA\u5B50\u4EFB\u52A1: ID=${newId}, \u6807\u9898=${taskName}`);
1079
+ result.totalCreated++;
1080
+ result.created.push({
1081
+ sourceId,
1082
+ newId,
1083
+ subject: taskName,
1084
+ parentId: targetParentId
1085
+ });
1086
+ if (child.children && child.children.length > 0) {
1087
+ logger_default.info(`\u{1F4C1} \u53D1\u73B0\u5B50\u4EFB\u52A1 ${newId} \u6709 ${child.children.length} \u4E2A\u5B50\u4EFB\u52A1\uFF0C\u7EE7\u7EED\u9012\u5F52\u521B\u5EFA...`);
1088
+ await this.syncChildrenRecursive(
1089
+ token,
1090
+ host,
1091
+ project,
1092
+ child,
1093
+ newId,
1094
+ targetParentInfo,
1095
+ existingTaskNames,
1096
+ assignedToMail,
1097
+ dryRun,
1098
+ result
1099
+ );
1100
+ }
1101
+ } else {
1102
+ logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u5931\u8D25: ${taskName}`, createResult.message || createResult.api_error_msg);
1103
+ result.totalFailed++;
1104
+ result.failed.push({
1105
+ sourceId,
1106
+ subject: taskName,
1107
+ error: createResult.message || createResult.api_error_msg || "\u521B\u5EFA\u5931\u8D25"
1108
+ });
1109
+ }
1110
+ } catch (error) {
1111
+ logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u65F6\u53D1\u751F\u9519\u8BEF: ${taskName}`, error);
1112
+ result.totalFailed++;
1113
+ result.failed.push({
1114
+ sourceId,
1115
+ subject: taskName,
1116
+ error: error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"
1117
+ });
1118
+ }
1119
+ await sleep(500);
1120
+ }
1121
+ }
1122
+ }
675
1123
  };
676
1124
  var issueService = new IssueService();
677
1125
 
@@ -703,13 +1151,13 @@ function parsePmLink(url) {
703
1151
  // src/commands/issue/index.ts
704
1152
  function createIssueCommand() {
705
1153
  const issueCmd = new Command3("issue").description("\u95EE\u9898\u7BA1\u7406");
706
- issueCmd.command("get [id]").description("\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5\uFF08\u9ED8\u8BA4\u9012\u5F52\u83B7\u53D6\u5B50\u5355\uFF0C\u8F93\u51FA\u5230\u6587\u4EF6\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--depth <depth>", "\u9012\u5F52\u83B7\u53D6\u5B50\u5355\u7684\u6DF1\u5EA6\uFF080 \u8868\u793A\u4E0D\u83B7\u53D6\u5B50\u5355\uFF09", "10").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 /tmp\uFF09").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--include-relations", "\u5305\u542B\u5173\u8054\u95EE\u9898").action(async (id, options) => {
1154
+ issueCmd.command("get [id]").description("\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5\uFF08\u9ED8\u8BA4\u9012\u5F52\u83B7\u53D6\u5B50\u5355\uFF0C\u8F93\u51FA\u5230\u6587\u4EF6\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--depth <depth>", "\u9012\u5F52\u83B7\u53D6\u5B50\u5355\u7684\u6DF1\u5EA6\uFF080 \u8868\u793A\u4E0D\u83B7\u53D6\u5B50\u5355\uFF09", "10").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 /tmp\uFF09").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--include-relations", "\u5305\u542B\u5173\u8054\u95EE\u9898").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
707
1155
  let issueId;
708
1156
  let host;
709
1157
  if (options.url) {
710
1158
  const linkInfo = parsePmLink(options.url);
711
1159
  if (!linkInfo) {
712
- outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F");
1160
+ outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
713
1161
  process.exit(1);
714
1162
  }
715
1163
  issueId = parseInt(linkInfo.issueId, 10);
@@ -718,11 +1166,11 @@ function createIssueCommand() {
718
1166
  const cleanId = id.replace(/^#/, "");
719
1167
  issueId = parseInt(cleanId, 10);
720
1168
  if (isNaN(issueId)) {
721
- outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
1169
+ outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
722
1170
  process.exit(1);
723
1171
  }
724
1172
  } else {
725
- outputError("\u8BF7\u63D0\u4F9B\u95EE\u9898 ID \u6216 --url \u53C2\u6570");
1173
+ outputError("\u8BF7\u63D0\u4F9B\u95EE\u9898 ID \u6216 --url \u53C2\u6570", options.pretty);
726
1174
  process.exit(1);
727
1175
  }
728
1176
  const creds = resolveCredentials({
@@ -731,7 +1179,7 @@ function createIssueCommand() {
731
1179
  });
732
1180
  const validation = validateCredentials(creds, ["token", "host"]);
733
1181
  if (!validation.valid) {
734
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1182
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
735
1183
  process.exit(1);
736
1184
  }
737
1185
  const depth = parseInt(options.depth, 10);
@@ -748,10 +1196,11 @@ function createIssueCommand() {
748
1196
  stdout: options.stdout,
749
1197
  output: options.output,
750
1198
  command: "issue-get",
751
- identifier: issueId.toString()
1199
+ identifier: issueId.toString(),
1200
+ pretty: options.pretty
752
1201
  });
753
1202
  } else {
754
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25");
1203
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25", options.pretty);
755
1204
  process.exit(1);
756
1205
  }
757
1206
  } else {
@@ -768,19 +1217,20 @@ function createIssueCommand() {
768
1217
  stdout: options.stdout,
769
1218
  output: options.output,
770
1219
  command: "issue-get",
771
- identifier: issueId.toString()
1220
+ identifier: issueId.toString(),
1221
+ pretty: options.pretty
772
1222
  });
773
1223
  } else {
774
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25");
1224
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25", options.pretty);
775
1225
  process.exit(1);
776
1226
  }
777
1227
  }
778
1228
  });
779
- issueCmd.command("create").description("\u521B\u5EFA\u95EE\u9898").requiredOption("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--tracker-id <id>", "\u8DDF\u8E2A\u5668 ID").option("--priority-id <id>", "\u4F18\u5148\u7EA7 ID").option("--assigned-to-id <id>", "\u6307\u6D3E\u4EBA ID").option("--parent-id <id>", "\u7236\u95EE\u9898 ID").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
1229
+ issueCmd.command("create").description("\u521B\u5EFA\u95EE\u9898").requiredOption("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--tracker <tracker>", "\u8DDF\u8E2A\u5668\u540D\u79F0").option("--tracker-id <id>", "\u8DDF\u8E2A\u5668 ID").option("--priority-id <id>", "\u4F18\u5148\u7EA7 ID").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1").option("--assigned-to-id <id>", "\u6307\u6D3E\u4EBA ID").option("--parent-id <id>", "\u7236\u95EE\u9898 ID\uFF08\u5B50\u5355\u4F1A\u7EE7\u627F\u7236\u5355\u7684 tracker\u3001version\u3001assigned_to \u7B49\u4FE1\u606F\uFF09").option("--version <version>", "\u76EE\u6807\u7248\u672C\u540D\u79F0").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
780
1230
  const creds = resolveCredentials(options);
781
1231
  const validation = validateCredentials(creds);
782
1232
  if (!validation.valid) {
783
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1233
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
784
1234
  process.exit(1);
785
1235
  }
786
1236
  const params = {
@@ -789,29 +1239,89 @@ function createIssueCommand() {
789
1239
  project: creds.project,
790
1240
  subject: options.subject
791
1241
  };
1242
+ if (options.parentId) {
1243
+ const parentId = parseInt(options.parentId, 10);
1244
+ const parentResult = await issueService.getIssue(
1245
+ creds.token,
1246
+ creds.host,
1247
+ creds.project || "",
1248
+ parentId,
1249
+ false,
1250
+ false
1251
+ );
1252
+ if (parentResult.success && parentResult.data) {
1253
+ const parent = parentResult.data;
1254
+ if (!options.tracker && !options.trackerId && parent.tracker?.name) {
1255
+ params.tracker = parent.tracker.name;
1256
+ }
1257
+ if (!options.assignedToMail && !options.assignedToId && parent.assigned_to?.mail) {
1258
+ params.assigned_to_mail = parent.assigned_to.mail;
1259
+ }
1260
+ if (!options.version && parent.fixed_version?.name) {
1261
+ params.version = parent.fixed_version.name;
1262
+ }
1263
+ if (!options.priorityId && parent.priority?.id) {
1264
+ params.priority_id = parent.priority.id;
1265
+ }
1266
+ params.status = "\u65B0\u5EFA";
1267
+ if (parent.custom_fields && parent.custom_fields.length > 0) {
1268
+ const customFieldMap = {};
1269
+ const followsMails = [];
1270
+ for (const field of parent.custom_fields) {
1271
+ if (field.value !== null && field.value !== void 0 && field.value !== "") {
1272
+ const fieldWithIdentify = field;
1273
+ if (fieldWithIdentify.identify === "IssuesQCFollow") {
1274
+ const followsValue = field.value;
1275
+ if (Array.isArray(followsValue)) {
1276
+ for (const item of followsValue) {
1277
+ if (item.user?.mail) {
1278
+ followsMails.push(item.user.mail);
1279
+ }
1280
+ }
1281
+ }
1282
+ } else {
1283
+ customFieldMap[field.id] = field.value;
1284
+ }
1285
+ }
1286
+ }
1287
+ if (Object.keys(customFieldMap).length > 0) {
1288
+ params.custom_field = JSON.stringify(customFieldMap);
1289
+ }
1290
+ if (followsMails.length > 0) {
1291
+ params.follows = followsMails;
1292
+ }
1293
+ }
1294
+ } else {
1295
+ outputError(`\u65E0\u6CD5\u83B7\u53D6\u7236\u5355 #${parentId} \u7684\u4FE1\u606F: ${parentResult.message || "\u672A\u77E5\u9519\u8BEF"}`, options.pretty);
1296
+ process.exit(1);
1297
+ }
1298
+ params.parent_issue_id = parentId;
1299
+ }
792
1300
  if (options.description) params.description = options.description;
1301
+ if (options.tracker) params.tracker = options.tracker;
793
1302
  if (options.trackerId) params.tracker_id = parseInt(options.trackerId, 10);
794
1303
  if (options.priorityId) params.priority_id = parseInt(options.priorityId, 10);
1304
+ if (options.assignedToMail) params.assigned_to_mail = options.assignedToMail;
795
1305
  if (options.assignedToId) params.assigned_to_id = parseInt(options.assignedToId, 10);
796
- if (options.parentId) params.parent_issue_id = parseInt(options.parentId, 10);
1306
+ if (options.version) params.version = options.version;
797
1307
  if (options.startDate) params.start_date = options.startDate;
798
1308
  if (options.dueDate) params.due_date = options.dueDate;
799
1309
  if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours);
800
1310
  const result = await issueService.createIssue(params);
801
1311
  if (result.success && result.data) {
802
- outputSuccess(result.data);
1312
+ outputSuccess(result.data, options.pretty);
803
1313
  } else {
804
- outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u95EE\u9898\u5931\u8D25");
1314
+ outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u95EE\u9898\u5931\u8D25", options.pretty);
805
1315
  process.exit(1);
806
1316
  }
807
1317
  });
808
- issueCmd.command("update <id>").description("\u66F4\u65B0\u95EE\u9898").option("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--status <status>", "\u72B6\u6001\u540D\u79F0 (\u5982: \u65B0\u5EFA\u3001\u5F00\u53D1\u4E2D\u3001\u5DF2\u89E3\u51B3)").option("--tracker <tracker>", "\u8DDF\u8E2A\u6807\u7B7E\u540D\u79F0 (\u5982: BUG\u3001\u4EFB\u52A1)").option("--version <version>", "\u76EE\u6807\u7248\u672C\u540D\u79F0").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1").option("--notes <notes>", "\u66F4\u65B0\u8BF4\u660E/\u5907\u6CE8").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--follows <email>", "\u8DDF\u8FDBQA\u90AE\u7BB1").option("--url <url>", "PM \u94FE\u63A5 (\u81EA\u52A8\u89E3\u6790 host \u548C issue_id)").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
1318
+ issueCmd.command("update <id>").description("\u66F4\u65B0\u95EE\u9898").option("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--status <status>", "\u72B6\u6001\u540D\u79F0 (\u5982: \u65B0\u5EFA\u3001\u5F00\u53D1\u4E2D\u3001\u5DF2\u89E3\u51B3)").option("--tracker <tracker>", "\u8DDF\u8E2A\u6807\u7B7E\u540D\u79F0 (\u5982: BUG\u3001\u4EFB\u52A1)").option("--version <version>", "\u76EE\u6807\u7248\u672C\u540D\u79F0").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1").option("--notes <notes>", "\u66F4\u65B0\u8BF4\u660E/\u5907\u6CE8").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--follows <email>", "\u8DDF\u8FDBQA\u90AE\u7BB1").option("--custom-field <json>", `\u81EA\u5B9A\u4E49\u5B57\u6BB5 (JSON\u683C\u5F0F\uFF0C\u5982: '{"123":"value"}'\uFF0C\u652F\u6301\u4F20\u5165\u5B57\u6BB5\u540D\u6216\u5B57\u6BB5ID)`).option("--url <url>", "PM \u94FE\u63A5 (\u81EA\u52A8\u89E3\u6790 host \u548C issue_id)").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
809
1319
  let issueId;
810
1320
  let urlHost;
811
1321
  if (options.url) {
812
1322
  const linkInfo = parsePmLink(options.url);
813
1323
  if (!linkInfo) {
814
- outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F");
1324
+ outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
815
1325
  process.exit(1);
816
1326
  }
817
1327
  issueId = parseInt(linkInfo.issueId, 10);
@@ -819,7 +1329,7 @@ function createIssueCommand() {
819
1329
  } else {
820
1330
  issueId = parseInt(id.replace(/^#/, ""), 10);
821
1331
  if (isNaN(issueId)) {
822
- outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
1332
+ outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
823
1333
  process.exit(1);
824
1334
  }
825
1335
  }
@@ -829,7 +1339,7 @@ function createIssueCommand() {
829
1339
  });
830
1340
  const validation = validateCredentials(creds);
831
1341
  if (!validation.valid) {
832
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1342
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
833
1343
  process.exit(1);
834
1344
  }
835
1345
  const params = {
@@ -849,19 +1359,66 @@ function createIssueCommand() {
849
1359
  if (options.dueDate) params.due_date = options.dueDate;
850
1360
  if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours);
851
1361
  if (options.follows) params.follows = options.follows;
1362
+ if (options.customField) {
1363
+ try {
1364
+ const customFieldData = JSON.parse(options.customField);
1365
+ const hasNonNumericKey = Object.keys(customFieldData).some((key) => isNaN(Number(key)));
1366
+ if (hasNonNumericKey) {
1367
+ const fieldOptionsResult = await issueService.getIssueFieldOptions(
1368
+ creds.token,
1369
+ creds.host,
1370
+ creds.project
1371
+ );
1372
+ if (fieldOptionsResult.success && fieldOptionsResult.data) {
1373
+ const fieldOptions = fieldOptionsResult.data;
1374
+ if (fieldOptions.data?.custom_fields) {
1375
+ const fieldMap = /* @__PURE__ */ new Map();
1376
+ for (const field of fieldOptions.data.custom_fields) {
1377
+ fieldMap.set(field.name, field.id);
1378
+ }
1379
+ const convertedData = {};
1380
+ for (const [key, value] of Object.entries(customFieldData)) {
1381
+ const fieldId = isNaN(Number(key)) ? fieldMap.get(key) : Number(key);
1382
+ if (fieldId !== void 0) {
1383
+ convertedData[fieldId] = value;
1384
+ } else {
1385
+ outputError(`\u672A\u627E\u5230\u81EA\u5B9A\u4E49\u5B57\u6BB5: ${key}`, options.pretty);
1386
+ process.exit(1);
1387
+ }
1388
+ }
1389
+ params.custom_field = JSON.stringify(convertedData);
1390
+ } else {
1391
+ outputError("\u65E0\u6CD5\u83B7\u53D6\u81EA\u5B9A\u4E49\u5B57\u6BB5\u5217\u8868", options.pretty);
1392
+ process.exit(1);
1393
+ }
1394
+ } else {
1395
+ outputError("\u83B7\u53D6\u81EA\u5B9A\u4E49\u5B57\u6BB5\u5217\u8868\u5931\u8D25", options.pretty);
1396
+ process.exit(1);
1397
+ }
1398
+ } else {
1399
+ params.custom_field = JSON.stringify(customFieldData);
1400
+ }
1401
+ } catch (error) {
1402
+ outputError(
1403
+ `\u81EA\u5B9A\u4E49\u5B57\u6BB5 JSON \u683C\u5F0F\u9519\u8BEF: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
1404
+ options.pretty
1405
+ );
1406
+ process.exit(1);
1407
+ }
1408
+ }
852
1409
  const result = await issueService.updateIssue(params);
853
1410
  if (result.success && result.data) {
854
- outputSuccess(result.data);
1411
+ outputSuccess(result.data, options.pretty);
855
1412
  } else {
856
- outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u95EE\u9898\u5931\u8D25");
1413
+ outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u95EE\u9898\u5931\u8D25", options.pretty);
857
1414
  process.exit(1);
858
1415
  }
859
1416
  });
860
- issueCmd.command("query").description("\u81EA\u5B9A\u4E49\u67E5\u8BE2").requiredOption("--query-id <id>", "\u67E5\u8BE2 ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
1417
+ issueCmd.command("query").description("\u81EA\u5B9A\u4E49\u67E5\u8BE2").requiredOption("--query-id <id>", "\u67E5\u8BE2 ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
861
1418
  const creds = resolveCredentials(options);
862
1419
  const validation = validateCredentials(creds);
863
1420
  if (!validation.valid) {
864
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1421
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
865
1422
  process.exit(1);
866
1423
  }
867
1424
  const queryId = parseInt(options.queryId, 10);
@@ -876,17 +1433,17 @@ function createIssueCommand() {
876
1433
  offset
877
1434
  );
878
1435
  if (result.success && result.data) {
879
- outputSuccess(result.data);
1436
+ outputSuccess(result.data, options.pretty);
880
1437
  } else {
881
- outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25");
1438
+ outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25", options.pretty);
882
1439
  process.exit(1);
883
1440
  }
884
1441
  });
885
- issueCmd.command("filter").description("V6 \u8FC7\u6EE4\u5668\u67E5\u8BE2").option("--mode <mode>", "\u67E5\u8BE2\u6A21\u5F0F (normal/simple/advanced)", "normal").option("--status <status>", "\u72B6\u6001\u8FC7\u6EE4").option("--tracker <tracker>", "\u8DDF\u8E2A\u5668\u8FC7\u6EE4").option("--assigned-to <user>", "\u6307\u6D3E\u4EBA\u8FC7\u6EE4").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
1442
+ issueCmd.command("filter").description("V6 \u8FC7\u6EE4\u5668\u67E5\u8BE2").option("--mode <mode>", "\u67E5\u8BE2\u6A21\u5F0F (normal/simple/advanced)", "normal").option("--status <status>", "\u72B6\u6001\u8FC7\u6EE4").option("--tracker <tracker>", "\u8DDF\u8E2A\u5668\u8FC7\u6EE4").option("--assigned-to <user>", "\u6307\u6D3E\u4EBA\u8FC7\u6EE4").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
886
1443
  const creds = resolveCredentials(options);
887
1444
  const validation = validateCredentials(creds);
888
1445
  if (!validation.valid) {
889
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1446
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
890
1447
  process.exit(1);
891
1448
  }
892
1449
  const filterParams = {};
@@ -903,17 +1460,17 @@ function createIssueCommand() {
903
1460
  filterParams
904
1461
  );
905
1462
  if (result.success && result.data) {
906
- outputSuccess(result.data);
1463
+ outputSuccess(result.data, options.pretty);
907
1464
  } else {
908
- outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25");
1465
+ outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25", options.pretty);
909
1466
  process.exit(1);
910
1467
  }
911
1468
  });
912
- issueCmd.command("field-options").description("\u83B7\u53D6\u95EE\u9898\u5B57\u6BB5\u9009\u9879").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
1469
+ issueCmd.command("field-options").description("\u83B7\u53D6\u95EE\u9898\u5B57\u6BB5\u9009\u9879").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
913
1470
  const creds = resolveCredentials(options);
914
1471
  const validation = validateCredentials(creds);
915
1472
  if (!validation.valid) {
916
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1473
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
917
1474
  process.exit(1);
918
1475
  }
919
1476
  const result = await issueService.getIssueFieldOptions(
@@ -922,19 +1479,19 @@ function createIssueCommand() {
922
1479
  creds.project
923
1480
  );
924
1481
  if (result.success) {
925
- outputSuccess(result.data);
1482
+ outputSuccess(result.data, options.pretty);
926
1483
  } else {
927
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u5B57\u6BB5\u9009\u9879\u5931\u8D25");
1484
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u5B57\u6BB5\u9009\u9879\u5931\u8D25", options.pretty);
928
1485
  process.exit(1);
929
1486
  }
930
1487
  });
931
- issueCmd.command("children <id>").description("\u67E5\u8BE2\u5B50\u4EFB\u52A1\uFF08\u652F\u6301\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--assigned-to <name>", "\u8D1F\u8D23\u4EBA\u59D3\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF09").option("--assigned-to-id <id>", "\u8D1F\u8D23\u4EBA ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236", "100").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
1488
+ issueCmd.command("children <id>").description("\u67E5\u8BE2\u5B50\u4EFB\u52A1\uFF08\u652F\u6301\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--assigned-to <name>", "\u8D1F\u8D23\u4EBA\u59D3\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF09").option("--assigned-to-id <id>", "\u8D1F\u8D23\u4EBA ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236", "100").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
932
1489
  let issueId;
933
1490
  let host;
934
1491
  if (options.url) {
935
1492
  const linkInfo = parsePmLink(options.url);
936
1493
  if (!linkInfo) {
937
- outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F");
1494
+ outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
938
1495
  process.exit(1);
939
1496
  }
940
1497
  issueId = parseInt(linkInfo.issueId, 10);
@@ -943,7 +1500,7 @@ function createIssueCommand() {
943
1500
  const cleanId = id.replace(/^#/, "");
944
1501
  issueId = parseInt(cleanId, 10);
945
1502
  if (isNaN(issueId)) {
946
- outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
1503
+ outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
947
1504
  process.exit(1);
948
1505
  }
949
1506
  }
@@ -953,7 +1510,7 @@ function createIssueCommand() {
953
1510
  });
954
1511
  const validation = validateCredentials(creds);
955
1512
  if (!validation.valid) {
956
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1513
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
957
1514
  process.exit(1);
958
1515
  }
959
1516
  let assignedToId;
@@ -972,11 +1529,11 @@ function createIssueCommand() {
972
1529
  if (matchedUser) {
973
1530
  assignedToId = matchedUser.id;
974
1531
  } else {
975
- outputError(`\u672A\u627E\u5230\u5339\u914D\u7684\u7528\u6237: ${options.assignedTo}`);
1532
+ outputError(`\u672A\u627E\u5230\u5339\u914D\u7684\u7528\u6237: ${options.assignedTo}`, options.pretty);
976
1533
  process.exit(1);
977
1534
  }
978
1535
  } else {
979
- outputError("\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25");
1536
+ outputError("\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25", options.pretty);
980
1537
  process.exit(1);
981
1538
  }
982
1539
  } else if (options.assignedToId) {
@@ -997,12 +1554,174 @@ function createIssueCommand() {
997
1554
  outputSuccess({
998
1555
  total: data.data.list.length,
999
1556
  issues: data.data.list
1000
- });
1557
+ }, options.pretty);
1001
1558
  } else {
1002
- outputSuccess(result.data);
1559
+ outputSuccess(result.data, options.pretty);
1560
+ }
1561
+ } else {
1562
+ outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5B50\u4EFB\u52A1\u5931\u8D25", options.pretty);
1563
+ process.exit(1);
1564
+ }
1565
+ });
1566
+ issueCmd.command("mget [ids...]").description("\u6279\u91CF\u83B7\u53D6\u591A\u4E2A\u95EE\u9898\u8BE6\u60C5").option("--ids <ids>", "\u9017\u53F7\u5206\u9694\u7684\u95EE\u9898 ID \u5217\u8868").option("--depth <depth>", "\u9012\u5F52\u83B7\u53D6\u5B50\u5355\u7684\u6DF1\u5EA6\uFF080 \u8868\u793A\u4E0D\u83B7\u53D6\u5B50\u5355\uFF09", "0").option("--include-relations", "\u5305\u542B\u5173\u8054\u95EE\u9898").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 /tmp\uFF09").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (ids, options) => {
1567
+ let issueIds = [];
1568
+ if (ids && ids.length > 0) {
1569
+ const parsedIds = ids.map((id) => parseInt(id.replace(/^#/, ""), 10)).filter((id) => !isNaN(id));
1570
+ issueIds.push(...parsedIds);
1571
+ }
1572
+ if (options.ids) {
1573
+ const idsFromOption = options.ids.split(",").map((id) => parseInt(id.trim().replace(/^#/, ""), 10)).filter((id) => !isNaN(id));
1574
+ issueIds.push(...idsFromOption);
1575
+ }
1576
+ issueIds = [...new Set(issueIds)];
1577
+ if (issueIds.length === 0) {
1578
+ outputError("\u8BF7\u63D0\u4F9B\u81F3\u5C11\u4E00\u4E2A\u95EE\u9898 ID", options.pretty);
1579
+ process.exit(1);
1580
+ }
1581
+ const creds = resolveCredentials(options);
1582
+ const validation = validateCredentials(creds, ["token", "host"]);
1583
+ if (!validation.valid) {
1584
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1585
+ process.exit(1);
1586
+ }
1587
+ const depth = parseInt(options.depth, 10);
1588
+ const includeChildren = depth > 0;
1589
+ const includeRelations = options.includeRelations;
1590
+ const result = await issueService.getMultipleIssues(
1591
+ creds.token,
1592
+ creds.host,
1593
+ creds.project || "",
1594
+ issueIds,
1595
+ { includeChildren, includeRelations, depth }
1596
+ );
1597
+ if (result.success && result.data) {
1598
+ const successItems = result.data.filter((item) => item.success);
1599
+ const failedItems = result.data.filter((item) => !item.success);
1600
+ const output = {
1601
+ success: true,
1602
+ summary: {
1603
+ total: issueIds.length,
1604
+ success: successItems.length,
1605
+ failed: failedItems.length
1606
+ },
1607
+ issues: successItems.map((item) => item.data),
1608
+ errors: failedItems.length > 0 ? failedItems.map((item) => ({ id: item.id, error: item.error })) : void 0
1609
+ };
1610
+ smartOutput(output, {
1611
+ stdout: options.stdout,
1612
+ output: options.output,
1613
+ command: "issue-mget",
1614
+ identifier: issueIds.join("_"),
1615
+ pretty: options.pretty
1616
+ });
1617
+ } else {
1618
+ outputError(result.message || result.msg || result.api_error_msg || "\u6279\u91CF\u83B7\u53D6\u95EE\u9898\u5931\u8D25", options.pretty);
1619
+ process.exit(1);
1620
+ }
1621
+ });
1622
+ issueCmd.command("sync").description("\u540C\u6B65\u5B50\u5355\uFF1A\u4ECE\u6E90\u7236\u5355\u590D\u5236\u5B50\u5355\u5230\u76EE\u6807\u7236\u5355").option("--from <id>", "\u6E90\u7236\u5355 ID").option("--to <id>", "\u76EE\u6807\u7236\u5355 ID").option("--from-url <url>", "\u6E90\u7236\u5355 PM \u94FE\u63A5").option("--to-url <url>", "\u76EE\u6807\u7236\u5355 PM \u94FE\u63A5").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u7684 userMail\uFF09").option("--depth <depth>", "\u9012\u5F52\u6DF1\u5EA6", "10").option("--dry-run", "\u6A21\u62DF\u8FD0\u884C\uFF0C\u4E0D\u5B9E\u9645\u521B\u5EFA").option("--no-skip-existing", "\u4E0D\u8DF3\u8FC7\u540C\u540D\u4EFB\u52A1").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1623
+ let sourceId;
1624
+ let targetId;
1625
+ let sourceHost;
1626
+ let targetHost;
1627
+ if (options.fromUrl) {
1628
+ const linkInfo = parsePmLink(options.fromUrl);
1629
+ if (!linkInfo) {
1630
+ outputError("\u65E0\u6548\u7684\u6E90\u7236\u5355 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
1631
+ process.exit(1);
1632
+ }
1633
+ sourceId = parseInt(linkInfo.issueId, 10);
1634
+ sourceHost = linkInfo.host;
1635
+ } else if (options.from) {
1636
+ const cleanId = options.from.replace(/^#/, "");
1637
+ sourceId = parseInt(cleanId, 10);
1638
+ if (isNaN(sourceId)) {
1639
+ outputError("\u65E0\u6548\u7684\u6E90\u7236\u5355 ID", options.pretty);
1640
+ process.exit(1);
1641
+ }
1642
+ }
1643
+ if (options.toUrl) {
1644
+ const linkInfo = parsePmLink(options.toUrl);
1645
+ if (!linkInfo) {
1646
+ outputError("\u65E0\u6548\u7684\u76EE\u6807\u7236\u5355 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
1647
+ process.exit(1);
1648
+ }
1649
+ targetId = parseInt(linkInfo.issueId, 10);
1650
+ targetHost = linkInfo.host;
1651
+ } else if (options.to) {
1652
+ const cleanId = options.to.replace(/^#/, "");
1653
+ targetId = parseInt(cleanId, 10);
1654
+ if (isNaN(targetId)) {
1655
+ outputError("\u65E0\u6548\u7684\u76EE\u6807\u7236\u5355 ID", options.pretty);
1656
+ process.exit(1);
1657
+ }
1658
+ }
1659
+ if (!sourceId) {
1660
+ outputError("\u8BF7\u63D0\u4F9B\u6E90\u7236\u5355 ID\uFF08--from\uFF09\u6216 PM \u94FE\u63A5\uFF08--from-url\uFF09", options.pretty);
1661
+ process.exit(1);
1662
+ }
1663
+ if (!targetId) {
1664
+ outputError("\u8BF7\u63D0\u4F9B\u76EE\u6807\u7236\u5355 ID\uFF08--to\uFF09\u6216 PM \u94FE\u63A5\uFF08--to-url\uFF09", options.pretty);
1665
+ process.exit(1);
1666
+ }
1667
+ const creds = resolveCredentials({
1668
+ ...options,
1669
+ host: sourceHost || targetHost || options.host
1670
+ });
1671
+ const validation = validateCredentials(creds, ["token", "host"]);
1672
+ if (!validation.valid) {
1673
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1674
+ process.exit(1);
1675
+ }
1676
+ let assignedToMail = options.assignedToMail;
1677
+ if (!assignedToMail) {
1678
+ const config = readConfig(options.config);
1679
+ assignedToMail = config.default?.userMail;
1680
+ if (options.profile && config.profiles[options.profile]?.userMail) {
1681
+ assignedToMail = config.profiles[options.profile].userMail;
1003
1682
  }
1683
+ }
1684
+ if (!assignedToMail) {
1685
+ outputError("\u8BF7\u63D0\u4F9B\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08--assigned-to-mail\uFF09\u6216\u914D\u7F6E userMail\uFF08pm-cli config set user-mail xxx\uFF09", options.pretty);
1686
+ process.exit(1);
1687
+ }
1688
+ const depth = parseInt(options.depth, 10);
1689
+ const dryRun = options.dryRun || false;
1690
+ const skipExisting = options.skipExisting !== false;
1691
+ const result = await issueService.syncChildIssues(
1692
+ creds.token,
1693
+ creds.host,
1694
+ creds.project || "",
1695
+ sourceId,
1696
+ targetId,
1697
+ assignedToMail,
1698
+ { dryRun, depth, skipExisting }
1699
+ );
1700
+ if (result.success && result.data) {
1701
+ const output = {
1702
+ success: true,
1703
+ dryRun,
1704
+ sourceParentId: sourceId,
1705
+ targetParentId: targetId,
1706
+ assignedToMail,
1707
+ summary: {
1708
+ totalCreated: result.data.totalCreated,
1709
+ totalSkipped: result.data.totalSkipped,
1710
+ totalFailed: result.data.totalFailed
1711
+ },
1712
+ created: result.data.created,
1713
+ skipped: result.data.skipped.length > 0 ? result.data.skipped : void 0,
1714
+ failed: result.data.failed.length > 0 ? result.data.failed : void 0
1715
+ };
1716
+ smartOutput(output, {
1717
+ stdout: options.stdout,
1718
+ output: options.output,
1719
+ command: "issue-sync",
1720
+ identifier: `${sourceId}_to_${targetId}`,
1721
+ pretty: options.pretty
1722
+ });
1004
1723
  } else {
1005
- outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5B50\u4EFB\u52A1\u5931\u8D25");
1724
+ outputError(result.message || result.msg || result.api_error_msg || "\u540C\u6B65\u5B50\u5355\u5931\u8D25", options.pretty);
1006
1725
  process.exit(1);
1007
1726
  }
1008
1727
  });
@@ -1036,18 +1755,22 @@ var TimeEntryService = class {
1036
1755
  return await apiClient.get("query_time_entries", requestParams);
1037
1756
  }
1038
1757
  /**
1039
- * 获取工时条目选项
1758
+ * 获取工时条目选项(活动类型列表等)
1759
+ * 使用 time_entry GET API
1040
1760
  */
1041
- async getTimeEntryOptions(token, host, project) {
1042
- logger_default.info("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879", { host, project });
1043
- return await apiClient.post("time_entry_options", { token, host, project });
1761
+ async getTimeEntryOptions(token, host, project, issueId) {
1762
+ logger_default.info("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879", { host, project, issueId });
1763
+ const params = { token, host, project };
1764
+ if (issueId) params.issue_id = issueId;
1765
+ return await apiClient.get("time_entry", params);
1044
1766
  }
1045
1767
  /**
1046
1768
  * 创建工时条目
1769
+ * 使用 time_entry API(非 save_time_entry)
1047
1770
  */
1048
1771
  async createTimeEntry(params) {
1049
1772
  logger_default.info("\u521B\u5EFA\u5DE5\u65F6\u6761\u76EE", { params });
1050
- return await apiClient.post("save_time_entry", params);
1773
+ return await apiClient.post("time_entry", params);
1051
1774
  }
1052
1775
  /**
1053
1776
  * 更新工时条目
@@ -1059,13 +1782,13 @@ var TimeEntryService = class {
1059
1782
  /**
1060
1783
  * 删除工时条目
1061
1784
  */
1062
- async deleteTimeEntry(token, host, project, timeEntryId) {
1063
- logger_default.info("\u5220\u9664\u5DE5\u65F6\u6761\u76EE", { host, project, timeEntryId });
1785
+ async deleteTimeEntry(token, host, timeEntryId) {
1786
+ logger_default.info("\u5220\u9664\u5DE5\u65F6\u6761\u76EE", { host, timeEntryId });
1064
1787
  return await apiClient.get("delete_time_entry", {
1065
1788
  token,
1066
1789
  host,
1067
- project,
1068
- time_entry_id: timeEntryId
1790
+ id: timeEntryId
1791
+ // 使用 id 参数,非 time_entry_id
1069
1792
  });
1070
1793
  }
1071
1794
  };
@@ -1104,19 +1827,19 @@ function getWeekDateRange(week) {
1104
1827
  }
1105
1828
  function createTimeCommand() {
1106
1829
  const timeCmd = new Command4("time").description("\u5DE5\u65F6\u7BA1\u7406");
1107
- timeCmd.command("list").description("\u67E5\u8BE2\u5DE5\u65F6\u6761\u76EE").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)").option("--user-id <id>", "\u7528\u6237 ID").option("--activity-id <id>", "\u6D3B\u52A8\u7C7B\u578B ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--all-projects", "\u67E5\u8BE2\u6240\u6709\u9879\u76EE\u7684\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
1830
+ timeCmd.command("list").description("\u67E5\u8BE2\u5DE5\u65F6\u6761\u76EE").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)").option("--user-id <id>", "\u7528\u6237 ID").option("--activity-id <id>", "\u6D3B\u52A8\u7C7B\u578B ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--all-projects", "\u67E5\u8BE2\u6240\u6709\u9879\u76EE\u7684\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1108
1831
  const creds = resolveCredentials(options);
1109
1832
  const requiredFields = options.allProjects ? ["token", "host"] : ["token", "host", "project"];
1110
1833
  const validation = validateCredentials(creds, requiredFields);
1111
1834
  if (!validation.valid) {
1112
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1835
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1113
1836
  process.exit(1);
1114
1837
  }
1115
1838
  if (options.allProjects) {
1116
1839
  logger_default.info("\u67E5\u8BE2\u6240\u6709\u9879\u76EE\u7684\u5DE5\u65F6...");
1117
1840
  const projectsResult = await userService.getProjects(creds.token, creds.host);
1118
1841
  if (!projectsResult.success || !projectsResult.data) {
1119
- outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25");
1842
+ outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25", options.pretty);
1120
1843
  process.exit(1);
1121
1844
  }
1122
1845
  const projects = projectsResult.data;
@@ -1172,28 +1895,42 @@ function createTimeCommand() {
1172
1895
  offset: options.offset ? parseInt(options.offset, 10) : void 0
1173
1896
  });
1174
1897
  if (result.success && result.data) {
1175
- outputSuccess(result.data);
1898
+ outputSuccess(result.data, options.pretty);
1176
1899
  } else {
1177
- outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5DE5\u65F6\u5931\u8D25");
1900
+ outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5DE5\u65F6\u5931\u8D25", options.pretty);
1178
1901
  process.exit(1);
1179
1902
  }
1180
1903
  }
1181
1904
  });
1182
- timeCmd.command("create").description("\u521B\u5EFA\u5DE5\u65F6\u6761\u76EE").requiredOption("--issue <id>", "\u95EE\u9898 ID").requiredOption("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09").option("--activity <id>", "\u6D3B\u52A8\u7C7B\u578B ID").option("--date <date>", "\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u4ECA\u5929").option("--comments <comments>", "\u5907\u6CE8").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
1905
+ timeCmd.command("create").description("\u521B\u5EFA\u5DE5\u65F6\u6761\u76EE").requiredOption("--issue <id>", "\u95EE\u9898 ID").requiredOption("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09").requiredOption("--activity <id>", "\u6D3B\u52A8\u7C7B\u578B ID\uFF08\u4F7F\u7528 pm-cli time options \u83B7\u53D6\u53EF\u7528\u503C\uFF09").option("--date <date>", "\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u4ECA\u5929").option("--comments <comments>", "\u5907\u6CE8").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1183
1906
  const creds = resolveCredentials(options);
1184
1907
  const validation = validateCredentials(creds);
1185
1908
  if (!validation.valid) {
1186
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1909
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1910
+ process.exit(1);
1911
+ }
1912
+ let userId;
1913
+ const configUserId = getConfigValue("userId", options.config);
1914
+ if (configUserId) {
1915
+ userId = parseInt(configUserId, 10);
1916
+ }
1917
+ if (!userId) {
1918
+ outputError("\u7F3A\u5C11\u7528\u6237 ID\uFF0C\u8BF7\u5148\u8BBE\u7F6E: pm-cli config set user-id <\u60A8\u7684\u7528\u6237ID>", options.pretty);
1187
1919
  process.exit(1);
1188
1920
  }
1189
1921
  const issueId = parseInt(options.issue.replace(/^#/, ""), 10);
1190
1922
  if (isNaN(issueId)) {
1191
- outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
1923
+ outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
1192
1924
  process.exit(1);
1193
1925
  }
1194
1926
  const days = parseFloat(options.days);
1195
1927
  if (isNaN(days) || days <= 0) {
1196
- outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6570");
1928
+ outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6570", options.pretty);
1929
+ process.exit(1);
1930
+ }
1931
+ const activityId = parseInt(options.activity, 10);
1932
+ if (isNaN(activityId)) {
1933
+ outputError("\u65E0\u6548\u7684\u6D3B\u52A8\u7C7B\u578B ID\uFF0C\u4F7F\u7528 pm-cli time options \u83B7\u53D6\u53EF\u7528\u503C", options.pretty);
1197
1934
  process.exit(1);
1198
1935
  }
1199
1936
  const params = {
@@ -1203,97 +1940,133 @@ function createTimeCommand() {
1203
1940
  issue_id: issueId,
1204
1941
  hours: days,
1205
1942
  // API 参数名为 hours,但单位是天
1206
- spent_on: options.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
1943
+ spent_on: options.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
1944
+ user_id: userId,
1945
+ // 用户 ID(必填)
1946
+ activity_id: activityId,
1947
+ // 活动类型 ID(必填)
1948
+ bulk_create: "true"
1949
+ // 固定为 true
1207
1950
  };
1208
- if (options.activity) params.activity_id = parseInt(options.activity, 10);
1209
1951
  if (options.comments) params.comments = options.comments;
1210
1952
  const result = await timeEntryService.createTimeEntry(params);
1211
1953
  if (result.success && result.data) {
1212
- outputSuccess(result.data);
1954
+ outputSuccess(result.data, options.pretty);
1213
1955
  } else {
1214
- outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u5DE5\u65F6\u5931\u8D25");
1956
+ outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u5DE5\u65F6\u5931\u8D25", options.pretty);
1215
1957
  process.exit(1);
1216
1958
  }
1217
1959
  });
1218
- timeCmd.command("update <id>").description("\u66F4\u65B0\u5DE5\u65F6\u6761\u76EE").option("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09").option("--activity <id>", "\u6D3B\u52A8\u7C7B\u578B ID").option("--date <date>", "\u65E5\u671F (YYYY-MM-DD)").option("--comments <comments>", "\u5907\u6CE8").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
1960
+ timeCmd.command("update <id>").description("\u66F4\u65B0\u5DE5\u65F6\u6761\u76EE").requiredOption("--issue <id>", "\u95EE\u9898 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09\uFF08\u5FC5\u586B\uFF09").requiredOption("--activity <id>", "\u6D3B\u52A8\u7C7B\u578B ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--date <date>", "\u65E5\u671F (YYYY-MM-DD)\uFF08\u5FC5\u586B\uFF09").option("--comments <comments>", "\u5907\u6CE8").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
1219
1961
  const creds = resolveCredentials(options);
1220
1962
  const validation = validateCredentials(creds);
1221
1963
  if (!validation.valid) {
1222
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
1964
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1965
+ process.exit(1);
1966
+ }
1967
+ let userId;
1968
+ const configUserId = getConfigValue("userId", options.config);
1969
+ if (configUserId) {
1970
+ userId = parseInt(configUserId, 10);
1971
+ }
1972
+ if (!userId) {
1973
+ outputError("\u7F3A\u5C11\u7528\u6237 ID\uFF0C\u8BF7\u5148\u8BBE\u7F6E: pm-cli config set user-id <\u60A8\u7684\u7528\u6237ID>", options.pretty);
1223
1974
  process.exit(1);
1224
1975
  }
1225
1976
  const timeEntryId = parseInt(id, 10);
1226
1977
  if (isNaN(timeEntryId)) {
1227
- outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID");
1978
+ outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID", options.pretty);
1979
+ process.exit(1);
1980
+ }
1981
+ const issueId = parseInt(options.issue.replace(/^#/, ""), 10);
1982
+ if (isNaN(issueId)) {
1983
+ outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
1228
1984
  process.exit(1);
1229
1985
  }
1230
1986
  const params = {
1231
1987
  token: creds.token,
1232
1988
  host: creds.host,
1233
1989
  project: creds.project,
1234
- time_entry_id: timeEntryId
1990
+ id: timeEntryId,
1991
+ // 工时 ID
1992
+ issue_id: issueId,
1993
+ // 问题 ID(必填)
1994
+ hours: parseFloat(options.days),
1995
+ // 工时(必填)
1996
+ activity_id: parseInt(options.activity, 10),
1997
+ // 活动类型(必填)
1998
+ spent_on: options.date,
1999
+ // 日期(必填)
2000
+ user_id: userId,
2001
+ // 用户 ID(必填)
2002
+ updated_by: userId
2003
+ // 操作用户 ID(必填)
1235
2004
  };
1236
- if (options.days) params.hours = parseFloat(options.days);
1237
- if (options.activity) params.activity_id = parseInt(options.activity, 10);
1238
- if (options.date) params.spent_on = options.date;
1239
2005
  if (options.comments) params.comments = options.comments;
1240
2006
  const result = await timeEntryService.updateTimeEntry(params);
1241
2007
  if (result.success && result.data) {
1242
- outputSuccess(result.data);
2008
+ outputSuccess(result.data, options.pretty);
1243
2009
  } else {
1244
- outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u5DE5\u65F6\u5931\u8D25");
2010
+ outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u5DE5\u65F6\u5931\u8D25", options.pretty);
1245
2011
  process.exit(1);
1246
2012
  }
1247
2013
  });
1248
- timeCmd.command("delete <id>").description("\u5220\u9664\u5DE5\u65F6\u6761\u76EE").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
2014
+ timeCmd.command("delete <id>").description("\u5220\u9664\u5DE5\u65F6\u6761\u76EE").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
1249
2015
  const creds = resolveCredentials(options);
1250
- const validation = validateCredentials(creds);
2016
+ const validation = validateCredentials(creds, ["token", "host"]);
1251
2017
  if (!validation.valid) {
1252
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
2018
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1253
2019
  process.exit(1);
1254
2020
  }
1255
2021
  const timeEntryId = parseInt(id, 10);
1256
2022
  if (isNaN(timeEntryId)) {
1257
- outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID");
2023
+ outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID", options.pretty);
1258
2024
  process.exit(1);
1259
2025
  }
1260
2026
  const result = await timeEntryService.deleteTimeEntry(
1261
2027
  creds.token,
1262
2028
  creds.host,
1263
- creds.project,
1264
2029
  timeEntryId
1265
2030
  );
1266
2031
  if (result.success) {
1267
- outputSuccess({ message: "\u5DE5\u65F6\u6761\u76EE\u5DF2\u5220\u9664", id: timeEntryId });
2032
+ outputSuccess({ message: "\u5DE5\u65F6\u6761\u76EE\u5DF2\u5220\u9664", id: timeEntryId }, options.pretty);
1268
2033
  } else {
1269
- outputError(result.message || result.msg || result.api_error_msg || "\u5220\u9664\u5DE5\u65F6\u5931\u8D25");
2034
+ outputError(result.message || result.msg || result.api_error_msg || "\u5220\u9664\u5DE5\u65F6\u5931\u8D25", options.pretty);
1270
2035
  process.exit(1);
1271
2036
  }
1272
2037
  });
1273
- timeCmd.command("options").description("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
2038
+ timeCmd.command("options").description("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879\uFF08\u6D3B\u52A8\u7C7B\u578B\u5217\u8868\u7B49\uFF09").option("--issue <id>", "\u95EE\u9898 ID\uFF08\u53EF\u9009\uFF0C\u7528\u4E8E\u83B7\u53D6\u7279\u5B9A\u95EE\u9898\u7684\u5DE5\u65F6\u9009\u9879\uFF09").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1274
2039
  const creds = resolveCredentials(options);
1275
2040
  const validation = validateCredentials(creds);
1276
2041
  if (!validation.valid) {
1277
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
2042
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1278
2043
  process.exit(1);
1279
2044
  }
2045
+ const issueId = options.issue ? parseInt(options.issue.replace(/^#/, ""), 10) : void 0;
1280
2046
  const result = await timeEntryService.getTimeEntryOptions(
1281
2047
  creds.token,
1282
2048
  creds.host,
1283
- creds.project
2049
+ creds.project,
2050
+ issueId
1284
2051
  );
1285
- if (result.success) {
1286
- outputSuccess(result.data);
2052
+ if (result.success && result.data) {
2053
+ const data = result.data;
2054
+ if (data.options?.activities) {
2055
+ const activities = data.options.activities.map(([name, id]) => ({ id, name }));
2056
+ outputSuccess({ activities }, options.pretty);
2057
+ } else {
2058
+ outputSuccess(result.data, options.pretty);
2059
+ }
1287
2060
  } else {
1288
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9009\u9879\u5931\u8D25");
2061
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9009\u9879\u5931\u8D25", options.pretty);
1289
2062
  process.exit(1);
1290
2063
  }
1291
2064
  });
1292
- timeCmd.command("summary").description("\u5DE5\u65F6\u7EDF\u8BA1\u6C47\u603B\uFF08\u67E5\u8BE2\u6307\u5B9A\u65F6\u95F4\u6BB5\u7684\u5DE5\u65F6\u5E76\u8BA1\u7B97\u7F3A\u53E3\uFF09").option("--week <week>", "\u5468\u9009\u62E9: current (\u672C\u5468), last (\u4E0A\u5468), \u6216\u6570\u5B57\u504F\u79FB", "current").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--target <days>", "\u76EE\u6807\u5DE5\u65F6\uFF08\u5929\uFF09\uFF0C\u9ED8\u8BA4 5", "5").option("--user-id <id>", "\u7528\u6237 ID\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 user-id\uFF09").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
2065
+ timeCmd.command("summary").description("\u5DE5\u65F6\u7EDF\u8BA1\u6C47\u603B\uFF08\u67E5\u8BE2\u6307\u5B9A\u65F6\u95F4\u6BB5\u7684\u5DE5\u65F6\u5E76\u8BA1\u7B97\u7F3A\u53E3\uFF09").option("--week <week>", "\u5468\u9009\u62E9: current (\u672C\u5468), last (\u4E0A\u5468), \u6216\u6570\u5B57\u504F\u79FB", "current").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--target <days>", "\u76EE\u6807\u5DE5\u65F6\uFF08\u5929\uFF09\uFF0C\u9ED8\u8BA4 5", "5").option("--detail", "\u663E\u793A\u5B8C\u6574\u5DE5\u65F6\u6761\u76EE\u8BE6\u60C5").option("--user-id <id>", "\u7528\u6237 ID\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 user-id\uFF09").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1293
2066
  const creds = resolveCredentials(options);
1294
2067
  const validation = validateCredentials(creds, ["token", "host"]);
1295
2068
  if (!validation.valid) {
1296
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
2069
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1297
2070
  process.exit(1);
1298
2071
  }
1299
2072
  let fromDate;
@@ -1324,13 +2097,14 @@ function createTimeCommand() {
1324
2097
  }
1325
2098
  const projectsResult = await userService.getProjects(creds.token, creds.host);
1326
2099
  if (!projectsResult.success || !projectsResult.data) {
1327
- outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25");
2100
+ outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25", options.pretty);
1328
2101
  process.exit(1);
1329
2102
  }
1330
2103
  const projects = projectsResult.data;
1331
2104
  const projectNames = Object.values(projects).map((p) => p.name);
1332
2105
  logger_default.info(`\u67E5\u8BE2 ${projectNames.length} \u4E2A\u9879\u76EE...`);
1333
2106
  const projectSummary = [];
2107
+ const allTimeEntries = [];
1334
2108
  let totalHours = 0;
1335
2109
  for (const projectName of projectNames) {
1336
2110
  const result = await timeEntryService.queryTimeEntries({
@@ -1353,13 +2127,14 @@ function createTimeCommand() {
1353
2127
  });
1354
2128
  totalHours += projectHours;
1355
2129
  }
2130
+ allTimeEntries.push(...data.time_entries);
1356
2131
  }
1357
2132
  }
1358
2133
  }
1359
2134
  const targetHours = parseFloat(options.target) || 5;
1360
2135
  const remainingHours = Math.max(0, targetHours - totalHours);
1361
2136
  totalHours = Math.round(totalHours * 100) / 100;
1362
- outputSuccess({
2137
+ const output = {
1363
2138
  period: periodLabel,
1364
2139
  userId: userId || "\u672A\u6307\u5B9A",
1365
2140
  totalHours,
@@ -1367,7 +2142,15 @@ function createTimeCommand() {
1367
2142
  remainingHours: Math.round(remainingHours * 100) / 100,
1368
2143
  isFilled: remainingHours === 0,
1369
2144
  projectBreakdown: projectSummary.sort((a, b) => b.hours - a.hours)
1370
- });
2145
+ };
2146
+ if (options.detail) {
2147
+ output.timeEntries = allTimeEntries.sort((a, b) => {
2148
+ const dateA = a.spent_on || "";
2149
+ const dateB = b.spent_on || "";
2150
+ return dateA.localeCompare(dateB);
2151
+ });
2152
+ }
2153
+ outputSuccess(output, options.pretty);
1371
2154
  });
1372
2155
  return timeCmd;
1373
2156
  }
@@ -1376,26 +2159,26 @@ function createTimeCommand() {
1376
2159
  import { Command as Command5 } from "commander";
1377
2160
  function createProjectCommand() {
1378
2161
  const projectCmd = new Command5("project").description("\u9879\u76EE\u7BA1\u7406");
1379
- projectCmd.command("list").description("\u83B7\u53D6\u9879\u76EE\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
2162
+ projectCmd.command("list").description("\u83B7\u53D6\u9879\u76EE\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1380
2163
  const creds = resolveCredentials(options);
1381
2164
  const validation = validateCredentials(creds, ["token", "host"]);
1382
2165
  if (!validation.valid) {
1383
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
2166
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1384
2167
  process.exit(1);
1385
2168
  }
1386
2169
  const result = await userService.getProjects(creds.token, creds.host);
1387
2170
  if (result.success && result.data) {
1388
- outputSuccess(result.data);
2171
+ outputSuccess(result.data, options.pretty);
1389
2172
  } else {
1390
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25");
2173
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25", options.pretty);
1391
2174
  process.exit(1);
1392
2175
  }
1393
2176
  });
1394
- projectCmd.command("users").description("\u83B7\u53D6\u9879\u76EE\u7528\u6237\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
2177
+ projectCmd.command("users").description("\u83B7\u53D6\u9879\u76EE\u7528\u6237\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1395
2178
  const creds = resolveCredentials(options);
1396
2179
  const validation = validateCredentials(creds);
1397
2180
  if (!validation.valid) {
1398
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
2181
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1399
2182
  process.exit(1);
1400
2183
  }
1401
2184
  const result = await userService.getProjectUsers(
@@ -1404,24 +2187,24 @@ function createProjectCommand() {
1404
2187
  creds.project
1405
2188
  );
1406
2189
  if (result.success && result.data) {
1407
- outputSuccess(result.data);
2190
+ outputSuccess(result.data, options.pretty);
1408
2191
  } else {
1409
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25");
2192
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25", options.pretty);
1410
2193
  process.exit(1);
1411
2194
  }
1412
2195
  });
1413
- projectCmd.command("info").description("\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
2196
+ projectCmd.command("info").description("\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
1414
2197
  const creds = resolveCredentials(options);
1415
2198
  const validation = validateCredentials(creds, ["token", "host"]);
1416
2199
  if (!validation.valid) {
1417
- outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`);
2200
+ outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
1418
2201
  process.exit(1);
1419
2202
  }
1420
2203
  const result = await userService.getHostInfo(creds.token, creds.host);
1421
2204
  if (result.success) {
1422
- outputSuccess(result.data);
2205
+ outputSuccess(result.data, options.pretty);
1423
2206
  } else {
1424
- outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F\u5931\u8D25");
2207
+ outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F\u5931\u8D25", options.pretty);
1425
2208
  process.exit(1);
1426
2209
  }
1427
2210
  });
@@ -1429,8 +2212,15 @@ function createProjectCommand() {
1429
2212
  }
1430
2213
 
1431
2214
  // src/index.ts
2215
+ import { readFileSync as readFileSync2 } from "fs";
2216
+ import { dirname as dirname2, join as join2 } from "path";
2217
+ import { fileURLToPath } from "url";
2218
+ var __filename = fileURLToPath(import.meta.url);
2219
+ var __dirname = dirname2(__filename);
2220
+ var packageJson = JSON.parse(readFileSync2(join2(__dirname, "../package.json"), "utf-8"));
2221
+ var version = packageJson.version;
1432
2222
  var program = new Command6();
1433
- program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase) \u547D\u4EE4\u884C\u5DE5\u5177").version("0.1.0").option("--verbose", "\u8BE6\u7EC6\u8F93\u51FA").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F").hook("preAction", (thisCommand) => {
2223
+ program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase) \u547D\u4EE4\u884C\u5DE5\u5177").version(version).option("--verbose", "\u8BE6\u7EC6\u8F93\u51FA").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F").option("--no-mapping", "\u4E0D\u8F93\u51FA key \u6620\u5C04\u8868\uFF08\u9ED8\u8BA4\u8F93\u51FA\uFF09").hook("preAction", (thisCommand) => {
1434
2224
  const opts = thisCommand.opts();
1435
2225
  if (opts.debug) {
1436
2226
  logger_default.setLevel("debug");
@@ -1439,6 +2229,9 @@ program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase)
1439
2229
  } else {
1440
2230
  logger_default.setLevel("silent");
1441
2231
  }
2232
+ if (opts.mapping === false) {
2233
+ setKeyMappingEnabled(false);
2234
+ }
1442
2235
  });
1443
2236
  program.addCommand(createTestCommand());
1444
2237
  program.addCommand(createConfigCommand());