@decencia/ch-cli 1.0.0 → 1.2.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/src/config.ts DELETED
@@ -1,98 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import * as os from 'os';
4
-
5
- export interface ChConfig {
6
- apiKey: string;
7
- projectId: string;
8
- apiUrl: string;
9
- defaultFormat: 'table' | 'json' | 'csv';
10
- }
11
-
12
- export interface ChProjectLocal {
13
- projectId: string;
14
- projectName?: string;
15
- }
16
-
17
- export interface ProjectContext {
18
- projectId: string;
19
- projectName?: string;
20
- source: 'cli-option' | 'local' | 'global';
21
- }
22
-
23
- const CONFIG_DIR = path.join(os.homedir(), '.ch');
24
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
25
- const LOCAL_PROJECT_FILE = '.ch-project';
26
-
27
- export function loadConfig(): ChConfig | null {
28
- try {
29
- if (!fs.existsSync(CONFIG_FILE)) {
30
- return null;
31
- }
32
- const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
33
- return JSON.parse(raw) as ChConfig;
34
- } catch {
35
- return null;
36
- }
37
- }
38
-
39
- /**
40
- * Search for .ch-project file from cwd upward.
41
- */
42
- export function findLocalProject(): ChProjectLocal | null {
43
- try {
44
- let dir = process.cwd();
45
- const root = path.parse(dir).root;
46
- while (true) {
47
- const filePath = path.join(dir, LOCAL_PROJECT_FILE);
48
- if (fs.existsSync(filePath)) {
49
- const raw = fs.readFileSync(filePath, 'utf-8');
50
- return JSON.parse(raw) as ChProjectLocal;
51
- }
52
- const parent = path.dirname(dir);
53
- if (parent === dir || dir === root) break;
54
- dir = parent;
55
- }
56
- } catch {
57
- // ignore parse errors
58
- }
59
- return null;
60
- }
61
-
62
- /**
63
- * Resolve projectId with priority: CLI --project > .ch-project > ~/.ch/config.json
64
- */
65
- export function getProjectContext(cliProjectId?: string): ProjectContext | null {
66
- if (cliProjectId) {
67
- return { projectId: cliProjectId, source: 'cli-option' };
68
- }
69
-
70
- const local = findLocalProject();
71
- if (local?.projectId) {
72
- return { projectId: local.projectId, projectName: local.projectName, source: 'local' };
73
- }
74
-
75
- const global = loadConfig();
76
- if (global?.projectId) {
77
- return { projectId: global.projectId, source: 'global' };
78
- }
79
-
80
- return null;
81
- }
82
-
83
- export function saveConfig(config: ChConfig): void {
84
- if (!fs.existsSync(CONFIG_DIR)) {
85
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
86
- }
87
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
88
- }
89
-
90
- export function clearConfig(): void {
91
- if (fs.existsSync(CONFIG_FILE)) {
92
- fs.unlinkSync(CONFIG_FILE);
93
- }
94
- }
95
-
96
- export function getConfigPath(): string {
97
- return CONFIG_FILE;
98
- }
package/src/index.ts DELETED
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import * as path from 'path';
5
- import * as fs from 'fs';
6
-
7
- import { registerAuthCommand } from './commands/auth';
8
- import { registerInitCommand } from './commands/init';
9
- import { registerProjectsCommand } from './commands/projects';
10
- import { registerSpecsCommand } from './commands/specs';
11
- import { registerSprintsCommand } from './commands/sprints';
12
- import { registerQnaCommand } from './commands/qna';
13
- import { registerSqaCommand } from './commands/sqa';
14
- import { registerArchivesCommand } from './commands/archives';
15
- import { registerMembersCommand } from './commands/members';
16
- import { registerDevStatusCommand } from './commands/dev-status';
17
- import { registerPrdCommand } from './commands/prd';
18
- import { registerDbSchemaCommand } from './commands/db-schema';
19
-
20
- import { registerNotificationsCommand } from './commands/notifications';
21
- import { registerDashboardCommand } from './commands/dashboard';
22
-
23
- // Read version from package.json
24
- function getVersion(): string {
25
- try {
26
- const pkgPath = path.join(__dirname, '..', 'package.json');
27
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
28
- return pkg.version || '1.0.0';
29
- } catch {
30
- return '1.0.0';
31
- }
32
- }
33
-
34
- const program = new Command();
35
-
36
- program
37
- .name('ch')
38
- .description('Decencia Communication Channel CLI')
39
- .version(getVersion(), '-v, --version', '버전 표시')
40
- .option('--json', 'JSON 형식으로 출력')
41
- .option('--table', '테이블 형식으로 출력 (기본값)')
42
- .option('--csv', 'CSV 형식으로 출력')
43
- .option('--project <projectId>', '프로젝트 ID 오버라이드')
44
- .option('--verbose', '상세 로그 출력')
45
- .option('--quiet', '결과만 출력 (스크립팅용)')
46
- .option('--dry-run', '쓰기 작업 시뮬레이션');
47
-
48
- // Register all command modules
49
- registerInitCommand(program);
50
- registerAuthCommand(program);
51
- registerProjectsCommand(program);
52
- registerSpecsCommand(program);
53
- registerSprintsCommand(program);
54
- registerQnaCommand(program);
55
- registerSqaCommand(program);
56
- registerArchivesCommand(program);
57
- registerMembersCommand(program);
58
- registerDevStatusCommand(program);
59
- registerPrdCommand(program);
60
- registerDbSchemaCommand(program);
61
-
62
- registerNotificationsCommand(program);
63
- registerDashboardCommand(program);
64
-
65
- // Parse and execute
66
- program.parseAsync(process.argv).catch((err) => {
67
- console.error(err);
68
- process.exit(1);
69
- });
package/src/output.ts DELETED
@@ -1,186 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { getLastProjectContext } from './client';
4
-
5
- export interface OutputOptions {
6
- json?: boolean;
7
- table?: boolean;
8
- csv?: boolean;
9
- quiet?: boolean;
10
- }
11
-
12
- export function formatOutput(data: any, options: OutputOptions = {}): void {
13
- if (options.quiet) {
14
- // In quiet mode, only output raw JSON for piping
15
- console.log(JSON.stringify(data));
16
- return;
17
- }
18
-
19
- if (options.json) {
20
- console.log(JSON.stringify(data, null, 2));
21
- return;
22
- }
23
-
24
- if (options.csv) {
25
- printCsv(data);
26
- return;
27
- }
28
-
29
- // Default: table or pretty print
30
- if (Array.isArray(data)) {
31
- if (data.length === 0) {
32
- console.log(chalk.yellow('데이터가 없습니다.'));
33
- return;
34
- }
35
- printAutoTable(data);
36
- } else if (data && typeof data === 'object') {
37
- // Check if it's a paginated response
38
- if (data.data && Array.isArray(data.data)) {
39
- if (data.data.length === 0) {
40
- console.log(chalk.yellow('데이터가 없습니다.'));
41
- } else {
42
- printAutoTable(data.data);
43
- }
44
- if (data.pagination) {
45
- const p = data.pagination;
46
- console.log(
47
- chalk.gray(
48
- `\n페이지 ${p.page}/${p.totalPages} (총 ${p.total}건)`
49
- )
50
- );
51
- }
52
- } else {
53
- // Single object - print key-value pairs
54
- printKeyValue(data);
55
- }
56
- } else {
57
- console.log(data);
58
- }
59
- }
60
-
61
- function printAutoTable(rows: any[]): void {
62
- if (rows.length === 0) return;
63
-
64
- const keys = Object.keys(rows[0]);
65
- // Limit columns to avoid overly wide tables
66
- const displayKeys = keys.slice(0, 8);
67
-
68
- const table = new Table({
69
- head: displayKeys.map((k) => chalk.cyan(k)),
70
- style: { head: [], border: [] },
71
- wordWrap: true,
72
- });
73
-
74
- for (const row of rows) {
75
- table.push(
76
- displayKeys.map((k) => {
77
- const val = row[k];
78
- if (val === null || val === undefined) return '';
79
- if (typeof val === 'object') return JSON.stringify(val).substring(0, 50);
80
- const str = String(val);
81
- return str.length > 60 ? str.substring(0, 57) + '...' : str;
82
- })
83
- );
84
- }
85
-
86
- console.log(table.toString());
87
- }
88
-
89
- function printKeyValue(obj: any): void {
90
- const table = new Table({
91
- style: { head: [], border: [] },
92
- });
93
-
94
- for (const [key, value] of Object.entries(obj)) {
95
- let display: string;
96
- if (value === null || value === undefined) {
97
- display = chalk.gray('(없음)');
98
- } else if (Array.isArray(value)) {
99
- display = value.length > 0
100
- ? JSON.stringify(value, null, 2)
101
- : chalk.gray('(비어있음)');
102
- } else if (typeof value === 'object') {
103
- display = JSON.stringify(value, null, 2);
104
- } else {
105
- display = String(value);
106
- }
107
- table.push({ [chalk.cyan(key)]: display });
108
- }
109
-
110
- console.log(table.toString());
111
- }
112
-
113
- function printCsv(data: any): void {
114
- let rows: any[];
115
- if (Array.isArray(data)) {
116
- rows = data;
117
- } else if (data && data.data && Array.isArray(data.data)) {
118
- rows = data.data;
119
- } else {
120
- // Single object
121
- rows = [data];
122
- }
123
-
124
- if (rows.length === 0) return;
125
-
126
- const keys = Object.keys(rows[0]);
127
- // CSV header
128
- console.log(keys.map(escapeCsvField).join(','));
129
- // CSV rows
130
- for (const row of rows) {
131
- console.log(
132
- keys
133
- .map((k) => {
134
- const val = row[k];
135
- if (val === null || val === undefined) return '';
136
- if (typeof val === 'object') return escapeCsvField(JSON.stringify(val));
137
- return escapeCsvField(String(val));
138
- })
139
- .join(',')
140
- );
141
- }
142
- }
143
-
144
- function escapeCsvField(field: string): string {
145
- if (field.includes(',') || field.includes('"') || field.includes('\n')) {
146
- return `"${field.replace(/"/g, '""')}"`;
147
- }
148
- return field;
149
- }
150
-
151
- export function printTable(headers: string[], rows: string[][]): void {
152
- const table = new Table({
153
- head: headers.map((h) => chalk.cyan(h)),
154
- style: { head: [], border: [] },
155
- });
156
-
157
- for (const row of rows) {
158
- table.push(row);
159
- }
160
-
161
- console.log(table.toString());
162
- }
163
-
164
- export function printError(message: string): void {
165
- console.error(chalk.red(`오류: ${message}`));
166
- }
167
-
168
- function projectPrefix(): string {
169
- const ctx = getLastProjectContext();
170
- if (ctx?.projectName) {
171
- return chalk.cyan(`[${ctx.projectName}] `);
172
- }
173
- return '';
174
- }
175
-
176
- export function printSuccess(message: string): void {
177
- console.log(projectPrefix() + chalk.green(message));
178
- }
179
-
180
- export function printWarning(message: string): void {
181
- console.log(projectPrefix() + chalk.yellow(message));
182
- }
183
-
184
- export function printInfo(message: string): void {
185
- console.log(projectPrefix() + chalk.blue(message));
186
- }
package/src/utils.ts DELETED
@@ -1,3 +0,0 @@
1
- export function parseCommaSeparated(value: string): string[] {
2
- return value.split(',').map((v) => v.trim()).filter(Boolean);
3
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "outDir": "dist",
6
- "rootDir": "src",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "resolveJsonModule": true,
12
- "declaration": true,
13
- "declarationMap": true,
14
- "sourceMap": true
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules", "dist"]
18
- }