@hangox/pm-cli 1.1.3 → 1.1.4

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
@@ -1354,6 +1354,23 @@ var IssueService = class {
1354
1354
  }
1355
1355
  }
1356
1356
  }
1357
+ } else if (field.field_format === "user") {
1358
+ if (field.multiple && Array.isArray(field.value)) {
1359
+ const userIds = [];
1360
+ for (const item of field.value) {
1361
+ if (item.user?.id) {
1362
+ userIds.push(item.user.id);
1363
+ }
1364
+ }
1365
+ if (userIds.length > 0) {
1366
+ customFieldMap[field.id] = userIds.join(",");
1367
+ }
1368
+ } else {
1369
+ const userValue = field.value;
1370
+ if (userValue.user?.id) {
1371
+ customFieldMap[field.id] = userValue.user.id;
1372
+ }
1373
+ }
1357
1374
  } else {
1358
1375
  customFieldMap[field.id] = field.value;
1359
1376
  }
@@ -1458,7 +1475,7 @@ var IssueService = class {
1458
1475
  * @param options 选项
1459
1476
  */
1460
1477
  async batchCreateFromMarkdown(token, host, project, parentId, markdown, assignedToMail, options) {
1461
- const { dryRun = false, interval = 5e3 } = options || {};
1478
+ const { dryRun = false, interval = 5e3, extraCustomFields = {} } = options || {};
1462
1479
  logger_default.info("\u5F00\u59CB\u6279\u91CF\u521B\u5EFA\u5B50\u5355", {
1463
1480
  parentId,
1464
1481
  assignedToMail,
@@ -1507,7 +1524,8 @@ var IssueService = class {
1507
1524
  assignedToMail,
1508
1525
  dryRun,
1509
1526
  interval,
1510
- result
1527
+ result,
1528
+ extraCustomFields
1511
1529
  );
1512
1530
  logger_default.info("\u6279\u91CF\u521B\u5EFA\u5B8C\u6210", {
1513
1531
  totalCreated: result.totalCreated,
@@ -1525,7 +1543,7 @@ var IssueService = class {
1525
1543
  /**
1526
1544
  * 递归批量创建子单
1527
1545
  */
1528
- async batchCreateRecursive(token, host, project, taskNodes, targetParentId, parentInfo, assignedToMail, dryRun, interval, result) {
1546
+ async batchCreateRecursive(token, host, project, taskNodes, targetParentId, parentInfo, assignedToMail, dryRun, interval, result, extraCustomFields = {}) {
1529
1547
  for (const node of taskNodes) {
1530
1548
  const taskName = node.subject;
1531
1549
  const isLeafNode = node.children.length === 0;
@@ -1562,19 +1580,40 @@ var IssueService = class {
1562
1580
  }
1563
1581
  }
1564
1582
  }
1583
+ } else if (field.field_format === "user") {
1584
+ if (field.multiple && Array.isArray(field.value)) {
1585
+ const userIds = [];
1586
+ for (const item of field.value) {
1587
+ if (item.user?.id) {
1588
+ userIds.push(item.user.id);
1589
+ }
1590
+ }
1591
+ if (userIds.length > 0) {
1592
+ customFieldMap[field.id] = userIds.join(",");
1593
+ }
1594
+ } else {
1595
+ const userValue = field.value;
1596
+ if (userValue.user?.id) {
1597
+ customFieldMap[field.id] = userValue.user.id;
1598
+ }
1599
+ }
1565
1600
  } else {
1566
1601
  customFieldMap[field.id] = field.value;
1567
1602
  }
1568
1603
  }
1569
1604
  }
1570
- if (Object.keys(customFieldMap).length > 0) {
1571
- createParams.custom_field = JSON.stringify(customFieldMap);
1605
+ const mergedCustomFields = { ...customFieldMap, ...extraCustomFields };
1606
+ if (Object.keys(mergedCustomFields).length > 0) {
1607
+ createParams.custom_field = JSON.stringify(mergedCustomFields);
1572
1608
  }
1573
1609
  if (followsMails.length > 0) {
1574
1610
  createParams.follows = followsMails;
1575
1611
  }
1612
+ } else if (Object.keys(extraCustomFields).length > 0) {
1613
+ createParams.custom_field = JSON.stringify(extraCustomFields);
1576
1614
  }
1577
1615
  logger_default.info(`\u6B63\u5728\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`, { targetParentId, isLeafNode, estimatedHours: node.estimatedHours });
1616
+ logger_default.debug("\u521B\u5EFA\u53C2\u6570", { custom_field: createParams.custom_field, follows: createParams.follows });
1578
1617
  if (dryRun) {
1579
1618
  logger_default.info(`[\u6A21\u62DF] \u5C06\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`);
1580
1619
  result.totalCreated++;
@@ -1597,7 +1636,8 @@ var IssueService = class {
1597
1636
  assignedToMail,
1598
1637
  dryRun,
1599
1638
  interval,
1600
- result
1639
+ result,
1640
+ extraCustomFields
1601
1641
  );
1602
1642
  }
1603
1643
  } else {
@@ -1627,7 +1667,8 @@ var IssueService = class {
1627
1667
  assignedToMail,
1628
1668
  dryRun,
1629
1669
  interval,
1630
- result
1670
+ result,
1671
+ extraCustomFields
1631
1672
  );
1632
1673
  }
1633
1674
  } else {
@@ -1859,6 +1900,74 @@ function generateAIGuideOutput(exportDir, fileCount, totalSize) {
1859
1900
  `.trim();
1860
1901
  }
1861
1902
 
1903
+ // src/utils/custom-field-parser.ts
1904
+ var issueService2 = new IssueService();
1905
+ async function parseCustomFieldInputs(customFieldInputs, token, host, project) {
1906
+ if (customFieldInputs.length === 0) {
1907
+ return { fields: {} };
1908
+ }
1909
+ const customFieldData = {};
1910
+ for (const input of customFieldInputs) {
1911
+ const match = input.match(/^([^=]+)=(.*)$/);
1912
+ if (!match) {
1913
+ return {
1914
+ fields: {},
1915
+ error: `\u81EA\u5B9A\u4E49\u5B57\u6BB5\u683C\u5F0F\u9519\u8BEF: ${input}\uFF0C\u6B63\u786E\u683C\u5F0F\u4E3A "\u5B57\u6BB5ID=\u503C" \u6216 "\u5B57\u6BB5\u540D=\u503C"`
1916
+ };
1917
+ }
1918
+ const [, key, value] = match;
1919
+ customFieldData[key.trim()] = value.trim();
1920
+ }
1921
+ const hasNonNumericKey = Object.keys(customFieldData).some((key) => isNaN(Number(key)));
1922
+ if (hasNonNumericKey) {
1923
+ const fieldOptionsResult = await issueService2.getIssueFieldOptions(token, host, project);
1924
+ if (fieldOptionsResult.success && fieldOptionsResult.data) {
1925
+ const fieldOptions = fieldOptionsResult.data;
1926
+ if (fieldOptions.data?.custom_fields) {
1927
+ const fieldMap = /* @__PURE__ */ new Map();
1928
+ for (const field of fieldOptions.data.custom_fields) {
1929
+ fieldMap.set(field.name, field.id);
1930
+ }
1931
+ const convertedData = {};
1932
+ for (const [key, value] of Object.entries(customFieldData)) {
1933
+ const fieldId = isNaN(Number(key)) ? fieldMap.get(key) : Number(key);
1934
+ if (fieldId !== void 0) {
1935
+ convertedData[fieldId] = value;
1936
+ } else {
1937
+ return { fields: {}, error: `\u672A\u627E\u5230\u81EA\u5B9A\u4E49\u5B57\u6BB5: ${key}` };
1938
+ }
1939
+ }
1940
+ return { fields: convertedData };
1941
+ } else {
1942
+ return { fields: {}, error: "\u65E0\u6CD5\u83B7\u53D6\u81EA\u5B9A\u4E49\u5B57\u6BB5\u5217\u8868" };
1943
+ }
1944
+ } else {
1945
+ return { fields: {}, error: "\u83B7\u53D6\u81EA\u5B9A\u4E49\u5B57\u6BB5\u5217\u8868\u5931\u8D25" };
1946
+ }
1947
+ } else {
1948
+ const convertedData = {};
1949
+ for (const [key, value] of Object.entries(customFieldData)) {
1950
+ convertedData[Number(key)] = value;
1951
+ }
1952
+ return { fields: convertedData };
1953
+ }
1954
+ }
1955
+ function collectCustomFieldInputs(options) {
1956
+ const customFieldInputs = [];
1957
+ if (options.customField) {
1958
+ customFieldInputs.push(
1959
+ ...Array.isArray(options.customField) ? options.customField : [options.customField]
1960
+ );
1961
+ }
1962
+ if (options.cf) {
1963
+ customFieldInputs.push(...Array.isArray(options.cf) ? options.cf : [options.cf]);
1964
+ }
1965
+ return customFieldInputs;
1966
+ }
1967
+ function mergeCustomFields(inheritedFields, userFields) {
1968
+ return { ...inheritedFields, ...userFields };
1969
+ }
1970
+
1862
1971
  // src/commands/issue/index.ts
1863
1972
  function createIssueCommand() {
1864
1973
  const issueCmd = new Command3("issue").description("\u95EE\u9898\u7BA1\u7406");
@@ -1955,7 +2064,7 @@ function createIssueCommand() {
1955
2064
  }
1956
2065
  }
1957
2066
  });
1958
- 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) => {
2067
+ 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("--custom-field <key=value>", "\u81EA\u5B9A\u4E49\u5B57\u6BB5 (\u683C\u5F0F: \u5B57\u6BB5ID=\u503C \u6216 \u5B57\u6BB5\u540D=\u503C\uFF0C\u53EF\u591A\u6B21\u4F7F\u7528)").option("--cf <key=value>", "\u81EA\u5B9A\u4E49\u5B57\u6BB5\u7684\u7B80\u5199\u522B\u540D").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) => {
1959
2068
  const creds = resolveCredentials(options);
1960
2069
  const validation = validateCredentials(creds);
1961
2070
  if (!validation.valid) {
@@ -1998,8 +2107,8 @@ function createIssueCommand() {
1998
2107
  const followsMails = [];
1999
2108
  for (const field of parent.custom_fields) {
2000
2109
  if (field.value !== null && field.value !== void 0 && field.value !== "") {
2001
- const fieldWithIdentify = field;
2002
- if (fieldWithIdentify.identify === "IssuesQCFollow") {
2110
+ const fieldWithMeta = field;
2111
+ if (fieldWithMeta.identify === "IssuesQCFollow") {
2003
2112
  const followsValue = field.value;
2004
2113
  if (Array.isArray(followsValue)) {
2005
2114
  for (const item of followsValue) {
@@ -2008,6 +2117,23 @@ function createIssueCommand() {
2008
2117
  }
2009
2118
  }
2010
2119
  }
2120
+ } else if (fieldWithMeta.field_format === "user") {
2121
+ if (fieldWithMeta.multiple && Array.isArray(field.value)) {
2122
+ const userIds = [];
2123
+ for (const item of field.value) {
2124
+ if (item.user?.id) {
2125
+ userIds.push(item.user.id);
2126
+ }
2127
+ }
2128
+ if (userIds.length > 0) {
2129
+ customFieldMap[field.id] = userIds.join(",");
2130
+ }
2131
+ } else {
2132
+ const userValue = field.value;
2133
+ if (userValue.user?.id) {
2134
+ customFieldMap[field.id] = userValue.user.id;
2135
+ }
2136
+ }
2011
2137
  } else {
2012
2138
  customFieldMap[field.id] = field.value;
2013
2139
  }
@@ -2036,6 +2162,24 @@ function createIssueCommand() {
2036
2162
  if (options.startDate) params.start_date = options.startDate;
2037
2163
  if (options.dueDate) params.due_date = options.dueDate;
2038
2164
  if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours);
2165
+ const customFieldInputs = collectCustomFieldInputs(options);
2166
+ if (customFieldInputs.length > 0) {
2167
+ const parseResult = await parseCustomFieldInputs(
2168
+ customFieldInputs,
2169
+ creds.token,
2170
+ creds.host,
2171
+ creds.project
2172
+ );
2173
+ if (parseResult.error) {
2174
+ outputError(parseResult.error, options.pretty);
2175
+ process.exit(1);
2176
+ }
2177
+ const existingFields = params.custom_field ? JSON.parse(params.custom_field) : {};
2178
+ const mergedFields = mergeCustomFields(existingFields, parseResult.fields);
2179
+ if (Object.keys(mergedFields).length > 0) {
2180
+ params.custom_field = JSON.stringify(mergedFields);
2181
+ }
2182
+ }
2039
2183
  const result = await issueService.createIssue(params);
2040
2184
  if (result.success && result.data) {
2041
2185
  outputSuccess(result.data, options.pretty);
@@ -2474,7 +2618,7 @@ function createIssueCommand() {
2474
2618
  process.exit(1);
2475
2619
  }
2476
2620
  });
2477
- issueCmd.command("batch-create").description("\u4ECE Markdown \u6279\u91CF\u521B\u5EFA\u5B50\u5355").option("--parent-id <id>", "\u7236\u5355 ID").option("--parent-url <url>", "\u7236\u5355 PM \u94FE\u63A5").option("--markdown <file>", "Markdown \u6587\u4EF6\u8DEF\u5F84").option("--stdin", "\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB\u53D6 Markdown").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u7684 userMail\uFF09").option("--interval <ms>", "\u521B\u5EFA\u95F4\u9694\uFF08\u6BEB\u79D2\uFF09", "5000").option("--dry-run", "\u6A21\u62DF\u8FD0\u884C\uFF0C\u4E0D\u5B9E\u9645\u521B\u5EFA").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) => {
2621
+ issueCmd.command("batch-create").description("\u4ECE Markdown \u6279\u91CF\u521B\u5EFA\u5B50\u5355").option("--parent-id <id>", "\u7236\u5355 ID").option("--parent-url <url>", "\u7236\u5355 PM \u94FE\u63A5").option("--markdown <file>", "Markdown \u6587\u4EF6\u8DEF\u5F84").option("--stdin", "\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB\u53D6 Markdown").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u7684 userMail\uFF09").option("--interval <ms>", "\u521B\u5EFA\u95F4\u9694\uFF08\u6BEB\u79D2\uFF09", "5000").option("--dry-run", "\u6A21\u62DF\u8FD0\u884C\uFF0C\u4E0D\u5B9E\u9645\u521B\u5EFA").option("--custom-field <key=value>", "\u81EA\u5B9A\u4E49\u5B57\u6BB5 (\u683C\u5F0F: \u5B57\u6BB5ID=\u503C\uFF0C\u8986\u76D6\u7EE7\u627F\u7684\u503C\uFF0C\u53EF\u591A\u6B21\u4F7F\u7528)").option("--cf <key=value>", "\u81EA\u5B9A\u4E49\u5B57\u6BB5\u7684\u7B80\u5199\u522B\u540D").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) => {
2478
2622
  let parentId;
2479
2623
  let parentHost;
2480
2624
  if (options.parentUrl) {
@@ -2547,6 +2691,21 @@ function createIssueCommand() {
2547
2691
  }
2548
2692
  const interval = parseInt(options.interval, 10);
2549
2693
  const dryRun = options.dryRun || false;
2694
+ const customFieldInputs = collectCustomFieldInputs(options);
2695
+ let extraCustomFields = {};
2696
+ if (customFieldInputs.length > 0) {
2697
+ const parseResult = await parseCustomFieldInputs(
2698
+ customFieldInputs,
2699
+ creds.token,
2700
+ creds.host,
2701
+ creds.project || ""
2702
+ );
2703
+ if (parseResult.error) {
2704
+ outputError(parseResult.error, options.pretty);
2705
+ process.exit(1);
2706
+ }
2707
+ extraCustomFields = parseResult.fields;
2708
+ }
2550
2709
  const result = await issueService.batchCreateFromMarkdown(
2551
2710
  creds.token,
2552
2711
  creds.host,
@@ -2554,7 +2713,7 @@ function createIssueCommand() {
2554
2713
  parentId,
2555
2714
  markdown,
2556
2715
  assignedToMail,
2557
- { dryRun, interval }
2716
+ { dryRun, interval, extraCustomFields }
2558
2717
  );
2559
2718
  if (result.success && result.data) {
2560
2719
  const output = {
@@ -2824,6 +2983,7 @@ function createTimeCommand() {
2824
2983
  process.exit(1);
2825
2984
  }
2826
2985
  let projectName = creds.project;
2986
+ let projectAutoDetected = false;
2827
2987
  if (!projectName) {
2828
2988
  logger_default.info(`\u672A\u6307\u5B9A\u9879\u76EE\uFF0C\u6B63\u5728\u4ECE\u95EE\u9898 #${issueId} \u83B7\u53D6\u9879\u76EE\u4FE1\u606F...`);
2829
2989
  const issueResult = await issueService.getIssue(creds.token, creds.host, "", issueId);
@@ -2837,6 +2997,7 @@ function createTimeCommand() {
2837
2997
  process.exit(1);
2838
2998
  }
2839
2999
  logger_default.info(`\u4ECE\u95EE\u9898\u83B7\u53D6\u5230\u9879\u76EE\u540D\u79F0: ${projectName}`);
3000
+ projectAutoDetected = true;
2840
3001
  }
2841
3002
  const params = {
2842
3003
  token: creds.token,
@@ -2856,7 +3017,15 @@ function createTimeCommand() {
2856
3017
  if (options.comments) params.comments = options.comments;
2857
3018
  const result = await timeEntryService.createTimeEntry(params);
2858
3019
  if (result.success && result.data) {
2859
- outputSuccess(result.data, options.pretty);
3020
+ if (projectAutoDetected) {
3021
+ const dataWithHint = {
3022
+ ...result.data,
3023
+ _hint: `\u9879\u76EE\u540D\u79F0 "${projectName}" \u662F\u4ECE\u95EE\u9898\u81EA\u52A8\u83B7\u53D6\u7684\u3002\u5982\u9700\u8BBE\u4E3A\u9ED8\u8BA4\uFF0C\u8BF7\u6267\u884C: pm-cli config set project "${projectName}"`
3024
+ };
3025
+ outputSuccess(dataWithHint, options.pretty);
3026
+ } else {
3027
+ outputSuccess(result.data, options.pretty);
3028
+ }
2860
3029
  } else {
2861
3030
  outputError(extractErrorMessage(result, "\u521B\u5EFA\u5DE5\u65F6\u5931\u8D25"), options.pretty);
2862
3031
  process.exit(1);