jstar-reviewer 2.1.3 ā 2.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 +2 -2
- package/bin/jstar.js +15 -4
- package/dist/scripts/chat.js +150 -0
- package/dist/scripts/config.js +6 -2
- package/dist/scripts/core/critique.js +137 -0
- package/dist/scripts/core/debate.js +95 -0
- package/dist/scripts/detective.js +5 -4
- package/dist/scripts/gemini-embedding.js +2 -2
- package/dist/scripts/indexer.js +4 -3
- package/dist/scripts/reviewer.js +139 -43
- package/dist/scripts/session.js +273 -0
- package/dist/scripts/ui/interaction.js +43 -0
- package/dist/scripts/utils/logger.js +110 -0
- package/package.json +14 -10
- package/scripts/chat.ts +130 -0
- package/scripts/config.ts +6 -2
- package/scripts/core/critique.ts +162 -0
- package/scripts/core/debate.ts +111 -0
- package/scripts/detective.ts +5 -4
- package/scripts/gemini-embedding.ts +2 -2
- package/scripts/indexer.ts +4 -3
- package/scripts/reviewer.ts +154 -43
- package/scripts/session.ts +312 -0
- package/scripts/types.ts +9 -0
- package/scripts/ui/interaction.ts +38 -0
- package/scripts/utils/logger.ts +118 -0
- package/setup.js +5 -5
- package/scripts/local-embedding.ts +0 -55
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
export type UserAction = 'accept' | 'discuss' | 'ignore' | 'exit';
|
|
5
|
+
|
|
6
|
+
export async function showActionMenu(issueTitle: string): Promise<UserAction> {
|
|
7
|
+
try {
|
|
8
|
+
const response = await prompts({
|
|
9
|
+
type: 'select',
|
|
10
|
+
name: 'action',
|
|
11
|
+
message: `Action for: ${chalk.yellow(issueTitle)}`,
|
|
12
|
+
choices: [
|
|
13
|
+
{ title: 'ā
Accept', value: 'accept', description: 'Mark as valid issue' },
|
|
14
|
+
{ title: 'š¬ Discuss', value: 'discuss', description: 'Debate this finding with AI' },
|
|
15
|
+
{ title: 'ā Ignore', value: 'ignore', description: 'Discard this issue' },
|
|
16
|
+
{ title: 'šŖ Exit', value: 'exit', description: 'Stop review session' }
|
|
17
|
+
],
|
|
18
|
+
initial: 0
|
|
19
|
+
});
|
|
20
|
+
return response.action || 'exit';
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return 'exit';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function askForArgument(): Promise<string> {
|
|
27
|
+
try {
|
|
28
|
+
const response = await prompts({
|
|
29
|
+
type: 'text',
|
|
30
|
+
name: 'argument',
|
|
31
|
+
message: 'Your argument (e.g., "Check utils.ts, logic is handled there"):',
|
|
32
|
+
validate: value => value.length < 5 ? 'Please provide more context' : true
|
|
33
|
+
});
|
|
34
|
+
return response.argument || '';
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logger Utility
|
|
5
|
+
* Centralizes all CLI output to support both human-readable (TTY) and machine-readable (JSON) modes.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* Logger.init(); // Auto-detects --json flag
|
|
9
|
+
* Logger.info("Starting..."); // Suppressed in JSON mode
|
|
10
|
+
* Logger.json({ status: "ok" }); // Only outputs in JSON mode
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
let jsonMode = false;
|
|
14
|
+
|
|
15
|
+
export const Logger = {
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the logger.
|
|
18
|
+
* Auto-detects --json or --headless flags from process.argv.
|
|
19
|
+
*/
|
|
20
|
+
init() {
|
|
21
|
+
jsonMode = process.argv.includes('--json') || process.argv.includes('--headless');
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if we are in headless/JSON mode.
|
|
26
|
+
*/
|
|
27
|
+
isHeadless() {
|
|
28
|
+
return jsonMode;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Alias for isHeadless for backwards compatibility.
|
|
33
|
+
*/
|
|
34
|
+
isJsonMode() {
|
|
35
|
+
return jsonMode;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Standard informational message (suppressed in JSON mode).
|
|
40
|
+
*/
|
|
41
|
+
info(message: string) {
|
|
42
|
+
if (!jsonMode) {
|
|
43
|
+
console.log(message);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Success message with green styling (suppressed in JSON mode).
|
|
49
|
+
*/
|
|
50
|
+
success(message: string) {
|
|
51
|
+
if (!jsonMode) {
|
|
52
|
+
console.log(chalk.green(message));
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Warning message with yellow styling (suppressed in JSON mode).
|
|
58
|
+
*/
|
|
59
|
+
warn(message: string) {
|
|
60
|
+
if (!jsonMode) {
|
|
61
|
+
console.log(chalk.yellow(message));
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Error message - always outputs to stderr.
|
|
67
|
+
*/
|
|
68
|
+
error(message: string) {
|
|
69
|
+
console.error(chalk.red(message));
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Dim/faded message for secondary info (suppressed in JSON mode).
|
|
74
|
+
*/
|
|
75
|
+
dim(message: string) {
|
|
76
|
+
if (!jsonMode) {
|
|
77
|
+
console.log(chalk.dim(message));
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Write inline (no newline) for progress indicators (suppressed in JSON mode).
|
|
83
|
+
* Alias: progress()
|
|
84
|
+
*/
|
|
85
|
+
inline(message: string) {
|
|
86
|
+
if (!jsonMode) {
|
|
87
|
+
process.stdout.write(message);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Progress indicator - writes inline without newline.
|
|
93
|
+
*/
|
|
94
|
+
progress(message: string) {
|
|
95
|
+
if (!jsonMode) {
|
|
96
|
+
process.stdout.write(message);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Output structured JSON to stdout.
|
|
102
|
+
* Only outputs in JSON mode. For API/AI consumption.
|
|
103
|
+
*/
|
|
104
|
+
json(data: object) {
|
|
105
|
+
if (jsonMode) {
|
|
106
|
+
console.log(JSON.stringify(data, null, 2));
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Output a single-line JSON object (for streaming events).
|
|
112
|
+
*/
|
|
113
|
+
jsonLine(data: object) {
|
|
114
|
+
if (jsonMode) {
|
|
115
|
+
console.log(JSON.stringify(data));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
package/setup.js
CHANGED
|
@@ -67,8 +67,8 @@ const SCRIPTS = {
|
|
|
67
67
|
const ENV_EXAMPLE = `# J-Star Code Reviewer Configuration
|
|
68
68
|
# Copy this to .env.local and fill in your keys
|
|
69
69
|
|
|
70
|
-
# Required:
|
|
71
|
-
|
|
70
|
+
# Required: Gemini API key (or GOOGLE_API_KEY)
|
|
71
|
+
GEMINI_API_KEY=your_gemini_api_key_here
|
|
72
72
|
|
|
73
73
|
# Required: Groq API key for LLM reviews
|
|
74
74
|
GROQ_API_KEY=your_groq_api_key_here
|
|
@@ -115,7 +115,7 @@ async function main() {
|
|
|
115
115
|
|
|
116
116
|
// HARDCODED: Tagged release URL - NOT configurable for security
|
|
117
117
|
// To update, modify this constant and publish a new version of setup.js
|
|
118
|
-
const BASE_URL = 'https://raw.githubusercontent.com/JStaRFilms/jstar-code-review/v2.
|
|
118
|
+
const BASE_URL = 'https://raw.githubusercontent.com/JStaRFilms/jstar-code-review/v2.2.0';
|
|
119
119
|
|
|
120
120
|
// Validate URL matches our exact expected pattern (defense in depth)
|
|
121
121
|
function isValidUrl(url) {
|
|
@@ -272,7 +272,7 @@ async function main() {
|
|
|
272
272
|
// 6. Update .env.example (intelligently merge, don't override)
|
|
273
273
|
const envExamplePath = path.join(cwd, '.env.example');
|
|
274
274
|
const REQUIRED_ENV_VARS = {
|
|
275
|
-
'
|
|
275
|
+
'GEMINI_API_KEY': '# Required: Gemini API key (or GOOGLE_API_KEY)\nGEMINI_API_KEY=your_gemini_api_key_here',
|
|
276
276
|
'GROQ_API_KEY': '# Required: Groq API key for LLM reviews\nGROQ_API_KEY=your_groq_api_key_here',
|
|
277
277
|
'REVIEW_MODEL_NAME': '# Optional: Override the default model\n# REVIEW_MODEL_NAME=moonshotai/kimi-k2-instruct-0905'
|
|
278
278
|
};
|
|
@@ -355,7 +355,7 @@ async function main() {
|
|
|
355
355
|
console.log('\nš J-Star Code Reviewer installed!\n');
|
|
356
356
|
console.log('Next steps:');
|
|
357
357
|
console.log(' 1. Copy .env.example to .env.local');
|
|
358
|
-
console.log(' 2. Add your
|
|
358
|
+
console.log(' 2. Add your GEMINI_API_KEY and GROQ_API_KEY');
|
|
359
359
|
console.log(' 3. Run: pnpm run index:init');
|
|
360
360
|
console.log(' 4. Stage changes and run: pnpm run review');
|
|
361
361
|
console.log('\n' + 'ā'.repeat(50) + '\n');
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { pipeline, env } from "@xenova/transformers";
|
|
2
|
-
|
|
3
|
-
// Skip local model checks if needed, or let it download
|
|
4
|
-
env.allowLocalModels = false;
|
|
5
|
-
env.useBrowserCache = false;
|
|
6
|
-
|
|
7
|
-
export class LocalEmbedding {
|
|
8
|
-
private pipe: any;
|
|
9
|
-
private modelName: string;
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
this.modelName = "Xenova/bge-small-en-v1.5";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async init() {
|
|
16
|
-
if (!this.pipe) {
|
|
17
|
-
console.log("š„ Loading local embedding model (Xenova/bge-small-en-v1.5)...");
|
|
18
|
-
this.pipe = await pipeline("feature-extraction", this.modelName);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async getTextEmbedding(text: string): Promise<number[]> {
|
|
23
|
-
await this.init();
|
|
24
|
-
const result = await this.pipe(text, { pooling: "mean", normalize: true });
|
|
25
|
-
return Array.from(result.data);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async getQueryEmbedding(query: string): Promise<number[]> {
|
|
29
|
-
return this.getTextEmbedding(query);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Batch method (Required by LlamaIndex)
|
|
33
|
-
async getTextEmbeddings(texts: string[]): Promise<number[][]> {
|
|
34
|
-
await this.init();
|
|
35
|
-
const embeddings: number[][] = [];
|
|
36
|
-
for (const text of texts) {
|
|
37
|
-
embeddings.push(await this.getTextEmbedding(text));
|
|
38
|
-
}
|
|
39
|
-
return embeddings;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Stubs for BaseEmbedding interface compliance
|
|
43
|
-
embedBatchSize = 10;
|
|
44
|
-
similarity(embedding1: number[], embedding2: number[]): number {
|
|
45
|
-
// Simple dot product for normalized vectors
|
|
46
|
-
return embedding1.reduce((sum, val, i) => sum + val * embedding2[i], 0);
|
|
47
|
-
}
|
|
48
|
-
async transform(nodes: any[], _options?: any): Promise<any[]> {
|
|
49
|
-
for (const node of nodes) {
|
|
50
|
-
node.embedding = await this.getTextEmbedding(node.getContent("text"));
|
|
51
|
-
}
|
|
52
|
-
return nodes;
|
|
53
|
-
}
|
|
54
|
-
async getTextEmbeddingsBatch(texts: string[]): Promise<number[][]> { return this.getTextEmbeddings(texts); }
|
|
55
|
-
}
|