/** * Generate a deterministic summary of what a Claude Code instance is likely * working on, based on its working directory and git context. * * Pure git-based — no external API calls, no dependencies. */ export async function generateSummary(context: { cwd: string; git_root: string | null; git_branch?: string | null; recent_files?: string[]; session_name?: string | null; tty?: string | null; }): Promise { const parts: string[] = []; // Extract project name from git root or cwd const dir = context.git_root || context.cwd; const projectName = dir.split("/").pop() || dir; const branch = context.git_branch || "main"; // Branch info if (context.git_branch && context.git_branch !== "main" && context.git_branch !== "master") { parts.push(`on ${context.git_branch}`); } // Recent file activity if (context.recent_files && context.recent_files.length > 0) { const fileList = context.recent_files.slice(0, 3).join(", "); parts.push(`recently touched ${fileList}`); } // Build prefix: [SessionName] or [project:branch], with optional TTY for disambiguation const ttyTag = context.tty ? `:${context.tty.replace("/dev/", "")}` : ""; const prefix = context.session_name ? `[${context.session_name}${ttyTag}]` : `[${projectName}:${branch}${ttyTag}]`; if (parts.length === 0) { return `${prefix} Working in ${projectName}`; } return `${prefix} ${parts.join(", ")} in ${projectName}`; } /** * Get the current git branch name for a directory. */ export async function getGitBranch(cwd: string): Promise { try { const proc = Bun.spawn(["git", "rev-parse", "--abbrev-ref", "HEAD"], { cwd, stdout: "pipe", stderr: "ignore", }); const text = await new Response(proc.stdout).text(); const code = await proc.exited; if (code === 0) { return text.trim(); } } catch { // not a git repo } return null; } /** * Get recently modified tracked files in the git repo. */ export async function getRecentFiles( cwd: string, limit = 10 ): Promise { try { // Get modified/staged files first const diffProc = Bun.spawn(["git", "diff", "--name-only", "HEAD"], { cwd, stdout: "pipe", stderr: "ignore", }); const diffText = await new Response(diffProc.stdout).text(); await diffProc.exited; const files = diffText .trim() .split("\n") .filter((f) => f.length > 0); if (files.length >= limit) { return files.slice(0, limit); } // Also get recently committed files const logProc = Bun.spawn( ["git", "log", "--oneline", "--name-only", "-5", "--format="], { cwd, stdout: "pipe", stderr: "ignore", } ); const logText = await new Response(logProc.stdout).text(); await logProc.exited; const logFiles = logText .trim() .split("\n") .filter((f) => f.length > 0); const allFiles = [...new Set([...files, ...logFiles])]; return allFiles.slice(0, limit); } catch { return []; } }