@hecom/codearts 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/bin/cli.js +11 -0
  2. package/dist/charts/chart.interface.d.ts +5 -0
  3. package/dist/charts/chart.interface.js +2 -0
  4. package/dist/charts/index.d.ts +2 -0
  5. package/dist/charts/index.js +17 -0
  6. package/dist/charts/modules/__tests__/bug-by-developer-hours.test.d.ts +1 -0
  7. package/dist/charts/modules/__tests__/bug-by-developer-hours.test.js +54 -0
  8. package/dist/charts/modules/__tests__/bug-by-fix-duration.test.d.ts +1 -0
  9. package/dist/charts/modules/__tests__/bug-by-fix-duration.test.js +89 -0
  10. package/dist/charts/modules/__tests__/bug-open-priority-heatmap.test.d.ts +1 -0
  11. package/dist/charts/modules/__tests__/bug-open-priority-heatmap.test.js +61 -0
  12. package/dist/charts/modules/bug-by-assignee.d.ts +2 -0
  13. package/dist/charts/modules/bug-by-assignee.js +30 -0
  14. package/dist/charts/modules/bug-by-defect-analysis.d.ts +2 -0
  15. package/dist/charts/modules/bug-by-defect-analysis.js +30 -0
  16. package/dist/charts/modules/bug-by-developer-hours.d.ts +2 -0
  17. package/dist/charts/modules/bug-by-developer-hours.js +32 -0
  18. package/dist/charts/modules/bug-by-fix-duration.d.ts +2 -0
  19. package/dist/charts/modules/bug-by-fix-duration.js +46 -0
  20. package/dist/charts/modules/bug-by-module.d.ts +2 -0
  21. package/dist/charts/modules/bug-by-module.js +34 -0
  22. package/dist/charts/modules/bug-open-priority-heatmap.d.ts +2 -0
  23. package/dist/charts/modules/bug-open-priority-heatmap.js +74 -0
  24. package/dist/charts/renderer.d.ts +21 -0
  25. package/dist/charts/renderer.js +163 -0
  26. package/dist/commands/index.d.ts +1 -0
  27. package/dist/commands/index.js +3 -1
  28. package/dist/commands/rebug.command.d.ts +5 -0
  29. package/dist/commands/rebug.command.js +113 -0
  30. package/dist/services/business.service.d.ts +8 -0
  31. package/dist/services/business.service.js +38 -0
  32. package/dist/types/index.d.ts +8 -1
  33. package/dist/types/index.js +7 -0
  34. package/dist/utils/config-loader.d.ts +1 -0
  35. package/package.json +5 -2
package/dist/bin/cli.js CHANGED
@@ -41,6 +41,7 @@ const bug_command_1 = require("../commands/bug.command");
41
41
  const config_command_1 = require("../commands/config.command");
42
42
  const daily_command_1 = require("../commands/daily.command");
43
43
  const fix_command_1 = require("../commands/fix.command");
44
+ const rebug_command_1 = require("../commands/rebug.command");
44
45
  const work_hour_command_1 = require("../commands/work-hour.command");
45
46
  const config_loader_1 = require("../utils/config-loader");
46
47
  const console_1 = require("../utils/console");
@@ -120,6 +121,16 @@ program
120
121
  logger_1.logger.setOutputFormat(cliOptions.output);
121
122
  await (0, fix_command_1.fixCommand)(cliOptions);
122
123
  });
124
+ // rebug 命令
125
+ program
126
+ .command('rebug')
127
+ .description('Bug 列表交互式查询与多维度可视化分析')
128
+ .option('--output-dir <path>', '输出 HTML 报告的目录(默认输出到系统 cache 目录,指定此参数则输出到当前目录)')
129
+ .action(async (options, command) => {
130
+ const cliOptions = { ...command.parent.opts(), outputDir: options.outputDir };
131
+ logger_1.logger.setOutputFormat(cliOptions.output);
132
+ await (0, rebug_command_1.rebugCommand)(cliOptions);
133
+ });
123
134
  // 检查配置并自动执行 config 命令
