vibe-code-explainer 0.1.0 → 0.1.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.
@@ -22,10 +22,10 @@ function detectNvidiaVram() {
22
22
  }
23
23
  var MODEL_OPTIONS = [
24
24
  {
25
- model: "qwen3-coder:30b",
26
- label: "qwen3-coder:30b",
27
- hint: "recommended for \u22648 GB VRAM (MoE: 30B total but 3.3B active, fits on low VRAM)",
28
- minVramGb: 8
25
+ model: "qwen2.5-coder:7b",
26
+ label: "qwen2.5-coder:7b",
27
+ hint: "recommended for \u22648 GB VRAM (\u223C4.5 GB quantized, fast)",
28
+ minVramGb: 4
29
29
  },
30
30
  {
31
31
  model: "qwen2.5-coder:14b",
@@ -34,23 +34,23 @@ var MODEL_OPTIONS = [
34
34
  minVramGb: 12
35
35
  },
36
36
  {
37
- model: "qwen2.5-coder:32b",
38
- label: "qwen2.5-coder:32b",
39
- hint: "recommended for \u226520 GB VRAM (best local quality)",
37
+ model: "qwen3-coder:30b",
38
+ label: "qwen3-coder:30b",
39
+ hint: "recommended for \u226520 GB VRAM (MoE, fast inference when it fits)",
40
40
  minVramGb: 20
41
41
  },
42
42
  {
43
- model: "qwen2.5-coder:7b",
44
- label: "qwen2.5-coder:7b",
45
- hint: "fallback \u2014 works on any GPU, smallest dense model",
46
- minVramGb: 4
43
+ model: "qwen2.5-coder:32b",
44
+ label: "qwen2.5-coder:32b",
45
+ hint: "recommended for \u226524 GB VRAM (best dense-model quality)",
46
+ minVramGb: 24
47
47
  }
48
48
  ];
49
49
  function pickModelForVram(totalMb) {
50
50
  const totalGb = totalMb / 1024;
51
- if (totalGb >= 20) return "qwen2.5-coder:32b";
51
+ if (totalGb >= 24) return "qwen2.5-coder:32b";
52
+ if (totalGb >= 20) return "qwen3-coder:30b";
52
53
  if (totalGb >= 12) return "qwen2.5-coder:14b";
53
- if (totalGb >= 8) return "qwen3-coder:30b";
54
54
  return "qwen2.5-coder:7b";
55
55
  }
56
56
 
@@ -59,4 +59,4 @@ export {
59
59
  MODEL_OPTIONS,
60
60
  pickModelForVram
61
61
  };
62
- //# sourceMappingURL=chunk-DI7A5QEG.js.map
62
+ //# sourceMappingURL=chunk-OXXWT37Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/detect/vram.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nexport interface VramInfo {\n gpuName: string;\n totalMb: number;\n}\n\n/**\n * Detect NVIDIA GPU VRAM via nvidia-smi. Returns null if nvidia-smi is\n * unavailable or fails. Other vendors (Apple Silicon, AMD) are intentionally\n * not auto-detected for v1 — the user picks their model via the chooser.\n */\nexport function detectNvidiaVram(): VramInfo | null {\n try {\n const output = execFileSync(\n \"nvidia-smi\",\n [\"--query-gpu=name,memory.total\", \"--format=csv,noheader,nounits\"],\n { encoding: \"utf-8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] }\n ).trim();\n\n if (!output) return null;\n const firstLine = output.split(\"\\n\")[0];\n const parts = firstLine.split(\",\").map((s) => s.trim());\n if (parts.length < 2) return null;\n\n const totalMb = parseInt(parts[1], 10);\n if (isNaN(totalMb) || totalMb <= 0) return null;\n\n return { gpuName: parts[0], totalMb };\n } catch {\n return null;\n }\n}\n\nexport interface ModelOption {\n model: string;\n label: string;\n hint: string;\n minVramGb: number;\n}\n\n// Ollama loads the entire model into VRAM when possible (including inactive\n// experts for MoE models). The qwen3-coder:30b is ~18 GB quantized and\n// requires 20+ GB VRAM to stay on GPU. On 8 GB cards it falls back to\n// CPU offload which is too slow for real-time hooks.\nexport const MODEL_OPTIONS: ModelOption[] = [\n {\n model: \"qwen2.5-coder:7b\",\n label: \"qwen2.5-coder:7b\",\n hint: \"recommended for \\u22648 GB VRAM (\\u223c4.5 GB quantized, fast)\",\n minVramGb: 4,\n },\n {\n model: \"qwen2.5-coder:14b\",\n label: \"qwen2.5-coder:14b\",\n hint: \"recommended for 12-16 GB VRAM (strong code understanding)\",\n minVramGb: 12,\n },\n {\n model: \"qwen3-coder:30b\",\n label: \"qwen3-coder:30b\",\n hint: \"recommended for \\u226520 GB VRAM (MoE, fast inference when it fits)\",\n minVramGb: 20,\n },\n {\n model: \"qwen2.5-coder:32b\",\n label: \"qwen2.5-coder:32b\",\n hint: \"recommended for \\u226524 GB VRAM (best dense-model quality)\",\n minVramGb: 24,\n },\n];\n\nexport function pickModelForVram(totalMb: number): string {\n const totalGb = totalMb / 1024;\n if (totalGb >= 24) return \"qwen2.5-coder:32b\";\n if (totalGb >= 20) return \"qwen3-coder:30b\";\n if (totalGb >= 12) return \"qwen2.5-coder:14b\";\n return \"qwen2.5-coder:7b\";\n}\n"],"mappings":";;;AAAA,SAAS,oBAAoB;AAYtB,SAAS,mBAAoC;AAClD,MAAI;AACF,UAAM,SAAS;AAAA,MACb;AAAA,MACA,CAAC,iCAAiC,+BAA+B;AAAA,MACjE,EAAE,UAAU,SAAS,OAAO,CAAC,UAAU,QAAQ,QAAQ,EAAE;AAAA,IAC3D,EAAE,KAAK;AAEP,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,YAAY,OAAO,MAAM,IAAI,EAAE,CAAC;AACtC,UAAM,QAAQ,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACtD,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,UAAU,SAAS,MAAM,CAAC,GAAG,EAAE;AACrC,QAAI,MAAM,OAAO,KAAK,WAAW,EAAG,QAAO;AAE3C,WAAO,EAAE,SAAS,MAAM,CAAC,GAAG,QAAQ;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,IAAM,gBAA+B;AAAA,EAC1C;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AACF;AAEO,SAAS,iBAAiB,SAAyB;AACxD,QAAM,UAAU,UAAU;AAC1B,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI,WAAW,GAAI,QAAO;AAC1B,SAAO;AACT;","names":[]}
@@ -4,7 +4,7 @@
4
4
  import { readFileSync } from "fs";
5
5
  var DEFAULT_CONFIG = {
6
6
  engine: "ollama",
7
- ollamaModel: "qwen3-coder:30b",
7
+ ollamaModel: "qwen2.5-coder:7b",
8
8
  ollamaUrl: "http://localhost:11434",
9
9
  detailLevel: "standard",
10
10
  hooks: {
@@ -47,4 +47,4 @@ export {
47
47
  DEFAULT_CONFIG,
48
48
  loadConfig
49
49
  };
50
- //# sourceMappingURL=chunk-ZXH4TBKR.js.map
50
+ //# sourceMappingURL=chunk-PGDNR7HQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/schema.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\n\nexport type Engine = \"ollama\" | \"claude\";\nexport type DetailLevel = \"minimal\" | \"standard\" | \"verbose\";\nexport type RiskLevel = \"none\" | \"low\" | \"medium\" | \"high\";\n\nexport interface HooksConfig {\n edit: boolean;\n write: boolean;\n bash: boolean;\n}\n\nexport interface BashFilterConfig {\n capturePatterns: string[];\n}\n\nexport interface Config {\n engine: Engine;\n ollamaModel: string;\n ollamaUrl: string;\n detailLevel: DetailLevel;\n hooks: HooksConfig;\n exclude: string[];\n skipIfSlowMs: number;\n bashFilter: BashFilterConfig;\n}\n\nexport interface ExplanationResult {\n summary: string;\n risk: RiskLevel;\n riskReason: string;\n}\n\nexport interface HookPayload {\n session_id: string;\n transcript_path: string;\n cwd: string;\n permission_mode: string;\n hook_event_name: string;\n tool_name: string;\n tool_input: Record<string, unknown>;\n tool_response: string;\n}\n\nexport const DEFAULT_CONFIG: Config = {\n engine: \"ollama\",\n ollamaModel: \"qwen2.5-coder:7b\",\n ollamaUrl: \"http://localhost:11434\",\n detailLevel: \"standard\",\n hooks: {\n edit: true,\n write: true,\n bash: true,\n },\n exclude: [\"*.lock\", \"dist/**\", \"node_modules/**\"],\n skipIfSlowMs: 8000,\n bashFilter: {\n capturePatterns: [\n \"rm\",\n \"mv\",\n \"cp\",\n \"mkdir\",\n \"npm install\",\n \"pip install\",\n \"yarn add\",\n \"pnpm add\",\n \"chmod\",\n \"chown\",\n \"git checkout\",\n \"git reset\",\n \"git revert\",\n \"sed -i\",\n ],\n },\n};\n\nexport function loadConfig(configPath: string): Config {\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n return { ...DEFAULT_CONFIG, ...parsed, hooks: { ...DEFAULT_CONFIG.hooks, ...parsed.hooks } };\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n"],"mappings":";;;AAAA,SAAS,oBAAoB;AA4CtB,IAAM,iBAAyB;AAAA,EACpC,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,SAAS,CAAC,UAAU,WAAW,iBAAiB;AAAA,EAChD,cAAc;AAAA,EACd,YAAY;AAAA,IACV,iBAAiB;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,WAAW,YAA4B;AACrD,MAAI;AACF,UAAM,MAAM,aAAa,YAAY,OAAO;AAC5C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,EAAE,GAAG,gBAAgB,GAAG,QAAQ,OAAO,EAAE,GAAG,eAAe,OAAO,GAAG,OAAO,MAAM,EAAE;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -472,7 +472,7 @@ async function callOllama(inputs) {
472
472
  }
473
473
  }
474
474
  async function runWarmup() {
475
- const { loadConfig, DEFAULT_CONFIG } = await import("./schema-RJBDBMC7.js");
475
+ const { loadConfig, DEFAULT_CONFIG } = await import("./schema-SJTKT73Y.js");
476
476
  const config = (() => {
477
477
  try {
478
478
  return loadConfig("code-explainer.config.json");
@@ -504,4 +504,4 @@ export {
504
504
  callOllama,
505
505
  runWarmup
506
506
  };
507
- //# sourceMappingURL=chunk-V5INDF2Y.js.map
507
+ //# sourceMappingURL=chunk-UMBWRK7X.js.map
package/dist/cli/index.js CHANGED
@@ -6,12 +6,12 @@ var command = args[0];
6
6
  async function main() {
7
7
  switch (command) {
8
8
  case "init": {
9
- const { runInit } = await import("../init-MMC45CGN.js");
9
+ const { runInit } = await import("../init-RE4ENXBR.js");
10
10
  await runInit(args.slice(1));
11
11
  break;
12
12
  }
13
13
  case "config": {
14
- const { runConfig } = await import("../config-AMXV3VXQ.js");
14
+ const { runConfig } = await import("../config-NF5WYSJB.js");
15
15
  await runConfig();
16
16
  break;
17
17
  }
@@ -37,7 +37,7 @@ async function main() {
37
37
  break;
38
38
  }
39
39
  case "warmup": {
40
- const { runWarmup } = await import("../ollama-5BVST2OB.js");
40
+ const { runWarmup } = await import("../ollama-6VJWZ6MD.js");
41
41
  await runWarmup();
42
42
  break;
43
43
  }
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MODEL_OPTIONS
4
- } from "./chunk-DI7A5QEG.js";
4
+ } from "./chunk-OXXWT37Z.js";
5
5
  import {
6
6
  DEFAULT_CONFIG,
7
7
  loadConfig
8
- } from "./chunk-ZXH4TBKR.js";
8
+ } from "./chunk-PGDNR7HQ.js";
9
9
  import "./chunk-7OCVIDC7.js";
10
10
 
11
11
  // src/cli/config.ts
@@ -197,4 +197,4 @@ async function runConfig() {
197
197
  export {
198
198
  runConfig
199
199
  };
200
- //# sourceMappingURL=config-AMXV3VXQ.js.map
200
+ //# sourceMappingURL=config-NF5WYSJB.js.map
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  buildClaudePrompt,
4
4
  callOllama
5
- } from "../chunk-V5INDF2Y.js";
5
+ } from "../chunk-UMBWRK7X.js";
6
6
  import {
7
7
  DEFAULT_CONFIG,
8
8
  loadConfig
9
- } from "../chunk-ZXH4TBKR.js";
9
+ } from "../chunk-PGDNR7HQ.js";
10
10
  import {
11
11
  cleanStaleSessionFiles,
12
12
  formatDriftAlert,
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  callOllama
4
- } from "./chunk-V5INDF2Y.js";
4
+ } from "./chunk-UMBWRK7X.js";
5
5
  import {
6
6
  MODEL_OPTIONS,
7
7
  detectNvidiaVram,
8
8
  pickModelForVram
9
- } from "./chunk-DI7A5QEG.js";
9
+ } from "./chunk-OXXWT37Z.js";
10
10
  import {
11
11
  DEFAULT_CONFIG
12
- } from "./chunk-ZXH4TBKR.js";
12
+ } from "./chunk-PGDNR7HQ.js";
13
13
  import {
14
14
  mergeHooksIntoSettings
15
15
  } from "./chunk-IIUJ6UAO.js";
