codexa 1.2.0 → 1.2.2

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 CHANGED
@@ -51,7 +51,6 @@
51
51
  - šŸ¤– **Multiple LLM Support**: Works with Groq (cloud)
52
52
  - šŸ’¾ **Local Storage**: SQLite database for embeddings and context
53
53
  - šŸŽÆ **Smart Chunking**: Intelligent code splitting with configurable overlap
54
- - šŸ”„ **Session Management**: Maintain conversation context across queries
55
54
  - šŸ“Š **Streaming Output**: Real-time response streaming for better UX
56
55
  - šŸŽØ **Multiple File Types**: Supports TypeScript, JavaScript, Python, Go, Rust, Java, and more
57
56
  - 🧠 **Smart Configuration**: Automatically detects project languages and optimizes config
@@ -59,6 +58,8 @@
59
58
  - āš™ļø **Highly Configurable**: Fine-tune chunking, retrieval, and model parameters
60
59
  - šŸš€ **Zero Setup**: Works out of the box with sensible defaults
61
60
 
61
+ > āš ļø **Codebase Size Limitation**: Codexa is optimized for small to medium-sized codebases. It currently supports projects with up to **200 files** and **20,000 chunks**. For larger codebases, consider using more restrictive `includeGlobs` patterns to focus on specific directories or file types.
62
+
62
63
  ## Installation
63
64
 
64
65
  ### Prerequisites
@@ -87,7 +88,7 @@ npm install -g codexa
87
88
  Verify installation:
88
89
 
89
90
  ```bash
90
- codexa --version
91
+ codexa
91
92
  ```
92
93
 
93
94
  #### Method 2: Homebrew (macOS)
@@ -143,35 +144,20 @@ Groq provides fast cloud-based LLMs with a generous free tier.
143
144
  4. Create a new API key
144
145
  5. Copy your API key (starts with `gsk_`)
145
146
 
146
- **Step 2: Set Environment Variable**
147
-
148
- **macOS/Linux:**
149
- ```bash
150
- # Add to your shell profile (~/.zshrc, ~/.bashrc, etc.)
151
- export GROQ_API_KEY="gsk_your_api_key_here"
152
-
153
- # Reload your shell or run:
154
- source ~/.zshrc # or ~/.bashrc
155
- ```
147
+ **Step 2: Set GROQ API Key**
156
148
 
157
- **Windows (PowerShell):**
158
- ```powershell
159
- $env:GROQ_API_KEY="gsk_your_api_key_here"
149
+ Run the following command to securely save your API key:
160
150
 
161
- # Or add permanently:
162
- [System.Environment]::SetEnvironmentVariable('GROQ_API_KEY', 'gsk_your_api_key_here', 'User')
151
+ ```bash
152
+ codexa config set GROQ_API_KEY "gsk_your_api_key_here"
163
153
  ```
164
154
 
165
- **Windows (Command Prompt):**
166
- ```cmd
167
- setx GROQ_API_KEY "gsk_your_api_key_here"
168
- ```
155
+ This will save the key to your local configuration file (`.codexarc.json`).
169
156
 
170
157
  **Step 3: Verify API Key is Set**
171
158
 
172
159
  ```bash
173
- echo $GROQ_API_KEY # macOS/Linux
174
- echo %GROQ_API_KEY% # Windows CMD
160
+ codexa config get GROQ_API_KEY
175
161
  ```
176
162
 
177
163
  **Step 4: Configure Codexa**
@@ -181,15 +167,15 @@ Codexa defaults to using Groq when you run `codexa init`. If you need to manuall
181
167
  ```json
182
168
  {
183
169
  "modelProvider": "groq",
184
- "model": "llama-3.1-8b-instant",
170
+ "model": "openai/gpt-oss-120b",
185
171
  "embeddingProvider": "local",
186
172
  "embeddingModel": "Xenova/all-MiniLM-L6-v2"
187
173
  }
188
174
  ```
189
175
 
190
- **Available Groq Models:**
191
- - `llama-3.1-8b-instant` - Fast responses (recommended, default)
192
- - `llama-3.1-70b-versatile` - Higher quality, slower
176
+ **Models you can use:**
177
+ - `openai/gpt-oss-120b` (recommended, default)
178
+ - `llama-3.1-70b-versatile`
193
179
 
194
180
 
195
181
 
@@ -198,13 +184,14 @@ Codexa defaults to using Groq when you run `codexa init`. If you need to manuall
198
184
  **For Groq:**
199
185
  ```bash
200
186
  # 1. Get API key from console.groq.com
201
- # 2. Set environment variable
202
- export GROQ_API_KEY="gsk_your_key"
203
187
 
204
- # 3. Run codexa init (defaults to Groq)
188
+ # 2. Run codexa init (defaults to Groq)
205
189
  codexa init
