vibe-code-explainer 0.2.0 → 0.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
|
@@ -214,14 +214,36 @@ Every explanation includes a risk rating:
|
|
|
214
214
|
|
|
215
215
|
## Configuration
|
|
216
216
|
|
|
217
|
-
Change any setting at any time by running
|
|
217
|
+
Change any setting at any time by running `config`. Which command depends on
|
|
218
|
+
how you installed it:
|
|
219
|
+
|
|
220
|
+
**If you installed globally** (the package is on your PATH):
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
vibe-code-explainer config
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**If you installed per-project** (or want to be safe):
|
|
218
227
|
|
|
219
228
|
```bash
|
|
220
229
|
npx vibe-code-explainer config
|
|
221
230
|
```
|
|
222
231
|
|
|
223
|
-
|
|
224
|
-
|
|
232
|
+
Both commands do the same thing. Use whichever works in your shell. If you
|
|
233
|
+
see a stale version run via `npx`, force the latest with:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npx vibe-code-explainer@latest config
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The menu auto-detects whether to edit the project config (`./code-explainer.config.json`)
|
|
240
|
+
or the global config (`~/.code-explainer.config.json`): project config wins
|
|
241
|
+
if both exist.
|
|
242
|
+
|
|
243
|
+
It opens an interactive menu showing your current settings. Pick what you
|
|
244
|
+
want to change, one at a time. Every change is saved immediately. When you
|
|
245
|
+
change the model, the tool checks whether Ollama already has it pulled and
|
|
246
|
+
offers to download it on the spot if not.
|
|
225
247
|
|
|
226
248
|
```
|
|
227
249
|
code-explainer config (project)
|
|
@@ -332,10 +354,14 @@ Changes take effect on the next Claude Code tool call — no restart needed.
|
|
|
332
354
|
|
|
333
355
|
## Session tools
|
|
334
356
|
|
|
357
|
+
All commands below accept both forms: `vibe-code-explainer <cmd>` (if
|
|
358
|
+
globally installed, on PATH) or `npx vibe-code-explainer <cmd>` (if per-project).
|
|
359
|
+
|
|
335
360
|
### Summary of what Claude has done
|
|
336
361
|
|
|
337
362
|
```bash
|
|
338
|
-
|
|
363
|
+
vibe-code-explainer summary
|
|
364
|
+
# or: npx vibe-code-explainer summary
|
|
339
365
|
```
|
|
340
366
|
|
|
341
367
|
Prints a report of every explained change in your current Claude Code session:
|
|
@@ -344,7 +370,8 @@ total changes, files touched, risk breakdown, and flagged unrelated files.
|
|
|
344
370
|
### Clear the session
|
|
345
371
|
|
|
346
372
|
```bash
|
|
347
|
-
|
|
373
|
+
vibe-code-explainer session end
|
|
374
|
+
# or: npx vibe-code-explainer session end
|
|
348
375
|
```
|
|
349
376
|
|
|
350
377
|
Clears the session state (tracked files and cache). Run this when you start a
|
|
@@ -353,7 +380,8 @@ new task so the drift-detection summary doesn't include old edits.
|
|
|
353
380
|
### Warm up the model
|
|
354
381
|
|
|
355
382
|
```bash
|
|
356
|
-
|
|
383
|
+
vibe-code-explainer warmup
|
|
384
|
+
# or: npx vibe-code-explainer warmup
|
|
357
385
|
```
|
|
358
386
|
|
|
359
387
|
Sends a trivial diff to Ollama to pre-load the model. Useful if you closed and
|
|
@@ -365,17 +393,29 @@ slow (10–15 seconds) as the model gets loaded into VRAM.
|
|
|
365
393
|
## Uninstall
|
|
366
394
|
|
|
367
395
|
```bash
|
|
368
|
-
|
|
396
|
+
vibe-code-explainer uninstall
|
|
397
|
+
# or: npx vibe-code-explainer uninstall
|
|
369
398
|
```
|
|
370
399
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
400
|
+
The uninstaller auto-detects whether you have a project install, a global
|
|
401
|
+
install, or both. If both exist, it asks which to remove.
|
|
402
|
+
|
|
403
|
+
**Project uninstall:** removes the PostToolUse hook entries from
|
|
404
|
+
`.claude/settings.local.json` and deletes `code-explainer.config.json`. Other
|
|
405
|
+
entries (hooks from other tools, other settings) are preserved.
|
|
406
|
+
|
|
407
|
+
**Global uninstall:** removes the PostToolUse hooks from
|
|
408
|
+
`~/.claude/settings.json` and deletes `~/.code-explainer.config.json`. The
|
|
409
|
+
globally-installed npm package stays on disk — remove it with:
|
|
374
410
|
|
|
375
|
-
|
|
411
|
+
```bash
|
|
412
|
+
npm uninstall -g vibe-code-explainer
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Ollama and any pulled models stay installed either way. To remove them:
|
|
376
416
|
|
|
377
417
|
```bash
|
|
378
|
-
ollama rm
|
|
418
|
+
ollama rm qwen3.5:4b
|
|
379
419
|
# or whichever model you pulled
|
|
380
420
|
```
|
|
381
421
|
|
|
@@ -395,9 +435,10 @@ Most common causes, in order:
|
|
|
395
435
|
ollama serve
|
|
396
436
|
```
|
|
397
437
|
Then try another edit.
|
|
398
|
-
3. **The engine is misconfigured.** Run `
|
|
399
|
-
check the engine and URL. Try
|
|
400
|
-
test — if that works, Ollama is
|
|
438
|
+
3. **The engine is misconfigured.** Run `vibe-code-explainer config` (or
|
|
439
|
+
`npx vibe-code-explainer config`) and check the engine and URL. Try
|
|
440
|
+
switching to the Claude Code engine as a test — if that works, Ollama is
|
|
441
|
+
the issue.
|
|
401
442
|
4. **The file is excluded.** Check your `exclude` patterns in the config.
|
|
402
443
|
|
|
403
444
|
### The explanation took too long and got skipped
|
|
@@ -405,8 +446,8 @@ Most common causes, in order:
|
|
|
405
446
|
Default timeout is 8 seconds. Cold starts with Ollama (first explanation
|
|
406
447
|
after starting the service) can be 10–15 seconds. Two fixes:
|
|
407
448
|
|
|
408
|
-
- Run `
|
|
409
|
-
- Raise the timeout to 15 seconds via `
|
|
449
|
+
- Run `vibe-code-explainer warmup` right after starting Ollama.
|
|
450
|
+
- Raise the timeout to 15 seconds via `vibe-code-explainer config →
|
|
410
451
|
Latency timeout`.
|
|
411
452
|
|
|
412
453
|
### Explanations are low-quality
|
|
@@ -416,7 +457,7 @@ business logic, security-relevant changes). If explanations feel wrong on
|
|
|
416
457
|
tricky diffs, switch to the Claude Code engine:
|
|
417
458
|
|
|
418
459
|
```bash
|
|
419
|
-
|
|
460
|
+
vibe-code-explainer config
|
|
420
461
|
# → Engine → Claude Code (native)
|
|
421
462
|
```
|
|
422
463
|
|
|
@@ -428,7 +469,7 @@ cost of API tokens per explanation.
|
|
|
428
469
|
Add a glob pattern via the config menu:
|
|
429
470
|
|
|
430
471
|
```
|
|
431
|
-
|
|
472
|
+
vibe-code-explainer config → File exclusions → Add a pattern
|
|
432
473
|
```
|
|
433
474
|
|
|
434
475
|
For example, `*.md` to skip all markdown files, or `src/generated/**` to skip
|
|
@@ -437,7 +478,7 @@ a whole directory.
|
|
|
437
478
|
### I want to turn off Bash explanations but keep Edit/Write
|
|
438
479
|
|
|
439
480
|
```
|
|
440
|
-
|
|
481
|
+
vibe-code-explainer config → Enable/disable hooks
|
|
441
482
|
```
|
|
442
483
|
|
|
443
484
|
Uncheck Bash, keep Edit and Write checked.
|
package/dist/cli/index.js
CHANGED
|
@@ -14,8 +14,64 @@ import "./chunk-7OCVIDC7.js";
|
|
|
14
14
|
// src/cli/config.ts
|
|
15
15
|
import { intro, outro, select, confirm, text, cancel, isCancel, note } from "@clack/prompts";
|
|
16
16
|
import pc from "picocolors";
|
|
17
|
+
import { spawn } from "child_process";
|
|
17
18
|
import { existsSync, writeFileSync } from "fs";
|
|
18
19
|
import { join } from "path";
|
|
20
|
+
async function listInstalledOllamaModels(url) {
|
|
21
|
+
try {
|
|
22
|
+
const ctrl = new AbortController();
|
|
23
|
+
const timer = setTimeout(() => ctrl.abort(), 3e3);
|
|
24
|
+
const res = await fetch(`${url}/api/tags`, { signal: ctrl.signal });
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
if (!res.ok) return null;
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
if (!data.models) return [];
|
|
29
|
+
return data.models.map((m) => m.name ?? m.model ?? "").filter((n) => n.length > 0);
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function normalizeModelName(name) {
|
|
35
|
+
return name.toLowerCase().split(/[-_]/)[0];
|
|
36
|
+
}
|
|
37
|
+
function hasModel(installed, wanted) {
|
|
38
|
+
const wantedNorm = normalizeModelName(wanted);
|
|
39
|
+
return installed.some((n) => {
|
|
40
|
+
const base = n.toLowerCase();
|
|
41
|
+
if (base === wanted.toLowerCase()) return true;
|
|
42
|
+
if (base === wanted.toLowerCase()) return true;
|
|
43
|
+
return normalizeModelName(base).startsWith(wantedNorm);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function pullOllamaModel(model) {
|
|
47
|
+
note(
|
|
48
|
+
`Pulling ${pc.cyan(model)}
|
|
49
|
+
${pc.dim("This can take a while on the first run (several GB download).")}`,
|
|
50
|
+
"Downloading model"
|
|
51
|
+
);
|
|
52
|
+
return new Promise((resolvePromise) => {
|
|
53
|
+
const child = spawn("ollama", ["pull", model], { stdio: "inherit" });
|
|
54
|
+
child.on("error", () => {
|
|
55
|
+
process.stderr.write(
|
|
56
|
+
pc.red("\nFailed to run `ollama pull`. Make sure Ollama is installed and running.\n")
|
|
57
|
+
);
|
|
58
|
+
resolvePromise(false);
|
|
59
|
+
});
|
|
60
|
+
child.on("close", (code) => {
|
|
61
|
+
if (code === 0) {
|
|
62
|
+
process.stdout.write(pc.green(`
|
|
63
|
+
\u2713 Pulled ${model}
|
|
64
|
+
`));
|
|
65
|
+
resolvePromise(true);
|
|
66
|
+
} else {
|
|
67
|
+
process.stderr.write(pc.red(`
|
|
68
|
+
\u2717 ollama pull exited with code ${code}
|
|
69
|
+
`));
|
|
70
|
+
resolvePromise(false);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
19
75
|
function handleCancel(value) {
|
|
20
76
|
if (isCancel(value)) {
|
|
21
77
|
cancel("Exited without saving.");
|
|
@@ -63,6 +119,40 @@ async function changeModel(config) {
|
|
|
63
119
|
initialValue: config.ollamaModel
|
|
64
120
|
});
|
|
65
121
|
handleCancel(value);
|
|
122
|
+
if (value === config.ollamaModel) {
|
|
123
|
+
return config;
|
|
124
|
+
}
|
|
125
|
+
const installed = await listInstalledOllamaModels(config.ollamaUrl);
|
|
126
|
+
if (installed === null) {
|
|
127
|
+
note(
|
|
128
|
+
`Could not reach Ollama at ${pc.cyan(config.ollamaUrl)}. The model will be selected, but you'll need to pull it manually with ${pc.cyan(`ollama pull ${value}`)} before the first explanation.`,
|
|
129
|
+
"Ollama unreachable"
|
|
130
|
+
);
|
|
131
|
+
return { ...config, ollamaModel: value };
|
|
132
|
+
}
|
|
133
|
+
if (hasModel(installed, value)) {
|
|
134
|
+
note(`${pc.green("\u2713")} Model ${pc.cyan(value)} is already installed.`, "Model ready");
|
|
135
|
+
return { ...config, ollamaModel: value };
|
|
136
|
+
}
|
|
137
|
+
const shouldPull = await confirm({
|
|
138
|
+
message: `Model ${value} is not installed locally. Pull it now?`,
|
|
139
|
+
initialValue: true
|
|
140
|
+
});
|
|
141
|
+
handleCancel(shouldPull);
|
|
142
|
+
if (!shouldPull) {
|
|
143
|
+
note(
|
|
144
|
+
`Saved the selection, but you must run ${pc.cyan(`ollama pull ${value}`)} before it works.`,
|
|
145
|
+
"Model not pulled"
|
|
146
|
+
);
|
|
147
|
+
return { ...config, ollamaModel: value };
|
|
148
|
+
}
|
|
149
|
+
const pullOk = await pullOllamaModel(value);
|
|
150
|
+
if (!pullOk) {
|
|
151
|
+
note(
|
|
152
|
+
`Pull failed. Saving the model selection anyway \u2014 run ${pc.cyan(`ollama pull ${value}`)} manually when Ollama is reachable.`,
|
|
153
|
+
"Pull failed"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
66
156
|
return { ...config, ollamaModel: value };
|
|
67
157
|
}
|
|
68
158
|
async function changeUrl(config) {
|
|
@@ -227,4 +317,4 @@ Run ${pc.cyan("npx vibe-code-explainer init")} first.`
|
|
|
227
317
|
export {
|
|
228
318
|
runConfig
|
|
229
319
|
};
|
|
230
|
-
//# sourceMappingURL=config-
|
|
320
|
+
//# sourceMappingURL=config-5PDPXG7Z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/config.ts"],"sourcesContent":["import { intro, outro, select, confirm, text, cancel, isCancel, note } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { spawn } from \"node:child_process\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n DEFAULT_CONFIG,\n loadConfig,\n LANGUAGE_NAMES,\n CONFIG_FILENAME,\n getGlobalConfigPath,\n type Config,\n type Engine,\n type DetailLevel,\n type Language,\n} from \"../config/schema.js\";\nimport { MODEL_OPTIONS } from \"../detect/vram.js\";\n\ninterface OllamaTagResponse {\n models?: Array<{ name?: string; model?: string }>;\n}\n\nasync function listInstalledOllamaModels(url: string): Promise<string[] | null> {\n try {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), 3000);\n const res = await fetch(`${url}/api/tags`, { signal: ctrl.signal });\n clearTimeout(timer);\n if (!res.ok) return null;\n const data = (await res.json()) as OllamaTagResponse;\n if (!data.models) return [];\n return data.models\n .map((m) => m.name ?? m.model ?? \"\")\n .filter((n) => n.length > 0);\n } catch {\n return null;\n }\n}\n\nfunction normalizeModelName(name: string): string {\n // Ollama sometimes returns tags as \"qwen3.5:9b\" and sometimes as\n // \"qwen3.5:9b-q4_K_M\". Compare on the base \"<model>:<tag>\" prefix.\n return name.toLowerCase().split(/[-_]/)[0];\n}\n\nfunction hasModel(installed: string[], wanted: string): boolean {\n const wantedNorm = normalizeModelName(wanted);\n return installed.some((n) => {\n const base = n.toLowerCase();\n if (base === wanted.toLowerCase()) return true;\n // Match \"qwen3.5:9b\" when Ollama returns \"qwen3.5:9b\" exactly\n if (base === wanted.toLowerCase()) return true;\n // Looser match for variant tags\n return normalizeModelName(base).startsWith(wantedNorm);\n });\n}\n\nasync function pullOllamaModel(model: string): Promise<boolean> {\n note(\n `Pulling ${pc.cyan(model)}\\n${pc.dim(\"This can take a while on the first run (several GB download).\")}`,\n \"Downloading model\"\n );\n return new Promise((resolvePromise) => {\n const child = spawn(\"ollama\", [\"pull\", model], { stdio: \"inherit\" });\n child.on(\"error\", () => {\n process.stderr.write(\n pc.red(\"\\nFailed to run `ollama pull`. Make sure Ollama is installed and running.\\n\")\n );\n resolvePromise(false);\n });\n child.on(\"close\", (code) => {\n if (code === 0) {\n process.stdout.write(pc.green(`\\n\\u2713 Pulled ${model}\\n`));\n resolvePromise(true);\n } else {\n process.stderr.write(pc.red(`\\n\\u2717 ollama pull exited with code ${code}\\n`));\n resolvePromise(false);\n }\n });\n });\n}\n\n\nfunction handleCancel<T>(value: T | symbol): asserts value is T {\n if (isCancel(value)) {\n cancel(\"Exited without saving.\");\n process.exit(0);\n }\n}\n\nfunction renderCurrent(config: Config): string {\n const hooks: string[] = [];\n if (config.hooks.edit) hooks.push(\"Edit\");\n if (config.hooks.write) hooks.push(\"Write\");\n if (config.hooks.bash) hooks.push(\"Bash\");\n\n const excluded = config.exclude.length > 0 ? config.exclude.join(\", \") : \"(none)\";\n const timeoutLabel =\n config.skipIfSlowMs === 0 ? \"Never skip\" : `${Math.round(config.skipIfSlowMs / 1000)}s`;\n\n return [\n `${pc.bold(\"Engine: \")} ${config.engine === \"ollama\" ? \"Local LLM (Ollama)\" : \"Claude Code (native)\"}`,\n `${pc.bold(\"Model: \")} ${config.ollamaModel}`,\n `${pc.bold(\"Ollama URL: \")} ${config.ollamaUrl}`,\n `${pc.bold(\"Detail level: \")} ${config.detailLevel}`,\n `${pc.bold(\"Language: \")} ${LANGUAGE_NAMES[config.language]}`,\n `${pc.bold(\"Hooks: \")} ${hooks.join(\" \\u2713 \") || \"(all disabled)\"}`,\n `${pc.bold(\"Excluded: \")} ${excluded}`,\n `${pc.bold(\"Skip if slow: \")} ${timeoutLabel}`,\n ].join(\"\\n\");\n}\n\ntype MenuChoice =\n | \"engine\"\n | \"model\"\n | \"url\"\n | \"detail\"\n | \"language\"\n | \"hooks\"\n | \"exclude\"\n | \"timeout\"\n | \"back\";\n\nasync function changeEngine(config: Config): Promise<Config> {\n const value = await select<Engine>({\n message: \"Explanation engine\",\n options: [\n { label: \"Local LLM (Ollama)\", value: \"ollama\", hint: \"free, private, works offline\" },\n { label: \"Claude Code (native)\", value: \"claude\", hint: \"best quality, uses API tokens\" },\n ],\n initialValue: config.engine,\n });\n handleCancel(value);\n return { ...config, engine: value };\n}\n\nasync function changeModel(config: Config): Promise<Config> {\n const value = await select({\n message: \"Ollama model\",\n options: MODEL_OPTIONS.map((m) => ({\n label: m.label,\n value: m.model,\n hint: m.hint,\n })),\n initialValue: config.ollamaModel,\n });\n handleCancel(value);\n\n if (value === config.ollamaModel) {\n // Nothing actually changed; skip the download check.\n return config;\n }\n\n // Check whether Ollama already has the model pulled. If not, offer to pull it.\n const installed = await listInstalledOllamaModels(config.ollamaUrl);\n if (installed === null) {\n note(\n `Could not reach Ollama at ${pc.cyan(config.ollamaUrl)}. The model will be selected, but you'll need to pull it manually with ${pc.cyan(`ollama pull ${value}`)} before the first explanation.`,\n \"Ollama unreachable\"\n );\n return { ...config, ollamaModel: value };\n }\n\n if (hasModel(installed, value)) {\n note(`${pc.green(\"\\u2713\")} Model ${pc.cyan(value)} is already installed.`, \"Model ready\");\n return { ...config, ollamaModel: value };\n }\n\n const shouldPull = await confirm({\n message: `Model ${value} is not installed locally. Pull it now?`,\n initialValue: true,\n });\n handleCancel(shouldPull);\n\n if (!shouldPull) {\n note(\n `Saved the selection, but you must run ${pc.cyan(`ollama pull ${value}`)} before it works.`,\n \"Model not pulled\"\n );\n return { ...config, ollamaModel: value };\n }\n\n const pullOk = await pullOllamaModel(value);\n if (!pullOk) {\n note(\n `Pull failed. Saving the model selection anyway — run ${pc.cyan(`ollama pull ${value}`)} manually when Ollama is reachable.`,\n \"Pull failed\"\n );\n }\n return { ...config, ollamaModel: value };\n}\n\nasync function changeUrl(config: Config): Promise<Config> {\n const value = await text({\n message: \"Ollama endpoint URL\",\n initialValue: config.ollamaUrl,\n validate(v) {\n try {\n new URL(v);\n return;\n } catch {\n return \"Must be a valid URL (e.g., http://localhost:11434)\";\n }\n },\n });\n handleCancel(value);\n return { ...config, ollamaUrl: value };\n}\n\nasync function changeDetail(config: Config): Promise<Config> {\n const value = await select<DetailLevel>({\n message: \"Detail level\",\n options: [\n { label: \"Standard\", value: \"standard\", hint: \"1-2 sentence explanation per change (recommended)\" },\n { label: \"Minimal\", value: \"minimal\", hint: \"one short sentence per change\" },\n { label: \"Verbose\", value: \"verbose\", hint: \"detailed bullet-point breakdown\" },\n ],\n initialValue: config.detailLevel,\n });\n handleCancel(value);\n return { ...config, detailLevel: value };\n}\n\nasync function changeLanguage(config: Config): Promise<Config> {\n const value = await select<Language>({\n message: \"Language for explanations\",\n options: (Object.keys(LANGUAGE_NAMES) as Language[]).map((code) => ({\n label: LANGUAGE_NAMES[code],\n value: code,\n hint: code === \"en\" ? \"default\" : undefined,\n })),\n initialValue: config.language,\n });\n handleCancel(value);\n return { ...config, language: value };\n}\n\nasync function changeHooks(config: Config): Promise<Config> {\n const editOn = await confirm({ message: \"Explain file edits?\", initialValue: config.hooks.edit });\n handleCancel(editOn);\n const writeOn = await confirm({ message: \"Explain new files?\", initialValue: config.hooks.write });\n handleCancel(writeOn);\n const bashOn = await confirm({\n message: \"Explain destructive Bash commands (rm, git reset, etc.)?\",\n initialValue: config.hooks.bash,\n });\n handleCancel(bashOn);\n\n return {\n ...config,\n hooks: { edit: editOn, write: writeOn, bash: bashOn },\n };\n}\n\nasync function changeExclude(config: Config): Promise<Config> {\n const action = await select({\n message: `Current exclusions: ${config.exclude.join(\", \") || \"(none)\"}`,\n options: [\n { label: \"Add a pattern\", value: \"add\", hint: \"e.g., *.generated.*\" },\n { label: \"Remove a pattern\", value: \"remove\" },\n { label: \"Reset to defaults\", value: \"reset\", hint: DEFAULT_CONFIG.exclude.join(\", \") },\n { label: \"Back\", value: \"back\" },\n ],\n });\n handleCancel(action);\n\n if (action === \"back\") return config;\n if (action === \"reset\") return { ...config, exclude: [...DEFAULT_CONFIG.exclude] };\n\n if (action === \"add\") {\n const pattern = await text({ message: \"Glob pattern to exclude (e.g., *.generated.*)\" });\n handleCancel(pattern);\n if (!pattern.trim()) return config;\n const exclude = Array.from(new Set([...config.exclude, pattern.trim()]));\n return { ...config, exclude };\n }\n\n if (action === \"remove\") {\n if (config.exclude.length === 0) {\n note(\"No exclusions to remove.\", \"Exclusions\");\n return config;\n }\n const target = await select({\n message: \"Which pattern to remove?\",\n options: config.exclude.map((p) => ({ label: p, value: p })),\n });\n handleCancel(target);\n const exclude = config.exclude.filter((p) => p !== target);\n return { ...config, exclude };\n }\n\n return config;\n}\n\nasync function changeTimeout(config: Config): Promise<Config> {\n const value = await select<number>({\n message: \"Skip explanation if it takes longer than...\",\n options: [\n { label: \"5 seconds\", value: 5000, hint: \"fast, may skip complex changes\" },\n { label: \"8 seconds\", value: 8000, hint: \"balanced (recommended)\" },\n { label: \"15 seconds\", value: 15000, hint: \"patient, rarely skips\" },\n { label: \"Never skip\", value: 0, hint: \"always wait for the explanation\" },\n ],\n initialValue: config.skipIfSlowMs,\n });\n handleCancel(value);\n return { ...config, skipIfSlowMs: value };\n}\n\nexport async function runConfig(): Promise<void> {\n // Prefer project config if present; otherwise edit the global config.\n const projectPath = join(process.cwd(), CONFIG_FILENAME);\n const globalPath = getGlobalConfigPath();\n\n let configPath: string;\n let scope: \"project\" | \"global\";\n if (existsSync(projectPath)) {\n configPath = projectPath;\n scope = \"project\";\n } else if (existsSync(globalPath)) {\n configPath = globalPath;\n scope = \"global\";\n } else {\n intro(pc.bold(\"code-explainer config\"));\n cancel(\n `No config file found.\\nSearched: ${pc.cyan(projectPath)}\\n ${pc.cyan(globalPath)}\\nRun ${pc.cyan(\"npx vibe-code-explainer init\")} first.`\n );\n process.exit(1);\n }\n\n intro(pc.bold(`code-explainer config (${scope})`));\n\n let config = loadConfig(configPath);\n\n while (true) {\n note(renderCurrent(config), \"Current settings\");\n\n const choice = await select<MenuChoice>({\n message: \"What would you like to change?\",\n options: [\n { label: \"Engine\", value: \"engine\" },\n { label: \"Model\", value: \"model\" },\n { label: \"Ollama URL\", value: \"url\" },\n { label: \"Detail level\", value: \"detail\" },\n { label: \"Language\", value: \"language\" },\n { label: \"Enable/disable hooks\", value: \"hooks\" },\n { label: \"File exclusions\", value: \"exclude\" },\n { label: \"Latency timeout\", value: \"timeout\" },\n { label: \"Back (save and exit)\", value: \"back\" },\n ],\n });\n handleCancel(choice);\n\n if (choice === \"back\") break;\n if (choice === \"engine\") config = await changeEngine(config);\n if (choice === \"model\") config = await changeModel(config);\n if (choice === \"url\") config = await changeUrl(config);\n if (choice === \"detail\") config = await changeDetail(config);\n if (choice === \"language\") config = await changeLanguage(config);\n if (choice === \"hooks\") config = await changeHooks(config);\n if (choice === \"exclude\") config = await changeExclude(config);\n if (choice === \"timeout\") config = await changeTimeout(config);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n\n outro(pc.green(\"Settings saved.\"));\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/config.ts"],"sourcesContent":["import { intro, outro, select, confirm, text, cancel, isCancel, note } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n DEFAULT_CONFIG,\n loadConfig,\n LANGUAGE_NAMES,\n CONFIG_FILENAME,\n getGlobalConfigPath,\n type Config,\n type Engine,\n type DetailLevel,\n type Language,\n} from \"../config/schema.js\";\nimport { MODEL_OPTIONS } from \"../detect/vram.js\";\n\n\nfunction handleCancel<T>(value: T | symbol): asserts value is T {\n if (isCancel(value)) {\n cancel(\"Exited without saving.\");\n process.exit(0);\n }\n}\n\nfunction renderCurrent(config: Config): string {\n const hooks: string[] = [];\n if (config.hooks.edit) hooks.push(\"Edit\");\n if (config.hooks.write) hooks.push(\"Write\");\n if (config.hooks.bash) hooks.push(\"Bash\");\n\n const excluded = config.exclude.length > 0 ? config.exclude.join(\", \") : \"(none)\";\n const timeoutLabel =\n config.skipIfSlowMs === 0 ? \"Never skip\" : `${Math.round(config.skipIfSlowMs / 1000)}s`;\n\n return [\n `${pc.bold(\"Engine: \")} ${config.engine === \"ollama\" ? \"Local LLM (Ollama)\" : \"Claude Code (native)\"}`,\n `${pc.bold(\"Model: \")} ${config.ollamaModel}`,\n `${pc.bold(\"Ollama URL: \")} ${config.ollamaUrl}`,\n `${pc.bold(\"Detail level: \")} ${config.detailLevel}`,\n `${pc.bold(\"Language: \")} ${LANGUAGE_NAMES[config.language]}`,\n `${pc.bold(\"Hooks: \")} ${hooks.join(\" \\u2713 \") || \"(all disabled)\"}`,\n `${pc.bold(\"Excluded: \")} ${excluded}`,\n `${pc.bold(\"Skip if slow: \")} ${timeoutLabel}`,\n ].join(\"\\n\");\n}\n\ntype MenuChoice =\n | \"engine\"\n | \"model\"\n | \"url\"\n | \"detail\"\n | \"language\"\n | \"hooks\"\n | \"exclude\"\n | \"timeout\"\n | \"back\";\n\nasync function changeEngine(config: Config): Promise<Config> {\n const value = await select<Engine>({\n message: \"Explanation engine\",\n options: [\n { label: \"Local LLM (Ollama)\", value: \"ollama\", hint: \"free, private, works offline\" },\n { label: \"Claude Code (native)\", value: \"claude\", hint: \"best quality, uses API tokens\" },\n ],\n initialValue: config.engine,\n });\n handleCancel(value);\n return { ...config, engine: value };\n}\n\nasync function changeModel(config: Config): Promise<Config> {\n const value = await select({\n message: \"Ollama model\",\n options: MODEL_OPTIONS.map((m) => ({\n label: m.label,\n value: m.model,\n hint: m.hint,\n })),\n initialValue: config.ollamaModel,\n });\n handleCancel(value);\n return { ...config, ollamaModel: value };\n}\n\nasync function changeUrl(config: Config): Promise<Config> {\n const value = await text({\n message: \"Ollama endpoint URL\",\n initialValue: config.ollamaUrl,\n validate(v) {\n try {\n new URL(v);\n return;\n } catch {\n return \"Must be a valid URL (e.g., http://localhost:11434)\";\n }\n },\n });\n handleCancel(value);\n return { ...config, ollamaUrl: value };\n}\n\nasync function changeDetail(config: Config): Promise<Config> {\n const value = await select<DetailLevel>({\n message: \"Detail level\",\n options: [\n { label: \"Standard\", value: \"standard\", hint: \"1-2 sentence explanation per change (recommended)\" },\n { label: \"Minimal\", value: \"minimal\", hint: \"one short sentence per change\" },\n { label: \"Verbose\", value: \"verbose\", hint: \"detailed bullet-point breakdown\" },\n ],\n initialValue: config.detailLevel,\n });\n handleCancel(value);\n return { ...config, detailLevel: value };\n}\n\nasync function changeLanguage(config: Config): Promise<Config> {\n const value = await select<Language>({\n message: \"Language for explanations\",\n options: (Object.keys(LANGUAGE_NAMES) as Language[]).map((code) => ({\n label: LANGUAGE_NAMES[code],\n value: code,\n hint: code === \"en\" ? \"default\" : undefined,\n })),\n initialValue: config.language,\n });\n handleCancel(value);\n return { ...config, language: value };\n}\n\nasync function changeHooks(config: Config): Promise<Config> {\n const editOn = await confirm({ message: \"Explain file edits?\", initialValue: config.hooks.edit });\n handleCancel(editOn);\n const writeOn = await confirm({ message: \"Explain new files?\", initialValue: config.hooks.write });\n handleCancel(writeOn);\n const bashOn = await confirm({\n message: \"Explain destructive Bash commands (rm, git reset, etc.)?\",\n initialValue: config.hooks.bash,\n });\n handleCancel(bashOn);\n\n return {\n ...config,\n hooks: { edit: editOn, write: writeOn, bash: bashOn },\n };\n}\n\nasync function changeExclude(config: Config): Promise<Config> {\n const action = await select({\n message: `Current exclusions: ${config.exclude.join(\", \") || \"(none)\"}`,\n options: [\n { label: \"Add a pattern\", value: \"add\", hint: \"e.g., *.generated.*\" },\n { label: \"Remove a pattern\", value: \"remove\" },\n { label: \"Reset to defaults\", value: \"reset\", hint: DEFAULT_CONFIG.exclude.join(\", \") },\n { label: \"Back\", value: \"back\" },\n ],\n });\n handleCancel(action);\n\n if (action === \"back\") return config;\n if (action === \"reset\") return { ...config, exclude: [...DEFAULT_CONFIG.exclude] };\n\n if (action === \"add\") {\n const pattern = await text({ message: \"Glob pattern to exclude (e.g., *.generated.*)\" });\n handleCancel(pattern);\n if (!pattern.trim()) return config;\n const exclude = Array.from(new Set([...config.exclude, pattern.trim()]));\n return { ...config, exclude };\n }\n\n if (action === \"remove\") {\n if (config.exclude.length === 0) {\n note(\"No exclusions to remove.\", \"Exclusions\");\n return config;\n }\n const target = await select({\n message: \"Which pattern to remove?\",\n options: config.exclude.map((p) => ({ label: p, value: p })),\n });\n handleCancel(target);\n const exclude = config.exclude.filter((p) => p !== target);\n return { ...config, exclude };\n }\n\n return config;\n}\n\nasync function changeTimeout(config: Config): Promise<Config> {\n const value = await select<number>({\n message: \"Skip explanation if it takes longer than...\",\n options: [\n { label: \"5 seconds\", value: 5000, hint: \"fast, may skip complex changes\" },\n { label: \"8 seconds\", value: 8000, hint: \"balanced (recommended)\" },\n { label: \"15 seconds\", value: 15000, hint: \"patient, rarely skips\" },\n { label: \"Never skip\", value: 0, hint: \"always wait for the explanation\" },\n ],\n initialValue: config.skipIfSlowMs,\n });\n handleCancel(value);\n return { ...config, skipIfSlowMs: value };\n}\n\nexport async function runConfig(): Promise<void> {\n // Prefer project config if present; otherwise edit the global config.\n const projectPath = join(process.cwd(), CONFIG_FILENAME);\n const globalPath = getGlobalConfigPath();\n\n let configPath: string;\n let scope: \"project\" | \"global\";\n if (existsSync(projectPath)) {\n configPath = projectPath;\n scope = \"project\";\n } else if (existsSync(globalPath)) {\n configPath = globalPath;\n scope = \"global\";\n } else {\n intro(pc.bold(\"code-explainer config\"));\n cancel(\n `No config file found.\\nSearched: ${pc.cyan(projectPath)}\\n ${pc.cyan(globalPath)}\\nRun ${pc.cyan(\"npx vibe-code-explainer init\")} first.`\n );\n process.exit(1);\n }\n\n intro(pc.bold(`code-explainer config (${scope})`));\n\n let config = loadConfig(configPath);\n\n while (true) {\n note(renderCurrent(config), \"Current settings\");\n\n const choice = await select<MenuChoice>({\n message: \"What would you like to change?\",\n options: [\n { label: \"Engine\", value: \"engine\" },\n { label: \"Model\", value: \"model\" },\n { label: \"Ollama URL\", value: \"url\" },\n { label: \"Detail level\", value: \"detail\" },\n { label: \"Language\", value: \"language\" },\n { label: \"Enable/disable hooks\", value: \"hooks\" },\n { label: \"File exclusions\", value: \"exclude\" },\n { label: \"Latency timeout\", value: \"timeout\" },\n { label: \"Back (save and exit)\", value: \"back\" },\n ],\n });\n handleCancel(choice);\n\n if (choice === \"back\") break;\n if (choice === \"engine\") config = await changeEngine(config);\n if (choice === \"model\") config = await changeModel(config);\n if (choice === \"url\") config = await changeUrl(config);\n if (choice === \"detail\") config = await changeDetail(config);\n if (choice === \"language\") config = await changeLanguage(config);\n if (choice === \"hooks\") config = await changeHooks(config);\n if (choice === \"exclude\") config = await changeExclude(config);\n if (choice === \"timeout\") config = await changeTimeout(config);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n\n outro(pc.green(\"Settings saved.\"));\n}\n"],"mappings":"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","names":[]}
|