@decencia/ch-cli 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/setup-skill.d.ts +3 -0
- package/dist/commands/setup-skill.d.ts.map +1 -0
- package/dist/commands/setup-skill.js +65 -0
- package/dist/commands/setup-skill.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/skill/SKILL.md +490 -0
- package/src/client.ts +0 -137
- package/src/commands/archives.ts +0 -334
- package/src/commands/auth.ts +0 -154
- package/src/commands/dashboard.ts +0 -112
- package/src/commands/db-schema.ts +0 -74
- package/src/commands/dev-status.ts +0 -166
- package/src/commands/init.ts +0 -92
- package/src/commands/members.ts +0 -92
- package/src/commands/notifications.ts +0 -169
- package/src/commands/prd.ts +0 -72
- package/src/commands/projects.ts +0 -207
- package/src/commands/qna.ts +0 -205
- package/src/commands/specs.ts +0 -257
- package/src/commands/sprints.ts +0 -255
- package/src/commands/sqa.ts +0 -422
- package/src/config.ts +0 -98
- package/src/index.ts +0 -69
- package/src/output.ts +0 -186
- package/src/utils.ts +0 -3
- package/tsconfig.json +0 -18
package/src/commands/specs.ts
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createClient } from '../client';
|
|
3
|
-
import { formatOutput, printSuccess, printError, printInfo } from '../output';
|
|
4
|
-
import { parseCommaSeparated } from '../utils';
|
|
5
|
-
|
|
6
|
-
export function registerSpecsCommand(program: Command): void {
|
|
7
|
-
const specs = program
|
|
8
|
-
.command('specs')
|
|
9
|
-
.description('기능명세 관리');
|
|
10
|
-
|
|
11
|
-
// ch specs meta
|
|
12
|
-
specs
|
|
13
|
-
.command('meta')
|
|
14
|
-
.description('기능명세 메타데이터 조회 (프로젝트 스키마 + 사용 현황)')
|
|
15
|
-
.action(async () => {
|
|
16
|
-
const globalOpts = program.opts();
|
|
17
|
-
try {
|
|
18
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
19
|
-
const { data } = await client.get('/specs/meta');
|
|
20
|
-
formatOutput(data, globalOpts);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
// Error already handled
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// ch specs list
|
|
27
|
-
specs
|
|
28
|
-
.command('list')
|
|
29
|
-
.description('기능명세 목록 조회')
|
|
30
|
-
.option('--device <device>', '디바이스 필터 (해당 값을 포함하는 기능명세)')
|
|
31
|
-
.option('--domain <domain>', '도메인 필터')
|
|
32
|
-
.option('--type <featureType>', '기능유형 필터 (해당 값을 포함하는 기능명세)')
|
|
33
|
-
.option('--status <status>', '진행상황 필터')
|
|
34
|
-
.option('--group-by <field>', '그룹핑 (devices|domain|featureTypes|status)')
|
|
35
|
-
.option('--search <keyword>', '이름 검색')
|
|
36
|
-
.option('--sort <field>', '정렬 필드')
|
|
37
|
-
.option('--desc', '내림차순 정렬')
|
|
38
|
-
.option('--page <number>', '페이지 번호', '1')
|
|
39
|
-
.option('--per-page <number>', '페이지당 건수', '20')
|
|
40
|
-
.action(async (options) => {
|
|
41
|
-
const globalOpts = program.opts();
|
|
42
|
-
try {
|
|
43
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
44
|
-
const params: any = {};
|
|
45
|
-
if (options.device) params.device = options.device;
|
|
46
|
-
if (options.domain) params.domain = options.domain;
|
|
47
|
-
if (options.type) params.featureType = options.type;
|
|
48
|
-
if (options.status) params.status = options.status;
|
|
49
|
-
if (options.groupBy) params.groupBy = options.groupBy;
|
|
50
|
-
if (options.search) params.search = options.search;
|
|
51
|
-
if (options.sort) params.sortBy = options.sort;
|
|
52
|
-
if (options.desc) params.sortOrder = 'desc';
|
|
53
|
-
params.page = parseInt(options.page, 10);
|
|
54
|
-
params.perPage = parseInt(options.perPage, 10);
|
|
55
|
-
|
|
56
|
-
const { data } = await client.get('/specs', { params });
|
|
57
|
-
formatOutput(data, globalOpts);
|
|
58
|
-
} catch (error) {
|
|
59
|
-
// Error already handled
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// ch specs get
|
|
64
|
-
specs
|
|
65
|
-
.command('get <specId>')
|
|
66
|
-
.description('기능명세 상세 조회')
|
|
67
|
-
.action(async (specId: string) => {
|
|
68
|
-
const globalOpts = program.opts();
|
|
69
|
-
try {
|
|
70
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
71
|
-
const { data } = await client.get(`/specs/${specId}`);
|
|
72
|
-
formatOutput(data, globalOpts);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
// Error already handled
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// ch specs create
|
|
79
|
-
specs
|
|
80
|
-
.command('create')
|
|
81
|
-
.description('기능명세 생성')
|
|
82
|
-
.requiredOption('--name <name>', '기능명')
|
|
83
|
-
.requiredOption('--device <devices>', '디바이스 (쉼표 구분, 예: web,app)')
|
|
84
|
-
.requiredOption('--domain <domain>', '도메인')
|
|
85
|
-
.requiredOption('--type <featureTypes>', '기능유형 (쉼표 구분, 예: 인증,마이페이지)')
|
|
86
|
-
.option('--permission <permissions>', '권한 (쉼표 구분, 예: 비회원,회원)')
|
|
87
|
-
.option('--content <content>', '내용')
|
|
88
|
-
.option('--dto <dto>', 'DTO')
|
|
89
|
-
.option('--status <status>', '진행상황')
|
|
90
|
-
.option('--note <note>', '비고')
|
|
91
|
-
.action(async (options) => {
|
|
92
|
-
const globalOpts = program.opts();
|
|
93
|
-
try {
|
|
94
|
-
if (globalOpts.dryRun) {
|
|
95
|
-
printInfo(`[DRY-RUN] 기능명세 "${options.name}" 생성 요청`);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
99
|
-
const body: any = {
|
|
100
|
-
name: options.name,
|
|
101
|
-
devices: parseCommaSeparated(options.device),
|
|
102
|
-
domain: options.domain,
|
|
103
|
-
featureTypes: parseCommaSeparated(options.type),
|
|
104
|
-
};
|
|
105
|
-
if (options.permission) body.permissions = parseCommaSeparated(options.permission);
|
|
106
|
-
if (options.content) body.content = options.content;
|
|
107
|
-
if (options.dto) body.dto = options.dto;
|
|
108
|
-
if (options.status) body.status = options.status;
|
|
109
|
-
if (options.note) body.note = options.note;
|
|
110
|
-
|
|
111
|
-
const { data } = await client.post('/specs', body);
|
|
112
|
-
formatOutput(data, globalOpts);
|
|
113
|
-
printSuccess('기능명세가 생성되었습니다.');
|
|
114
|
-
} catch (error) {
|
|
115
|
-
// Error already handled
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// ch specs update
|
|
120
|
-
specs
|
|
121
|
-
.command('update <specId>')
|
|
122
|
-
.description('기능명세 수정')
|
|
123
|
-
.option('--name <name>', '기능명')
|
|
124
|
-
.option('--device <devices>', '디바이스 (쉼표 구분)')
|
|
125
|
-
.option('--domain <domain>', '도메인')
|
|
126
|
-
.option('--type <featureTypes>', '기능유형 (쉼표 구분)')
|
|
127
|
-
.option('--permission <permissions>', '권한 (쉼표 구분)')
|
|
128
|
-
.option('--content <content>', '내용')
|
|
129
|
-
.option('--dto <dto>', 'DTO')
|
|
130
|
-
.option('--status <status>', '진행상황')
|
|
131
|
-
.option('--note <note>', '비고')
|
|
132
|
-
.option('--new-version', '새 버전으로 수정')
|
|
133
|
-
.action(async (specId: string, options) => {
|
|
134
|
-
const globalOpts = program.opts();
|
|
135
|
-
try {
|
|
136
|
-
if (globalOpts.dryRun) {
|
|
137
|
-
printInfo(`[DRY-RUN] 기능명세 "${specId}" 수정 요청`);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
141
|
-
const body: any = {};
|
|
142
|
-
if (options.name) body.name = options.name;
|
|
143
|
-
if (options.device) body.devices = parseCommaSeparated(options.device);
|
|
144
|
-
if (options.domain) body.domain = options.domain;
|
|
145
|
-
if (options.type) body.featureTypes = parseCommaSeparated(options.type);
|
|
146
|
-
if (options.permission) body.permissions = parseCommaSeparated(options.permission);
|
|
147
|
-
if (options.content) body.content = options.content;
|
|
148
|
-
if (options.dto) body.dto = options.dto;
|
|
149
|
-
if (options.status) body.status = options.status;
|
|
150
|
-
if (options.note) body.note = options.note;
|
|
151
|
-
|
|
152
|
-
if (Object.keys(body).length === 0) {
|
|
153
|
-
printError('수정할 필드를 지정하세요.');
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const params: any = {};
|
|
158
|
-
if (options.newVersion) params.newVersion = 'true';
|
|
159
|
-
|
|
160
|
-
const { data } = await client.patch(`/specs/${specId}`, body, { params });
|
|
161
|
-
formatOutput(data, globalOpts);
|
|
162
|
-
printSuccess(options.newVersion ? '기능명세가 새 버전으로 수정되었습니다.' : '기능명세가 수정되었습니다.');
|
|
163
|
-
} catch (error) {
|
|
164
|
-
// Error already handled
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// ch specs set-status
|
|
169
|
-
specs
|
|
170
|
-
.command('set-status <specId> <status>')
|
|
171
|
-
.description('기능명세 진행상황 변경')
|
|
172
|
-
.action(async (specId: string, status: string) => {
|
|
173
|
-
const globalOpts = program.opts();
|
|
174
|
-
try {
|
|
175
|
-
if (globalOpts.dryRun) {
|
|
176
|
-
printInfo(`[DRY-RUN] 기능명세 "${specId}" 상태를 "${status}"로 변경 요청`);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
180
|
-
const { data } = await client.patch(`/specs/${specId}/status`, { status });
|
|
181
|
-
formatOutput(data, globalOpts);
|
|
182
|
-
printSuccess(`기능명세 상태가 "${status}"로 변경되었습니다.`);
|
|
183
|
-
} catch (error) {
|
|
184
|
-
// Error already handled
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// ch specs delete
|
|
189
|
-
specs
|
|
190
|
-
.command('delete <specId>')
|
|
191
|
-
.description('기능명세 삭제')
|
|
192
|
-
.action(async (specId: string) => {
|
|
193
|
-
const globalOpts = program.opts();
|
|
194
|
-
try {
|
|
195
|
-
if (globalOpts.dryRun) {
|
|
196
|
-
printInfo(`[DRY-RUN] 기능명세 "${specId}" 삭제 요청`);
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
200
|
-
await client.delete(`/specs/${specId}`);
|
|
201
|
-
printSuccess('기능명세가 삭제되었습니다.');
|
|
202
|
-
} catch (error) {
|
|
203
|
-
// Error already handled
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// ch specs versions
|
|
208
|
-
specs
|
|
209
|
-
.command('versions <specId>')
|
|
210
|
-
.description('기능명세 버전 이력 조회')
|
|
211
|
-
.action(async (specId: string) => {
|
|
212
|
-
const globalOpts = program.opts();
|
|
213
|
-
try {
|
|
214
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
215
|
-
const { data } = await client.get(`/specs/${specId}/versions`);
|
|
216
|
-
formatOutput(data, globalOpts);
|
|
217
|
-
} catch (error) {
|
|
218
|
-
// Error already handled
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// ch specs related
|
|
223
|
-
specs
|
|
224
|
-
.command('related <specId>')
|
|
225
|
-
.description('기능명세 관련 데이터 조회')
|
|
226
|
-
.requiredOption('--type <type>', '관련 데이터 유형 (sprints|qna|sqa)')
|
|
227
|
-
.action(async (specId: string, options) => {
|
|
228
|
-
const globalOpts = program.opts();
|
|
229
|
-
try {
|
|
230
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
231
|
-
const { data } = await client.get(`/specs/${specId}/related`, {
|
|
232
|
-
params: { type: options.type },
|
|
233
|
-
});
|
|
234
|
-
formatOutput(data, globalOpts);
|
|
235
|
-
} catch (error) {
|
|
236
|
-
// Error already handled
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// ch specs export
|
|
241
|
-
specs
|
|
242
|
-
.command('export')
|
|
243
|
-
.description('기능명세 전체 내보내기')
|
|
244
|
-
.option('--format <format>', '출력 형식 (json|csv)', 'json')
|
|
245
|
-
.action(async (options) => {
|
|
246
|
-
const globalOpts = program.opts();
|
|
247
|
-
try {
|
|
248
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
249
|
-
const { data } = await client.get('/specs/export', {
|
|
250
|
-
params: { format: options.format },
|
|
251
|
-
});
|
|
252
|
-
formatOutput(data, globalOpts);
|
|
253
|
-
} catch (error) {
|
|
254
|
-
// Error already handled
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
}
|
package/src/commands/sprints.ts
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createClient } from '../client';
|
|
3
|
-
import { formatOutput, printSuccess, printError, printInfo } from '../output';
|
|
4
|
-
|
|
5
|
-
export function registerSprintsCommand(program: Command): void {
|
|
6
|
-
const sprints = program
|
|
7
|
-
.command('sprints')
|
|
8
|
-
.description('스프린트 관리');
|
|
9
|
-
|
|
10
|
-
// ch sprints list
|
|
11
|
-
sprints
|
|
12
|
-
.command('list')
|
|
13
|
-
.description('스프린트 목록 조회')
|
|
14
|
-
.option('--status <status>', '상태 필터 (scheduled|inProgress|completed)')
|
|
15
|
-
.option('--sort <field>', '정렬 필드')
|
|
16
|
-
.option('--desc', '내림차순 정렬')
|
|
17
|
-
.option('--page <number>', '페이지 번호', '1')
|
|
18
|
-
.action(async (options) => {
|
|
19
|
-
const globalOpts = program.opts();
|
|
20
|
-
try {
|
|
21
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
22
|
-
const params: any = {};
|
|
23
|
-
if (options.status) params.status = options.status;
|
|
24
|
-
if (options.sort) params.sortBy = options.sort;
|
|
25
|
-
if (options.desc) params.sortOrder = 'desc';
|
|
26
|
-
params.page = parseInt(options.page, 10);
|
|
27
|
-
|
|
28
|
-
const { data } = await client.get('/sprints', { params });
|
|
29
|
-
formatOutput(data, globalOpts);
|
|
30
|
-
} catch (error) {
|
|
31
|
-
// Error already handled
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// ch sprints get
|
|
36
|
-
sprints
|
|
37
|
-
.command('get <sprintId>')
|
|
38
|
-
.description('스프린트 상세 조회')
|
|
39
|
-
.action(async (sprintId: string) => {
|
|
40
|
-
const globalOpts = program.opts();
|
|
41
|
-
try {
|
|
42
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
43
|
-
const { data } = await client.get(`/sprints/${sprintId}`);
|
|
44
|
-
formatOutput(data, globalOpts);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
// Error already handled
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// ch sprints create
|
|
51
|
-
sprints
|
|
52
|
-
.command('create')
|
|
53
|
-
.description('스프린트 생성')
|
|
54
|
-
.requiredOption('--name <name>', 'Sprint명')
|
|
55
|
-
.requiredOption('--start <date>', '시작일 (YYYY-MM-DD)')
|
|
56
|
-
.requiredOption('--end <date>', '종료일 (YYYY-MM-DD)')
|
|
57
|
-
.option('--content <content>', '내용')
|
|
58
|
-
.option('--status <status>', '상태')
|
|
59
|
-
.option('--specs <specIds>', '연결할 기능명세 ID (쉼표 구분)')
|
|
60
|
-
.action(async (options) => {
|
|
61
|
-
const globalOpts = program.opts();
|
|
62
|
-
try {
|
|
63
|
-
if (globalOpts.dryRun) {
|
|
64
|
-
printInfo(`[DRY-RUN] 스프린트 "${options.name}" 생성 요청`);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
68
|
-
const body: any = {
|
|
69
|
-
name: options.name,
|
|
70
|
-
startDate: options.start,
|
|
71
|
-
endDate: options.end,
|
|
72
|
-
};
|
|
73
|
-
if (options.content) body.content = options.content;
|
|
74
|
-
if (options.status) body.status = options.status;
|
|
75
|
-
if (options.specs) body.relatedSpecs = options.specs.split(',').map((s: string) => s.trim());
|
|
76
|
-
|
|
77
|
-
const { data } = await client.post('/sprints', body);
|
|
78
|
-
formatOutput(data, globalOpts);
|
|
79
|
-
printSuccess('스프린트가 생성되었습니다.');
|
|
80
|
-
} catch (error) {
|
|
81
|
-
// Error already handled
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// ch sprints update
|
|
86
|
-
sprints
|
|
87
|
-
.command('update <sprintId>')
|
|
88
|
-
.description('스프린트 수정')
|
|
89
|
-
.option('--name <name>', 'Sprint명')
|
|
90
|
-
.option('--content <content>', '내용')
|
|
91
|
-
.option('--status <status>', '상태')
|
|
92
|
-
.action(async (sprintId: string, options) => {
|
|
93
|
-
const globalOpts = program.opts();
|
|
94
|
-
try {
|
|
95
|
-
if (globalOpts.dryRun) {
|
|
96
|
-
printInfo(`[DRY-RUN] 스프린트 "${sprintId}" 수정 요청`);
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const body: any = {};
|
|
100
|
-
if (options.name) body.name = options.name;
|
|
101
|
-
if (options.content) body.content = options.content;
|
|
102
|
-
if (options.status) body.status = options.status;
|
|
103
|
-
|
|
104
|
-
if (Object.keys(body).length === 0) {
|
|
105
|
-
printError('수정할 필드를 지정해주세요.');
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
110
|
-
const { data } = await client.patch(`/sprints/${sprintId}`, body);
|
|
111
|
-
formatOutput(data, globalOpts);
|
|
112
|
-
printSuccess('스프린트가 수정되었습니다.');
|
|
113
|
-
} catch (error) {
|
|
114
|
-
// Error already handled
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// ch sprints set-status
|
|
119
|
-
sprints
|
|
120
|
-
.command('set-status <sprintId> <status>')
|
|
121
|
-
.description('스프린트 상태 변경')
|
|
122
|
-
.action(async (sprintId: string, status: string) => {
|
|
123
|
-
const globalOpts = program.opts();
|
|
124
|
-
try {
|
|
125
|
-
if (globalOpts.dryRun) {
|
|
126
|
-
printInfo(`[DRY-RUN] 스프린트 "${sprintId}" 상태를 "${status}"로 변경 요청`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
130
|
-
const { data } = await client.patch(`/sprints/${sprintId}/status`, { status });
|
|
131
|
-
formatOutput(data, globalOpts);
|
|
132
|
-
printSuccess(`스프린트 상태가 "${status}"로 변경되었습니다.`);
|
|
133
|
-
} catch (error) {
|
|
134
|
-
// Error already handled
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// ch sprints set-dates
|
|
139
|
-
sprints
|
|
140
|
-
.command('set-dates <sprintId>')
|
|
141
|
-
.description('스프린트 기간 변경')
|
|
142
|
-
.requiredOption('--start <date>', '시작일 (YYYY-MM-DD)')
|
|
143
|
-
.requiredOption('--end <date>', '종료일 (YYYY-MM-DD)')
|
|
144
|
-
.action(async (sprintId: string, options) => {
|
|
145
|
-
const globalOpts = program.opts();
|
|
146
|
-
try {
|
|
147
|
-
if (globalOpts.dryRun) {
|
|
148
|
-
printInfo(`[DRY-RUN] 스프린트 "${sprintId}" 기간 변경 요청`);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
152
|
-
const { data } = await client.patch(`/sprints/${sprintId}/dates`, {
|
|
153
|
-
startDate: options.start,
|
|
154
|
-
endDate: options.end,
|
|
155
|
-
});
|
|
156
|
-
formatOutput(data, globalOpts);
|
|
157
|
-
printSuccess('스프린트 기간이 변경되었습니다.');
|
|
158
|
-
} catch (error) {
|
|
159
|
-
// Error already handled
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// ch sprints add-specs
|
|
164
|
-
sprints
|
|
165
|
-
.command('add-specs <sprintId> <specIds>')
|
|
166
|
-
.description('스프린트에 기능명세 연결 (쉼표 구분)')
|
|
167
|
-
.action(async (sprintId: string, specIds: string) => {
|
|
168
|
-
const globalOpts = program.opts();
|
|
169
|
-
try {
|
|
170
|
-
if (globalOpts.dryRun) {
|
|
171
|
-
printInfo(`[DRY-RUN] 스프린트 "${sprintId}"에 기능명세 연결 요청`);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
175
|
-
const ids = specIds.split(',').map((s) => s.trim());
|
|
176
|
-
const { data } = await client.post(`/sprints/${sprintId}/specs`, { specIds: ids });
|
|
177
|
-
formatOutput(data, globalOpts);
|
|
178
|
-
printSuccess('기능명세가 스프린트에 연결되었습니다.');
|
|
179
|
-
} catch (error) {
|
|
180
|
-
// Error already handled
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// ch sprints remove-specs
|
|
185
|
-
sprints
|
|
186
|
-
.command('remove-specs <sprintId> <specIds>')
|
|
187
|
-
.description('스프린트에서 기능명세 연결 해제 (쉼표 구분)')
|
|
188
|
-
.action(async (sprintId: string, specIds: string) => {
|
|
189
|
-
const globalOpts = program.opts();
|
|
190
|
-
try {
|
|
191
|
-
if (globalOpts.dryRun) {
|
|
192
|
-
printInfo(`[DRY-RUN] 스프린트 "${sprintId}" 기능명세 해제 요청`);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
196
|
-
const ids = specIds.split(',').map((s) => s.trim());
|
|
197
|
-
await client.delete(`/sprints/${sprintId}/specs`, { data: { specIds: ids } });
|
|
198
|
-
printSuccess('기능명세 연결이 해제되었습니다.');
|
|
199
|
-
} catch (error) {
|
|
200
|
-
// Error already handled
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// ch sprints delete
|
|
205
|
-
sprints
|
|
206
|
-
.command('delete <sprintId>')
|
|
207
|
-
.description('스프린트 삭제')
|
|
208
|
-
.action(async (sprintId: string) => {
|
|
209
|
-
const globalOpts = program.opts();
|
|
210
|
-
try {
|
|
211
|
-
if (globalOpts.dryRun) {
|
|
212
|
-
printInfo(`[DRY-RUN] 스프린트 "${sprintId}" 삭제 요청`);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
216
|
-
await client.delete(`/sprints/${sprintId}`);
|
|
217
|
-
printSuccess('스프린트가 삭제되었습니다.');
|
|
218
|
-
} catch (error) {
|
|
219
|
-
// Error already handled
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ch sprints progress
|
|
224
|
-
sprints
|
|
225
|
-
.command('progress <sprintId>')
|
|
226
|
-
.description('스프린트 진행률 조회')
|
|
227
|
-
.action(async (sprintId: string) => {
|
|
228
|
-
const globalOpts = program.opts();
|
|
229
|
-
try {
|
|
230
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
231
|
-
const { data } = await client.get(`/sprints/${sprintId}/progress`);
|
|
232
|
-
formatOutput(data, globalOpts);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
// Error already handled
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// ch sprints timeline
|
|
239
|
-
sprints
|
|
240
|
-
.command('timeline')
|
|
241
|
-
.description('스프린트 타임라인 조회')
|
|
242
|
-
.option('--weeks <number>', '주 수', '8')
|
|
243
|
-
.action(async (options) => {
|
|
244
|
-
const globalOpts = program.opts();
|
|
245
|
-
try {
|
|
246
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
247
|
-
const { data } = await client.get('/sprints/timeline', {
|
|
248
|
-
params: { weeks: parseInt(options.weeks, 10) },
|
|
249
|
-
});
|
|
250
|
-
formatOutput(data, globalOpts);
|
|
251
|
-
} catch (error) {
|
|
252
|
-
// Error already handled
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
}
|