124
135
  async function checkConfigAndRun() {
125
136
  const args = process.argv.slice(2);
@@ -0,0 +1,5 @@
1
+ import { IssueItem } from '../types';
2
+ export interface ChartModule {
3
+ title: string;
4
+ buildOption(bugs: IssueItem[]): object;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from './chart.interface';
2
+ export declare const allCharts: ChartModule[];
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.allCharts = void 0;
4
+ const bug_by_assignee_1 = require("./modules/bug-by-assignee");
5
+ const bug_by_defect_analysis_1 = require("./modules/bug-by-defect-analysis");
6
+ const bug_by_module_1 = require("./modules/bug-by-module");
7
+ const bug_by_fix_duration_1 = require("./modules/bug-by-fix-duration");
8
+ const bug_by_developer_hours_1 = require("./modules/bug-by-developer-hours");
9
+ const bug_open_priority_heatmap_1 = require("./modules/bug-open-priority-heatmap");
10
+ exports.allCharts = [
11
+ bug_by_defect_analysis_1.bugByDefectAnalysisChart,
12
+ bug_by_assignee_1.bugByAssigneeChart,
13
+ bug_by_module_1.bugByModuleChart,
14
+ bug_by_fix_duration_1.bugByFixDurationChart,
15
+ bug_by_developer_hours_1.bugByDeveloperHoursChart,
16
+ bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart,
17
+ ];
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bug_by_developer_hours_1 = require("../bug-by-developer-hours");
4
+ function makeIssue(nickName, hours) {
5
+ return {
6
+ developer: {
7
+ id: 1,
8
+ name: '',
9
+ nick_name: nickName,
10
+ user_id: '',
11
+ user_num_id: 1,
12
+ first_name: '',
13
+ },
14
+ actual_work_hours: hours,
15
+ new_custom_fields: [],
16
+ deleted: false,
17
+ };
18
+ }
19
+ describe('bugByDeveloperHoursChart', () => {
20
+ it('标题应为"开发人员工时消耗"', () => {
21
+ expect(bug_by_developer_hours_1.bugByDeveloperHoursChart.title).toBe('开发人员工时消耗');
22
+ });
23
+ it('按开发人员汇总工时并降序排列', () => {
24
+ const bugs = [makeIssue('张三', 3), makeIssue('李四', 5), makeIssue('张三', 1)];
25
+ const option = bug_by_developer_hours_1.bugByDeveloperHoursChart.buildOption(bugs);
26
+ const names = option.yAxis.data;
27
+ const values = option.series[0].data;
28
+ expect(names[0]).toBe('李四');
29
+ expect(values[0]).toBe(5);
30
+ expect(names[1]).toBe('张三');
31
+ expect(values[1]).toBe(4);
32
+ });
33
+ it('过滤掉 nick_name 为空的条目', () => {
34
+ const bugs = [makeIssue('', 4), makeIssue('王五', 2)];
35
+ const option = bug_by_developer_hours_1.bugByDeveloperHoursChart.buildOption(bugs);
36
+ const names = option.yAxis.data;
37
+ expect(names).not.toContain('');
38
+ expect(names).toContain('王五');
39
+ });
40
+ it('过滤掉 developer 未设置的条目', () => {
41
+ const bugs = [
42
+ { ...makeIssue('赵六', 3), developer: null },
43
+ makeIssue('赵六', 1),
44
+ ];
45
+ const option = bug_by_developer_hours_1.bugByDeveloperHoursChart.buildOption(bugs);
46
+ const values = option.series[0].data;
47
+ expect(values[0]).toBe(1);
48
+ });
49
+ it('空数组时返回空图表', () => {
50
+ const option = bug_by_developer_hours_1.bugByDeveloperHoursChart.buildOption([]);
51
+ expect(option.yAxis.data).toHaveLength(0);
52
+ expect(option.series[0].data).toHaveLength(0);
53
+ });
54
+ });
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bug_by_fix_duration_1 = require("../bug-by-fix-duration");
4
+ function makeIssue(overrides) {
5
+ return {
6
+ actual_work_hours: 0,
7
+ assigned_cc_user: [],
8
+ assigned_user: {
9
+ id: 1,
10
+ name: '',
11
+ nick_name: '测试人',
12
+ user_id: '',
13
+ user_num_id: 1,
14
+ first_name: '',
15
+ },
16
+ begin_time: '',
17
+ closed_time: '',
18
+ created_time: '2026-03-01T08:00:00Z',
19
+ creator: { id: 1, name: '', nick_name: '', user_id: '', user_num_id: 1, first_name: '' },
20
+ custom_fields: [],
21
+ developer: {
22
+ id: 1,
23
+ name: '',
24
+ nick_name: '开发人',
25
+ user_id: '',
26
+ user_num_id: 1,
27
+ first_name: '',
28
+ },
29
+ domain: { id: 1, name: '' },
30
+ done_ratio: 0,
31
+ end_time: '',
32
+ expected_work_hours: 0,
33
+ id: 1,
34
+ iteration: { id: 1, name: '' },
35
+ module: { id: 1, name: '' },
36
+ name: 'test bug',
37
+ new_custom_fields: [],
38
+ parent_issue: { id: 0, name: '' },
39
+ priority: { id: 1, name: '中' },
40
+ project: { project_id: '', project_name: '', project_num_id: 1 },
41
+ severity: { id: 1, name: '重要' },
42
+ status: { id: 5, name: '已关闭' },
43
+ tracker: { id: 3, name: 'Bug' },
44
+ updated_time: '',
45
+ deleted: false,
46
+ ...overrides,
47
+ };
48
+ }
49
+ describe('bugByFixDurationChart', () => {
50
+ it('标题应为"修复周期分布"', () => {
51
+ expect(bug_by_fix_duration_1.bugByFixDurationChart.title).toBe('修复周期分布');
52
+ });
53
+ it('closed_time 为空时归入"未关闭"桶', () => {
54
+ const bugs = [makeIssue({ closed_time: '' })];
55
+ const option = bug_by_fix_duration_1.bugByFixDurationChart.buildOption(bugs);
56
+ const data = option.series[0].data;
57
+ const categories = option.xAxis.data;
58
+ const idx = categories.indexOf('未关闭');
59
+ expect(idx).toBeGreaterThanOrEqual(0);
60
+ expect(data[idx]).toBe(1);
61
+ });
62
+ it('1天内关闭归入"≤1天"桶(ceil,间隔0.5天)', () => {
63
+ const created = '2026-03-01T08:00:00Z';
64
+ const closed = '2026-03-01T20:00:00Z'; // 12h → Math.ceil(0.5) = 1
65
+ const bugs = [makeIssue({ created_time: created, closed_time: closed })];
66
+ const option = bug_by_fix_duration_1.bugByFixDurationChart.buildOption(bugs);
67
+ const data = option.series[0].data;
68
+ const categories = option.xAxis.data;
69
+ const idx = categories.indexOf('≤1天');
70
+ expect(idx).toBeGreaterThanOrEqual(0);
71
+ expect(data[idx]).toBe(1);
72
+ });
73
+ it('3天关闭归入"2-3天"桶', () => {
74
+ const created = '2026-03-01T00:00:00Z';
75
+ const closed = '2026-03-04T00:00:00Z'; // 3天整
76
+ const bugs = [makeIssue({ created_time: created, closed_time: closed })];
77
+ const option = bug_by_fix_duration_1.bugByFixDurationChart.buildOption(bugs);
78
+ const data = option.series[0].data;
79
+ const categories = option.xAxis.data;
80
+ const idx = categories.indexOf('2-3天');
81
+ expect(idx).toBeGreaterThanOrEqual(0);
82
+ expect(data[idx]).toBe(1);
83
+ });
84
+ it('空数组时所有桶数量均为0', () => {
85
+ const option = bug_by_fix_duration_1.bugByFixDurationChart.buildOption([]);
86
+ const data = option.series[0].data;
87
+ expect(data.every((v) => v === 0)).toBe(true);
88
+ });
89
+ });
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bug_open_priority_heatmap_1 = require("../bug-open-priority-heatmap");
4
+ function makeIssue(assignee, severityName, statusName, deleted = false) {
5
+ return {
6
+ assigned_user: {
7
+ id: 1,
8
+ name: '',
9
+ nick_name: assignee,
10
+ user_id: '',
11
+ user_num_id: 1,
12
+ first_name: '',
13
+ },
14
+ severity: { id: 1, name: severityName },
15
+ status: { id: 5, name: statusName },
16
+ deleted,
17
+ new_custom_fields: [],
18
+ };
19
+ }
20
+ describe('bugOpenPriorityHeatmapChart', () => {
21
+ it('标题应为"未关闭 Bug 优先级分布"', () => {
22
+ expect(bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart.title).toBe('未关闭 Bug 优先级分布');
23
+ });
24
+ it('过滤掉已关闭的 bug', () => {
25
+ const bugs = [
26
+ makeIssue('张三', '重要', '已关闭'),
27
+ makeIssue('张三', '严重', '进行中'),
28
+ ];
29
+ const option = bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart.buildOption(bugs);
30
+ const seriesData = option.series[0].data;
31
+ const total = seriesData.reduce((sum, d) => sum + d[2], 0);
32
+ expect(total).toBe(1);
33
+ });
34
+ it('过滤掉 deleted=true 的 bug', () => {
35
+ const bugs = [
36
+ makeIssue('张三', '重要', '进行中', true),
37
+ makeIssue('李四', '一般', '新问题', false),
38
+ ];
39
+ const option = bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart.buildOption(bugs);
40
+ const seriesData = option.series[0].data;
41
+ const total = seriesData.reduce((sum, d) => sum + d[2], 0);
42
+ expect(total).toBe(1);
43
+ });
44
+ it('X 轴严重程度按 一般→重要→严重 顺序排列', () => {
45
+ const bugs = [];
46
+ const option = bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart.buildOption(bugs);
47
+ const xData = option.xAxis.data;
48
+ expect(xData.indexOf('一般')).toBeLessThan(xData.indexOf('重要'));
49
+ expect(xData.indexOf('重要')).toBeLessThan(xData.indexOf('严重'));
50
+ });
51
+ it('visualMap max 至少为 1(防止空数据时 min===max===0)', () => {
52
+ const option = bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart.buildOption([]);
53
+ expect(option.visualMap.max).toBeGreaterThanOrEqual(1);
54
+ });
55
+ it('未知 severity 值放 X 轴末尾', () => {
56
+ const bugs = [makeIssue('张三', '未知等级', '进行中')];
57
+ const option = bug_open_priority_heatmap_1.bugOpenPriorityHeatmapChart.buildOption(bugs);
58
+ const xData = option.xAxis.data;
59
+ expect(xData[xData.length - 1]).toBe('未知等级');
60
+ });
61
+ });
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from '../chart.interface';
2
+ export declare const bugByAssigneeChart: ChartModule;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bugByAssigneeChart = void 0;
4
+ exports.bugByAssigneeChart = {
5
+ title: '修复人 Bug 数量',
6
+ buildOption(bugs) {
7
+ const countMap = new Map();
8
+ bugs.forEach((bug) => {
9
+ const name = bug.developer?.nick_name || '未设置';
10
+ countMap.set(name, (countMap.get(name) || 0) + 1);
11
+ });
12
+ const sorted = Array.from(countMap.entries()).sort((a, b) => b[1] - a[1]);
13
+ const names = sorted.map(([name]) => name);
14
+ const values = sorted.map(([, value]) => value);
15
+ return {
16
+ tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
17
+ grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true },
18
+ xAxis: { type: 'value' },
19
+ yAxis: { type: 'category', data: names, inverse: true },
20
+ series: [
21
+ {
22
+ type: 'bar',
23
+ data: values,
24
+ label: { show: true, position: 'right' },
25
+ itemStyle: { color: '#5470c6' },
26
+ },
27
+ ],
28
+ };
29
+ },
30
+ };
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from '../chart.interface';
2
+ export declare const bugByDefectAnalysisChart: ChartModule;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bugByDefectAnalysisChart = void 0;
4
+ exports.bugByDefectAnalysisChart = {
5
+ title: '缺陷技术分析分布',
6
+ buildOption(bugs) {
7
+ const countMap = new Map();
8
+ bugs.forEach((bug) => {
9
+ const field = bug.new_custom_fields?.find((f) => f.custom_field === 'custom_field32');
10
+ const value = field?.value || '未填写';
11
+ countMap.set(value, (countMap.get(value) || 0) + 1);
12
+ });
13
+ const data = Array.from(countMap.entries())
14
+ .sort((a, b) => b[1] - a[1])
15
+ .map(([name, value]) => ({ name, value }));
16
+ return {
17
+ tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
18
+ legend: { orient: 'vertical', left: 'left', type: 'scroll' },
19
+ series: [
20
+ {
21
+ type: 'pie',
22
+ radius: ['40%', '70%'],
23
+ avoidLabelOverlap: true,
24
+ label: { show: true, formatter: '{b}: {d}%' },
25
+ data,
26
+ },
27
+ ],
28
+ };
29
+ },
30
+ };
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from '../chart.interface';
2
+ export declare const bugByDeveloperHoursChart: ChartModule;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bugByDeveloperHoursChart = void 0;
4
+ exports.bugByDeveloperHoursChart = {
5
+ title: '开发人员工时消耗',
6
+ buildOption(bugs) {
7
+ const hoursMap = new Map();
8
+ bugs.forEach((bug) => {
9
+ const name = bug.developer?.nick_name;
10
+ if (!name || name === '未设置')
11
+ return;
12
+ hoursMap.set(name, (hoursMap.get(name) ?? 0) + (bug.actual_work_hours ?? 0));
13
+ });
14
+ const sorted = Array.from(hoursMap.entries()).sort((a, b) => b[1] - a[1]);
15
+ const names = sorted.map(([name]) => name);
16
+ const values = sorted.map(([, hours]) => hours);
17
+ return {
18
+ tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
19
+ grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true },
20
+ xAxis: { type: 'value', name: '小时' },
21
+ yAxis: { type: 'category', data: names, inverse: true },
22
+ series: [
23
+ {
24
+ type: 'bar',
25
+ data: values,
26
+ label: { show: true, position: 'right', formatter: '{c}h' },
27
+ itemStyle: { color: '#ee6666' },
28
+ },
29
+ ],
30
+ };
31
+ },
32
+ };
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from '../chart.interface';
2
+ export declare const bugByFixDurationChart: ChartModule;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bugByFixDurationChart = void 0;
4
+ const BUCKETS = ['未关闭', '≤1天', '2-3天', '4-7天', '8-14天', '>14天'];
5
+ function getDurationBucket(created, closed) {
6
+ if (!closed)
7
+ return '未关闭';
8
+ const days = Math.ceil((new Date(closed).getTime() - new Date(created).getTime()) / (1000 * 60 * 60 * 24));
9
+ if (days <= 1)
10
+ return '≤1天';
11
+ if (days <= 3)
12
+ return '2-3天';
13
+ if (days <= 7)
14
+ return '4-7天';
15
+ if (days <= 14)
16
+ return '8-14天';
17
+ return '>14天';
18
+ }
19
+ exports.bugByFixDurationChart = {
20
+ title: '修复周期分布',
21
+ buildOption(bugs) {
22
+ const countMap = new Map(BUCKETS.map((b) => [b, 0]));
23
+ bugs.forEach((bug) => {
24
+ const bucket = getDurationBucket(bug.created_time, bug.closed_time);
25
+ countMap.set(bucket, (countMap.get(bucket) ?? 0) + 1);
26
+ });
27
+ const values = BUCKETS.map((b) => countMap.get(b) ?? 0);
28
+ return {
29
+ tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
30
+ grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
31
+ xAxis: {
32
+ type: 'category',
33
+ data: [...BUCKETS],
34
+ },
35
+ yAxis: { type: 'value', minInterval: 1 },
36
+ series: [
37
+ {
38
+ type: 'bar',
39
+ data: values,
40
+ label: { show: true, position: 'top' },
41
+ itemStyle: { color: '#fac858' },
42
+ },
43
+ ],
44
+ };
45
+ },
46
+ };
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from '../chart.interface';
2
+ export declare const bugByModuleChart: ChartModule;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bugByModuleChart = void 0;
4
+ exports.bugByModuleChart = {
5
+ title: '模块 Bug 分布',
6
+ buildOption(bugs) {
7
+ const countMap = new Map();
8
+ bugs.forEach((bug) => {
9
+ const name = bug.module?.name || '未分配模块';
10
+ countMap.set(name, (countMap.get(name) || 0) + 1);
11
+ });
12
+ const sorted = Array.from(countMap.entries()).sort((a, b) => b[1] - a[1]);
13
+ const names = sorted.map(([name]) => name);
14
+ const values = sorted.map(([, value]) => value);
15
+ return {
16
+ tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
17
+ grid: { left: '3%', right: '4%', bottom: '15%', containLabel: true },
18
+ xAxis: {
19
+ type: 'category',
20
+ data: names,
21
+ axisLabel: { rotate: 30, overflow: 'truncate', width: 80 },
22
+ },
23
+ yAxis: { type: 'value' },
24
+ series: [
25
+ {
26
+ type: 'bar',
27
+ data: values,
28
+ label: { show: true, position: 'top' },
29
+ itemStyle: { color: '#91cc75' },
30
+ },
31
+ ],
32
+ };
33
+ },
34
+ };
@@ -0,0 +1,2 @@
1
+ import { ChartModule } from '../chart.interface';
2
+ export declare const bugOpenPriorityHeatmapChart: ChartModule;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bugOpenPriorityHeatmapChart = void 0;
4
+ const SEVERITY_ORDER = ['一般', '重要', '严重'];
5
+ exports.bugOpenPriorityHeatmapChart = {
6
+ title: '未关闭 Bug 优先级分布',
7
+ buildOption(bugs) {
8
+ const openBugs = bugs.filter((b) => !b.deleted && b.status?.name !== '已关闭');
9
+ const unknownSeverities = [
10
+ ...new Set(openBugs.map((b) => b.severity?.name ?? '').filter((s) => s && !SEVERITY_ORDER.includes(s))),
11
+ ];
12
+ const xCategories = [...SEVERITY_ORDER, ...unknownSeverities];
13
+ const assigneeCounts = new Map();
14
+ openBugs.forEach((b) => {
15
+ const name = b.assigned_user?.nick_name ?? '未分配';
16
+ assigneeCounts.set(name, (assigneeCounts.get(name) ?? 0) + 1);
17
+ });
18
+ const yCategories = Array.from(assigneeCounts.entries())
19
+ .sort((a, b) => b[1] - a[1])
20
+ .map(([name]) => name);
21
+ const heatMap = new Map();
22
+ openBugs.forEach((b) => {
23
+ const xKey = b.severity?.name ?? '';
24
+ const yKey = b.assigned_user?.nick_name ?? '未分配';
25
+ const key = `${xKey}|${yKey}`;
26
+ heatMap.set(key, (heatMap.get(key) ?? 0) + 1);
27
+ });
28
+ const data = [];
29
+ xCategories.forEach((x, xi) => {
30
+ yCategories.forEach((y, yi) => {
31
+ const v = heatMap.get(`${x}|${y}`) ?? 0;
32
+ data.push([xi, yi, v]);
33
+ });
34
+ });
35
+ const maxValue = data.reduce((max, d) => Math.max(max, d[2]), 0);
36
+ return {
37
+ tooltip: {
38
+ position: 'top',
39
+ formatter: (params) => `${yCategories[params.data[1]]} / ${xCategories[params.data[0]]}: ${params.data[2]} 个`,
40
+ },
41
+ grid: { left: '3%', right: '4%', bottom: '10%', containLabel: true },
42
+ xAxis: {
43
+ type: 'category',
44
+ data: xCategories,
45
+ splitArea: { show: true },
46
+ },
47
+ yAxis: {
48
+ type: 'category',
49
+ data: yCategories,
50
+ splitArea: { show: true },
51
+ },
52
+ visualMap: {
53
+ min: 0,
54
+ max: Math.max(1, maxValue),
55
+ calculable: true,
56
+ orient: 'horizontal',
57
+ left: 'center',
58
+ bottom: '0%',
59
+ inRange: { color: ['#ffffff', '#ee6666'] },
60
+ },
61
+ series: [
62
+ {
63
+ type: 'heatmap',
64
+ data,
65
+ label: {
66
+ show: true,
67
+ formatter: (params) => params.data[2] > 0 ? String(params.data[2]) : '',
68
+ },
69
+ emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.5)' } },
70
+ },
71
+ ],
72
+ };
73
+ },
74
+ };
@@ -0,0 +1,21 @@
1
+ import { IssueItem } from '../types';
2
+ import { ChartModule } from './chart.interface';
3
+ export interface ReportMeta {
4
+ iterationNames: string[];
5
+ terminalTypes: string[];
6
+ totalCount: number;
7
+ generatedAt: string;
8
+ }
9
+ /**
10
+ * 生成 HTML 报告并写入文件,返回文件路径。
11
+ * @param bugs Bug 列表
12
+ * @param charts 图表模块列表
13
+ * @param meta 报告元数据
14
+ * @param outputDir 输出目录,若不指定则使用系统 cache 目录
15
+ * 若文件写入失败,将 HTML 内容通过 logger 输出作为兜底,然后抛出错误。
16
+ */
17
+ export declare function renderReport(bugs: IssueItem[], charts: ChartModule[], meta: ReportMeta, outputDir?: string): string;
18
+ /**
19
+ * 自动在系统默认浏览器中打开文件
20
+ */
21
+ export declare function openInBrowser(filePath: string): void;
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.renderReport = renderReport;
37
+ exports.openInBrowser = openInBrowser;
38
+ const child_process_1 = require("child_process");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const logger_1 = require("../utils/logger");
42
+ /**
43
+ * 生成 HTML 报告并写入文件,返回文件路径。
44
+ * @param bugs Bug 列表
45
+ * @param charts 图表模块列表
46
+ * @param meta 报告元数据
47
+ * @param outputDir 输出目录,若不指定则使用系统 cache 目录
48
+ * 若文件写入失败,将 HTML 内容通过 logger 输出作为兜底,然后抛出错误。
49
+ */
50
+ function renderReport(bugs, charts, meta, outputDir) {
51
+ const d = new Date(meta.generatedAt);
52
+ const pad = (n) => String(n).padStart(2, '0');
53
+ const timestamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
54
+ const filename = `rebug-report-${timestamp}.html`;
55
+ // 如果未指定输出目录,使用系统 cache 目录
56
+ let targetDir = outputDir;
57
+ if (!targetDir) {
58
+ const os = require('os');
59
+ const platform = process.platform;
60
+ if (platform === 'darwin') {
61
+ targetDir = path.join(os.homedir(), 'Library', 'Caches', 'hecom-codearts');
62
+ }
63
+ else if (platform === 'linux') {
64
+ targetDir = path.join(os.homedir(), '.cache', 'hecom-codearts');
65
+ }
66
+ else if (platform === 'win32') {
67
+ targetDir = path.join(os.homedir(), 'AppData', 'Local', 'Temp', 'hecom-codearts');
68
+ }
69
+ else {
70
+ targetDir = path.join(os.tmpdir(), 'hecom-codearts');
71
+ }
72
+ }
73
+ // 确保目录存在
74
+ if (!fs.existsSync(targetDir)) {
75
+ fs.mkdirSync(targetDir, { recursive: true });
76
+ }
77
+ const outputPath = path.join(targetDir, filename);
78
+ const html = buildHtml(bugs, charts, meta);
79
+ try {
80
+ fs.writeFileSync(outputPath, html, 'utf-8');
81
+ }
82
+ catch (writeError) {
83
+ logger_1.logger.warn('文件写入失败,请手动保存以下 HTML 内容:');
84
+ logger_1.logger.warn(html);
85
+ throw writeError;
86
+ }
87
+ return outputPath;
88
+ }
89
+ function buildHtml(bugs, charts, meta) {
90
+ const chartContainers = charts
91
+ .map((chart, i) => `<div class="chart-card">
92
+ <div class="chart-title">${chart.title}</div>
93
+ <div id="chart-${i}" style="height:400px;"></div>
94
+ </div>`)
95
+ .join('\n');
96
+ const chartScripts = charts
97
+ .map((chart, i) => {
98
+ const option = JSON.stringify(chart.buildOption(bugs));
99
+ return `echarts.init(document.getElementById('chart-${i}')).setOption(${option});`;
100
+ })
101
+ .join('\n');
102
+ const iterationsText = meta.iterationNames.join(', ') || '全部';
103
+ const terminalText = meta.terminalTypes.join(', ') || '全部';
104
+ return `<!DOCTYPE html>
105
+ <html lang="zh-CN">
106
+ <head>
107
+ <meta charset="UTF-8">
108
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
109
+ <title>Bug 分析报告</title>
110
+ <style>
111
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; padding: 20px; background: #f5f5f5; color: #333; }
112
+ .header { background: #fff; border-radius: 8px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
113
+ .header h1 { margin: 0 0 16px; font-size: 22px; color: #1a1a1a; }
114
+ .meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
115
+ .meta-item { background: #f8f9fa; border-radius: 6px; padding: 12px; }
116
+ .meta-label { font-size: 12px; color: #888; margin-bottom: 4px; }
117
+ .meta-value { font-size: 14px; font-weight: 600; color: #333; }
118
+ .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
119
+ .chart-card { background: #fff; border-radius: 8px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
120
+ .chart-title { font-size: 15px; font-weight: 600; color: #1a1a1a; margin-bottom: 12px; }
121
+ @media (max-width: 900px) { .charts-grid { grid-template-columns: 1fr; } }
122
+ </style>
123
+ </head>
124
+ <body>
125
+ <div class="header">
126
+ <h1>Bug 分析报告</h1>
127
+ <div class="meta-grid">
128
+ <div class="meta-item"><div class="meta-label">总 Bug 数</div><div class="meta-value">${meta.totalCount}</div></div>
129
+ <div class="meta-item"><div class="meta-label">迭代</div><div class="meta-value">${iterationsText}</div></div>
130
+ <div class="meta-item"><div class="meta-label">终端类型</div><div class="meta-value">${terminalText}</div></div>
131
+ <div class="meta-item"><div class="meta-label">生成时间</div><div class="meta-value">${new Date(meta.generatedAt).toLocaleString('zh-CN')}</div></div>
132
+ </div>
133
+ </div>
134
+ <div class="charts-grid">
135
+ ${chartContainers}
136
+ </div>
137
+ <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
138
+ <script>
139
+ ${chartScripts}
140
+ </script>
141
+ </body>
142
+ </html>`;
143
+ }
144
+ /**
145
+ * 自动在系统默认浏览器中打开文件
146
+ */
147
+ function openInBrowser(filePath) {
148
+ const platform = process.platform;
149
+ try {
150
+ if (platform === 'darwin') {
151
+ (0, child_process_1.execSync)(`open "${filePath}"`);
152
+ }
153
+ else if (platform === 'linux') {
154
+ (0, child_process_1.execSync)(`xdg-open "${filePath}"`);
155
+ }
156
+ else if (platform === 'win32') {
157
+ (0, child_process_1.execSync)(`start "" "${filePath}"`);
158
+ }
159
+ }
160
+ catch {
161
+ // 打开失败不中断流程,路径已打印
162
+ }
163
+ }
@@ -3,3 +3,4 @@ export { workHourCommand } from './work-hour.command';
3
3
  export { configCommand } from './config.command';
