@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,112 +0,0 @@
1
- import { Command } from 'commander';
2
- import { createClient } from '../client';
3
- import { formatOutput } from '../output';
4
-
5
- export function registerDashboardCommand(program: Command): void {
6
- const dashboard = program
7
- .command('dashboard')
8
- .description('대시보드')
9
- .action(async () => {
10
- const globalOpts = program.opts();
11
- try {
12
- const client = createClient({ projectId: globalOpts.project });
13
- const { data } = await client.get('/dashboard');
14
- formatOutput(data, globalOpts);
15
- } catch (error) {
16
- // Error already handled
17
- }
18
- });
19
-
20
- // ch dashboard progress
21
- dashboard
22
- .command('progress')
23
- .description('전체 진행률 조회')
24
- .action(async () => {
25
- const globalOpts = program.opts();
26
- try {
27
- const client = createClient({ projectId: globalOpts.project });
28
- const { data } = await client.get('/dashboard/progress');
29
- formatOutput(data, globalOpts);
30
- } catch (error) {
31
- // Error already handled
32
- }
33
- });
34
-
35
- // ch dashboard sprint
36
- dashboard
37
- .command('sprint')
38
- .description('현재 Sprint 요약')
39
- .action(async () => {
40
- const globalOpts = program.opts();
41
- try {
42
- const client = createClient({ projectId: globalOpts.project });
43
- const { data } = await client.get('/dashboard/sprint');
44
- formatOutput(data, globalOpts);
45
- } catch (error) {
46
- // Error already handled
47
- }
48
- });
49
-
50
- // ch dashboard qna-pending
51
- dashboard
52
- .command('qna-pending')
53
- .description('답변대기 QnA 조회')
54
- .action(async () => {
55
- const globalOpts = program.opts();
56
- try {
57
- const client = createClient({ projectId: globalOpts.project });
58
- const { data } = await client.get('/dashboard/qna-pending');
59
- formatOutput(data, globalOpts);
60
- } catch (error) {
61
- // Error already handled
62
- }
63
- });
64
-
65
- // ch dashboard sqa-latest
66
- dashboard
67
- .command('sqa-latest')
68
- .description('최근 SQA 결과 조회')
69
- .action(async () => {
70
- const globalOpts = program.opts();
71
- try {
72
- const client = createClient({ projectId: globalOpts.project });
73
- const { data } = await client.get('/dashboard/sqa-latest');
74
- formatOutput(data, globalOpts);
75
- } catch (error) {
76
- // Error already handled
77
- }
78
- });
79
-
80
- // ch dashboard recent-changes
81
- dashboard
82
- .command('recent-changes')
83
- .description('최근 변경 이력 조회')
84
- .option('--limit <number>', '조회 건수', '10')
85
- .action(async (options) => {
86
- const globalOpts = program.opts();
87
- try {
88
- const client = createClient({ projectId: globalOpts.project });
89
- const { data } = await client.get('/dashboard/recent-changes', {
90
- params: { limit: parseInt(options.limit, 10) },
91
- });
92
- formatOutput(data, globalOpts);
93
- } catch (error) {
94
- // Error already handled
95
- }
96
- });
97
-
98
- // ch dashboard members-summary
99
- dashboard
100
- .command('members-summary')
101
- .description('멤버 요약 조회')
102
- .action(async () => {
103
- const globalOpts = program.opts();
104
- try {
105
- const client = createClient({ projectId: globalOpts.project });
106
- const { data } = await client.get('/dashboard/members-summary');
107
- formatOutput(data, globalOpts);
108
- } catch (error) {
109
- // Error already handled
110
- }
111
- });
112
- }
@@ -1,74 +0,0 @@
1
- import { Command } from 'commander';
2
- import { createClient } from '../client';
3
- import { formatOutput, printSuccess, printError, printInfo } from '../output';
4
-
5
- export function registerDbSchemaCommand(program: Command): void {
6
- const dbSchema = program
7
- .command('db-schema')
8
- .description('DB 스키마 관리');
9
-
10
- // ch db-schema get
11
- dbSchema
12
- .command('get')
13
- .description('DB 스키마 조회')
14
- .action(async () => {
15
- const globalOpts = program.opts();
16
- try {
17
- const client = createClient({ projectId: globalOpts.project });
18
- const { data } = await client.get('/db-schema');
19
- if (!data) {
20
- printInfo('DB 스키마가 아직 작성되지 않았습니다.');
21
- return;
22
- }
23
- formatOutput(data, globalOpts);
24
- } catch (error) {
25
- // Error already handled
26
- }
27
- });
28
-
29
- // ch db-schema set
30
- dbSchema
31
- .command('set')
32
- .description('DB 스키마 생성/수정')
33
- .requiredOption('--title <title>', 'DB 스키마 제목')
34
- .requiredOption('--content <content>', 'DB 스키마 본문 (마크다운)')
35
- .option('--db-type <dbType>', 'DB 유형 (firestore, supabase, mysql, generic)', 'generic')
36
- .option('--new-version', '새 버전으로 기록')
37
- .action(async (options) => {
38
- const globalOpts = program.opts();
39
- try {
40
- if (globalOpts.dryRun) {
41
- printInfo(`[DRY-RUN] DB 스키마 "${options.title}" 생성/수정 요청`);
42
- return;
43
- }
44
- const client = createClient({ projectId: globalOpts.project });
45
- const params: any = {};
46
- if (options.newVersion) params.newVersion = 'true';
47
-
48
- const { data } = await client.put('/db-schema', {
49
- title: options.title,
50
- content: options.content,
51
- dbType: options.dbType,
52
- }, { params });
53
- formatOutput(data, globalOpts);
54
- printSuccess(data.message || 'DB 스키마가 저장되었습니다.');
55
- } catch (error) {
56
- // Error already handled
57
- }
58
- });
59
-
60
- // ch db-schema versions
61
- dbSchema
62
- .command('versions')
63
- .description('DB 스키마 버전 이력 조회')
64
- .action(async () => {
65
- const globalOpts = program.opts();
66
- try {
67
- const client = createClient({ projectId: globalOpts.project });
68
- const { data } = await client.get('/db-schema/versions');
69
- formatOutput(data, globalOpts);
70
- } catch (error) {
71
- // Error already handled
72
- }
73
- });
74
- }
@@ -1,166 +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 registerDevStatusCommand(program: Command): void {
7
- const devStatus = program
8
- .command('dev-status')
9
- .description('개발현황 문서 관리');
10
-
11
- // ch dev-status list
12
- devStatus
13
- .command('list')
14
- .description('개발현황 문서 목록 조회')
15
- .option('--sprint <sprintId>', '관련 스프린트 ID 필터')
16
- .option('--spec <specId>', '관련 기능명세 ID 필터')
17
- .option('--search <keyword>', '제목 검색')
18
- .option('--sort <field>', '정렬 필드')
19
- .option('--desc', '내림차순 정렬')
20
- .option('--page <number>', '페이지 번호', '1')
21
- .option('--per-page <number>', '페이지당 건수', '20')
22
- .action(async (options) => {
23
- const globalOpts = program.opts();
24
- try {
25
- const client = createClient({ projectId: globalOpts.project });
26
- const params: any = {};
27
- if (options.sprint) params.relatedSprintId = options.sprint;
28
- if (options.spec) params.relatedSpecId = options.spec;
29
- if (options.search) params.search = options.search;
30
- if (options.sort) params.sortBy = options.sort;
31
- if (options.desc) params.sortOrder = 'desc';
32
- params.page = parseInt(options.page, 10);
33
- params.perPage = parseInt(options.perPage, 10);
34
-
35
- const { data } = await client.get('/dev-status', { params });
36
- formatOutput(data, globalOpts);
37
- } catch (error) {
38
- // Error already handled
39
- }
40
- });
41
-
42
- // ch dev-status get
43
- devStatus
44
- .command('get <docId>')
45
- .description('개발현황 문서 상세 조회')
46
- .action(async (docId: string) => {
47
- const globalOpts = program.opts();
48
- try {
49
- const client = createClient({ projectId: globalOpts.project });
50
- const { data } = await client.get(`/dev-status/${docId}`);
51
- formatOutput(data, globalOpts);
52
- } catch (error) {
53
- // Error already handled
54
- }
55
- });
56
-
57
- // ch dev-status create
58
- devStatus
59
- .command('create')
60
- .description('개발현황 문서 생성')
61
- .requiredOption('--title <title>', '문서 제목')
62
- .option('--content <content>', '본문 (HTML)')
63
- .option('--sprints <sprintIds>', '관련 스프린트 ID (쉼표 구분)')
64
- .option('--specs <specIds>', '관련 기능명세 ID (쉼표 구분)')
65
- .action(async (options) => {
66
- const globalOpts = program.opts();
67
- try {
68
- if (globalOpts.dryRun) {
69
- printInfo(`[DRY-RUN] 개발현황 문서 "${options.title}" 생성 요청`);
70
- return;
71
- }
72
- const client = createClient({ projectId: globalOpts.project });
73
- const body: any = {
74
- title: options.title,
75
- };
76
- if (options.content) body.content = options.content;
77
- if (options.sprints) body.relatedSprintIds = parseCommaSeparated(options.sprints);
78
- if (options.specs) body.relatedSpecIds = parseCommaSeparated(options.specs);
79
-
80
- const { data } = await client.post('/dev-status', body);
81
- formatOutput(data, globalOpts);
82
- printSuccess('개발현황 문서가 생성되었습니다.');
83
- } catch (error) {
84
- // Error already handled
85
- }
86
- });
87
-
88
- // ch dev-status update
89
- devStatus
90
- .command('update <docId>')
91
- .description('개발현황 문서 수정')
92
- .option('--title <title>', '문서 제목')
93
- .option('--content <content>', '본문 (HTML)')
94
- .option('--sprints <sprintIds>', '관련 스프린트 ID (쉼표 구분)')
95
- .option('--specs <specIds>', '관련 기능명세 ID (쉼표 구분)')
96
- .option('--change-note <note>', '변경 사유')
97
- .option('--new-version', '새 버전으로 수정')
98
- .action(async (docId: string, options) => {
99
- const globalOpts = program.opts();
100
- try {
101
- if (globalOpts.dryRun) {
102
- printInfo(`[DRY-RUN] 개발현황 문서 "${docId}" 수정 요청`);
103
- return;
104
- }
105
- const client = createClient({ projectId: globalOpts.project });
106
- const body: any = {};
107
- if (options.title) body.title = options.title;
108
- if (options.content) body.content = options.content;
109
- if (options.sprints) body.relatedSprintIds = parseCommaSeparated(options.sprints);
110
- if (options.specs) body.relatedSpecIds = parseCommaSeparated(options.specs);
111
- if (options.changeNote) body.changeNote = options.changeNote;
112
-
113
- if (Object.keys(body).length === 0) {
114
- printError('수정할 필드를 지정하세요.');
115
- return;
116
- }
117
-
118
- const params: any = {};
119
- if (options.newVersion) params.newVersion = 'true';
120
-
121
- const { data } = await client.patch(`/dev-status/${docId}`, body, { params });
122
- formatOutput(data, globalOpts);
123
- printSuccess(
124
- options.newVersion
125
- ? '개발현황 문서가 새 버전으로 수정되었습니다.'
126
- : '개발현황 문서가 수정되었습니다.',
127
- );
128
- } catch (error) {
129
- // Error already handled
130
- }
131
- });
132
-
133
- // ch dev-status delete
134
- devStatus
135
- .command('delete <docId>')
136
- .description('개발현황 문서 삭제')
137
- .action(async (docId: string) => {
138
- const globalOpts = program.opts();
139
- try {
140
- if (globalOpts.dryRun) {
141
- printInfo(`[DRY-RUN] 개발현황 문서 "${docId}" 삭제 요청`);
142
- return;
143
- }
144
- const client = createClient({ projectId: globalOpts.project });
145
- await client.delete(`/dev-status/${docId}`);
146
- printSuccess('개발현황 문서가 삭제되었습니다.');
147
- } catch (error) {
148
- // Error already handled
149
- }
150
- });
151
-
152
- // ch dev-status versions
153
- devStatus
154
- .command('versions <docId>')
155
- .description('개발현황 문서 버전 이력 조회')
156
- .action(async (docId: string) => {
157
- const globalOpts = program.opts();
158
- try {
159
- const client = createClient({ projectId: globalOpts.project });
160
- const { data } = await client.get(`/dev-status/${docId}/versions`);
161
- formatOutput(data, globalOpts);
162
- } catch (error) {
163
- // Error already handled
164
- }
165
- });
166
- }
@@ -1,92 +0,0 @@
1
- import { Command } from 'commander';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
4
- import { createClient } from '../client';
5
- import { printSuccess, printError, printInfo } from '../output';
6
- import { loadConfig, findLocalProject } from '../config';
7
-
8
- export function registerInitCommand(program: Command): void {
9
- program
10
- .command('init')
11
- .description('현재 디렉토리에 .ch-project 파일 생성 (로컬 프로젝트 설정)')
12
- .requiredOption('--project-id <projectId>', '프로젝트 ID')
13
- .option('--project-name <projectName>', '프로젝트명 (표시용)')
14
- .action(async (options) => {
15
- const targetPath = path.join(process.cwd(), '.ch-project');
16
-
17
- if (fs.existsSync(targetPath)) {
18
- try {
19
- const existing = JSON.parse(fs.readFileSync(targetPath, 'utf-8'));
20
- printError(
21
- `.ch-project 파일이 이미 존재합니다 (projectId: ${existing.projectId}). 덮어쓰려면 파일을 삭제 후 다시 실행하세요.`
22
- );
23
- } catch {
24
- printError('.ch-project 파일이 이미 존재합니다. 덮어쓰려면 파일을 삭제 후 다시 실행하세요.');
25
- }
26
- return;
27
- }
28
-
29
- // If projectName not given, try to fetch from API (only if API key is available)
30
- let projectName = options.projectName;
31
- if (!projectName) {
32
- const config = loadConfig();
33
- if (config?.apiKey) {
34
- try {
35
- const client = createClient({ projectId: options.projectId });
36
- const { data } = await client.get('/projects/current');
37
- projectName = data.name;
38
- } catch {
39
- // API call failed, proceed without name
40
- }
41
- }
42
- }
43
-
44
- const content: any = { projectId: options.projectId };
45
- if (projectName) content.projectName = projectName;
46
-
47
- fs.writeFileSync(targetPath, JSON.stringify(content, null, 2) + '\n', 'utf-8');
48
- printSuccess(`✅ .ch-project 파일이 생성되었습니다.`);
49
- printInfo(` projectId: ${options.projectId}`);
50
- if (projectName) {
51
- printInfo(` projectName: ${projectName}`);
52
- }
53
- printInfo(` 경로: ${targetPath}`);
54
- });
55
-
56
- // ch status - show current project context
57
- program
58
- .command('status')
59
- .description('현재 프로젝트 설정 상태 확인')
60
- .action(() => {
61
- const config = loadConfig();
62
- const local = findLocalProject();
63
-
64
- console.log('');
65
- if (local) {
66
- printSuccess(`로컬 설정 (.ch-project):`);
67
- printInfo(` projectId: ${local.projectId}`);
68
- if (local.projectName) printInfo(` projectName: ${local.projectName}`);
69
- } else {
70
- printInfo('로컬 설정 (.ch-project): 없음');
71
- }
72
-
73
- console.log('');
74
- if (config) {
75
- printSuccess('글로벌 설정 (~/.ch/config.json):');
76
- printInfo(` apiUrl: ${config.apiUrl}`);
77
- printInfo(` projectId: ${config.projectId || '(없음)'}`);
78
- printInfo(` apiKey: ${config.apiKey ? '***설정됨***' : '(없음)'}`);
79
- } else {
80
- printInfo('글로벌 설정 (~/.ch/config.json): 없음');
81
- }
82
-
83
- console.log('');
84
- const effectiveProjectId = local?.projectId || config?.projectId;
85
- const source = local ? '.ch-project (로컬)' : config?.projectId ? '~/.ch/config.json (글로벌)' : '없음';
86
- if (effectiveProjectId) {
87
- printSuccess(`→ 현재 사용할 projectId: ${effectiveProjectId} (출처: ${source})`);
88
- } else {
89
- printError('프로젝트가 설정되지 않았습니다. "ch init --project-id <id>" 또는 "ch auth login"을 실행하세요.');
90
- }
91
- });
92
- }
@@ -1,92 +0,0 @@
1
- import { Command } from 'commander';
2
- import { createClient } from '../client';
3
- import { formatOutput, printSuccess, printError, printInfo } from '../output';
4
-
5
- export function registerMembersCommand(program: Command): void {
6
- const members = program
7
- .command('members')
8
- .description('멤버 관리');
9
-
10
- // ch members list
11
- members
12
- .command('list')
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('/members');
19
- formatOutput(data, globalOpts);
20
- } catch (error) {
21
- // Error already handled
22
- }
23
- });
24
-
25
- // ch members invite
26
- members
27
- .command('invite')
28
- .description('멤버 추가 (이메일로 사용자를 찾아 바로 권한 부여)')
29
- .requiredOption('--email <email>', '추가할 사용자 이메일')
30
- .requiredOption('--role <role>', '역할 (developer|client)')
31
- .action(async (options) => {
32
- const globalOpts = program.opts();
33
- try {
34
- if (globalOpts.dryRun) {
35
- printInfo(`[DRY-RUN] "${options.email}" 멤버 추가 요청`);
36
- return;
37
- }
38
- const client = createClient({ projectId: globalOpts.project });
39
- const { data } = await client.post('/members/invite', {
40
- email: options.email,
41
- role: options.role,
42
- });
43
- formatOutput(data, globalOpts);
44
- printSuccess(data.message || '멤버가 추가되었습니다.');
45
- } catch (error) {
46
- // Error already handled
47
- }
48
- });
49
-
50
- // ch members set-role
51
- members
52
- .command('set-role <userId>')
53
- .description('멤버 역할 변경')
54
- .requiredOption('--role <role>', '새 역할 (admin|developer|client)')
55
- .action(async (userId: string, options) => {
56
- const globalOpts = program.opts();
57
- try {
58
- if (globalOpts.dryRun) {
59
- printInfo(`[DRY-RUN] 멤버 "${userId}" 역할을 "${options.role}"로 변경 요청`);
60
- return;
61
- }
62
- const client = createClient({ projectId: globalOpts.project });
63
- const { data } = await client.patch(`/members/${userId}/role`, {
64
- role: options.role,
65
- });
66
- formatOutput(data, globalOpts);
67
- printSuccess(`멤버 역할이 "${options.role}"로 변경되었습니다.`);
68
- } catch (error) {
69
- // Error already handled
70
- }
71
- });
72
-
73
- // ch members remove
74
- members
75
- .command('remove <userId>')
76
- .description('멤버 제거')
77
- .action(async (userId: string) => {
78
- const globalOpts = program.opts();
79
- try {
80
- if (globalOpts.dryRun) {
81
- printInfo(`[DRY-RUN] 멤버 "${userId}" 제거 요청`);
82
- return;
83
- }
84
- const client = createClient({ projectId: globalOpts.project });
85
- await client.delete(`/members/${userId}`);
86
- printSuccess('멤버가 제거되었습니다.');
87
- } catch (error) {
88
- // Error already handled
89
- }
90
- });
91
-
92
- }