vibe-code-explainer 0.3.4 → 0.3.5
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/dist/chunk-GEAH6PTG.js +37 -0
- package/dist/chunk-GEAH6PTG.js.map +1 -0
- package/dist/chunk-GU4Y5ZWY.js +140 -0
- package/dist/chunk-GU4Y5ZWY.js.map +1 -0
- package/dist/{chunk-2PUO5G3C.js → chunk-KK76JK7S.js} +32 -92
- package/dist/chunk-KK76JK7S.js.map +1 -0
- package/dist/{chunk-XW3S5GNV.js → chunk-VJN7Y4SI.js} +114 -33
- package/dist/chunk-VJN7Y4SI.js.map +1 -0
- package/dist/{chunk-ABPTVWQ3.js → chunk-ZZY3IDL2.js} +86 -85
- package/dist/chunk-ZZY3IDL2.js.map +1 -0
- package/dist/cli/index.js +37 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/{config-AHHWBME7.js → config-YLMDBCIR.js} +116 -6
- package/dist/config-YLMDBCIR.js.map +1 -0
- package/dist/hooks/post-tool.js +143 -162
- package/dist/hooks/post-tool.js.map +1 -1
- package/dist/{init-XXK6SGF2.js → init-UDODKO25.js} +12 -16
- package/dist/init-UDODKO25.js.map +1 -0
- package/dist/ollama-YSRRK7LL.js +12 -0
- package/dist/{schema-YEJIXFMK.js → schema-R3THK35H.js} +8 -4
- package/dist/{tracker-Z5EEYUUZ.js → tracker-Y2G5DW6Y.js} +2 -2
- package/dist/{uninstall-AIH4HVPZ.js → uninstall-5RVTDKTA.js} +3 -3
- package/package.json +3 -2
- package/dist/chunk-2PUO5G3C.js.map +0 -1
- package/dist/chunk-ABPTVWQ3.js.map +0 -1
- package/dist/chunk-RK7ZFN4W.js +0 -97
- package/dist/chunk-RK7ZFN4W.js.map +0 -1
- package/dist/chunk-XW3S5GNV.js.map +0 -1
- package/dist/config-AHHWBME7.js.map +0 -1
- package/dist/init-XXK6SGF2.js.map +0 -1
- package/dist/ollama-2WHLTTDD.js +0 -14
- /package/dist/{ollama-2WHLTTDD.js.map → ollama-YSRRK7LL.js.map} +0 -0
- /package/dist/{schema-YEJIXFMK.js.map → schema-R3THK35H.js.map} +0 -0
- /package/dist/{tracker-Z5EEYUUZ.js.map → tracker-Y2G5DW6Y.js.map} +0 -0
- /package/dist/{uninstall-AIH4HVPZ.js.map → uninstall-5RVTDKTA.js.map} +0 -0
package/dist/chunk-RK7ZFN4W.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/config/schema.ts
|
|
4
|
-
import { existsSync, readFileSync } from "fs";
|
|
5
|
-
import { homedir } from "os";
|
|
6
|
-
import { join } from "path";
|
|
7
|
-
var LANGUAGE_NAMES = {
|
|
8
|
-
en: "English",
|
|
9
|
-
pt: "Portuguese",
|
|
10
|
-
es: "Spanish",
|
|
11
|
-
fr: "French",
|
|
12
|
-
de: "German",
|
|
13
|
-
it: "Italian",
|
|
14
|
-
zh: "Chinese",
|
|
15
|
-
ja: "Japanese",
|
|
16
|
-
ko: "Korean"
|
|
17
|
-
};
|
|
18
|
-
var LEARNER_LEVEL_NAMES = {
|
|
19
|
-
none: "Never programmed",
|
|
20
|
-
beginner: "Just starting out",
|
|
21
|
-
intermediate: "Read code with difficulty",
|
|
22
|
-
regular: "Code regularly"
|
|
23
|
-
};
|
|
24
|
-
var CONFIG_FILENAME = "code-explainer.config.json";
|
|
25
|
-
function getGlobalConfigPath() {
|
|
26
|
-
return join(homedir(), ".code-explainer.config.json");
|
|
27
|
-
}
|
|
28
|
-
var DEFAULT_CONFIG = {
|
|
29
|
-
engine: "ollama",
|
|
30
|
-
ollamaModel: "qwen3.5:4b",
|
|
31
|
-
ollamaUrl: "http://localhost:11434",
|
|
32
|
-
detailLevel: "standard",
|
|
33
|
-
language: "en",
|
|
34
|
-
learnerLevel: "intermediate",
|
|
35
|
-
hooks: {
|
|
36
|
-
edit: true,
|
|
37
|
-
write: true,
|
|
38
|
-
bash: true
|
|
39
|
-
},
|
|
40
|
-
exclude: ["*.lock", "dist/**", "node_modules/**"],
|
|
41
|
-
skipIfSlowMs: 0,
|
|
42
|
-
bashFilter: {
|
|
43
|
-
capturePatterns: [
|
|
44
|
-
"rm",
|
|
45
|
-
"mv",
|
|
46
|
-
"cp",
|
|
47
|
-
"mkdir",
|
|
48
|
-
"npm install",
|
|
49
|
-
"pip install",
|
|
50
|
-
"yarn add",
|
|
51
|
-
"pnpm add",
|
|
52
|
-
"chmod",
|
|
53
|
-
"chown",
|
|
54
|
-
"git checkout",
|
|
55
|
-
"git reset",
|
|
56
|
-
"git revert",
|
|
57
|
-
"sed -i"
|
|
58
|
-
]
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
function mergeConfig(base, overlay) {
|
|
62
|
-
return {
|
|
63
|
-
...base,
|
|
64
|
-
...overlay,
|
|
65
|
-
hooks: { ...base.hooks, ...overlay.hooks ?? {} },
|
|
66
|
-
bashFilter: {
|
|
67
|
-
...base.bashFilter,
|
|
68
|
-
...overlay.bashFilter ?? {}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function tryReadJson(path) {
|
|
73
|
-
if (!existsSync(path)) return null;
|
|
74
|
-
try {
|
|
75
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function loadConfig(configPath) {
|
|
81
|
-
const globalConfig = tryReadJson(getGlobalConfigPath());
|
|
82
|
-
const projectConfig = tryReadJson(configPath);
|
|
83
|
-
let result = DEFAULT_CONFIG;
|
|
84
|
-
if (globalConfig) result = mergeConfig(result, globalConfig);
|
|
85
|
-
if (projectConfig) result = mergeConfig(result, projectConfig);
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export {
|
|
90
|
-
LANGUAGE_NAMES,
|
|
91
|
-
LEARNER_LEVEL_NAMES,
|
|
92
|
-
CONFIG_FILENAME,
|
|
93
|
-
getGlobalConfigPath,
|
|
94
|
-
DEFAULT_CONFIG,
|
|
95
|
-
loadConfig
|
|
96
|
-
};
|
|
97
|
-
//# sourceMappingURL=chunk-RK7ZFN4W.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/schema.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport type Engine = \"ollama\" | \"claude\";\nexport type DetailLevel = \"minimal\" | \"standard\" | \"verbose\";\nexport type RiskLevel = \"none\" | \"low\" | \"medium\" | \"high\";\n\nexport type Language =\n | \"en\"\n | \"pt\"\n | \"es\"\n | \"fr\"\n | \"de\"\n | \"it\"\n | \"zh\"\n | \"ja\"\n | \"ko\";\n\nexport const LANGUAGE_NAMES: Record<Language, string> = {\n en: \"English\",\n pt: \"Portuguese\",\n es: \"Spanish\",\n fr: \"French\",\n de: \"German\",\n it: \"Italian\",\n zh: \"Chinese\",\n ja: \"Japanese\",\n ko: \"Korean\",\n};\n\nexport type LearnerLevel = \"none\" | \"beginner\" | \"intermediate\" | \"regular\";\n\nexport const LEARNER_LEVEL_NAMES: Record<LearnerLevel, string> = {\n none: \"Never programmed\",\n beginner: \"Just starting out\",\n intermediate: \"Read code with difficulty\",\n regular: \"Code regularly\",\n};\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 language: Language;\n learnerLevel: LearnerLevel;\n hooks: HooksConfig;\n exclude: string[];\n skipIfSlowMs: number;\n bashFilter: BashFilterConfig;\n}\n\nexport interface DeepDiveItem {\n term: string;\n explanation: string;\n}\n\nexport interface ExplanationResult {\n impact: string;\n howItWorks: string;\n why: string;\n deepDive: DeepDiveItem[];\n isSamePattern: boolean;\n samePatternNote: 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 CONFIG_FILENAME = \"code-explainer.config.json\";\n\nexport function getGlobalConfigPath(): string {\n return join(homedir(), \".code-explainer.config.json\");\n}\n\nexport const DEFAULT_CONFIG: Config = {\n engine: \"ollama\",\n ollamaModel: \"qwen3.5:4b\",\n ollamaUrl: \"http://localhost:11434\",\n detailLevel: \"standard\",\n language: \"en\",\n learnerLevel: \"intermediate\",\n hooks: {\n edit: true,\n write: true,\n bash: true,\n },\n exclude: [\"*.lock\", \"dist/**\", \"node_modules/**\"],\n skipIfSlowMs: 0,\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\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\n return {\n ...base,\n ...overlay,\n hooks: { ...base.hooks, ...(overlay.hooks ?? {}) },\n bashFilter: {\n ...base.bashFilter,\n ...(overlay.bashFilter ?? {}),\n },\n };\n}\n\nfunction tryReadJson(path: string): Partial<Config> | null {\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as Partial<Config>;\n } catch {\n return null;\n }\n}\n\n/**\n * Load config with three-level resolution, most specific first:\n * 1. Project config (passed as configPath) — overrides everything\n * 2. Global user config (~/.code-explainer.config.json)\n * 3. Built-in defaults\n *\n * A project config that lacks a field falls through to the global; a global\n * that lacks a field falls through to defaults. This lets a global install\n * set everyone's defaults while still allowing per-project overrides.\n */\nexport function loadConfig(configPath: string): Config {\n const globalConfig = tryReadJson(getGlobalConfigPath());\n const projectConfig = tryReadJson(configPath);\n\n let result = DEFAULT_CONFIG;\n if (globalConfig) result = mergeConfig(result, globalConfig);\n if (projectConfig) result = mergeConfig(result, projectConfig);\n return result;\n}\n"],"mappings":";;;AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe;AACxB,SAAS,YAAY;AAiBd,IAAM,iBAA2C;AAAA,EACtD,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAIO,IAAM,sBAAoD;AAAA,EAC/D,MAAM;AAAA,EACN,UAAU;AAAA,EACV,cAAc;AAAA,EACd,SAAS;AACX;AAoDO,IAAM,kBAAkB;AAExB,SAAS,sBAA8B;AAC5C,SAAO,KAAK,QAAQ,GAAG,6BAA6B;AACtD;AAEO,IAAM,iBAAyB;AAAA,EACpC,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,UAAU;AAAA,EACV,cAAc;AAAA,EACd,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;AAEA,SAAS,YAAY,MAAc,SAAkC;AACnE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,OAAO,EAAE,GAAG,KAAK,OAAO,GAAI,QAAQ,SAAS,CAAC,EAAG;AAAA,IACjD,YAAY;AAAA,MACV,GAAG,KAAK;AAAA,MACR,GAAI,QAAQ,cAAc,CAAC;AAAA,IAC7B;AAAA,EACF;AACF;AAEA,SAAS,YAAY,MAAsC;AACzD,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,EAC/C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,WAAW,YAA4B;AACrD,QAAM,eAAe,YAAY,oBAAoB,CAAC;AACtD,QAAM,gBAAgB,YAAY,UAAU;AAE5C,MAAI,SAAS;AACb,MAAI,aAAc,UAAS,YAAY,QAAQ,YAAY;AAC3D,MAAI,cAAe,UAAS,YAAY,QAAQ,aAAa;AAC7D,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/session/tracker.ts","../src/cache/explanation-cache.ts","../src/format/box.ts"],"sourcesContent":["import { existsSync, readFileSync, appendFileSync, unlinkSync, mkdirSync, readdirSync, statSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { RiskLevel } from \"../config/schema.js\";\nimport { clearCache } from \"../cache/explanation-cache.js\";\nimport { formatDriftAlert, printToStderr } from \"../format/box.js\";\n\nconst TWO_HOURS_MS = 2 * 60 * 60 * 1000;\n\nexport interface SessionEntry {\n file: string;\n timestamp: number;\n risk: RiskLevel;\n summary: string;\n unrelated?: boolean;\n}\n\nfunction getUserTmpDir(): string {\n const dir = join(tmpdir(), `code-explainer-${process.getuid?.() ?? \"user\"}`);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n return dir;\n}\n\nexport function getSessionFilePath(sessionId: string): string {\n return join(getUserTmpDir(), `session-${sessionId}.jsonl`);\n}\n\nexport function recordEntry(sessionId: string, entry: SessionEntry): void {\n const path = getSessionFilePath(sessionId);\n try {\n appendFileSync(path, JSON.stringify(entry) + \"\\n\", { mode: 0o600 });\n } catch {\n // Non-fatal\n }\n}\n\nexport function readSession(sessionId: string): SessionEntry[] {\n const path = getSessionFilePath(sessionId);\n if (!existsSync(path)) return [];\n\n try {\n const content = readFileSync(path, \"utf-8\");\n return content\n .split(\"\\n\")\n .filter((l) => l.trim())\n .map((line) => {\n try {\n return JSON.parse(line) as SessionEntry;\n } catch {\n return null;\n }\n })\n .filter((e): e is SessionEntry => e !== null);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the last N recorded summaries for this session, oldest-first.\n * Used to feed prompt context for \"same pattern\" detection.\n */\nexport function getRecentSummaries(sessionId: string, n: number): string[] {\n const entries = readSession(sessionId);\n if (entries.length === 0) return [];\n return entries.slice(-n).map((e) => `${e.file}: ${e.summary}`);\n}\n\nexport function cleanStaleSessionFiles(): void {\n try {\n const dir = getUserTmpDir();\n const now = Date.now();\n const entries = readdirSync(dir);\n for (const name of entries) {\n if (!name.startsWith(\"session-\") && !name.startsWith(\"cache-\")) continue;\n const filePath = join(dir, name);\n try {\n const stat = statSync(filePath);\n if (now - stat.mtimeMs > TWO_HOURS_MS) {\n unlinkSync(filePath);\n }\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n}\n\nfunction getSessionIdFromEnv(): string | undefined {\n return process.env.CODE_EXPLAINER_SESSION_ID;\n}\n\nfunction findLatestSession(): string | undefined {\n try {\n const dir = getUserTmpDir();\n const entries = readdirSync(dir)\n .filter((n) => n.startsWith(\"session-\") && n.endsWith(\".jsonl\"))\n .map((n) => ({\n name: n,\n id: n.slice(\"session-\".length, -\".jsonl\".length),\n mtime: statSync(join(dir, n)).mtimeMs,\n }))\n .sort((a, b) => b.mtime - a.mtime);\n return entries[0]?.id;\n } catch {\n return undefined;\n }\n}\n\nexport async function printSummary(): Promise<void> {\n const sessionId = getSessionIdFromEnv() ?? findLatestSession();\n if (!sessionId) {\n process.stderr.write(\"[code-explainer] No active session found. Session data is created when Claude Code makes changes.\\n\");\n return;\n }\n\n const entries = readSession(sessionId);\n if (entries.length === 0) {\n process.stderr.write(`[code-explainer] Session '${sessionId}' has no recorded changes yet.\\n`);\n return;\n }\n\n const related = entries.filter((e) => !e.unrelated);\n const unrelated = entries.filter((e) => e.unrelated);\n const uniqueFiles = Array.from(new Set(entries.map((e) => e.file)));\n const unrelatedFiles = Array.from(new Set(unrelated.map((e) => e.file)));\n\n const alert = formatDriftAlert(uniqueFiles.length, unrelatedFiles);\n printToStderr(alert);\n\n process.stderr.write(`\\nTotal changes: ${entries.length}\\n`);\n process.stderr.write(`Files touched: ${uniqueFiles.length}\\n`);\n process.stderr.write(`Related changes: ${related.length}\\n`);\n process.stderr.write(`Unrelated/risky: ${unrelated.length}\\n`);\n\n const risks: Record<RiskLevel, number> = { none: 0, low: 0, medium: 0, high: 0 };\n for (const e of entries) risks[e.risk]++;\n process.stderr.write(`\\nRisk breakdown:\\n`);\n process.stderr.write(` None: ${risks.none}\\n`);\n process.stderr.write(` Low: ${risks.low}\\n`);\n process.stderr.write(` Medium: ${risks.medium}\\n`);\n process.stderr.write(` High: ${risks.high}\\n`);\n}\n\nexport async function endSession(): Promise<void> {\n const sessionId = getSessionIdFromEnv() ?? findLatestSession();\n if (!sessionId) {\n process.stderr.write(\"[code-explainer] No active session to end.\\n\");\n return;\n }\n\n const sessionPath = getSessionFilePath(sessionId);\n if (existsSync(sessionPath)) {\n try {\n unlinkSync(sessionPath);\n } catch {\n // ignore\n }\n }\n clearCache(sessionId);\n process.stderr.write(`[code-explainer] Session '${sessionId}' ended. State cleared.\\n`);\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync, appendFileSync, unlinkSync, mkdirSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ExplanationResult } from \"../config/schema.js\";\n\nfunction getUserTmpDir(): string {\n const dir = join(tmpdir(), `code-explainer-${process.getuid?.() ?? \"user\"}`);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n return dir;\n}\n\nexport function getCacheFilePath(sessionId: string): string {\n return join(getUserTmpDir(), `cache-${sessionId}.jsonl`);\n}\n\nexport function hashDiff(diff: string): string {\n return createHash(\"sha256\").update(diff, \"utf-8\").digest(\"hex\");\n}\n\ninterface CacheEntry {\n hash: string;\n result: ExplanationResult;\n}\n\nexport function getCached(sessionId: string, diff: string): ExplanationResult | undefined {\n const path = getCacheFilePath(sessionId);\n if (!existsSync(path)) return undefined;\n\n const hash = hashDiff(diff);\n try {\n const content = readFileSync(path, \"utf-8\");\n const lines = content.split(\"\\n\").filter((l) => l.trim());\n\n // Iterate in reverse so the most recent entry wins on duplicates.\n for (let i = lines.length - 1; i >= 0; i--) {\n try {\n const entry = JSON.parse(lines[i]) as CacheEntry;\n if (entry.hash === hash) {\n return entry.result;\n }\n } catch {\n // Skip malformed line\n }\n }\n } catch {\n return undefined;\n }\n return undefined;\n}\n\nexport function setCached(sessionId: string, diff: string, result: ExplanationResult): void {\n const path = getCacheFilePath(sessionId);\n const entry: CacheEntry = { hash: hashDiff(diff), result };\n try {\n appendFileSync(path, JSON.stringify(entry) + \"\\n\", { mode: 0o600 });\n } catch {\n // Cache write failures are non-fatal\n }\n}\n\nexport function clearCache(sessionId: string): void {\n const path = getCacheFilePath(sessionId);\n if (existsSync(path)) {\n try {\n unlinkSync(path);\n } catch {\n // ignore\n }\n }\n}\n","import type { ExplanationResult, Language, RiskLevel, DetailLevel } from \"../config/schema.js\";\n\n// ===========================================================================\n// Section header translations per language\n// ===========================================================================\n\ninterface SectionLabels {\n impact: string;\n howItWorks: string;\n why: string;\n deepDive: string;\n risk: string;\n riskNone: string;\n riskLow: string;\n riskMedium: string;\n riskHigh: string;\n samePatternFallback: string;\n}\n\nconst LABELS: Record<Language, SectionLabels> = {\n en: {\n impact: \"Impact\",\n howItWorks: \"How it works\",\n why: \"Why\",\n deepDive: \"Deeper dive\",\n risk: \"Risk\",\n riskNone: \"None\",\n riskLow: \"Low\",\n riskMedium: \"Medium\",\n riskHigh: \"High\",\n samePatternFallback: \"Same pattern as before applied to this file.\",\n },\n pt: {\n impact: \"Impacto\",\n howItWorks: \"Como funciona\",\n why: \"Por que\",\n deepDive: \"Pra aprofundar\",\n risk: \"Risco\",\n riskNone: \"Nenhum\",\n riskLow: \"Baixo\",\n riskMedium: \"M\\u00e9dio\",\n riskHigh: \"Alto\",\n samePatternFallback: \"Mesmo padr\\u00e3o anterior aplicado a este arquivo.\",\n },\n es: {\n impact: \"Impacto\",\n howItWorks: \"C\\u00f3mo funciona\",\n why: \"Por qu\\u00e9\",\n deepDive: \"Para profundizar\",\n risk: \"Riesgo\",\n riskNone: \"Ninguno\",\n riskLow: \"Bajo\",\n riskMedium: \"Medio\",\n riskHigh: \"Alto\",\n samePatternFallback: \"Mismo patr\\u00f3n anterior aplicado a este archivo.\",\n },\n fr: {\n impact: \"Impact\",\n howItWorks: \"Comment \\u00e7a marche\",\n why: \"Pourquoi\",\n deepDive: \"Pour approfondir\",\n risk: \"Risque\",\n riskNone: \"Aucun\",\n riskLow: \"Faible\",\n riskMedium: \"Moyen\",\n riskHigh: \"\\u00c9lev\\u00e9\",\n samePatternFallback: \"M\\u00eame motif que pr\\u00e9c\\u00e9demment, appliqu\\u00e9 \\u00e0 ce fichier.\",\n },\n de: {\n impact: \"Auswirkung\",\n howItWorks: \"Wie es funktioniert\",\n why: \"Warum\",\n deepDive: \"Mehr lernen\",\n risk: \"Risiko\",\n riskNone: \"Keines\",\n riskLow: \"Gering\",\n riskMedium: \"Mittel\",\n riskHigh: \"Hoch\",\n samePatternFallback: \"Gleiches Muster wie zuvor auf diese Datei angewendet.\",\n },\n it: {\n impact: \"Impatto\",\n howItWorks: \"Come funziona\",\n why: \"Perch\\u00e9\",\n deepDive: \"Per approfondire\",\n risk: \"Rischio\",\n riskNone: \"Nessuno\",\n riskLow: \"Basso\",\n riskMedium: \"Medio\",\n riskHigh: \"Alto\",\n samePatternFallback: \"Stesso schema applicato a questo file.\",\n },\n zh: {\n impact: \"\\u5f71\\u54cd\",\n howItWorks: \"\\u5982\\u4f55\\u5de5\\u4f5c\",\n why: \"\\u4e3a\\u4ec0\\u4e48\",\n deepDive: \"\\u6df1\\u5165\\u5b66\\u4e60\",\n risk: \"\\u98ce\\u9669\",\n riskNone: \"\\u65e0\",\n riskLow: \"\\u4f4e\",\n riskMedium: \"\\u4e2d\",\n riskHigh: \"\\u9ad8\",\n samePatternFallback: \"\\u540c\\u6837\\u7684\\u6a21\\u5f0f\\u5e94\\u7528\\u5230\\u6b64\\u6587\\u4ef6\\u3002\",\n },\n ja: {\n impact: \"\\u5f71\\u97ff\",\n howItWorks: \"\\u4ed5\\u7d44\\u307f\",\n why: \"\\u306a\\u305c\",\n deepDive: \"\\u3055\\u3089\\u306b\\u5b66\\u3076\",\n risk: \"\\u30ea\\u30b9\\u30af\",\n riskNone: \"\\u306a\\u3057\",\n riskLow: \"\\u4f4e\",\n riskMedium: \"\\u4e2d\",\n riskHigh: \"\\u9ad8\",\n samePatternFallback: \"\\u4ee5\\u524d\\u3068\\u540c\\u3058\\u30d1\\u30bf\\u30fc\\u30f3\\u3092\\u3053\\u306e\\u30d5\\u30a1\\u30a4\\u30eb\\u306b\\u9069\\u7528\\u3002\",\n },\n ko: {\n impact: \"\\uc601\\ud5a5\",\n howItWorks: \"\\uc791\\ub3d9 \\ubc29\\uc2dd\",\n why: \"\\uc774\\uc720\",\n deepDive: \"\\ub354 \\uc54c\\uc544\\ubcf4\\uae30\",\n risk: \"\\uc704\\ud5d8\",\n riskNone: \"\\uc5c6\\uc74c\",\n riskLow: \"\\ub0ae\\uc74c\",\n riskMedium: \"\\ubcf4\\ud1b5\",\n riskHigh: \"\\ub192\\uc74c\",\n samePatternFallback: \"\\uc774\\uc804\\uacfc \\ub3d9\\uc77c\\ud55c \\ud328\\ud134\\uc774 \\uc774 \\ud30c\\uc77c\\uc5d0 \\uc801\\uc6a9\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\",\n },\n};\n\nfunction getLabels(language: Language): SectionLabels {\n return LABELS[language] ?? LABELS.en;\n}\n\n// ===========================================================================\n// Color helpers — soft palette via truecolor (24-bit) escapes.\n// Most modern terminals (Windows Terminal, iTerm2, VS Code, gnome-terminal)\n// support truecolor. NO_COLOR and TERM=dumb still produce plain text.\n// ===========================================================================\n\nconst RESET = \"\\u001b[0m\";\nconst BOLD = \"\\u001b[1m\";\nconst DIM = \"\\u001b[2m\";\n\n// Project palette (softer than saturated ANSI)\nconst PALETTE = {\n blue: [91, 158, 245], // #5B9EF5\n green: [91, 245, 160], // #5BF5A0\n yellow: [245, 200, 91], // #F5C85B\n red: [245, 91, 91], // #F55B5B\n purple: [224, 91, 245], // #E05BF5\n white: [255, 255, 255], // #FFFFFF\n} as const;\n\ntype PaletteKey = keyof typeof PALETTE;\n\nfunction isNoColor(): boolean {\n return \"NO_COLOR\" in process.env || process.env.TERM === \"dumb\";\n}\n\nfunction rgb(name: PaletteKey, text: string): string {\n if (isNoColor()) return text;\n const [r, g, b] = PALETTE[name];\n return `\\u001b[38;2;${r};${g};${b}m${text}${RESET}`;\n}\n\nfunction bold(text: string): string {\n if (isNoColor()) return text;\n return `${BOLD}${text}${RESET}`;\n}\n\nfunction dim(text: string): string {\n if (isNoColor()) return text;\n return `${DIM}${text}${RESET}`;\n}\n\nfunction boldRgb(name: PaletteKey, text: string): string {\n if (isNoColor()) return text;\n const [r, g, b] = PALETTE[name];\n return `${BOLD}\\u001b[38;2;${r};${g};${b}m${text}${RESET}`;\n}\n\nfunction dimRgb(name: PaletteKey, text: string): string {\n if (isNoColor()) return text;\n const [r, g, b] = PALETTE[name];\n return `${DIM}\\u001b[38;2;${r};${g};${b}m${text}${RESET}`;\n}\n\nfunction getTerminalWidth(): number {\n return process.stderr.columns || 80;\n}\n\n// ===========================================================================\n// Risk presentation\n// ===========================================================================\n\nfunction riskBorderColor(risk: RiskLevel): PaletteKey {\n switch (risk) {\n case \"none\": return \"green\";\n case \"low\": return \"yellow\";\n case \"medium\": return \"yellow\";\n case \"high\": return \"red\";\n }\n}\n\nfunction riskIcon(risk: RiskLevel): string {\n if (isNoColor()) {\n switch (risk) {\n case \"none\": return \"[OK]\";\n case \"low\": return \"[!]\";\n case \"medium\": return \"[!!]\";\n case \"high\": return \"[!!!]\";\n }\n }\n switch (risk) {\n case \"none\": return rgb(\"green\", \"\\u2713\");\n case \"low\": return rgb(\"yellow\", \"\\u26a0\");\n case \"medium\": return rgb(\"yellow\", \"\\u26a0\");\n case \"high\": return rgb(\"red\", \"\\u{1F6A8}\");\n }\n}\n\nfunction riskLabelText(risk: RiskLevel, labels: SectionLabels): string {\n switch (risk) {\n case \"none\": return labels.riskNone;\n case \"low\": return labels.riskLow;\n case \"medium\": return labels.riskMedium;\n case \"high\": return labels.riskHigh;\n }\n}\n\nfunction riskLabelColor(risk: RiskLevel): PaletteKey {\n switch (risk) {\n case \"none\": return \"green\";\n case \"low\": return \"yellow\";\n case \"medium\": return \"yellow\";\n case \"high\": return \"red\";\n }\n}\n\n// ===========================================================================\n// Inline code highlighting (`backticks` -> soft blue)\n// ===========================================================================\n\nfunction highlightInlineCode(text: string): string {\n if (isNoColor()) return text;\n return text.replace(/`([^`]+)`/g, (_, code: string) => rgb(\"blue\", code));\n}\n\n// ===========================================================================\n// Word wrap that respects a content width (no ANSI awareness needed since\n// we wrap BEFORE adding color)\n// ===========================================================================\n\nfunction wrapText(text: string, maxWidth: number): string[] {\n const out: string[] = [];\n for (const raw of text.split(\"\\n\")) {\n if (raw.length <= maxWidth) {\n out.push(raw);\n continue;\n }\n let remaining = raw;\n while (remaining.length > maxWidth) {\n let breakAt = remaining.lastIndexOf(\" \", maxWidth);\n if (breakAt <= 0) breakAt = maxWidth;\n out.push(remaining.slice(0, breakAt));\n remaining = remaining.slice(breakAt).trimStart();\n }\n if (remaining) out.push(remaining);\n }\n return out;\n}\n\n// ===========================================================================\n// Box construction\n// ===========================================================================\n\nconst BOX_TITLE = \"vibe-code-explainer\";\nconst PAD_LEFT = 2;\nconst PAD_RIGHT = 2;\n\ninterface BoxLine {\n text: string; // Already styled\n raw: string; // Raw (uncolored) version, used for width calculation\n}\n\nfunction line(raw: string, styled?: string): BoxLine {\n return { text: styled ?? raw, raw };\n}\n\nfunction blankLine(): BoxLine {\n return line(\"\");\n}\n\nfunction buildBoxOutput(\n contentLines: BoxLine[],\n borderColor: PaletteKey\n): string {\n const width = Math.min(getTerminalWidth() - 2, 70);\n const innerWidth = width - 2; // chars between │ │\n\n const top = `\\u256d\\u2500 ${dim(BOX_TITLE)} ${\"\\u2500\".repeat(Math.max(0, innerWidth - BOX_TITLE.length - 4))}\\u2500\\u256e`;\n const bottom = `\\u2570${\"\\u2500\".repeat(innerWidth)}\\u256f`;\n\n const sideChar = rgb(borderColor, \"\\u2502\");\n\n const middle = contentLines.map((bl) => {\n const padding = \" \".repeat(Math.max(0, innerWidth - bl.raw.length - PAD_LEFT - PAD_RIGHT));\n return `${sideChar}${\" \".repeat(PAD_LEFT)}${bl.text}${padding}${\" \".repeat(PAD_RIGHT)}${sideChar}`;\n });\n\n return [rgb(borderColor, top), ...middle, rgb(borderColor, bottom)].join(\"\\n\");\n}\n\n// ===========================================================================\n// Section rendering\n// ===========================================================================\n\ninterface SectionDef {\n header: string;\n headerColor: PaletteKey;\n body: string;\n innerWidth: number;\n dimHeader?: boolean;\n}\n\nfunction renderSection(def: SectionDef): BoxLine[] {\n const out: BoxLine[] = [];\n const headerRaw = `\\u25b8 ${def.header}`;\n const headerStyled = def.dimHeader\n ? dimRgb(def.headerColor, headerRaw)\n : boldRgb(def.headerColor, headerRaw);\n out.push(line(headerRaw, headerStyled));\n\n const bodyMax = def.innerWidth - PAD_LEFT - PAD_RIGHT - 2; // 2 = body indent\n const wrapped = wrapText(def.body, bodyMax);\n for (const w of wrapped) {\n const indented = ` ${w}`;\n const styled = ` ${highlightInlineCode(w)}`;\n out.push(line(indented, styled));\n }\n\n return out;\n}\n\n// ===========================================================================\n// Public API\n// ===========================================================================\n\nexport interface BoxInputs {\n filePath: string;\n result: ExplanationResult;\n detailLevel: DetailLevel;\n language: Language;\n}\n\nexport function formatExplanationBox(inputs: BoxInputs): string {\n const labels = getLabels(inputs.language);\n const result = inputs.result;\n const borderKey = riskBorderColor(result.risk);\n const lines: BoxLine[] = [];\n const innerWidth = Math.min(getTerminalWidth() - 2, 70) - 2;\n\n lines.push(blankLine());\n\n // File path with 📄 icon, soft blue + bold\n const filePathRaw = `\\ud83d\\udcc4 ${inputs.filePath}`;\n const filePathStyled = boldRgb(\"blue\", filePathRaw);\n lines.push(line(filePathRaw, filePathStyled));\n\n // Same-pattern collapse: short note, no teaching sections\n if (result.isSamePattern) {\n lines.push(blankLine());\n const noteRaw = result.samePatternNote || labels.samePatternFallback;\n const noteWrapped = wrapText(noteRaw, innerWidth - PAD_LEFT - PAD_RIGHT);\n for (const w of noteWrapped) {\n lines.push(line(w, dim(w)));\n }\n } else {\n // Impact (always shown when not collapsed)\n if (result.impact) {\n lines.push(blankLine());\n if (inputs.detailLevel === \"minimal\") {\n // Minimal: no header, just the text\n const wrapped = wrapText(result.impact, innerWidth - PAD_LEFT - PAD_RIGHT);\n for (const w of wrapped) {\n lines.push(line(w, highlightInlineCode(w)));\n }\n } else {\n const sec = renderSection({\n header: labels.impact,\n headerColor: \"blue\",\n body: result.impact,\n innerWidth,\n });\n lines.push(...sec);\n }\n }\n\n // How it works (standard + verbose)\n if (inputs.detailLevel !== \"minimal\" && result.howItWorks) {\n lines.push(blankLine());\n const sec = renderSection({\n header: labels.howItWorks,\n headerColor: \"green\",\n body: result.howItWorks,\n innerWidth,\n });\n lines.push(...sec);\n }\n\n // Why (standard + verbose)\n if (inputs.detailLevel !== \"minimal\" && result.why) {\n lines.push(blankLine());\n const sec = renderSection({\n header: labels.why,\n headerColor: \"purple\",\n body: result.why,\n innerWidth,\n });\n lines.push(...sec);\n }\n\n // Deep dive (verbose only) — uses white-dim header to sit quieter\n if (\n inputs.detailLevel === \"verbose\" &&\n result.deepDive &&\n result.deepDive.length > 0\n ) {\n lines.push(blankLine());\n const headerRaw = `\\u25b8 ${labels.deepDive}`;\n const headerStyled = dimRgb(\"white\", headerRaw);\n lines.push(line(headerRaw, headerStyled));\n const itemMax = innerWidth - PAD_LEFT - PAD_RIGHT - 4;\n for (const item of result.deepDive) {\n const text = `${item.term}: ${item.explanation}`;\n const wrapped = wrapText(text, itemMax);\n for (let i = 0; i < wrapped.length; i++) {\n const prefix = i === 0 ? \" \\u2014 \" : \" \";\n const raw = `${prefix}${wrapped[i]}`;\n const styled = `${prefix}${highlightInlineCode(wrapped[i])}`;\n lines.push(line(raw, styled));\n }\n }\n }\n }\n\n // Divider before risk\n lines.push(blankLine());\n const dividerWidth = innerWidth - PAD_LEFT - PAD_RIGHT;\n const dividerRaw = \"\\u2504\".repeat(dividerWidth);\n lines.push(line(dividerRaw, dim(dividerRaw)));\n lines.push(blankLine());\n\n // Risk row\n const riskKey = riskLabelColor(result.risk);\n const riskHeaderRaw = `${stripAnsi(riskIcon(result.risk))} ${labels.risk}: ${riskLabelText(result.risk, labels)}`;\n const riskHeaderStyled = `${riskIcon(result.risk)} ${boldRgb(riskKey, `${labels.risk}: ${riskLabelText(result.risk, labels)}`)}`;\n lines.push(line(riskHeaderRaw, riskHeaderStyled));\n\n if (result.risk !== \"none\" && result.riskReason) {\n const reasonMax = innerWidth - PAD_LEFT - PAD_RIGHT - 3;\n const wrapped = wrapText(result.riskReason, reasonMax);\n for (const w of wrapped) {\n const raw = ` ${w}`;\n const styled = ` ${dimRgb(riskKey, w)}`;\n lines.push(line(raw, styled));\n }\n }\n\n lines.push(blankLine());\n\n return buildBoxOutput(lines, borderKey);\n}\n\n// ===========================================================================\n// Misc box variants (skip notice, error notice, drift alert)\n// ===========================================================================\n\nexport function formatSkipNotice(reason: string): string {\n return dim(`[code-explainer] skipped: ${reason}`);\n}\n\nexport function formatErrorNotice(problem: string, cause: string, fix: string): string {\n return rgb(\"yellow\", `[code-explainer] ${problem}. ${cause}. Fix: ${fix}.`);\n}\n\nexport function formatDriftAlert(\n totalFiles: number,\n unrelatedFiles: string[],\n userRequest?: string,\n language: Language = \"en\"\n): string {\n const labels = getLabels(language);\n const lines: BoxLine[] = [];\n const innerWidth = Math.min(getTerminalWidth() - 2, 70) - 2;\n\n lines.push(blankLine());\n\n const headerRaw = `\\u26a1 SESSION DRIFT`;\n const headerStyled = boldRgb(\"yellow\", headerRaw);\n lines.push(line(headerRaw, headerStyled));\n\n lines.push(blankLine());\n\n const summaryRaw = `Claude has modified ${totalFiles} files this session.`;\n lines.push(line(summaryRaw));\n\n const unrelatedRaw = `${unrelatedFiles.length} may be unrelated:`;\n lines.push(line(unrelatedRaw));\n\n for (const file of unrelatedFiles) {\n const truncated = file.length > innerWidth - 8 ? file.slice(0, innerWidth - 11) + \"...\" : file;\n const raw = ` \\u2022 ${truncated}`;\n const styled = ` ${rgb(\"yellow\", \"\\u2022\")} ${truncated}`;\n lines.push(line(raw, styled));\n }\n\n if (userRequest) {\n lines.push(blankLine());\n const requestLines = wrapText(`Your request: \"${userRequest}\"`, innerWidth - PAD_LEFT - PAD_RIGHT);\n for (const w of requestLines) {\n lines.push(line(w, dim(w)));\n }\n }\n\n lines.push(blankLine());\n const noticeRaw = `\\u26a0 Consider reviewing these changes.`;\n lines.push(line(noticeRaw, boldRgb(\"yellow\", noticeRaw)));\n lines.push(blankLine());\n\n return buildBoxOutput(lines, \"yellow\");\n}\n\n/**\n * Write directly to the controlling terminal — Claude Code captures stdio,\n * but for non-hook contexts (init, summary, warmup) we want output on the\n * actual terminal. Falls back to stderr.\n */\nexport function printToStderr(text: string): void {\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 ttyPath = process.platform === \"win32\" ? \"\\\\\\\\.\\\\CONOUT$\" : \"/dev/tty\";\n const fd = fs.openSync(ttyPath, \"w\");\n fs.writeSync(fd, text + \"\\n\");\n fs.closeSync(fd);\n } catch {\n process.stderr.write(text + \"\\n\");\n }\n}\n\nfunction stripAnsi(s: string): string {\n // eslint-disable-next-line no-control-regex\n return s.replace(/\\u001b\\[[0-9;]*m/g, \"\");\n}\n"],"mappings":"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","names":["existsSync","readFileSync","appendFileSync","unlinkSync","mkdirSync","tmpdir","join","getUserTmpDir","join","tmpdir","existsSync","mkdirSync","appendFileSync","readFileSync","line","unlinkSync"]}
|
|
@@ -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 { 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 LEARNER_LEVEL_NAMES,\n CONFIG_FILENAME,\n getGlobalConfigPath,\n type Config,\n type Engine,\n type DetailLevel,\n type Language,\n type LearnerLevel,\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(\"Learner level:\")} ${LEARNER_LEVEL_NAMES[config.learnerLevel]}`,\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 | \"level\"\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 changeLevel(config: Config): Promise<Config> {\n const value = await select<LearnerLevel>({\n message: \"Programming knowledge level\",\n options: (Object.keys(LEARNER_LEVEL_NAMES) as LearnerLevel[]).map((code) => ({\n label: LEARNER_LEVEL_NAMES[code],\n value: code,\n hint: code === \"intermediate\" ? \"default\" : undefined,\n })),\n initialValue: config.learnerLevel,\n });\n handleCancel(value);\n return { ...config, learnerLevel: 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: \"Learner level\", value: \"level\" },\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 === \"level\") config = await changeLevel(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":[]}
|
|
@@ -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 { execFileSync, spawn } from \"node:child_process\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve, basename } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n DEFAULT_CONFIG,\n CONFIG_FILENAME,\n getGlobalConfigPath,\n LANGUAGE_NAMES,\n LEARNER_LEVEL_NAMES,\n type Config,\n type Engine,\n type DetailLevel,\n type Language,\n type LearnerLevel,\n} from \"../config/schema.js\";\nimport { detectPlatform, ollamaInstallCommand } from \"../detect/platform.js\";\nimport { detectNvidiaVram, pickModelForVram, MODEL_OPTIONS } from \"../detect/vram.js\";\nimport { mergeHooksIntoSettings, mergeHooksIntoUserSettings } from \"../config/merge.js\";\nimport { callOllama } from \"../engines/ollama.js\";\n\ntype InstallScope = \"project\" | \"global\";\n\nfunction isInsideNpxCache(path: string): boolean {\n const norm = path.replace(/\\\\/g, \"/\").toLowerCase();\n return norm.includes(\"/_npx/\") || norm.includes(\"/.npm/_npx/\");\n}\n\nfunction isInsideGlobalNpmRoot(path: string): boolean {\n const norm = path.replace(/\\\\/g, \"/\").toLowerCase();\n return norm.includes(\"/node_modules/vibe-code-explainer/\") &&\n (norm.includes(\"/npm/\") || norm.includes(\"/npm-global/\") || norm.includes(\"/appdata/roaming/npm/\") || norm.includes(\"/.nvm/\"));\n}\n\nasync function runNpmInstall(args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolvePromise, rejectPromise) => {\n const child = spawn(\"npm\", args, {\n stdio: \"inherit\",\n cwd,\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", rejectPromise);\n child.on(\"close\", (code) => {\n if (code === 0) resolvePromise();\n else rejectPromise(new Error(`npm install exited with code ${code}`));\n });\n });\n}\n\nfunction resolveGlobalHookScriptPath(): string | null {\n // Find the global npm root and build the hook path.\n try {\n const root = execFileSync(\"npm\", [\"root\", \"-g\"], {\n encoding: \"utf-8\",\n shell: process.platform === \"win32\",\n }).trim();\n return join(root, \"vibe-code-explainer\", \"dist\", \"hooks\", \"post-tool.js\");\n } catch {\n return null;\n }\n}\n\nasync function ensureProjectInstall(projectRoot: string): Promise<string> {\n const thisFile = fileURLToPath(import.meta.url);\n\n if (!isInsideNpxCache(thisFile)) {\n const distDir = resolve(thisFile, \"..\");\n return join(distDir, \"hooks\", \"post-tool.js\");\n }\n\n note(\n \"Installing vibe-code-explainer as a dev dependency so the hook path is stable...\",\n \"Local install\"\n );\n\n const pkgPath = join(projectRoot, \"package.json\");\n if (!existsSync(pkgPath)) {\n writeFileSync(\n pkgPath,\n JSON.stringify({ name: basename(projectRoot), private: true, version: \"0.0.0\" }, null, 2) + \"\\n\"\n );\n }\n\n await runNpmInstall([\"install\", \"--save-dev\", \"vibe-code-explainer\"], projectRoot);\n return join(projectRoot, \"node_modules\", \"vibe-code-explainer\", \"dist\", \"hooks\", \"post-tool.js\");\n}\n\nasync function ensureGlobalInstall(): Promise<string> {\n const thisFile = fileURLToPath(import.meta.url);\n if (isInsideGlobalNpmRoot(thisFile)) {\n const distDir = resolve(thisFile, \"..\");\n return join(distDir, \"hooks\", \"post-tool.js\");\n }\n\n note(\n \"Installing vibe-code-explainer globally so every project picks it up...\",\n \"Global install\"\n );\n\n await runNpmInstall([\"install\", \"-g\", \"vibe-code-explainer\"], process.cwd());\n\n const resolved = resolveGlobalHookScriptPath();\n if (!resolved) {\n throw new Error(\n \"Global install completed but 'npm root -g' failed. Run 'npm root -g' manually to locate the install path and file an issue.\"\n );\n }\n return resolved;\n}\n\nasync function checkOllama(): Promise<\"running\" | \"installed-not-running\" | \"missing\"> {\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 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 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 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.5:4b\",\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 // Step 1: Install scope — project or global\n const scope = await select<InstallScope>({\n message: \"Where should code-explainer be installed?\",\n options: [\n {\n label: \"This project only\",\n value: \"project\",\n hint: \"Hooks in .claude/settings.local.json, config in this folder\",\n },\n {\n label: \"Globally (every project)\",\n value: \"global\",\n hint: \"Hooks in ~/.claude/settings.json, config in ~/.code-explainer.config.json\",\n },\n ],\n initialValue: \"project\",\n });\n handleCancel(scope);\n\n // Step 2: Engine\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 // Step 3: Detail level\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 // Step 4: Language\n const languageChoice = await select<Language>({\n message: \"What language should explanations be written in?\",\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: \"en\",\n });\n handleCancel(languageChoice);\n\n // Step 5: Learner level (only meaningful when teaching is on, i.e. not minimal)\n let learnerLevelChoice: LearnerLevel = DEFAULT_CONFIG.learnerLevel;\n if (detailChoice !== \"minimal\") {\n const choice = await select<LearnerLevel>({\n message: \"What's your programming knowledge level? (used to calibrate explanations)\",\n options: (Object.keys(LEARNER_LEVEL_NAMES) as LearnerLevel[]).map((code) => ({\n label: LEARNER_LEVEL_NAMES[code],\n value: code,\n hint: code === \"intermediate\" ? \"default\" : undefined,\n })),\n initialValue: \"intermediate\",\n });\n handleCancel(choice);\n learnerLevelChoice = choice;\n }\n\n // Step 6: Ollama-specific setup\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 language: languageChoice,\n learnerLevel: learnerLevelChoice,\n ollamaModel,\n };\n\n // Write config file (global or project path)\n const projectRoot = process.cwd();\n const configPath = scope === \"global\" ? getGlobalConfigPath() : join(projectRoot, CONFIG_FILENAME);\n\n if (existsSync(configPath)) {\n const overwrite = await confirm({\n message: `${configPath} 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 // Install and wire up hooks\n let hookScript: string;\n let mergeResult;\n\n if (scope === \"global\") {\n hookScript = await ensureGlobalInstall();\n mergeResult = mergeHooksIntoUserSettings(hookScript);\n } else {\n hookScript = await ensureProjectInstall(projectRoot);\n mergeResult = mergeHooksIntoSettings(projectRoot, hookScript);\n }\n\n note(\n `${pc.green(\"\\u2713\")} Wrote ${pc.cyan(configPath)}\\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 const whereMsg =\n scope === \"global\"\n ? `\\nEvery Claude Code session on ${homedir()} will now explain every Edit, Write, and destructive Bash command.`\n : \"\\nClaude Code sessions in this project will now explain every Edit, Write, and destructive Bash command.\";\n\n outro(pc.bold(\"code-explainer is active.\") + whereMsg);\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"]}
|
package/dist/ollama-2WHLTTDD.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
callOllama,
|
|
4
|
-
parseResponse,
|
|
5
|
-
runWarmup
|
|
6
|
-
} from "./chunk-ABPTVWQ3.js";
|
|
7
|
-
import "./chunk-RK7ZFN4W.js";
|
|
8
|
-
import "./chunk-7OCVIDC7.js";
|
|
9
|
-
export {
|
|
10
|
-
callOllama,
|
|
11
|
-
parseResponse,
|
|
12
|
-
runWarmup
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=ollama-2WHLTTDD.js.map
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|