@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.
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }