codexa 1.1.1 → 1.2.1

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
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
  <h1>
3
- <img src="https://github.com/user-attachments/assets/8d571bd6-ba2b-469a-8ddc-3f3ded0fd766" alt="Codexa Logo" width="90" align="absmiddle"> Codexa
3
+ Codexa
4
4
  </h1>
5
5
 
6
6
  <p>
@@ -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
@@ -87,7 +86,7 @@ npm install -g codexa
87
86
  Verify installation:
88
87
 
89
88
  ```bash
90
- codexa --version
89
+ codexa
91
90
  ```
92
91
 
93
92
  #### Method 2: Homebrew (macOS)
@@ -143,35 +142,20 @@ Groq provides fast cloud-based LLMs with a generous free tier.
143
142
  4. Create a new API key
144
143
  5. Copy your API key (starts with `gsk_`)
145
144
 
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
- ```
145
+ **Step 2: Set GROQ API Key**
156
146
 
157
- **Windows (PowerShell):**
158
- ```powershell
159
- $env:GROQ_API_KEY="gsk_your_api_key_here"
147
+ Run the following command to securely save your API key:
160
148
 
161
- # Or add permanently:
162
- [System.Environment]::SetEnvironmentVariable('GROQ_API_KEY', 'gsk_your_api_key_here', 'User')
149
+ ```bash
150
+ codexa config set GROQ_API_KEY "gsk_your_api_key_here"
163
151
  ```
164
152
 
165
- **Windows (Command Prompt):**
166
- ```cmd
167
- setx GROQ_API_KEY "gsk_your_api_key_here"
168
- ```
153
+ This will save the key to your local configuration file (`.codexarc.json`).
169
154
 
170
155
  **Step 3: Verify API Key is Set**
171
156
 
172
157
  ```bash
173
- echo $GROQ_API_KEY # macOS/Linux
174
- echo %GROQ_API_KEY% # Windows CMD
158
+ codexa config get GROQ_API_KEY
175
159
  ```
176
160
 
177
161
  **Step 4: Configure Codexa**
@@ -198,13 +182,14 @@ Codexa defaults to using Groq when you run `codexa init`. If you need to manuall
198
182
  **For Groq:**
199
183
  ```bash
200
184
  # 1. Get API key from console.groq.com
201
- # 2. Set environment variable
202
- export GROQ_API_KEY="gsk_your_key"
203
185
 
204
- # 3. Run codexa init (defaults to Groq)
186
+ # 2. Run codexa init (defaults to Groq)
205
187
  codexa init
206
188
 
207
- # 4. Ready to use!
189
+ # 3. Set GROQ API key
190
+ codexa config set GROQ_API_KEY "gsk_your_key"
191
+
192
+ # 4. Proceed to igestion
208
193
  ```
209
194
 
210
195
 
@@ -224,13 +209,19 @@ Once Codexa is installed and your LLM is configured, you're ready to use it:
224
209
  ```
225
210
  This creates a `.codexarc.json` configuration file with sensible defaults.
226
211
 
227
- 3. **Ingest your codebase:**
212
+ 3. **Set GROQ API Key**
213
+ ```bash
214
+ codexa config set GROQ_API_KEY "gsk_your_key"
215
+ ```
216
+ This will save the key to your local configuration file (`.codexarc.json`).
217
+
218
+ 4. **Ingest your codebase:**
228
219
  ```bash
229
220
  codexa ingest
230
221
  ```
231
222
  This indexes your codebase and creates embeddings. First run may take a few minutes.
232
223
 
233
- 4. **Ask questions:**
224
+ 5. **Ask questions:**
234
225
  ```bash
235
226
  codexa ask "How does the authentication flow work?"
236
227
  codexa ask "What is the main entry point of this application?"
@@ -269,9 +260,10 @@ Analyzing codebase...
269
260
  │ │
270
261
  │ Next Steps: │
