vibe-code-explainer 0.2.0 → 0.3.0

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.
Files changed (31) hide show
  1. package/README.md +152 -55
  2. package/dist/{chunk-5NCRRHU7.js → chunk-JPDU5ASR.js} +9 -1
  3. package/dist/chunk-JPDU5ASR.js.map +1 -0
  4. package/dist/chunk-KS3PATTI.js +429 -0
  5. package/dist/chunk-KS3PATTI.js.map +1 -0
  6. package/dist/chunk-Y55I7ZS5.js +604 -0
  7. package/dist/chunk-Y55I7ZS5.js.map +1 -0
  8. package/dist/cli/index.js +6 -6
  9. package/dist/{config-H57D4GXB.js → config-74UP7RRD.js} +109 -2
  10. package/dist/config-74UP7RRD.js.map +1 -0
  11. package/dist/hooks/post-tool.js +62 -26
  12. package/dist/hooks/post-tool.js.map +1 -1
  13. package/dist/{init-KUVD2YGA.js → init-OTODBBPP.js} +19 -3
  14. package/dist/init-OTODBBPP.js.map +1 -0
  15. package/dist/ollama-PGPTPYS4.js +14 -0
  16. package/dist/{schema-TBXFNCIG.js → schema-TEWSY7EF.js} +4 -2
  17. package/dist/{tracker-HCWPUZIO.js → tracker-4ORSFJQB.js} +4 -2
  18. package/dist/{uninstall-CNGJWJYQ.js → uninstall-PN7724RX.js} +2 -2
  19. package/package.json +1 -1
  20. package/dist/chunk-5NCRRHU7.js.map +0 -1
  21. package/dist/chunk-W67RX53R.js +0 -347
  22. package/dist/chunk-W67RX53R.js.map +0 -1
  23. package/dist/chunk-YS2XIZIA.js +0 -544
  24. package/dist/chunk-YS2XIZIA.js.map +0 -1
  25. package/dist/config-H57D4GXB.js.map +0 -1
  26. package/dist/init-KUVD2YGA.js.map +0 -1
  27. package/dist/ollama-34TOVCUY.js +0 -12
  28. /package/dist/{ollama-34TOVCUY.js.map → ollama-PGPTPYS4.js.map} +0 -0
  29. /package/dist/{schema-TBXFNCIG.js.map → schema-TEWSY7EF.js.map} +0 -0
  30. /package/dist/{tracker-HCWPUZIO.js.map → tracker-4ORSFJQB.js.map} +0 -0
  31. /package/dist/{uninstall-CNGJWJYQ.js.map → uninstall-PN7724RX.js.map} +0 -0