206
190
 
207
- # 4. Ready to use!
191
+ # 3. Set GROQ API key
192
+ codexa config set GROQ_API_KEY "gsk_your_key"
193
+
194
+ # 4. Proceed to igestion
208
195
  ```
209
196
 
210
197
 
@@ -224,13 +211,19 @@ Once Codexa is installed and your LLM is configured, you're ready to use it:
224
211
  ```
225
212
  This creates a `.codexarc.json` configuration file with sensible defaults.
226
213
 
227
- 3. **Ingest your codebase:**
214
+ 3. **Set GROQ API Key**
215
+ ```bash
216
+ codexa config set GROQ_API_KEY "gsk_your_key"
217
+ ```
218
+ This will save the key to your local configuration file (`.codexarc.json`).
219
+
220
+ 4. **Ingest your codebase:**
228
221
  ```bash
229
222
  codexa ingest
230
223
  ```
231
224
  This indexes your codebase and creates embeddings. First run may take a few minutes.
232
225
 
233
- 4. **Ask questions:**
226
+ 5. **Ask questions:**
234
227
  ```bash
235
228
  codexa ask "How does the authentication flow work?"
236
229
  codexa ask "What is the main entry point of this application?"
@@ -269,9 +262,10 @@ Analyzing codebase...
269
262
  │ │
270
263
  │ Next Steps: │
271
264
  │ │
272
- │ 1. Review .codexarc.json - Update provider keys if needed │
273
- │ 2. Run: codexa ingest - Start indexing your codebase │
274
- │ 3. Run: codexa ask "your question" - Ask questions │
265
+ │ 1. Review .codexarc.json - Update provider keys if needed |
266
+ │ 2. Set your GROQ API Key: codexa config set GROQ_API_KEY |
267
+ │ 3. Run: codexa ingest - Start indexing your codebase │
268
+ │ 4. Run: codexa ask "your question" - Ask questions │
275
269
  │ │
276
270
  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
277
271
  ```
@@ -314,6 +308,30 @@ codexa ingest --force
314
308
 
315
309
  ---
316
310
 
311
+ ### `config`
312
+
313
+ Manage configuration values, including API keys.
314
+
315
+ ```bash
316
+ codexa config <action> [key] [value]
317
+ ```
318
+
319
+ **Actions:**
320
+ - `set <key> <value>` - Set a configuration value
321
+ - `get <key>` - Get a configuration value
322
+ - `list` - List all configuration values
323
+
324
+ **Examples:**
325
+ ```bash
326
+ # Set Groq API Key
327
+ codexa config set GROQ_API_KEY "gsk_..."
328
+
329
+ # Check current key
330
+ codexa config get GROQ_API_KEY
331
+ ```
332
+
333
+ ---
334
+
317
335
  ### `ask`
318
336
 
319
337
  Ask natural language questions about your codebase.
@@ -326,8 +344,8 @@ codexa ask <question...> [options]
326
344
  - `<question...>` - Your question (can be multiple words)
327
345
 
328
346
  **Options:**
329
- - `-s, --session <name>` - Session identifier to maintain conversation context (default: `"default"`)
330
- - `--no-stream` - Disable streaming output (show full response at once)
347
+ <!-- - `-s, --session <name>` - Session identifier to maintain conversation context (default: `"default"`) -->
348
+ - `--stream` - Enable streaming output
331
349
 
332
350
  **Examples:**
333
351
  ```bash
@@ -337,14 +355,8 @@ codexa ask "How does user authentication work?"
337
355
  # Question with multiple words
338
356
  codexa ask "What is the main entry point of this application?"
339
357
 
340
- # Use a specific session for context
341
- codexa ask "How does the login function work?" --session my-analysis
342
-
343
- # Disable streaming
344
- codexa ask "Summarize the codebase structure" --no-stream
345
-
346
- # Follow-up question in the same session
347
- codexa ask "Can you explain that in more detail?" --session my-analysis
358
+ # Enable streaming
359
+ codexa ask "Summarize the codebase structure" --stream
348
360
  ```
349
361
 
350
362
  **How it works:**
@@ -393,12 +405,15 @@ Some settings can be configured via environment variables:
393
405
  | Variable | Description | Required For |
394
406
  |----------|-------------|--------------|
395
407
  | `GROQ_API_KEY` | Groq API key for cloud LLM | Groq provider |
396
- | `OPENAI_API_KEY` | OpenAI API key (for embeddings) | OpenAI embeddings |
408
+ <!-- | `OPENAI_API_KEY` | OpenAI API key (for embeddings) | OpenAI embeddings | -->
397
409
 
398
410
  **Example:**
