vibe-code-explainer 0.3.7 → 0.3.8
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-VJN7Y4SI.js → chunk-JA5NWYY7.js} +5 -4
- package/dist/chunk-JA5NWYY7.js.map +1 -0
- package/dist/cli/index.js +2 -2
- package/dist/hooks/post-tool.js +1 -1
- package/dist/{tracker-Y2G5DW6Y.js → tracker-YZWTVUQG.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-VJN7Y4SI.js.map +0 -1
- /package/dist/{tracker-Y2G5DW6Y.js.map → tracker-YZWTVUQG.js.map} +0 -0
|
@@ -364,14 +364,15 @@ function blankLine() {
|
|
|
364
364
|
function buildBoxOutput(contentLines, borderColor) {
|
|
365
365
|
const width = Math.min(getTerminalWidth() - 2, 70);
|
|
366
366
|
const innerWidth = width - 2;
|
|
367
|
-
const
|
|
368
|
-
const
|
|
367
|
+
const dashesRight = "\u2500".repeat(Math.max(0, innerWidth - BOX_TITLE.length - 4));
|
|
368
|
+
const top = rgb(borderColor, `\u256D\u2500 `) + dim(BOX_TITLE) + rgb(borderColor, ` ${dashesRight}\u2500\u256E`);
|
|
369
|
+
const bottom = rgb(borderColor, `\u2570${"\u2500".repeat(innerWidth)}\u256F`);
|
|
369
370
|
const sideChar = rgb(borderColor, "\u2502");
|
|
370
371
|
const middle = contentLines.map((bl) => {
|
|
371
372
|
const padding = " ".repeat(Math.max(0, innerWidth - bl.raw.length - PAD_LEFT - PAD_RIGHT));
|
|
372
373
|
return `${sideChar}${" ".repeat(PAD_LEFT)}${bl.text}${padding}${" ".repeat(PAD_RIGHT)}${sideChar}`;
|
|
373
374
|
});
|
|
374
|
-
return [
|
|
375
|
+
return [top, ...middle, bottom].join("\n");
|
|
375
376
|
}
|
|
376
377
|
function renderSection(def) {
|
|
377
378
|
const out = [];
|
|
@@ -715,4 +716,4 @@ export {
|
|
|
715
716
|
printSummary,
|
|
716
717
|
endSession
|
|
717
718
|
};
|
|
718
|
-
//# sourceMappingURL=chunk-
|
|
719
|
+
//# sourceMappingURL=chunk-JA5NWYY7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session/tracker.ts","../src/cache/explanation-cache.ts","../src/session/session-id.ts","../src/session/tmpdir.ts","../src/format/box.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, readdirSync, statSync } from \"node:fs\";\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\";\nimport { assertSafeSessionId } from \"./session-id.js\";\nimport { getUserTmpDir } from \"./tmpdir.js\";\n\nconst TWO_HOURS_MS = 2 * 60 * 60 * 1000;\n// Minimum interval between stale-file cleanup runs to avoid stat-ing the\n// tmpdir on every hook invocation (which fires for every Edit/Write/Bash).\nconst CLEANUP_THROTTLE_MS = 60 * 1000;\n\nexport interface SessionEntry {\n file: string;\n timestamp: number;\n risk: RiskLevel;\n summary: string;\n unrelated?: boolean;\n}\n\nexport function getSessionFilePath(sessionId: string): string {\n assertSafeSessionId(sessionId);\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 *\n * Pass `entries` if you've already called readSession() to avoid a second\n * disk read within the same hook invocation.\n */\nexport function getRecentSummaries(sessionId: string, n: number, entries?: SessionEntry[]): string[] {\n const all = entries ?? readSession(sessionId);\n if (all.length === 0) return [];\n return all.slice(-n).map((e) => `${e.file}: ${e.summary}`);\n}\n\nfunction getCleanupTimestampPath(): string {\n return join(getUserTmpDir(), \".last-cleanup\");\n}\n\nexport function cleanStaleSessionFiles(): void {\n try {\n const tsPath = getCleanupTimestampPath();\n const now = Date.now();\n\n // Throttle: skip if we cleaned up recently.\n if (existsSync(tsPath)) {\n try {\n const ts = parseInt(readFileSync(tsPath, \"utf-8\").trim(), 10);\n if (!isNaN(ts) && now - ts < CLEANUP_THROTTLE_MS) return;\n } catch {\n // If the timestamp file is malformed, proceed with cleanup.\n }\n }\n\n // Record the timestamp before cleaning so concurrent invocations see it.\n try {\n writeFileSync(tsPath, String(now), { mode: 0o600 });\n } catch {\n // Non-fatal — proceed with cleanup even if we can't update the timestamp.\n }\n\n const dir = getUserTmpDir();\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({ json = false }: { json?: boolean } = {}): Promise<void> {\n const sessionId = getSessionIdFromEnv() ?? findLatestSession();\n if (!sessionId) {\n if (json) {\n process.stdout.write(JSON.stringify({ error: \"No active session found\" }) + \"\\n\");\n } else {\n process.stderr.write(\"[code-explainer] No active session found. Session data is created when Claude Code makes changes.\\n\");\n }\n return;\n }\n\n const entries = readSession(sessionId);\n if (entries.length === 0) {\n if (json) {\n process.stdout.write(JSON.stringify({ sessionId, totalChanges: 0, files: [], risks: { none: 0, low: 0, medium: 0, high: 0 } }) + \"\\n\");\n } else {\n process.stderr.write(`[code-explainer] Session '${sessionId}' has no recorded changes yet.\\n`);\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 risks: Record<RiskLevel, number> = { none: 0, low: 0, medium: 0, high: 0 };\n for (const e of entries) risks[e.risk]++;\n\n if (json) {\n process.stdout.write(JSON.stringify({\n sessionId,\n totalChanges: entries.length,\n filesCount: uniqueFiles.length,\n relatedChanges: related.length,\n unrelatedChanges: unrelated.length,\n unrelatedFiles,\n risks,\n entries: entries.map((e) => ({ file: e.file, risk: e.risk, summary: e.summary, unrelated: !!e.unrelated })),\n }, null, 2) + \"\\n\");\n return;\n }\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 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, writeFileSync, renameSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ExplanationResult } from \"../config/schema.js\";\nimport { assertSafeSessionId } from \"../session/session-id.js\";\nimport { getUserTmpDir } from \"../session/tmpdir.js\";\n\n// Rotate the JSONL cache file when it reaches this many lines to prevent\n// unbounded growth within a single long session.\nconst CACHE_ROTATE_THRESHOLD = 500;\n// After rotation, keep the N most recent unique entries (by hash).\nconst CACHE_COMPACT_TARGET = 250;\n\nexport function getCacheFilePath(sessionId: string): string {\n assertSafeSessionId(sessionId);\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\n/**\n * If the cache file has grown beyond CACHE_ROTATE_THRESHOLD lines, compact\n * it: deduplicate by hash (keeping the last occurrence) and write back the\n * CACHE_COMPACT_TARGET most recent unique entries atomically via a .tmp file.\n *\n * Atomic write (writeFileSync + renameSync) prevents a crash mid-write from\n * leaving a truncated cache. Non-fatal — any error is silently swallowed.\n *\n * Note: concurrent hook invocations can both pass the size check and attempt\n * to compact the same file. The last rename wins; both will produce a valid\n * compacted file, so data integrity is preserved without a lockfile.\n */\nfunction rotateCacheIfNeeded(path: string): void {\n try {\n const content = readFileSync(path, \"utf-8\");\n const lines = content.split(\"\\n\").filter((l) => l.trim());\n if (lines.length <= CACHE_ROTATE_THRESHOLD) return;\n\n // Deduplicate by hash — later lines overwrite earlier (newest wins).\n const seen = new Map<string, CacheEntry>();\n for (const line of lines) {\n try {\n const entry = JSON.parse(line) as CacheEntry;\n seen.set(entry.hash, entry);\n } catch {\n // skip malformed lines\n }\n }\n\n // Keep the CACHE_COMPACT_TARGET most recent unique entries.\n const unique = Array.from(seen.values());\n const compacted = unique.slice(-CACHE_COMPACT_TARGET);\n\n const tmp = path + \".tmp\";\n writeFileSync(tmp, compacted.map((e) => JSON.stringify(e)).join(\"\\n\") + \"\\n\", { mode: 0o600 });\n renameSync(tmp, path);\n } catch {\n // Non-fatal — rotation failure just means the file keeps growing.\n }\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 rotateCacheIfNeeded(path);\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","/**\n * Session ID validation. Claude Code generates UUID-style IDs, but the\n * value is untrusted input from the hook's stdin — an attacker-controlled\n * session_id like `../../evil` would escape the user tmpdir and drop files\n * at arbitrary paths. Reject anything outside [A-Za-z0-9_-]{1,64}.\n */\nconst SAFE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;\n\nexport function isSafeSessionId(id: unknown): id is string {\n return typeof id === \"string\" && SAFE_ID_PATTERN.test(id);\n}\n\n/**\n * Defence-in-depth: throw when an unsafe ID reaches a path builder.\n * The hook's parsePayload already rejects unsafe IDs, so this is only\n * reachable via an internal caller that forgot to validate.\n */\nexport function assertSafeSessionId(id: string): void {\n if (!isSafeSessionId(id)) {\n throw new Error(`unsafe session id: ${JSON.stringify(id)}`);\n }\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { tmpdir, userInfo } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Per-user tmp subdirectory for session and cache files.\n * Suffixes with the OS username (works cross-platform; `process.getuid`\n * is undefined on Windows) so a shared %TEMP% on a multi-user Windows\n * box does not leak one user's session state into another's.\n */\nexport function getUserTmpDir(): string {\n let suffix: string;\n try {\n const info = userInfo();\n suffix = typeof info.username === \"string\" && info.username ? info.username : \"user\";\n } catch {\n suffix = \"user\";\n }\n // Defensive: keep the suffix itself path-safe.\n suffix = suffix.replace(/[^A-Za-z0-9_-]/g, \"_\").slice(0, 64) || \"user\";\n const dir = join(tmpdir(), `code-explainer-${suffix}`);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n return dir;\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 dashesRight = \"\\u2500\".repeat(Math.max(0, innerWidth - BOX_TITLE.length - 4));\n const top =\n rgb(borderColor, `\\u256d\\u2500 `) +\n dim(BOX_TITLE) +\n rgb(borderColor, ` ${dashesRight}\\u2500\\u256e`);\n const bottom = rgb(borderColor, `\\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 [top, ...middle, 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","writeFileSync","appendFileSync","unlinkSync","join","existsSync","join","join","line","existsSync","join","appendFileSync","existsSync","readFileSync","line","writeFileSync","unlinkSync"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -28,14 +28,14 @@ async function main() {
|
|
|
28
28
|
case "summary": {
|
|
29
29
|
const { flags } = parseFlags(args.slice(1));
|
|
30
30
|
const json = flagBool(flags, "json", "j");
|
|
31
|
-
const { printSummary } = await import("../tracker-
|
|
31
|
+
const { printSummary } = await import("../tracker-YZWTVUQG.js");
|
|
32
32
|
await printSummary({ json });
|
|
33
33
|
break;
|
|
34
34
|
}
|
|
35
35
|
case "session": {
|
|
36
36
|
const subcommand = args[1];
|
|
37
37
|
if (subcommand === "end") {
|
|
38
|
-
const { endSession } = await import("../tracker-
|
|
38
|
+
const { endSession } = await import("../tracker-YZWTVUQG.js");
|
|
39
39
|
await endSession();
|
|
40
40
|
} else {
|
|
41
41
|
console.error("[code-explainer] Unknown session command. Usage: code-explainer session end");
|
package/dist/hooks/post-tool.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
printSummary,
|
|
8
8
|
readSession,
|
|
9
9
|
recordEntry
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JA5NWYY7.js";
|
|
11
11
|
import "./chunk-7OCVIDC7.js";
|
|
12
12
|
export {
|
|
13
13
|
cleanStaleSessionFiles,
|
|
@@ -18,4 +18,4 @@ export {
|
|
|
18
18
|
readSession,
|
|
19
19
|
recordEntry
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=tracker-
|
|
21
|
+
//# sourceMappingURL=tracker-YZWTVUQG.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/session/tracker.ts","../src/cache/explanation-cache.ts","../src/session/session-id.ts","../src/session/tmpdir.ts","../src/format/box.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, readdirSync, statSync } from \"node:fs\";\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\";\nimport { assertSafeSessionId } from \"./session-id.js\";\nimport { getUserTmpDir } from \"./tmpdir.js\";\n\nconst TWO_HOURS_MS = 2 * 60 * 60 * 1000;\n// Minimum interval between stale-file cleanup runs to avoid stat-ing the\n// tmpdir on every hook invocation (which fires for every Edit/Write/Bash).\nconst CLEANUP_THROTTLE_MS = 60 * 1000;\n\nexport interface SessionEntry {\n file: string;\n timestamp: number;\n risk: RiskLevel;\n summary: string;\n unrelated?: boolean;\n}\n\nexport function getSessionFilePath(sessionId: string): string {\n assertSafeSessionId(sessionId);\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 *\n * Pass `entries` if you've already called readSession() to avoid a second\n * disk read within the same hook invocation.\n */\nexport function getRecentSummaries(sessionId: string, n: number, entries?: SessionEntry[]): string[] {\n const all = entries ?? readSession(sessionId);\n if (all.length === 0) return [];\n return all.slice(-n).map((e) => `${e.file}: ${e.summary}`);\n}\n\nfunction getCleanupTimestampPath(): string {\n return join(getUserTmpDir(), \".last-cleanup\");\n}\n\nexport function cleanStaleSessionFiles(): void {\n try {\n const tsPath = getCleanupTimestampPath();\n const now = Date.now();\n\n // Throttle: skip if we cleaned up recently.\n if (existsSync(tsPath)) {\n try {\n const ts = parseInt(readFileSync(tsPath, \"utf-8\").trim(), 10);\n if (!isNaN(ts) && now - ts < CLEANUP_THROTTLE_MS) return;\n } catch {\n // If the timestamp file is malformed, proceed with cleanup.\n }\n }\n\n // Record the timestamp before cleaning so concurrent invocations see it.\n try {\n writeFileSync(tsPath, String(now), { mode: 0o600 });\n } catch {\n // Non-fatal — proceed with cleanup even if we can't update the timestamp.\n }\n\n const dir = getUserTmpDir();\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({ json = false }: { json?: boolean } = {}): Promise<void> {\n const sessionId = getSessionIdFromEnv() ?? findLatestSession();\n if (!sessionId) {\n if (json) {\n process.stdout.write(JSON.stringify({ error: \"No active session found\" }) + \"\\n\");\n } else {\n process.stderr.write(\"[code-explainer] No active session found. Session data is created when Claude Code makes changes.\\n\");\n }\n return;\n }\n\n const entries = readSession(sessionId);\n if (entries.length === 0) {\n if (json) {\n process.stdout.write(JSON.stringify({ sessionId, totalChanges: 0, files: [], risks: { none: 0, low: 0, medium: 0, high: 0 } }) + \"\\n\");\n } else {\n process.stderr.write(`[code-explainer] Session '${sessionId}' has no recorded changes yet.\\n`);\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 risks: Record<RiskLevel, number> = { none: 0, low: 0, medium: 0, high: 0 };\n for (const e of entries) risks[e.risk]++;\n\n if (json) {\n process.stdout.write(JSON.stringify({\n sessionId,\n totalChanges: entries.length,\n filesCount: uniqueFiles.length,\n relatedChanges: related.length,\n unrelatedChanges: unrelated.length,\n unrelatedFiles,\n risks,\n entries: entries.map((e) => ({ file: e.file, risk: e.risk, summary: e.summary, unrelated: !!e.unrelated })),\n }, null, 2) + \"\\n\");\n return;\n }\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 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, writeFileSync, renameSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ExplanationResult } from \"../config/schema.js\";\nimport { assertSafeSessionId } from \"../session/session-id.js\";\nimport { getUserTmpDir } from \"../session/tmpdir.js\";\n\n// Rotate the JSONL cache file when it reaches this many lines to prevent\n// unbounded growth within a single long session.\nconst CACHE_ROTATE_THRESHOLD = 500;\n// After rotation, keep the N most recent unique entries (by hash).\nconst CACHE_COMPACT_TARGET = 250;\n\nexport function getCacheFilePath(sessionId: string): string {\n assertSafeSessionId(sessionId);\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\n/**\n * If the cache file has grown beyond CACHE_ROTATE_THRESHOLD lines, compact\n * it: deduplicate by hash (keeping the last occurrence) and write back the\n * CACHE_COMPACT_TARGET most recent unique entries atomically via a .tmp file.\n *\n * Atomic write (writeFileSync + renameSync) prevents a crash mid-write from\n * leaving a truncated cache. Non-fatal — any error is silently swallowed.\n *\n * Note: concurrent hook invocations can both pass the size check and attempt\n * to compact the same file. The last rename wins; both will produce a valid\n * compacted file, so data integrity is preserved without a lockfile.\n */\nfunction rotateCacheIfNeeded(path: string): void {\n try {\n const content = readFileSync(path, \"utf-8\");\n const lines = content.split(\"\\n\").filter((l) => l.trim());\n if (lines.length <= CACHE_ROTATE_THRESHOLD) return;\n\n // Deduplicate by hash — later lines overwrite earlier (newest wins).\n const seen = new Map<string, CacheEntry>();\n for (const line of lines) {\n try {\n const entry = JSON.parse(line) as CacheEntry;\n seen.set(entry.hash, entry);\n } catch {\n // skip malformed lines\n }\n }\n\n // Keep the CACHE_COMPACT_TARGET most recent unique entries.\n const unique = Array.from(seen.values());\n const compacted = unique.slice(-CACHE_COMPACT_TARGET);\n\n const tmp = path + \".tmp\";\n writeFileSync(tmp, compacted.map((e) => JSON.stringify(e)).join(\"\\n\") + \"\\n\", { mode: 0o600 });\n renameSync(tmp, path);\n } catch {\n // Non-fatal — rotation failure just means the file keeps growing.\n }\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 rotateCacheIfNeeded(path);\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","/**\n * Session ID validation. Claude Code generates UUID-style IDs, but the\n * value is untrusted input from the hook's stdin — an attacker-controlled\n * session_id like `../../evil` would escape the user tmpdir and drop files\n * at arbitrary paths. Reject anything outside [A-Za-z0-9_-]{1,64}.\n */\nconst SAFE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;\n\nexport function isSafeSessionId(id: unknown): id is string {\n return typeof id === \"string\" && SAFE_ID_PATTERN.test(id);\n}\n\n/**\n * Defence-in-depth: throw when an unsafe ID reaches a path builder.\n * The hook's parsePayload already rejects unsafe IDs, so this is only\n * reachable via an internal caller that forgot to validate.\n */\nexport function assertSafeSessionId(id: string): void {\n if (!isSafeSessionId(id)) {\n throw new Error(`unsafe session id: ${JSON.stringify(id)}`);\n }\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { tmpdir, userInfo } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Per-user tmp subdirectory for session and cache files.\n * Suffixes with the OS username (works cross-platform; `process.getuid`\n * is undefined on Windows) so a shared %TEMP% on a multi-user Windows\n * box does not leak one user's session state into another's.\n */\nexport function getUserTmpDir(): string {\n let suffix: string;\n try {\n const info = userInfo();\n suffix = typeof info.username === \"string\" && info.username ? info.username : \"user\";\n } catch {\n suffix = \"user\";\n }\n // Defensive: keep the suffix itself path-safe.\n suffix = suffix.replace(/[^A-Za-z0-9_-]/g, \"_\").slice(0, 64) || \"user\";\n const dir = join(tmpdir(), `code-explainer-${suffix}`);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n return dir;\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","writeFileSync","appendFileSync","unlinkSync","join","existsSync","join","join","line","existsSync","join","appendFileSync","existsSync","readFileSync","line","writeFileSync","unlinkSync"]}
|
|
File without changes
|