@@ -1,347 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- __require
4
- } from "./chunk-7OCVIDC7.js";
5
-
6
- // src/session/tracker.ts
7
- import { existsSync as existsSync2, readFileSync as readFileSync2, appendFileSync as appendFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
8
- import { tmpdir as tmpdir2 } from "os";
9
- import { join as join2 } from "path";
10
-
11
- // src/cache/explanation-cache.ts
12
- import { createHash } from "crypto";
13
- import { existsSync, readFileSync, appendFileSync, unlinkSync, mkdirSync } from "fs";
14
- import { tmpdir } from "os";
15
- import { join } from "path";
16
- function getUserTmpDir() {
17
- const dir = join(tmpdir(), `code-explainer-${process.getuid?.() ?? "user"}`);
18
- if (!existsSync(dir)) {
19
- mkdirSync(dir, { recursive: true, mode: 448 });
20
- }
21
- return dir;
22
- }
23
- function getCacheFilePath(sessionId) {
24
- return join(getUserTmpDir(), `cache-${sessionId}.jsonl`);
25
- }
26
- function hashDiff(diff) {
27
- return createHash("sha256").update(diff, "utf-8").digest("hex");
28
- }
29
- function getCached(sessionId, diff) {
30
- const path = getCacheFilePath(sessionId);
31
- if (!existsSync(path)) return void 0;
32
- const hash = hashDiff(diff);
33
- try {
34
- const content = readFileSync(path, "utf-8");
35
- const lines = content.split("\n").filter((l) => l.trim());
36
- for (let i = lines.length - 1; i >= 0; i--) {
37
- try {
38
- const entry = JSON.parse(lines[i]);
39
- if (entry.hash === hash) {
40
- return entry.result;
41
- }
42
- } catch {
43
- }
44
- }
45
- } catch {
46
- return void 0;
47
- }
48
- return void 0;
49
- }
50
- function setCached(sessionId, diff, result) {
51
- const path = getCacheFilePath(sessionId);
52
- const entry = { hash: hashDiff(diff), result };
53
- try {
54
- appendFileSync(path, JSON.stringify(entry) + "\n", { mode: 384 });
55
- } catch {
56
- }
57
- }
58
- function clearCache(sessionId) {
59
- const path = getCacheFilePath(sessionId);
60
- if (existsSync(path)) {
61
- try {
62
- unlinkSync(path);
63
- } catch {
64
- }
65
- }
66
- }
67
-
68
- // src/format/box.ts
69
- import pc from "picocolors";
70
- function isNoColor() {
71
- return "NO_COLOR" in process.env || process.env.TERM === "dumb";
72
- }
73
- function color(fn, text) {
74
- return isNoColor() ? text : fn(text);
75
- }
76
- function getTerminalWidth() {
77
- return process.stderr.columns || 80;
78
- }
79
- function wrapText(text, maxWidth) {
80
- const lines = [];
81
- for (const raw of text.split("\n")) {
82
- if (raw.length <= maxWidth) {
83
- lines.push(raw);
84
- } else {
85
- let remaining = raw;
86
- while (remaining.length > maxWidth) {
87
- let breakAt = remaining.lastIndexOf(" ", maxWidth);
88
- if (breakAt <= 0) breakAt = maxWidth;
89
- lines.push(remaining.slice(0, breakAt));
90
- remaining = remaining.slice(breakAt).trimStart();
91
- }
92
- if (remaining) lines.push(remaining);
93
- }
94
- }
95
- return lines;
96
- }
97
- function riskIcon(risk) {
98
- if (isNoColor()) {
99
- switch (risk) {
100
- case "none":
101
- return "[OK]";
102
- case "low":
103
- return "[LOW]";
104
- case "medium":
105
- return "[WARN]";
106
- case "high":
107
- return "[DANGER]";
108
- }
109
- }
110
- switch (risk) {
111
- case "none":
112
- return color(pc.green, "\u2705");
113
- case "low":
114
- return color(pc.yellow, "\u26A0\uFE0F");
115
- case "medium":
116
- return color(pc.yellow, "\u26A0\uFE0F");
117
- case "high":
118
- return color(pc.red, "\u{1F6A8}");
119
- }
120
- }
121
- function riskLabel(risk) {
122
- switch (risk) {
123
- case "none":
124
- return "None";
125
- case "low":
126
- return "Low";
127
- case "medium":
128
- return "Medium";
129
- case "high":
130
- return "High";
131
- }
132
- }
133
- function formatExplanationBox(filePath, summary, risk, riskReason) {
134
- const width = Math.min(getTerminalWidth() - 4, 60);
135
- const contentWidth = width - 4;
136
- const isRisky = risk === "medium" || risk === "high";
137
- const headerLabel = isRisky ? "code-explainer \u26A0\uFE0F" : "code-explainer";
138
- const topBorder = `\u250C\u2500 ${headerLabel} ${"\u2500".repeat(Math.max(0, width - headerLabel.length - 4))}\u2500`;
139
- const bottomBorder = `\u2514${"\u2500".repeat(width)}\u2500`;
140
- const lines = [];
141
- lines.push(isRisky ? color(pc.yellow, topBorder) : color(pc.dim, topBorder));
142
- const fileDisplay = isRisky ? `${riskIcon(risk)} ${filePath}` : ` ${filePath}`;
143
- lines.push(`${color(pc.dim, "\u2502")}${fileDisplay}`);
144
- lines.push(`${color(pc.dim, "\u2502")}`);
145
- const summaryLines = wrapText(summary, contentWidth);
146
- for (const line of summaryLines) {
147
- lines.push(`${color(pc.dim, "\u2502")} ${line}`);
148
- }
149
- lines.push(`${color(pc.dim, "\u2502")}`);
150
- const riskText = `Risk: ${riskIcon(risk)} ${riskLabel(risk)}`;
151
- lines.push(`${color(pc.dim, "\u2502")} ${riskText}`);
152
- if (riskReason) {
153
- const reasonLines = wrapText(riskReason, contentWidth);
154
- for (const line of reasonLines) {
155
- lines.push(`${color(pc.dim, "\u2502")} ${color(pc.dim, line)}`);
156
- }
157
- }
158
- lines.push(isRisky ? color(pc.yellow, bottomBorder) : color(pc.dim, bottomBorder));
159
- return lines.join("\n");
160
- }
161
- function formatDriftAlert(totalFiles, unrelatedFiles, userRequest) {
162
- const width = Math.min(getTerminalWidth() - 4, 60);
163
- const contentWidth = width - 4;
164
- const headerLabel = "code-explainer \u26A1 SESSION DRIFT";
165
- const topBorder = `\u250C\u2500 ${headerLabel} ${"\u2500".repeat(Math.max(0, width - headerLabel.length - 4))}\u2500`;
166
- const bottomBorder = `\u2514${"\u2500".repeat(width)}\u2500`;
167
- const lines = [];
168
- lines.push(color(pc.yellow, topBorder));
169
- lines.push(`${color(pc.dim, "\u2502")} Claude has modified ${totalFiles} files this session.`);
170
- lines.push(`${color(pc.dim, "\u2502")} ${unrelatedFiles.length} may be unrelated:`);
171
- for (const file of unrelatedFiles) {
172
- const truncated = file.length > contentWidth - 4 ? file.slice(0, contentWidth - 7) + "..." : file;
173
- lines.push(`${color(pc.dim, "\u2502")} ${color(pc.yellow, "\u2022")} ${truncated}`);
174
- }
175
- if (userRequest) {
176
- lines.push(`${color(pc.dim, "\u2502")}`);
177
- const requestLines = wrapText(`Your request: "${userRequest}"`, contentWidth);
178
- for (const line of requestLines) {
179
- lines.push(`${color(pc.dim, "\u2502")} ${line}`);
180
- }
181
- }
182
- lines.push(`${color(pc.dim, "\u2502")} ${color(pc.yellow, "\u26A0\uFE0F Consider reviewing these changes.")}`);
183
- lines.push(color(pc.yellow, bottomBorder));
184
- return lines.join("\n");
185
- }
186
- function formatSkipNotice(reason) {
187
- return color(pc.dim, `[code-explainer] skipped: ${reason}`);
188
- }
189
- function formatErrorNotice(problem, cause, fix) {
190
- return color(pc.yellow, `[code-explainer] ${problem}. ${cause}. Fix: ${fix}.`);
191
- }
192
- function printToStderr(text) {
193
- try {
194
- const fs = __require("fs");
195
- const ttyPath = process.platform === "win32" ? "\\\\.\\CONOUT$" : "/dev/tty";
196
- const fd = fs.openSync(ttyPath, "w");
197
- fs.writeSync(fd, text + "\n");
198
- fs.closeSync(fd);
199
- } catch {
200
- process.stderr.write(text + "\n");
201
- }
202
- }
203
-
204
- // src/session/tracker.ts
205
- var TWO_HOURS_MS = 2 * 60 * 60 * 1e3;
206
- function getUserTmpDir2() {
207
- const dir = join2(tmpdir2(), `code-explainer-${process.getuid?.() ?? "user"}`);
208
- if (!existsSync2(dir)) {
209
- mkdirSync2(dir, { recursive: true, mode: 448 });
210
- }
211
- return dir;
212
- }
213
- function getSessionFilePath(sessionId) {
214
- return join2(getUserTmpDir2(), `session-${sessionId}.jsonl`);
215
- }
216
- function recordEntry(sessionId, entry) {
217
- const path = getSessionFilePath(sessionId);
218
- try {
219
- appendFileSync2(path, JSON.stringify(entry) + "\n", { mode: 384 });
220
- } catch {
221
- }
222
- }
223
- function readSession(sessionId) {
224
- const path = getSessionFilePath(sessionId);
225
- if (!existsSync2(path)) return [];
226
- try {
227
- const content = readFileSync2(path, "utf-8");
228
- return content.split("\n").filter((l) => l.trim()).map((line) => {
229
- try {
230
- return JSON.parse(line);
231
- } catch {
232
- return null;
233
- }
234
- }).filter((e) => e !== null);
235
- } catch {
236
- return [];
237
- }
238
- }
239
- function cleanStaleSessionFiles() {
240
- try {
241
- const dir = getUserTmpDir2();
242
- const now = Date.now();
243
- const entries = readdirSync(dir);
244
- for (const name of entries) {
245
- if (!name.startsWith("session-") && !name.startsWith("cache-")) continue;
246
- const filePath = join2(dir, name);
247
- try {
248
- const stat = statSync(filePath);
249
- if (now - stat.mtimeMs > TWO_HOURS_MS) {
250
- unlinkSync2(filePath);
251
- }
252
- } catch {
253
- }
254
- }
255
- } catch {
256
- }
257
- }
258
- function getSessionIdFromEnv() {
259
- return process.env.CODE_EXPLAINER_SESSION_ID;
260
- }
261
- function findLatestSession() {
262
- try {
263
- const dir = getUserTmpDir2();
264
- const entries = readdirSync(dir).filter((n) => n.startsWith("session-") && n.endsWith(".jsonl")).map((n) => ({
265
- name: n,
266
- id: n.slice("session-".length, -".jsonl".length),
267
- mtime: statSync(join2(dir, n)).mtimeMs
268
- })).sort((a, b) => b.mtime - a.mtime);
269
- return entries[0]?.id;
270
- } catch {
271
- return void 0;
272
- }
273
- }
274
- async function printSummary() {
275
- const sessionId = getSessionIdFromEnv() ?? findLatestSession();
276
- if (!sessionId) {
277
- process.stderr.write("[code-explainer] No active session found. Session data is created when Claude Code makes changes.\n");
278
- return;
279
- }
280
- const entries = readSession(sessionId);
281
- if (entries.length === 0) {
282
- process.stderr.write(`[code-explainer] Session '${sessionId}' has no recorded changes yet.
283
- `);
284
- return;
285
- }
286
- const related = entries.filter((e) => !e.unrelated);
287
- const unrelated = entries.filter((e) => e.unrelated);
288
- const uniqueFiles = Array.from(new Set(entries.map((e) => e.file)));
289
- const unrelatedFiles = Array.from(new Set(unrelated.map((e) => e.file)));
290
- const alert = formatDriftAlert(uniqueFiles.length, unrelatedFiles);
291
- printToStderr(alert);
292
- process.stderr.write(`
293
- Total changes: ${entries.length}
294
- `);
295
- process.stderr.write(`Files touched: ${uniqueFiles.length}
296
- `);
297
- process.stderr.write(`Related changes: ${related.length}
298
- `);
299
- process.stderr.write(`Unrelated/risky: ${unrelated.length}
300
- `);
301
- const risks = { none: 0, low: 0, medium: 0, high: 0 };
302
- for (const e of entries) risks[e.risk]++;
303
- process.stderr.write(`
304
- Risk breakdown:
305
- `);
306
- process.stderr.write(` None: ${risks.none}
307
- `);
308
- process.stderr.write(` Low: ${risks.low}
309
- `);
310
- process.stderr.write(` Medium: ${risks.medium}
311
- `);
312
- process.stderr.write(` High: ${risks.high}
313
- `);
314
- }
315
- async function endSession() {
316
- const sessionId = getSessionIdFromEnv() ?? findLatestSession();
317
- if (!sessionId) {
318
- process.stderr.write("[code-explainer] No active session to end.\n");
319
- return;
320
- }
321
- const sessionPath = getSessionFilePath(sessionId);
322
- if (existsSync2(sessionPath)) {
323
- try {
324
- unlinkSync2(sessionPath);
325
- } catch {
326
- }
327
- }
328
- clearCache(sessionId);
329
- process.stderr.write(`[code-explainer] Session '${sessionId}' ended. State cleared.
330
- `);
331
- }
332
-
333
- export {
334
- getCached,
335
- setCached,
336
- formatExplanationBox,
337
- formatDriftAlert,
338
- formatSkipNotice,
339
- formatErrorNotice,
340
- getSessionFilePath,
341
- recordEntry,
342
- readSession,
343
- cleanStaleSessionFiles,
344
- printSummary,
345
- endSession
346
- };
347
- //# sourceMappingURL=chunk-W67RX53R.js.map
@@ -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\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 pc from \"picocolors\";\nimport type { RiskLevel } from \"../config/schema.js\";\n\nfunction isNoColor(): boolean {\n return \"NO_COLOR\" in process.env || process.env.TERM === \"dumb\";\n}\n\nfunction color(fn: (s: string) => string, text: string): string {\n return isNoColor() ? text : fn(text);\n}\n\nfunction getTerminalWidth(): number {\n return process.stderr.columns || 80;\n}\n\nfunction wrapText(text: string, maxWidth: number): string[] {\n const lines: string[] = [];\n for (const raw of text.split(\"\\n\")) {\n if (raw.length <= maxWidth) {\n lines.push(raw);\n } else {\n let remaining = raw;\n while (remaining.length > maxWidth) {\n let breakAt = remaining.lastIndexOf(\" \", maxWidth);\n if (breakAt <= 0) breakAt = maxWidth;\n lines.push(remaining.slice(0, breakAt));\n remaining = remaining.slice(breakAt).trimStart();\n }\n if (remaining) lines.push(remaining);\n }\n }\n return lines;\n}\n\nfunction riskIcon(risk: RiskLevel): string {\n if (isNoColor()) {\n switch (risk) {\n case \"none\": return \"[OK]\";\n case \"low\": return \"[LOW]\";\n case \"medium\": return \"[WARN]\";\n case \"high\": return \"[DANGER]\";\n }\n }\n switch (risk) {\n case \"none\": return color(pc.green, \"\\u2705\");\n case \"low\": return color(pc.yellow, \"\\u26A0\\uFE0F\");\n case \"medium\": return color(pc.yellow, \"\\u26A0\\uFE0F\");\n case \"high\": return color(pc.red, \"\\u{1F6A8}\");\n }\n}\n\nfunction riskLabel(risk: RiskLevel): string {\n switch (risk) {\n case \"none\": return \"None\";\n case \"low\": return \"Low\";\n case \"medium\": return \"Medium\";\n case \"high\": return \"High\";\n }\n}\n\nexport function formatExplanationBox(\n filePath: string,\n summary: string,\n risk: RiskLevel,\n riskReason: string\n): string {\n const width = Math.min(getTerminalWidth() - 4, 60);\n const contentWidth = width - 4; // \"│ \" prefix + \" \" suffix\n\n const isRisky = risk === \"medium\" || risk === \"high\";\n const headerLabel = isRisky ? \"code-explainer \\u26A0\\uFE0F\" : \"code-explainer\";\n const topBorder = `\\u250C\\u2500 ${headerLabel} ${ \"\\u2500\".repeat(Math.max(0, width - headerLabel.length - 4))}\\u2500`;\n const bottomBorder = `\\u2514${ \"\\u2500\".repeat(width)}\\u2500`;\n\n const lines: string[] = [];\n lines.push(isRisky ? color(pc.yellow, topBorder) : color(pc.dim, topBorder));\n\n // File path\n const fileDisplay = isRisky ? `${riskIcon(risk)} ${filePath}` : ` ${filePath}`;\n lines.push(`${color(pc.dim, \"\\u2502\")}${fileDisplay}`);\n lines.push(`${color(pc.dim, \"\\u2502\")}`);\n\n // Summary\n const summaryLines = wrapText(summary, contentWidth);\n for (const line of summaryLines) {\n lines.push(`${color(pc.dim, \"\\u2502\")} ${line}`);\n }\n\n lines.push(`${color(pc.dim, \"\\u2502\")}`);\n\n // Risk line\n const riskText = `Risk: ${riskIcon(risk)} ${riskLabel(risk)}`;\n lines.push(`${color(pc.dim, \"\\u2502\")} ${riskText}`);\n\n // Risk reason (if any)\n if (riskReason) {\n const reasonLines = wrapText(riskReason, contentWidth);\n for (const line of reasonLines) {\n lines.push(`${color(pc.dim, \"\\u2502\")} ${color(pc.dim, line)}`);\n }\n }\n\n lines.push(isRisky ? color(pc.yellow, bottomBorder) : color(pc.dim, bottomBorder));\n\n return lines.join(\"\\n\");\n}\n\nexport function formatDriftAlert(\n totalFiles: number,\n unrelatedFiles: string[],\n userRequest?: string\n): string {\n const width = Math.min(getTerminalWidth() - 4, 60);\n const contentWidth = width - 4;\n\n const headerLabel = \"code-explainer \\u26A1 SESSION DRIFT\";\n const topBorder = `\\u250C\\u2500 ${headerLabel} ${ \"\\u2500\".repeat(Math.max(0, width - headerLabel.length - 4))}\\u2500`;\n const bottomBorder = `\\u2514${ \"\\u2500\".repeat(width)}\\u2500`;\n\n const lines: string[] = [];\n lines.push(color(pc.yellow, topBorder));\n\n lines.push(`${color(pc.dim, \"\\u2502\")} Claude has modified ${totalFiles} files this session.`);\n lines.push(`${color(pc.dim, \"\\u2502\")} ${unrelatedFiles.length} may be unrelated:`);\n\n for (const file of unrelatedFiles) {\n const truncated = file.length > contentWidth - 4 ? file.slice(0, contentWidth - 7) + \"...\" : file;\n lines.push(`${color(pc.dim, \"\\u2502\")} ${color(pc.yellow, \"\\u2022\")} ${truncated}`);\n }\n\n if (userRequest) {\n lines.push(`${color(pc.dim, \"\\u2502\")}`);\n const requestLines = wrapText(`Your request: \"${userRequest}\"`, contentWidth);\n for (const line of requestLines) {\n lines.push(`${color(pc.dim, \"\\u2502\")} ${line}`);\n }\n }\n\n lines.push(`${color(pc.dim, \"\\u2502\")} ${color(pc.yellow, \"\\u26A0\\uFE0F Consider reviewing these changes.\")}`);\n lines.push(color(pc.yellow, bottomBorder));\n\n return lines.join(\"\\n\");\n}\n\nexport function formatSkipNotice(reason: string): string {\n return color(pc.dim, `[code-explainer] skipped: ${reason}`);\n}\n\nexport function formatErrorNotice(problem: string, cause: string, fix: string): string {\n return color(pc.yellow, `[code-explainer] ${problem}. ${cause}. Fix: ${fix}.`);\n}\n\n/**\n * Write directly to the controlling terminal. Claude Code captures stdout\n * and stderr from hooks, so they never appear in the user's terminal unless\n * we bypass stdio and write to the tty device directly.\n *\n * Falls back to stderr if the tty cannot be opened (non-interactive runs,\n * CI, tests) so output is not silently swallowed.\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"],"mappings":"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","names":["existsSync","readFileSync","appendFileSync","unlinkSync","mkdirSync","tmpdir","join","getUserTmpDir","join","tmpdir","existsSync","mkdirSync","appendFileSync","readFileSync","unlinkSync"]}