271
262
  │ │
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 │
263
+ │ 1. Review .codexarc.json - Update provider keys if needed |
264
+ │ 2. Set your GROQ API Key: codexa config set GROQ_API_KEY |
265
+ │ 3. Run: codexa ingest - Start indexing your codebase │
266
+ │ 4. Run: codexa ask "your question" - Ask questions │
275
267
  │ │
276
268
  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
277
269
  ```
@@ -314,6 +306,30 @@ codexa ingest --force
314
306
 
315
307
  ---
316
308
 
309
+ ### `config`
310
+
311
+ Manage configuration values, including API keys.
312
+
313
+ ```bash
314
+ codexa config <action> [key] [value]
315
+ ```
316
+
317
+ **Actions:**
318
+ - `set <key> <value>` - Set a configuration value
319
+ - `get <key>` - Get a configuration value
320
+ - `list` - List all configuration values
321
+
322
+ **Examples:**
323
+ ```bash
324
+ # Set Groq API Key
325
+ codexa config set GROQ_API_KEY "gsk_..."
326
+
327
+ # Check current key
328
+ codexa config get GROQ_API_KEY
329
+ ```
330
+
331
+ ---
332
+
317
333
  ### `ask`
318
334
 
319
335
  Ask natural language questions about your codebase.
@@ -326,8 +342,8 @@ codexa ask <question...> [options]
326
342
  - `<question...>` - Your question (can be multiple words)
327
343
 
328
344
  **Options:**
329
- - `-s, --session <name>` - Session identifier to maintain conversation context (default: `"default"`)
330
- - `--no-stream` - Disable streaming output (show full response at once)
345
+ <!-- - `-s, --session <name>` - Session identifier to maintain conversation context (default: `"default"`) -->
346
+ - `--stream` - Enable streaming output
331
347
 
332
348
  **Examples:**
333
349
  ```bash
@@ -337,14 +353,8 @@ codexa ask "How does user authentication work?"
337
353
  # Question with multiple words
338
354
  codexa ask "What is the main entry point of this application?"
339
355
 
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
356
+ # Enable streaming
357
+ codexa ask "Summarize the codebase structure" --stream
348
358
  ```
349
359
 
350
360
  **How it works:**
@@ -393,12 +403,15 @@ Some settings can be configured via environment variables:
393
403
  | Variable | Description | Required For |
394
404
  |----------|-------------|--------------|
395
405
  | `GROQ_API_KEY` | Groq API key for cloud LLM | Groq provider |
396
- | `OPENAI_API_KEY` | OpenAI API key (for embeddings) | OpenAI embeddings |
406
+ <!-- | `OPENAI_API_KEY` | OpenAI API key (for embeddings) | OpenAI embeddings | -->
397
407
 
398
408
  **Example:**
399
409
  ```bash
400
- export GROQ_API_KEY="gsk_your_key_here"
401
- export OPENAI_API_KEY="sk-your_key_here" # If using OpenAI embeddings
410
+ # Using config command (Recommended)
411
+ codexa config set GROQ_API_KEY "gsk_your_key_here"
412
+
413
+ # Or using environment variables
414
+ export GROQ_API_KEY="gsk_your_key_here" # macOS/Linux
402
415
  ```
403
416
 
404
417
  ### Configuration Options
@@ -532,7 +545,7 @@ Maximum file size in bytes. Files larger than this will be excluded from indexin
532
545
  **Example:**
533
546
  ```json
534
547
  {
535
- "maxFileSize": 10485760 // 10MB
548
+ "maxFileSize": 10485760
536
549
  }
537
550
  ```
538
551
 
@@ -561,7 +574,7 @@ Whether to skip files exceeding `maxFileSize` during indexing. Set to `false` if
561
574
  ```json
562
575
  {
563
576
  "skipLargeFiles": true,
564
- "maxFileSize": 10485760 // 10MB
577
+ "maxFileSize": 10485760
565
578
  }
566
579
  ```
567
580
 
@@ -582,9 +595,9 @@ Whether to skip files exceeding `maxFileSize` during indexing. Set to `false` if
582
595
  }
583
596
  ```
584
597
 
