Skip to content

Commit 396f3da

Browse files
committed
feat: add doctor command and overhaul global config workflow
- Add new `doctor` CLI command to inspect active configuration and its source locations - Rewrite config loader to use global-first priority, with ~/.codeindex/.env as primary runtime config - Add automatic migration of legacy global config.json to standardized .env format - Update `setup` command to save both legacy config.json and new .env global files - Revise all project and CLI documentation to recommend global setup over per-project .env - Simplify `init` command by removing redundant setup prompts and using global config defaults - Add comprehensive test coverage for the new config loading and migration logic
1 parent 55a9a91 commit 396f3da

9 files changed

Lines changed: 628 additions & 150 deletions

File tree

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ After: Paste 3 files (2KB) → Same answer
7070
# 1. Install
7171
pnpm install -g @codeindex/cli
7272

73-
# 2. Configure your project's .env
74-
cp .env.example .env
75-
# then edit .env with your API key
73+
# 2. Setup once globally
74+
codeindex setup
7675

7776
# 3. Index your project
7877
cd your-project
@@ -82,14 +81,18 @@ codeindex index .
8281
codeindex query "How does the auth module work?"
8382
```
8483

85-
For NVIDIA, a minimal `.env` is enough:
84+
Global setup is stored under `~/.codeindex/` and reused for all future projects.
85+
86+
If you prefer env-based global runtime config, `codeindex setup` also writes `~/.codeindex/.env`.
87+
88+
For NVIDIA, a minimal global env looks like:
8689

8790
```env
8891
NVIDIA_API_KEY=nvapi-...
8992
CODEINDEX_BASE_URL=https://integrate.api.nvidia.com/v1
9093
```
9194

92-
**That's it.** Project-first config, no need to run global setup for every project.
95+
**That's it.** Run setup once, then `codeindex index` works across projects.
9396

9497
***
9598

docs/API.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ Project-level configuration. Recommended: keep this file minimal and put API/pro
168168

169169
### `.env`
170170

171-
Recommended per-project API configuration.
171+
Optional per-project override configuration.
172172

173173
```env
174174
NVIDIA_API_KEY=nvapi-...
@@ -180,9 +180,20 @@ CODEINDEX_BASE_URL=https://integrate.api.nvidia.com/v1
180180

181181
If `NVIDIA_API_KEY` or the NVIDIA base URL is present, `codeindex` can infer the NVIDIA provider automatically.
182182

183+
### `~/.codeindex/.env`
184+
185+
Recommended global runtime configuration when you want to run `codeindex setup` once and reuse it across all projects.
186+
187+
```env
188+
CODEINDEX_PROVIDER=nvidia
189+
CODEINDEX_API_KEY=nvapi-...
190+
CODEINDEX_MODEL=minimaxai/minimax-m3
191+
CODEINDEX_BASE_URL=https://integrate.api.nvidia.com/v1
192+
```
193+
183194
### `~/.codeindex/config.json`
184195

185-
Optional global configuration (created by `codeindex setup`).
196+
Backward-compatible global configuration (also created by `codeindex setup`).
186197

