cciwon-code-review-cli 1.0.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/README.md +233 -0
- package/bin/code-review.js +155 -0
- package/lib/api-client.js +91 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# cciwon-code-review-cli
|
|
2
|
+
|
|
3
|
+
AI 기반 코드 리뷰 CLI 도구 (Qwen3-Coder-30B DPO 파인튜닝 모델 사용)
|
|
4
|
+
|
|
5
|
+
## 특징
|
|
6
|
+
|
|
7
|
+
- 🔒 **폐쇄망 지원**: IP 화이트리스트 기반 (192.168.10.152)
|
|
8
|
+
- 📁 **폴더 전체 리뷰**: 프로젝트 전체를 한 번에 분석
|
|
9
|
+
- 📄 **파일 단위 리뷰**: 개별 파일 심층 분석
|
|
10
|
+
- 🛡️ **보안 중심**: SQL Injection, XSS 등 보안 취약점 탐지
|
|
11
|
+
- ⚡ **빠른 응답**: vLLM 기반 고성능 추론
|
|
12
|
+
|
|
13
|
+
## 설치
|
|
14
|
+
|
|
15
|
+
### 1. 서버 설정
|
|
16
|
+
|
|
17
|
+
먼저 vLLM 서버를 시작해야 합니다:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 필수 패키지 설치
|
|
21
|
+
pip install vllm fastapi uvicorn
|
|
22
|
+
|
|
23
|
+
# 서버 실행
|
|
24
|
+
cd /home/cwk317/codereivew
|
|
25
|
+
python vllm_server.py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
서버가 `192.168.10.152:8000`에서 실행됩니다.
|
|
29
|
+
|
|
30
|
+
### 2. CLI 설치
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd code-review-cli
|
|
34
|
+
npm install
|
|
35
|
+
npm link # 전역 설치
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
또는 로컬에서 직접 사용:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node bin/code-review.js <command>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 사용법
|
|
45
|
+
|
|
46
|
+
### 서버 상태 확인
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
code-review health
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
출력 예시:
|
|
53
|
+
```
|
|
54
|
+
✅ 서버 정상 작동 중
|
|
55
|
+
|
|
56
|
+
서버 정보:
|
|
57
|
+
- 상태: healthy
|
|
58
|
+
- 모델 로드: 완료
|
|
59
|
+
- 서버 URL: http://192.168.10.152:8000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 폴더 전체 리뷰
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
code-review folder /path/to/project
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
옵션:
|
|
69
|
+
- `-i, --include <patterns>`: 포함할 파일 패턴 (기본: `*.py,*.js,*.ts,*.java,*.cpp,*.go`)
|
|
70
|
+
- `-e, --exclude <patterns>`: 제외할 패턴 (기본: `node_modules,__pycache__,.git,*.pyc`)
|
|
71
|
+
- `-m, --max-files <number>`: 최대 파일 수 (기본: 50)
|
|
72
|
+
|
|
73
|
+
예시:
|
|
74
|
+
```bash
|
|
75
|
+
# Python 프로젝트 리뷰
|
|
76
|
+
code-review folder ./my-python-project -i "*.py" -e "__pycache__,*.pyc,venv"
|
|
77
|
+
|
|
78
|
+
# JavaScript 프로젝트 리뷰
|
|
79
|
+
code-review folder ./my-js-project -i "*.js,*.jsx" -e "node_modules,dist,build"
|
|
80
|
+
|
|
81
|
+
# 모든 코드 파일 리뷰 (최대 100개)
|
|
82
|
+
code-review folder ./my-project -m 100
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 단일 파일 리뷰
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
code-review file /path/to/file.py
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
옵션:
|
|
92
|
+
- `-l, --language <lang>`: 프로그래밍 언어 (기본: python)
|
|
93
|
+
- `-c, --checklist <path>`: 체크리스트 파일 경로
|
|
94
|
+
|
|
95
|
+
예시:
|
|
96
|
+
```bash
|
|
97
|
+
# Python 파일 리뷰
|
|
98
|
+
code-review file ./app.py -l python
|
|
99
|
+
|
|
100
|
+
# TypeScript 파일 + 체크리스트
|
|
101
|
+
code-review file ./api.ts -l typescript -c ./checklist.txt
|
|
102
|
+
|
|
103
|
+
# Java 파일 리뷰
|
|
104
|
+
code-review file ./Main.java -l java
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## VSCode 통합
|
|
108
|
+
|
|
109
|
+
### 방법 1: Tasks 사용
|
|
110
|
+
|
|
111
|
+
`.vscode/tasks.json` 파일 생성:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"version": "2.0.0",
|
|
116
|
+
"tasks": [
|
|
117
|
+
{
|
|
118
|
+
"label": "Code Review: Current Folder",
|
|
119
|
+
"type": "shell",
|
|
120
|
+
"command": "code-review folder ${workspaceFolder}",
|
|
121
|
+
"problemMatcher": [],
|
|
122
|
+
"presentation": {
|
|
123
|
+
"reveal": "always",
|
|
124
|
+
"panel": "new"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"label": "Code Review: Current File",
|
|
129
|
+
"type": "shell",
|
|
130
|
+
"command": "code-review file ${file}",
|
|
131
|
+
"problemMatcher": [],
|
|
132
|
+
"presentation": {
|
|
133
|
+
"reveal": "always",
|
|
134
|
+
"panel": "new"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
사용법:
|
|
142
|
+
1. `Ctrl+Shift+P` → `Tasks: Run Task`
|
|
143
|
+
2. `Code Review: Current Folder` 또는 `Code Review: Current File` 선택
|
|
144
|
+
|
|
145
|
+
### 방법 2: 터미널에서 직접 사용
|
|
146
|
+
|
|
147
|
+
VSCode 내장 터미널에서:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# 현재 워크스페이스 리뷰
|
|
151
|
+
code-review folder .
|
|
152
|
+
|
|
153
|
+
# 특정 파일 리뷰
|
|
154
|
+
code-review file src/main.py
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 환경 변수
|
|
158
|
+
|
|
159
|
+
서버 URL을 변경하려면 환경 변수 설정:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
export CODE_REVIEW_SERVER=http://192.168.10.152:8000
|
|
163
|
+
code-review health
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## IP 화이트리스트
|
|
167
|
+
|
|
168
|
+
현재 허용된 IP:
|
|
169
|
+
- `192.168.10.152` (서버 IP)
|
|
170
|
+
- `127.0.0.1` (로컬 테스트용)
|
|
171
|
+
|
|
172
|
+
다른 IP에서 접근 시:
|
|
173
|
+
```
|
|
174
|
+
❌ 허용된 아이피가 아닙니다
|
|
175
|
+
현재 IP: 192.168.10.xxx
|
|
176
|
+
허용된 IP: 192.168.10.152, 127.0.0.1
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
IP 추가하려면 `vllm_server.py`의 `ALLOWED_IPS` 수정:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
ALLOWED_IPS = ["192.168.10.152", "127.0.0.1", "192.168.10.100"] # 새 IP 추가
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 트러블슈팅
|
|
186
|
+
|
|
187
|
+
### 서버 연결 실패
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
❌ 서버 연결 실패
|
|
191
|
+
서버 연결 실패: http://192.168.10.152:8000
|
|
192
|
+
서버가 실행 중인지 확인하세요.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
해결:
|
|
196
|
+
1. vLLM 서버가 실행 중인지 확인: `ps aux | grep vllm_server`
|
|
197
|
+
2. 서버 로그 확인: `tail -f /path/to/server.log`
|
|
198
|
+
3. 방화벽 확인: `sudo ufw status`
|
|
199
|
+
|
|
200
|
+
### IP 차단
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
❌ 허용된 아이피가 아닙니다
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
해결:
|
|
207
|
+
1. 현재 IP 확인: `curl ifconfig.me`
|
|
208
|
+
2. `vllm_server.py`의 `ALLOWED_IPS`에 추가
|
|
209
|
+
3. 서버 재시작
|
|
210
|
+
|
|
211
|
+
### 메모리 부족
|
|
212
|
+
|
|
213
|
+
vLLM 서버가 OOM으로 죽는 경우:
|
|
214
|
+
|
|
215
|
+
`vllm_server.py` 수정:
|
|
216
|
+
```python
|
|
217
|
+
llm = LLM(
|
|
218
|
+
model=OUTPUT_DIR,
|
|
219
|
+
tensor_parallel_size=2,
|
|
220
|
+
gpu_memory_utilization=0.7, # 0.9 → 0.7로 감소
|
|
221
|
+
max_model_len=1024, # 2048 → 1024로 감소
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## 성능
|
|
226
|
+
|
|
227
|
+
- 평균 응답 시간: ~5초 (폴더 리뷰, 10개 파일 기준)
|
|
228
|
+
- 동시 처리: vLLM 배치 처리로 여러 요청 동시 처리 가능
|
|
229
|
+
- GPU 사용량: 2x RTX 5090 (50-60GB)
|
|
230
|
+
|
|
231
|
+
## 라이선스
|
|
232
|
+
|
|
233
|
+
MIT
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/code-review.js
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const CodeReviewClient = require('../lib/api-client');
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
// CLI 설정
|
|
13
|
+
program
|
|
14
|
+
.name('code-review')
|
|
15
|
+
.description('AI-powered code review CLI using Qwen3-Coder model')
|
|
16
|
+
.version('1.0.0');
|
|
17
|
+
|
|
18
|
+
// 서버 URL 설정 (환경변수 또는 기본값)
|
|
19
|
+
const SERVER_URL = process.env.CODE_REVIEW_SERVER || 'http://192.168.10.152:8000';
|
|
20
|
+
const client = new CodeReviewClient(SERVER_URL);
|
|
21
|
+
|
|
22
|
+
// ==========================================
|
|
23
|
+
// 폴더 리뷰 명령어
|
|
24
|
+
// ==========================================
|
|
25
|
+
program
|
|
26
|
+
.command('folder <path>')
|
|
27
|
+
.description('폴더 전체를 리뷰합니다')
|
|
28
|
+
.option('-i, --include <patterns>', '포함할 파일 패턴 (쉼표로 구분)', '*.py,*.js,*.ts,*.java,*.cpp,*.go')
|
|
29
|
+
.option('-e, --exclude <patterns>', '제외할 패턴 (쉼표로 구분)', 'node_modules,__pycache__,.git,*.pyc')
|
|
30
|
+
.option('-m, --max-files <number>', '최대 파일 수', '50')
|
|
31
|
+
.action(async (folderPath, options) => {
|
|
32
|
+
const spinner = ora('폴더 리뷰 진행 중...').start();
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// 절대 경로로 변환
|
|
36
|
+
const absolutePath = path.resolve(folderPath);
|
|
37
|
+
|
|
38
|
+
// 폴더 존재 확인
|
|
39
|
+
if (!fs.existsSync(absolutePath)) {
|
|
40
|
+
spinner.fail(chalk.red(`❌ 폴더가 존재하지 않습니다: ${absolutePath}`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// API 호출
|
|
45
|
+
const result = await client.reviewFolder(absolutePath, {
|
|
46
|
+
includePatterns: options.include.split(','),
|
|
47
|
+
excludePatterns: options.exclude.split(','),
|
|
48
|
+
maxFiles: parseInt(options.maxFiles)
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
spinner.succeed(chalk.green(`✅ 리뷰 완료! (분석된 파일: ${result.files_analyzed}개)`));
|
|
52
|
+
|
|
53
|
+
// 결과 출력
|
|
54
|
+
console.log('\n' + chalk.bold.cyan('=== 코드 리뷰 결과 ==='));
|
|
55
|
+
console.log(result.review);
|
|
56
|
+
|
|
57
|
+
if (result.warnings.length > 0) {
|
|
58
|
+
console.log('\n' + chalk.yellow('⚠️ 경고:'));
|
|
59
|
+
result.warnings.forEach(warning => {
|
|
60
|
+
console.log(chalk.yellow(` - ${warning}`));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
} catch (error) {
|
|
65
|
+
spinner.fail(chalk.red('❌ 리뷰 실패'));
|
|
66
|
+
console.error(chalk.red(error.message));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// ==========================================
|
|
72
|
+
// 파일 리뷰 명령어
|
|
73
|
+
// ==========================================
|
|
74
|
+
program
|
|
75
|
+
.command('file <path>')
|
|
76
|
+
.description('단일 파일을 리뷰합니다')
|
|
77
|
+
.option('-l, --language <lang>', '프로그래밍 언어', 'python')
|
|
78
|
+
.option('-c, --checklist <path>', '체크리스트 파일 경로')
|
|
79
|
+
.action(async (filePath, options) => {
|
|
80
|
+
const spinner = ora('파일 리뷰 진행 중...').start();
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
// 절대 경로로 변환
|
|
84
|
+
const absolutePath = path.resolve(filePath);
|
|
85
|
+
|
|
86
|
+
// 파일 존재 확인
|
|
87
|
+
if (!fs.existsSync(absolutePath)) {
|
|
88
|
+
spinner.fail(chalk.red(`❌ 파일이 존재하지 않습니다: ${absolutePath}`));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 파일 읽기
|
|
93
|
+
const code = fs.readFileSync(absolutePath, 'utf-8');
|
|
94
|
+
|
|
95
|
+
// 체크리스트 읽기 (옵션)
|
|
96
|
+
let checklist = null;
|
|
97
|
+
if (options.checklist) {
|
|
98
|
+
const checklistPath = path.resolve(options.checklist);
|
|
99
|
+
if (fs.existsSync(checklistPath)) {
|
|
100
|
+
checklist = fs.readFileSync(checklistPath, 'utf-8');
|
|
101
|
+
} else {
|
|
102
|
+
spinner.warn(chalk.yellow(`⚠️ 체크리스트 파일을 찾을 수 없습니다: ${checklistPath}`));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// API 호출
|
|
107
|
+
const result = await client.reviewCode(code, options.language, checklist);
|
|
108
|
+
|
|
109
|
+
spinner.succeed(chalk.green('✅ 리뷰 완료!'));
|
|
110
|
+
|
|
111
|
+
// 결과 출력
|
|
112
|
+
console.log('\n' + chalk.bold.cyan('=== 코드 리뷰 결과 ==='));
|
|
113
|
+
console.log(result.review);
|
|
114
|
+
|
|
115
|
+
} catch (error) {
|
|
116
|
+
spinner.fail(chalk.red('❌ 리뷰 실패'));
|
|
117
|
+
console.error(chalk.red(error.message));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// ==========================================
|
|
123
|
+
// 헬스 체크 명령어
|
|
124
|
+
// ==========================================
|
|
125
|
+
program
|
|
126
|
+
.command('health')
|
|
127
|
+
.description('서버 상태를 확인합니다')
|
|
128
|
+
.action(async () => {
|
|
129
|
+
const spinner = ora('서버 연결 확인 중...').start();
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const health = await client.healthCheck();
|
|
133
|
+
spinner.succeed(chalk.green('✅ 서버 정상 작동 중'));
|
|
134
|
+
|
|
135
|
+
console.log(chalk.cyan('\n서버 정보:'));
|
|
136
|
+
console.log(` - 상태: ${chalk.green(health.status)}`);
|
|
137
|
+
console.log(` - 모델 로드: ${health.model_loaded ? chalk.green('완료') : chalk.red('실패')}`);
|
|
138
|
+
console.log(` - 서버 URL: ${chalk.blue(SERVER_URL)}`);
|
|
139
|
+
|
|
140
|
+
} catch (error) {
|
|
141
|
+
spinner.fail(chalk.red('❌ 서버 연결 실패'));
|
|
142
|
+
console.error(chalk.red(error.message));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ==========================================
|
|
148
|
+
// CLI 실행
|
|
149
|
+
// ==========================================
|
|
150
|
+
program.parse(process.argv);
|
|
151
|
+
|
|
152
|
+
// 명령어가 없으면 help 출력
|
|
153
|
+
if (!process.argv.slice(2).length) {
|
|
154
|
+
program.outputHelp();
|
|
155
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// lib/api-client.js
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
|
|
4
|
+
class CodeReviewClient {
|
|
5
|
+
constructor(serverUrl = 'http://192.168.10.152:8000') {
|
|
6
|
+
this.serverUrl = serverUrl;
|
|
7
|
+
this.client = axios.create({
|
|
8
|
+
baseURL: serverUrl,
|
|
9
|
+
timeout: 120000, // 2분 타임아웃 (추론 시간 고려)
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 서버 헬스 체크
|
|
18
|
+
*/
|
|
19
|
+
async healthCheck() {
|
|
20
|
+
try {
|
|
21
|
+
const response = await this.client.get('/health');
|
|
22
|
+
return response.data;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
this.handleError(error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 폴더 전체 리뷰
|
|
30
|
+
* @param {string} folderPath - 리뷰할 폴더 경로
|
|
31
|
+
* @param {object} options - 옵션 (include_patterns, exclude_patterns, max_files)
|
|
32
|
+
*/
|
|
33
|
+
async reviewFolder(folderPath, options = {}) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await this.client.post('/review/folder', {
|
|
36
|
+
folder_path: folderPath,
|
|
37
|
+
include_patterns: options.includePatterns || ['*.py', '*.js', '*.ts', '*.java', '*.cpp', '*.go'],
|
|
38
|
+
exclude_patterns: options.excludePatterns || ['node_modules', '__pycache__', '.git', '*.pyc'],
|
|
39
|
+
max_files: options.maxFiles || 50
|
|
40
|
+
});
|
|
41
|
+
return response.data;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
this.handleError(error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 단일 코드 스니펫 리뷰
|
|
49
|
+
* @param {string} code - 리뷰할 코드
|
|
50
|
+
* @param {string} language - 프로그래밍 언어
|
|
51
|
+
* @param {string} checklist - 체크리스트 (선택)
|
|
52
|
+
*/
|
|
53
|
+
async reviewCode(code, language = 'python', checklist = null) {
|
|
54
|
+
try {
|
|
55
|
+
const response = await this.client.post('/review/code', {
|
|
56
|
+
code,
|
|
57
|
+
language,
|
|
58
|
+
checklist
|
|
59
|
+
});
|
|
60
|
+
return response.data;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
this.handleError(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 에러 핸들링
|
|
68
|
+
*/
|
|
69
|
+
handleError(error) {
|
|
70
|
+
if (error.response) {
|
|
71
|
+
// 서버 응답이 있는 경우
|
|
72
|
+
const data = error.response.data;
|
|
73
|
+
|
|
74
|
+
// IP 차단 메시지
|
|
75
|
+
if (data.error === '허용된 아이피가 아닙니다') {
|
|
76
|
+
throw new Error(`❌ ${data.error}\n현재 IP: ${data.client_ip}\n허용된 IP: ${data.allowed_ips.join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 기타 서버 에러
|
|
80
|
+
throw new Error(`서버 에러 (${error.response.status}): ${data.detail || JSON.stringify(data)}`);
|
|
81
|
+
} else if (error.request) {
|
|
82
|
+
// 요청은 보냈지만 응답이 없는 경우
|
|
83
|
+
throw new Error(`서버 연결 실패: ${this.serverUrl}\n서버가 실행 중인지 확인하세요.`);
|
|
84
|
+
} else {
|
|
85
|
+
// 요청 설정 중 에러
|
|
86
|
+
throw new Error(`요청 실패: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = CodeReviewClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cciwon-code-review-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered code review CLI tool using Qwen3-Coder-30B model with IP whitelist support",
|
|
5
|
+
"main": "lib/api-client.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"code-review": "bin/code-review.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"code-review",
|
|
14
|
+
"ai",
|
|
15
|
+
"qwen",
|
|
16
|
+
"qwen3-coder",
|
|
17
|
+
"code-analysis",
|
|
18
|
+
"security",
|
|
19
|
+
"llm",
|
|
20
|
+
"code-quality",
|
|
21
|
+
"static-analysis"
|
|
22
|
+
],
|
|
23
|
+
"author": "cciwon",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"axios": "^1.6.0",
|
|
27
|
+
"commander": "^11.1.0",
|
|
28
|
+
"chalk": "^4.1.2",
|
|
29
|
+
"ora": "^5.4.1"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=14.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|