codepolisher-cli 1.1.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/README.md +246 -240
- package/package.json +1 -1
- package/src/commands/review.js +87 -77
- package/src/comparison.js +126 -0
- package/src/display.js +151 -86
- package/src/index.js +28 -28
- package/src/prompt.js +14 -10
package/README.md
CHANGED
|
@@ -1,250 +1,256 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:6366f1,100:a855f7&height=160§ion=header&text=CodePolisher%20CLI&fontSize=48&fontColor=ffffff&fontAlignY=38&desc=AI-powered%20code%20review%20from%20your%20terminal&descAlignY=58&descSize=16" width="100%" />
|
|
4
|
-
|
|
5
|
-
<br/>
|
|
6
|
-
|
|
7
|
-
[](https://nodejs.org/)
|
|
8
|
-
[](LICENSE)
|
|
9
|
-
[](#supported-providers)
|
|
11
|
-
[](https://nodejs.org/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://github.com/v1ral-ITS/codepolisher-cli/actions/workflows/test.yml)
|
|
10
|
+
[](#supported-providers)
|
|
11
|
+
[](https://www.npmjs.com/package/codepolisher-cli)
|
|
12
|
+
[](https://www.npmjs.com/package/codepolisher-cli)
|
|
13
|
+
|
|
14
|
+
<br/>
|
|
15
|
+
|
|
16
|
+
> **Polish code. Catch issues. Ship with confidence.**
|
|
17
|
+
> Review files or piped source with the AI provider you already use—without tying your workflow to a hosted backend.
|
|
18
|
+
|
|
19
|
+
<br/>
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Table of Contents
|
|
26
|
+
|
|
27
|
+
- [How it works](#how-it-works)
|
|
28
|
+
- [Supported Providers](#supported-providers)
|
|
29
|
+
- [Install](#install)
|
|
30
|
+
- [Usage](#usage)
|
|
31
|
+
- [Configuration](#configuration)
|
|
32
|
+
- [Architecture](#architecture)
|
|
33
|
+
- [Security](#security)
|
|
34
|
+
- [Development](#development)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## How it works
|
|
39
|
+
|
|
40
|
+
CodePolisher reads a file or standard input, builds a focused review prompt, sends it directly to your selected provider, and renders structured findings for a human or CI pipeline. Every review renders the original and the AI-suggested correction side by side, line by line.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Source Code Review Pipeline Result
|
|
44
|
+
┌──────────────┐ ┌────────────────────┐ ┌─────────────────┐
|
|
45
|
+
│ file.js │────▶│ Detect language │────▶│ Summary │
|
|
46
|
+
│ script.py │────▶│ Apply focus/rules │────▶│ Inline findings │
|
|
47
|
+
│ stdin pipe │────▶│ Call your provider │────▶│ JSON or terminal│
|
|
48
|
+
└──────────────┘ └────────────────────┘ └─────────────────┘
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Your provider key stays under your control. CodePolisher CLI does not require Base44 and does not proxy reviews through the CodePolisher website.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Supported Providers
|
|
56
|
+
|
|
57
|
+
| Provider | Config name | Environment variable | Default model |
|
|
58
|
+
|----------|-------------|----------------------|---------------|
|
|
59
|
+
| OpenAI | `openai` | `OPENAI_API_KEY` | `gpt-4o-mini` |
|
|
60
|
+
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | `claude-3-5-haiku-20241022` |
|
|
61
|
+
| Google Gemini | `gemini` | `GEMINI_API_KEY` | `gemini-1.5-flash` |
|
|
62
|
+
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-chat` |
|
|
63
|
+
| Venice.ai | `venice` | `VENICE_API_KEY` | `zai-org-glm-5-1` |
|
|
64
|
+
| Groq | `groq` | `GROQ_API_KEY` | `openai/gpt-oss-20b` |
|
|
65
|
+
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | Account default |
|
|
66
|
+
| Ollama | `ollama` | None required | `llama3.2` |
|
|
67
|
+
|
|
68
|
+
Larger files automatically use the provider's configured large-file model unless you set an explicit model.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
### From npm
|
|
75
|
+
|
|
76
|
+
CodePolisher CLI is officially published on the public npm registry.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install -g codepolisher-cli
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### From source
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone https://github.com/v1ral-ITS/codepolisher-cli.git
|
|
86
|
+
cd codepolisher-cli
|
|
87
|
+
npm install
|
|
88
|
+
npm link
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
> **Requires:** Node.js 18 or newer
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Review a file
|
|
99
|
+
codepolisher review src/index.js
|
|
100
|
+
|
|
101
|
+
# Review piped code
|
|
102
|
+
cat src/index.js | codepolisher review --language javascript
|
|
103
|
+
|
|
104
|
+
# Focus the review
|
|
105
|
+
codepolisher review app.py --focus security,performance
|
|
106
|
+
|
|
107
|
+
# Run a security audit
|
|
108
|
+
codepolisher review server.js --security
|
|
109
|
+
|
|
110
|
+
# Apply project-specific rules
|
|
111
|
+
codepolisher review api.ts --rules "Require error handling and input validation."
|
|
112
|
+
|
|
110
113
|
# Emit machine-readable output for CI
|
|
111
114
|
codepolisher review app.js --output json --fail-on-critical
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Review controls
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
|
121
|
-
|
|
122
|
-
| `--
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Review controls
|
|
119
|
+
|
|
120
|
+
| Option | Purpose |
|
|
121
|
+
|--------|---------|
|
|
122
|
+
| `--language <lang>` | Supply a language hint |
|
|
123
|
+
| `--focus <areas>` | Focus on security, performance, readability, error handling, best practices, or testing |
|
|
124
|
+
| `--rules <text>` | Add custom review rules |
|
|
125
|
+
| `--strict` | Flag every issue |
|
|
126
|
+
| `--security` | Run a security-focused audit |
|
|
123
127
|
| `--output json` | Produce JSON for scripts and CI |
|
|
124
128
|
| `--fail-on-critical` | Exit with code 1 when critical issues are found |
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## Configuration
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
# Choose a provider
|
|
132
|
-
codepolisher config set provider openai
|
|
133
|
-
|
|
134
|
-
# Save a provider-specific API key
|
|
135
|
-
codepolisher config set api-key <your-key>
|
|
136
|
-
|
|
137
|
-
# Optionally override the model
|
|
138
|
-
codepolisher config set model gpt-4o
|
|
139
|
-
|
|
140
|
-
# Inspect or clear configuration
|
|
141
|
-
codepolisher config get
|
|
142
|
-
codepolisher config clear
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Environment variables work without a local configuration file, so CI can inject provider keys through repository secrets.
|
|
146
|
-
|
|
147
|
-
For Ollama, no API key is required:
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
codepolisher config set provider ollama
|
|
151
|
-
codepolisher config set model llama3.2
|
|
152
|
-
codepolisher config set ollama-host http://localhost:11434
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
## Architecture
|
|
158
|
-
|
|
159
|
-
<details>
|
|
160
|
-
<summary>View project structure</summary>
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
bin/
|
|
164
|
-
└── codepolisher.js # Executable entry point
|
|
165
|
-
src/
|
|
166
|
-
├── index.js # Commander program and commands
|
|
167
|
-
├── api.js # Provider adapters and response parsing
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Configuration
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Choose a provider
|
|
136
|
+
codepolisher config set provider openai
|
|
137
|
+
|
|
138
|
+
# Save a provider-specific API key
|
|
139
|
+
codepolisher config set api-key <your-key>
|
|
140
|
+
|
|
141
|
+
# Optionally override the model
|
|
142
|
+
codepolisher config set model gpt-4o
|
|
143
|
+
|
|
144
|
+
# Inspect or clear configuration
|
|
145
|
+
codepolisher config get
|
|
146
|
+
codepolisher config clear
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Environment variables work without a local configuration file, so CI can inject provider keys through repository secrets.
|
|
150
|
+
|
|
151
|
+
For Ollama, no API key is required:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
codepolisher config set provider ollama
|
|
155
|
+
codepolisher config set model llama3.2
|
|
156
|
+
codepolisher config set ollama-host http://localhost:11434
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Architecture
|
|
162
|
+
|
|
163
|
+
<details>
|
|
164
|
+
<summary>View project structure</summary>
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
bin/
|
|
168
|
+
└── codepolisher.js # Executable entry point
|
|
169
|
+
src/
|
|
170
|
+
├── index.js # Commander program and commands
|
|
171
|
+
├── api.js # Provider adapters and response parsing
|
|
168
172
|
├── config.js # Provider detection and secure local config
|
|
169
173
|
├── prompt.js # Review and security prompt builders
|
|
174
|
+
├── comparison.js # Line alignment and safe side-by-side comparisons
|
|
170
175
|
├── display.js # Human-readable terminal output
|
|
171
|
-
└── commands/
|
|
172
|
-
├── review.js # File/stdin review workflow
|
|
173
|
-
└── config-cmd.js # Configuration commands
|
|
176
|
+
└── commands/
|
|
177
|
+
├── review.js # File/stdin review workflow
|
|
178
|
+
└── config-cmd.js # Configuration commands
|
|
174
179
|
test/
|
|
180
|
+
├── comparison.test.js # Comparison, normalization, and terminal safety tests
|
|
175
181
|
└── providers.test.js # Provider endpoint and environment tests
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
</details>
|
|
179
|
-
|
|
180
|
-
### Design principles
|
|
181
|
-
|
|
182
|
-
- 🔌 **Provider-independent** — use a cloud provider, an aggregator, or local Ollama
|
|
183
|
-
- 🔐 **Keys stay yours** — environment variables are first-class and saved keys are provider-specific
|
|
184
|
-
- 🧰 **Terminal-native** — review files, pipes, and CI jobs without a browser
|
|
185
|
-
- 🤖 **Automation-ready** — structured JSON and meaningful failure exit codes
|
|
186
|
-
- 🧩 **Extensible** — provider adapters and prompts are isolated by responsibility
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## Security
|
|
191
|
-
|
|
192
|
-
Saved configuration lives in `~/.codepolisher/config.json`. CodePolisher applies restrictive permissions on macOS and Linux and a private current-user ACL on Windows. It fails instead of silently saving when those protections cannot be applied.
|
|
193
|
-
|
|
194
|
-
For CI and shared machines, prefer provider environment variables and your platform's encrypted secret store.
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Development
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
# Install exact dependencies
|
|
202
|
-
npm ci
|
|
203
|
-
|
|
204
|
-
# Run the test suite
|
|
205
|
-
npm test
|
|
206
|
-
|
|
207
|
-
# Exercise the CLI locally
|
|
208
|
-
node bin/codepolisher.js --help
|
|
209
|
-
|
|
210
|
-
# Verify the package contents before publishing
|
|
211
|
-
npm pack --dry-run
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Tests run on Node.js 18, 20, and 22 through GitHub Actions.
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## License
|
|
219
|
-
|
|
220
|
-
[MIT](LICENSE) — use it however you want.
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
<div align="center">
|
|
225
|
-
|
|
226
|
-
<br/>
|
|
227
|
-
|
|
228
|
-
<img src="https://i.ibb.co/gFJwwVL4/ITSolutions-LOGO.jpg" alt="ImPerial TeK. Solutions" width="120" />
|
|
229
|
-
|
|
230
|
-
<br/>
|
|
231
|
-
|
|
232
|
-
**Bear Carrington**
|
|
233
|
-
|
|
234
|
-
*Founder | ImPerial TeK. Solutions (ITSolutions)*
|
|
235
|
-
|
|
236
|
-
📧 [ITSolutions_MGNT@proton.me](mailto:ITSolutions_MGNT@proton.me) · 🌐 [codepolisher.app](https://codepolisher.app)
|
|
237
|
-
|
|
238
|
-
<br/>
|
|
239
|
-
|
|
240
|
-
*Innovating technology with precision and integrity.*
|
|
241
|
-
|
|
242
|
-
© ImPerial TeK. Solutions — All Rights Reserved
|
|
243
|
-
|
|
244
|
-
<br/>
|
|
245
|
-
|
|
246
|
-
*If CodePolisher CLI improved your workflow, consider giving it a star ⭐*
|
|
247
|
-
|
|
248
|
-
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:a855f7,100:6366f1&height=80§ion=footer" width="100%" />
|
|
249
|
-
|
|
250
|
-
</div>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
</details>
|
|
185
|
+
|
|
186
|
+
### Design principles
|
|
187
|
+
|
|
188
|
+
- 🔌 **Provider-independent** — use a cloud provider, an aggregator, or local Ollama
|
|
189
|
+
- 🔐 **Keys stay yours** — environment variables are first-class and saved keys are provider-specific
|
|
190
|
+
- 🧰 **Terminal-native** — review files, pipes, and CI jobs without a browser
|
|
191
|
+
- 🤖 **Automation-ready** — structured JSON and meaningful failure exit codes
|
|
192
|
+
- 🧩 **Extensible** — provider adapters and prompts are isolated by responsibility
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Security
|
|
197
|
+
|
|
198
|
+
Saved configuration lives in `~/.codepolisher/config.json`. CodePolisher applies restrictive permissions on macOS and Linux and a private current-user ACL on Windows. It fails instead of silently saving when those protections cannot be applied.
|
|
199
|
+
|
|
200
|
+
For CI and shared machines, prefer provider environment variables and your platform's encrypted secret store.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Install exact dependencies
|
|
208
|
+
npm ci
|
|
209
|
+
|
|
210
|
+
# Run the test suite
|
|
211
|
+
npm test
|
|
212
|
+
|
|
213
|
+
# Exercise the CLI locally
|
|
214
|
+
node bin/codepolisher.js --help
|
|
215
|
+
|
|
216
|
+
# Verify the package contents before publishing
|
|
217
|
+
npm pack --dry-run
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Tests run on Node.js 18, 20, and 22 through GitHub Actions.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
[MIT](LICENSE) — use it however you want.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
<div align="center">
|
|
231
|
+
|
|
232
|
+
<br/>
|
|
233
|
+
|
|
234
|
+
<img src="https://i.ibb.co/gFJwwVL4/ITSolutions-LOGO.jpg" alt="ImPerial TeK. Solutions" width="120" />
|
|
235
|
+
|
|
236
|
+
<br/>
|
|
237
|
+
|
|
238
|
+
**Bear Carrington**
|
|
239
|
+
|
|
240
|
+
*Founder | ImPerial TeK. Solutions (ITSolutions)*
|
|
241
|
+
|
|
242
|
+
📧 [ITSolutions_MGNT@proton.me](mailto:ITSolutions_MGNT@proton.me) · 🌐 [codepolisher.app](https://codepolisher.app)
|
|
243
|
+
|
|
244
|
+
<br/>
|
|
245
|
+
|
|
246
|
+
*Innovating technology with precision and integrity.*
|
|
247
|
+
|
|
248
|
+
© ImPerial TeK. Solutions — All Rights Reserved
|
|
249
|
+
|
|
250
|
+
<br/>
|
|
251
|
+
|
|
252
|
+
*If CodePolisher CLI improved your workflow, consider giving it a star ⭐*
|
|
253
|
+
|
|
254
|
+
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:a855f7,100:6366f1&height=80§ion=footer" width="100%" />
|
|
255
|
+
|
|
256
|
+
</div>
|
package/package.json
CHANGED
package/src/commands/review.js
CHANGED
|
@@ -1,77 +1,87 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { basename } from 'path';
|
|
3
|
-
import ora from 'ora';
|
|
4
|
-
import { requireConfig } from '../config.js';
|
|
5
|
-
import { invokeLLM } from '../api.js';
|
|
6
|
-
import { buildReviewPrompt, buildSecurityPrompt } from '../prompt.js';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { basename } from 'path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { requireConfig } from '../config.js';
|
|
5
|
+
import { invokeLLM } from '../api.js';
|
|
6
|
+
import { buildReviewPrompt, buildSecurityPrompt } from '../prompt.js';
|
|
7
|
+
import { normalizeCorrectedCode } from '../comparison.js';
|
|
8
|
+
import {
|
|
9
|
+
printHeader,
|
|
10
|
+
printSummary,
|
|
11
|
+
printComments,
|
|
12
|
+
printComparison,
|
|
13
|
+
printFooter,
|
|
14
|
+
printError,
|
|
15
|
+
} from '../display.js';
|
|
16
|
+
|
|
17
|
+
export async function reviewCommand(filePath, options) {
|
|
18
|
+
const config = requireConfig();
|
|
19
|
+
|
|
20
|
+
// Read code
|
|
21
|
+
let code, filename;
|
|
22
|
+
if (filePath) {
|
|
23
|
+
try {
|
|
24
|
+
code = readFileSync(filePath, 'utf8');
|
|
25
|
+
filename = basename(filePath);
|
|
26
|
+
} catch {
|
|
27
|
+
printError(`Could not read file: ${filePath}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
// Read from stdin
|
|
32
|
+
code = readFileSync(0, 'utf8');
|
|
33
|
+
filename = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!code.trim()) {
|
|
37
|
+
printError('No code to review.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Build settings from flags
|
|
42
|
+
const settings = {
|
|
43
|
+
focusAreas: options.focus ? options.focus.split(',').map(s => s.trim()) : [],
|
|
44
|
+
customRules: options.rules || '',
|
|
45
|
+
strictMode: !!options.strict,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const prompt = options.security
|
|
49
|
+
? buildSecurityPrompt(code, options.language || 'auto', filename)
|
|
50
|
+
: buildReviewPrompt(code, options.language || 'auto', filename, settings);
|
|
51
|
+
|
|
52
|
+
const codeLines = code.split('\n').length;
|
|
53
|
+
|
|
54
|
+
const spinner = ora(`Reviewing with ${config.provider}…`).start();
|
|
55
|
+
|
|
56
|
+
let results;
|
|
57
|
+
try {
|
|
58
|
+
results = await invokeLLM(config, {
|
|
59
|
+
prompt,
|
|
60
|
+
codeLines,
|
|
61
|
+
});
|
|
62
|
+
spinner.stop();
|
|
63
|
+
} catch (err) {
|
|
64
|
+
spinner.stop();
|
|
65
|
+
printError(`Review failed: ${err.message || err}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// JSON output mode
|
|
70
|
+
if (options.output === 'json') {
|
|
71
|
+
console.log(JSON.stringify(results, null, 2));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
printHeader(filename, results.detected_language);
|
|
76
|
+
printSummary(results);
|
|
77
|
+
printComments(results.comments);
|
|
78
|
+
const correctedCode = normalizeCorrectedCode(results.corrected_code);
|
|
79
|
+
printComparison(code, correctedCode);
|
|
80
|
+
printFooter();
|
|
81
|
+
|
|
82
|
+
// Exit with non-zero if critical issues found
|
|
83
|
+
const hasCritical = results.comments?.some(c => c.severity === 'critical' || c.severity === 'security');
|
|
84
|
+
if (hasCritical && options.failOnCritical) {
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const MAX_LCS_CELLS = 2_000_000;
|
|
2
|
+
const ANSI_SEQUENCE = /\u001B(?:\][^\u0007]*(?:\u0007|\u001B\\)|\[[0-?]*[ -/]*[@-~]|[@-_])/g;
|
|
3
|
+
|
|
4
|
+
export function sanitizeTerminalText(value) {
|
|
5
|
+
return String(value ?? '')
|
|
6
|
+
.replace(ANSI_SEQUENCE, '')
|
|
7
|
+
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function normalizeCorrectedCode(value) {
|
|
11
|
+
const code = String(value ?? '');
|
|
12
|
+
const fenced = code.match(/^\s*```[^\n]*\n([\s\S]*?)\n```\s*$/);
|
|
13
|
+
return fenced ? fenced[1] : code;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function hasMeaningfulChanges(original, corrected) {
|
|
17
|
+
if (!String(corrected ?? '').trim()) return false;
|
|
18
|
+
const normalize = (value) => String(value ?? '').replace(/\r\n/g, '\n');
|
|
19
|
+
return normalize(original) !== normalize(corrected);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function splitLines(value) {
|
|
23
|
+
return sanitizeTerminalText(value).replace(/\r\n/g, '\n').split('\n');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function fallbackRows(left, right) {
|
|
27
|
+
const rows = [];
|
|
28
|
+
const length = Math.max(left.length, right.length);
|
|
29
|
+
for (let index = 0; index < length; index += 1) {
|
|
30
|
+
const before = index < left.length ? left[index] : null;
|
|
31
|
+
const after = index < right.length ? right[index] : null;
|
|
32
|
+
rows.push({
|
|
33
|
+
kind: before === after ? 'same' : before === null ? 'added' : after === null ? 'removed' : 'changed',
|
|
34
|
+
before: before === null ? null : { number: index + 1, text: before },
|
|
35
|
+
after: after === null ? null : { number: index + 1, text: after },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return rows;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function compareLines(original, corrected) {
|
|
42
|
+
const left = splitLines(original);
|
|
43
|
+
const right = splitLines(corrected);
|
|
44
|
+
if (left.length * right.length > MAX_LCS_CELLS) return fallbackRows(left, right);
|
|
45
|
+
|
|
46
|
+
const width = right.length + 1;
|
|
47
|
+
const table = new Uint32Array((left.length + 1) * width);
|
|
48
|
+
for (let i = left.length - 1; i >= 0; i -= 1) {
|
|
49
|
+
for (let j = right.length - 1; j >= 0; j -= 1) {
|
|
50
|
+
table[(i * width) + j] = left[i] === right[j]
|
|
51
|
+
? table[((i + 1) * width) + j + 1] + 1
|
|
52
|
+
: Math.max(table[((i + 1) * width) + j], table[(i * width) + j + 1]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const operations = [];
|
|
57
|
+
let i = 0;
|
|
58
|
+
let j = 0;
|
|
59
|
+
while (i < left.length && j < right.length) {
|
|
60
|
+
if (left[i] === right[j]) {
|
|
61
|
+
operations.push({ kind: 'same', number: i + 1, otherNumber: j + 1, text: left[i] });
|
|
62
|
+
i += 1;
|
|
63
|
+
j += 1;
|
|
64
|
+
} else if (table[((i + 1) * width) + j] >= table[(i * width) + j + 1]) {
|
|
65
|
+
operations.push({ kind: 'removed', number: i + 1, text: left[i] });
|
|
66
|
+
i += 1;
|
|
67
|
+
} else {
|
|
68
|
+
operations.push({ kind: 'added', number: j + 1, text: right[j] });
|
|
69
|
+
j += 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
while (i < left.length) operations.push({ kind: 'removed', number: ++i, text: left[i - 1] });
|
|
73
|
+
while (j < right.length) operations.push({ kind: 'added', number: ++j, text: right[j - 1] });
|
|
74
|
+
|
|
75
|
+
const rows = [];
|
|
76
|
+
for (let cursor = 0; cursor < operations.length;) {
|
|
77
|
+
const operation = operations[cursor];
|
|
78
|
+
if (operation.kind === 'same') {
|
|
79
|
+
rows.push({
|
|
80
|
+
kind: 'same',
|
|
81
|
+
before: { number: operation.number, text: operation.text },
|
|
82
|
+
after: { number: operation.otherNumber, text: operation.text },
|
|
83
|
+
});
|
|
84
|
+
cursor += 1;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const removed = [];
|
|
89
|
+
const added = [];
|
|
90
|
+
while (cursor < operations.length && operations[cursor].kind !== 'same') {
|
|
91
|
+
const change = operations[cursor];
|
|
92
|
+
(change.kind === 'removed' ? removed : added).push(change);
|
|
93
|
+
cursor += 1;
|
|
94
|
+
}
|
|
95
|
+
const changeLength = Math.max(removed.length, added.length);
|
|
96
|
+
for (let changeIndex = 0; changeIndex < changeLength; changeIndex += 1) {
|
|
97
|
+
const before = removed[changeIndex] || null;
|
|
98
|
+
const after = added[changeIndex] || null;
|
|
99
|
+
rows.push({
|
|
100
|
+
kind: before && after ? 'changed' : before ? 'removed' : 'added',
|
|
101
|
+
before: before ? { number: before.number, text: before.text } : null,
|
|
102
|
+
after: after ? { number: after.number, text: after.text } : null,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return rows;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function changedHunks(rows, context = 2) {
|
|
110
|
+
const selected = new Set();
|
|
111
|
+
rows.forEach((row, index) => {
|
|
112
|
+
if (row.kind === 'same') return;
|
|
113
|
+
for (let nearby = Math.max(0, index - context); nearby <= Math.min(rows.length - 1, index + context); nearby += 1) {
|
|
114
|
+
selected.add(nearby);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const output = [];
|
|
119
|
+
let previous = -2;
|
|
120
|
+
for (const index of [...selected].sort((a, b) => a - b)) {
|
|
121
|
+
if (index > previous + 1) output.push({ kind: 'separator', before: null, after: null });
|
|
122
|
+
output.push(rows[index]);
|
|
123
|
+
previous = index;
|
|
124
|
+
}
|
|
125
|
+
return output;
|
|
126
|
+
}
|
package/src/display.js
CHANGED
|
@@ -1,86 +1,151 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
console.log(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import {
|
|
3
|
+
compareLines,
|
|
4
|
+
sanitizeTerminalText,
|
|
5
|
+
} from './comparison.js';
|
|
6
|
+
|
|
7
|
+
const SEVERITY_COLOR = {
|
|
8
|
+
critical: (s) => chalk.bgRed.white.bold(` ${s.toUpperCase()} `),
|
|
9
|
+
security: (s) => chalk.bgMagenta.white.bold(` ${s.toUpperCase()} `),
|
|
10
|
+
warning: (s) => chalk.bgYellow.black.bold(` ${s.toUpperCase()} `),
|
|
11
|
+
suggestion: (s) => chalk.bgBlue.white(` ${s.toUpperCase()} `),
|
|
12
|
+
performance: (s) => chalk.bgCyan.black(` ${s.toUpperCase()} `),
|
|
13
|
+
good: (s) => chalk.bgGreen.black(` ${s.toUpperCase()} `),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function badge(severity) {
|
|
17
|
+
const normalized = typeof severity === 'string' && severity ? severity : 'unknown';
|
|
18
|
+
const fn = SEVERITY_COLOR[normalized] || ((s) => chalk.bgGray.white(` ${s.toUpperCase()} `));
|
|
19
|
+
return fn(normalized);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function printHeader(filename, language) {
|
|
23
|
+
const lang = language ? chalk.cyan(sanitizeTerminalText(language)) : '';
|
|
24
|
+
const file = chalk.bold(sanitizeTerminalText(filename || 'stdin'));
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
27
|
+
console.log(` ${chalk.bold.white('CodePolisher')} ${file} ${lang}`);
|
|
28
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function printSummary(results) {
|
|
32
|
+
const { summary, correction_summary: correctionSummary } = results;
|
|
33
|
+
const comments = Array.isArray(results.comments) ? results.comments : [];
|
|
34
|
+
|
|
35
|
+
const counts = {
|
|
36
|
+
critical: comments.filter(c => c.severity === 'critical').length,
|
|
37
|
+
security: comments.filter(c => c.severity === 'security').length,
|
|
38
|
+
warning: comments.filter(c => c.severity === 'warning').length,
|
|
39
|
+
suggestion: comments.filter(c => c.severity === 'suggestion' || c.severity === 'performance').length,
|
|
40
|
+
good: comments.filter(c => c.severity === 'good').length,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
console.log('');
|
|
44
|
+
console.log(
|
|
45
|
+
` ${chalk.red.bold(counts.critical)} critical ` +
|
|
46
|
+
`${chalk.magenta.bold(counts.security)} security ` +
|
|
47
|
+
`${chalk.yellow.bold(counts.warning)} warnings ` +
|
|
48
|
+
`${chalk.blue.bold(counts.suggestion)} suggestions ` +
|
|
49
|
+
`${chalk.green.bold(counts.good)} good`
|
|
50
|
+
);
|
|
51
|
+
console.log('');
|
|
52
|
+
|
|
53
|
+
if (summary) {
|
|
54
|
+
console.log(chalk.dim(' Summary'));
|
|
55
|
+
console.log(` ${chalk.white(sanitizeTerminalText(summary))}`);
|
|
56
|
+
console.log('');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (correctionSummary) {
|
|
60
|
+
console.log(chalk.dim(' Correction approach'));
|
|
61
|
+
console.log(` ${chalk.white(sanitizeTerminalText(correctionSummary))}`);
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function printComments(comments = []) {
|
|
67
|
+
if (!Array.isArray(comments)) comments = [];
|
|
68
|
+
if (!comments.length) {
|
|
69
|
+
console.log(chalk.green(' No issues found.'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const order = ['critical', 'security', 'warning', 'performance', 'suggestion', 'good'];
|
|
74
|
+
const sorted = [...comments].sort((a, b) => {
|
|
75
|
+
return order.indexOf(a.severity) - order.indexOf(b.severity);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(chalk.dim(' Issues'));
|
|
79
|
+
console.log('');
|
|
80
|
+
|
|
81
|
+
for (const c of sorted) {
|
|
82
|
+
const line = c.line ? chalk.dim(`line ${c.line}`) : chalk.dim('general');
|
|
83
|
+
console.log(` ${badge(c.severity)} ${line}`);
|
|
84
|
+
console.log(` ${chalk.white(sanitizeTerminalText(c.message))}`);
|
|
85
|
+
if (c.impact) {
|
|
86
|
+
console.log(` ${chalk.dim('why it matters:')} ${chalk.white(sanitizeTerminalText(c.impact))}`);
|
|
87
|
+
}
|
|
88
|
+
if (c.fix) {
|
|
89
|
+
console.log(` ${chalk.dim('recommended change:')} ${chalk.cyan(sanitizeTerminalText(c.fix))}`);
|
|
90
|
+
}
|
|
91
|
+
console.log('');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function truncate(value, width) {
|
|
96
|
+
if (value.length <= width) return value.padEnd(width);
|
|
97
|
+
if (width <= 1) return value.slice(0, width);
|
|
98
|
+
return `${value.slice(0, width - 1)}…`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function comparisonCell(line, marker, width) {
|
|
102
|
+
if (!line) return ' '.repeat(width);
|
|
103
|
+
const prefix = `${String(line.number).padStart(4)} ${marker} `;
|
|
104
|
+
return prefix + truncate(line.text.replace(/\t/g, ' '), Math.max(1, width - prefix.length));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function printComparison(original, corrected, columns = process.stdout.columns || 120) {
|
|
108
|
+
if (!String(corrected ?? '').trim()) {
|
|
109
|
+
console.log(chalk.yellow(' The provider did not return a corrected script.'));
|
|
110
|
+
console.log('');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const rows = compareLines(original, corrected);
|
|
115
|
+
const visibleRows = rows.length <= 400
|
|
116
|
+
? rows
|
|
117
|
+
: [
|
|
118
|
+
...rows.slice(0, 200),
|
|
119
|
+
{ kind: 'separator', before: null, after: null, omitted: rows.length - 400 },
|
|
120
|
+
...rows.slice(-200),
|
|
121
|
+
];
|
|
122
|
+
const displayWidth = Math.min(Math.max(columns, 80), 200);
|
|
123
|
+
const cellWidth = Math.max(36, Math.floor((displayWidth - 5) / 2));
|
|
124
|
+
console.log(chalk.dim(' Side-by-side comparison (original vs. AI-suggested correction)'));
|
|
125
|
+
console.log(` ${chalk.bold(truncate('ORIGINAL', cellWidth))} | ${chalk.bold(truncate('CORRECTED', cellWidth))}`);
|
|
126
|
+
console.log(` ${'-'.repeat(cellWidth)}--+--${'-'.repeat(cellWidth)}`);
|
|
127
|
+
|
|
128
|
+
for (const row of visibleRows) {
|
|
129
|
+
if (row.kind === 'separator') {
|
|
130
|
+
const label = row.omitted ? `… ${row.omitted} comparison rows omitted …` : '…';
|
|
131
|
+
console.log(chalk.dim(` ${truncate(label, cellWidth)} | ${truncate(label, cellWidth)}`));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const left = comparisonCell(row.before, row.kind === 'same' ? ' ' : '-', cellWidth);
|
|
135
|
+
const right = comparisonCell(row.after, row.kind === 'same' ? ' ' : '+', cellWidth);
|
|
136
|
+
const color = row.kind === 'same' ? chalk.dim : row.kind === 'changed' ? chalk.yellow : chalk.cyan;
|
|
137
|
+
console.log(` ${color(left)} | ${color(right)}`);
|
|
138
|
+
}
|
|
139
|
+
console.log('');
|
|
140
|
+
console.log(chalk.dim(' Review this suggestion before using it in production.'));
|
|
141
|
+
console.log('');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function printFooter() {
|
|
145
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
146
|
+
console.log('');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function printError(msg) {
|
|
150
|
+
console.error(`\n${chalk.red.bold('Error:')} ${sanitizeTerminalText(msg)}\n`);
|
|
151
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { reviewCommand } from './commands/review.js';
|
|
3
|
-
import { configCommand } from './commands/config-cmd.js';
|
|
4
|
-
|
|
5
|
-
const program = new Command();
|
|
6
|
-
|
|
7
|
-
program
|
|
8
|
-
.name('codepolisher')
|
|
9
|
-
.description('AI-powered code review in your terminal — by ImPerial TeK. Solutions')
|
|
10
|
-
.version('1.
|
|
11
|
-
|
|
12
|
-
// review command
|
|
13
|
-
program
|
|
14
|
-
.command('review [file]')
|
|
15
|
-
.description('Review a file (or pipe code via stdin)')
|
|
16
|
-
.option('-l, --language <lang>', 'Language hint (default: auto-detect)')
|
|
17
|
-
.option('-f, --focus <areas>', 'Comma-separated focus areas: security,performance,readability,error_handling,best_practices,testing')
|
|
18
|
-
.option('-r, --rules <text>', 'Custom review rules')
|
|
19
|
-
.option('--strict', 'Strict mode — flag every issue')
|
|
20
|
-
.option('--security', 'Run a full security audit')
|
|
21
|
-
.option('--output <format>', 'Output format: pretty (default) or json')
|
|
22
|
-
.option('--fail-on-critical', 'Exit code 1 if critical/security issues found')
|
|
23
|
-
.action(reviewCommand);
|
|
24
|
-
|
|
25
|
-
// config command
|
|
26
|
-
configCommand(program);
|
|
27
|
-
|
|
28
|
-
program.parse(process.argv);
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { reviewCommand } from './commands/review.js';
|
|
3
|
+
import { configCommand } from './commands/config-cmd.js';
|
|
4
|
+
|
|
5
|
+
const program = new Command();
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name('codepolisher')
|
|
9
|
+
.description('AI-powered code review in your terminal — by ImPerial TeK. Solutions')
|
|
10
|
+
.version('1.2.0');
|
|
11
|
+
|
|
12
|
+
// review command
|
|
13
|
+
program
|
|
14
|
+
.command('review [file]')
|
|
15
|
+
.description('Review a file (or pipe code via stdin)')
|
|
16
|
+
.option('-l, --language <lang>', 'Language hint (default: auto-detect)')
|
|
17
|
+
.option('-f, --focus <areas>', 'Comma-separated focus areas: security,performance,readability,error_handling,best_practices,testing')
|
|
18
|
+
.option('-r, --rules <text>', 'Custom review rules')
|
|
19
|
+
.option('--strict', 'Strict mode — flag every issue')
|
|
20
|
+
.option('--security', 'Run a full security audit')
|
|
21
|
+
.option('--output <format>', 'Output format: pretty (default) or json')
|
|
22
|
+
.option('--fail-on-critical', 'Exit code 1 if critical/security issues found')
|
|
23
|
+
.action(reviewCommand);
|
|
24
|
+
|
|
25
|
+
// config command
|
|
26
|
+
configCommand(program);
|
|
27
|
+
|
|
28
|
+
program.parse(process.argv);
|
package/src/prompt.js
CHANGED
|
@@ -2,16 +2,18 @@ export const REVIEW_SCHEMA = {
|
|
|
2
2
|
type: "object",
|
|
3
3
|
properties: {
|
|
4
4
|
detected_language: { type: "string" },
|
|
5
|
-
summary: { type: "string" },
|
|
5
|
+
summary: { type: "string" },
|
|
6
|
+
correction_summary: { type: "string" },
|
|
6
7
|
comments: {
|
|
7
8
|
type: "array",
|
|
8
9
|
items: {
|
|
9
10
|
type: "object",
|
|
10
11
|
properties: {
|
|
11
12
|
severity: { type: "string" },
|
|
12
|
-
line: { type: "number" },
|
|
13
|
-
message: { type: "string" },
|
|
14
|
-
|
|
13
|
+
line: { type: ["number", "null"] },
|
|
14
|
+
message: { type: "string" },
|
|
15
|
+
impact: { type: ["string", "null"] },
|
|
16
|
+
fix: { type: ["string", "null"] },
|
|
15
17
|
},
|
|
16
18
|
},
|
|
17
19
|
},
|
|
@@ -66,14 +68,16 @@ ${code}
|
|
|
66
68
|
|
|
67
69
|
Provide your review as a JSON object with this exact structure:
|
|
68
70
|
- "detected_language": the language you detected (string)
|
|
69
|
-
- "summary": a 2-3 sentence overall assessment of the code quality
|
|
70
|
-
- "
|
|
71
|
+
- "summary": a 2-3 sentence overall assessment of the code quality
|
|
72
|
+
- "correction_summary": a 2-4 sentence explanation of the correction strategy, including important behavior changes or tradeoffs
|
|
73
|
+
- "comments": an array of review comments, each with:
|
|
71
74
|
- "severity": one of "critical", "warning", "suggestion", "performance", "security", "good"
|
|
72
75
|
- "line": the line number (number or null if general)
|
|
73
|
-
- "message": a
|
|
74
|
-
- "
|
|
75
|
-
- "
|
|
76
|
-
|
|
76
|
+
- "message": a detailed, plain-language explanation of the issue or suggestion
|
|
77
|
+
- "impact": why the issue matters in practice (string or null)
|
|
78
|
+
- "fix": a concrete code fix or recommendation (string or null)
|
|
79
|
+
- "corrected_code": the complete, runnable corrected script with all fixes applied; return code only, without Markdown fences`;
|
|
80
|
+
}
|
|
77
81
|
|
|
78
82
|
export function buildSecurityPrompt(code, language = "auto", filename = null) {
|
|
79
83
|
return buildReviewPrompt(code, language, filename, {
|