@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
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createClient } from '../client';
|
|
3
|
-
import { formatOutput, printSuccess, printError, printInfo } from '../output';
|
|
4
|
-
|
|
5
|
-
export function registerNotificationsCommand(program: Command): void {
|
|
6
|
-
const notifications = program
|
|
7
|
-
.command('notifications')
|
|
8
|
-
.description('알림 설정 관리');
|
|
9
|
-
|
|
10
|
-
// ch notifications get
|
|
11
|
-
notifications
|
|
12
|
-
.command('get')
|
|
13
|
-
.description('현재 알림 설정 조회')
|
|
14
|
-
.action(async () => {
|
|
15
|
-
const globalOpts = program.opts();
|
|
16
|
-
try {
|
|
17
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
18
|
-
const { data } = await client.get('/notifications');
|
|
19
|
-
formatOutput(data, globalOpts);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
// Error already handled
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// ch notifications set-webhook
|
|
26
|
-
notifications
|
|
27
|
-
.command('set-webhook')
|
|
28
|
-
.description('Webhook URL 설정')
|
|
29
|
-
.option('--kakao <url>', '카카오워크 Webhook URL')
|
|
30
|
-
.option('--slack <url>', 'Slack Webhook URL')
|
|
31
|
-
.action(async (options) => {
|
|
32
|
-
const globalOpts = program.opts();
|
|
33
|
-
try {
|
|
34
|
-
if (!options.kakao && !options.slack) {
|
|
35
|
-
printError('--kakao 또는 --slack 옵션을 지정하세요.');
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (globalOpts.dryRun) {
|
|
40
|
-
printInfo('[DRY-RUN] Webhook URL 설정 요청');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const body: any = {};
|
|
45
|
-
if (options.kakao) body.kakaoworkWebhookUrl = options.kakao;
|
|
46
|
-
if (options.slack) body.slackWebhookUrl = options.slack;
|
|
47
|
-
|
|
48
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
49
|
-
const { data } = await client.patch('/notifications/webhook', body);
|
|
50
|
-
formatOutput(data, globalOpts);
|
|
51
|
-
printSuccess('Webhook URL이 설정되었습니다.');
|
|
52
|
-
} catch (error) {
|
|
53
|
-
// Error already handled
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// ch notifications remove-webhook
|
|
58
|
-
notifications
|
|
59
|
-
.command('remove-webhook')
|
|
60
|
-
.description('Webhook URL 제거')
|
|
61
|
-
.option('--kakao', '카카오워크 Webhook 제거')
|
|
62
|
-
.option('--slack', 'Slack Webhook 제거')
|
|
63
|
-
.action(async (options) => {
|
|
64
|
-
const globalOpts = program.opts();
|
|
65
|
-
try {
|
|
66
|
-
if (!options.kakao && !options.slack) {
|
|
67
|
-
printError('--kakao 또는 --slack 옵션을 지정하세요.');
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (globalOpts.dryRun) {
|
|
72
|
-
printInfo('[DRY-RUN] Webhook URL 제거 요청');
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
77
|
-
|
|
78
|
-
if (options.kakao) {
|
|
79
|
-
await client.delete('/notifications/webhook/kakao');
|
|
80
|
-
printSuccess('카카오워크 Webhook URL이 제거되었습니다.');
|
|
81
|
-
}
|
|
82
|
-
if (options.slack) {
|
|
83
|
-
await client.delete('/notifications/webhook/slack');
|
|
84
|
-
printSuccess('Slack Webhook URL이 제거되었습니다.');
|
|
85
|
-
}
|
|
86
|
-
} catch (error) {
|
|
87
|
-
// Error already handled
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// ch notifications test
|
|
92
|
-
notifications
|
|
93
|
-
.command('test')
|
|
94
|
-
.description('테스트 알림 발송')
|
|
95
|
-
.option('--kakao', '카카오워크로 테스트')
|
|
96
|
-
.option('--slack', 'Slack으로 테스트')
|
|
97
|
-
.action(async (options) => {
|
|
98
|
-
const globalOpts = program.opts();
|
|
99
|
-
try {
|
|
100
|
-
if (!options.kakao && !options.slack) {
|
|
101
|
-
printError('--kakao 또는 --slack 옵션을 지정하세요.');
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
106
|
-
|
|
107
|
-
if (options.kakao) {
|
|
108
|
-
const { data } = await client.post('/notifications/test/kakao');
|
|
109
|
-
formatOutput(data, globalOpts);
|
|
110
|
-
printSuccess('카카오워크 테스트 알림이 발송되었습니다.');
|
|
111
|
-
}
|
|
112
|
-
if (options.slack) {
|
|
113
|
-
const { data } = await client.post('/notifications/test/slack');
|
|
114
|
-
formatOutput(data, globalOpts);
|
|
115
|
-
printSuccess('Slack 테스트 알림이 발송되었습니다.');
|
|
116
|
-
}
|
|
117
|
-
} catch (error) {
|
|
118
|
-
// Error already handled
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// ch notifications toggle
|
|
123
|
-
notifications
|
|
124
|
-
.command('toggle <eventName>')
|
|
125
|
-
.description('이벤트 알림 ON/OFF 토글')
|
|
126
|
-
.option('--on', '알림 활성화')
|
|
127
|
-
.option('--off', '알림 비활성화')
|
|
128
|
-
.action(async (eventName: string, options) => {
|
|
129
|
-
const globalOpts = program.opts();
|
|
130
|
-
try {
|
|
131
|
-
if (!options.on && !options.off) {
|
|
132
|
-
printError('--on 또는 --off 옵션을 지정하세요.');
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (globalOpts.dryRun) {
|
|
137
|
-
const state = options.on ? 'ON' : 'OFF';
|
|
138
|
-
printInfo(`[DRY-RUN] 이벤트 "${eventName}" ${state} 요청`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const enabled = !!options.on;
|
|
143
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
144
|
-
const { data } = await client.patch(`/notifications/events/${eventName}`, { enabled });
|
|
145
|
-
formatOutput(data, globalOpts);
|
|
146
|
-
printSuccess(`이벤트 "${eventName}"이 ${enabled ? '활성화' : '비활성화'}되었습니다.`);
|
|
147
|
-
} catch (error) {
|
|
148
|
-
// Error already handled
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// ch notifications logs
|
|
153
|
-
notifications
|
|
154
|
-
.command('logs')
|
|
155
|
-
.description('알림 발송 로그 조회')
|
|
156
|
-
.option('--page <number>', '페이지 번호', '1')
|
|
157
|
-
.action(async (options) => {
|
|
158
|
-
const globalOpts = program.opts();
|
|
159
|
-
try {
|
|
160
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
161
|
-
const { data } = await client.get('/notifications/logs', {
|
|
162
|
-
params: { page: parseInt(options.page, 10) },
|
|
163
|
-
});
|
|
164
|
-
formatOutput(data, globalOpts);
|
|
165
|
-
} catch (error) {
|
|
166
|
-
// Error already handled
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
package/src/commands/prd.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createClient } from '../client';
|
|
3
|
-
import { formatOutput, printSuccess, printError, printInfo } from '../output';
|
|
4
|
-
|
|
5
|
-
export function registerPrdCommand(program: Command): void {
|
|
6
|
-
const prd = program
|
|
7
|
-
.command('prd')
|
|
8
|
-
.description('PRD(Product Requirements Document) 관리');
|
|
9
|
-
|
|
10
|
-
// ch prd get
|
|
11
|
-
prd
|
|
12
|
-
.command('get')
|
|
13
|
-
.description('PRD 조회')
|
|
14
|
-
.action(async () => {
|
|
15
|
-
const globalOpts = program.opts();
|
|
16
|
-
try {
|
|
17
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
18
|
-
const { data } = await client.get('/prd');
|
|
19
|
-
if (!data) {
|
|
20
|
-
printInfo('PRD가 아직 작성되지 않았습니다.');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
formatOutput(data, globalOpts);
|
|
24
|
-
} catch (error) {
|
|
25
|
-
// Error already handled
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// ch prd set
|
|
30
|
-
prd
|
|
31
|
-
.command('set')
|
|
32
|
-
.description('PRD 생성/수정')
|
|
33
|
-
.requiredOption('--title <title>', 'PRD 제목')
|
|
34
|
-
.requiredOption('--content <content>', 'PRD 본문 (마크다운)')
|
|
35
|
-
.option('--new-version', '새 버전으로 기록')
|
|
36
|
-
.action(async (options) => {
|
|
37
|
-
const globalOpts = program.opts();
|
|
38
|
-
try {
|
|
39
|
-
if (globalOpts.dryRun) {
|
|
40
|
-
printInfo(`[DRY-RUN] PRD "${options.title}" 생성/수정 요청`);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
44
|
-
const params: any = {};
|
|
45
|
-
if (options.newVersion) params.newVersion = 'true';
|
|
46
|
-
|
|
47
|
-
const { data } = await client.put('/prd', {
|
|
48
|
-
title: options.title,
|
|
49
|
-
content: options.content,
|
|
50
|
-
}, { params });
|
|
51
|
-
formatOutput(data, globalOpts);
|
|
52
|
-
printSuccess(data.message || 'PRD가 저장되었습니다.');
|
|
53
|
-
} catch (error) {
|
|
54
|
-
// Error already handled
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// ch prd versions
|
|
59
|
-
prd
|
|
60
|
-
.command('versions')
|
|
61
|
-
.description('PRD 버전 이력 조회')
|
|
62
|
-
.action(async () => {
|
|
63
|
-
const globalOpts = program.opts();
|
|
64
|
-
try {
|
|
65
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
66
|
-
const { data } = await client.get('/prd/versions');
|
|
67
|
-
formatOutput(data, globalOpts);
|
|
68
|
-
} catch (error) {
|
|
69
|
-
// Error already handled
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}
|
package/src/commands/projects.ts
DELETED
|
@@ -1,207 +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 registerProjectsCommand(program: Command): void {
|
|
7
|
-
const projects = program
|
|
8
|
-
.command('projects')
|
|
9
|
-
.description('프로젝트 관리');
|
|
10
|
-
|
|
11
|
-
// ch projects list
|
|
12
|
-
projects
|
|
13
|
-
.command('list')
|
|
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('/projects');
|
|
20
|
-
formatOutput(data, globalOpts);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
// Error already handled
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// ch projects create
|
|
27
|
-
projects
|
|
28
|
-
.command('create')
|
|
29
|
-
.description('프로젝트 생성')
|
|
30
|
-
.requiredOption('--name <name>', '프로젝트명')
|
|
31
|
-
.option('--desc <description>', '프로젝트 설명')
|
|
32
|
-
.action(async (options) => {
|
|
33
|
-
const globalOpts = program.opts();
|
|
34
|
-
try {
|
|
35
|
-
if (globalOpts.dryRun) {
|
|
36
|
-
printInfo(`[DRY-RUN] 프로젝트 "${options.name}" 생성 요청`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
40
|
-
const body: any = { name: options.name };
|
|
41
|
-
if (options.desc) body.description = options.desc;
|
|
42
|
-
const { data } = await client.post('/projects', body);
|
|
43
|
-
formatOutput(data, globalOpts);
|
|
44
|
-
printSuccess('프로젝트가 생성되었습니다.');
|
|
45
|
-
} catch (error) {
|
|
46
|
-
// Error already handled
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// ch projects info
|
|
51
|
-
projects
|
|
52
|
-
.command('info')
|
|
53
|
-
.description('현재 프로젝트 상세 정보')
|
|
54
|
-
.action(async () => {
|
|
55
|
-
const globalOpts = program.opts();
|
|
56
|
-
try {
|
|
57
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
58
|
-
const { data } = await client.get('/projects/current');
|
|
59
|
-
formatOutput(data, globalOpts);
|
|
60
|
-
} catch (error) {
|
|
61
|
-
// Error already handled
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// ch projects update
|
|
66
|
-
projects
|
|
67
|
-
.command('update')
|
|
68
|
-
.description('프로젝트 정보 수정')
|
|
69
|
-
.option('--name <name>', '프로젝트명')
|
|
70
|
-
.option('--desc <description>', '프로젝트 설명')
|
|
71
|
-
.action(async (options) => {
|
|
72
|
-
const globalOpts = program.opts();
|
|
73
|
-
try {
|
|
74
|
-
if (globalOpts.dryRun) {
|
|
75
|
-
printInfo('[DRY-RUN] 프로젝트 수정 요청');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const body: any = {};
|
|
79
|
-
if (options.name) body.name = options.name;
|
|
80
|
-
if (options.desc) body.description = options.desc;
|
|
81
|
-
if (Object.keys(body).length === 0) {
|
|
82
|
-
printError('수정할 필드를 지정하세요 (--name 또는 --desc).');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
86
|
-
const { data } = await client.patch('/projects/current', body);
|
|
87
|
-
formatOutput(data, globalOpts);
|
|
88
|
-
printSuccess('프로젝트가 수정되었습니다.');
|
|
89
|
-
} catch (error) {
|
|
90
|
-
// Error already handled
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// ch projects archive
|
|
95
|
-
projects
|
|
96
|
-
.command('archive')
|
|
97
|
-
.description('프로젝트 아카이브')
|
|
98
|
-
.action(async () => {
|
|
99
|
-
const globalOpts = program.opts();
|
|
100
|
-
try {
|
|
101
|
-
if (globalOpts.dryRun) {
|
|
102
|
-
printInfo('[DRY-RUN] 프로젝트 아카이브 요청');
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
106
|
-
await client.post('/projects/current/archive');
|
|
107
|
-
printSuccess('프로젝트가 아카이브되었습니다.');
|
|
108
|
-
} catch (error) {
|
|
109
|
-
// Error already handled
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// ch projects unarchive
|
|
114
|
-
projects
|
|
115
|
-
.command('unarchive')
|
|
116
|
-
.description('프로젝트 아카이브 해제')
|
|
117
|
-
.action(async () => {
|
|
118
|
-
const globalOpts = program.opts();
|
|
119
|
-
try {
|
|
120
|
-
if (globalOpts.dryRun) {
|
|
121
|
-
printInfo('[DRY-RUN] 프로젝트 아카이브 해제 요청');
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
125
|
-
await client.post('/projects/current/unarchive');
|
|
126
|
-
printSuccess('프로젝트 아카이브가 해제되었습니다.');
|
|
127
|
-
} catch (error) {
|
|
128
|
-
// Error already handled
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// ch projects set-schema
|
|
133
|
-
projects
|
|
134
|
-
.command('set-schema')
|
|
135
|
-
.description('프로젝트 기능명세 스키마 설정 (허용값 관리)')
|
|
136
|
-
.option('--devices <values>', '허용 디바이스 목록 (쉼표 구분, 예: web,app,kiosk)')
|
|
137
|
-
.option('--domains <values>', '허용 도메인 목록 (쉼표 구분, 예: user,admin)')
|
|
138
|
-
.option('--types <values>', '허용 기능유형 목록 (쉼표 구분, 예: 인증,커뮤니티,결제)')
|
|
139
|
-
.option('--permissions <values>', '허용 권한 목록 (쉼표 구분, 예: 비회원,회원,관리자)')
|
|
140
|
-
.action(async (options) => {
|
|
141
|
-
const globalOpts = program.opts();
|
|
142
|
-
try {
|
|
143
|
-
if (globalOpts.dryRun) {
|
|
144
|
-
printInfo('[DRY-RUN] 프로젝트 스키마 설정 요청');
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const specSchema: any = {};
|
|
148
|
-
if (options.devices) specSchema.devices = parseCommaSeparated(options.devices);
|
|
149
|
-
if (options.domains) specSchema.domains = parseCommaSeparated(options.domains);
|
|
150
|
-
if (options.types) specSchema.featureTypes = parseCommaSeparated(options.types);
|
|
151
|
-
if (options.permissions) specSchema.permissions = parseCommaSeparated(options.permissions);
|
|
152
|
-
|
|
153
|
-
if (Object.keys(specSchema).length === 0) {
|
|
154
|
-
printError('설정할 스키마 필드를 지정하세요 (--devices, --domains, --types, --permissions).');
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
159
|
-
const { data } = await client.patch('/projects/current', { specSchema });
|
|
160
|
-
formatOutput(data, globalOpts);
|
|
161
|
-
printSuccess('프로젝트 스키마가 설정되었습니다.');
|
|
162
|
-
} catch (error) {
|
|
163
|
-
// Error already handled
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// ch projects get-schema
|
|
168
|
-
projects
|
|
169
|
-
.command('get-schema')
|
|
170
|
-
.description('프로젝트 기능명세 스키마 조회')
|
|
171
|
-
.action(async () => {
|
|
172
|
-
const globalOpts = program.opts();
|
|
173
|
-
try {
|
|
174
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
175
|
-
const { data } = await client.get('/projects/current');
|
|
176
|
-
if (data.specSchema) {
|
|
177
|
-
formatOutput(data.specSchema, globalOpts);
|
|
178
|
-
} else {
|
|
179
|
-
printInfo('스키마가 설정되지 않았습니다. "ch projects set-schema"로 설정하세요.');
|
|
180
|
-
}
|
|
181
|
-
} catch (error) {
|
|
182
|
-
// Error already handled
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// ch projects delete
|
|
187
|
-
projects
|
|
188
|
-
.command('delete')
|
|
189
|
-
.description('프로젝트 영구 삭제')
|
|
190
|
-
.requiredOption('--confirm <projectName>', '삭제 확인을 위한 프로젝트명 입력')
|
|
191
|
-
.action(async (options) => {
|
|
192
|
-
const globalOpts = program.opts();
|
|
193
|
-
try {
|
|
194
|
-
if (globalOpts.dryRun) {
|
|
195
|
-
printInfo(`[DRY-RUN] 프로젝트 삭제 요청 (확인: "${options.confirm}")`);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
199
|
-
await client.delete('/projects/current', {
|
|
200
|
-
data: { confirmName: options.confirm },
|
|
201
|
-
});
|
|
202
|
-
printSuccess('프로젝트가 영구 삭제되었습니다.');
|
|
203
|
-
} catch (error) {
|
|
204
|
-
// Error already handled
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
}
|
package/src/commands/qna.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createClient } from '../client';
|
|
3
|
-
import { formatOutput, printSuccess, printError, printInfo } from '../output';
|
|
4
|
-
|
|
5
|
-
export function registerQnaCommand(program: Command): void {
|
|
6
|
-
const qna = program
|
|
7
|
-
.command('qna')
|
|
8
|
-
.description('QnA 관리');
|
|
9
|
-
|
|
10
|
-
// ch qna list
|
|
11
|
-
qna
|
|
12
|
-
.command('list')
|
|
13
|
-
.description('QnA 목록 조회')
|
|
14
|
-
.option('--status <status>', '상태 필터 (awaitingReply|replied|reflected)')
|
|
15
|
-
.option('--category <category>', '카테고리 필터 (modification|question|etc)')
|
|
16
|
-
.option('--search <keyword>', '제목/내용 검색')
|
|
17
|
-
.option('--sort <field>', '정렬 필드')
|
|
18
|
-
.option('--desc', '내림차순 정렬')
|
|
19
|
-
.option('--page <number>', '페이지 번호', '1')
|
|
20
|
-
.action(async (options) => {
|
|
21
|
-
const globalOpts = program.opts();
|
|
22
|
-
try {
|
|
23
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
24
|
-
const params: any = {};
|
|
25
|
-
if (options.status) params.status = options.status;
|
|
26
|
-
if (options.category) params.category = options.category;
|
|
27
|
-
if (options.search) params.search = options.search;
|
|
28
|
-
if (options.sort) params.sortBy = options.sort;
|
|
29
|
-
if (options.desc) params.sortOrder = 'desc';
|
|
30
|
-
params.page = parseInt(options.page, 10);
|
|
31
|
-
|
|
32
|
-
const { data } = await client.get('/qna', { params });
|
|
33
|
-
formatOutput(data, globalOpts);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
// Error already handled
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// ch qna get
|
|
40
|
-
qna
|
|
41
|
-
.command('get <qnaId>')
|
|
42
|
-
.description('QnA 상세 조회')
|
|
43
|
-
.action(async (qnaId: string) => {
|
|
44
|
-
const globalOpts = program.opts();
|
|
45
|
-
try {
|
|
46
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
47
|
-
const { data } = await client.get(`/qna/${qnaId}`);
|
|
48
|
-
formatOutput(data, globalOpts);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
// Error already handled
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// ch qna create
|
|
55
|
-
qna
|
|
56
|
-
.command('create')
|
|
57
|
-
.description('QnA 질문 등록')
|
|
58
|
-
.requiredOption('--title <title>', '제목')
|
|
59
|
-
.requiredOption('--category <category>', '카테고리 (modification|question|etc)')
|
|
60
|
-
.requiredOption('--content <content>', '내용')
|
|
61
|
-
.option('--spec <specId>', '관련 기능명세 ID')
|
|
62
|
-
.action(async (options) => {
|
|
63
|
-
const globalOpts = program.opts();
|
|
64
|
-
try {
|
|
65
|
-
if (globalOpts.dryRun) {
|
|
66
|
-
printInfo(`[DRY-RUN] QnA "${options.title}" 등록 요청`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
70
|
-
const body: any = {
|
|
71
|
-
title: options.title,
|
|
72
|
-
category: options.category,
|
|
73
|
-
content: options.content,
|
|
74
|
-
};
|
|
75
|
-
if (options.spec) body.relatedSpec = options.spec;
|
|
76
|
-
|
|
77
|
-
const { data } = await client.post('/qna', body);
|
|
78
|
-
formatOutput(data, globalOpts);
|
|
79
|
-
printSuccess('QnA가 등록되었습니다.');
|
|
80
|
-
} catch (error) {
|
|
81
|
-
// Error already handled
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// ch qna answer
|
|
86
|
-
qna
|
|
87
|
-
.command('answer <qnaId>')
|
|
88
|
-
.description('QnA 답변 등록')
|
|
89
|
-
.requiredOption('--content <content>', '답변 내용')
|
|
90
|
-
.action(async (qnaId: string, options) => {
|
|
91
|
-
const globalOpts = program.opts();
|
|
92
|
-
try {
|
|
93
|
-
if (globalOpts.dryRun) {
|
|
94
|
-
printInfo(`[DRY-RUN] QnA "${qnaId}" 답변 등록 요청`);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
98
|
-
const { data } = await client.patch(`/qna/${qnaId}/answer`, {
|
|
99
|
-
content: options.content,
|
|
100
|
-
});
|
|
101
|
-
formatOutput(data, globalOpts);
|
|
102
|
-
printSuccess('답변이 등록되었습니다.');
|
|
103
|
-
} catch (error) {
|
|
104
|
-
// Error already handled
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ch qna update-answer
|
|
109
|
-
qna
|
|
110
|
-
.command('update-answer <qnaId>')
|
|
111
|
-
.description('QnA 답변 수정')
|
|
112
|
-
.requiredOption('--content <content>', '수정할 답변 내용')
|
|
113
|
-
.action(async (qnaId: string, options) => {
|
|
114
|
-
const globalOpts = program.opts();
|
|
115
|
-
try {
|
|
116
|
-
if (globalOpts.dryRun) {
|
|
117
|
-
printInfo(`[DRY-RUN] QnA "${qnaId}" 답변 수정 요청`);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
121
|
-
const { data } = await client.patch(`/qna/${qnaId}/update-answer`, {
|
|
122
|
-
content: options.content,
|
|
123
|
-
});
|
|
124
|
-
formatOutput(data, globalOpts);
|
|
125
|
-
printSuccess('답변이 수정되었습니다.');
|
|
126
|
-
} catch (error) {
|
|
127
|
-
// Error already handled
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// ch qna reflect
|
|
132
|
-
qna
|
|
133
|
-
.command('reflect <qnaId>')
|
|
134
|
-
.description('QnA 반영 완료')
|
|
135
|
-
.requiredOption('--note <note>', '반영 내역')
|
|
136
|
-
.action(async (qnaId: string, options) => {
|
|
137
|
-
const globalOpts = program.opts();
|
|
138
|
-
try {
|
|
139
|
-
if (globalOpts.dryRun) {
|
|
140
|
-
printInfo(`[DRY-RUN] QnA "${qnaId}" 반영 완료 요청`);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
144
|
-
const { data } = await client.patch(`/qna/${qnaId}/reflect`, {
|
|
145
|
-
note: options.note,
|
|
146
|
-
});
|
|
147
|
-
formatOutput(data, globalOpts);
|
|
148
|
-
printSuccess('반영이 완료되었습니다.');
|
|
149
|
-
} catch (error) {
|
|
150
|
-
// Error already handled
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// ch qna delete
|
|
155
|
-
qna
|
|
156
|
-
.command('delete <qnaId>')
|
|
157
|
-
.description('QnA 삭제')
|
|
158
|
-
.action(async (qnaId: string) => {
|
|
159
|
-
const globalOpts = program.opts();
|
|
160
|
-
try {
|
|
161
|
-
if (globalOpts.dryRun) {
|
|
162
|
-
printInfo(`[DRY-RUN] QnA "${qnaId}" 삭제 요청`);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
166
|
-
await client.delete(`/qna/${qnaId}`);
|
|
167
|
-
printSuccess('QnA가 삭제되었습니다.');
|
|
168
|
-
} catch (error) {
|
|
169
|
-
// Error already handled
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// ch qna navigate
|
|
174
|
-
qna
|
|
175
|
-
.command('navigate <qnaId>')
|
|
176
|
-
.description('이전/다음 QnA 조회')
|
|
177
|
-
.requiredOption('--direction <direction>', '방향 (prev|next)')
|
|
178
|
-
.action(async (qnaId: string, options) => {
|
|
179
|
-
const globalOpts = program.opts();
|
|
180
|
-
try {
|
|
181
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
182
|
-
const { data } = await client.get(`/qna/${qnaId}/navigate`, {
|
|
183
|
-
params: { direction: options.direction },
|
|
184
|
-
});
|
|
185
|
-
formatOutput(data, globalOpts);
|
|
186
|
-
} catch (error) {
|
|
187
|
-
// Error already handled
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// ch qna pending-count
|
|
192
|
-
qna
|
|
193
|
-
.command('pending-count')
|
|
194
|
-
.description('답변대기 QnA 건수 조회')
|
|
195
|
-
.action(async () => {
|
|
196
|
-
const globalOpts = program.opts();
|
|
197
|
-
try {
|
|
198
|
-
const client = createClient({ projectId: globalOpts.project });
|
|
199
|
-
const { data } = await client.get('/qna/pending-count');
|
|
200
|
-
formatOutput(data, globalOpts);
|
|
201
|
-
} catch (error) {
|
|
202
|
-
// Error already handled
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|