187198
```json
188199
{

packages/cli/README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
# Install
1111
npm install -g @codeindex/cli
1212

13-
# Configure your project's .env
14-
cp .env.example .env
15-
# then edit .env with your API key
13+
# Setup once globally
14+
codeindex setup
1615

1716
# Index your project
1817
cd your-project
@@ -26,22 +25,24 @@ codeindex query "How does authentication work?"
2625

2726
| Command | Description |
2827
|---------|-------------|
29-
| `codeindex setup` | Optional global configuration |
28+
| `codeindex setup` | Global runtime configuration (recommended) |
3029
| `codeindex init [path]` | Initialize project |
3130
| `codeindex index [path]` | Build/rebuild index |
3231
| `codeindex query "<text>"` | Query the index |
3332
| `codeindex update [path]` | Incremental update |
3433
| `codeindex status [path]` | Check index health |
3534
| `codeindex serve [path]` | HTTP server for IDE integration |
3635

37-
## Recommended Project Config
36+
## Global Config
3837

3938
```env
40-
NVIDIA_API_KEY=nvapi-...
39+
CODEINDEX_PROVIDER=nvidia
40+
CODEINDEX_API_KEY=nvapi-...
41+
CODEINDEX_MODEL=minimaxai/minimax-m3
4142
CODEINDEX_BASE_URL=https://integrate.api.nvidia.com/v1
4243
```
4344

44-
`codeindex` automatically reads `.env` from the project root, so in most cases you do not need `codeindex setup`.
45+
`codeindex setup` writes global config to `~/.codeindex/config.json` and `~/.codeindex/.env`, so in most cases you only need to configure it once.
4546

4647
## Features
4748

packages/cli/src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { registerStatusCommand } from "./commands/status.js"
1919
import { registerServeCommand } from "./commands/serve.js"
2020
import { registerInitCommand } from "./commands/init.js"
2121
import { registerSetupCommand } from "./commands/setup.js"
22+
import { registerDoctorCommand } from "./commands/doctor.js"
2223

2324
const program = new Command()
2425

@@ -37,5 +38,6 @@ registerQueryCommand(program)
3738
registerUpdateCommand(program)
3839
registerStatusCommand(program)
3940
registerServeCommand(program)
41+
registerDoctorCommand(program)
4042

4143
program.parse()
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { Command } from "commander"
2+
import * as path from "path"
3+
import { type CodeIndexConfig, inspectConfig, resolveApiKey } from "../config.js"
4+
5+
function formatValue(key: keyof CodeIndexConfig, value: unknown): string {
6+
if (value === undefined || value === null || value === "") {
7+
return "(unset)"
8+
}
9+
if (key === "apiKey" || key === "serverApiKey") {
10+
return "(set)"
11+
}
12+
return String(value)
13+
}
14+
15+
export function registerDoctorCommand(program: Command): void {
16+
program
17+
.command("doctor [path]")
18+
.description("Hiển thị config hiệu lực và nguồn cấu hình đang được dùng")
19+
.option("--provider <provider>", "Override provider khi kiểm tra")
20+
.option("--model <model>", "Override model khi kiểm tra")
21+
.option("--index-dir <dir>", "Override index dir khi kiểm tra")
22+
.option("--json", "Output as JSON")
23+
.action((targetPath: string | undefined, options: Record<string, string | boolean>) => {
24+
const projectRoot = path.resolve(targetPath ?? ".")
25+
26+
const overrides: Partial<CodeIndexConfig> = {}
27+
if (options["provider"]) overrides.provider = options["provider"] as CodeIndexConfig["provider"]
28+
if (options["model"]) overrides.model = options["model"] as string
29+
if (options["indexDir"]) overrides.indexDir = options["indexDir"] as string
30+
31+
const debug = inspectConfig(projectRoot, overrides)
32+
33+
if (options["json"] === true) {
34+
let resolvedApiKey = false
35+
try {
36+
resolveApiKey(debug.effective)
37+
resolvedApiKey = true
38+
} catch {}
39+
40+
console.log(JSON.stringify({ ...debug, resolvedApiKey }, null, 2))
41+
return
42+
}
43+
44+
console.log(`🩺 codeindex doctor: ${debug.projectRoot}`)
45+
console.log(` Global dir : ${debug.globalConfigDir}`)
46+
console.log("")
47+
48+
const orderedKeys: Array<keyof CodeIndexConfig> = [
49+
"provider",
50+
"model",
51+
"apiKey",
52+
"baseURL",
53+
"indexDir",
54+
"projectName",
55+
"verbose",
56+
"serverApiKey",
57+
"serverCorsOrigin",
58+
"serverMaxBodyBytes",
59+
"serverRateLimitPerMinute",
60+
]
61+
62+
for (const key of orderedKeys) {
63+
const source = debug.fields[key]
64+
const value = debug.effective[key]
65+
console.log(`${key.padEnd(24)} ${formatValue(key, value)}`)
66+
if (source) {
67+
const sourceText = source.key ? `${source.source} -> ${source.location} (${source.key})` : `${source.source} -> ${source.location}`
68+
console.log(` from ${sourceText}`)
69+
}
70+
}
71+
72+
try {
73+
resolveApiKey(debug.effective)
74+
console.log("\n✅ API key resolved successfully")
75+
} catch (error) {
76+
console.log(`\n❌ API key not resolved: ${(error as Error).message}`)
77+
}
78+
})
79+
}

packages/cli/src/commands/init.ts

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ export function registerInitCommand(program: Command): void {
3030
// Load global config để lấy defaults
3131
const currentConfig = loadConfig(projectRoot)
3232

33-
let provider = currentConfig.provider
34-
let model = currentConfig.model
3533
let indexDir = currentConfig.indexDir
3634

3735
// Nếu global config đã có đủ thông tin (apiKey + provider), skip hỏi
@@ -45,25 +43,7 @@ export function registerInitCommand(program: Command): void {
4543

4644
console.log("\n🔧 codeindex project init\n")
4745
console.log(`⚠️ Chưa tìm thấy cấu hình toàn cục. Bạn nên chạy 'codeindex setup' trước.`)
48-
console.log(` Hoặc tạo file .env trong project root, ví dụ: NVIDIA_API_KEY=... \n`)
49-
50-
const providerInput = await ask(
51-
rl,
52-
`LLM provider [openai/anthropic/google/nvidia/custom/ollama] (mặc định: ${provider}): `
53-
)
54-
provider = (providerInput.trim() || provider) as any
55-
56-
const modelDefaults: Record<string, string> = {
57-
openai: "gpt-4o",
58-
anthropic: "claude-sonnet-4-5",
59-
google: "gemini-1.5-flash",
60-
nvidia: "minimaxai/minimax-m3",
61-
custom: "gpt-4o-compatible",
62-
ollama: "llama3.2",
63-
}
64-
const defaultModel = modelDefaults[provider] ?? "gpt-4o"
65-
const modelInput = await ask(rl, `Model (mặc định: ${model || defaultModel}): `)
66-
model = modelInput.trim() || model || defaultModel
46+
console.log(` Lệnh 'setup' sẽ lưu vào ~/.codeindex/.env và dùng cho mọi project sau này.\n`)
6747

6848
const indexDirInput = await ask(rl, `Index directory (mặc định: ${indexDir}): `)
6949
indexDir = indexDirInput.trim() || indexDir
@@ -76,8 +56,6 @@ export function registerInitCommand(program: Command): void {
7656
}
7757

7858
const config: any = {
79-
provider,
80-
model,
8159
indexDir,
8260
}
8361

@@ -96,21 +74,11 @@ export function registerInitCommand(program: Command): void {
9674
console.log(`\n✅ Đã tạo .codeindex.json`)
9775
console.log(`\nNext steps:`)
9876

99-
const envMap: Record<string, string> = {
100-
openai: "OPENAI_API_KEY",
101-
anthropic: "ANTHROPIC_API_KEY",
102-
google: "GOOGLE_API_KEY",
103-
nvidia: "NVIDIA_API_KEY",
104-
custom: "CUSTOM_API_KEY",
105-
ollama: "(không cần key)",
106-
}
107-
const envVar = envMap[provider] ?? "OPENAI_API_KEY"
108-
109-
if (currentConfig.apiKey || provider === "ollama") {
77+
if (currentConfig.apiKey || currentConfig.provider === "ollama") {
11078
console.log(` 1. Build the index : codeindex index .`)
11179
console.log(` 2. Query the index : codeindex query "how does auth work?"`)
11280
} else {
113-
console.log(` 1. Set your API key: chạy 'codeindex setup', tạo file .env, hoặc 'export ${envVar}=<your-key>'`)
81+
console.log(` 1. Chạy 'codeindex setup' để lưu cấu hình toàn cục vào ~/.codeindex/.env`)
11482
console.log(` 2. Build the index : codeindex index .`)
11583
console.log(` 3. Query the index : codeindex query "how does auth work?"`)
11684
}

packages/cli/src/commands/setup.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Command } from "commander"
22
import * as readline from "readline"
3-
import { saveGlobalConfig } from "../config.js"
3+
import { saveGlobalConfig, saveGlobalEnv } from "../config.js"
44

55
function ask(rl: readline.Interface, question: string): Promise<string> {
66
return new Promise((resolve) => rl.question(question, resolve))
@@ -69,9 +69,11 @@ export function registerSetupCommand(program: Command): void {
6969
}
7070

7171
saveGlobalConfig(config)
72+
saveGlobalEnv(config)
7273

7374
rl.close()
7475
console.log(`\n✅ Đã lưu cấu hình toàn cục vào ~/.codeindex/config.json`)
75-
console.log(`\n✨ Xong! Bây giờ bạn có thể dùng 'codeindex init' hoặc 'codeindex index' ở bất kỳ đâu.`)
76+
console.log(`✅ Đã lưu runtime env toàn cục vào ~/.codeindex/.env`)
77+
console.log(`\n✨ Xong! Chạy setup một lần, các lần sau 'codeindex index' sẽ dùng cấu hình toàn cục này.`)
7678
})
7779
}

0 commit comments

Comments
 (0)