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 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,5 @@
1
+ /**
2
+ * Logout command — clears stored GitHub credentials.
3
+ */
4
+ export declare function logoutCommand(): void;
5
+ //# sourceMappingURL=logout.d.ts.map
@@ -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"}