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.
@@ -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: Google API key for Gemini embeddings
71
- GOOGLE_API_KEY=your_google_api_key_here
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.1.3';
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
- 'GOOGLE_API_KEY': '# Required: Google API key for Gemini embeddings\nGOOGLE_API_KEY=your_google_api_key_here',
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 GOOGLE_API_KEY and GROQ_API_KEY');
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
- }