inspecto 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -14
- package/dist/index.js +645 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/audit.ts","../src/parser/project-scanner.ts","../src/utils/paths.ts","../src/parser/jsonl-reader.ts","../src/parser/session-builder.ts","../src/parser/types.ts","../src/metrics/reads-per-edit.ts","../src/metrics/rewrite-ratio.ts","../src/metrics/cache-hit-rate.ts","../src/metrics/task-completion.ts","../src/utils/levenshtein.ts","../src/metrics/retry-density.ts","../src/metrics/tool-diversity.ts","../src/metrics/tokens-per-edit.ts","../src/metrics/subagent-overhead.ts","../src/metrics/grader.ts","../src/reporter/terminal.ts","../src/utils/format.ts","../src/reporter/tips.ts","../src/version.ts","../src/reporter/json-reporter.ts","../src/anomaly/baseline.ts","../src/anomaly/regression-detector.ts","../src/utils/duration.ts","../src/utils/concurrent.ts","../src/cache/grade-cache.ts","../src/commands/trend.ts","../src/anomaly/cache-anomaly.ts","../src/commands/cache-check.ts","../src/commands/compare.ts"],"sourcesContent":["/**\n * inspecto — Claude Code Session Quality Analyzer\n *\n * Grade sessions, detect regressions, catch cache bugs.\n * All from the JSONL logs Claude Code already writes.\n */\n\nimport { Command } from \"commander\";\nimport { unlink } from \"node:fs/promises\";\nimport { runAudit } from \"./commands/audit.js\";\nimport { runTrend } from \"./commands/trend.js\";\nimport { runCacheCheck } from \"./commands/cache-check.js\";\nimport { runCompare } from \"./commands/compare.js\";\nimport { getCacheFilePath } from \"./utils/paths.js\";\nimport { VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"inspecto\")\n .description(\"Claude Code session quality analyzer — grade sessions, detect regressions, catch cache bugs\")\n .version(VERSION);\n\nprogram\n .command(\"audit\", { isDefault: true })\n .description(\"Grade the most recent Claude Code session\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--verbose\", \"Show per-message breakdown\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--project <name>\", \"Filter to a specific project\")\n .action(async (options) => {\n try {\n await runAudit(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"trend\")\n .description(\"Analyze quality trends and detect regressions over time\")\n .option(\"--since <duration>\", \"Time range: 7d, 14d, 30d\", \"7d\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--project <name>\", \"Filter to a specific project\")\n .action(async (options) => {\n try {\n await runTrend(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"cache-check\")\n .description(\"Detect prompt cache bugs that inflate token costs\")\n .option(\"--since <duration>\", \"Time range: 7d, 14d, 30d\", \"7d\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .action(async (options) => {\n try {\n await runCacheCheck(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"compare\")\n .description(\"Compare quality metrics across projects\")\n .requiredOption(\"--projects <names>\", \"Comma-separated project names\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--since <duration>\", \"Time range: 7d, 14d, 30d\")\n .action(async (options) => {\n try {\n await runCompare(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nconst cache = program\n .command(\"cache\")\n .description(\"Manage the inspecto grade cache\");\n\ncache\n .command(\"clear\")\n .description(\"Delete the grade cache file (~/.claude/inspecto-cache.db)\")\n .action(async () => {\n try {\n const cachePath = getCacheFilePath();\n try {\n await unlink(cachePath);\n console.log(`Cache cleared: ${cachePath}`);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n console.log(\"No cache file found.\");\n } else {\n throw err;\n }\n }\n } catch (error) {\n handleError(error);\n }\n });\n\nfunction handleError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\nError: ${message}\\n`);\n process.exit(1);\n}\n\nprogram.parse();\n","/**\n * Default command — grade the most recent session.\n */\n\nimport chalk from \"chalk\";\nimport { getMostRecentSession } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { gradeSession } from \"../metrics/grader.js\";\nimport { renderAuditReport } from \"../reporter/terminal.js\";\nimport { formatAuditJson } from \"../reporter/json-reporter.js\";\n\nconst KNOWN_FORMAT_VERSION = \"2.1.167\";\n\nexport interface AuditOptions {\n json?: boolean;\n verbose?: boolean;\n dataDir?: string;\n project?: string;\n}\n\nexport async function runAudit(options: AuditOptions): Promise<void> {\n const sessionFile = await getMostRecentSession({\n dataDir: options.dataDir,\n project: options.project,\n });\n\n const records = readJsonl(sessionFile.path);\n const session = await buildSession(\n records,\n sessionFile.sessionId,\n sessionFile.projectSlug,\n sessionFile.subagentPaths,\n );\n\n const grade = gradeSession(session);\n\n if (options.json) {\n console.log(formatAuditJson(session, grade));\n return;\n }\n\n if (session.formatVersion && session.formatVersion !== KNOWN_FORMAT_VERSION) {\n console.log(\n chalk.yellow(\n `⚠ JSONL format version ${session.formatVersion} detected (expected ${KNOWN_FORMAT_VERSION}). Metrics may be inaccurate.`,\n ),\n );\n }\n\n if (session.unknownRecordTypes.size > 0) {\n const types = [...session.unknownRecordTypes].sort().join(\", \");\n process.stdout.write(chalk.dim(`Note: skipped unknown record types: ${types}\\n`));\n }\n\n console.log(renderAuditReport(session, grade));\n}\n","/**\n * Discovers Claude Code session files under ~/.claude/projects/.\n *\n * Session files are at: ~/.claude/projects/{project-slug}/{sessionId}.jsonl\n * Subagent files are at: ~/.claude/projects/{project-slug}/{sessionId}/subagents/agent-*.jsonl\n */\n\nimport { readdir, stat } from \"node:fs/promises\";\nimport { join, basename, extname } from \"node:path\";\nimport { getClaudeDir } from \"../utils/paths.js\";\nimport type { SessionFile } from \"./types.js\";\n\n/**\n * Scan ~/.claude/projects/ for all main session JSONL files.\n * Returns files sorted by modification time (most recent first).\n */\nexport async function scanSessions(options?: {\n dataDir?: string;\n project?: string;\n since?: Date;\n}): Promise<SessionFile[]> {\n const claudeDir = options?.dataDir ?? getClaudeDir();\n const projectsDir = join(claudeDir, \"projects\");\n\n let projectDirs: string[];\n try {\n projectDirs = await readdir(projectsDir);\n } catch {\n throw new Error(\n \"Claude Code data directory not found. \" +\n \"Make sure Claude Code is installed and has been used at least once.\\n\" +\n `Expected: ${projectsDir}`,\n );\n }\n\n // Filter to specific project if requested\n if (options?.project) {\n projectDirs = projectDirs.filter((dir) =>\n dir.toLowerCase().includes(options.project!.toLowerCase()),\n );\n }\n\n const projectResults = await Promise.all(\n projectDirs\n .filter((dir) => !dir.startsWith(\".\"))\n .map(async (projectDir) => {\n const fullProjectDir = join(projectsDir, projectDir);\n let entries: string[];\n try {\n entries = await readdir(fullProjectDir);\n } catch {\n return [] as SessionFile[];\n }\n\n const fileResults = await Promise.all(\n entries\n .filter((entry) => extname(entry) === \".jsonl\")\n .map(async (entry) => {\n const filePath = join(fullProjectDir, entry);\n const sessionId = basename(entry, \".jsonl\");\n try {\n const fileStat = await stat(filePath);\n if (options?.since && fileStat.mtime < options.since) return null;\n\n let subagentPaths: string[] | undefined;\n try {\n const subagentDir = join(fullProjectDir, sessionId, \"subagents\");\n const agentFiles = await readdir(subagentDir);\n const paths = agentFiles\n .filter((f) => f.startsWith(\"agent-\") && f.endsWith(\".jsonl\"))\n .map((f) => join(subagentDir, f));\n if (paths.length > 0) subagentPaths = paths;\n } catch {\n // No subagents directory — normal for older sessions\n }\n\n return {\n path: filePath,\n sessionId,\n projectSlug: projectDir,\n mtime: fileStat.mtime,\n subagentPaths,\n } as SessionFile;\n } catch {\n return null;\n }\n }),\n );\n return fileResults.filter((f): f is SessionFile => f !== null);\n }),\n );\n\n const sessions: SessionFile[] = projectResults.flat();\n\n // Sort most recent first\n sessions.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n return sessions;\n}\n\n/**\n * Get the most recent session file, optionally filtered by project.\n */\nexport async function getMostRecentSession(options?: {\n dataDir?: string;\n project?: string;\n}): Promise<SessionFile> {\n const sessions = await scanSessions(options);\n if (sessions.length === 0) {\n throw new Error(\n \"No Claude Code sessions found. \" +\n \"Use Claude Code in a project first to generate session data.\",\n );\n }\n return sessions[0];\n}\n","/**\n * Cross-platform path resolution for Claude Code data directories.\n */\n\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/**\n * Returns the Claude Code data directory.\n * macOS/Linux: ~/.claude\n * Windows: %USERPROFILE%\\.claude\n */\nexport function getClaudeDir(): string {\n return join(homedir(), \".claude\");\n}\n\nexport function getCacheFilePath(): string {\n return join(getClaudeDir(), \"inspecto-cache.db\");\n}\n","/**\n * Streaming JSONL reader using Node's readline + createReadStream.\n * Processes files line-by-line to handle 100MB+ session files without\n * loading them into memory.\n */\n\nimport { createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport type { RawRecord } from \"./types.js\";\n\n/**\n * Stream-reads a JSONL file, yielding one parsed record per line.\n * Malformed lines are silently skipped (common near session end during crashes).\n */\nexport async function* readJsonl(filePath: string): AsyncGenerator<RawRecord> {\n const stream = createReadStream(filePath, { encoding: \"utf-8\" });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n\n for await (const line of rl) {\n const trimmed = line.trim();\n if (trimmed.length === 0) continue;\n\n try {\n const record = JSON.parse(trimmed) as RawRecord;\n if (record && typeof record === \"object\" && \"type\" in record) {\n yield record;\n }\n } catch {\n // Skip malformed lines — common at session boundaries\n }\n }\n}\n","/**\n * Builds a Session from raw JSONL records.\n *\n * Handles the core complexity of Claude Code's streaming format:\n * - Assistant turns are split across multiple JSONL records sharing the same\n * `message.id`. Content blocks from each chunk are merged into one turn.\n * - Only the final chunk (stop_reason != null) has real output_tokens.\n * - Synthetic records (model: \"<synthetic>\") and errored turns are excluded.\n */\n\nimport { basename } from \"node:path\";\nimport { readJsonl } from \"./jsonl-reader.js\";\nimport { SKIP_TYPES } from \"./types.js\";\nimport type {\n AssistantRecord,\n ContentBlock,\n MergedTurn,\n RawRecord,\n Session,\n UsageData,\n UserRecord,\n} from \"./types.js\";\n\ninterface AssistantAccumulator {\n content: ContentBlock[];\n usage: UsageData | null;\n complete: boolean;\n timestamp: string;\n model: string;\n}\n\n/**\n * Build a processed Session from an async stream of raw records.\n * @param records - AsyncIterable of raw JSONL records (from readJsonl)\n * @param sessionId - The session ID (from filename)\n * @param projectSlug - The project slug (from parent directory name)\n * @param subagentPaths - Optional paths to subagent JSONL files to merge in\n */\nexport async function buildSession(\n records: AsyncIterable<RawRecord>,\n sessionId: string,\n projectSlug: string,\n subagentPaths?: string[],\n): Promise<Session> {\n const turns: MergedTurn[] = [];\n const unknownRecordTypes = new Set<string>();\n\n let cwd = \"\";\n let gitBranch: string | null = null;\n let model = \"\";\n let firstTimestamp = \"\";\n let lastTimestamp = \"\";\n let formatVersion = \"\";\n\n async function processRecords(\n stream: AsyncIterable<RawRecord>,\n agentId: string | undefined,\n ) {\n const assistantChunks = new Map<string, AssistantAccumulator>();\n\n for await (const record of stream) {\n if (!formatVersion && \"version\" in record && typeof record.version === \"string\") {\n formatVersion = record.version;\n }\n\n if (SKIP_TYPES.has(record.type)) continue;\n\n if (record.type === \"user\") {\n const userRecord = record as UserRecord;\n handleUserRecord(userRecord, agentId);\n captureMetadata(userRecord);\n } else if (record.type === \"assistant\") {\n const assistantRecord = record as AssistantRecord;\n if (assistantRecord.message.model === \"<synthetic>\") continue;\n if (assistantRecord.error) continue;\n handleAssistantChunk(assistantRecord, assistantChunks);\n captureMetadata(assistantRecord);\n } else {\n unknownRecordTypes.add(record.type);\n }\n }\n\n // Flush all accumulated assistant chunks into turns\n for (const [, acc] of assistantChunks) {\n turns.push({\n role: \"assistant\",\n content: acc.content,\n usage: acc.usage,\n complete: acc.complete,\n timestamp: acc.timestamp,\n isHumanTurn: false,\n model: acc.model,\n agentId,\n });\n }\n }\n\n await processRecords(records, undefined);\n\n for (const agentPath of subagentPaths ?? []) {\n const agentId = basename(agentPath, \".jsonl\");\n await processRecords(readJsonl(agentPath), agentId);\n }\n\n // Sort all turns (main + subagents) by timestamp\n turns.sort((a, b) => a.timestamp.localeCompare(b.timestamp));\n\n const subagentIds = new Set(\n turns.filter((t) => t.agentId !== undefined).map((t) => t.agentId!),\n );\n const subagentTurnCount = turns.filter((t) => t.agentId !== undefined).length;\n\n return {\n id: sessionId,\n projectSlug,\n model,\n turns,\n startTime: firstTimestamp,\n endTime: lastTimestamp,\n cwd,\n gitBranch,\n durationMs:\n firstTimestamp && lastTimestamp\n ? new Date(lastTimestamp).getTime() - new Date(firstTimestamp).getTime()\n : 0,\n subagentCount: subagentIds.size,\n subagentTurnCount,\n formatVersion,\n unknownRecordTypes,\n };\n\n // -- Inner helpers --------------------------------------------------------\n\n function captureMetadata(record: UserRecord | AssistantRecord) {\n if (!firstTimestamp && record.timestamp) {\n firstTimestamp = record.timestamp;\n }\n if (record.timestamp) {\n lastTimestamp = record.timestamp;\n }\n if (!cwd && record.cwd) {\n cwd = record.cwd;\n }\n if (gitBranch === null && record.gitBranch) {\n gitBranch = record.gitBranch;\n }\n if (!model && record.type === \"assistant\") {\n const ar = record as AssistantRecord;\n if (ar.message.model && ar.message.model !== \"<synthetic>\") {\n model = ar.message.model;\n }\n }\n }\n\n function handleUserRecord(record: UserRecord, agentId: string | undefined) {\n const content = record.message.content;\n const isHumanTurn = typeof content === \"string\" && !record.isMeta;\n turns.push({\n role: \"user\",\n content: normalizeContent(content),\n usage: null,\n complete: true,\n timestamp: record.timestamp,\n isHumanTurn,\n agentId,\n });\n }\n\n function handleAssistantChunk(\n record: AssistantRecord,\n chunks: Map<string, AssistantAccumulator>,\n ) {\n const messageId = record.message.id;\n let acc = chunks.get(messageId);\n\n if (!acc) {\n acc = {\n content: [],\n usage: null,\n complete: false,\n timestamp: record.timestamp,\n model: record.message.model,\n };\n chunks.set(messageId, acc);\n }\n\n // Append content blocks from this streaming chunk\n for (const block of record.message.content) {\n acc.content.push(block);\n }\n\n // Final chunk has the real usage data\n if (record.message.stop_reason !== null) {\n acc.complete = true;\n acc.usage = record.message.usage;\n }\n }\n}\n\nfunction normalizeContent(content: string | ContentBlock[]): ContentBlock[] {\n if (typeof content === \"string\") {\n return [{ type: \"text\", text: content }];\n }\n return content;\n}\n","/**\n * Type definitions for Claude Code JSONL session data.\n *\n * Claude Code writes one JSONL file per session. Each line is a JSON record\n * with a discriminated `type` field. Assistant responses are streamed as\n * multiple chunks sharing the same `message.id` — only the final chunk\n * (with `stop_reason != null`) carries real token usage data.\n */\n\n// ---------------------------------------------------------------------------\n// Content blocks\n// ---------------------------------------------------------------------------\n\nexport interface TextBlock {\n type: \"text\";\n text: string;\n}\n\nexport interface ThinkingBlock {\n type: \"thinking\";\n thinking: string;\n signature?: string;\n}\n\nexport interface ToolUseBlock {\n type: \"tool_use\";\n id: string;\n name: string;\n input: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: \"tool_result\";\n tool_use_id: string;\n content: string | ContentBlock[];\n is_error?: boolean;\n}\n\nexport type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock;\n\n// ---------------------------------------------------------------------------\n// Usage data (on assistant messages)\n// ---------------------------------------------------------------------------\n\nexport interface UsageData {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number;\n cache_read_input_tokens: number;\n cache_creation?: {\n ephemeral_1h_input_tokens: number;\n ephemeral_5m_input_tokens: number;\n };\n server_tool_use?: {\n web_search_requests: number;\n web_fetch_requests: number;\n };\n service_tier?: string;\n speed?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Raw JSONL record types (discriminated union on `type`)\n// ---------------------------------------------------------------------------\n\nexport interface BaseRecord {\n uuid: string;\n parentUuid: string | null;\n sessionId: string;\n timestamp: string;\n version: string;\n cwd: string;\n type: string;\n isSidechain?: boolean;\n entrypoint?: string;\n gitBranch?: string | null;\n slug?: string;\n userType?: string;\n agentId?: string;\n}\n\nexport interface UserRecord extends BaseRecord {\n type: \"user\";\n message: {\n role: \"user\";\n content: string | ContentBlock[];\n };\n isMeta?: boolean;\n permissionMode?: string;\n}\n\nexport interface AssistantRecord extends BaseRecord {\n type: \"assistant\";\n requestId?: string;\n message: {\n id: string;\n type: \"message\";\n role: \"assistant\";\n model: string;\n content: ContentBlock[];\n stop_reason: \"tool_use\" | \"end_turn\" | \"stop_sequence\" | null;\n usage: UsageData;\n };\n error?: string;\n isApiErrorMessage?: boolean;\n}\n\nexport interface QueueOperationRecord {\n type: \"queue-operation\";\n operation: string;\n timestamp: string;\n sessionId: string;\n content?: string;\n}\n\nexport interface AttachmentRecord extends BaseRecord {\n type: \"attachment\";\n attachment: Record<string, unknown>;\n}\n\nexport interface SystemRecord extends BaseRecord {\n type: \"system\";\n subtype?: string;\n [key: string]: unknown;\n}\n\nexport interface LastPromptRecord {\n type: \"last-prompt\";\n lastPrompt: string;\n sessionId: string;\n}\n\nexport type RawRecord =\n | UserRecord\n | AssistantRecord\n | QueueOperationRecord\n | AttachmentRecord\n | SystemRecord\n | LastPromptRecord;\n\n/** Record types to skip during session building. */\nexport const SKIP_TYPES = new Set([\n \"queue-operation\",\n \"attachment\",\n \"system\",\n \"last-prompt\",\n]);\n\n// ---------------------------------------------------------------------------\n// Processed session types (output of session builder)\n// ---------------------------------------------------------------------------\n\nexport interface MergedTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n /** Real usage from the final streaming chunk. Null for user turns. */\n usage: UsageData | null;\n /** Whether the assistant turn completed (stop_reason was non-null). */\n complete: boolean;\n timestamp: string;\n /** True for human-authored user messages (not tool results or hook injections). */\n isHumanTurn: boolean;\n /** The model that generated this turn (assistant only). */\n model?: string;\n /** Subagent ID (e.g. \"agent-abc123\"). Undefined = main agent. */\n agentId?: string;\n}\n\nexport interface Session {\n id: string;\n projectSlug: string;\n model: string;\n turns: MergedTurn[];\n startTime: string;\n endTime: string;\n cwd: string;\n gitBranch: string | null;\n durationMs: number;\n subagentCount: number;\n subagentTurnCount: number;\n formatVersion: string;\n unknownRecordTypes: Set<string>;\n}\n\n// ---------------------------------------------------------------------------\n// Metric result types\n// ---------------------------------------------------------------------------\n\nexport type MetricStatus = \"healthy\" | \"warning\" | \"critical\";\n\nexport interface MetricResult {\n name: string;\n value: number | null;\n status: MetricStatus;\n label: string;\n detail?: string;\n}\n\nexport interface GradeResult {\n letter: string;\n score: number;\n metrics: MetricResult[];\n}\n\n// ---------------------------------------------------------------------------\n// Session discovery\n// ---------------------------------------------------------------------------\n\nexport interface SessionFile {\n path: string;\n sessionId: string;\n projectSlug: string;\n mtime: Date;\n subagentPaths?: string[];\n}\n","/**\n * M1: Reads-before-edit ratio.\n *\n * Counts how many Read tool_use events occur before each Write or Edit event.\n * High values mean Claude is reading context before modifying files.\n * The AMD data showed this dropped from 6.6 to 2.0 after March 8.\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\n\nconst EDIT_TOOLS = new Set([\"Write\", \"Edit\", \"NotebookEdit\"]);\nconst READ_TOOL = \"Read\";\n\nexport function computeReadsPerEdit(session: Session): MetricResult {\n let readsSinceLastEdit = 0;\n const ratios: number[] = [];\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n\n if (toolBlock.name === READ_TOOL) {\n readsSinceLastEdit++;\n } else if (EDIT_TOOLS.has(toolBlock.name)) {\n ratios.push(readsSinceLastEdit);\n readsSinceLastEdit = 0;\n }\n }\n }\n\n if (ratios.length === 0) {\n return {\n name: \"reads-per-edit\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No file modifications in this session\",\n };\n }\n\n const average = ratios.reduce((a, b) => a + b, 0) / ratios.length;\n\n return {\n name: \"reads-per-edit\",\n value: round(average),\n status: average >= 4.0 ? \"healthy\" : average >= 2.0 ? \"warning\" : \"critical\",\n label: round(average).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M2: Full-file rewrite ratio.\n *\n * Ratio of Write calls (full file replacement) to total file modifications\n * (Write + Edit). Rising ratio means Claude is rewriting instead of\n * making surgical edits.\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\n\nexport function computeRewriteRatio(session: Session): MetricResult {\n let writes = 0;\n let edits = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n\n if (toolBlock.name === \"Write\") writes++;\n else if (toolBlock.name === \"Edit\" || toolBlock.name === \"NotebookEdit\") edits++;\n }\n }\n\n const total = writes + edits;\n if (total === 0) {\n return {\n name: \"rewrite-ratio\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No file modifications in this session\",\n };\n }\n\n const ratio = writes / total;\n\n return {\n name: \"rewrite-ratio\",\n value: round(ratio),\n status: ratio <= 0.25 ? \"healthy\" : ratio <= 0.5 ? \"warning\" : \"critical\",\n label: round(ratio).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M3: Cache hit rate.\n *\n * Ratio of cache_read_input_tokens to total input tokens\n * (cache_read + cache_creation). Detects the prompt cache bug\n * that caused 10-20x cost inflation.\n *\n * Note: raw `input_tokens` is always a streaming placeholder (1 or 3).\n * Real input cost = cache_read + cache_creation.\n */\n\nimport type { MetricResult, Session } from \"../parser/types.js\";\n\nexport function computeCacheHitRate(session: Session): MetricResult {\n let totalCacheRead = 0;\n let totalCacheCreation = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\" || !turn.usage || !turn.complete) continue;\n\n totalCacheRead += turn.usage.cache_read_input_tokens;\n totalCacheCreation += turn.usage.cache_creation_input_tokens;\n }\n\n const totalInput = totalCacheRead + totalCacheCreation;\n if (totalInput === 0) {\n return {\n name: \"cache-hit-rate\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No token usage data available\",\n };\n }\n\n const rate = totalCacheRead / totalInput;\n\n return {\n name: \"cache-hit-rate\",\n value: round(rate),\n status: rate >= 0.5 ? \"healthy\" : rate >= 0.2 ? \"warning\" : \"critical\",\n label: round(rate).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M4: Task completion rate.\n *\n * Detects sessions where Claude says it will do something but doesn't\n * follow through. Looks for intent phrases in assistant text that\n * aren't followed by a tool_use in the next assistant turn.\n */\n\nimport type { MetricResult, Session, MergedTurn, TextBlock, ToolUseBlock } from \"../parser/types.js\";\n\nconst INTENT_PATTERNS = [\n /\\bI'll now\\b/i,\n /\\bLet me\\b/i,\n /\\bI'll update\\b/i,\n /\\bNext,? I'll\\b/i,\n /\\bI'll (?:also |then )?(?:fix|add|create|implement|refactor|modify|change|write|edit|update)\\b/i,\n /\\bI'm going to\\b/i,\n];\n\nexport function computeTaskCompletion(session: Session): MetricResult {\n const assistantTurns = session.turns.filter(\n (t) => t.role === \"assistant\" && t.complete,\n );\n\n let totalIntents = 0;\n let unfulfilledIntents = 0;\n\n for (const turn of assistantTurns) {\n const hasIntent = hasIntentPhrase(turn);\n if (!hasIntent) continue;\n\n totalIntents++;\n\n // An intent is fulfilled if the same merged turn also contains a tool_use.\n // Since streaming chunks are merged, a real action within this turn means\n // Claude followed through. An intent without a tool_use in the same turn\n // is a dangling promise.\n const hasToolUse = turn.content.some((b) => b.type === \"tool_use\");\n if (!hasToolUse) {\n unfulfilledIntents++;\n }\n }\n\n if (totalIntents === 0) {\n return {\n name: \"task-completion\",\n value: 1,\n status: \"healthy\",\n label: \"1.00\",\n detail: \"No intent phrases detected\",\n };\n }\n\n const rate = 1 - unfulfilledIntents / totalIntents;\n\n return {\n name: \"task-completion\",\n value: round(rate),\n status: rate >= 0.9 ? \"healthy\" : rate >= 0.7 ? \"warning\" : \"critical\",\n label: round(rate).toString(),\n };\n}\n\nfunction hasIntentPhrase(turn: MergedTurn): boolean {\n for (const block of turn.content) {\n if (block.type === \"text\") {\n const textBlock = block as TextBlock;\n if (INTENT_PATTERNS.some((p) => p.test(textBlock.text))) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * Levenshtein distance and normalized similarity.\n * Pure implementation — no external dependencies.\n */\n\n/**\n * Compute the Levenshtein edit distance between two strings.\n * Uses a single-row DP approach for O(min(m,n)) space.\n */\nexport function levenshteinDistance(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure a is the shorter string for space optimization\n if (a.length > b.length) [a, b] = [b, a];\n\n const aLen = a.length;\n const bLen = b.length;\n const row = new Array<number>(aLen + 1);\n\n for (let i = 0; i <= aLen; i++) row[i] = i;\n\n for (let j = 1; j <= bLen; j++) {\n let prev = row[0];\n row[0] = j;\n\n for (let i = 1; i <= aLen; i++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n const temp = row[i];\n row[i] = Math.min(\n row[i] + 1, // deletion\n row[i - 1] + 1, // insertion\n prev + cost, // substitution\n );\n prev = temp;\n }\n }\n\n return row[aLen];\n}\n\n/**\n * Compute normalized similarity between two strings (0 = different, 1 = identical).\n * Only compares the first `maxLen` characters for performance.\n */\nexport function normalizedSimilarity(\n a: string,\n b: string,\n maxLen = 200,\n): number {\n const aTrunc = a.slice(0, maxLen);\n const bTrunc = b.slice(0, maxLen);\n const maxLength = Math.max(aTrunc.length, bTrunc.length);\n\n if (maxLength === 0) return 1;\n\n const distance = levenshteinDistance(aTrunc, bTrunc);\n return 1 - distance / maxLength;\n}\n","/**\n * M5: Retry density.\n *\n * Measures how often the user sends messages very similar to their\n * previous message — a proxy for \"Claude got it wrong and I'm asking again.\"\n */\n\nimport type { MetricResult, Session, TextBlock } from \"../parser/types.js\";\nimport { normalizedSimilarity } from \"../utils/levenshtein.js\";\n\nexport function computeRetryDensity(session: Session): MetricResult {\n // Extract text from human-authored user turns only\n const humanTexts: string[] = [];\n for (const turn of session.turns) {\n if (!turn.isHumanTurn) continue;\n const text = turn.content\n .filter((b): b is TextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\" \");\n if (text.length > 0) humanTexts.push(text);\n }\n\n if (humanTexts.length < 2) {\n return {\n name: \"retry-density\",\n value: 0,\n status: \"healthy\",\n label: \"0.00\",\n detail: \"Not enough user messages to detect retries\",\n };\n }\n\n let retries = 0;\n const pairs = humanTexts.length - 1;\n\n for (let i = 0; i < pairs; i++) {\n const similarity = normalizedSimilarity(humanTexts[i], humanTexts[i + 1]);\n if (similarity > 0.6) {\n retries++;\n }\n }\n\n const density = retries / pairs;\n\n return {\n name: \"retry-density\",\n value: round(density),\n status: density <= 0.1 ? \"healthy\" : density <= 0.25 ? \"warning\" : \"critical\",\n label: round(density).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M6: Tool diversity score.\n *\n * Shannon entropy over the distribution of tool_use events by tool name.\n * Normalized to 0-1. Low diversity means Claude is over-relying on\n * one tool (often Write).\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\n\nexport function computeToolDiversity(session: Session): MetricResult {\n const toolCounts = new Map<string, number>();\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n toolCounts.set(toolBlock.name, (toolCounts.get(toolBlock.name) ?? 0) + 1);\n }\n }\n\n const uniqueTools = toolCounts.size;\n if (uniqueTools <= 1) {\n return {\n name: \"tool-diversity\",\n value: uniqueTools === 0 ? null : 0,\n status: uniqueTools === 0 ? \"healthy\" : \"critical\",\n label: uniqueTools === 0 ? \"N/A\" : \"0.00\",\n detail: uniqueTools === 0\n ? \"No tool usage in this session\"\n : `Only one tool used: ${[...toolCounts.keys()][0]}`,\n };\n }\n\n const totalCalls = [...toolCounts.values()].reduce((a, b) => a + b, 0);\n const maxEntropy = Math.log2(uniqueTools);\n\n let entropy = 0;\n for (const count of toolCounts.values()) {\n const p = count / totalCalls;\n entropy -= p * Math.log2(p);\n }\n\n const normalized = entropy / maxEntropy;\n\n // Build detail showing top tools\n const sorted = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]);\n const topTool = sorted[0];\n const topPercent = Math.round((topTool[1] / totalCalls) * 100);\n const detail = `Most used: ${topTool[0]} (${topPercent}%)`;\n\n return {\n name: \"tool-diversity\",\n value: round(normalized),\n status: normalized >= 0.6 ? \"healthy\" : normalized >= 0.4 ? \"warning\" : \"critical\",\n label: round(normalized).toString(),\n detail,\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M7: Tokens per useful edit.\n *\n * Total output tokens consumed divided by number of file modification\n * operations (Write + Edit). Rising ratio means Claude is burning more\n * tokens per productive action.\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\n\nconst EDIT_TOOLS = new Set([\"Write\", \"Edit\", \"NotebookEdit\"]);\n\nexport function computeTokensPerEdit(session: Session): MetricResult {\n let totalOutputTokens = 0;\n let editCount = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n // Count tokens from completed turns only\n if (turn.complete && turn.usage) {\n totalOutputTokens += turn.usage.output_tokens;\n }\n\n // Count edit operations\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n if (EDIT_TOOLS.has(toolBlock.name)) editCount++;\n }\n }\n\n if (editCount === 0) {\n return {\n name: \"tokens-per-edit\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No file modifications in this session\",\n };\n }\n\n const ratio = totalOutputTokens / editCount;\n\n return {\n name: \"tokens-per-edit\",\n value: Math.round(ratio),\n status: ratio <= 5000 ? \"healthy\" : ratio <= 15000 ? \"warning\" : \"critical\",\n label: Math.round(ratio).toLocaleString(\"en-US\"),\n };\n}\n","/**\n * M8: Subagent delegation ratio.\n *\n * Measures what fraction of total output tokens came from the main agent vs\n * subagents. A low main-agent ratio means the orchestrator delegated effectively.\n * Healthy = main agent produced < 60% of total output tokens.\n */\n\nimport type { MetricResult, Session } from \"../parser/types.js\";\n\nexport function computeSubagentOverhead(session: Session): MetricResult {\n if (session.subagentCount === 0) {\n return {\n name: \"subagent-overhead\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No subagents in this session\",\n };\n }\n\n let mainTokens = 0;\n let subagentTokens = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\" || !turn.usage) continue;\n const out = turn.usage.output_tokens ?? 0;\n if (turn.agentId === undefined) {\n mainTokens += out;\n } else {\n subagentTokens += out;\n }\n }\n\n const total = mainTokens + subagentTokens;\n if (total === 0) {\n return {\n name: \"subagent-overhead\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No output tokens recorded\",\n };\n }\n\n const ratio = mainTokens / total;\n const status = ratio < 0.6 ? \"healthy\" : ratio < 0.8 ? \"warning\" : \"critical\";\n\n return {\n name: \"subagent-overhead\",\n value: Math.round(ratio * 100) / 100,\n status,\n label: `${Math.round(ratio * 100)}% main`,\n };\n}\n","/**\n * Composite grading from all 8 quality metrics.\n *\n * Each metric is scored 0-100 based on its thresholds, then weighted\n * into a composite score mapped to a letter grade A+ through F.\n */\n\nimport type { GradeResult, MetricResult, Session } from \"../parser/types.js\";\nimport { computeReadsPerEdit } from \"./reads-per-edit.js\";\nimport { computeRewriteRatio } from \"./rewrite-ratio.js\";\nimport { computeCacheHitRate } from \"./cache-hit-rate.js\";\nimport { computeTaskCompletion } from \"./task-completion.js\";\nimport { computeRetryDensity } from \"./retry-density.js\";\nimport { computeToolDiversity } from \"./tool-diversity.js\";\nimport { computeTokensPerEdit } from \"./tokens-per-edit.js\";\nimport { computeSubagentOverhead } from \"./subagent-overhead.js\";\n\ninterface MetricWeight {\n compute: (session: Session) => MetricResult;\n weight: number;\n /** Convert metric value to 0-100 score. Higher is better. */\n score: (value: number) => number;\n}\n\nconst METRIC_WEIGHTS: MetricWeight[] = [\n {\n compute: computeReadsPerEdit,\n weight: 0.2,\n // 0 reads → 0, 2 reads → 50, 4+ reads → 100\n score: (v) => clamp(v / 4 * 100, 0, 100),\n },\n {\n compute: computeRewriteRatio,\n weight: 0.15,\n // 0 ratio → 100, 0.25 → 50, 0.5+ → 0 (inverted: lower is better)\n score: (v) => clamp((1 - v / 0.5) * 100, 0, 100),\n },\n {\n compute: computeCacheHitRate,\n weight: 0.15,\n // 0% → 0, 50% → 100\n score: (v) => clamp(v / 0.5 * 100, 0, 100),\n },\n {\n compute: computeTaskCompletion,\n weight: 0.15,\n // 0.7 → 0, 0.9 → 50, 1.0 → 100\n score: (v) => clamp((v - 0.7) / 0.3 * 100, 0, 100),\n },\n {\n compute: computeRetryDensity,\n weight: 0.1,\n // 0% → 100, 10% → 60, 25%+ → 0 (inverted)\n score: (v) => clamp((1 - v / 0.25) * 100, 0, 100),\n },\n {\n compute: computeToolDiversity,\n weight: 0.05,\n // 0 → 0, 0.4 → 50, 0.6+ → 100\n score: (v) => clamp(v / 0.6 * 100, 0, 100),\n },\n {\n compute: computeTokensPerEdit,\n weight: 0.15,\n // 5000 → 100, 10000 → 50, 15000+ → 0 (inverted)\n score: (v) => clamp((1 - (v - 5000) / 10000) * 100, 0, 100),\n },\n {\n compute: computeSubagentOverhead,\n weight: 0.05,\n // main ratio 0 → 100, 0.6 → 100 (threshold), 0.8 → 50, 1.0 → 0 (inverted)\n score: (v) => clamp((1 - v) / 0.4 * 100, 0, 100),\n },\n];\n\nconst GRADE_THRESHOLDS: Array<[number, string]> = [\n [97, \"A+\"],\n [93, \"A\"],\n [90, \"A-\"],\n [87, \"B+\"],\n [83, \"B\"],\n [80, \"B-\"],\n [77, \"C+\"],\n [73, \"C\"],\n [70, \"C-\"],\n [67, \"D+\"],\n [63, \"D\"],\n [60, \"D-\"],\n [0, \"F\"],\n];\n\nexport function gradeLetterFromScore(score: number): string {\n return GRADE_THRESHOLDS.find(([threshold]) => score >= threshold)?.[1] ?? \"F\";\n}\n\nexport function gradeSession(session: Session): GradeResult {\n const metrics: MetricResult[] = [];\n let weightedSum = 0;\n let totalWeight = 0;\n\n for (const mw of METRIC_WEIGHTS) {\n const result = mw.compute(session);\n metrics.push(result);\n\n if (result.value !== null) {\n weightedSum += mw.score(result.value) * mw.weight;\n totalWeight += mw.weight;\n }\n }\n\n // Normalize if some metrics returned null (insufficient data)\n const compositeScore = totalWeight > 0 ? weightedSum / totalWeight : 0;\n\n const letter = gradeLetterFromScore(compositeScore);\n\n return {\n letter,\n score: Math.round(compositeScore),\n metrics,\n };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n","/**\n * Terminal output formatting using chalk and cli-table3.\n */\n\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { GradeResult, MetricResult, Session } from \"../parser/types.js\";\nimport type { RegressionResult } from \"../anomaly/regression-detector.js\";\nimport type { CacheCheckResult } from \"../anomaly/cache-anomaly.js\";\nimport { formatDuration, shortSessionId, projectNameFromSlug, formatNumber } from \"../utils/format.js\";\nimport { getAllTips } from \"./tips.js\";\nimport { VERSION } from \"../version.js\";\n\nconst STATUS_ICONS: Record<string, string> = {\n healthy: chalk.green(\"✓\"),\n warning: chalk.yellow(\"⚠\"),\n critical: chalk.red(\"✗\"),\n};\n\nconst STATUS_LABELS: Record<string, string> = {\n healthy: chalk.green(\"healthy\"),\n warning: chalk.yellow(\"warning\"),\n critical: chalk.red(\"critical\"),\n};\n\nconst METRIC_DISPLAY_NAMES: Record<string, string> = {\n \"reads-per-edit\": \"Reads/edit\",\n \"rewrite-ratio\": \"Rewrite ratio\",\n \"cache-hit-rate\": \"Cache hit rate\",\n \"task-completion\": \"Task completion\",\n \"retry-density\": \"Retry density\",\n \"tool-diversity\": \"Tool diversity\",\n \"tokens-per-edit\": \"Tokens/useful-edit\",\n \"subagent-overhead\": \"Subagent delegation\",\n};\n\nexport function renderAuditReport(session: Session, grade: GradeResult): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(chalk.bold(` inspecto v${VERSION}`) + chalk.dim(\" — Claude Code Session Quality Analyzer\"));\n lines.push(\"\");\n\n const agentInfo = session.subagentCount > 0\n ? `${session.subagentCount} subagents | ${session.turns.length} turns`\n : `${session.turns.length} turns`;\n\n const sessionInfo = [\n `Session: ${chalk.cyan(shortSessionId(session.id))}`,\n projectNameFromSlug(session.projectSlug),\n formatDuration(session.durationMs),\n session.model,\n agentInfo,\n ].join(chalk.dim(\" | \"));\n lines.push(` ${sessionInfo}`);\n lines.push(\"\");\n\n const gradeColor = getGradeColor(grade.letter);\n lines.push(` Overall grade: ${gradeColor(chalk.bold(grade.letter))}`);\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Metric\", \"Value\", \"Status\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const metric of grade.metrics) {\n const displayName = METRIC_DISPLAY_NAMES[metric.name] ?? metric.name;\n const icon = STATUS_ICONS[metric.status] ?? \"\";\n table.push([displayName, metric.label, `${icon} ${STATUS_LABELS[metric.status] ?? metric.status}`]);\n }\n\n lines.push(table.toString());\n\n const tips = getAllTips(grade.metrics);\n if (tips.length > 0) {\n lines.push(\"\");\n lines.push(chalk.yellow(\" Tips:\"));\n for (const tip of tips) {\n lines.push(` ${chalk.dim(\"→\")} ${tip}`);\n }\n }\n\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nexport function renderTrendReport(\n results: RegressionResult[],\n sessionCount: number,\n period: string,\n): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(chalk.bold(` Trend report: last ${period}`) + chalk.dim(` (${sessionCount} sessions)`));\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Metric\", \"Recent avg\", \"Full avg\", \"Change\", \"Status\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const result of results) {\n const displayName = METRIC_DISPLAY_NAMES[result.name] ?? result.name;\n const recentStr = result.recentAvg !== null ? result.recentAvg.toFixed(2) : \"N/A\";\n const fullStr = result.fullAvg !== null ? result.fullAvg.toFixed(2) : \"N/A\";\n\n let changeStr = \"N/A\";\n if (result.changePercent !== null) {\n const arrow = result.changePercent > 0 ? \"▲\" : result.changePercent < 0 ? \"▼\" : \"\";\n changeStr = `${arrow} ${Math.abs(Math.round(result.changePercent))}%`;\n }\n\n const statusStr = formatRegressionStatus(result.status);\n table.push([displayName, recentStr, fullStr, changeStr, statusStr]);\n }\n\n lines.push(table.toString());\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nexport function renderCacheCheckReport(results: CacheCheckResult[]): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(chalk.bold(\" Cache health check\"));\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Session\", \"Project\", \"Cache Hit\", \"Status\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const result of results) {\n const hitStr = result.cacheHitRate !== null ? result.cacheHitRate.toFixed(2) : \"N/A\";\n const statusStr = result.isAnomaly\n ? chalk.red(\"✗ ANOMALY\")\n : chalk.green(\"✓ normal\");\n\n table.push([\n shortSessionId(result.sessionId),\n projectNameFromSlug(result.projectSlug),\n hitStr,\n statusStr,\n ]);\n }\n\n lines.push(table.toString());\n\n const anomalies = results.filter((r) => r.isAnomaly);\n if (anomalies.length > 0) {\n lines.push(\"\");\n lines.push(\n chalk.yellow(` ⚠ ${anomalies.length} session(s) with abnormally low cache hit rate.`),\n );\n for (const a of anomalies) {\n if (a.estimatedInflation) {\n lines.push(\n ` ${chalk.dim(\"→\")} Session ${shortSessionId(a.sessionId)} consumed ~${a.estimatedInflation}x more input tokens than expected.`,\n );\n }\n }\n lines.push(\n chalk.dim(\" Try: restart Claude Code or downgrade to a previous version.\"),\n );\n } else {\n lines.push(\"\");\n lines.push(chalk.green(\" ✓ No cache anomalies detected.\"));\n }\n\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nfunction getGradeColor(letter: string): (text: string) => string {\n if (letter.startsWith(\"A\")) return chalk.green;\n if (letter.startsWith(\"B\")) return chalk.cyan;\n if (letter.startsWith(\"C\")) return chalk.yellow;\n return chalk.red;\n}\n\nfunction formatRegressionStatus(status: string): string {\n switch (status) {\n case \"stable\":\n return chalk.green(\"✓ stable\");\n case \"declining\":\n return chalk.yellow(\"⚠ declining\");\n case \"regression\":\n return chalk.red(\"⚠ REGRESSION\");\n default:\n return status;\n }\n}\n","/**\n * Number and string formatting helpers for terminal output.\n */\n\n/** Format a number with comma separators: 3218 → \"3,218\" */\nexport function formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\n/** Format a ratio as a fixed-2 decimal: 0.734 → \"0.73\" */\nexport function formatRatio(n: number): string {\n return n.toFixed(2);\n}\n\n/** Format a percentage: 0.734 → \"73%\" */\nexport function formatPercent(n: number): string {\n return `${Math.round(n * 100)}%`;\n}\n\n/** Format milliseconds into a human-readable duration: 2820000 → \"47 min\" */\nexport function formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes} min`;\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n if (remainingMinutes === 0) return `${hours}h`;\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/** Truncate a session ID for display: \"31f3f224-abcd-...\" → \"31f3f224\" */\nexport function shortSessionId(id: string): string {\n return id.slice(0, 8);\n}\n\n/** Extract a human-readable project name from a slug like \"-Users-foo-my-app\" */\nexport function projectNameFromSlug(slug: string): string {\n const parts = slug.split(\"-\").filter(Boolean);\n return parts[parts.length - 1] || slug;\n}\n","/**\n * Contextual tips based on metric values.\n * Maps poor-scoring metrics to actionable suggestions.\n */\n\nimport type { MetricResult } from \"../parser/types.js\";\n\nconst TIPS: Record<string, Record<string, string>> = {\n \"reads-per-edit\": {\n warning: \"Claude is editing with less context. Add 'Always read files before editing' to your CLAUDE.md.\",\n critical: \"Very low reads before edits. Claude is making blind changes. Consider adding explicit read instructions.\",\n },\n \"rewrite-ratio\": {\n warning: \"High ratio of full-file rewrites. Add 'Prefer Edit over Write for existing files' to CLAUDE.md.\",\n critical: \"Claude is rewriting entire files instead of making surgical edits. This wastes tokens and risks data loss.\",\n },\n \"cache-hit-rate\": {\n warning: \"Cache hit rate is below normal. Sessions may be too short for caching to help.\",\n critical: \"Very low cache hit rate — possible cache bug. Try restarting Claude Code or downgrading to a previous version.\",\n },\n \"task-completion\": {\n warning: \"Claude is occasionally promising actions without following through.\",\n critical: \"Frequent unfulfilled promises. Claude says it will do things but doesn't. Try breaking tasks into smaller steps.\",\n },\n \"retry-density\": {\n warning: \"Some user messages look like retries. Claude may be misunderstanding requests.\",\n critical: \"High retry rate. Users are frequently re-asking. Consider providing more context in prompts.\",\n },\n \"tool-diversity\": {\n warning: \"Low tool diversity. Claude is over-relying on a narrow set of tools.\",\n critical: \"Very narrow tool usage. Claude may be stuck in a pattern. Try prompting for specific tool usage.\",\n },\n \"tokens-per-edit\": {\n warning: \"Tokens per edit is above average. Claude may be verbose without being productive.\",\n critical: \"Very high token cost per edit. Claude is burning tokens without proportional output.\",\n },\n};\n\nexport function getTip(metric: MetricResult): string | null {\n if (metric.status === \"healthy\") return null;\n\n const metricTips = TIPS[metric.name];\n if (!metricTips) return null;\n\n return metricTips[metric.status] ?? null;\n}\n\nexport function getAllTips(metrics: MetricResult[]): string[] {\n return metrics\n .map(getTip)\n .filter((tip): tip is string => tip !== null);\n}\n","import { createRequire } from \"node:module\";\n\nconst _require = createRequire(import.meta.url);\nexport const VERSION: string = (_require(\"../package.json\") as { version: string }).version;\n","/**\n * JSON output mode for scripting and CI.\n */\n\nimport type { GradeResult, Session } from \"../parser/types.js\";\nimport type { RegressionResult } from \"../anomaly/regression-detector.js\";\nimport type { CacheCheckResult } from \"../anomaly/cache-anomaly.js\";\n\nexport interface AuditJsonOutput {\n session: {\n id: string;\n project: string;\n model: string;\n durationMs: number;\n startTime: string;\n };\n grade: string;\n score: number;\n metrics: Array<{\n name: string;\n value: number | null;\n status: string;\n label: string;\n }>;\n}\n\nexport function formatAuditJson(session: Session, grade: GradeResult): string {\n const output: AuditJsonOutput = {\n session: {\n id: session.id,\n project: session.projectSlug,\n model: session.model,\n durationMs: session.durationMs,\n startTime: session.startTime,\n },\n grade: grade.letter,\n score: grade.score,\n metrics: grade.metrics.map((m) => ({\n name: m.name,\n value: m.value,\n status: m.status,\n label: m.label,\n })),\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nexport function formatTrendJson(results: RegressionResult[]): string {\n return JSON.stringify({ trend: results }, null, 2);\n}\n\nexport function formatCacheCheckJson(results: CacheCheckResult[]): string {\n return JSON.stringify({ cacheCheck: results }, null, 2);\n}\n","/**\n * Compute rolling averages from multiple sessions for trend analysis.\n */\n\nimport type { GradeResult } from \"../parser/types.js\";\n\nexport interface MetricAverage {\n name: string;\n recentAvg: number | null;\n fullAvg: number | null;\n changePercent: number | null;\n}\n\n/**\n * Compute per-metric averages for a recent window vs. full range.\n * @param grades - All graded sessions, sorted most recent first\n * @param recentCount - Number of sessions in the \"recent\" window\n */\nexport function computeBaselines(\n grades: GradeResult[],\n recentCount: number,\n): MetricAverage[] {\n if (grades.length === 0) return [];\n\n const recent = grades.slice(0, recentCount);\n const full = grades;\n\n const metricNames = grades[0].metrics.map((m) => m.name);\n\n return metricNames.map((name) => {\n const recentValues = extractValues(recent, name);\n const fullValues = extractValues(full, name);\n\n const recentAvg = average(recentValues);\n const fullAvg = average(fullValues);\n\n let changePercent: number | null = null;\n if (recentAvg !== null && fullAvg !== null && fullAvg !== 0) {\n changePercent = ((recentAvg - fullAvg) / Math.abs(fullAvg)) * 100;\n }\n\n return { name, recentAvg, fullAvg, changePercent };\n });\n}\n\nfunction extractValues(grades: GradeResult[], metricName: string): number[] {\n const values: number[] = [];\n for (const grade of grades) {\n const metric = grade.metrics.find((m) => m.name === metricName);\n if (metric?.value !== null && metric?.value !== undefined) {\n values.push(metric.value);\n }\n }\n return values;\n}\n\nfunction average(values: number[]): number | null {\n if (values.length === 0) return null;\n return values.reduce((a, b) => a + b, 0) / values.length;\n}\n","/**\n * Z-score based regression detection.\n *\n * Compares recent metric values against historical baseline to flag\n * statistically significant regressions.\n */\n\nimport type { MetricAverage } from \"./baseline.js\";\n\nexport type RegressionStatus = \"stable\" | \"declining\" | \"regression\";\n\nexport interface RegressionResult {\n name: string;\n recentAvg: number | null;\n fullAvg: number | null;\n changePercent: number | null;\n status: RegressionStatus;\n}\n\n/** Metrics where HIGHER values are WORSE (inverted for regression detection). */\nconst INVERTED_METRICS = new Set([\n \"rewrite-ratio\",\n \"retry-density\",\n \"tokens-per-edit\",\n]);\n\n/**\n * Detect regressions from baseline averages.\n * A change > 30% in the \"bad\" direction is a regression.\n * A change > 10% is \"declining\".\n */\nexport function detectRegressions(\n baselines: MetricAverage[],\n): RegressionResult[] {\n return baselines.map((b) => {\n let status: RegressionStatus = \"stable\";\n\n if (b.changePercent !== null) {\n const isInverted = INVERTED_METRICS.has(b.name);\n // For normal metrics, negative change is bad. For inverted, positive change is bad.\n const badDirection = isInverted ? b.changePercent > 0 : b.changePercent < 0;\n const magnitude = Math.abs(b.changePercent);\n\n if (badDirection && magnitude > 30) {\n status = \"regression\";\n } else if (badDirection && magnitude > 10) {\n status = \"declining\";\n }\n }\n\n return {\n name: b.name,\n recentAvg: b.recentAvg,\n fullAvg: b.fullAvg,\n changePercent: b.changePercent,\n status,\n };\n });\n}\n","/**\n * Parse human-readable duration strings into Date offsets.\n */\n\n/**\n * Parse a duration string like \"7d\", \"14d\", \"30d\" into a Date\n * representing that many days before `now`.\n */\nexport function parseDuration(duration: string, now = new Date()): Date {\n const match = duration.match(/^(\\d+)d$/);\n if (!match) {\n throw new Error(\n `Invalid duration: \"${duration}\". Use format like \"7d\", \"14d\", \"30d\".`,\n );\n }\n\n const days = parseInt(match[1], 10);\n const result = new Date(now);\n result.setDate(result.getDate() - days);\n return result;\n}\n","/**\n * Runs fn over items with at most `limit` concurrent in-flight promises.\n * Returns settled results in input order, matching Promise.allSettled semantics.\n * One rejection never aborts the remaining items.\n */\nexport async function concurrentSettled<T, R>(\n items: T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<PromiseSettledResult<R>[]> {\n const results = new Array<PromiseSettledResult<R>>(items.length);\n let next = 0;\n\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const i = next++;\n try {\n results[i] = { status: \"fulfilled\", value: await fn(items[i]) };\n } catch (error) {\n results[i] = { status: \"rejected\", reason: error };\n }\n }\n };\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return results;\n}\n","import { DatabaseSync } from \"node:sqlite\";\nimport { createHash } from \"node:crypto\";\nimport type { GradeResult } from \"../parser/types.js\";\nimport { getCacheFilePath } from \"../utils/paths.js\";\n\nlet db: DatabaseSync | null = null;\n\nfunction getDb(): DatabaseSync {\n if (!db) {\n db = new DatabaseSync(getCacheFilePath());\n db.exec(`\n CREATE TABLE IF NOT EXISTS grade_cache (\n cache_key TEXT PRIMARY KEY,\n grade_result TEXT NOT NULL\n )\n `);\n }\n return db;\n}\n\nfunction makeCacheKey(sessionPath: string, mtime: Date): string {\n return createHash(\"sha256\")\n .update(`${sessionPath}:${mtime.getTime()}`)\n .digest(\"hex\");\n}\n\nexport function getCachedGrade(sessionPath: string, mtime: Date): GradeResult | null {\n try {\n const key = makeCacheKey(sessionPath, mtime);\n const stmt = getDb().prepare(\"SELECT grade_result FROM grade_cache WHERE cache_key = ?\");\n const row = stmt.get(key) as { grade_result: string } | undefined;\n if (!row) return null;\n return JSON.parse(row.grade_result) as GradeResult;\n } catch {\n process.stderr.write(\"[inspecto cache] read error, skipping cache\\n\");\n return null;\n }\n}\n\nexport function setCachedGrade(sessionPath: string, mtime: Date, grade: GradeResult): void {\n try {\n const key = makeCacheKey(sessionPath, mtime);\n const stmt = getDb().prepare(\n \"INSERT OR REPLACE INTO grade_cache (cache_key, grade_result) VALUES (?, ?)\",\n );\n stmt.run(key, JSON.stringify(grade));\n } catch {\n process.stderr.write(\"[inspecto cache] write error, skipping cache\\n\");\n }\n}\n","/**\n * Trend analysis command — detect regressions over time.\n */\n\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { gradeSession } from \"../metrics/grader.js\";\nimport { computeBaselines } from \"../anomaly/baseline.js\";\nimport { detectRegressions } from \"../anomaly/regression-detector.js\";\nimport { renderTrendReport } from \"../reporter/terminal.js\";\nimport { formatTrendJson } from \"../reporter/json-reporter.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport { concurrentSettled } from \"../utils/concurrent.js\";\nimport { getCachedGrade, setCachedGrade } from \"../cache/grade-cache.js\";\nimport type { GradeResult, SessionFile } from \"../parser/types.js\";\n\nconst CONCURRENCY = 16;\n\nexport interface TrendOptions {\n since?: string;\n json?: boolean;\n dataDir?: string;\n project?: string;\n}\n\nexport async function runTrend(options: TrendOptions): Promise<void> {\n const duration = options.since ?? \"7d\";\n const sinceDate = parseDuration(duration);\n\n const sessionFiles = await scanSessions({\n dataDir: options.dataDir,\n project: options.project,\n since: sinceDate,\n });\n\n if (sessionFiles.length === 0) {\n console.log(`No sessions found in the last ${duration}.`);\n return;\n }\n\n const settled = await concurrentSettled(sessionFiles, CONCURRENCY, async (sf: SessionFile) => {\n const cached = getCachedGrade(sf.path, sf.mtime);\n if (cached) return cached;\n const records = readJsonl(sf.path);\n const session = await buildSession(records, sf.sessionId, sf.projectSlug, sf.subagentPaths);\n const grade = gradeSession(session);\n setCachedGrade(sf.path, sf.mtime, grade);\n return grade;\n });\n\n const grades: GradeResult[] = settled\n .filter((r): r is PromiseFulfilledResult<GradeResult> => r.status === \"fulfilled\")\n .map((r) => r.value);\n\n if (grades.length === 0) {\n console.log(\"No valid sessions found to analyze.\");\n return;\n }\n\n // Use half the sessions as the \"recent\" window, minimum 1\n const recentCount = Math.max(1, Math.floor(grades.length / 2));\n const baselines = computeBaselines(grades, recentCount);\n const regressions = detectRegressions(baselines);\n\n if (options.json) {\n console.log(formatTrendJson(regressions));\n } else {\n console.log(renderTrendReport(regressions, grades.length, duration));\n }\n}\n","/**\n * Cache hit rate anomaly detection.\n *\n * Specifically checks for the prompt cache bug that caused 10-20x\n * token cost inflation by detecting sessions with near-zero cache hit rates.\n */\n\nimport type { Session } from \"../parser/types.js\";\nimport { computeCacheHitRate } from \"../metrics/cache-hit-rate.js\";\n\nexport interface CacheCheckResult {\n sessionId: string;\n projectSlug: string;\n timestamp: string;\n cacheHitRate: number | null;\n isAnomaly: boolean;\n estimatedInflation: number | null;\n}\n\nconst ANOMALY_THRESHOLD = 0.05;\nconst NORMAL_CACHE_RATE = 0.65;\n\n/**\n * Check a single session for cache hit rate anomalies.\n */\nexport function checkCacheAnomaly(session: Session): CacheCheckResult {\n const metric = computeCacheHitRate(session);\n\n const isAnomaly = metric.value !== null && metric.value < ANOMALY_THRESHOLD;\n\n let estimatedInflation: number | null = null;\n if (isAnomaly && metric.value !== null) {\n // If normal rate is 65% cache reads, the effective input cost multiplier\n // when cache is broken is roughly 1 / (1 - normalRate)\n // Normal: 35% full-price tokens. Broken: 100% full-price tokens.\n estimatedInflation = Math.round(1 / (1 - NORMAL_CACHE_RATE));\n }\n\n return {\n sessionId: session.id,\n projectSlug: session.projectSlug,\n timestamp: session.startTime,\n cacheHitRate: metric.value,\n isAnomaly,\n estimatedInflation,\n };\n}\n","/**\n * Cache bug detection command.\n * Scans recent sessions for abnormally low cache hit rates.\n */\n\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { checkCacheAnomaly } from \"../anomaly/cache-anomaly.js\";\nimport { renderCacheCheckReport } from \"../reporter/terminal.js\";\nimport { formatCacheCheckJson } from \"../reporter/json-reporter.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport type { CacheCheckResult } from \"../anomaly/cache-anomaly.js\";\n\nexport interface CacheCheckOptions {\n since?: string;\n json?: boolean;\n dataDir?: string;\n}\n\nexport async function runCacheCheck(options: CacheCheckOptions): Promise<void> {\n const duration = options.since ?? \"7d\";\n const sinceDate = parseDuration(duration);\n\n const sessionFiles = await scanSessions({\n dataDir: options.dataDir,\n since: sinceDate,\n });\n\n if (sessionFiles.length === 0) {\n console.log(`No sessions found in the last ${duration}.`);\n return;\n }\n\n const settled = await Promise.allSettled(\n sessionFiles.map(async (sf) => {\n const records = readJsonl(sf.path);\n const session = await buildSession(records, sf.sessionId, sf.projectSlug, sf.subagentPaths);\n return checkCacheAnomaly(session);\n }),\n );\n const results: CacheCheckResult[] = settled\n .filter((r): r is PromiseFulfilledResult<CacheCheckResult> => r.status === \"fulfilled\")\n .map((r) => r.value);\n\n if (results.length === 0) {\n console.log(\"No valid sessions found to analyze.\");\n return;\n }\n\n if (options.json) {\n console.log(formatCacheCheckJson(results));\n } else {\n console.log(renderCacheCheckReport(results));\n }\n}\n","/**\n * Cross-project comparison command.\n * Compares average quality metrics across multiple projects.\n */\n\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { gradeSession, gradeLetterFromScore } from \"../metrics/grader.js\";\nimport { concurrentSettled } from \"../utils/concurrent.js\";\nimport { getCachedGrade, setCachedGrade } from \"../cache/grade-cache.js\";\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { GradeResult, SessionFile } from \"../parser/types.js\";\n\nconst CONCURRENCY = 16;\n\nexport interface CompareOptions {\n projects: string;\n json?: boolean;\n dataDir?: string;\n since?: string;\n}\n\ninterface ProjectSummary {\n name: string;\n sessionCount: number;\n avgGrade: number;\n avgLetter: string;\n metrics: Map<string, number>;\n}\n\nexport async function runCompare(options: CompareOptions): Promise<void> {\n const projectNames = options.projects.split(\",\").map((p) => p.trim());\n const summaries: ProjectSummary[] = [];\n\n const projectSummaries = await Promise.all(\n projectNames.map(async (projectFilter) => {\n const sessionFiles = await scanSessions({\n dataDir: options.dataDir,\n project: projectFilter,\n });\n\n if (sessionFiles.length === 0) return null;\n\n const settled = await concurrentSettled(\n sessionFiles,\n CONCURRENCY,\n async (sf: SessionFile) => {\n const cached = getCachedGrade(sf.path, sf.mtime);\n if (cached) return cached;\n const records = readJsonl(sf.path);\n const session = await buildSession(\n records,\n sf.sessionId,\n sf.projectSlug,\n sf.subagentPaths,\n );\n const grade = gradeSession(session);\n setCachedGrade(sf.path, sf.mtime, grade);\n return grade;\n },\n );\n\n const grades: GradeResult[] = settled\n .filter((r): r is PromiseFulfilledResult<GradeResult> => r.status === \"fulfilled\")\n .map((r) => r.value);\n\n if (grades.length === 0) return null;\n\n const avgScore = grades.reduce((s, g) => s + g.score, 0) / grades.length;\n const metricAvgs = new Map<string, number>();\n for (const metric of grades[0].metrics) {\n const values = grades\n .map((g) => g.metrics.find((m) => m.name === metric.name)?.value)\n .filter((v): v is number => v !== null);\n if (values.length > 0) {\n metricAvgs.set(metric.name, values.reduce((a, b) => a + b, 0) / values.length);\n }\n }\n\n return {\n name: projectFilter,\n sessionCount: grades.length,\n avgGrade: Math.round(avgScore),\n avgLetter: gradeLetterFromScore(avgScore),\n metrics: metricAvgs,\n } as ProjectSummary;\n }),\n );\n\n for (const s of projectSummaries) {\n if (s !== null) summaries.push(s);\n }\n\n if (summaries.length === 0) {\n console.log(\"No matching projects found.\");\n return;\n }\n\n if (options.json) {\n const jsonOutput = summaries.map((s) => ({\n project: s.name,\n sessions: s.sessionCount,\n grade: s.avgLetter,\n score: s.avgGrade,\n metrics: Object.fromEntries(s.metrics),\n }));\n console.log(JSON.stringify({ compare: jsonOutput }, null, 2));\n return;\n }\n\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(chalk.bold(\" Project comparison\"));\n lines.push(\"\");\n\n const head = [\"Project\", \"Sessions\", \"Grade\", ...summaries[0]?.metrics.keys() ?? []].map(\n (h) => chalk.dim(h),\n );\n\n const table = new Table({\n head,\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const summary of summaries) {\n const row: string[] = [\n summary.name,\n summary.sessionCount.toString(),\n summary.avgLetter,\n ];\n for (const [, value] of summary.metrics) {\n row.push(value.toFixed(2));\n }\n table.push(row);\n }\n\n lines.push(table.toString());\n lines.push(\"\");\n console.log(lines.join(\"\\n\"));\n}\n\n"],"mappings":"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","names":["chalk","join","join","basename","basename","average","round","round","round","round","round","EDIT_TOOLS","chalk","chalk","Table","CONCURRENCY"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/audit.ts","../src/parser/project-scanner.ts","../src/utils/paths.ts","../src/parser/jsonl-reader.ts","../src/parser/session-builder.ts","../src/parser/types.ts","../src/metrics/reads-per-edit.ts","../src/metrics/rewrite-ratio.ts","../src/metrics/cache-hit-rate.ts","../src/metrics/task-completion.ts","../src/utils/levenshtein.ts","../src/metrics/retry-density.ts","../src/metrics/tool-diversity.ts","../src/metrics/tokens-per-edit.ts","../src/metrics/subagent-overhead.ts","../src/metrics/tool-error-rate.ts","../src/metrics/thinking-utilization.ts","../src/metrics/mcp-usage.ts","../src/metrics/session-cost.ts","../src/metrics/grader.ts","../src/reporter/terminal.ts","../src/utils/format.ts","../src/reporter/tips.ts","../src/version.ts","../src/reporter/json-reporter.ts","../src/reporter/csv-reporter.ts","../src/config/loader.ts","../src/anomaly/baseline.ts","../src/anomaly/regression-detector.ts","../src/utils/duration.ts","../src/utils/concurrent.ts","../src/cache/grade-cache.ts","../src/commands/trend.ts","../src/anomaly/cache-anomaly.ts","../src/commands/cache-check.ts","../src/commands/compare.ts","../src/commands/list.ts","../src/commands/config-validate.ts","../src/config/types.ts"],"sourcesContent":["/**\n * inspecto — Claude Code Session Quality Analyzer\n *\n * Grade sessions, detect regressions, catch cache bugs.\n * All from the JSONL logs Claude Code already writes.\n */\n\nimport { Command } from \"commander\";\nimport { unlink } from \"node:fs/promises\";\nimport { runAudit } from \"./commands/audit.js\";\nimport { runTrend } from \"./commands/trend.js\";\nimport { runCacheCheck } from \"./commands/cache-check.js\";\nimport { runCompare } from \"./commands/compare.js\";\nimport { runList } from \"./commands/list.js\";\nimport { runConfigValidate } from \"./commands/config-validate.js\";\nimport { getCacheFilePath } from \"./utils/paths.js\";\nimport { VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"inspecto\")\n .description(\"Claude Code session quality analyzer — grade sessions, detect regressions, catch cache bugs\")\n .version(VERSION);\n\nprogram\n .command(\"audit\", { isDefault: true })\n .description(\"Grade the most recent Claude Code session\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--format <format>\", \"Output format: json, csv\")\n .option(\"--verbose\", \"Show per-message breakdown\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--project <name>\", \"Filter to a specific project\")\n .option(\"--no-fail\", \"Always exit 0, even for D/F grades\")\n .action(async (options) => {\n try {\n await runAudit(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"trend\")\n .description(\"Analyze quality trends and detect regressions over time\")\n .option(\"--since <duration>\", \"Time range: 7d, 14d, 30d\", \"7d\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--format <format>\", \"Output format: json, csv\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--project <name>\", \"Filter to a specific project\")\n .option(\"--no-fail\", \"Always exit 0, even on regressions\")\n .action(async (options) => {\n try {\n await runTrend(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"cache-check\")\n .description(\"Detect prompt cache bugs that inflate token costs\")\n .option(\"--since <duration>\", \"Time range: 7d, 14d, 30d\", \"7d\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--no-fail\", \"Always exit 0, even when anomalies are detected\")\n .action(async (options) => {\n try {\n await runCacheCheck(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"compare\")\n .description(\"Compare quality metrics across projects\")\n .requiredOption(\"--projects <names>\", \"Comma-separated project names\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .option(\"--since <duration>\", \"Time range: 7d, 14d, 30d\")\n .option(\"--no-fail\", \"No-op (compare always exits 0)\")\n .action(async (options) => {\n try {\n await runCompare(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"list\")\n .description(\"List discovered projects and sessions\")\n .option(\"--sessions\", \"Show 20 most recent sessions instead of projects\")\n .option(\"--project <name>\", \"Filter to sessions for a specific project\")\n .option(\"--data-dir <path>\", \"Custom Claude data directory\")\n .action(async (options) => {\n try {\n await runList(options);\n } catch (error) {\n handleError(error);\n }\n });\n\nconst config = program\n .command(\"config\")\n .description(\"Manage inspecto configuration\");\n\nconfig\n .command(\"validate\")\n .description(\"Show effective configuration merged from .inspecto.json and defaults\")\n .action(async () => {\n try {\n await runConfigValidate();\n } catch (error) {\n handleError(error);\n }\n });\n\nconst cache = program\n .command(\"cache\")\n .description(\"Manage the inspecto grade cache\");\n\ncache\n .command(\"clear\")\n .description(\"Delete the grade cache file (~/.claude/inspecto-cache.db)\")\n .action(async () => {\n try {\n const cachePath = getCacheFilePath();\n try {\n await unlink(cachePath);\n console.log(`Cache cleared: ${cachePath}`);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n console.log(\"No cache file found.\");\n } else {\n throw err;\n }\n }\n } catch (error) {\n handleError(error);\n }\n });\n\nfunction handleError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\nError: ${message}\\n`);\n process.exit(1);\n}\n\nprogram.parse();\n","/**\n * Default command — grade the most recent session.\n */\n\nimport chalk from \"chalk\";\nimport { getMostRecentSession } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { gradeSession } from \"../metrics/grader.js\";\nimport { renderAuditReport } from \"../reporter/terminal.js\";\nimport { formatAuditJson } from \"../reporter/json-reporter.js\";\nimport { exportAuditCsv } from \"../reporter/csv-reporter.js\";\nimport { loadConfig } from \"../config/loader.js\";\n\nconst KNOWN_FORMAT_VERSION = \"2.1.167\";\n\nexport interface AuditOptions {\n json?: boolean;\n verbose?: boolean;\n dataDir?: string;\n project?: string;\n /** false when --no-fail is passed; defaults to true via Commander */\n fail?: boolean;\n format?: string;\n}\n\nexport async function runAudit(options: AuditOptions): Promise<void> {\n const config = loadConfig();\n\n // CLI flags take precedence over config file, config file takes precedence over defaults\n const dataDir = options.dataDir ?? config.dataDir;\n const project = options.project ?? config.defaultProject;\n\n const sessionFile = await getMostRecentSession({ dataDir, project });\n\n const records = readJsonl(sessionFile.path);\n const session = await buildSession(\n records,\n sessionFile.sessionId,\n sessionFile.projectSlug,\n sessionFile.subagentPaths,\n );\n\n const grade = gradeSession(session, config);\n\n if (options.json || options.format === \"json\") {\n console.log(formatAuditJson(session, grade));\n } else if (options.format === \"csv\") {\n console.log(exportAuditCsv(grade));\n } else {\n if (session.formatVersion && session.formatVersion !== KNOWN_FORMAT_VERSION) {\n console.log(\n chalk.yellow(\n `⚠ JSONL format version ${session.formatVersion} detected (expected ${KNOWN_FORMAT_VERSION}). Metrics may be inaccurate.`,\n ),\n );\n }\n\n if (session.unknownRecordTypes.size > 0) {\n const types = [...session.unknownRecordTypes].sort().join(\", \");\n process.stdout.write(chalk.dim(`Note: skipped unknown record types: ${types}\\n`));\n }\n\n console.log(renderAuditReport(session, grade));\n }\n\n if (options.fail !== false && grade.score < 67) {\n process.exitCode = 1;\n }\n}\n","/**\n * Discovers Claude Code session files under ~/.claude/projects/.\n *\n * Session files are at: ~/.claude/projects/{project-slug}/{sessionId}.jsonl\n * Subagent files are at: ~/.claude/projects/{project-slug}/{sessionId}/subagents/agent-*.jsonl\n */\n\nimport { readdir, stat } from \"node:fs/promises\";\nimport { join, basename, extname } from \"node:path\";\nimport { getClaudeDir } from \"../utils/paths.js\";\nimport type { SessionFile } from \"./types.js\";\n\n/**\n * Scan ~/.claude/projects/ for all main session JSONL files.\n * Returns files sorted by modification time (most recent first).\n */\nexport async function scanSessions(options?: {\n dataDir?: string;\n project?: string;\n since?: Date;\n}): Promise<SessionFile[]> {\n const claudeDir = options?.dataDir ?? getClaudeDir();\n const projectsDir = join(claudeDir, \"projects\");\n\n let projectDirs: string[];\n try {\n projectDirs = await readdir(projectsDir);\n } catch {\n throw new Error(\n \"Claude Code data directory not found. \" +\n \"Make sure Claude Code is installed and has been used at least once.\\n\" +\n `Expected: ${projectsDir}`,\n );\n }\n\n // Filter to specific project if requested\n if (options?.project) {\n projectDirs = projectDirs.filter((dir) =>\n dir.toLowerCase().includes(options.project!.toLowerCase()),\n );\n }\n\n const projectResults = await Promise.all(\n projectDirs\n .filter((dir) => !dir.startsWith(\".\"))\n .map(async (projectDir) => {\n const fullProjectDir = join(projectsDir, projectDir);\n let entries: string[];\n try {\n entries = await readdir(fullProjectDir);\n } catch {\n return [] as SessionFile[];\n }\n\n const fileResults = await Promise.all(\n entries\n .filter((entry) => extname(entry) === \".jsonl\")\n .map(async (entry) => {\n const filePath = join(fullProjectDir, entry);\n const sessionId = basename(entry, \".jsonl\");\n try {\n const fileStat = await stat(filePath);\n if (options?.since && fileStat.mtime < options.since) return null;\n\n let subagentPaths: string[] | undefined;\n try {\n const subagentDir = join(fullProjectDir, sessionId, \"subagents\");\n const agentFiles = await readdir(subagentDir);\n const paths = agentFiles\n .filter((f) => f.startsWith(\"agent-\") && f.endsWith(\".jsonl\"))\n .map((f) => join(subagentDir, f));\n if (paths.length > 0) subagentPaths = paths;\n } catch {\n // No subagents directory — normal for older sessions\n }\n\n return {\n path: filePath,\n sessionId,\n projectSlug: projectDir,\n mtime: fileStat.mtime,\n birthtime: fileStat.birthtime,\n subagentPaths,\n } as SessionFile;\n } catch {\n return null;\n }\n }),\n );\n return fileResults.filter((f): f is SessionFile => f !== null);\n }),\n );\n\n const sessions: SessionFile[] = projectResults.flat();\n\n // Sort most recent first\n sessions.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n return sessions;\n}\n\n/**\n * Get the most recent session file, optionally filtered by project.\n */\nexport async function getMostRecentSession(options?: {\n dataDir?: string;\n project?: string;\n}): Promise<SessionFile> {\n const sessions = await scanSessions(options);\n if (sessions.length === 0) {\n throw new Error(\n \"No Claude Code sessions found. \" +\n \"Use Claude Code in a project first to generate session data.\",\n );\n }\n return sessions[0];\n}\n","/**\n * Cross-platform path resolution for Claude Code data directories.\n */\n\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/**\n * Returns the Claude Code data directory.\n * macOS/Linux: ~/.claude\n * Windows: %USERPROFILE%\\.claude\n */\nexport function getClaudeDir(): string {\n return join(homedir(), \".claude\");\n}\n\nexport function getCacheFilePath(): string {\n return join(getClaudeDir(), \"inspecto-cache.db\");\n}\n","/**\n * Streaming JSONL reader using Node's readline + createReadStream.\n * Processes files line-by-line to handle 100MB+ session files without\n * loading them into memory.\n */\n\nimport { createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport type { RawRecord } from \"./types.js\";\n\n/**\n * Stream-reads a JSONL file, yielding one parsed record per line.\n * Malformed lines are silently skipped (common near session end during crashes).\n */\nexport async function* readJsonl(filePath: string): AsyncGenerator<RawRecord> {\n const stream = createReadStream(filePath, { encoding: \"utf-8\" });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n\n for await (const line of rl) {\n const trimmed = line.trim();\n if (trimmed.length === 0) continue;\n\n try {\n const record = JSON.parse(trimmed) as RawRecord;\n if (record && typeof record === \"object\" && \"type\" in record) {\n yield record;\n }\n } catch {\n // Skip malformed lines — common at session boundaries\n }\n }\n}\n","/**\n * Builds a Session from raw JSONL records.\n *\n * Handles the core complexity of Claude Code's streaming format:\n * - Assistant turns are split across multiple JSONL records sharing the same\n * `message.id`. Content blocks from each chunk are merged into one turn.\n * - Only the final chunk (stop_reason != null) has real output_tokens.\n * - Synthetic records (model: \"<synthetic>\") and errored turns are excluded.\n */\n\nimport { basename } from \"node:path\";\nimport { readJsonl } from \"./jsonl-reader.js\";\nimport { SKIP_TYPES } from \"./types.js\";\nimport type {\n AssistantRecord,\n ContentBlock,\n MergedTurn,\n RawRecord,\n Session,\n UsageData,\n UserRecord,\n} from \"./types.js\";\n\ninterface AssistantAccumulator {\n content: ContentBlock[];\n usage: UsageData | null;\n complete: boolean;\n timestamp: string;\n model: string;\n}\n\n/**\n * Build a processed Session from an async stream of raw records.\n * @param records - AsyncIterable of raw JSONL records (from readJsonl)\n * @param sessionId - The session ID (from filename)\n * @param projectSlug - The project slug (from parent directory name)\n * @param subagentPaths - Optional paths to subagent JSONL files to merge in\n */\nexport async function buildSession(\n records: AsyncIterable<RawRecord>,\n sessionId: string,\n projectSlug: string,\n subagentPaths?: string[],\n): Promise<Session> {\n const turns: MergedTurn[] = [];\n const unknownRecordTypes = new Set<string>();\n\n let cwd = \"\";\n let gitBranch: string | null = null;\n let model = \"\";\n let firstTimestamp = \"\";\n let lastTimestamp = \"\";\n let formatVersion = \"\";\n\n async function processRecords(\n stream: AsyncIterable<RawRecord>,\n agentId: string | undefined,\n ) {\n const assistantChunks = new Map<string, AssistantAccumulator>();\n\n for await (const record of stream) {\n if (!formatVersion && \"version\" in record && typeof record.version === \"string\") {\n formatVersion = record.version;\n }\n\n if (SKIP_TYPES.has(record.type)) continue;\n\n if (record.type === \"user\") {\n const userRecord = record as UserRecord;\n handleUserRecord(userRecord, agentId);\n captureMetadata(userRecord);\n } else if (record.type === \"assistant\") {\n const assistantRecord = record as AssistantRecord;\n if (assistantRecord.message.model === \"<synthetic>\") continue;\n if (assistantRecord.error) continue;\n handleAssistantChunk(assistantRecord, assistantChunks);\n captureMetadata(assistantRecord);\n } else {\n unknownRecordTypes.add(record.type);\n }\n }\n\n // Flush all accumulated assistant chunks into turns\n for (const [, acc] of assistantChunks) {\n turns.push({\n role: \"assistant\",\n content: acc.content,\n usage: acc.usage,\n complete: acc.complete,\n timestamp: acc.timestamp,\n isHumanTurn: false,\n model: acc.model,\n agentId,\n });\n }\n }\n\n await processRecords(records, undefined);\n\n for (const agentPath of subagentPaths ?? []) {\n const agentId = basename(agentPath, \".jsonl\");\n await processRecords(readJsonl(agentPath), agentId);\n }\n\n // Sort all turns (main + subagents) by timestamp\n turns.sort((a, b) => a.timestamp.localeCompare(b.timestamp));\n\n const subagentIds = new Set(\n turns.filter((t) => t.agentId !== undefined).map((t) => t.agentId!),\n );\n const subagentTurnCount = turns.filter((t) => t.agentId !== undefined).length;\n\n return {\n id: sessionId,\n projectSlug,\n model,\n turns,\n startTime: firstTimestamp,\n endTime: lastTimestamp,\n cwd,\n gitBranch,\n durationMs:\n firstTimestamp && lastTimestamp\n ? new Date(lastTimestamp).getTime() - new Date(firstTimestamp).getTime()\n : 0,\n subagentCount: subagentIds.size,\n subagentTurnCount,\n formatVersion,\n unknownRecordTypes,\n };\n\n // -- Inner helpers --------------------------------------------------------\n\n function captureMetadata(record: UserRecord | AssistantRecord) {\n if (!firstTimestamp && record.timestamp) {\n firstTimestamp = record.timestamp;\n }\n if (record.timestamp) {\n lastTimestamp = record.timestamp;\n }\n if (!cwd && record.cwd) {\n cwd = record.cwd;\n }\n if (gitBranch === null && record.gitBranch) {\n gitBranch = record.gitBranch;\n }\n if (!model && record.type === \"assistant\") {\n const ar = record as AssistantRecord;\n if (ar.message.model && ar.message.model !== \"<synthetic>\") {\n model = ar.message.model;\n }\n }\n }\n\n function handleUserRecord(record: UserRecord, agentId: string | undefined) {\n const content = record.message.content;\n const isHumanTurn = typeof content === \"string\" && !record.isMeta;\n turns.push({\n role: \"user\",\n content: normalizeContent(content),\n usage: null,\n complete: true,\n timestamp: record.timestamp,\n isHumanTurn,\n agentId,\n });\n }\n\n function handleAssistantChunk(\n record: AssistantRecord,\n chunks: Map<string, AssistantAccumulator>,\n ) {\n const messageId = record.message.id;\n let acc = chunks.get(messageId);\n\n if (!acc) {\n acc = {\n content: [],\n usage: null,\n complete: false,\n timestamp: record.timestamp,\n model: record.message.model,\n };\n chunks.set(messageId, acc);\n }\n\n // Append content blocks from this streaming chunk\n for (const block of record.message.content) {\n acc.content.push(block);\n }\n\n // Final chunk has the real usage data\n if (record.message.stop_reason !== null) {\n acc.complete = true;\n acc.usage = record.message.usage;\n }\n }\n}\n\nfunction normalizeContent(content: string | ContentBlock[]): ContentBlock[] {\n if (typeof content === \"string\") {\n return [{ type: \"text\", text: content }];\n }\n return content;\n}\n","/**\n * Type definitions for Claude Code JSONL session data.\n *\n * Claude Code writes one JSONL file per session. Each line is a JSON record\n * with a discriminated `type` field. Assistant responses are streamed as\n * multiple chunks sharing the same `message.id` — only the final chunk\n * (with `stop_reason != null`) carries real token usage data.\n */\n\n// ---------------------------------------------------------------------------\n// Content blocks\n// ---------------------------------------------------------------------------\n\nexport interface TextBlock {\n type: \"text\";\n text: string;\n}\n\nexport interface ThinkingBlock {\n type: \"thinking\";\n thinking: string;\n signature?: string;\n}\n\nexport interface ToolUseBlock {\n type: \"tool_use\";\n id: string;\n name: string;\n input: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: \"tool_result\";\n tool_use_id: string;\n content: string | ContentBlock[];\n is_error?: boolean;\n}\n\nexport type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock;\n\n// ---------------------------------------------------------------------------\n// Usage data (on assistant messages)\n// ---------------------------------------------------------------------------\n\nexport interface UsageData {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number;\n cache_read_input_tokens: number;\n cache_creation?: {\n ephemeral_1h_input_tokens: number;\n ephemeral_5m_input_tokens: number;\n };\n server_tool_use?: {\n web_search_requests: number;\n web_fetch_requests: number;\n };\n service_tier?: string;\n speed?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Raw JSONL record types (discriminated union on `type`)\n// ---------------------------------------------------------------------------\n\nexport interface BaseRecord {\n uuid: string;\n parentUuid: string | null;\n sessionId: string;\n timestamp: string;\n version: string;\n cwd: string;\n type: string;\n isSidechain?: boolean;\n entrypoint?: string;\n gitBranch?: string | null;\n slug?: string;\n userType?: string;\n agentId?: string;\n}\n\nexport interface UserRecord extends BaseRecord {\n type: \"user\";\n message: {\n role: \"user\";\n content: string | ContentBlock[];\n };\n isMeta?: boolean;\n permissionMode?: string;\n}\n\nexport interface AssistantRecord extends BaseRecord {\n type: \"assistant\";\n requestId?: string;\n message: {\n id: string;\n type: \"message\";\n role: \"assistant\";\n model: string;\n content: ContentBlock[];\n stop_reason: \"tool_use\" | \"end_turn\" | \"stop_sequence\" | null;\n usage: UsageData;\n };\n error?: string;\n isApiErrorMessage?: boolean;\n}\n\nexport interface QueueOperationRecord {\n type: \"queue-operation\";\n operation: string;\n timestamp: string;\n sessionId: string;\n content?: string;\n}\n\nexport interface AttachmentRecord extends BaseRecord {\n type: \"attachment\";\n attachment: Record<string, unknown>;\n}\n\nexport interface SystemRecord extends BaseRecord {\n type: \"system\";\n subtype?: string;\n [key: string]: unknown;\n}\n\nexport interface LastPromptRecord {\n type: \"last-prompt\";\n lastPrompt: string;\n sessionId: string;\n}\n\nexport type RawRecord =\n | UserRecord\n | AssistantRecord\n | QueueOperationRecord\n | AttachmentRecord\n | SystemRecord\n | LastPromptRecord;\n\n/** Record types to skip during session building. */\nexport const SKIP_TYPES = new Set([\n \"queue-operation\",\n \"attachment\",\n \"system\",\n \"last-prompt\",\n]);\n\n// ---------------------------------------------------------------------------\n// Processed session types (output of session builder)\n// ---------------------------------------------------------------------------\n\nexport interface MergedTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n /** Real usage from the final streaming chunk. Null for user turns. */\n usage: UsageData | null;\n /** Whether the assistant turn completed (stop_reason was non-null). */\n complete: boolean;\n timestamp: string;\n /** True for human-authored user messages (not tool results or hook injections). */\n isHumanTurn: boolean;\n /** The model that generated this turn (assistant only). */\n model?: string;\n /** Subagent ID (e.g. \"agent-abc123\"). Undefined = main agent. */\n agentId?: string;\n}\n\nexport interface Session {\n id: string;\n projectSlug: string;\n model: string;\n turns: MergedTurn[];\n startTime: string;\n endTime: string;\n cwd: string;\n gitBranch: string | null;\n durationMs: number;\n subagentCount: number;\n subagentTurnCount: number;\n formatVersion: string;\n unknownRecordTypes: Set<string>;\n}\n\n// ---------------------------------------------------------------------------\n// Metric result types\n// ---------------------------------------------------------------------------\n\nexport type MetricStatus = \"healthy\" | \"warning\" | \"critical\";\n\nexport interface MetricResult {\n name: string;\n value: number | null;\n status: MetricStatus;\n label: string;\n detail?: string;\n}\n\nexport interface GradeResult {\n letter: string;\n score: number;\n metrics: MetricResult[];\n}\n\n// ---------------------------------------------------------------------------\n// Session discovery\n// ---------------------------------------------------------------------------\n\nexport interface SessionFile {\n path: string;\n sessionId: string;\n projectSlug: string;\n mtime: Date;\n birthtime?: Date;\n subagentPaths?: string[];\n}\n","/**\n * M1: Reads-before-edit ratio.\n *\n * Counts how many Read tool_use events occur before each Write or Edit event.\n * High values mean Claude is reading context before modifying files.\n * The AMD data showed this dropped from 6.6 to 2.0 after March 8.\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nconst EDIT_TOOLS = new Set([\"Write\", \"Edit\", \"NotebookEdit\"]);\nconst READ_TOOL = \"Read\";\n\nexport function computeReadsPerEdit(session: Session, thresholds?: ThresholdConfig): MetricResult {\n let readsSinceLastEdit = 0;\n const ratios: number[] = [];\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n\n if (toolBlock.name === READ_TOOL) {\n readsSinceLastEdit++;\n } else if (EDIT_TOOLS.has(toolBlock.name)) {\n ratios.push(readsSinceLastEdit);\n readsSinceLastEdit = 0;\n }\n }\n }\n\n if (ratios.length === 0) {\n return {\n name: \"reads-per-edit\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No file modifications in this session\",\n };\n }\n\n const average = ratios.reduce((a, b) => a + b, 0) / ratios.length;\n const { healthy, warning } = thresholds ?? { healthy: 4.0, warning: 2.0 };\n\n return {\n name: \"reads-per-edit\",\n value: round(average),\n status: average >= healthy ? \"healthy\" : average >= warning ? \"warning\" : \"critical\",\n label: round(average).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M2: Full-file rewrite ratio.\n *\n * Ratio of Write calls (full file replacement) to total file modifications\n * (Write + Edit). Rising ratio means Claude is rewriting instead of\n * making surgical edits.\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nexport function computeRewriteRatio(session: Session, thresholds?: ThresholdConfig): MetricResult {\n let writes = 0;\n let edits = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n\n if (toolBlock.name === \"Write\") writes++;\n else if (toolBlock.name === \"Edit\" || toolBlock.name === \"NotebookEdit\") edits++;\n }\n }\n\n const total = writes + edits;\n if (total === 0) {\n return {\n name: \"rewrite-ratio\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No file modifications in this session\",\n };\n }\n\n const ratio = writes / total;\n const { healthy, warning } = thresholds ?? { healthy: 0.25, warning: 0.5 };\n\n return {\n name: \"rewrite-ratio\",\n value: round(ratio),\n status: ratio <= healthy ? \"healthy\" : ratio <= warning ? \"warning\" : \"critical\",\n label: round(ratio).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M3: Cache hit rate.\n *\n * Ratio of cache_read_input_tokens to total input tokens\n * (cache_read + cache_creation). Detects the prompt cache bug\n * that caused 10-20x cost inflation.\n *\n * Note: raw `input_tokens` is always a streaming placeholder (1 or 3).\n * Real input cost = cache_read + cache_creation.\n */\n\nimport type { MetricResult, Session } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nexport function computeCacheHitRate(session: Session, thresholds?: ThresholdConfig): MetricResult {\n let totalCacheRead = 0;\n let totalCacheCreation = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\" || !turn.usage || !turn.complete) continue;\n\n totalCacheRead += turn.usage.cache_read_input_tokens;\n totalCacheCreation += turn.usage.cache_creation_input_tokens;\n }\n\n const totalInput = totalCacheRead + totalCacheCreation;\n if (totalInput === 0) {\n return {\n name: \"cache-hit-rate\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No token usage data available\",\n };\n }\n\n const rate = totalCacheRead / totalInput;\n const { healthy, warning } = thresholds ?? { healthy: 0.5, warning: 0.2 };\n\n return {\n name: \"cache-hit-rate\",\n value: round(rate),\n status: rate >= healthy ? \"healthy\" : rate >= warning ? \"warning\" : \"critical\",\n label: round(rate).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M4: Task completion rate.\n *\n * Detects sessions where Claude says it will do something but doesn't\n * follow through. Looks for intent phrases in assistant text that\n * aren't followed by a tool_use in the next assistant turn.\n */\n\nimport type { MetricResult, Session, MergedTurn, TextBlock, ToolUseBlock } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nconst INTENT_PATTERNS = [\n /\\bI'll now\\b/i,\n /\\bLet me\\b/i,\n /\\bI'll update\\b/i,\n /\\bNext,? I'll\\b/i,\n /\\bI'll (?:also |then )?(?:fix|add|create|implement|refactor|modify|change|write|edit|update)\\b/i,\n /\\bI'm going to\\b/i,\n];\n\nexport function computeTaskCompletion(session: Session, thresholds?: ThresholdConfig): MetricResult {\n const assistantTurns = session.turns.filter(\n (t) => t.role === \"assistant\" && t.complete,\n );\n\n let totalIntents = 0;\n let unfulfilledIntents = 0;\n\n for (const turn of assistantTurns) {\n const hasIntent = hasIntentPhrase(turn);\n if (!hasIntent) continue;\n\n totalIntents++;\n\n // An intent is fulfilled if the same merged turn also contains a tool_use.\n // Since streaming chunks are merged, a real action within this turn means\n // Claude followed through. An intent without a tool_use in the same turn\n // is a dangling promise.\n const hasToolUse = turn.content.some((b) => b.type === \"tool_use\");\n if (!hasToolUse) {\n unfulfilledIntents++;\n }\n }\n\n if (totalIntents === 0) {\n return {\n name: \"task-completion\",\n value: 1,\n status: \"healthy\",\n label: \"1.00\",\n detail: \"No intent phrases detected\",\n };\n }\n\n const rate = 1 - unfulfilledIntents / totalIntents;\n const { healthy, warning } = thresholds ?? { healthy: 0.9, warning: 0.7 };\n\n return {\n name: \"task-completion\",\n value: round(rate),\n status: rate >= healthy ? \"healthy\" : rate >= warning ? \"warning\" : \"critical\",\n label: round(rate).toString(),\n };\n}\n\nfunction hasIntentPhrase(turn: MergedTurn): boolean {\n for (const block of turn.content) {\n if (block.type === \"text\") {\n const textBlock = block as TextBlock;\n if (INTENT_PATTERNS.some((p) => p.test(textBlock.text))) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * Levenshtein distance and normalized similarity.\n * Pure implementation — no external dependencies.\n */\n\n/**\n * Compute the Levenshtein edit distance between two strings.\n * Uses a single-row DP approach for O(min(m,n)) space.\n */\nexport function levenshteinDistance(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure a is the shorter string for space optimization\n if (a.length > b.length) [a, b] = [b, a];\n\n const aLen = a.length;\n const bLen = b.length;\n const row = new Array<number>(aLen + 1);\n\n for (let i = 0; i <= aLen; i++) row[i] = i;\n\n for (let j = 1; j <= bLen; j++) {\n let prev = row[0];\n row[0] = j;\n\n for (let i = 1; i <= aLen; i++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n const temp = row[i];\n row[i] = Math.min(\n row[i] + 1, // deletion\n row[i - 1] + 1, // insertion\n prev + cost, // substitution\n );\n prev = temp;\n }\n }\n\n return row[aLen];\n}\n\n/**\n * Compute normalized similarity between two strings (0 = different, 1 = identical).\n * Only compares the first `maxLen` characters for performance.\n */\nexport function normalizedSimilarity(\n a: string,\n b: string,\n maxLen = 200,\n): number {\n const aTrunc = a.slice(0, maxLen);\n const bTrunc = b.slice(0, maxLen);\n const maxLength = Math.max(aTrunc.length, bTrunc.length);\n\n if (maxLength === 0) return 1;\n\n const distance = levenshteinDistance(aTrunc, bTrunc);\n return 1 - distance / maxLength;\n}\n","/**\n * M5: Retry density.\n *\n * Measures how often the user sends messages very similar to their\n * previous message — a proxy for \"Claude got it wrong and I'm asking again.\"\n */\n\nimport type { MetricResult, Session, TextBlock } from \"../parser/types.js\";\nimport { normalizedSimilarity } from \"../utils/levenshtein.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nexport function computeRetryDensity(session: Session, thresholds?: ThresholdConfig): MetricResult {\n // Extract text from human-authored user turns only\n const humanTexts: string[] = [];\n for (const turn of session.turns) {\n if (!turn.isHumanTurn) continue;\n const text = turn.content\n .filter((b): b is TextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\" \");\n if (text.length > 0) humanTexts.push(text);\n }\n\n if (humanTexts.length < 2) {\n return {\n name: \"retry-density\",\n value: 0,\n status: \"healthy\",\n label: \"0.00\",\n detail: \"Not enough user messages to detect retries\",\n };\n }\n\n let retries = 0;\n const pairs = humanTexts.length - 1;\n\n for (let i = 0; i < pairs; i++) {\n const similarity = normalizedSimilarity(humanTexts[i], humanTexts[i + 1]);\n if (similarity > 0.6) {\n retries++;\n }\n }\n\n const density = retries / pairs;\n const { healthy, warning } = thresholds ?? { healthy: 0.1, warning: 0.25 };\n\n return {\n name: \"retry-density\",\n value: round(density),\n status: density <= healthy ? \"healthy\" : density <= warning ? \"warning\" : \"critical\",\n label: round(density).toString(),\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M6: Tool diversity score.\n *\n * Shannon entropy over the distribution of tool_use events by tool name.\n * Normalized to 0-1. Low diversity means Claude is over-relying on\n * one tool (often Write).\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nexport function computeToolDiversity(session: Session, thresholds?: ThresholdConfig): MetricResult {\n const toolCounts = new Map<string, number>();\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n toolCounts.set(toolBlock.name, (toolCounts.get(toolBlock.name) ?? 0) + 1);\n }\n }\n\n const uniqueTools = toolCounts.size;\n if (uniqueTools <= 1) {\n return {\n name: \"tool-diversity\",\n value: uniqueTools === 0 ? null : 0,\n status: uniqueTools === 0 ? \"healthy\" : \"critical\",\n label: uniqueTools === 0 ? \"N/A\" : \"0.00\",\n detail: uniqueTools === 0\n ? \"No tool usage in this session\"\n : `Only one tool used: ${[...toolCounts.keys()][0]}`,\n };\n }\n\n const totalCalls = [...toolCounts.values()].reduce((a, b) => a + b, 0);\n const maxEntropy = Math.log2(uniqueTools);\n\n let entropy = 0;\n for (const count of toolCounts.values()) {\n const p = count / totalCalls;\n entropy -= p * Math.log2(p);\n }\n\n const normalized = entropy / maxEntropy;\n\n // Build detail showing top tools\n const sorted = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]);\n const topTool = sorted[0];\n const topPercent = Math.round((topTool[1] / totalCalls) * 100);\n const detail = `Most used: ${topTool[0]} (${topPercent}%)`;\n\n const { healthy, warning } = thresholds ?? { healthy: 0.6, warning: 0.4 };\n\n return {\n name: \"tool-diversity\",\n value: round(normalized),\n status: normalized >= healthy ? \"healthy\" : normalized >= warning ? \"warning\" : \"critical\",\n label: round(normalized).toString(),\n detail,\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/**\n * M7: Tokens per useful edit.\n *\n * Total output tokens consumed divided by number of file modification\n * operations (Write + Edit). Rising ratio means Claude is burning more\n * tokens per productive action.\n */\n\nimport type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\nconst EDIT_TOOLS = new Set([\"Write\", \"Edit\", \"NotebookEdit\"]);\n\nexport function computeTokensPerEdit(session: Session, thresholds?: ThresholdConfig): MetricResult {\n let totalOutputTokens = 0;\n let editCount = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\") continue;\n\n // Count tokens from completed turns only\n if (turn.complete && turn.usage) {\n totalOutputTokens += turn.usage.output_tokens;\n }\n\n // Count edit operations\n for (const block of turn.content) {\n if (block.type !== \"tool_use\") continue;\n const toolBlock = block as ToolUseBlock;\n if (EDIT_TOOLS.has(toolBlock.name)) editCount++;\n }\n }\n\n if (editCount === 0) {\n return {\n name: \"tokens-per-edit\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No file modifications in this session\",\n };\n }\n\n const ratio = totalOutputTokens / editCount;\n const { healthy, warning } = thresholds ?? { healthy: 5000, warning: 15000 };\n\n return {\n name: \"tokens-per-edit\",\n value: Math.round(ratio),\n status: ratio <= healthy ? \"healthy\" : ratio <= warning ? \"warning\" : \"critical\",\n label: Math.round(ratio).toLocaleString(\"en-US\"),\n };\n}\n","/**\n * M8: Subagent delegation ratio.\n *\n * Measures what fraction of total output tokens came from the main agent vs\n * subagents. A low main-agent ratio means the orchestrator delegated effectively.\n * Healthy = main agent produced < 60% of total output tokens.\n */\n\nimport type { MetricResult, Session } from \"../parser/types.js\";\n\nexport function computeSubagentOverhead(session: Session): MetricResult {\n if (session.subagentCount === 0) {\n return {\n name: \"subagent-overhead\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No subagents in this session\",\n };\n }\n\n let mainTokens = 0;\n let subagentTokens = 0;\n\n for (const turn of session.turns) {\n if (turn.role !== \"assistant\" || !turn.usage) continue;\n const out = turn.usage.output_tokens ?? 0;\n if (turn.agentId === undefined) {\n mainTokens += out;\n } else {\n subagentTokens += out;\n }\n }\n\n const total = mainTokens + subagentTokens;\n if (total === 0) {\n return {\n name: \"subagent-overhead\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No output tokens recorded\",\n };\n }\n\n const ratio = mainTokens / total;\n const status = ratio < 0.6 ? \"healthy\" : ratio < 0.8 ? \"warning\" : \"critical\";\n\n return {\n name: \"subagent-overhead\",\n value: Math.round(ratio * 100) / 100,\n status,\n label: `${Math.round(ratio * 100)}% main`,\n };\n}\n","import type { MetricResult, Session, ToolResultBlock } from \"../parser/types.js\";\n\nexport function computeToolErrorRate(session: Session): MetricResult {\n let totalResults = 0;\n let errorResults = 0;\n\n for (const turn of session.turns) {\n for (const block of turn.content) {\n if (block.type !== \"tool_result\") continue;\n const resultBlock = block as ToolResultBlock;\n totalResults++;\n if (resultBlock.is_error === true) errorResults++;\n }\n }\n\n if (totalResults === 0) {\n return {\n name: \"tool-error-rate\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No tool calls in this session\",\n };\n }\n\n const rate = errorResults / totalResults;\n\n return {\n name: \"tool-error-rate\",\n value: round(rate),\n status: rate <= 0.05 ? \"healthy\" : rate <= 0.15 ? \"warning\" : \"critical\",\n label: `${(rate * 100).toFixed(1)}%`,\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 10000) / 10000;\n}\n","import type { MetricResult, Session, ThinkingBlock } from \"../parser/types.js\";\n\nexport function computeThinkingUtilization(session: Session): MetricResult {\n const assistantTurns = session.turns.filter((t) => t.role === \"assistant\");\n\n const hasAnyToolUse = assistantTurns.some((t) =>\n t.content.some((b) => b.type === \"tool_use\")\n );\n\n if (!hasAnyToolUse) {\n return {\n name: \"thinking-utilization\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No tool use in this session\",\n };\n }\n\n const turnsWithThinking = assistantTurns.filter((t) =>\n t.content.some((b): b is ThinkingBlock => b.type === \"thinking\")\n ).length;\n\n if (turnsWithThinking === 0) {\n return {\n name: \"thinking-utilization\",\n value: 0,\n status: \"critical\",\n label: \"0%\",\n detail: \"No extended thinking blocks detected\",\n };\n }\n\n const ratio = Math.min(turnsWithThinking / assistantTurns.length, 1.0);\n\n return {\n name: \"thinking-utilization\",\n value: round(ratio),\n status: ratio >= 0.3 ? \"healthy\" : ratio >= 0.1 ? \"warning\" : \"critical\",\n label: `${(ratio * 100).toFixed(1)}%`,\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 10000) / 10000;\n}\n","import type { MetricResult, Session, ToolUseBlock } from \"../parser/types.js\";\n\nexport function computeMcpUsage(session: Session): MetricResult {\n const assistantTurnsWithTools = session.turns.filter(\n (t) => t.role === \"assistant\" && t.content.some((b) => b.type === \"tool_use\")\n );\n\n if (assistantTurnsWithTools.length === 0) {\n return {\n name: \"mcp-usage\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No tool use in this session\",\n };\n }\n\n let mcpTurnCount = 0;\n\n for (const turn of assistantTurnsWithTools) {\n const hasMcpTool = turn.content.some(\n (b): b is ToolUseBlock =>\n b.type === \"tool_use\" && b.name.startsWith(\"mcp__\")\n );\n\n const hasServerToolUse =\n (turn.usage?.server_tool_use?.web_search_requests ?? 0) > 0 ||\n (turn.usage?.server_tool_use?.web_fetch_requests ?? 0) > 0;\n\n if (hasMcpTool || hasServerToolUse) mcpTurnCount++;\n }\n\n const ratio = mcpTurnCount / assistantTurnsWithTools.length;\n\n return {\n name: \"mcp-usage\",\n value: round(ratio),\n status: \"healthy\",\n label: `${mcpTurnCount} turn${mcpTurnCount !== 1 ? \"s\" : \"\"}`,\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 10000) / 10000;\n}\n","import type { MetricResult, Session } from \"../parser/types.js\";\nimport type { ThresholdConfig } from \"../config/types.js\";\n\n// Pricing per 1M tokens (Claude Sonnet 4.5/4.6)\nconst PRICE_PER_M = {\n output: 15.0,\n cacheCreation: 3.75,\n cacheRead: 0.30,\n input: 3.0,\n};\n\nexport function computeSessionCost(session: Session, thresholds?: ThresholdConfig): MetricResult {\n let outputTokens = 0;\n let cacheCreationTokens = 0;\n let cacheReadTokens = 0;\n\n for (const turn of session.turns) {\n if (!turn.usage) continue;\n outputTokens += turn.usage.output_tokens ?? 0;\n cacheCreationTokens += turn.usage.cache_creation_input_tokens ?? 0;\n cacheReadTokens += turn.usage.cache_read_input_tokens ?? 0;\n }\n\n const totalTokens = outputTokens + cacheCreationTokens + cacheReadTokens;\n if (totalTokens === 0) {\n return {\n name: \"session-cost\",\n value: null,\n status: \"healthy\",\n label: \"N/A\",\n detail: \"No token usage data in this session\",\n };\n }\n\n const cost =\n (outputTokens * PRICE_PER_M.output +\n cacheCreationTokens * PRICE_PER_M.cacheCreation +\n cacheReadTokens * PRICE_PER_M.cacheRead) /\n 1_000_000;\n\n const { healthy, warning } = thresholds ?? { healthy: 2.0, warning: 5.0 };\n\n return {\n name: \"session-cost\",\n value: round(cost),\n status: cost <= healthy ? \"healthy\" : cost <= warning ? \"warning\" : \"critical\",\n label: `$${cost.toFixed(2)}`,\n };\n}\n\nfunction round(n: number): number {\n return Math.round(n * 10000) / 10000;\n}\n","/**\n * Composite grading from all 8 quality metrics.\n *\n * Each metric is scored 0-100 based on its thresholds, then weighted\n * into a composite score mapped to a letter grade A+ through F.\n */\n\nimport type { GradeResult, MetricResult, Session } from \"../parser/types.js\";\nimport type { InspectoConfig } from \"../config/types.js\";\nimport { computeReadsPerEdit } from \"./reads-per-edit.js\";\nimport { computeRewriteRatio } from \"./rewrite-ratio.js\";\nimport { computeCacheHitRate } from \"./cache-hit-rate.js\";\nimport { computeTaskCompletion } from \"./task-completion.js\";\nimport { computeRetryDensity } from \"./retry-density.js\";\nimport { computeToolDiversity } from \"./tool-diversity.js\";\nimport { computeTokensPerEdit } from \"./tokens-per-edit.js\";\nimport { computeSubagentOverhead } from \"./subagent-overhead.js\";\nimport { computeToolErrorRate } from \"./tool-error-rate.js\";\nimport { computeThinkingUtilization } from \"./thinking-utilization.js\";\nimport { computeMcpUsage } from \"./mcp-usage.js\";\nimport { computeSessionCost } from \"./session-cost.js\";\n\ninterface MetricWeight {\n compute: (session: Session) => MetricResult;\n weight: number;\n /** Convert metric value to 0-100 score. Higher is better. */\n score: (value: number) => number;\n}\n\nfunction buildMetricWeights(config?: InspectoConfig): MetricWeight[] {\n const t = config?.thresholds ?? {};\n const w = config?.weights ?? {};\n\n return [\n {\n compute: (session) => computeReadsPerEdit(session, t.readsPerEdit),\n weight: w.readsPerEdit ?? 0.14,\n // 0 reads → 0, 2 reads → 50, 4+ reads → 100\n score: (v) => clamp(v / 4 * 100, 0, 100),\n },\n {\n compute: (session) => computeRewriteRatio(session, t.rewriteRatio),\n weight: w.rewriteRatio ?? 0.11,\n // 0 ratio → 100, 0.25 → 50, 0.5+ → 0 (inverted: lower is better)\n score: (v) => clamp((1 - v / 0.5) * 100, 0, 100),\n },\n {\n compute: (session) => computeCacheHitRate(session, t.cacheHitRate),\n weight: w.cacheHitRate ?? 0.11,\n // 0% → 0, 50% → 100\n score: (v) => clamp(v / 0.5 * 100, 0, 100),\n },\n {\n compute: (session) => computeTaskCompletion(session, t.taskCompletion),\n weight: w.taskCompletion ?? 0.10,\n // 0.7 → 0, 0.9 → 50, 1.0 → 100\n score: (v) => clamp((v - 0.7) / 0.3 * 100, 0, 100),\n },\n {\n compute: (session) => computeRetryDensity(session, t.retryDensity),\n weight: w.retryDensity ?? 0.07,\n // 0% → 100, 10% → 60, 25%+ → 0 (inverted)\n score: (v) => clamp((1 - v / 0.25) * 100, 0, 100),\n },\n {\n compute: (session) => computeToolDiversity(session, t.toolDiversity),\n weight: w.toolDiversity ?? 0.06,\n // 0 → 0, 0.4 → 50, 0.6+ → 100\n score: (v) => clamp(v / 0.6 * 100, 0, 100),\n },\n {\n compute: (session) => computeTokensPerEdit(session, t.tokensPerEdit),\n weight: w.tokensPerEdit ?? 0.11,\n // 5000 → 100, 10000 → 50, 15000+ → 0 (inverted)\n score: (v) => clamp((1 - (v - 5000) / 10000) * 100, 0, 100),\n },\n {\n compute: computeSubagentOverhead,\n weight: 0.05,\n // main ratio 0 → 100, 0.6 → 100 (threshold), 0.8 → 50, 1.0 → 0 (inverted)\n score: (v) => clamp((1 - v) / 0.4 * 100, 0, 100),\n },\n {\n compute: computeToolErrorRate,\n weight: 0.08,\n // 0% → 100, 5% → 83, 15% → 50, 30%+ → 0 (inverted: lower is better)\n score: (v) => clamp((1 - v / 0.30) * 100, 0, 100),\n },\n {\n compute: computeThinkingUtilization,\n weight: 0.07,\n // 0% → 0, 10% → 33, 30%+ → 100\n score: (v) => clamp(v / 0.30 * 100, 0, 100),\n },\n {\n compute: computeMcpUsage,\n weight: 0.05,\n // Informational: always contributes max score\n score: (_v) => 100,\n },\n {\n compute: (session) => computeSessionCost(session, t.sessionCost),\n weight: 0.05,\n // $0 → 100, $5 → 50, $10+ → 0 (inverted: lower cost is better)\n score: (v) => clamp((1 - v / 10) * 100, 0, 100),\n },\n ];\n}\n\nconst GRADE_THRESHOLDS: Array<[number, string]> = [\n [97, \"A+\"],\n [93, \"A\"],\n [90, \"A-\"],\n [87, \"B+\"],\n [83, \"B\"],\n [80, \"B-\"],\n [77, \"C+\"],\n [73, \"C\"],\n [70, \"C-\"],\n [67, \"D+\"],\n [63, \"D\"],\n [60, \"D-\"],\n [0, \"F\"],\n];\n\nexport function gradeLetterFromScore(score: number): string {\n return GRADE_THRESHOLDS.find(([threshold]) => score >= threshold)?.[1] ?? \"F\";\n}\n\nexport function gradeSession(session: Session, config?: InspectoConfig): GradeResult {\n const metricWeights = buildMetricWeights(config);\n const metrics: MetricResult[] = [];\n let weightedSum = 0;\n let totalWeight = 0;\n\n for (const mw of metricWeights) {\n const result = mw.compute(session);\n metrics.push(result);\n\n if (result.value !== null) {\n weightedSum += mw.score(result.value) * mw.weight;\n totalWeight += mw.weight;\n }\n }\n\n // Normalize if some metrics returned null (insufficient data)\n const compositeScore = totalWeight > 0 ? weightedSum / totalWeight : 0;\n\n const letter = gradeLetterFromScore(compositeScore);\n\n return {\n letter,\n score: Math.round(compositeScore),\n metrics,\n };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n","/**\n * Terminal output formatting using chalk and cli-table3.\n */\n\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { GradeResult, MetricResult, Session } from \"../parser/types.js\";\nimport type { RegressionResult } from \"../anomaly/regression-detector.js\";\nimport type { CacheCheckResult } from \"../anomaly/cache-anomaly.js\";\nimport { formatDuration, shortSessionId, projectNameFromSlug, formatNumber } from \"../utils/format.js\";\nimport { getAllTips } from \"./tips.js\";\nimport { VERSION } from \"../version.js\";\n\nconst STATUS_ICONS: Record<string, string> = {\n healthy: chalk.green(\"✓\"),\n warning: chalk.yellow(\"⚠\"),\n critical: chalk.red(\"✗\"),\n};\n\nconst STATUS_LABELS: Record<string, string> = {\n healthy: chalk.green(\"healthy\"),\n warning: chalk.yellow(\"warning\"),\n critical: chalk.red(\"critical\"),\n};\n\nconst METRIC_DISPLAY_NAMES: Record<string, string> = {\n \"reads-per-edit\": \"Reads/edit\",\n \"rewrite-ratio\": \"Rewrite ratio\",\n \"cache-hit-rate\": \"Cache hit rate\",\n \"task-completion\": \"Task completion\",\n \"retry-density\": \"Retry density\",\n \"tool-diversity\": \"Tool diversity\",\n \"tokens-per-edit\": \"Tokens/useful-edit\",\n \"subagent-overhead\": \"Subagent delegation\",\n};\n\nexport function renderAuditReport(session: Session, grade: GradeResult): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(chalk.bold(` inspecto v${VERSION}`) + chalk.dim(\" — Claude Code Session Quality Analyzer\"));\n lines.push(\"\");\n\n const agentInfo = session.subagentCount > 0\n ? `${session.subagentCount} subagents | ${session.turns.length} turns`\n : `${session.turns.length} turns`;\n\n const sessionInfo = [\n `Session: ${chalk.cyan(shortSessionId(session.id))}`,\n projectNameFromSlug(session.projectSlug),\n formatDuration(session.durationMs),\n session.model,\n agentInfo,\n ].join(chalk.dim(\" | \"));\n lines.push(` ${sessionInfo}`);\n lines.push(\"\");\n\n const gradeColor = getGradeColor(grade.letter);\n lines.push(` Overall grade: ${gradeColor(chalk.bold(grade.letter))}`);\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Metric\", \"Value\", \"Status\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const metric of grade.metrics) {\n const displayName = METRIC_DISPLAY_NAMES[metric.name] ?? metric.name;\n const icon = STATUS_ICONS[metric.status] ?? \"\";\n table.push([displayName, metric.label, `${icon} ${STATUS_LABELS[metric.status] ?? metric.status}`]);\n }\n\n lines.push(table.toString());\n\n const tips = getAllTips(grade.metrics);\n if (tips.length > 0) {\n lines.push(\"\");\n lines.push(chalk.yellow(\" Tips:\"));\n for (const tip of tips) {\n lines.push(` ${chalk.dim(\"→\")} ${tip}`);\n }\n }\n\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nexport function renderTrendReport(\n results: RegressionResult[],\n sessionCount: number,\n period: string,\n): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(chalk.bold(` Trend report: last ${period}`) + chalk.dim(` (${sessionCount} sessions)`));\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Metric\", \"Recent avg\", \"Full avg\", \"Change\", \"Status\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const result of results) {\n const displayName = METRIC_DISPLAY_NAMES[result.name] ?? result.name;\n const recentStr = result.recentAvg !== null ? result.recentAvg.toFixed(2) : \"N/A\";\n const fullStr = result.fullAvg !== null ? result.fullAvg.toFixed(2) : \"N/A\";\n\n let changeStr = \"N/A\";\n if (result.changePercent !== null) {\n const arrow = result.changePercent > 0 ? \"▲\" : result.changePercent < 0 ? \"▼\" : \"\";\n changeStr = `${arrow} ${Math.abs(Math.round(result.changePercent))}%`;\n }\n\n const statusStr = formatRegressionStatus(result.status);\n table.push([displayName, recentStr, fullStr, changeStr, statusStr]);\n }\n\n lines.push(table.toString());\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nexport function renderCacheCheckReport(results: CacheCheckResult[]): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(chalk.bold(\" Cache health check\"));\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Session\", \"Project\", \"Cache Hit\", \"Status\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const result of results) {\n const hitStr = result.cacheHitRate !== null ? result.cacheHitRate.toFixed(2) : \"N/A\";\n const statusStr = result.isAnomaly\n ? chalk.red(\"✗ ANOMALY\")\n : chalk.green(\"✓ normal\");\n\n table.push([\n shortSessionId(result.sessionId),\n projectNameFromSlug(result.projectSlug),\n hitStr,\n statusStr,\n ]);\n }\n\n lines.push(table.toString());\n\n const anomalies = results.filter((r) => r.isAnomaly);\n if (anomalies.length > 0) {\n lines.push(\"\");\n lines.push(\n chalk.yellow(` ⚠ ${anomalies.length} session(s) with abnormally low cache hit rate.`),\n );\n for (const a of anomalies) {\n if (a.estimatedInflation) {\n lines.push(\n ` ${chalk.dim(\"→\")} Session ${shortSessionId(a.sessionId)} consumed ~${a.estimatedInflation}x more input tokens than expected.`,\n );\n }\n }\n lines.push(\n chalk.dim(\" Try: restart Claude Code or downgrade to a previous version.\"),\n );\n } else {\n lines.push(\"\");\n lines.push(chalk.green(\" ✓ No cache anomalies detected.\"));\n }\n\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nfunction getGradeColor(letter: string): (text: string) => string {\n if (letter.startsWith(\"A\")) return chalk.green;\n if (letter.startsWith(\"B\")) return chalk.cyan;\n if (letter.startsWith(\"C\")) return chalk.yellow;\n return chalk.red;\n}\n\nfunction formatRegressionStatus(status: string): string {\n switch (status) {\n case \"stable\":\n return chalk.green(\"✓ stable\");\n case \"declining\":\n return chalk.yellow(\"⚠ declining\");\n case \"regression\":\n return chalk.red(\"⚠ REGRESSION\");\n default:\n return status;\n }\n}\n","/**\n * Number and string formatting helpers for terminal output.\n */\n\n/** Format a number with comma separators: 3218 → \"3,218\" */\nexport function formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\n/** Format a ratio as a fixed-2 decimal: 0.734 → \"0.73\" */\nexport function formatRatio(n: number): string {\n return n.toFixed(2);\n}\n\n/** Format a percentage: 0.734 → \"73%\" */\nexport function formatPercent(n: number): string {\n return `${Math.round(n * 100)}%`;\n}\n\n/** Format milliseconds into a human-readable duration: 2820000 → \"47 min\" */\nexport function formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes} min`;\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n if (remainingMinutes === 0) return `${hours}h`;\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/** Truncate a session ID for display: \"31f3f224-abcd-...\" → \"31f3f224\" */\nexport function shortSessionId(id: string): string {\n return id.slice(0, 8);\n}\n\n/** Extract a human-readable project name from a slug like \"-Users-foo-my-app\" */\nexport function projectNameFromSlug(slug: string): string {\n const parts = slug.split(\"-\").filter(Boolean);\n return parts[parts.length - 1] || slug;\n}\n","/**\n * Contextual tips based on metric values.\n * Maps poor-scoring metrics to actionable suggestions.\n */\n\nimport type { MetricResult } from \"../parser/types.js\";\n\nconst TIPS: Record<string, Record<string, string>> = {\n \"reads-per-edit\": {\n warning: \"Claude is editing with less context. Add 'Always read files before editing' to your CLAUDE.md.\",\n critical: \"Very low reads before edits. Claude is making blind changes. Consider adding explicit read instructions.\",\n },\n \"rewrite-ratio\": {\n warning: \"High ratio of full-file rewrites. Add 'Prefer Edit over Write for existing files' to CLAUDE.md.\",\n critical: \"Claude is rewriting entire files instead of making surgical edits. This wastes tokens and risks data loss.\",\n },\n \"cache-hit-rate\": {\n warning: \"Cache hit rate is below normal. Sessions may be too short for caching to help.\",\n critical: \"Very low cache hit rate — possible cache bug. Try restarting Claude Code or downgrading to a previous version.\",\n },\n \"task-completion\": {\n warning: \"Claude is occasionally promising actions without following through.\",\n critical: \"Frequent unfulfilled promises. Claude says it will do things but doesn't. Try breaking tasks into smaller steps.\",\n },\n \"retry-density\": {\n warning: \"Some user messages look like retries. Claude may be misunderstanding requests.\",\n critical: \"High retry rate. Users are frequently re-asking. Consider providing more context in prompts.\",\n },\n \"tool-diversity\": {\n warning: \"Low tool diversity. Claude is over-relying on a narrow set of tools.\",\n critical: \"Very narrow tool usage. Claude may be stuck in a pattern. Try prompting for specific tool usage.\",\n },\n \"tokens-per-edit\": {\n warning: \"Tokens per edit is above average. Claude may be verbose without being productive.\",\n critical: \"Very high token cost per edit. Claude is burning tokens without proportional output.\",\n },\n \"tool-error-rate\": {\n warning: \"Elevated tool error rate. Claude may be calling tools with invalid arguments or on wrong paths.\",\n critical: \"High tool error rate. Frequent tool failures indicate misaligned inputs or environment issues.\",\n },\n \"thinking-utilization\": {\n warning: \"Extended thinking is underutilized. Enable it in your Claude Code settings for complex tasks.\",\n critical: \"No extended thinking detected on tool-using turns. Complex tasks benefit greatly from thinking blocks.\",\n },\n \"session-cost\": {\n warning: \"Session cost is above $2. Consider breaking long sessions into smaller focused tasks.\",\n critical: \"Session cost exceeds $5. Review if Claude is re-reading large files or running redundant operations.\",\n },\n};\n\nexport function getTip(metric: MetricResult): string | null {\n if (metric.status === \"healthy\") return null;\n\n const metricTips = TIPS[metric.name];\n if (!metricTips) return null;\n\n return metricTips[metric.status] ?? null;\n}\n\nexport function getAllTips(metrics: MetricResult[]): string[] {\n return metrics\n .map(getTip)\n .filter((tip): tip is string => tip !== null);\n}\n","import { createRequire } from \"node:module\";\n\nconst _require = createRequire(import.meta.url);\nexport const VERSION: string = (_require(\"../package.json\") as { version: string }).version;\n","/**\n * JSON output mode for scripting and CI.\n */\n\nimport type { GradeResult, Session } from \"../parser/types.js\";\nimport type { RegressionResult } from \"../anomaly/regression-detector.js\";\nimport type { CacheCheckResult } from \"../anomaly/cache-anomaly.js\";\n\nexport interface AuditJsonOutput {\n session: {\n id: string;\n project: string;\n model: string;\n durationMs: number;\n startTime: string;\n };\n grade: string;\n score: number;\n metrics: Array<{\n name: string;\n value: number | null;\n status: string;\n label: string;\n }>;\n}\n\nexport function formatAuditJson(session: Session, grade: GradeResult): string {\n const output: AuditJsonOutput = {\n session: {\n id: session.id,\n project: session.projectSlug,\n model: session.model,\n durationMs: session.durationMs,\n startTime: session.startTime,\n },\n grade: grade.letter,\n score: grade.score,\n metrics: grade.metrics.map((m) => ({\n name: m.name,\n value: m.value,\n status: m.status,\n label: m.label,\n })),\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nexport function formatTrendJson(results: RegressionResult[]): string {\n return JSON.stringify({ trend: results }, null, 2);\n}\n\nexport function formatCacheCheckJson(results: CacheCheckResult[]): string {\n return JSON.stringify({ cacheCheck: results }, null, 2);\n}\n","import type { GradeResult } from \"../parser/types.js\";\nimport type { RegressionResult } from \"../anomaly/regression-detector.js\";\n\nfunction csvEscape(value: string | number | null): string {\n if (value === null) return \"\";\n const str = String(value);\n if (str.includes(\",\") || str.includes('\"') || str.includes(\"\\n\") || str.includes(\"\\r\")) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction csvRow(fields: (string | number | null)[]): string {\n return fields.map(csvEscape).join(\",\");\n}\n\nexport function exportAuditCsv(grade: GradeResult): string {\n const lines: string[] = [];\n lines.push(csvRow([\"name\", \"value\", \"status\", \"label\"]));\n for (const metric of grade.metrics) {\n lines.push(csvRow([metric.name, metric.value, metric.status, metric.label]));\n }\n return lines.join(\"\\n\");\n}\n\nexport function exportTrendCsv(results: RegressionResult[]): string {\n const lines: string[] = [];\n lines.push(csvRow([\"name\", \"recentAvg\", \"fullAvg\", \"changePercent\", \"status\"]));\n for (const r of results) {\n lines.push(csvRow([r.name, r.recentAvg, r.fullAvg, r.changePercent, r.status]));\n }\n return lines.join(\"\\n\");\n}\n","import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { InspectoConfig } from \"./types.js\";\n\nconst KNOWN_THRESHOLD_KEYS = new Set([\n \"readsPerEdit\", \"rewriteRatio\", \"cacheHitRate\", \"taskCompletion\",\n \"retryDensity\", \"toolDiversity\", \"tokensPerEdit\", \"sessionCost\",\n]);\n\nconst KNOWN_WEIGHT_KEYS = new Set([\n \"readsPerEdit\", \"rewriteRatio\", \"cacheHitRate\", \"taskCompletion\",\n \"retryDensity\", \"toolDiversity\", \"tokensPerEdit\",\n]);\n\nconst KNOWN_TOP_KEYS = new Set([\"thresholds\", \"weights\", \"dataDir\", \"defaultProject\"]);\n\nexport function loadConfig(): InspectoConfig {\n const configPath = join(process.cwd(), \".inspecto.json\");\n let raw: string;\n try {\n raw = readFileSync(configPath, \"utf8\");\n } catch {\n return {};\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n process.stderr.write(\"inspecto: warning: .inspecto.json contains invalid JSON, using defaults\\n\");\n return {};\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n process.stderr.write(\"inspecto: warning: .inspecto.json must be a JSON object, using defaults\\n\");\n return {};\n }\n\n const config = parsed as Record<string, unknown>;\n\n for (const key of Object.keys(config)) {\n if (!KNOWN_TOP_KEYS.has(key)) {\n process.stderr.write(`inspecto: warning: .inspecto.json unknown key \"${key}\"\\n`);\n }\n }\n\n if (config[\"thresholds\"] && typeof config[\"thresholds\"] === \"object\" && !Array.isArray(config[\"thresholds\"])) {\n for (const key of Object.keys(config[\"thresholds\"] as object)) {\n if (!KNOWN_THRESHOLD_KEYS.has(key)) {\n process.stderr.write(`inspecto: warning: .inspecto.json unknown thresholds key \"${key}\"\\n`);\n }\n }\n }\n\n if (config[\"weights\"] && typeof config[\"weights\"] === \"object\" && !Array.isArray(config[\"weights\"])) {\n for (const key of Object.keys(config[\"weights\"] as object)) {\n if (!KNOWN_WEIGHT_KEYS.has(key)) {\n process.stderr.write(`inspecto: warning: .inspecto.json unknown weights key \"${key}\"\\n`);\n }\n }\n\n const weights = config[\"weights\"] as Record<string, unknown>;\n const numericValues = Object.values(weights).filter((v): v is number => typeof v === \"number\");\n const sum = numericValues.reduce((a, b) => a + b, 0);\n if (sum > 1.0 + 1e-9) {\n process.stderr.write(\n `inspecto: warning: weights in .inspecto.json sum to ${sum.toFixed(4)}, which exceeds 1.0\\n`,\n );\n }\n }\n\n return parsed as InspectoConfig;\n}\n","/**\n * Compute rolling averages from multiple sessions for trend analysis.\n */\n\nimport type { GradeResult } from \"../parser/types.js\";\n\nexport interface MetricAverage {\n name: string;\n recentAvg: number | null;\n fullAvg: number | null;\n changePercent: number | null;\n}\n\n/**\n * Compute per-metric averages for a recent window vs. full range.\n * @param grades - All graded sessions, sorted most recent first\n * @param recentCount - Number of sessions in the \"recent\" window\n */\nexport function computeBaselines(\n grades: GradeResult[],\n recentCount: number,\n): MetricAverage[] {\n if (grades.length === 0) return [];\n\n const recent = grades.slice(0, recentCount);\n const full = grades;\n\n const metricNames = grades[0].metrics.map((m) => m.name);\n\n return metricNames.map((name) => {\n const recentValues = extractValues(recent, name);\n const fullValues = extractValues(full, name);\n\n const recentAvg = average(recentValues);\n const fullAvg = average(fullValues);\n\n let changePercent: number | null = null;\n if (recentAvg !== null && fullAvg !== null && fullAvg !== 0) {\n changePercent = ((recentAvg - fullAvg) / Math.abs(fullAvg)) * 100;\n }\n\n return { name, recentAvg, fullAvg, changePercent };\n });\n}\n\nfunction extractValues(grades: GradeResult[], metricName: string): number[] {\n const values: number[] = [];\n for (const grade of grades) {\n const metric = grade.metrics.find((m) => m.name === metricName);\n if (metric?.value !== null && metric?.value !== undefined) {\n values.push(metric.value);\n }\n }\n return values;\n}\n\nfunction average(values: number[]): number | null {\n if (values.length === 0) return null;\n return values.reduce((a, b) => a + b, 0) / values.length;\n}\n","/**\n * Z-score based regression detection.\n *\n * Compares recent metric values against historical baseline to flag\n * statistically significant regressions.\n */\n\nimport type { MetricAverage } from \"./baseline.js\";\n\nexport type RegressionStatus = \"stable\" | \"declining\" | \"regression\";\n\nexport interface RegressionResult {\n name: string;\n recentAvg: number | null;\n fullAvg: number | null;\n changePercent: number | null;\n status: RegressionStatus;\n}\n\n/** Metrics where HIGHER values are WORSE (inverted for regression detection). */\nconst INVERTED_METRICS = new Set([\n \"rewrite-ratio\",\n \"retry-density\",\n \"tokens-per-edit\",\n]);\n\n/**\n * Detect regressions from baseline averages.\n * A change > 30% in the \"bad\" direction is a regression.\n * A change > 10% is \"declining\".\n */\nexport function detectRegressions(\n baselines: MetricAverage[],\n): RegressionResult[] {\n return baselines.map((b) => {\n let status: RegressionStatus = \"stable\";\n\n if (b.changePercent !== null) {\n const isInverted = INVERTED_METRICS.has(b.name);\n // For normal metrics, negative change is bad. For inverted, positive change is bad.\n const badDirection = isInverted ? b.changePercent > 0 : b.changePercent < 0;\n const magnitude = Math.abs(b.changePercent);\n\n if (badDirection && magnitude > 30) {\n status = \"regression\";\n } else if (badDirection && magnitude > 10) {\n status = \"declining\";\n }\n }\n\n return {\n name: b.name,\n recentAvg: b.recentAvg,\n fullAvg: b.fullAvg,\n changePercent: b.changePercent,\n status,\n };\n });\n}\n","/**\n * Parse human-readable duration strings into Date offsets.\n */\n\n/**\n * Parse a duration string like \"7d\", \"14d\", \"30d\" into a Date\n * representing that many days before `now`.\n */\nexport function parseDuration(duration: string, now = new Date()): Date {\n const match = duration.match(/^(\\d+)d$/);\n if (!match) {\n throw new Error(\n `Invalid duration: \"${duration}\". Use format like \"7d\", \"14d\", \"30d\".`,\n );\n }\n\n const days = parseInt(match[1], 10);\n const result = new Date(now);\n result.setDate(result.getDate() - days);\n return result;\n}\n","/**\n * Runs fn over items with at most `limit` concurrent in-flight promises.\n * Returns settled results in input order, matching Promise.allSettled semantics.\n * One rejection never aborts the remaining items.\n */\nexport async function concurrentSettled<T, R>(\n items: T[],\n limit: number,\n fn: (item: T) => Promise<R>,\n): Promise<PromiseSettledResult<R>[]> {\n const results = new Array<PromiseSettledResult<R>>(items.length);\n let next = 0;\n\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const i = next++;\n try {\n results[i] = { status: \"fulfilled\", value: await fn(items[i]) };\n } catch (error) {\n results[i] = { status: \"rejected\", reason: error };\n }\n }\n };\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return results;\n}\n","import { DatabaseSync } from \"node:sqlite\";\nimport { createHash } from \"node:crypto\";\nimport type { GradeResult } from \"../parser/types.js\";\nimport { getCacheFilePath } from \"../utils/paths.js\";\n\nlet db: DatabaseSync | null = null;\n\nfunction getDb(): DatabaseSync {\n if (!db) {\n db = new DatabaseSync(getCacheFilePath());\n db.exec(`\n CREATE TABLE IF NOT EXISTS grade_cache (\n cache_key TEXT PRIMARY KEY,\n grade_result TEXT NOT NULL\n )\n `);\n }\n return db;\n}\n\nfunction makeCacheKey(sessionPath: string, mtime: Date): string {\n return createHash(\"sha256\")\n .update(`${sessionPath}:${mtime.getTime()}`)\n .digest(\"hex\");\n}\n\nexport function getCachedGrade(sessionPath: string, mtime: Date): GradeResult | null {\n try {\n const key = makeCacheKey(sessionPath, mtime);\n const stmt = getDb().prepare(\"SELECT grade_result FROM grade_cache WHERE cache_key = ?\");\n const row = stmt.get(key) as { grade_result: string } | undefined;\n if (!row) return null;\n return JSON.parse(row.grade_result) as GradeResult;\n } catch {\n process.stderr.write(\"[inspecto cache] read error, skipping cache\\n\");\n return null;\n }\n}\n\nexport function setCachedGrade(sessionPath: string, mtime: Date, grade: GradeResult): void {\n try {\n const key = makeCacheKey(sessionPath, mtime);\n const stmt = getDb().prepare(\n \"INSERT OR REPLACE INTO grade_cache (cache_key, grade_result) VALUES (?, ?)\",\n );\n stmt.run(key, JSON.stringify(grade));\n } catch {\n process.stderr.write(\"[inspecto cache] write error, skipping cache\\n\");\n }\n}\n","/**\n * Trend analysis command — detect regressions over time.\n */\n\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { gradeSession } from \"../metrics/grader.js\";\nimport { computeBaselines } from \"../anomaly/baseline.js\";\nimport { detectRegressions } from \"../anomaly/regression-detector.js\";\nimport { renderTrendReport } from \"../reporter/terminal.js\";\nimport { formatTrendJson } from \"../reporter/json-reporter.js\";\nimport { exportTrendCsv } from \"../reporter/csv-reporter.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport { concurrentSettled } from \"../utils/concurrent.js\";\nimport { getCachedGrade, setCachedGrade } from \"../cache/grade-cache.js\";\nimport { loadConfig } from \"../config/loader.js\";\nimport type { GradeResult, SessionFile } from \"../parser/types.js\";\n\nconst CONCURRENCY = 16;\n\nexport interface TrendOptions {\n since?: string;\n json?: boolean;\n dataDir?: string;\n project?: string;\n /** false when --no-fail is passed; defaults to true via Commander */\n fail?: boolean;\n format?: string;\n}\n\nexport async function runTrend(options: TrendOptions): Promise<void> {\n const config = loadConfig();\n const dataDir = options.dataDir ?? config.dataDir;\n const project = options.project ?? config.defaultProject;\n\n const duration = options.since ?? \"7d\";\n const sinceDate = parseDuration(duration);\n\n const sessionFiles = await scanSessions({ dataDir, project, since: sinceDate });\n\n if (sessionFiles.length === 0) {\n console.log(`No sessions found in the last ${duration}.`);\n return;\n }\n\n const settled = await concurrentSettled(sessionFiles, CONCURRENCY, async (sf: SessionFile) => {\n const cached = getCachedGrade(sf.path, sf.mtime);\n if (cached) return cached;\n const records = readJsonl(sf.path);\n const session = await buildSession(records, sf.sessionId, sf.projectSlug, sf.subagentPaths);\n const grade = gradeSession(session, config);\n setCachedGrade(sf.path, sf.mtime, grade);\n return grade;\n });\n\n const grades: GradeResult[] = settled\n .filter((r): r is PromiseFulfilledResult<GradeResult> => r.status === \"fulfilled\")\n .map((r) => r.value);\n\n if (grades.length === 0) {\n console.log(\"No valid sessions found to analyze.\");\n return;\n }\n\n // Use half the sessions as the \"recent\" window, minimum 1\n const recentCount = Math.max(1, Math.floor(grades.length / 2));\n const baselines = computeBaselines(grades, recentCount);\n const regressions = detectRegressions(baselines);\n\n if (options.json || options.format === \"json\") {\n console.log(formatTrendJson(regressions));\n } else if (options.format === \"csv\") {\n console.log(exportTrendCsv(regressions));\n } else {\n console.log(renderTrendReport(regressions, grades.length, duration));\n }\n\n if (options.fail !== false && regressions.some((r) => r.status === \"regression\")) {\n process.exitCode = 1;\n }\n}\n","/**\n * Cache hit rate anomaly detection.\n *\n * Specifically checks for the prompt cache bug that caused 10-20x\n * token cost inflation by detecting sessions with near-zero cache hit rates.\n */\n\nimport type { Session } from \"../parser/types.js\";\nimport { computeCacheHitRate } from \"../metrics/cache-hit-rate.js\";\n\nexport interface CacheCheckResult {\n sessionId: string;\n projectSlug: string;\n timestamp: string;\n cacheHitRate: number | null;\n isAnomaly: boolean;\n estimatedInflation: number | null;\n}\n\nconst ANOMALY_THRESHOLD = 0.05;\nconst NORMAL_CACHE_RATE = 0.65;\n\n/**\n * Check a single session for cache hit rate anomalies.\n */\nexport function checkCacheAnomaly(session: Session): CacheCheckResult {\n const metric = computeCacheHitRate(session);\n\n const isAnomaly = metric.value !== null && metric.value < ANOMALY_THRESHOLD;\n\n let estimatedInflation: number | null = null;\n if (isAnomaly && metric.value !== null) {\n // If normal rate is 65% cache reads, the effective input cost multiplier\n // when cache is broken is roughly 1 / (1 - normalRate)\n // Normal: 35% full-price tokens. Broken: 100% full-price tokens.\n estimatedInflation = Math.round(1 / (1 - NORMAL_CACHE_RATE));\n }\n\n return {\n sessionId: session.id,\n projectSlug: session.projectSlug,\n timestamp: session.startTime,\n cacheHitRate: metric.value,\n isAnomaly,\n estimatedInflation,\n };\n}\n","/**\n * Cache bug detection command.\n * Scans recent sessions for abnormally low cache hit rates.\n */\n\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { checkCacheAnomaly } from \"../anomaly/cache-anomaly.js\";\nimport { renderCacheCheckReport } from \"../reporter/terminal.js\";\nimport { formatCacheCheckJson } from \"../reporter/json-reporter.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport type { CacheCheckResult } from \"../anomaly/cache-anomaly.js\";\n\nexport interface CacheCheckOptions {\n since?: string;\n json?: boolean;\n dataDir?: string;\n /** false when --no-fail is passed; defaults to true via Commander */\n fail?: boolean;\n}\n\nexport async function runCacheCheck(options: CacheCheckOptions): Promise<void> {\n const duration = options.since ?? \"7d\";\n const sinceDate = parseDuration(duration);\n\n const sessionFiles = await scanSessions({\n dataDir: options.dataDir,\n since: sinceDate,\n });\n\n if (sessionFiles.length === 0) {\n console.log(`No sessions found in the last ${duration}.`);\n return;\n }\n\n const settled = await Promise.allSettled(\n sessionFiles.map(async (sf) => {\n const records = readJsonl(sf.path);\n const session = await buildSession(records, sf.sessionId, sf.projectSlug, sf.subagentPaths);\n return checkCacheAnomaly(session);\n }),\n );\n const results: CacheCheckResult[] = settled\n .filter((r): r is PromiseFulfilledResult<CacheCheckResult> => r.status === \"fulfilled\")\n .map((r) => r.value);\n\n if (results.length === 0) {\n console.log(\"No valid sessions found to analyze.\");\n return;\n }\n\n if (options.json) {\n console.log(formatCacheCheckJson(results));\n } else {\n console.log(renderCacheCheckReport(results));\n }\n\n if (options.fail !== false && results.some((r) => r.isAnomaly)) {\n process.exitCode = 1;\n }\n}\n","/**\n * Cross-project comparison command.\n * Compares average quality metrics across multiple projects.\n */\n\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { readJsonl } from \"../parser/jsonl-reader.js\";\nimport { buildSession } from \"../parser/session-builder.js\";\nimport { gradeSession, gradeLetterFromScore } from \"../metrics/grader.js\";\nimport { concurrentSettled } from \"../utils/concurrent.js\";\nimport { getCachedGrade, setCachedGrade } from \"../cache/grade-cache.js\";\nimport { loadConfig } from \"../config/loader.js\";\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { GradeResult, SessionFile } from \"../parser/types.js\";\n\nconst CONCURRENCY = 16;\n\nexport interface CompareOptions {\n projects: string;\n json?: boolean;\n dataDir?: string;\n since?: string;\n}\n\ninterface ProjectSummary {\n name: string;\n sessionCount: number;\n avgGrade: number;\n avgLetter: string;\n metrics: Map<string, number>;\n}\n\nexport async function runCompare(options: CompareOptions): Promise<void> {\n const config = loadConfig();\n const dataDir = options.dataDir ?? config.dataDir;\n\n const projectNames = options.projects.split(\",\").map((p) => p.trim());\n const summaries: ProjectSummary[] = [];\n\n const projectSummaries = await Promise.all(\n projectNames.map(async (projectFilter) => {\n const sessionFiles = await scanSessions({ dataDir, project: projectFilter });\n\n if (sessionFiles.length === 0) return null;\n\n const settled = await concurrentSettled(\n sessionFiles,\n CONCURRENCY,\n async (sf: SessionFile) => {\n const cached = getCachedGrade(sf.path, sf.mtime);\n if (cached) return cached;\n const records = readJsonl(sf.path);\n const session = await buildSession(\n records,\n sf.sessionId,\n sf.projectSlug,\n sf.subagentPaths,\n );\n const grade = gradeSession(session, config);\n setCachedGrade(sf.path, sf.mtime, grade);\n return grade;\n },\n );\n\n const grades: GradeResult[] = settled\n .filter((r): r is PromiseFulfilledResult<GradeResult> => r.status === \"fulfilled\")\n .map((r) => r.value);\n\n if (grades.length === 0) return null;\n\n const avgScore = grades.reduce((s, g) => s + g.score, 0) / grades.length;\n const metricAvgs = new Map<string, number>();\n for (const metric of grades[0].metrics) {\n const values = grades\n .map((g) => g.metrics.find((m) => m.name === metric.name)?.value)\n .filter((v): v is number => v !== null);\n if (values.length > 0) {\n metricAvgs.set(metric.name, values.reduce((a, b) => a + b, 0) / values.length);\n }\n }\n\n return {\n name: projectFilter,\n sessionCount: grades.length,\n avgGrade: Math.round(avgScore),\n avgLetter: gradeLetterFromScore(avgScore),\n metrics: metricAvgs,\n } as ProjectSummary;\n }),\n );\n\n for (const s of projectSummaries) {\n if (s !== null) summaries.push(s);\n }\n\n if (summaries.length === 0) {\n console.log(\"No matching projects found.\");\n return;\n }\n\n if (options.json) {\n const jsonOutput = summaries.map((s) => ({\n project: s.name,\n sessions: s.sessionCount,\n grade: s.avgLetter,\n score: s.avgGrade,\n metrics: Object.fromEntries(s.metrics),\n }));\n console.log(JSON.stringify({ compare: jsonOutput }, null, 2));\n return;\n }\n\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(chalk.bold(\" Project comparison\"));\n lines.push(\"\");\n\n const head = [\"Project\", \"Sessions\", \"Grade\", ...summaries[0]?.metrics.keys() ?? []].map(\n (h) => chalk.dim(h),\n );\n\n const table = new Table({\n head,\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n for (const summary of summaries) {\n const row: string[] = [\n summary.name,\n summary.sessionCount.toString(),\n summary.avgLetter,\n ];\n for (const [, value] of summary.metrics) {\n row.push(value.toFixed(2));\n }\n table.push(row);\n }\n\n lines.push(table.toString());\n lines.push(\"\");\n console.log(lines.join(\"\\n\"));\n}\n\n","import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport { scanSessions } from \"../parser/project-scanner.js\";\nimport { formatDuration, shortSessionId } from \"../utils/format.js\";\n\nexport interface ListOptions {\n sessions?: boolean;\n project?: string;\n dataDir?: string;\n}\n\nconst TABLE_CHARS = {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n};\n\nconst TABLE_STYLE = { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 };\n\nexport async function runList(options: ListOptions): Promise<void> {\n const sessionFiles = await scanSessions({\n dataDir: options.dataDir,\n project: options.project,\n });\n\n if (sessionFiles.length === 0) {\n console.log(\"No sessions found.\");\n return;\n }\n\n if (options.sessions || options.project) {\n const toShow = options.project ? sessionFiles : sessionFiles.slice(0, 20);\n\n const lines: string[] = [\"\"];\n if (options.project) {\n lines.push(chalk.bold(` Sessions for ${options.project}`) + chalk.dim(` (${toShow.length})`));\n } else {\n lines.push(chalk.bold(\" 20 most recent sessions\"));\n }\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Session\", \"Project\", \"Date\", \"Duration\"].map((h) => chalk.dim(h)),\n style: TABLE_STYLE,\n chars: TABLE_CHARS,\n });\n\n for (const sf of toShow) {\n const dateStr = sf.mtime.toISOString().slice(0, 10);\n let durStr = \"—\";\n if (\n sf.birthtime &&\n sf.birthtime.getTime() > 0 &&\n sf.birthtime.getTime() < sf.mtime.getTime()\n ) {\n durStr = formatDuration(sf.mtime.getTime() - sf.birthtime.getTime());\n }\n table.push([shortSessionId(sf.sessionId), sf.projectSlug, dateStr, durStr]);\n }\n\n lines.push(table.toString());\n lines.push(\"\");\n console.log(lines.join(\"\\n\"));\n } else {\n // Group by project, sorted by most recent session\n const projectMap = new Map<string, { count: number; lastActive: Date }>();\n for (const sf of sessionFiles) {\n const existing = projectMap.get(sf.projectSlug);\n if (!existing) {\n projectMap.set(sf.projectSlug, { count: 1, lastActive: sf.mtime });\n } else {\n existing.count++;\n if (sf.mtime > existing.lastActive) existing.lastActive = sf.mtime;\n }\n }\n\n const projects = [...projectMap.entries()].sort(\n (a, b) => b[1].lastActive.getTime() - a[1].lastActive.getTime(),\n );\n\n const lines: string[] = [\"\"];\n lines.push(chalk.bold(\" Projects\") + chalk.dim(` (${projects.length} found)`));\n lines.push(\"\");\n\n const table = new Table({\n head: [\"Project\", \"Sessions\", \"Last active\"].map((h) => chalk.dim(h)),\n style: TABLE_STYLE,\n chars: TABLE_CHARS,\n });\n\n for (const [slug, info] of projects) {\n table.push([slug, info.count.toString(), info.lastActive.toISOString().slice(0, 10)]);\n }\n\n lines.push(table.toString());\n lines.push(\"\");\n console.log(lines.join(\"\\n\"));\n }\n}\n","import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport { loadConfig } from \"../config/loader.js\";\nimport { DEFAULT_THRESHOLDS, DEFAULT_WEIGHTS } from \"../config/types.js\";\n\nexport async function runConfigValidate(): Promise<void> {\n const config = loadConfig();\n const hasConfig = Object.keys(config).length > 0;\n\n console.log(\"\");\n if (!hasConfig) {\n console.log(chalk.dim(\" No .inspecto.json found in current directory. Showing all defaults.\\n\"));\n } else {\n console.log(chalk.bold(\" .inspecto.json\") + chalk.dim(\" — effective configuration\\n\"));\n }\n\n // Thresholds table\n const thresholdTable = new Table({\n head: [\"Metric\", \"healthy\", \"warning\", \"Source\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n const thresholdEntries: Array<[keyof typeof DEFAULT_THRESHOLDS, string]> = [\n [\"readsPerEdit\", \"reads-per-edit\"],\n [\"rewriteRatio\", \"rewrite-ratio\"],\n [\"cacheHitRate\", \"cache-hit-rate\"],\n [\"taskCompletion\", \"task-completion\"],\n [\"retryDensity\", \"retry-density\"],\n [\"toolDiversity\", \"tool-diversity\"],\n [\"tokensPerEdit\", \"tokens-per-edit\"],\n [\"sessionCost\", \"session-cost\"],\n ];\n\n for (const [key, label] of thresholdEntries) {\n const override = config.thresholds?.[key];\n const def = DEFAULT_THRESHOLDS[key];\n const effective = { ...def, ...override };\n const source = override ? chalk.green(\"config\") : chalk.dim(\"default\");\n thresholdTable.push([label, String(effective.healthy), String(effective.warning), source]);\n }\n\n console.log(chalk.bold(\" Thresholds\"));\n console.log(thresholdTable.toString());\n console.log(\"\");\n\n // Weights table\n const weightTable = new Table({\n head: [\"Metric\", \"Weight\", \"Source\"].map((h) => chalk.dim(h)),\n style: { head: [], border: [], \"padding-left\": 2, \"padding-right\": 2 },\n chars: {\n top: \"─\", \"top-mid\": \"─\", \"top-left\": \" \", \"top-right\": \"\",\n bottom: \"─\", \"bottom-mid\": \"─\", \"bottom-left\": \" \", \"bottom-right\": \"\",\n left: \" \", \"left-mid\": \" \", mid: \"─\", \"mid-mid\": \"─\",\n right: \"\", \"right-mid\": \"\", middle: \" \",\n },\n });\n\n const weightEntries: Array<[keyof typeof DEFAULT_WEIGHTS, string]> = [\n [\"readsPerEdit\", \"reads-per-edit\"],\n [\"rewriteRatio\", \"rewrite-ratio\"],\n [\"cacheHitRate\", \"cache-hit-rate\"],\n [\"taskCompletion\", \"task-completion\"],\n [\"retryDensity\", \"retry-density\"],\n [\"toolDiversity\", \"tool-diversity\"],\n [\"tokensPerEdit\", \"tokens-per-edit\"],\n ];\n\n let configuredWeightSum = 0;\n for (const [key] of weightEntries) {\n configuredWeightSum += config.weights?.[key] ?? DEFAULT_WEIGHTS[key];\n }\n\n for (const [key, label] of weightEntries) {\n const override = config.weights?.[key];\n const effective = override ?? DEFAULT_WEIGHTS[key];\n const source = override !== undefined ? chalk.green(\"config\") : chalk.dim(\"default\");\n weightTable.push([label, effective.toFixed(2), source]);\n }\n\n console.log(chalk.bold(\" Weights\") + chalk.dim(` (configurable sum: ${configuredWeightSum.toFixed(2)})`));\n console.log(weightTable.toString());\n console.log(\"\");\n\n // Other settings\n if (config.dataDir || config.defaultProject) {\n console.log(chalk.bold(\" Other settings\"));\n if (config.dataDir) {\n console.log(` ${chalk.dim(\"dataDir\")} ${config.dataDir} ${chalk.green(\"(config)\")}`);\n }\n if (config.defaultProject) {\n console.log(` ${chalk.dim(\"defaultProject\")} ${config.defaultProject} ${chalk.green(\"(config)\")}`);\n }\n console.log(\"\");\n }\n}\n","export interface ThresholdConfig {\n healthy: number;\n warning: number;\n}\n\nexport interface InspectoConfig {\n thresholds?: {\n readsPerEdit?: ThresholdConfig;\n rewriteRatio?: ThresholdConfig;\n cacheHitRate?: ThresholdConfig;\n taskCompletion?: ThresholdConfig;\n retryDensity?: ThresholdConfig;\n toolDiversity?: ThresholdConfig;\n tokensPerEdit?: ThresholdConfig;\n sessionCost?: ThresholdConfig;\n };\n weights?: {\n readsPerEdit?: number;\n rewriteRatio?: number;\n cacheHitRate?: number;\n taskCompletion?: number;\n retryDensity?: number;\n toolDiversity?: number;\n tokensPerEdit?: number;\n };\n dataDir?: string;\n defaultProject?: string;\n}\n\nexport const DEFAULT_THRESHOLDS = {\n readsPerEdit: { healthy: 4.0, warning: 2.0 },\n rewriteRatio: { healthy: 0.25, warning: 0.40 },\n cacheHitRate: { healthy: 0.50, warning: 0.30 },\n taskCompletion: { healthy: 0.90, warning: 0.70 },\n retryDensity: { healthy: 0.10, warning: 0.20 },\n toolDiversity: { healthy: 0.60, warning: 0.40 },\n tokensPerEdit: { healthy: 5000, warning: 10000 },\n sessionCost: { healthy: 2.00, warning: 5.00 },\n} satisfies Required<NonNullable<InspectoConfig[\"thresholds\"]>>;\n\nexport const DEFAULT_WEIGHTS = {\n readsPerEdit: 0.14,\n rewriteRatio: 0.11,\n cacheHitRate: 0.11,\n taskCompletion: 0.10,\n retryDensity: 0.07,\n toolDiversity: 0.06,\n tokensPerEdit: 0.11,\n} satisfies Required<NonNullable<InspectoConfig[\"weights\"]>>;\n"],"mappings":"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","names":["chalk","join","join","basename","basename","average","round","round","round","round","round","EDIT_TOOLS","round","round","round","round","config","join","config","config","chalk","config","chalk","Table","CONCURRENCY","config","chalk","Table","chalk","Table","chalk","Table","config","chalk","Table"]}
|