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 +75 -56
- package/dist/cli.js +104 -15
- package/dist/config.js +5 -4
- package/dist/ingest.js +26 -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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>
|
|
3
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
162
|
-
|
|
149
|
+
```bash
|
|
150
|
+
codexa config set GROQ_API_KEY "gsk_your_api_key_here"
|
|
163
151
|
```
|
|
164
152
|
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
186
|
+
# 2. Run codexa init (defaults to Groq)
|
|
205
187
|
codexa init
|
|
206
188
|
|
|
207
|
-
#
|
|
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. **
|
|
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
|
-
|
|
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.
|
|
274
|
-
ā 3. Run: codexa
|
|
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
|
-
- `--
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
401
|
-
|
|
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
|
|
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
|
|
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
|
|
598
|
+
**Remember:** Set `GROQ_API_KEY`:
|
|
586
599
|
```bash
|
|
587
|
-
|
|
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.
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
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.
|
|
77
|
+
.version('1.2.1')
|
|
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.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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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();
|
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.
|
|
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
|
-
"
|
|
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
|
-
});
|