585
- **Remember:** Set `GROQ_API_KEY` environment variable:
598
+ **Remember:** Set `GROQ_API_KEY`:
586
599
  ```bash
587
- export GROQ_API_KEY="your-api-key"
600
+ codexa config set GROQ_API_KEY "your-api-key"
588
601
  ```
589
602
 
590
603
 
@@ -623,10 +636,13 @@ export GROQ_API_KEY="your-api-key"
623
636
  cd my-project
624
637
  codexa init
625
638
 
626
- # 2. Index your codebase
639
+ # 2. Set Groq Api Key
640
+ codexa config set GROQ_API_KEY <your-groq-key>
641
+
642
+ # 3. Index your codebase
627
643
  codexa ingest
628
644
 
629
- # 3. Ask questions
645
+ # 4. Ask questions
630
646
  codexa ask "What is the main purpose of this codebase?"
631
647
  codexa ask "How does the user authentication work?"
632
648
  codexa ask "Where is the API routing configured?"
@@ -748,14 +764,17 @@ When you run `codexa ask`:
748
764
  **Problem:** Using Groq provider but API key is missing.
749
765
 
750
766
  **Solutions:**
751
- 1. Set the environment variable:
767
+ 1. Set the API key using the config command (Recommended):
768
+ ```bash
769
+ codexa config set GROQ_API_KEY "your-api-key"
770
+ ```
771
+ 2. Or set the environment variable:
752
772
  ```bash
753
- export GROQ_API_KEY="your-api-key"
773
+ export GROQ_API_KEY="your-api-key" # macOS/Linux
754
774
  ```
755
- 2. Or add it to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.)
756
775
  3. Verify it's set:
757
776
  ```bash
758
- echo $GROQ_API_KEY
777
+ codexa config get GROQ_API_KEY
759
778
  ```
760
779
 
761
780
  ### 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.1.1')
77
+ .version('1.2.1')
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) {
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,7 +14,7 @@ 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
20
  model: 'llama-3.1-8b-instant', // can also use llama-3.3-70b-versatile for better perf
@@ -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
@@ -112,9 +112,16 @@ async function ingestRepository({ cwd, config, force = false, }) {
112
112
  const spinnerChunk = (0, ora_1.default)('Chunking files...').start();
113
113
  const chunks = [];
114
114
  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);
115
+ try {
116
+ const ch = await (0, chunker_1.chunkFile)(file, config.maxChunkSize, config.chunkOverlap);
117
+ ch.forEach((c) => (c.filePath = node_path_1.default.relative(cwd, c.filePath)));
118
+ chunks.push(...ch);
119
+ }
120
+ catch (error) {
121
+ spinnerChunk.clear();
122
+ console.warn(`\nFailed to chunk file ${file}: ${error instanceof Error ? error.message : String(error)}`);
123
+ spinnerChunk.render();
124
+ }
118
125
  await tick();
119
126
  }
120
127
  spinnerChunk.succeed(`Chunked files (${chunks.length} chunks)`);
@@ -131,11 +138,22 @@ async function ingestRepository({ cwd, config, force = false, }) {
131
138
  const batchSize = 32;
132
139
  progress.start(chunks.length, 0);
133
140
  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);
141
+ try {
142
+ const batch = chunks.slice(i, i + batchSize);
143
+ const texts = batch.map((c) => c.content);
144
+ const vectors = await embedder.embed(texts);
145
+ batch.forEach((c, idx) => (c.embedding = vectors[idx]));
146
+ progress.increment(batch.length);
147
+ }
148
+ catch (error) {
149
+ // Log error but continue with next batch
150
+ // retry or DLQ this batch
151
+ const msg = error instanceof Error ? error.message : String(error);
152
+ // Temporarily stop progress bar to log error
153
+ progress.stop();
154
+ console.error(`\nFailed to embed batch starting at index ${i}: ${msg}`);
155
+ progress.start(chunks.length, i + batchSize);
156
+ }
139
157
  await tick();
140
158
  }
141
159
  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.1.1",
3
+ "version": "1.2.1",
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
- });