jstar-reviewer 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,147 @@
1
+ # J-Star Code Reviewer
2
+
3
+ **Local-first, context-aware AI code reviewer** powered by LlamaIndex + Groq.
4
+
5
+ Works with **any language** — TypeScript, Python, Rust, Go, you name it.
6
+
7
+ ## ✨ Features
8
+
9
+ - **Local Vector Index** — Embeddings stored locally, no external DB
10
+ - **Gemini Embeddings** — Free tier friendly, no OpenAI key needed
11
+ - **Chunked Reviews** — Handles large diffs without rate limits
12
+ - **Detective Engine** — Deterministic checks for common issues
13
+ - **Dashboard Output** — Professional review reports with fix prompts
14
+ - **Global CLI** — Install once, use in any project
15
+
16
+ ---
17
+
18
+ ## 🚀 Quick Install
19
+
20
+ ### Option 1: Global CLI (Recommended)
21
+
22
+ ```bash
23
+ # Install globally
24
+ npm install -g jstar-reviewer
25
+
26
+ # In any project directory:
27
+ jstar setup # Create config files
28
+ jstar init # Index the codebase
29
+ jstar review # Review staged changes
30
+ ```
31
+
32
+ ### Option 2: One-Curl (Adds to current project)
33
+
34
+ ```bash
35
+ curl -fsSL https://raw.githubusercontent.com/JStaRFilms/jstar-code-review/v2.0.0/setup.js | node
36
+ ```
37
+
38
+ ### After Install:
39
+
40
+ 1. Copy `.env.example` → `.env.local`
41
+ 2. Add your `GOOGLE_API_KEY` and `GROQ_API_KEY`
42
+ 3. Run `jstar init` to build the brain
43
+ 4. Run `jstar review` to review staged changes
44
+
45
+ ---
46
+
47
+ ```
48
+ git diff --staged
49
+
50
+
51
+ ┌──────────────────┐
52
+ │ Detective │ ← Static analysis (secrets, console.log, "use client")
53
+ │ Engine │
54
+ └────────┬─────────┘
55
+
56
+
57
+ ┌──────────────────┐
58
+ │ Local Brain │ ← Gemini embeddings via LlamaIndex
59
+ │ (Retrieval) │
60
+ └────────┬─────────┘
61
+
62
+
63
+ ┌──────────────────┐
64
+ │ Chunked Review │ ← Splits diff by file, delays between calls
65
+ │ Queue │
66
+ └────────┬─────────┘
67
+
68
+
69
+ ┌──────────────────┐
70
+ │ Groq LLM │ ← moonshotai/kimi-k2-instruct-0905
71
+ │ (The Judge) │
72
+ └────────┬─────────┘
73
+
74
+
75
+ 📝 Review Report
76
+ ```
77
+
78
+ ## 🚀 Quick Start
79
+
80
+ ### 1. Install Dependencies
81
+
82
+ ```bash
83
+ pnpm install
84
+ ```
85
+
86
+ ### 2. Set Environment Variables
87
+
88
+ Create `.env.local`:
89
+
90
+ ```env
91
+ GOOGLE_API_KEY=your_gemini_key
92
+ GROQ_API_KEY=your_groq_key
93
+ ```
94
+
95
+ ### 3. Index Your Codebase
96
+
97
+ ```bash
98
+ pnpm run index:init
99
+ ```
100
+
101
+ ### 4. Review Staged Changes
102
+
103
+ ```bash
104
+ git add <files>
105
+ pnpm run review
106
+ ```
107
+
108
+ ## 📁 Project Structure
109
+
110
+ ```
111
+ scripts/
112
+ ├── indexer.ts # Scans codebase, builds vector index
113
+ ├── reviewer.ts # Orchestrates review pipeline
114
+ ├── detective.ts # Static analysis engine
115
+ ├── gemini-embedding.ts # Google Gemini adapter
116
+ └── mock-llm.ts # LlamaIndex compatibility stub
117
+
118
+ .jstar/
119
+ └── storage/ # Persisted embeddings (gitignored)
120
+
121
+ docs/features/
122
+ ├── architecture-v2.md # Full architecture docs
123
+ ├── detective.md # Static analysis rules
124
+ ├── analyst.md # LLM reviewer (The Judge)
125
+ └── ...
126
+ ```
127
+
128
+ ## ⚙️ Configuration
129
+
130
+ Edit `scripts/reviewer.ts`:
131
+
132
+ ```typescript
133
+ const MODEL_NAME = "moonshotai/kimi-k2-instruct-0905";
134
+ const MAX_TOKENS_PER_REQUEST = 8000;
135
+ const DELAY_BETWEEN_CHUNKS_MS = 2000;
136
+ ```
137
+
138
+ ## 📚 Documentation
139
+
140
+ - [Architecture v2](docs/features/architecture-v2.md)
141
+ - [Detective Engine](docs/features/detective.md)
142
+ - [Token Budget](docs/features/token-budget.md)
143
+ - [Chunked Reviews](docs/features/map-reduce.md)
144
+
145
+ ---
146
+
147
+ Built with ⚡ by J Star Studios
package/bin/jstar.js ADDED
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * J-Star Reviewer CLI
4
+ * Global command-line tool for AI-powered code review
5
+ *
6
+ * Usage:
7
+ * jstar init - Index the current directory
8
+ * jstar review - Review staged changes
9
+ * jstar setup - Set up config in current project
10
+ */
11
+
12
+ const { spawn } = require('child_process');
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+
16
+ const COLORS = {
17
+ reset: '\x1b[0m',
18
+ green: '\x1b[32m',
19
+ yellow: '\x1b[33m',
20
+ blue: '\x1b[34m',
21
+ red: '\x1b[31m',
22
+ dim: '\x1b[2m',
23
+ bold: '\x1b[1m'
24
+ };
25
+
26
+ function log(msg) {
27
+ console.log(msg);
28
+ }
29
+
30
+ function printHelp() {
31
+ log(`
32
+ ${COLORS.bold}🌟 J-Star Reviewer v2${COLORS.reset}
33
+
34
+ ${COLORS.dim}AI-powered code review with local embeddings${COLORS.reset}
35
+
36
+ ${COLORS.bold}USAGE:${COLORS.reset}
37
+ jstar <command> [options]
38
+
39
+ ${COLORS.bold}COMMANDS:${COLORS.reset}
40
+ ${COLORS.green}init${COLORS.reset} Index the current codebase (build the brain)
41
+ ${COLORS.green}review${COLORS.reset} Review staged git changes
42
+ ${COLORS.green}setup${COLORS.reset} Create .env.example and .jstar/ in current directory
43
+
44
+ ${COLORS.bold}EXAMPLES:${COLORS.reset}
45
+ ${COLORS.dim}# First time setup${COLORS.reset}
46
+ jstar init
47
+
48
+ ${COLORS.dim}# Review staged changes${COLORS.reset}
49
+ git add .
50
+ jstar review
51
+
52
+ ${COLORS.bold}ENVIRONMENT:${COLORS.reset}
53
+ GOOGLE_API_KEY Required for Gemini embeddings
54
+ GROQ_API_KEY Required for Groq LLM reviews
55
+
56
+ ${COLORS.dim}Report issues: https://github.com/JStaRFilms/jstar-code-review${COLORS.reset}
57
+ `);
58
+ }
59
+
60
+ function runScript(scriptName) {
61
+ const scriptsDir = path.join(__dirname, '..', 'scripts');
62
+ const scriptPath = path.join(scriptsDir, scriptName);
63
+
64
+ if (!fs.existsSync(scriptPath)) {
65
+ log(`${COLORS.red}Error: Script not found: ${scriptPath}${COLORS.reset}`);
66
+ process.exit(1);
67
+ }
68
+
69
+ // Use ts-node to run TypeScript files
70
+ const child = spawn('npx', ['ts-node', scriptPath, ...process.argv.slice(3)], {
71
+ cwd: process.cwd(),
72
+ stdio: 'inherit',
73
+ shell: true,
74
+ env: {
75
+ ...process.env,
76
+ // Pass through the working directory for the scripts
77
+ JSTAR_CWD: process.cwd()
78
+ }
79
+ });
80
+
81
+ child.on('close', (code) => {
82
+ process.exit(code || 0);
83
+ });
84
+
85
+ child.on('error', (err) => {
86
+ log(`${COLORS.red}Error running script: ${err.message}${COLORS.reset}`);
87
+ process.exit(1);
88
+ });
89
+ }
90
+
91
+ function createSetupFiles() {
92
+ const cwd = process.cwd();
93
+
94
+ // Create .jstar directory
95
+ const jstarDir = path.join(cwd, '.jstar');
96
+ if (!fs.existsSync(jstarDir)) {
97
+ fs.mkdirSync(jstarDir, { recursive: true });
98
+ log(`${COLORS.green}✓${COLORS.reset} Created .jstar/`);
99
+ }
100
+
101
+ // Create .env.example
102
+ const envExample = `# J-Star Reviewer Configuration
103
+ # Copy this to .env.local and fill in your keys
104
+
105
+ # Required: Google API key for Gemini embeddings
106
+ GOOGLE_API_KEY=your_google_api_key_here
107
+
108
+ # Required: Groq API key for LLM reviews
109
+ GROQ_API_KEY=your_groq_api_key_here
110
+ `;
111
+
112
+ const envPath = path.join(cwd, '.env.example');
113
+ if (!fs.existsSync(envPath)) {
114
+ fs.writeFileSync(envPath, envExample);
115
+ log(`${COLORS.green}✓${COLORS.reset} Created .env.example`);
116
+ } else {
117
+ log(`${COLORS.dim} .env.example already exists${COLORS.reset}`);
118
+ }
119
+
120
+ // Update .gitignore
121
+ const gitignorePath = path.join(cwd, '.gitignore');
122
+ const gitignoreAdditions = `
123
+ # J-Star Reviewer
124
+ .jstar/
125
+ .env.local
126
+ `;
127
+
128
+ if (fs.existsSync(gitignorePath)) {
129
+ const content = fs.readFileSync(gitignorePath, 'utf-8');
130
+ if (!content.includes('.jstar/')) {
131
+ fs.appendFileSync(gitignorePath, gitignoreAdditions);
132
+ log(`${COLORS.green}✓${COLORS.reset} Updated .gitignore`);
133
+ }
134
+ } else {
135
+ fs.writeFileSync(gitignorePath, gitignoreAdditions.trim());
136
+ log(`${COLORS.green}✓${COLORS.reset} Created .gitignore`);
137
+ }
138
+
139
+ log(`
140
+ ${COLORS.bold}Next steps:${COLORS.reset}
141
+ 1. Copy .env.example to .env.local
142
+ 2. Add your API keys
143
+ 3. Run: jstar init
144
+ 4. Stage changes and run: jstar review
145
+ `);
146
+ }
147
+
148
+ // Main
149
+ const command = process.argv[2];
150
+
151
+ switch (command) {
152
+ case 'init':
153
+ runScript('indexer.ts');
154
+ break;
155
+ case 'review':
156
+ runScript('reviewer.ts');
157
+ break;
158
+ case 'setup':
159
+ createSetupFiles();
160
+ break;
161
+ case '--help':
162
+ case '-h':
163
+ case undefined:
164
+ printHelp();
165
+ break;
166
+ default:
167
+ log(`${COLORS.red}Unknown command: ${command}${COLORS.reset}`);
168
+ printHelp();
169
+ process.exit(1);
170
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "jstar-reviewer",
3
+ "version": "2.0.0",
4
+ "description": "Local-First, Context-Aware AI Code Reviewer - Works with any language",
5
+ "bin": {
6
+ "jstar": "bin/jstar.js"
7
+ },
8
+ "scripts": {
9
+ "index:init": "ts-node scripts/indexer.ts --init",
10
+ "index:watch": "ts-node scripts/indexer.ts --watch",
11
+ "review": "ts-node scripts/reviewer.ts",
12
+ "detect": "ts-node scripts/detective.ts",
13
+ "prepare": "husky install"
14
+ },
15
+ "keywords": [
16
+ "code-review",
17
+ "ai",
18
+ "llm",
19
+ "cli",
20
+ "groq",
21
+ "gemini",
22
+ "static-analysis"
23
+ ],
24
+ "author": "J Star Studios",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/JStaRFilms/jstar-code-review.git"
29
+ },
30
+ "homepage": "https://github.com/JStaRFilms/jstar-code-review#readme",
31
+ "dependencies": {
32
+ "@ai-sdk/google": "^2.0.49",
33
+ "@ai-sdk/groq": "^2.0.33",
34
+ "@google/generative-ai": "^0.24.1",
35
+ "ai": "^5.0.115",
36
+ "chalk": "^4.1.2",
37
+ "dotenv": "^16.3.1",
38
+ "llamaindex": "^0.1.0",
39
+ "simple-git": "^3.20.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^20.0.0",
43
+ "ts-node": "^10.9.1",
44
+ "typescript": "^5.2.2",
45
+ "husky": "^8.0.3"
46
+ },
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "files": [
51
+ "bin/",
52
+ "scripts/",
53
+ "README.md"
54
+ ],
55
+ "main": "setup.js",
56
+ "directories": {
57
+ "doc": "docs",
58
+ "test": "test"
59
+ },
60
+ "type": "commonjs",
61
+ "bugs": {
62
+ "url": "https://github.com/JStaRFilms/jstar-code-review/issues"
63
+ }
64
+ }
@@ -0,0 +1,21 @@
1
+ import dotenv from "dotenv";
2
+ import { Severity } from "./types";
3
+
4
+ // Load .env.local first, then .env
5
+ dotenv.config({ path: ".env.local" });
6
+ dotenv.config();
7
+
8
+ /**
9
+ * Default fallback values.
10
+ * These are intentional fallbacks when environment variables are not configured.
11
+ * Override via REVIEW_MODEL_NAME env var for production use.
12
+ */
13
+ const DEFAULT_MODEL = "moonshotai/kimi-k2-instruct-0905";
14
+
15
+ export const Config = {
16
+ MODEL_NAME: process.env.REVIEW_MODEL_NAME || DEFAULT_MODEL,
17
+ DEFAULT_SEVERITY: 'P2_MEDIUM' as Severity,
18
+ THRESHOLDS: {
19
+ MEDIUM: 5
20
+ }
21
+ };
@@ -0,0 +1,227 @@
1
+ /**
2
+ * J-Star Dashboard Renderer
3
+ * Generates professional markdown dashboard from review findings
4
+ */
5
+
6
+ import { DashboardReport, FileFinding, ReviewIssue, Severity } from './types';
7
+ import { Config } from './config';
8
+
9
+ const SEVERITY_EMOJI: Record<Severity, string> = {
10
+ 'P0_CRITICAL': '🛑',
11
+ 'P1_HIGH': '⚠️',
12
+ 'P2_MEDIUM': '📝',
13
+ 'LGTM': '✅'
14
+ };
15
+
16
+ const SEVERITY_LABEL: Record<Severity, string> = {
17
+ 'P0_CRITICAL': 'CRITICAL',
18
+ 'P1_HIGH': 'HIGH',
19
+ 'P2_MEDIUM': 'MEDIUM',
20
+ 'LGTM': 'PASSED'
21
+ };
22
+
23
+ function getStatusEmoji(status: DashboardReport['status']): string {
24
+ switch (status) {
25
+ case 'CRITICAL_FAILURE': return '🔴';
26
+ case 'NEEDS_REVIEW': return '🟡';
27
+ case 'APPROVED': return '🟢';
28
+ }
29
+ }
30
+
31
+ function formatDate(): string {
32
+ return new Date().toISOString().split('T')[0];
33
+ }
34
+
35
+ /**
36
+ * Renders a single issue row for the markdown table.
37
+ * Includes fallback handling for unexpected severity values to ensure
38
+ * the dashboard renders gracefully even if parsing produced invalid data.
39
+ */
40
+ function renderIssueRow(file: string, issue: ReviewIssue, severity: Severity): string {
41
+ // Fallback to '❓' and raw severity if the value is not in our known maps
42
+ const emoji = SEVERITY_EMOJI[severity] ?? '❓';
43
+ const label = SEVERITY_LABEL[severity] ?? severity;
44
+ return `| \`${file}\` | ${emoji} **${label}** | ${issue.title} |`;
45
+ }
46
+
47
+ function renderFixPrompt(issue: ReviewIssue): string {
48
+ if (!issue.fixPrompt) return '';
49
+ return `
50
+ <details>
51
+ <summary>🤖 <strong>Fix Prompt:</strong> ${issue.title}</summary>
52
+
53
+ \`\`\`
54
+ ${issue.fixPrompt}
55
+ \`\`\`
56
+
57
+ </details>
58
+ `;
59
+ }
60
+
61
+ /**
62
+ * Validates that the report object has the required structure.
63
+ * This guards against runtime errors from malformed LLM responses or corrupted data.
64
+ */
65
+ function validateReport(report: unknown): report is DashboardReport {
66
+ if (!report || typeof report !== 'object') return false;
67
+ const r = report as Record<string, unknown>;
68
+
69
+ // Check required fields exist
70
+ if (typeof r.status !== 'string') return false;
71
+ if (typeof r.recommendedAction !== 'string') return false;
72
+ if (!Array.isArray(r.findings)) return false;
73
+ if (!r.metrics || typeof r.metrics !== 'object') return false;
74
+
75
+ // Validate metrics structure
76
+ const m = r.metrics as Record<string, unknown>;
77
+ const requiredMetrics = ['filesScanned', 'totalTokens', 'violations', 'critical', 'high', 'medium', 'lgtm'];
78
+ for (const key of requiredMetrics) {
79
+ if (typeof m[key] !== 'number') return false;
80
+ }
81
+
82
+ return true;
83
+ }
84
+
85
+ export function renderDashboard(report: DashboardReport): string {
86
+ // Runtime validation to catch malformed reports early
87
+ if (!validateReport(report)) {
88
+ throw new Error('Invalid DashboardReport: missing or malformed required fields');
89
+ }
90
+
91
+ const { metrics, findings, status, recommendedAction } = report;
92
+
93
+ // Group findings by severity
94
+ const critical = findings.filter(f => f.severity === 'P0_CRITICAL');
95
+ const high = findings.filter(f => f.severity === 'P1_HIGH');
96
+ const medium = findings.filter(f => f.severity === 'P2_MEDIUM');
97
+ const lgtm = findings.filter(f => f.severity === 'LGTM');
98
+
99
+ let md = `# 📊 J-STAR CODE REVIEW DASHBOARD
100
+
101
+ **Date:** \`${formatDate()}\` | **Reviewer:** \`Detective Engine & Judge\` | **Status:** ${getStatusEmoji(status)} **${status.replace('_', ' ')}**
102
+
103
+ ---
104
+
105
+ ## 1. 📈 EXECUTIVE SUMMARY
106
+
107
+ | Metric | Value | Status |
108
+ | --- | --- | --- |
109
+ | **Files Scanned** | **${metrics.filesScanned}** | 🔎 Complete |
110
+ | **Total Tokens** | **~${metrics.totalTokens.toLocaleString()}** | ⚖️ Processed |
111
+ | **Total Violations** | **${metrics.violations}** | ${metrics.violations > 0 ? '🚨 Action Required' : '✅ Clean'} |
112
+ | **Critical (P0)** | **${metrics.critical}** | ${metrics.critical > 0 ? '🛑 **BLOCKER**' : '✅ None'} |
113
+ | **High (P1)** | **${metrics.high}** | ${metrics.high > 0 ? '⚠️ Needs Fix' : '✅ None'} |
114
+ | **Medium (P2)** | **${metrics.medium}** | ${metrics.medium > 0 ? '📝 Review' : '✅ None'} |
115
+ | **Passed (LGTM)** | **${metrics.lgtm}** | ✅ Clean |
116
+
117
+ ---
118
+
119
+ `;
120
+
121
+ // Critical Section
122
+ if (critical.length > 0) {
123
+ md += `## 2. 🛑 CRITICAL SECURITY VULNERABILITIES (P0)
124
+
125
+ > **These files contain blockers that must be fixed before any merge.**
126
+
127
+ | File | Severity | Issue |
128
+ | --- | --- | --- |
129
+ `;
130
+ for (const finding of critical) {
131
+ for (const issue of finding.issues) {
132
+ md += renderIssueRow(finding.file, issue, 'P0_CRITICAL') + '\n';
133
+ }
134
+ }
135
+ md += '\n### 🤖 Fix Prompts (P0)\n\n';
136
+ for (const finding of critical) {
137
+ for (const issue of finding.issues) {
138
+ md += renderFixPrompt(issue);
139
+ }
140
+ }
141
+ md += '\n---\n\n';
142
+ }
143
+
144
+ // High Section
145
+ if (high.length > 0) {
146
+ md += `## 3. ⚠️ HIGH PRIORITY ISSUES (P1)
147
+
148
+ > **Architecture and logic issues requiring significant attention.**
149
+
150
+ | File | Severity | Issue |
151
+ | --- | --- | --- |
152
+ `;
153
+ for (const finding of high) {
154
+ for (const issue of finding.issues) {
155
+ md += renderIssueRow(finding.file, issue, 'P1_HIGH') + '\n';
156
+ }
157
+ }
158
+ md += '\n### 🤖 Fix Prompts (P1)\n\n';
159
+ for (const finding of high) {
160
+ for (const issue of finding.issues) {
161
+ md += renderFixPrompt(issue);
162
+ }
163
+ }
164
+ md += '\n---\n\n';
165
+ }
166
+
167
+ // Medium Section
168
+ if (medium.length > 0) {
169
+ md += `## 4. 📝 MEDIUM PRIORITY ISSUES (P2)
170
+
171
+ > **Code quality and maintenance items.**
172
+
173
+ | File | Severity | Issue |
174
+ | --- | --- | --- |
175
+ `;
176
+ for (const finding of medium) {
177
+ for (const issue of finding.issues) {
178
+ md += renderIssueRow(finding.file, issue, 'P2_MEDIUM') + '\n';
179
+ }
180
+ }
181
+ md += '\n---\n\n';
182
+ }
183
+
184
+ // LGTM Section
185
+ if (lgtm.length > 0) {
186
+ md += `## 5. ✅ PASSED REVIEW (LGTM)
187
+
188
+ > **No issues found in these files.**
189
+
190
+ `;
191
+ for (const finding of lgtm) {
192
+ md += `- \`${finding.file}\`\n`;
193
+ }
194
+ md += '\n---\n\n';
195
+ }
196
+
197
+ // Recommended Action
198
+ md += `## 🎯 RECOMMENDED ACTION
199
+
200
+ > ${recommendedAction}
201
+
202
+ ---
203
+
204
+ *Generated by J-Star Code Reviewer v2*
205
+ `;
206
+
207
+ return md;
208
+ }
209
+
210
+ export function determineStatus(metrics: DashboardReport['metrics']): DashboardReport['status'] {
211
+ if (metrics.critical > 0) return 'CRITICAL_FAILURE';
212
+ if (metrics.high > 0 || metrics.medium > Config.THRESHOLDS.MEDIUM) return 'NEEDS_REVIEW';
213
+ return 'APPROVED';
214
+ }
215
+
216
+ export function generateRecommendation(metrics: DashboardReport['metrics']): string {
217
+ if (metrics.critical > 0) {
218
+ return `**BLOCK MERGE:** Fix ${metrics.critical} critical issue(s) immediately. Review P0 fix prompts above.`;
219
+ }
220
+ if (metrics.high > 0) {
221
+ return `**Request Changes:** Address ${metrics.high} high-priority issue(s) before merging.`;
222
+ }
223
+ if (metrics.medium > 0) {
224
+ return `**Approve with Notes:** ${metrics.medium} medium issues found. Consider fixing in follow-up PR.`;
225
+ }
226
+ return `**Approve:** All files passed review. Ship it! 🚀`;
227
+ }