399
411
  ```bash
400
- export GROQ_API_KEY="gsk_your_key_here"
401
- export OPENAI_API_KEY="sk-your_key_here" # If using OpenAI embeddings
412
+ # Using config command (Recommended)
413
+ codexa config set GROQ_API_KEY "gsk_your_key_here"
414
+
415
+ # Or using environment variables
416
+ export GROQ_API_KEY="gsk_your_key_here" # macOS/Linux
402
417
  ```
403
418
 
404
419
  ### Configuration Options
@@ -416,7 +431,7 @@ The LLM provider to use for generating answers.
416
431
 
417
432
  **Type:** `string`
418
433
  **Type:** `string`
419
- **Default:** `"llama-3.1-8b-instant"`
434
+ **Default:** `"openai/gpt-oss-120b"`
420
435
 
421
436
  The model identifier to use.
422
437
 
@@ -532,7 +547,7 @@ Maximum file size in bytes. Files larger than this will be excluded from indexin
532
547
  **Example:**
533
548
  ```json
534
549
  {
535
- "maxFileSize": 10485760 // 10MB
550
+ "maxFileSize": 10485760
536
551
  }
537
552
  ```
538
553
 
@@ -561,7 +576,7 @@ Whether to skip files exceeding `maxFileSize` during indexing. Set to `false` if
561
576
  ```json
562
577
  {
563
578
  "skipLargeFiles": true,
564
- "maxFileSize": 10485760 // 10MB
579
+ "maxFileSize": 10485760
565
580
  }
566
581
  ```
567
582
 
@@ -572,7 +587,7 @@ Whether to skip files exceeding `maxFileSize` during indexing. Set to `false` if
572
587
  ```json
573
588
  {
574
589
  "modelProvider": "groq",
575
- "model": "llama-3.1-8b-instant",
590
+ "model": "openai/gpt-oss-120b",
576
591
  "embeddingProvider": "local",
577
592
  "embeddingModel": "Xenova/all-MiniLM-L6-v2",
578
593
  "maxChunkSize": 300,
@@ -582,9 +597,9 @@ Whether to skip files exceeding `maxFileSize` during indexing. Set to `false` if
582
597
  }
583
598
  ```
584
599
 
585
- **Remember:** Set `GROQ_API_KEY` environment variable:
600
+ **Remember:** Set `GROQ_API_KEY`:
586
601
  ```bash
587
- export GROQ_API_KEY="your-api-key"
602
+ codexa config set GROQ_API_KEY "your-api-key"
588
603
  ```
589
604
 
590
605
 
@@ -594,7 +609,7 @@ export GROQ_API_KEY="your-api-key"
594
609
  ```json
595
610
  {
596
611
  "modelProvider": "groq",
597
- "model": "llama-3.1-8b-instant",
612
+ "model": "openai/gpt-oss-120b",
598
613
  "maxChunkSize": 150,
599
614
  "chunkOverlap": 15,
600
615
  "topK": 6,
@@ -623,10 +638,13 @@ export GROQ_API_KEY="your-api-key"
623
638
  cd my-project
624
639
  codexa init
625
640
 
626
- # 2. Index your codebase
641
+ # 2. Set Groq Api Key
642
+ codexa config set GROQ_API_KEY <your-groq-key>
643
+
644
+ # 3. Index your codebase
627
645
  codexa ingest
628
646
 
629
- # 3. Ask questions
647
+ # 4. Ask questions
630
648
  codexa ask "What is the main purpose of this codebase?"
631
649
  codexa ask "How does the user authentication work?"
632
650
  codexa ask "Where is the API routing configured?"
@@ -748,14 +766,17 @@ When you run `codexa ask`:
748
766
  **Problem:** Using Groq provider but API key is missing.
749
767
 
750
768
  **Solutions:**
751
- 1. Set the environment variable:
769
+ 1. Set the API key using the config command (Recommended):
770
+ ```bash
771
+ codexa config set GROQ_API_KEY "your-api-key"
772
+ ```
773
+ 2. Or set the environment variable:
752
774
  ```bash
753
- export GROQ_API_KEY="your-api-key"
775
+ export GROQ_API_KEY="your-api-key" # macOS/Linux
754
776
  ```
755
- 2. Or add it to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.)
756
777
  3. Verify it's set:
757
778
  ```bash