4
4
  export { bugCommand } from './bug.command';
5
5
  export { fixCommand } from './fix.command';
6
+ export { rebugCommand } from './rebug.command';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fixCommand = exports.bugCommand = exports.configCommand = exports.workHourCommand = exports.dailyCommand = void 0;
3
+ exports.rebugCommand = exports.fixCommand = exports.bugCommand = exports.configCommand = exports.workHourCommand = exports.dailyCommand = void 0;
4
4
  var daily_command_1 = require("./daily.command");
5
5
  Object.defineProperty(exports, "dailyCommand", { enumerable: true, get: function () { return daily_command_1.dailyCommand; } });
6
6
  var work_hour_command_1 = require("./work-hour.command");
@@ -11,3 +11,5 @@ var bug_command_1 = require("./bug.command");
11
11
  Object.defineProperty(exports, "bugCommand", { enumerable: true, get: function () { return bug_command_1.bugCommand; } });
12
12
  var fix_command_1 = require("./fix.command");
13
13
  Object.defineProperty(exports, "fixCommand", { enumerable: true, get: function () { return fix_command_1.fixCommand; } });
14
+ var rebug_command_1 = require("./rebug.command");
15
+ Object.defineProperty(exports, "rebugCommand", { enumerable: true, get: function () { return rebug_command_1.rebugCommand; } });
@@ -0,0 +1,5 @@
1
+ import { CliOptions } from '../utils/config-loader';
2
+ /**
3
+ * rebug 命令:交互式查询 Bug 列表并生成 ECharts 可视化报告
4
+ */
5
+ export declare function rebugCommand(cliOptions?: CliOptions): Promise<void>;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.rebugCommand = rebugCommand;
7
+ const prompts_1 = require("@inquirer/prompts");
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const charts_1 = require("../charts");
10
+ const renderer_1 = require("../charts/renderer");
11
+ const business_service_1 = require("../services/business.service");
12
+ const types_1 = require("../types");
13
+ const config_loader_1 = require("../utils/config-loader");
14
+ const inquirer_theme_1 = require("../utils/inquirer-theme");
15
+ const logger_1 = require("../utils/logger");
16
+ /**
17
+ * rebug 命令:交互式查询 Bug 列表并生成 ECharts 可视化报告
18
+ */
19
+ async function rebugCommand(cliOptions = {}) {
20
+ try {
21
+ const { projectId, config } = (0, config_loader_1.loadConfig)(cliOptions);
22
+ const businessService = new business_service_1.BusinessService(config);
23
+ // Step 1: 加载迭代列表(前12个)
24
+ const loadSpinner = (0, ora_1.default)('正在加载迭代列表...').start();
25
+ let iterations;
26
+ try {
27
+ iterations = await businessService.getIterations(projectId, { limit: 12 });
28
+ loadSpinner.succeed('迭代列表加载完成');
29
+ }
30
+ catch (error) {
31
+ loadSpinner.fail('加载迭代列表失败');
32
+ throw error;
33
+ }
34
+ if (iterations.length === 0) {
35
+ throw new Error('未获取到任何迭代信息');
36
+ }
37
+ // Step 2: 用户选择迭代
38
+ const selectedIterations = await (0, prompts_1.checkbox)({
39
+ message: '请选择要查询的迭代:',
40
+ choices: iterations.map((it) => ({
41
+ name: `${it.name} (${it.begin_time} ~ ${it.end_time})`,
42
+ value: it,
43
+ checked: false,
44
+ })),
45
+ validate: (answer) => (answer.length === 0 ? '至少需要选择一个迭代' : true),
46
+ theme: inquirer_theme_1.globalTheme,
47
+ });
48
+ // Step 3: 加载终端类型选项
49
+ let terminalTypeOptions = [];
50
+ try {
51
+ const customFieldOptions = await businessService.getCustomFieldOptions(projectId, [
52
+ types_1.CustomFieldId.TERMINAL_TYPE,
53
+ ]);
54
+ terminalTypeOptions = customFieldOptions[types_1.CustomFieldId.TERMINAL_TYPE] || [];
55
+ }
56
+ catch {
57
+ logger_1.logger.warn('获取终端类型选项失败,将跳过终端类型筛选');
58
+ }
59
+ // Step 4: 用户选择终端类型(如果有选项)
60
+ let selectedTerminalTypes = [];
61
+ if (terminalTypeOptions.length > 0) {
62
+ selectedTerminalTypes = await (0, prompts_1.checkbox)({
63
+ message: '请选择终端类型(不选则查询全部):',
64
+ choices: terminalTypeOptions.map((t) => ({ name: t, value: t, checked: false })),
65
+ theme: inquirer_theme_1.globalTheme,
66
+ });
67
+ }
68
+ // Step 5: 查询 Bug 列表
69
+ const querySpinner = (0, ora_1.default)('正在查询 Bug 列表...').start();
70
+ const iterationIds = selectedIterations.map((it) => it.id);
71
+ let allBugs;
72
+ try {
73
+ allBugs = await businessService.getBugsByIterationsAndTerminals(projectId, iterationIds, selectedTerminalTypes);
74
+ querySpinner.succeed(`查询完成:共找到 ${allBugs.length} 个 Bug`);
75
+ }
76
+ catch (error) {
77
+ querySpinner.fail('Bug 查询失败');
78
+ throw error;
79
+ }
80
+ // Step 6: 控制台摘要
81
+ logger_1.logger.info(`迭代:${selectedIterations.map((it) => it.name).join(', ')}`);
82
+ if (selectedTerminalTypes.length > 0) {
83
+ logger_1.logger.info(`终端类型:${selectedTerminalTypes.join(', ')}`);
84
+ }
85
+ logger_1.logger.info('正在生成分析报告...');
86
+ // Step 7: 生成报告
87
+ const meta = {
88
+ iterationNames: selectedIterations.map((it) => it.name),
89
+ terminalTypes: selectedTerminalTypes,
90
+ totalCount: allBugs.length,
91
+ generatedAt: new Date().toISOString(),
92
+ };
93
+ let reportPath;
94
+ try {
95
+ const outputDir = cliOptions.outputDir || undefined;
96
+ reportPath = (0, renderer_1.renderReport)(allBugs, charts_1.allCharts, meta, outputDir);
97
+ }
98
+ catch (error) {
99
+ logger_1.logger.error(`生成报告文件失败: ${String(error)}`);
100
+ return;
101
+ }
102
+ logger_1.logger.info(`报告已生成:${reportPath}`);
103
+ (0, renderer_1.openInBrowser)(reportPath);
104
+ }
105
+ catch (error) {
106
+ if (error instanceof Error && error.name === 'ExitPromptError') {
107
+ logger_1.logger.info('操作取消');
108
+ }
109
+ else {
110
+ logger_1.logger.error(`执行 rebug 命令失败: ${String(error)}`);
111
+ }
112
+ }
113
+ }
@@ -112,6 +112,14 @@ export declare class BusinessService {
112
112
  getCurrentUserBugs(projectId: string, options?: {
113
113
  hasIteration?: boolean;
114
114
  }): Promise<IssueItem[]>;
115
+ /**
116
+ * 根据迭代 ID 和终端类型查询有效 Bug(分页获取全量)
117
+ * @param projectId 项目ID
118
+ * @param iterationIds 迭代 ID 列表
119
+ * @param terminalTypes 终端类型列表(对应 custom_field24),为空则不过滤
120
+ * @returns Bug 类型工作项列表
121
+ */
122
+ getBugsByIterationsAndTerminals(projectId: string, iterationIds: number[], terminalTypes: string[]): Promise<IssueItem[]>;
115
123
  /**
116
124
  * 批量获取自定义字段的选项
117
125
  * @param projectId 项目ID
@@ -522,6 +522,44 @@ class BusinessService {
522
522
  });
523
523
  return filteredBugs;
524
524
  }
525
+ /**
526
+ * 根据迭代 ID 和终端类型查询有效 Bug(分页获取全量)
527
+ * @param projectId 项目ID
528
+ * @param iterationIds 迭代 ID 列表
529
+ * @param terminalTypes 终端类型列表(对应 custom_field24),为空则不过滤
530
+ * @returns Bug 类型工作项列表
531
+ */
532
+ async getBugsByIterationsAndTerminals(projectId, iterationIds, terminalTypes) {
533
+ if (iterationIds.length === 0) {
534
+ return [];
535
+ }
536
+ const customFieldsFilter = terminalTypes.length > 0
537
+ ? [{ custom_field: types_1.CustomFieldId.TERMINAL_TYPE, value: terminalTypes.join(',') }]
538
+ : [];
539
+ const allBugs = [];
540
+ const pageSize = 100;
541
+ let offset = 0;
542
+ let hasMore = true;
543
+ while (hasMore) {
544
+ const issuesResponse = await this.apiService.getIssues(projectId, {
545
+ tracker_ids: [types_1.IssueTrackerId.BUG],
546
+ iteration_ids: iterationIds,
547
+ custom_fields: customFieldsFilter.length > 0 ? customFieldsFilter : undefined,
548
+ include_deleted: false,
549
+ limit: pageSize,
550
+ offset,
551
+ });
552
+ if (!issuesResponse.success) {
553
+ throw new Error(`查询 Bug 失败: ${issuesResponse.error || '未知错误'}`);
554
+ }
555
+ const bugs = issuesResponse.data?.issues || [];
556
+ allBugs.push(...bugs);
557
+ const total = issuesResponse.data?.total || 0;
558
+ offset += pageSize;
559
+ hasMore = offset < total;
560
+ }
561
+ return allBugs.filter((bug) => bug.status?.id !== types_1.IssueStatusId.REJECTED);
562
+ }
525
563
  /**
526
564
  * 批量获取自定义字段的选项
527
565
  * @param projectId 项目ID
@@ -271,13 +271,20 @@ export declare enum IssueStatusId {
271
271
  RESOLVED = 3,// 已解决
272
272
  TESTING = 4,// 测试中
273
273
  CLOSED = 5,// 已关闭
274
+ REJECTED = 6,// 已拒绝
274
275
  PRODUCT_DESIGN = 7,// 产品设计
275
276
  REVIEW_READY = 8,// 可评审
276
277
  DEV_POOL = 9,// 开发池
277
278
  DEVELOPING = 10,// 开发中
278
279
  TEST_READY = 11,// 可提测
280
+ HANDOFF_EXPERIENCE = 12,// 转体验
281
+ ACCEPTED = 13,// 接受处理
282
+ VERIFIED = 14,// 已验证
279
283
  REOPENED = 15,// 重新打开
280
- NEW_ISSUE = 17
284
+ POSTPONED = 16,// 延期
285
+ NEW_ISSUE = 17,// 新问题
286
+ CONVERTED_TO_REQUIREMENT = 18,// 转需求
287
+ PENDING_TEST = 19
281
288
  }
282
289
  export interface IssueTracker {
283
290
  id: number;
@@ -9,13 +9,20 @@ var IssueStatusId;
9
9
  IssueStatusId[IssueStatusId["RESOLVED"] = 3] = "RESOLVED";
10
10
  IssueStatusId[IssueStatusId["TESTING"] = 4] = "TESTING";
11
11
  IssueStatusId[IssueStatusId["CLOSED"] = 5] = "CLOSED";
12
+ IssueStatusId[IssueStatusId["REJECTED"] = 6] = "REJECTED";
12
13
  IssueStatusId[IssueStatusId["PRODUCT_DESIGN"] = 7] = "PRODUCT_DESIGN";
13
14
  IssueStatusId[IssueStatusId["REVIEW_READY"] = 8] = "REVIEW_READY";
14
15
  IssueStatusId[IssueStatusId["DEV_POOL"] = 9] = "DEV_POOL";
15
16
  IssueStatusId[IssueStatusId["DEVELOPING"] = 10] = "DEVELOPING";
16
17
  IssueStatusId[IssueStatusId["TEST_READY"] = 11] = "TEST_READY";
18
+ IssueStatusId[IssueStatusId["HANDOFF_EXPERIENCE"] = 12] = "HANDOFF_EXPERIENCE";
19
+ IssueStatusId[IssueStatusId["ACCEPTED"] = 13] = "ACCEPTED";
20
+ IssueStatusId[IssueStatusId["VERIFIED"] = 14] = "VERIFIED";
17
21
  IssueStatusId[IssueStatusId["REOPENED"] = 15] = "REOPENED";
22
+ IssueStatusId[IssueStatusId["POSTPONED"] = 16] = "POSTPONED";
18
23
  IssueStatusId[IssueStatusId["NEW_ISSUE"] = 17] = "NEW_ISSUE";
24
+ IssueStatusId[IssueStatusId["CONVERTED_TO_REQUIREMENT"] = 18] = "CONVERTED_TO_REQUIREMENT";
25
+ IssueStatusId[IssueStatusId["PENDING_TEST"] = 19] = "PENDING_TEST";
19
26
  })(IssueStatusId || (exports.IssueStatusId = IssueStatusId = {}));
20
27
  // 工作项类型ID枚举
21
28
  var IssueTrackerId;
@@ -24,6 +24,7 @@ export interface CliOptions {
24
24
  role?: string;
25
25
  output?: string;
26
26
  report?: boolean;
27
+ outputDir?: string;
27
28
  }
28
29
  export interface LoadedConfig {
29
30
  projectId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hecom/codearts",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "华为云 CodeArts 统计分析工具",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,7 +17,10 @@
17
17
  "test": "jest --passWithNoTests",
18
18
  "test:coverage": "jest --coverage --passWithNoTests",
19
19
  "prepublishOnly": "npm run build",
20
- "dev": "ts-node src/bin/cli.ts"
20
+ "dev": "ts-node src/bin/cli.ts",
21
+ "minor": "npm version minor -m \"Bump version to %s\"; git push --tags",
22
+ "patch": "npm version patch -m \"Bump version to %s\"; git push --tags",
23
+ "major": "npm version major -m \"Bump version to %s\"; git push --tags"
21
24
  },
22
25
  "keywords": [
23
26
  "huawei-cloud",