ghagga 2.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 +171 -0
- package/dist/commands/login.d.ts +8 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +73 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +5 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +16 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/review.d.ts +22 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +269 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/review.test.d.ts +9 -0
- package/dist/commands/review.test.d.ts.map +1 -0
- package/dist/commands/review.test.js +161 -0
- package/dist/commands/review.test.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +33 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +41 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +73 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/oauth.d.ts +55 -0
- package/dist/lib/oauth.d.ts.map +1 -0
- package/dist/lib/oauth.js +110 -0
- package/dist/lib/oauth.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# GHAGGA CLI
|
|
2
|
+
|
|
3
|
+
AI-powered code review from the command line. **Free** with GitHub Models.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @ghagga/cli login
|
|
7
|
+
npx @ghagga/cli review
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
That's it. Zero config, zero cost.
|
|
11
|
+
|
|
12
|
+
## What is GHAGGA?
|
|
13
|
+
|
|
14
|
+
GHAGGA is a multi-agent AI code reviewer that analyzes your code changes using LLMs. It supports three review modes with increasing depth:
|
|
15
|
+
|
|
16
|
+
| Mode | Speed | Depth | LLM Calls |
|
|
17
|
+
|------|-------|-------|-----------|
|
|
18
|
+
| **simple** | ~2s | Single-pass review | 1 |
|
|
19
|
+
| **workflow** | ~15s | 5 specialist agents + synthesis | 6 |
|
|
20
|
+
| **consensus** | ~7s | For/against/neutral voting | 3 |
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Login (one time)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @ghagga/cli login
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This authenticates with GitHub using Device Flow. Your GitHub token gives you **free access** to AI models via [GitHub Models](https://github.com/marketplace/models).
|
|
31
|
+
|
|
32
|
+
### 2. Review your code
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Review staged/uncommitted changes (simple mode)
|
|
36
|
+
npx @ghagga/cli review
|
|
37
|
+
|
|
38
|
+
# Thorough review with 5 specialist agents
|
|
39
|
+
npx @ghagga/cli review --mode workflow
|
|
40
|
+
|
|
41
|
+
# Balanced review with for/against/neutral voting
|
|
42
|
+
npx @ghagga/cli review --mode consensus
|
|
43
|
+
|
|
44
|
+
# See detailed progress of each step
|
|
45
|
+
npx @ghagga/cli review --mode workflow --verbose
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. Check your status
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx @ghagga/cli status # Show auth & config
|
|
52
|
+
npx @ghagga/cli logout # Clear credentials
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Global Installation
|
|
56
|
+
|
|
57
|
+
If you use it frequently:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install -g @ghagga/cli
|
|
61
|
+
|
|
62
|
+
ghagga login
|
|
63
|
+
ghagga review
|
|
64
|
+
ghagga review -m workflow -v
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Options
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Usage: ghagga review [options] [path]
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
-m, --mode <mode> Review mode: simple, workflow, consensus (default: "simple")
|
|
74
|
+
-p, --provider <provider> LLM provider: github, openai, anthropic, google
|
|
75
|
+
--model <model> LLM model identifier
|
|
76
|
+
--api-key <key> LLM provider API key
|
|
77
|
+
-f, --format <format> Output format: markdown, json (default: "markdown")
|
|
78
|
+
-v, --verbose Show detailed progress during review
|
|
79
|
+
--no-semgrep Disable Semgrep static analysis
|
|
80
|
+
--no-trivy Disable Trivy vulnerability scanning
|
|
81
|
+
--no-cpd Disable CPD duplicate detection
|
|
82
|
+
-c, --config <path> Path to .ghagga.json config file
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## BYOK (Bring Your Own Key)
|
|
86
|
+
|
|
87
|
+
Use any supported LLM provider:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# GitHub Models (default, free)
|
|
91
|
+
ghagga review --provider github
|
|
92
|
+
|
|
93
|
+
# Ollama (local, free, 100% offline)
|
|
94
|
+
ghagga review --provider ollama
|
|
95
|
+
ghagga review --provider ollama --model codellama:13b
|
|
96
|
+
|
|
97
|
+
# OpenAI
|
|
98
|
+
ghagga review --provider openai --api-key sk-...
|
|
99
|
+
|
|
100
|
+
# Anthropic
|
|
101
|
+
ghagga review --provider anthropic --api-key sk-ant-...
|
|
102
|
+
|
|
103
|
+
# Google
|
|
104
|
+
ghagga review --provider google --api-key AIza...
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Local Models with Ollama
|
|
108
|
+
|
|
109
|
+
Run reviews 100% offline with [Ollama](https://ollama.com):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# 1. Install Ollama and pull a model
|
|
113
|
+
ollama pull qwen2.5-coder:7b
|
|
114
|
+
|
|
115
|
+
# 2. Review with local AI (no API key, no internet needed)
|
|
116
|
+
ghagga review --provider ollama
|
|
117
|
+
|
|
118
|
+
# Recommended models for code review:
|
|
119
|
+
# qwen2.5-coder:7b (~5 GB RAM, great for code)
|
|
120
|
+
# codellama:13b (~8 GB RAM, solid analysis)
|
|
121
|
+
# deepseek-coder-v2:16b (~10 GB RAM, excellent quality)
|
|
122
|
+
# llama3.1:8b (~5 GB RAM, general purpose)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Environment Variables
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
GHAGGA_API_KEY=<key> # API key for the LLM provider
|
|
129
|
+
GHAGGA_PROVIDER=<provider> # LLM provider override
|
|
130
|
+
GHAGGA_MODEL=<model> # Model identifier override
|
|
131
|
+
GITHUB_TOKEN=<token> # GitHub token (fallback for github provider)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Config File
|
|
135
|
+
|
|
136
|
+
Create a `.ghagga.json` in your project root:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"mode": "workflow",
|
|
141
|
+
"enableSemgrep": true,
|
|
142
|
+
"enableTrivy": true,
|
|
143
|
+
"enableCpd": true,
|
|
144
|
+
"ignorePatterns": ["*.test.ts", "*.spec.ts"],
|
|
145
|
+
"reviewLevel": "strict"
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## How It Works
|
|
150
|
+
|
|
151
|
+
1. Gets your `git diff` (staged or uncommitted changes)
|
|
152
|
+
2. Parses the diff and detects tech stacks
|
|
153
|
+
3. Runs static analysis (Semgrep, Trivy, CPD) if available
|
|
154
|
+
4. Sends the diff + context to the AI review agent
|
|
155
|
+
5. Returns findings with severity, file, line, and suggestions
|
|
156
|
+
|
|
157
|
+
## Requirements
|
|
158
|
+
|
|
159
|
+
- Node.js >= 20
|
|
160
|
+
- Git (for diff detection)
|
|
161
|
+
- A GitHub account (for free AI models)
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
|
166
|
+
|
|
167
|
+
## Links
|
|
168
|
+
|
|
169
|
+
- [GitHub Repository](https://github.com/JNZader/ghagga)
|
|
170
|
+
- [Documentation](https://jnzader.github.io/ghagga/docs/)
|
|
171
|
+
- [Landing Page](https://jnzader.github.io/ghagga/)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login command — authenticates with GitHub via Device Flow.
|
|
3
|
+
*
|
|
4
|
+
* Opens the browser to github.com/login/device, shows the user code,
|
|
5
|
+
* and polls until the user authorizes. Saves the token to config.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loginCommand(): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgCH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAwDlD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login command — authenticates with GitHub via Device Flow.
|
|
3
|
+
*
|
|
4
|
+
* Opens the browser to github.com/login/device, shows the user code,
|
|
5
|
+
* and polls until the user authorizes. Saves the token to config.
|
|
6
|
+
*/
|
|
7
|
+
import { loadConfig, saveConfig } from '../lib/config.js';
|
|
8
|
+
import { requestDeviceCode, pollForAccessToken, fetchGitHubUser, } from '../lib/oauth.js';
|
|
9
|
+
/**
|
|
10
|
+
* Try to open a URL in the default browser.
|
|
11
|
+
* Fails silently if no browser is available (e.g., headless server).
|
|
12
|
+
*/
|
|
13
|
+
async function tryOpenBrowser(url) {
|
|
14
|
+
try {
|
|
15
|
+
const { exec } = await import('node:child_process');
|
|
16
|
+
const { platform } = await import('node:os');
|
|
17
|
+
const cmd = platform() === 'darwin'
|
|
18
|
+
? `open "${url}"`
|
|
19
|
+
: platform() === 'win32'
|
|
20
|
+
? `start "${url}"`
|
|
21
|
+
: `xdg-open "${url}"`;
|
|
22
|
+
exec(cmd);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function loginCommand() {
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
// Check if already logged in
|
|
32
|
+
if (config.githubToken && config.githubLogin) {
|
|
33
|
+
console.log(`\u2139\ufe0f Already logged in as ${config.githubLogin}.`);
|
|
34
|
+
console.log(' Run "ghagga logout" first to switch accounts.\n');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log('\ud83d\udd10 Authenticating with GitHub...\n');
|
|
38
|
+
try {
|
|
39
|
+
// Step 1: Request device code
|
|
40
|
+
const deviceCode = await requestDeviceCode();
|
|
41
|
+
// Step 2: Show user code and open browser
|
|
42
|
+
console.log(' \u2794 Open this URL in your browser:\n');
|
|
43
|
+
console.log(` \x1b[1m\x1b[36mhttps://github.com/login/device\x1b[0m\n`);
|
|
44
|
+
console.log(` \u2794 Enter this code:\n`);
|
|
45
|
+
console.log(` \x1b[1m\x1b[33m${deviceCode.user_code}\x1b[0m\n`);
|
|
46
|
+
const opened = await tryOpenBrowser(deviceCode.verification_uri);
|
|
47
|
+
if (opened) {
|
|
48
|
+
console.log(' (Browser opened automatically)\n');
|
|
49
|
+
}
|
|
50
|
+
console.log(' Waiting for authorization...');
|
|
51
|
+
// Step 3: Poll for access token
|
|
52
|
+
const tokenResponse = await pollForAccessToken(deviceCode.device_code, deviceCode.interval, deviceCode.expires_in);
|
|
53
|
+
// Step 4: Fetch user profile
|
|
54
|
+
const user = await fetchGitHubUser(tokenResponse.access_token);
|
|
55
|
+
// Step 5: Save to config
|
|
56
|
+
saveConfig({
|
|
57
|
+
...config,
|
|
58
|
+
githubToken: tokenResponse.access_token,
|
|
59
|
+
githubLogin: user.login,
|
|
60
|
+
defaultProvider: 'github',
|
|
61
|
+
defaultModel: 'gpt-4o-mini',
|
|
62
|
+
});
|
|
63
|
+
console.log(`\n\u2705 Logged in as \x1b[1m${user.login}\x1b[0m`);
|
|
64
|
+
console.log(' Provider: github (gpt-4o-mini) — free tier');
|
|
65
|
+
console.log('\n Run "ghagga review ." to review your code!\n');
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
console.error(`\n\u274c Login failed: ${message}\n`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,aAAa,IAAI,IAAI,CAapC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logout command — clears stored GitHub credentials.
|
|
3
|
+
*/
|
|
4
|
+
import { clearConfig, isLoggedIn, loadConfig } from '../lib/config.js';
|
|
5
|
+
export function logoutCommand() {
|
|
6
|
+
if (!isLoggedIn()) {
|
|
7
|
+
console.log('\u2139\ufe0f Not currently logged in.\n');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const config = loadConfig();
|
|
11
|
+
const login = config.githubLogin ?? 'unknown';
|
|
12
|
+
clearConfig();
|
|
13
|
+
console.log(`\u2705 Logged out from ${login}.`);
|
|
14
|
+
console.log(' Stored credentials have been removed.\n');
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,IAAI,SAAS,CAAC;IAE9C,WAAW,EAAE,CAAC;IAEd,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,GAAG,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review command handler.
|
|
3
|
+
*
|
|
4
|
+
* Gets the local git diff, merges configuration from CLI options,
|
|
5
|
+
* environment, and optional .ghagga.json file, then runs the
|
|
6
|
+
* core review pipeline and formats the output.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReviewMode, LLMProvider } from 'ghagga-core';
|
|
9
|
+
export interface ReviewOptions {
|
|
10
|
+
mode: ReviewMode;
|
|
11
|
+
provider: LLMProvider;
|
|
12
|
+
model: string;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
format: 'markdown' | 'json';
|
|
15
|
+
semgrep: boolean;
|
|
16
|
+
trivy: boolean;
|
|
17
|
+
cpd: boolean;
|
|
18
|
+
config?: string;
|
|
19
|
+
verbose: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function reviewCommand(targetPath: string, options: ReviewOptions): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EAOZ,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAgBD,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CA4Df"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review command handler.
|
|
3
|
+
*
|
|
4
|
+
* Gets the local git diff, merges configuration from CLI options,
|
|
5
|
+
* environment, and optional .ghagga.json file, then runs the
|
|
6
|
+
* core review pipeline and formats the output.
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
10
|
+
import { resolve, join } from 'node:path';
|
|
11
|
+
import { reviewPipeline, DEFAULT_SETTINGS, } from 'ghagga-core';
|
|
12
|
+
// ─── Main Command ───────────────────────────────────────────────
|
|
13
|
+
export async function reviewCommand(targetPath, options) {
|
|
14
|
+
const repoPath = resolve(targetPath);
|
|
15
|
+
try {
|
|
16
|
+
// Step 1: Get the git diff
|
|
17
|
+
const diff = getGitDiff(repoPath);
|
|
18
|
+
if (!diff || diff.trim().length === 0) {
|
|
19
|
+
console.log('\u2139\ufe0f No changes detected. Stage some changes or make commits to review.');
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
// Step 2: Load optional config file
|
|
23
|
+
const fileConfig = loadConfigFile(repoPath, options.config);
|
|
24
|
+
// Step 3: Merge settings (CLI options take priority over config file)
|
|
25
|
+
const settings = mergeSettings(options, fileConfig);
|
|
26
|
+
// Step 4: Show progress
|
|
27
|
+
console.log('\ud83e\udd16 GHAGGA Code Review');
|
|
28
|
+
console.log(` Mode: ${options.mode} | Provider: ${options.provider} | Model: ${options.model}`);
|
|
29
|
+
console.log(' Analyzing...\n');
|
|
30
|
+
// Step 5: Run the review pipeline
|
|
31
|
+
const onProgress = options.verbose
|
|
32
|
+
? createProgressHandler()
|
|
33
|
+
: undefined;
|
|
34
|
+
const result = await reviewPipeline({
|
|
35
|
+
diff,
|
|
36
|
+
mode: options.mode,
|
|
37
|
+
provider: options.provider,
|
|
38
|
+
model: options.model,
|
|
39
|
+
apiKey: options.apiKey,
|
|
40
|
+
settings,
|
|
41
|
+
context: {
|
|
42
|
+
repoFullName: 'local/review',
|
|
43
|
+
prNumber: 0,
|
|
44
|
+
commitMessages: [],
|
|
45
|
+
fileList: [],
|
|
46
|
+
},
|
|
47
|
+
db: undefined,
|
|
48
|
+
onProgress,
|
|
49
|
+
});
|
|
50
|
+
// Step 6: Output the result
|
|
51
|
+
if (options.format === 'json') {
|
|
52
|
+
console.log(JSON.stringify(result, null, 2));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log(formatMarkdownResult(result));
|
|
56
|
+
}
|
|
57
|
+
// Step 7: Exit code based on status
|
|
58
|
+
const exitCode = getExitCode(result.status);
|
|
59
|
+
process.exit(exitCode);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
63
|
+
console.error(`\n\u274c Review failed: ${message}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ─── Git Diff ───────────────────────────────────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* Get the diff from git. Uses staged changes if available,
|
|
70
|
+
* otherwise falls back to `git diff HEAD`.
|
|
71
|
+
*/
|
|
72
|
+
function getGitDiff(repoPath) {
|
|
73
|
+
const execOpts = { cwd: repoPath, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 };
|
|
74
|
+
// Check for staged changes first
|
|
75
|
+
try {
|
|
76
|
+
const staged = execSync('git diff --staged', execOpts).toString();
|
|
77
|
+
if (staged.trim().length > 0) {
|
|
78
|
+
return staged;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// git diff --staged failed, try HEAD
|
|
83
|
+
}
|
|
84
|
+
// Fall back to diff against HEAD
|
|
85
|
+
try {
|
|
86
|
+
const headDiff = execSync('git diff HEAD', execOpts).toString();
|
|
87
|
+
if (headDiff.trim().length > 0) {
|
|
88
|
+
return headDiff;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// HEAD might not exist (fresh repo), try unstaged diff
|
|
93
|
+
}
|
|
94
|
+
// Last resort: unstaged diff
|
|
95
|
+
try {
|
|
96
|
+
return execSync('git diff', execOpts).toString();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
throw new Error(`Could not get git diff from "${repoPath}". ` +
|
|
100
|
+
'Make sure the path is a git repository with changes.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// ─── Config File ────────────────────────────────────────────────
|
|
104
|
+
/**
|
|
105
|
+
* Load and parse an optional .ghagga.json config file.
|
|
106
|
+
*/
|
|
107
|
+
function loadConfigFile(repoPath, configPath) {
|
|
108
|
+
const filePath = configPath
|
|
109
|
+
? resolve(configPath)
|
|
110
|
+
: join(repoPath, '.ghagga.json');
|
|
111
|
+
if (!existsSync(filePath)) {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
116
|
+
return JSON.parse(raw);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
console.warn(`\u26a0\ufe0f Could not parse config file: ${message}`);
|
|
121
|
+
return {};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ─── Settings Merge ─────────────────────────────────────────────
|
|
125
|
+
/**
|
|
126
|
+
* Merge CLI options, config file, and defaults.
|
|
127
|
+
* Priority: CLI options > config file > defaults.
|
|
128
|
+
*/
|
|
129
|
+
function mergeSettings(options, fileConfig) {
|
|
130
|
+
return {
|
|
131
|
+
enableSemgrep: options.semgrep ?? fileConfig.enableSemgrep ?? DEFAULT_SETTINGS.enableSemgrep,
|
|
132
|
+
enableTrivy: options.trivy ?? fileConfig.enableTrivy ?? DEFAULT_SETTINGS.enableTrivy,
|
|
133
|
+
enableCpd: options.cpd ?? fileConfig.enableCpd ?? DEFAULT_SETTINGS.enableCpd,
|
|
134
|
+
enableMemory: false, // Memory is disabled in CLI mode
|
|
135
|
+
customRules: fileConfig.customRules ?? DEFAULT_SETTINGS.customRules,
|
|
136
|
+
ignorePatterns: fileConfig.ignorePatterns ?? DEFAULT_SETTINGS.ignorePatterns,
|
|
137
|
+
reviewLevel: fileConfig.reviewLevel ?? DEFAULT_SETTINGS.reviewLevel,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// ─── Verbose Progress ───────────────────────────────────────────
|
|
141
|
+
/** Step-to-emoji mapping for verbose output. */
|
|
142
|
+
const STEP_ICON = {
|
|
143
|
+
'validate': '🔍',
|
|
144
|
+
'parse-diff': '📄',
|
|
145
|
+
'detect-stacks': '🧩',
|
|
146
|
+
'token-budget': '📊',
|
|
147
|
+
'static-analysis': '🛡️',
|
|
148
|
+
'static-results': '📋',
|
|
149
|
+
'agent-start': '🤖',
|
|
150
|
+
'simple-call': '💬',
|
|
151
|
+
'simple-done': '✅',
|
|
152
|
+
'workflow-start': '🔄',
|
|
153
|
+
'workflow-synthesis': '🧬',
|
|
154
|
+
'consensus-start': '🗳️',
|
|
155
|
+
'consensus-voting': '🏛️',
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Create a progress callback that prints real-time verbose output.
|
|
159
|
+
* Each step prints a single line with an icon, step name, and message.
|
|
160
|
+
* Specialist/vote steps (dynamic names) get a generic icon.
|
|
161
|
+
*/
|
|
162
|
+
function createProgressHandler() {
|
|
163
|
+
return (event) => {
|
|
164
|
+
const icon = STEP_ICON[event.step]
|
|
165
|
+
?? (event.step.startsWith('specialist-') ? '👤' : undefined)
|
|
166
|
+
?? (event.step.startsWith('vote-') ? '🗳️' : undefined)
|
|
167
|
+
?? '▸';
|
|
168
|
+
const prefix = ` ${icon} [${event.step}]`;
|
|
169
|
+
console.log(`${prefix} ${event.message}`);
|
|
170
|
+
if (event.detail) {
|
|
171
|
+
// Indent detail lines for readability
|
|
172
|
+
const indented = event.detail
|
|
173
|
+
.split('\n')
|
|
174
|
+
.map((line) => ` ${line}`)
|
|
175
|
+
.join('\n');
|
|
176
|
+
console.log(indented);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// ─── Output Formatting ──────────────────────────────────────────
|
|
181
|
+
const STATUS_EMOJI = {
|
|
182
|
+
PASSED: '\u2705 PASSED',
|
|
183
|
+
FAILED: '\u274c FAILED',
|
|
184
|
+
NEEDS_HUMAN_REVIEW: '\u26a0\ufe0f NEEDS HUMAN REVIEW',
|
|
185
|
+
SKIPPED: '\u23ed\ufe0f SKIPPED',
|
|
186
|
+
};
|
|
187
|
+
const SEVERITY_EMOJI = {
|
|
188
|
+
critical: '\ud83d\udd34',
|
|
189
|
+
high: '\ud83d\udfe0',
|
|
190
|
+
medium: '\ud83d\udfe1',
|
|
191
|
+
low: '\ud83d\udfe2',
|
|
192
|
+
info: '\ud83d\udfe3',
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Format a ReviewResult as a human-readable markdown string for the terminal.
|
|
196
|
+
*/
|
|
197
|
+
function formatMarkdownResult(result) {
|
|
198
|
+
const status = STATUS_EMOJI[result.status] ?? result.status;
|
|
199
|
+
const timeSeconds = (result.metadata.executionTimeMs / 1000).toFixed(1);
|
|
200
|
+
const lines = [];
|
|
201
|
+
// Header
|
|
202
|
+
lines.push('---');
|
|
203
|
+
lines.push(`\ud83e\udd16 GHAGGA Code Review | ${status}`);
|
|
204
|
+
lines.push(`Mode: ${result.metadata.mode} | Model: ${result.metadata.model} | Time: ${timeSeconds}s | Tokens: ${result.metadata.tokensUsed}`);
|
|
205
|
+
lines.push('---');
|
|
206
|
+
lines.push('');
|
|
207
|
+
// Summary
|
|
208
|
+
lines.push('## Summary');
|
|
209
|
+
lines.push(result.summary);
|
|
210
|
+
lines.push('');
|
|
211
|
+
// Findings
|
|
212
|
+
if (result.findings.length > 0) {
|
|
213
|
+
lines.push(`## Findings (${result.findings.length})`);
|
|
214
|
+
lines.push('');
|
|
215
|
+
for (const finding of result.findings) {
|
|
216
|
+
const emoji = SEVERITY_EMOJI[finding.severity] ?? '';
|
|
217
|
+
const location = finding.line
|
|
218
|
+
? `${finding.file}:${finding.line}`
|
|
219
|
+
: finding.file;
|
|
220
|
+
lines.push(`${emoji} [${finding.severity.toUpperCase()}] ${finding.category}`);
|
|
221
|
+
lines.push(` ${location}`);
|
|
222
|
+
lines.push(` ${finding.message}`);
|
|
223
|
+
if (finding.suggestion) {
|
|
224
|
+
lines.push(` \ud83d\udca1 ${finding.suggestion}`);
|
|
225
|
+
}
|
|
226
|
+
lines.push('');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
lines.push('No findings. Nice work! \ud83c\udf89');
|
|
231
|
+
lines.push('');
|
|
232
|
+
}
|
|
233
|
+
// Static analysis summary
|
|
234
|
+
const { toolsRun, toolsSkipped } = result.metadata;
|
|
235
|
+
if (toolsRun.length > 0 || toolsSkipped.length > 0) {
|
|
236
|
+
lines.push('## Static Analysis');
|
|
237
|
+
if (toolsRun.length > 0) {
|
|
238
|
+
lines.push(`\u2705 Tools run: ${toolsRun.join(', ')}`);
|
|
239
|
+
}
|
|
240
|
+
if (toolsSkipped.length > 0) {
|
|
241
|
+
lines.push(`\u23ed\ufe0f Tools skipped: ${toolsSkipped.join(', ')}`);
|
|
242
|
+
}
|
|
243
|
+
lines.push('');
|
|
244
|
+
}
|
|
245
|
+
lines.push('---');
|
|
246
|
+
lines.push('Powered by GHAGGA \u2014 AI Code Review');
|
|
247
|
+
return lines.join('\n');
|
|
248
|
+
}
|
|
249
|
+
// ─── Exit Code ──────────────────────────────────────────────────
|
|
250
|
+
/**
|
|
251
|
+
* Map review status to process exit code.
|
|
252
|
+
* PASSED and SKIPPED = 0, everything else = 1.
|
|
253
|
+
*/
|
|
254
|
+
function getExitCode(status) {
|
|
255
|
+
switch (status) {
|
|
256
|
+
case 'PASSED':
|
|
257
|
+
case 'SKIPPED':
|
|
258
|
+
return 0;
|
|
259
|
+
case 'FAILED':
|
|
260
|
+
case 'NEEDS_HUMAN_REVIEW':
|
|
261
|
+
return 1;
|
|
262
|
+
default: {
|
|
263
|
+
const _exhaustive = status;
|
|
264
|
+
console.warn(`Unknown status: ${_exhaustive}`);
|
|
265
|
+
return 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI review command tests.
|
|
3
|
+
*
|
|
4
|
+
* Tests argument parsing, config file resolution, output formatting,
|
|
5
|
+
* and exit code mapping. The reviewPipeline is mocked to avoid
|
|
6
|
+
* needing an actual LLM API key.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=review.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.test.d.ts","sourceRoot":"","sources":["../../src/commands/review.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|