@@ -20,7 +20,7 @@ import {
20
20
  // src/cli/init.ts
21
21
  import { intro, outro, select, confirm, cancel, isCancel, spinner, note } from "@clack/prompts";
22
22
  import pc from "picocolors";
23
- import { execFile, execFileSync } from "child_process";
23
+ import { execFileSync, spawn } from "child_process";
24
24
  import { existsSync, writeFileSync } from "fs";
25
25
  import { join, resolve } from "path";
26
26
  import { fileURLToPath } from "url";
@@ -80,20 +80,27 @@ async function checkOllama() {
80
80
  }
81
81
  }
82
82
  async function pullModel(model) {
83
- const s = spinner();
84
- s.start(`Pulling ${model} (this can take a few minutes on the first run)`);
83
+ note(
84
+ `Pulling ${pc.cyan(model)}
85
+ ${pc.dim("This can take a while on the first run (several GB download).")}`,
86
+ "Downloading model"
87
+ );
85
88
  return new Promise((resolvePromise) => {
86
- const child = execFile("ollama", ["pull", model], { maxBuffer: 1024 * 1024 * 50 });
89
+ const child = spawn("ollama", ["pull", model], { stdio: "inherit" });
87
90
  child.on("error", () => {
88
- s.stop(`Failed to run ollama pull. Make sure Ollama is running.`);
91
+ process.stderr.write(pc.red("\nFailed to run `ollama pull`. Make sure Ollama is running.\n"));
89
92
  resolvePromise(false);
90
93
  });
91
94
  child.on("close", (code) => {
92
95
  if (code === 0) {
93
- s.stop(`Pulled ${model}`);
96
+ process.stdout.write(pc.green(`
97
+ \u2713 Pulled ${model}
98
+ `));
94
99
  resolvePromise(true);
95
100
  } else {
96
- s.stop(`ollama pull exited with code ${code}`);
101
+ process.stderr.write(pc.red(`
102
+ \u2717 ollama pull exited with code ${code}
103
+ `));
97
104
  resolvePromise(false);
98
105
  }
99
106
  });
@@ -247,4 +254,4 @@ ${pc.green("\u2713")} ${mergeResult.created ? "Created" : "Updated"} ${pc.cyan(m
247
254
  export {
248
255
  runInit
249
256
  };
250
- //# sourceMappingURL=init-MMC45CGN.js.map
257
+ //# sourceMappingURL=init-RE4ENXBR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/init.ts","../src/detect/platform.ts"],"sourcesContent":["import { intro, outro, select, confirm, cancel, isCancel, spinner, note } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { execFile, execFileSync, spawn } from \"node:child_process\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_CONFIG, type Config, type Engine, type DetailLevel } from \"../config/schema.js\";\nimport { detectPlatform, ollamaInstallCommand } from \"../detect/platform.js\";\nimport { detectNvidiaVram, pickModelForVram, MODEL_OPTIONS } from \"../detect/vram.js\";\nimport { mergeHooksIntoSettings } from \"../config/merge.js\";\nimport { callOllama } from \"../engines/ollama.js\";\n\nconst CONFIG_FILE = \"code-explainer.config.json\";\n\nfunction resolveHookScriptPath(): string {\n // The compiled dist/hooks/post-tool.js lives next to dist/cli/index.js.\n const thisFile = fileURLToPath(import.meta.url);\n const distDir = resolve(thisFile, \"..\", \"..\");\n return join(distDir, \"hooks\", \"post-tool.js\");\n}\n\nasync function checkOllama(): Promise<\"running\" | \"installed-not-running\" | \"missing\"> {\n // First, try the HTTP endpoint.\n try {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), 1500);\n const res = await fetch(\"http://localhost:11434/api/tags\", { signal: ctrl.signal });\n clearTimeout(timer);\n if (res.ok) return \"running\";\n } catch {\n // fall through\n }\n\n // Check if the binary exists.\n try {\n execFileSync(\"ollama\", [\"--version\"], { stdio: \"ignore\" });\n return \"installed-not-running\";\n } catch {\n return \"missing\";\n }\n}\n\nasync function pullModel(model: string): Promise<boolean> {\n // Show note first so the user knows what's happening, then stream ollama's\n // own progress bar straight to the terminal by inheriting stdio.\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\n return new Promise((resolvePromise) => {\n const child = spawn(\"ollama\", [\"pull\", model], { stdio: \"inherit\" });\n child.on(\"error\", () => {\n process.stderr.write(pc.red(\"\\nFailed to run `ollama pull`. Make sure Ollama is running.\\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\nasync function runWarmup(config: Config): Promise<void> {\n const s = spinner();\n s.start(`Warming up ${config.ollamaModel}`);\n\n const outcome = await callOllama({\n filePath: \"warmup.txt\",\n diff: \"+ hello world\",\n config: { ...config, skipIfSlowMs: 60000 },\n });\n\n if (outcome.kind === \"ok\") {\n s.stop(\"Warmup complete. First real explanation will be fast.\");\n } else if (outcome.kind === \"error\") {\n s.stop(`Warmup failed: ${outcome.problem}`);\n } else {\n s.stop(`Warmup skipped: ${outcome.reason}`);\n }\n}\n\nasync function pickModel(): Promise<string | symbol> {\n const vram = detectNvidiaVram();\n if (vram) {\n const recommended = pickModelForVram(vram.totalMb);\n note(\n `Detected ${pc.green(vram.gpuName)} with ${pc.green(`${Math.round(vram.totalMb / 1024)} GB VRAM`)}.\\nRecommended model: ${pc.cyan(recommended)}`,\n \"GPU detection\"\n );\n return recommended;\n }\n\n // No auto-detect → show chooser with VRAM hints.\n note(\"No NVIDIA GPU detected (or nvidia-smi unavailable). Pick a model that fits your machine.\", \"GPU detection\");\n const choice = await select({\n message: \"Which model should code-explainer use?\",\n options: MODEL_OPTIONS.map((m) => ({\n label: m.label,\n value: m.model,\n hint: m.hint,\n })),\n initialValue: \"qwen3-coder:30b\",\n });\n return choice;\n}\n\nfunction handleCancel<T>(value: T | symbol): asserts value is T {\n if (isCancel(value)) {\n cancel(\"Setup cancelled.\");\n process.exit(0);\n }\n}\n\nexport async function runInit(args: string[]): Promise<void> {\n const skipWarmup = args.includes(\"--skip-warmup\");\n\n intro(pc.bold(\"code-explainer setup\"));\n\n const engineChoice = await select<Engine>({\n message: \"Which explanation engine do you want to use?\",\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: \"ollama\",\n });\n handleCancel(engineChoice);\n\n const detailChoice = await select<DetailLevel>({\n message: \"How detailed should explanations be?\",\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: \"standard\",\n });\n handleCancel(detailChoice);\n\n let ollamaModel = DEFAULT_CONFIG.ollamaModel;\n\n if (engineChoice === \"ollama\") {\n const status = await checkOllama();\n\n if (status === \"missing\") {\n const platform = detectPlatform();\n const installCmd = ollamaInstallCommand(platform);\n note(\n `Ollama is not installed.\\nInstall with: ${pc.cyan(installCmd)}\\nOr visit: ${pc.cyan(\"https://ollama.com/download\")}`,\n \"Missing prerequisite\"\n );\n const proceed = await confirm({\n message: \"Install Ollama manually and continue after it's ready?\",\n initialValue: true,\n });\n handleCancel(proceed);\n if (!proceed) {\n cancel(\"Setup paused. Run 'npx vibe-code-explainer init' again after installing Ollama.\");\n process.exit(0);\n }\n } else if (status === \"installed-not-running\") {\n note(\n `Ollama is installed but the service isn't running.\\nStart it with: ${pc.cyan(\"ollama serve\")} (in a separate terminal).`,\n \"Ollama not running\"\n );\n }\n\n const modelChoice = await pickModel();\n handleCancel(modelChoice);\n ollamaModel = modelChoice;\n\n const pullOk = await pullModel(ollamaModel);\n if (!pullOk) {\n const skipPull = await confirm({\n message: \"Continue without pulling the model? (You'll need to run 'ollama pull' manually.)\",\n initialValue: false,\n });\n handleCancel(skipPull);\n if (!skipPull) {\n cancel(\"Setup aborted.\");\n process.exit(1);\n }\n }\n }\n\n // Build config.\n const config: Config = {\n ...DEFAULT_CONFIG,\n engine: engineChoice,\n detailLevel: detailChoice,\n ollamaModel,\n };\n\n const projectRoot = process.cwd();\n const configPath = join(projectRoot, CONFIG_FILE);\n\n if (existsSync(configPath)) {\n const overwrite = await confirm({\n message: `${CONFIG_FILE} already exists. Overwrite?`,\n initialValue: false,\n });\n handleCancel(overwrite);\n if (!overwrite) {\n cancel(\"Setup aborted to avoid overwriting existing config.\");\n process.exit(0);\n }\n }\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n\n // Merge hooks into settings.local.json.\n const hookScript = resolveHookScriptPath();\n const mergeResult = mergeHooksIntoSettings(projectRoot, hookScript);\n\n note(\n `${pc.green(\"\\u2713\")} Wrote ${pc.cyan(CONFIG_FILE)}\\n${pc.green(\"\\u2713\")} ${mergeResult.created ? \"Created\" : \"Updated\"} ${pc.cyan(mergeResult.path)}`,\n \"Configuration saved\"\n );\n\n // Warmup.\n if (engineChoice === \"ollama\" && !skipWarmup) {\n await runWarmup(config);\n }\n\n outro(\n pc.bold(\"code-explainer is active.\") +\n \"\\nClaude Code will now explain every Edit, Write, and destructive Bash command.\"\n );\n}\n","import { platform } from \"node:os\";\n\nexport type Platform = \"windows\" | \"macos\" | \"linux\" | \"wsl\" | \"unknown\";\n\nexport function detectPlatform(): Platform {\n const p = platform();\n if (p === \"win32\") return \"windows\";\n if (p === \"darwin\") return \"macos\";\n if (p === \"linux\") {\n // WSL detection: Microsoft string in /proc/version\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require(\"node:fs\") as typeof import(\"node:fs\");\n const release = fs.readFileSync(\"/proc/version\", \"utf-8\").toLowerCase();\n if (release.includes(\"microsoft\") || release.includes(\"wsl\")) return \"wsl\";\n } catch {\n // ignore\n }\n return \"linux\";\n }\n return \"unknown\";\n}\n\nexport function ollamaInstallCommand(p: Platform): string {\n switch (p) {\n case \"macos\":\n return \"brew install ollama\";\n case \"windows\":\n return \"winget install Ollama.Ollama\";\n case \"linux\":\n case \"wsl\":\n return \"curl -fsSL https://ollama.com/install.sh | sh\";\n default:\n return \"Visit https://ollama.com/download to install Ollama\";\n }\n}\n"],"mappings":"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","names":["platform"]}
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  callOllama,
4
4
  runWarmup
5
- } from "./chunk-V5INDF2Y.js";
5
+ } from "./chunk-UMBWRK7X.js";
6
6
  import "./chunk-7OCVIDC7.js";
7
7
  export {
8
8
  callOllama,
9
9
  runWarmup
10
10
  };
11
- //# sourceMappingURL=ollama-5BVST2OB.js.map
11
+ //# sourceMappingURL=ollama-6VJWZ6MD.js.map
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  DEFAULT_CONFIG,
4
4
  loadConfig
5
- } from "./chunk-ZXH4TBKR.js";
5
+ } from "./chunk-PGDNR7HQ.js";
6
6
  import "./chunk-7OCVIDC7.js";
7
7
  export {
8
8
  DEFAULT_CONFIG,
9
9
  loadConfig
10
10
  };
11
- //# sourceMappingURL=schema-RJBDBMC7.js.map
11
+ //# sourceMappingURL=schema-SJTKT73Y.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-code-explainer",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Real-time diff explanations for vibe coders using Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/detect/vram.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nexport interface VramInfo {\n gpuName: string;\n totalMb: number;\n}\n\n/**\n * Detect NVIDIA GPU VRAM via nvidia-smi. Returns null if nvidia-smi is\n * unavailable or fails. Other vendors (Apple Silicon, AMD) are intentionally\n * not auto-detected for v1 — the user picks their model via the chooser.\n */\nexport function detectNvidiaVram(): VramInfo | null {\n try {\n const output = execFileSync(\n \"nvidia-smi\",\n [\"--query-gpu=name,memory.total\", \"--format=csv,noheader,nounits\"],\n { encoding: \"utf-8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] }\n ).trim();\n\n if (!output) return null;\n const firstLine = output.split(\"\\n\")[0];\n const parts = firstLine.split(\",\").map((s) => s.trim());\n if (parts.length < 2) return null;\n\n const totalMb = parseInt(parts[1], 10);\n if (isNaN(totalMb) || totalMb <= 0) return null;\n\n return { gpuName: parts[0], totalMb };\n } catch {\n return null;\n }\n}\n\nexport interface ModelOption {\n model: string;\n label: string;\n hint: string;\n minVramGb: number;\n}\n\nexport const MODEL_OPTIONS: ModelOption[] = [\n {\n model: \"qwen3-coder:30b\",\n label: \"qwen3-coder:30b\",\n hint: \"recommended for \\u22648 GB VRAM (MoE: 30B total but 3.3B active, fits on low VRAM)\",\n minVramGb: 8,\n },\n {\n model: \"qwen2.5-coder:14b\",\n label: \"qwen2.5-coder:14b\",\n hint: \"recommended for 12-16 GB VRAM (strong code understanding)\",\n minVramGb: 12,\n },\n {\n model: \"qwen2.5-coder:32b\",\n label: \"qwen2.5-coder:32b\",\n hint: \"recommended for \\u226520 GB VRAM (best local quality)\",\n minVramGb: 20,\n },\n {\n model: \"qwen2.5-coder:7b\",\n label: \"qwen2.5-coder:7b\",\n hint: \"fallback \\u2014 works on any GPU, smallest dense model\",\n minVramGb: 4,\n },\n];\n\nexport function pickModelForVram(totalMb: number): string {\n const totalGb = totalMb / 1024;\n // qwen3-coder:30b uses MoE (3.3B active), fits in 8GB+ and is preferred.\n if (totalGb >= 20) return \"qwen2.5-coder:32b\";\n if (totalGb >= 12) return \"qwen2.5-coder:14b\";\n if (totalGb >= 8) return \"qwen3-coder:30b\";\n return \"qwen2.5-coder:7b\";\n}\n"],"mappings":";;;AAAA,SAAS,oBAAoB;AAYtB,SAAS,mBAAoC;AAClD,MAAI;AACF,UAAM,SAAS;AAAA,MACb;AAAA,MACA,CAAC,iCAAiC,+BAA+B;AAAA,MACjE,EAAE,UAAU,SAAS,OAAO,CAAC,UAAU,QAAQ,QAAQ,EAAE;AAAA,IAC3D,EAAE,KAAK;AAEP,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,YAAY,OAAO,MAAM,IAAI,EAAE,CAAC;AACtC,UAAM,QAAQ,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACtD,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,UAAU,SAAS,MAAM,CAAC,GAAG,EAAE;AACrC,QAAI,MAAM,OAAO,KAAK,WAAW,EAAG,QAAO;AAE3C,WAAO,EAAE,SAAS,MAAM,CAAC,GAAG,QAAQ;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASO,IAAM,gBAA+B;AAAA,EAC1C;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AACF;AAEO,SAAS,iBAAiB,SAAyB;AACxD,QAAM,UAAU,UAAU;AAE1B,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/schema.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\n\nexport type Engine = \"ollama\" | \"claude\";\nexport type DetailLevel = \"minimal\" | \"standard\" | \"verbose\";\nexport type RiskLevel = \"none\" | \"low\" | \"medium\" | \"high\";\n\nexport interface HooksConfig {\n edit: boolean;\n write: boolean;\n bash: boolean;\n}\n\nexport interface BashFilterConfig {\n capturePatterns: string[];\n}\n\nexport interface Config {\n engine: Engine;\n ollamaModel: string;\n ollamaUrl: string;\n detailLevel: DetailLevel;\n hooks: HooksConfig;\n exclude: string[];\n skipIfSlowMs: number;\n bashFilter: BashFilterConfig;\n}\n\nexport interface ExplanationResult {\n summary: string;\n risk: RiskLevel;\n riskReason: string;\n}\n\nexport interface HookPayload {\n session_id: string;\n transcript_path: string;\n cwd: string;\n permission_mode: string;\n hook_event_name: string;\n tool_name: string;\n tool_input: Record<string, unknown>;\n tool_response: string;\n}\n\nexport const DEFAULT_CONFIG: Config = {\n engine: \"ollama\",\n ollamaModel: \"qwen3-coder:30b\",\n ollamaUrl: \"http://localhost:11434\",\n detailLevel: \"standard\",\n hooks: {\n edit: true,\n write: true,\n bash: true,\n },\n exclude: [\"*.lock\", \"dist/**\", \"node_modules/**\"],\n skipIfSlowMs: 8000,\n bashFilter: {\n capturePatterns: [\n \"rm\",\n \"mv\",\n \"cp\",\n \"mkdir\",\n \"npm install\",\n \"pip install\",\n \"yarn add\",\n \"pnpm add\",\n \"chmod\",\n \"chown\",\n \"git checkout\",\n \"git reset\",\n \"git revert\",\n \"sed -i\",\n ],\n },\n};\n\nexport function loadConfig(configPath: string): Config {\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n return { ...DEFAULT_CONFIG, ...parsed, hooks: { ...DEFAULT_CONFIG.hooks, ...parsed.hooks } };\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n"],"mappings":";;;AAAA,SAAS,oBAAoB;AA4CtB,IAAM,iBAAyB;AAAA,EACpC,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,SAAS,CAAC,UAAU,WAAW,iBAAiB;AAAA,EAChD,cAAc;AAAA,EACd,YAAY;AAAA,IACV,iBAAiB;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,WAAW,YAA4B;AACrD,MAAI;AACF,UAAM,MAAM,aAAa,YAAY,OAAO;AAC5C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,EAAE,GAAG,gBAAgB,GAAG,QAAQ,OAAO,EAAE,GAAG,eAAe,OAAO,GAAG,OAAO,MAAM,EAAE;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/init.ts","../src/detect/platform.ts"],"sourcesContent":["import { intro, outro, select, confirm, cancel, isCancel, spinner, note } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { execFile, execFileSync } from \"node:child_process\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_CONFIG, type Config, type Engine, type DetailLevel } from \"../config/schema.js\";\nimport { detectPlatform, ollamaInstallCommand } from \"../detect/platform.js\";\nimport { detectNvidiaVram, pickModelForVram, MODEL_OPTIONS } from \"../detect/vram.js\";\nimport { mergeHooksIntoSettings } from \"../config/merge.js\";\nimport { callOllama } from \"../engines/ollama.js\";\n\nconst CONFIG_FILE = \"code-explainer.config.json\";\n\nfunction resolveHookScriptPath(): string {\n // The compiled dist/hooks/post-tool.js lives next to dist/cli/index.js.\n const thisFile = fileURLToPath(import.meta.url);\n const distDir = resolve(thisFile, \"..\", \"..\");\n return join(distDir, \"hooks\", \"post-tool.js\");\n}\n\nasync function checkOllama(): Promise<\"running\" | \"installed-not-running\" | \"missing\"> {\n // First, try the HTTP endpoint.\n try {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), 1500);\n const res = await fetch(\"http://localhost:11434/api/tags\", { signal: ctrl.signal });\n clearTimeout(timer);\n if (res.ok) return \"running\";\n } catch {\n // fall through\n }\n\n // Check if the binary exists.\n try {\n execFileSync(\"ollama\", [\"--version\"], { stdio: \"ignore\" });\n return \"installed-not-running\";\n } catch {\n return \"missing\";\n }\n}\n\nasync function pullModel(model: string): Promise<boolean> {\n const s = spinner();\n s.start(`Pulling ${model} (this can take a few minutes on the first run)`);\n\n return new Promise((resolvePromise) => {\n const child = execFile(\"ollama\", [\"pull\", model], { maxBuffer: 1024 * 1024 * 50 });\n child.on(\"error\", () => {\n s.stop(`Failed to run ollama pull. Make sure Ollama is running.`);\n resolvePromise(false);\n });\n child.on(\"close\", (code) => {\n if (code === 0) {\n s.stop(`Pulled ${model}`);\n resolvePromise(true);\n } else {\n s.stop(`ollama pull exited with code ${code}`);\n resolvePromise(false);\n }\n });\n });\n}\n\nasync function runWarmup(config: Config): Promise<void> {\n const s = spinner();\n s.start(`Warming up ${config.ollamaModel}`);\n\n const outcome = await callOllama({\n filePath: \"warmup.txt\",\n diff: \"+ hello world\",\n config: { ...config, skipIfSlowMs: 60000 },\n });\n\n if (outcome.kind === \"ok\") {\n s.stop(\"Warmup complete. First real explanation will be fast.\");\n } else if (outcome.kind === \"error\") {\n s.stop(`Warmup failed: ${outcome.problem}`);\n } else {\n s.stop(`Warmup skipped: ${outcome.reason}`);\n }\n}\n\nasync function pickModel(): Promise<string | symbol> {\n const vram = detectNvidiaVram();\n if (vram) {\n const recommended = pickModelForVram(vram.totalMb);\n note(\n `Detected ${pc.green(vram.gpuName)} with ${pc.green(`${Math.round(vram.totalMb / 1024)} GB VRAM`)}.\\nRecommended model: ${pc.cyan(recommended)}`,\n \"GPU detection\"\n );\n return recommended;\n }\n\n // No auto-detect → show chooser with VRAM hints.\n note(\"No NVIDIA GPU detected (or nvidia-smi unavailable). Pick a model that fits your machine.\", \"GPU detection\");\n const choice = await select({\n message: \"Which model should code-explainer use?\",\n options: MODEL_OPTIONS.map((m) => ({\n label: m.label,\n value: m.model,\n hint: m.hint,\n })),\n initialValue: \"qwen3-coder:30b\",\n });\n return choice;\n}\n\nfunction handleCancel<T>(value: T | symbol): asserts value is T {\n if (isCancel(value)) {\n cancel(\"Setup cancelled.\");\n process.exit(0);\n }\n}\n\nexport async function runInit(args: string[]): Promise<void> {\n const skipWarmup = args.includes(\"--skip-warmup\");\n\n intro(pc.bold(\"code-explainer setup\"));\n\n const engineChoice = await select<Engine>({\n message: \"Which explanation engine do you want to use?\",\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: \"ollama\",\n });\n handleCancel(engineChoice);\n\n const detailChoice = await select<DetailLevel>({\n message: \"How detailed should explanations be?\",\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: \"standard\",\n });\n handleCancel(detailChoice);\n\n let ollamaModel = DEFAULT_CONFIG.ollamaModel;\n\n if (engineChoice === \"ollama\") {\n const status = await checkOllama();\n\n if (status === \"missing\") {\n const platform = detectPlatform();\n const installCmd = ollamaInstallCommand(platform);\n note(\n `Ollama is not installed.\\nInstall with: ${pc.cyan(installCmd)}\\nOr visit: ${pc.cyan(\"https://ollama.com/download\")}`,\n \"Missing prerequisite\"\n );\n const proceed = await confirm({\n message: \"Install Ollama manually and continue after it's ready?\",\n initialValue: true,\n });\n handleCancel(proceed);\n if (!proceed) {\n cancel(\"Setup paused. Run 'npx vibe-code-explainer init' again after installing Ollama.\");\n process.exit(0);\n }\n } else if (status === \"installed-not-running\") {\n note(\n `Ollama is installed but the service isn't running.\\nStart it with: ${pc.cyan(\"ollama serve\")} (in a separate terminal).`,\n \"Ollama not running\"\n );\n }\n\n const modelChoice = await pickModel();\n handleCancel(modelChoice);\n ollamaModel = modelChoice;\n\n const pullOk = await pullModel(ollamaModel);\n if (!pullOk) {\n const skipPull = await confirm({\n message: \"Continue without pulling the model? (You'll need to run 'ollama pull' manually.)\",\n initialValue: false,\n });\n handleCancel(skipPull);\n if (!skipPull) {\n cancel(\"Setup aborted.\");\n process.exit(1);\n }\n }\n }\n\n // Build config.\n const config: Config = {\n ...DEFAULT_CONFIG,\n engine: engineChoice,\n detailLevel: detailChoice,\n ollamaModel,\n };\n\n const projectRoot = process.cwd();\n const configPath = join(projectRoot, CONFIG_FILE);\n\n if (existsSync(configPath)) {\n const overwrite = await confirm({\n message: `${CONFIG_FILE} already exists. Overwrite?`,\n initialValue: false,\n });\n handleCancel(overwrite);\n if (!overwrite) {\n cancel(\"Setup aborted to avoid overwriting existing config.\");\n process.exit(0);\n }\n }\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n\n // Merge hooks into settings.local.json.\n const hookScript = resolveHookScriptPath();\n const mergeResult = mergeHooksIntoSettings(projectRoot, hookScript);\n\n note(\n `${pc.green(\"\\u2713\")} Wrote ${pc.cyan(CONFIG_FILE)}\\n${pc.green(\"\\u2713\")} ${mergeResult.created ? \"Created\" : \"Updated\"} ${pc.cyan(mergeResult.path)}`,\n \"Configuration saved\"\n );\n\n // Warmup.\n if (engineChoice === \"ollama\" && !skipWarmup) {\n await runWarmup(config);\n }\n\n outro(\n pc.bold(\"code-explainer is active.\") +\n \"\\nClaude Code will now explain every Edit, Write, and destructive Bash command.\"\n );\n}\n","import { platform } from \"node:os\";\n\nexport type Platform = \"windows\" | \"macos\" | \"linux\" | \"wsl\" | \"unknown\";\n\nexport function detectPlatform(): Platform {\n const p = platform();\n if (p === \"win32\") return \"windows\";\n if (p === \"darwin\") return \"macos\";\n if (p === \"linux\") {\n // WSL detection: Microsoft string in /proc/version\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require(\"node:fs\") as typeof import(\"node:fs\");\n const release = fs.readFileSync(\"/proc/version\", \"utf-8\").toLowerCase();\n if (release.includes(\"microsoft\") || release.includes(\"wsl\")) return \"wsl\";\n } catch {\n // ignore\n }\n return \"linux\";\n }\n return \"unknown\";\n}\n\nexport function ollamaInstallCommand(p: Platform): string {\n switch (p) {\n case \"macos\":\n return \"brew install ollama\";\n case \"windows\":\n return \"winget install Ollama.Ollama\";\n case \"linux\":\n case \"wsl\":\n return \"curl -fsSL https://ollama.com/install.sh | sh\";\n default:\n return \"Visit https://ollama.com/download to install Ollama\";\n }\n}\n"],"mappings":"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","names":["platform"]}