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 +83 -62
- package/dist/cli.js +104 -15
- package/dist/config/generator.js +1 -1
- package/dist/config.js +6 -5
- package/dist/ingest.js +34 -8
- package/dist/models/index.js +12 -6
- package/package.json +4 -7
- package/scripts/postinstall.js +0 -58
- package/scripts/smoke.js +0 -26
- package/scripts/smoke.ts +0 -21
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
162
|
-
|
|
151
|
+
```bash
|
|
152
|
+
codexa config set GROQ_API_KEY "gsk_your_api_key_here"
|
|
163
153
|
```
|
|
164
154
|
|
|
165
|
-
|
|
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
|
-
|
|
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": "
|
|
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
|
-
**
|
|
191
|
-
- `
|
|
192
|
-
- `llama-3.1-70b-versatile`
|
|
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
|
-
#
|
|
188
|
+
# 2. Run codexa init (defaults to Groq)
|
|
205
189
|
codexa init
|
|
206
190
|
|
|
207
|
-
#
|
|
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. **
|
|
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
|
-
|
|
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.
|
|
274
|
-
ā 3. Run: codexa
|
|
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
|
-
- `--
|
|
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
|
-
#
|
|
341
|
-
codexa ask "
|
|
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
|
-
|
|
401
|
-
|
|
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:** `"
|
|
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
|
|
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
|
|
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": "
|
|
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
|
|
600
|
+
**Remember:** Set `GROQ_API_KEY`:
|
|
586
601
|
```bash
|
|
587
|
-
|
|
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": "
|
|
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.
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
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.
|
|
77
|
+
.version('1.2.2')
|
|
27
78
|
.action(() => {
|
|
28
|
-
|
|
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('
|
|
53
|
-
`${
|
|
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/generator.js
CHANGED
|
@@ -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: '
|
|
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
|
-
|
|
17
|
+
exports.CONFIG_FILENAME = '.codexarc.json';
|
|
17
18
|
const DEFAULT_CONFIG = {
|
|
18
19
|
modelProvider: 'groq',
|
|
19
|
-
model: '
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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();
|
package/dist/models/index.js
CHANGED
|
@@ -59,8 +59,10 @@ class OllamaLLM {
|
|
|
59
59
|
options.onToken?.(token);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
catch {
|
|
63
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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,
|
|
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.
|
|
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
|
-
"
|
|
24
|
-
"
|
|
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
|
],
|
package/scripts/postinstall.js
DELETED
|
@@ -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
|
-
});
|