758
- echo $GROQ_API_KEY
779
+ codexa config get GROQ_API_KEY
759
780
  ```
760
781
 
761
782
  ### Ingestion is Very Slow
package/dist/cli.js CHANGED
@@ -7,6 +7,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
8
  const ora_1 = __importDefault(require("ora"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
12
  const config_1 = require("./config");
11
13
  const ingest_1 = require("./ingest");
12
14
  const agent_1 = require("./agent");
@@ -14,29 +16,67 @@ const logger_1 = require("./utils/logger");
14
16
  const formatter_1 = require("./utils/formatter");
15
17
  const marked_1 = require("marked");
16
18
  const marked_terminal_1 = __importDefault(require("marked-terminal"));
19
+ const gradient_string_1 = __importDefault(require("gradient-string"));
20
+ const boxen_1 = __importDefault(require("boxen"));
17
21
  marked_1.marked.setOptions({
18
22
  renderer: new marked_terminal_1.default({
19
23
  tab: 2,
20
24
  }),
21
25
  });
26
+ function showBanner() {
27
+ const asciiArt = `
28
+ ╔════════════════════════════════════════════════════════╗
29
+ ā•‘ ā•‘
30
+ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘
31
+ ā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•— ā•‘
32
+ ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā•‘
33
+ ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ ā•‘
34
+ ā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•”ā• ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā•‘
35
+ ā•‘ ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•‘
36
+ ā•‘ ā•‘
37
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
38
+ `;
39
+ try {
40
+ const gradientArt = (0, gradient_string_1.default)('cyan', 'blue', 'magenta')(asciiArt);
41
+ console.log(gradientArt);
42
+ const message = (0, boxen_1.default)(`${chalk_1.default.bold('šŸŽ‰ Codexa installed successfully!')}\n\n` +
43
+ `${chalk_1.default.dim('Codexa is a CLI tool that helps you understand your codebase using AI.')}\n\n` +
44
+ `${chalk_1.default.bold('Quick Start:')}\n\n` +
45
+ `${chalk_1.default.dim('1.')} ${chalk_1.default.white('Navigate to your project directory')}\n` +
46
+ `${chalk_1.default.dim('2.')} ${chalk_1.default.white('Initialize Codexa:')} ${chalk_1.default.cyan('codexa init')}\n` +
47
+ `${chalk_1.default.dim('3.')} ${chalk_1.default.white('Set your GROQ API Key:')} ` +
48
+ `${getAPIKeyStep()}\n` +
49
+ `${chalk_1.default.dim('4.')} ${chalk_1.default.white('Index your codebase:')} ${chalk_1.default.cyan('codexa ingest')}\n` +
50
+ `${chalk_1.default.dim('5.')} ${chalk_1.default.white('Ask questions:')} ${chalk_1.default.cyan('codexa ask "your question"')}\n\n` +
51
+ `${chalk_1.default.dim('For help, run:')} ${chalk_1.default.cyan('codexa --help')}\n` +
52
+ `${chalk_1.default.dim('Or visit:')} ${chalk_1.default.cyan('https://github.com/sahitya-chandra/codexa')}`, {
53
+ title: 'šŸš€ Welcome to Codexa',
54
+ borderColor: 'cyan',
55
+ padding: 1,
56
+ margin: 1,
57
+ });
58
+ console.log(message);
59
+ }
60
+ catch {
61
+ console.log('\nšŸŽ‰ Codexa installed successfully!\n');
62
+ console.log('Quick Start:');
63
+ console.log('1. Navigate to your project directory');
64
+ console.log('2. Initialize Codexa: codexa init');
65
+ console.log('3. Set your GROQ API Key');
66
+ console.log('4. Index your codebase: codexa ingest');
67
+ console.log('5. Ask questions: codexa ask "your question"\n');
68
+ }
69
+ }
70
+ function getAPIKeyStep() {
71
+ return `${chalk_1.default.cyan('codexa config set GROQ_API_KEY <your-groq-key>')}`;
72
+ }
22
73
  const program = new commander_1.Command();
23
74
  program
24
75
  .name('codexa')
25
76
  .description('Ask questions about any local repository from the command line.')
26
- .version('1.2.0')
77
+ .version('1.2.2')
27
78
  .action(() => {
28
- console.log('\n');
29
- logger_1.log.box(`${chalk_1.default.bold('Welcome to Codexa!')}\n\n` +
30
- `${chalk_1.default.dim('Codexa is a CLI tool that helps you understand your codebase using AI.')}\n\n` +
31
- `${chalk_1.default.bold('Getting Started:')}\n\n` +
32
- `${chalk_1.default.dim('1.')} ${chalk_1.default.white('Initialize Codexa in your project:')}\n` +
33
- ` ${chalk_1.default.cyan('codexa init')}\n\n` +
34
- `${chalk_1.default.dim('2.')} ${chalk_1.default.white('Index your codebase:')}\n` +
35
- ` ${chalk_1.default.cyan('codexa ingest')}\n\n` +
36
- `${chalk_1.default.dim('3.')} ${chalk_1.default.white('Ask questions:')}\n` +
37
- ` ${chalk_1.default.cyan('codexa ask "your question"')}\n\n` +
38
- `${chalk_1.default.dim('For more help, run:')} ${chalk_1.default.cyan('codexa --help')}`, 'šŸš€ Codexa');
39
- console.log('\n');
79
+ showBanner();
40
80
  });
41
81
  program
42
82
  .command('init')
@@ -49,8 +89,10 @@ program
49
89
  console.log('\n');
50
90
  logger_1.log.box(`${chalk_1.default.bold('Next Steps:')}\n\n` +
51
91
  `${chalk_1.default.dim('1.')} ${chalk_1.default.white('Review .codexarc.json')} - Update provider keys if needed\n` +
52
- `${chalk_1.default.dim('2.')} ${chalk_1.default.white('Run:')} ${chalk_1.default.cyan('codexa ingest')} ${chalk_1.default.dim('- Start indexing your codebase')}\n` +
53
- `${chalk_1.default.dim('3.')} ${chalk_1.default.white('Run:')} ${chalk_1.default.cyan('codexa ask "your question"')} ${chalk_1.default.dim('- Ask questions about your code')}`, 'šŸš€ Setup Complete');
92
+ `${chalk_1.default.dim('2.')} ${chalk_1.default.white('Set your GROQ API Key:')} ` +
93
+ `${getAPIKeyStep()}\n` +
94
+ `${chalk_1.default.dim('3.')} ${chalk_1.default.white('Run:')} ${chalk_1.default.cyan('codexa ingest')} ${chalk_1.default.dim('- Start indexing your codebase')}\n` +
95
+ `${chalk_1.default.dim('4.')} ${chalk_1.default.white('Run:')} ${chalk_1.default.cyan('codexa ask "your question"')} ${chalk_1.default.dim('- Ask questions about your code')}`, 'šŸš€ Setup Complete');
54
96
  console.log('\n');
55
97
  });
56
98
  program
@@ -114,6 +156,53 @@ program
114
156
  handleError(error);
115
157
  }
116
158
  });
159
+ program
160
+ .command('config')
161
+ .description('Manage configuration values')
162
+ .argument('[action]', 'Action to perform (set, get, list)')
163
+ .argument('[key]', 'Configuration key')
164
+ .argument('[value]', 'Configuration value')
165
+ .action(async (action, key, value) => {
166
+ const cwd = process.cwd();
167
+ const configPath = node_path_1.default.join(cwd, config_1.CONFIG_FILENAME);
168
+ if (!(await fs_extra_1.default.pathExists(configPath))) {
169
+ logger_1.log.error(`Configuration file not found. Please run ${chalk_1.default.cyan('codexa init')} first.`);
170
+ return;
171
+ }
172
+ const config = await (0, config_1.loadConfig)(cwd);
173
+ if (action === 'set') {
174
+ if (!key || !value) {
175
+ logger_1.log.error('Usage: codexa config set <key> <value>');
176
+ return;
177
+ }
178
+ if (key === 'GROQ_API_KEY') {
179
+ config.groqApiKey = value;
180
+ await (0, config_1.saveConfig)(cwd, config);
181
+ logger_1.log.success(`Updated ${key}`);
182
+ }
183
+ else {
184
+ logger_1.log.error(`Unknown config key: ${key}`);
185
+ }
186
+ }
187
+ else if (action === 'get') {
188
+ if (!key) {
189
+ logger_1.log.error('Usage: codexa config get <key>');
190
+ return;
191
+ }
192
+ if (key === 'GROQ_API_KEY') {
193
+ console.log(config.groqApiKey || 'Not set');
194
+ }
195
+ else {
196
+ logger_1.log.error(`Unknown config key: ${key}`);
197
+ }
198
+ }
199
+ else if (action === 'list') {
200
+ console.log(config);
201
+ }
202
+ else {
203
+ logger_1.log.error('Usage: codexa config <set|get|list> [key] [value]');
204
+ }
205
+ });
117
206
  program.parseAsync(process.argv).catch(handleError);
118
207
  function handleError(error) {
119
208
  if (error instanceof Error) {
@@ -352,7 +352,7 @@ function generateConfig(analysis, baseConfig) {
352
352
  // Start with base config or defaults
353
353
  const config = {
354
354
  modelProvider: 'groq',
355
- model: 'llama-3.1-8b-instant',
355
+ model: 'openai/gpt-oss-120b',
356
356
  embeddingProvider: 'local',
357
357
  embeddingModel: 'Xenova/all-MiniLM-L6-v2',
358
358
  maxChunkSize: 800,
package/dist/config.js CHANGED
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CONFIG_FILENAME = void 0;
6
7
  exports.ensureConfig = ensureConfig;
7
8
  exports.loadConfig = loadConfig;
8
9
  exports.saveConfig = saveConfig;
@@ -13,10 +14,10 @@ const ora_1 = __importDefault(require("ora"));
13
14
  const detector_1 = require("./config/detector");
14
15
  const generator_1 = require("./config/generator");
15
16
  dotenv_1.default.config();
16
- const CONFIG_FILENAME = '.codexarc.json';
17
+ exports.CONFIG_FILENAME = '.codexarc.json';
17
18
  const DEFAULT_CONFIG = {
18
19
  modelProvider: 'groq',
19
- model: 'llama-3.1-8b-instant', // can also use llama-3.3-70b-versatile for better perf
20
+ model: 'openai/gpt-oss-120b', // can also use llama-3.3-70b-versatile for better perf
20
21
  embeddingProvider: 'local',
21
22
  embeddingModel: 'Xenova/all-MiniLM-L6-v2',
22
23
  // localModelUrl: 'http://localhost:11434',
@@ -66,7 +67,7 @@ async function generateDynamicConfig(cwd) {
66
67
  }
67
68
  }
68
69
  async function ensureConfig(cwd) {
69
- const configPath = node_path_1.default.join(cwd, CONFIG_FILENAME);
70
+ const configPath = node_path_1.default.join(cwd, exports.CONFIG_FILENAME);
70
71
  if (!(await fs_extra_1.default.pathExists(configPath))) {
71
72
  const dynamicConfig = await generateDynamicConfig(cwd);
72
73
  await fs_extra_1.default.writeJson(configPath, dynamicConfig, { spaces: 2 });
@@ -74,7 +75,7 @@ async function ensureConfig(cwd) {
74
75
  return loadConfig(cwd);
75
76
  }
76
77
  async function loadConfig(cwd) {
77
- const configPath = node_path_1.default.join(cwd, CONFIG_FILENAME);
78
+ const configPath = node_path_1.default.join(cwd, exports.CONFIG_FILENAME);
78
79
  let config;
79
80
  if (!(await fs_extra_1.default.pathExists(configPath))) {
80
81
  config = { ...DEFAULT_CONFIG };
@@ -88,7 +89,7 @@ async function loadConfig(cwd) {
88
89
  return hydratePaths(cwd, config);
89
90
  }
90
91
  async function saveConfig(cwd, config) {
91
- const configPath = node_path_1.default.join(cwd, CONFIG_FILENAME);
92
+ const configPath = node_path_1.default.join(cwd, exports.CONFIG_FILENAME);
92
93
  const dehydrated = dehydratePaths(cwd, config);
93
94
  await fs_extra_1.default.writeJson(configPath, dehydrated, { spaces: 2 });
94
95
  }
package/dist/ingest.js CHANGED
@@ -92,6 +92,10 @@ async function ingestRepository({ cwd, config, force = false, }) {
92
92
  spinnerFiles.fail('No matching files found.');
93
93
  return;
94
94
  }
95
+ if (allFiles.length > 200) {
96
+ spinnerFiles.stop();
97
+ throw new Error('Codebase is too large, cannot index it');
98
+ }
95
99
  // Filter files: exclude binaries and large files
96
100
  spinnerFiles.text = `Filtering files (found ${allFiles.length})...`;
97
101
  const { included: files, excluded } = await (0, file_filter_1.filterFiles)(allFiles, {
@@ -112,11 +116,22 @@ async function ingestRepository({ cwd, config, force = false, }) {
112
116
  const spinnerChunk = (0, ora_1.default)('Chunking files...').start();
113
117
  const chunks = [];
114
118
  for (const file of files) {
115
- const ch = await (0, chunker_1.chunkFile)(file, config.maxChunkSize, config.chunkOverlap);
116
- ch.forEach((c) => (c.filePath = node_path_1.default.relative(cwd, c.filePath)));
117
- chunks.push(...ch);
119
+ try {
120
+ const ch = await (0, chunker_1.chunkFile)(file, config.maxChunkSize, config.chunkOverlap);
121
+ ch.forEach((c) => (c.filePath = node_path_1.default.relative(cwd, c.filePath)));
122
+ chunks.push(...ch);
123
+ }
124
+ catch (error) {
125
+ spinnerChunk.clear();
126
+ console.warn(`\nFailed to chunk file ${file}: ${error instanceof Error ? error.message : String(error)}`);
127
+ spinnerChunk.render();
128
+ }
118
129
  await tick();
119
130
  }
131
+ if (chunks.length > 20000) {
132
+ spinnerChunk.stop();
133
+ throw new Error('Chunk limit exceeded, unable to create embeddings!');
134
+ }
120
135
  spinnerChunk.succeed(`Chunked files (${chunks.length} chunks)`);
121
136
  const spinnerCompress = (0, ora_1.default)('Compressing chunks...').start();
122
137
  chunks.forEach((c) => (c.compressed = compressText(c.content)));
@@ -131,11 +146,22 @@ async function ingestRepository({ cwd, config, force = false, }) {
131
146
  const batchSize = 32;
132
147
  progress.start(chunks.length, 0);
133
148
  for (let i = 0; i < chunks.length; i += batchSize) {
134
- const batch = chunks.slice(i, i + batchSize);
135
- const texts = batch.map((c) => c.content);
136
- const vectors = await embedder.embed(texts);
137
- batch.forEach((c, idx) => (c.embedding = vectors[idx]));
138
- progress.increment(batch.length);
149
+ try {
150
+ const batch = chunks.slice(i, i + batchSize);
151
+ const texts = batch.map((c) => c.content);
152
+ const vectors = await embedder.embed(texts);
153
+ batch.forEach((c, idx) => (c.embedding = vectors[idx]));
154
+ progress.increment(batch.length);
155
+ }
156
+ catch (error) {
157
+ // Log error but continue with next batch
158
+ // retry or DLQ this batch
159
+ const msg = error instanceof Error ? error.message : String(error);
160
+ // Temporarily stop progress bar to log error
161
+ progress.stop();
162
+ console.error(`\nFailed to embed batch starting at index ${i}: ${msg}`);
163
+ progress.start(chunks.length, i + batchSize);
164
+ }
139
165
  await tick();
140
166
  }
141
167
  progress.stop();
@@ -59,8 +59,10 @@ class OllamaLLM {
59
59
  options.onToken?.(token);
60
60
  }
61
61
  }
62
- catch {
63
- // Should not happen with proper buffering, but ignore if it does
62
+ catch (e) {
63
+ if (process.env.AGENT_DEBUG) {
64
+ console.warn('Failed to parse token:', e);
65
+ }
64
66
  }
65
67
  }
66
68
  }
@@ -74,8 +76,11 @@ class OllamaLLM {
74
76
  options.onToken?.(token);
75
77
  }
76
78
  }
77
- catch {
79
+ catch (e) {
78
80
  // Ignore incomplete final chunk
81
+ if (process.env.AGENT_DEBUG) {
82
+ console.warn('Failed to parse final chunk:', e);
83
+ }
79
84
  }
80
85
  }
81
86
  return full;
@@ -125,10 +130,11 @@ function createLLMClient(config) {
125
130
  if (process.env.AGENT_DEBUG) {
126
131
  console.error('Using Groq client:', config.model);
127
132
  }
128
- if (!process.env.GROQ_API_KEY) {
129
- throw new Error('GROQ_API_KEY is not set. Please set the GROQ_API_KEY environment variable to use Groq models.');
133
+ const apiKey = process.env.GROQ_API_KEY || config.groqApiKey;
134
+ if (!apiKey) {
135
+ throw new Error('GROQ_API_KEY is not set. Please set the GROQ_API_KEY environment variable or use `codexa config set GROQ_API_KEY <key>` to set it.');
130
136
  }
131
- return new GroqLLM(config.model, process.env.GROQ_API_KEY);
137
+ return new GroqLLM(config.model, apiKey);
132
138
  }
133
139
  throw new Error('Only local provider supported for now.');
134
140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexa",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "CLI agent that indexes local repos and answers questions with hosted or local LLMs.",
5
5
  "bin": {
6
6
  "codexa": "bin/codexa.js"
@@ -10,7 +10,6 @@
10
10
  "clean": "rimraf dist",
11
11
  "prepare": "npm run build",
12
12
  "prepublishOnly": "npm run clean && npm run build",
13
- "postinstall": "node scripts/postinstall.js",
14
13
  "dev": "tsx src/cli.ts",
15
14
  "smoke": "ts-node --transpile-only scripts/smoke.ts",
16
15
  "lint": "eslint .",
@@ -19,10 +18,9 @@
19
18
  },
20
19
  "type": "commonjs",
21
20
  "keywords": [
22
- "ai",
23
- "cli",
24
- "rag",
25
- "codebase"
21
+ "ai", "cli", "rag", "codebase", "code-analysis",
22
+ "llm", "embeddings", "vector-search", "developer-tools",
23
+ "code-understanding", "semantic-search", "groq"
26
24
  ],
27
25
  "author": "",
28
26
  "license": "MIT",
@@ -37,7 +35,6 @@
37
35
  "files": [
38
36
  "bin",
39
37
  "dist",
40
- "scripts",
41
38
  "README.md",
42
39
  "LICENSE"
43
40
  ],
@@ -1,58 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Use dynamic import for ESM modules (chalk v5 is ESM-only)
4
- (async () => {
5
- try {
6
- const chalk = (await import('chalk')).default;
7
- const boxen = (await import('boxen')).default;
8
- const gradient = (await import('gradient-string')).default;
9
-
10
- const asciiArt = `
11
- ╔════════════════════════════════════════════════════════╗
12
- ā•‘ ā•‘
13
- ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘
14
- ā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•— ā•‘
15
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā•‘
16
- ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ ā•‘
17
- ā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•”ā• ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā•‘
18
- ā•‘ ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•‘
19
- ā•‘ ā•‘
20
- ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
21
- `;
22
-
23
- const gradientArt = gradient('cyan', 'blue', 'magenta')(asciiArt);
24
-
25
- console.log('\n');
26
- console.log(gradientArt);
27
- console.log('\n');
28
-
29
- const message = boxen(
30
- `${chalk.bold('šŸŽ‰ Codexa installed successfully!')}\n\n` +
31
- `${chalk.dim('Codexa is a CLI tool that helps you understand your codebase using AI.')}\n\n` +
32
- `${chalk.bold('Quick Start:')}\n\n` +
33
- `${chalk.dim('1.')} ${chalk.white('Navigate to your project directory')}\n` +
34
- `${chalk.dim('2.')} ${chalk.white('Initialize Codexa:')} ${chalk.cyan('codexa init')}\n` +
35
- `${chalk.dim('3.')} ${chalk.white('Index your codebase:')} ${chalk.cyan('codexa ingest')}\n` +
36
- `${chalk.dim('4.')} ${chalk.white('Ask questions:')} ${chalk.cyan('codexa ask "your question"')}\n\n` +
37
- `${chalk.dim('For help, run:')} ${chalk.cyan('codexa --help')}\n` +
38
- `${chalk.dim('Or visit:')} ${chalk.cyan('https://github.com/sahitya-chandra/codexa')}`,
39
- {
40
- title: 'šŸš€ Welcome to Codexa',
41
- borderColor: 'cyan',
42
- padding: 1,
43
- margin: 1,
44
- },
45
- );
46
-
47
- console.log(message);
48
- console.log('\n');
49
- } catch {
50
- // Fallback if modules aren't available (shouldn't happen, but just in case)
51
- console.log('\nšŸŽ‰ Codexa installed successfully!\n');
52
- console.log('Quick Start:');
53
- console.log('1. Navigate to your project directory');
54
- console.log('2. Initialize Codexa: codexa init');
55
- console.log('3. Index your codebase: codexa ingest');
56
- console.log('4. Ask questions: codexa ask "your question"\n');
57
- }
58
- })();
package/scripts/smoke.js DELETED
@@ -1,26 +0,0 @@
1
- 'use strict';
2
- var __importDefault =
3
- (this && this.__importDefault) ||
4
- function (mod) {
5
- return mod && mod.__esModule ? mod : { default: mod };
6
- };
7
- Object.defineProperty(exports, '__esModule', { value: true });
8
- const node_path_1 = __importDefault(require('node:path'));
9
- const config_1 = require('../src/config');
10
- const ingest_1 = require('../src/ingest');
11
- const retriever_1 = require('../src/retriever');
12
- async function main() {
13
- const sampleRoot = node_path_1.default.resolve(__dirname, '../examples/sample');
14
- await (0, config_1.ensureConfig)(sampleRoot);
15
- const config = await (0, config_1.loadConfig)(sampleRoot);
16
- await (0, ingest_1.ingestRepository)({ cwd: sampleRoot, config, force: true });
17
- const results = await (0, retriever_1.retrieveContext)('How do we greet someone?', config);
18
- if (results.length === 0) {
19
- throw new Error('Smoke test failed: no retrieval results.');
20
- }
21
- console.log('Top chunk:', results[0].filePath, `${results[0].startLine}-${results[0].endLine}`);
22
- }
23
- main().catch((error) => {
24
- console.error(error);
25
- process.exit(1);
26
- });
package/scripts/smoke.ts DELETED
@@ -1,21 +0,0 @@
1
- import path from 'node:path';
2
- import { ensureConfig, loadConfig } from '../src/config';
3
- import { ingestRepository } from '../src/ingest';
4
- import { retrieveContext } from '../src/retriever';
5
-
6
- async function main(): Promise<void> {
7
- const sampleRoot = path.resolve(__dirname, '../examples/sample');
8
- await ensureConfig(sampleRoot);
9
- const config = await loadConfig(sampleRoot);
10
- await ingestRepository({ cwd: sampleRoot, config, force: true });
11
- const results = await retrieveContext('How do we greet someone?', config);
12
- if (results.length === 0) {
13
- throw new Error('Smoke test failed: no retrieval results.');
14
- }
15
- console.log('Top chunk:', results[0].filePath, `${results[0].startLine}-${results[0].endLine}`);
16
- }
17
-
18
- main().catch((error) => {
19
- console.error(error);
20
- process.exit(1);
21
- });