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 +147 -0
- package/bin/jstar.js +170 -0
- package/package.json +64 -0
- package/scripts/config.ts +21 -0
- package/scripts/dashboard.ts +227 -0
- package/scripts/detective.ts +137 -0
- package/scripts/gemini-embedding.ts +97 -0
- package/scripts/indexer.ts +103 -0
- package/scripts/local-embedding.ts +55 -0
- package/scripts/mock-llm.ts +18 -0
- package/scripts/reviewer.ts +295 -0
- package/scripts/types.ts +61 -0
- package/setup.js +364 -0
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
|
+
}
|