@keepgoingdev/mcp-server 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../../packages/shared/src/session.ts","../../../packages/shared/src/timeUtils.ts","../../../packages/shared/src/gitUtils.ts","../../../packages/shared/src/briefingTier.ts","../../../packages/shared/src/reentry.ts","../../../packages/shared/src/briefingFormatter.ts","../../../packages/shared/src/continueOn.ts","../../../packages/shared/src/storage.ts","../../../packages/shared/src/registry.ts","../../../packages/shared/src/smartSummary.ts","../../../packages/shared/src/decisionStorage.ts","../../../packages/shared/src/license.ts","../../../packages/shared/src/featureGate.ts","../../../packages/shared/src/decisionDetection.ts","../../../packages/shared/src/reader.ts","../../../packages/shared/src/setup.ts","../../../packages/shared/src/licenseClient.ts","../../../packages/shared/src/licenseRevalidation.ts","../src/tools/getMomentum.ts","../src/tools/getSessionHistory.ts","../src/tools/getReentryBriefing.ts","../src/tools/saveCheckpoint.ts","../src/tools/getDecisions.ts","../src/tools/getCurrentTask.ts","../src/tools/setupProject.ts","../src/cli/migrate.ts","../src/tools/activateLicense.ts","../src/tools/deactivateLicense.ts","../src/tools/continueOn.ts","../src/prompts/resume.ts","../src/prompts/decisions.ts","../src/prompts/progress.ts","../src/cli/util.ts","../src/cli/print.ts","../src/cli/saveCheckpoint.ts","../src/cli/transcriptUtils.ts","../src/cli/updateTask.ts","../src/cli/statusline.ts","../src/cli/continueOn.ts","../src/cli/detectDecisions.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { findGitRoot } from '@keepgoingdev/shared';\nimport { KeepGoingReader } from './storage.js';\nimport { registerGetMomentum } from './tools/getMomentum.js';\nimport { registerGetSessionHistory } from './tools/getSessionHistory.js';\nimport { registerGetReentryBriefing } from './tools/getReentryBriefing.js';\nimport { registerSaveCheckpoint } from './tools/saveCheckpoint.js';\nimport { registerGetDecisions } from './tools/getDecisions.js';\nimport { registerGetCurrentTask } from './tools/getCurrentTask.js';\nimport { registerSetupProject } from './tools/setupProject.js';\nimport { registerActivateLicense } from './tools/activateLicense.js';\nimport { registerDeactivateLicense } from './tools/deactivateLicense.js';\nimport { registerContinueOn } from './tools/continueOn.js';\nimport { registerResumePrompt } from './prompts/resume.js';\nimport { registerDecisionsPrompt } from './prompts/decisions.js';\nimport { registerProgressPrompt } from './prompts/progress.js';\nimport { handlePrintMomentum, handlePrintCurrent } from './cli/print.js';\nimport { handleSaveCheckpoint } from './cli/saveCheckpoint.js';\nimport { handleUpdateTask, handleUpdateTaskFromHook } from './cli/updateTask.js';\nimport { handleStatusline } from './cli/statusline.js';\nimport { handleContinueOn } from './cli/continueOn.js';\nimport { handleDetectDecisions } from './cli/detectDecisions.js';\n\n// CLI flag dispatch table. Each handler calls process.exit() when done.\nconst CLI_HANDLERS: Record<string, () => Promise<void>> = {\n '--print-momentum': handlePrintMomentum,\n '--save-checkpoint': handleSaveCheckpoint,\n '--update-task': handleUpdateTask,\n '--update-task-from-hook': handleUpdateTaskFromHook,\n '--print-current': handlePrintCurrent,\n '--statusline': handleStatusline,\n '--continue-on': handleContinueOn,\n '--detect-decisions': handleDetectDecisions,\n};\n\nconst flag = process.argv.slice(2).find(a => a in CLI_HANDLERS);\nif (flag) {\n await CLI_HANDLERS[flag]();\n} else {\n // Default: start MCP server\n // Workspace path can be passed as an argument, otherwise defaults to CWD.\n // MCP hosts (Claude Code, etc.) typically launch the server with the project root as CWD.\n const workspacePath = findGitRoot(process.argv[2] || process.cwd());\n const reader = new KeepGoingReader(workspacePath);\n\n const server = new McpServer({\n name: 'keepgoing',\n version: '0.1.0',\n });\n\n // Register tools\n registerGetMomentum(server, reader, workspacePath);\n registerGetSessionHistory(server, reader);\n registerGetReentryBriefing(server, reader, workspacePath);\n registerGetDecisions(server, reader);\n registerGetCurrentTask(server, reader);\n registerSaveCheckpoint(server, reader, workspacePath);\n registerContinueOn(server, reader, workspacePath);\n registerSetupProject(server, workspacePath);\n registerActivateLicense(server);\n registerDeactivateLicense(server);\n\n // Register prompts\n registerResumePrompt(server);\n registerDecisionsPrompt(server);\n registerProgressPrompt(server);\n\n // Connect via stdio\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('KeepGoing MCP server started');\n}\n","import { randomUUID } from 'crypto';\nimport type { SessionCheckpoint, ProjectSessions, ProjectMeta, ProjectState, DecisionRecord, ProjectDecisions } from './types';\n\n/**\n * Generates a UUID v4 unique ID for checkpoints.\n */\nexport function generateCheckpointId(): string {\n return randomUUID();\n}\n\n/**\n * Creates a SessionCheckpoint with auto-generated id and timestamp.\n * Consolidates the repeated checkpoint construction pattern.\n */\nexport function createCheckpoint(\n fields: Omit<SessionCheckpoint, 'id' | 'timestamp'>,\n): SessionCheckpoint {\n return {\n id: generateCheckpointId(),\n timestamp: new Date().toISOString(),\n ...fields,\n };\n}\n\n/**\n * Creates a default empty project sessions container.\n */\nexport function createEmptyProjectSessions(projectName: string): ProjectSessions {\n return {\n version: 1,\n project: projectName,\n sessions: [],\n lastSessionId: undefined,\n };\n}\n\n/**\n * Creates a default project metadata object.\n */\nexport function createProjectMeta(): ProjectMeta {\n const now = new Date().toISOString();\n return {\n projectId: randomUUID(),\n createdAt: now,\n lastUpdated: now,\n };\n}\n\n/**\n * Creates a default empty project state object.\n */\nexport function createEmptyProjectState(): ProjectState {\n return {};\n}\n\n/**\n * Creates a DecisionRecord with an auto-generated UUID id.\n */\nexport function createDecisionRecord(\n fields: Omit<DecisionRecord, 'id'>,\n): DecisionRecord {\n return {\n id: randomUUID(),\n ...fields,\n };\n}\n\n/**\n * Creates a default empty project decisions container.\n */\nexport function createEmptyProjectDecisions(projectName: string): ProjectDecisions {\n return {\n version: 1,\n project: projectName,\n decisions: [],\n lastDecisionId: undefined,\n };\n}\n","/**\n * Formats a timestamp as a human-readable relative time string.\n * Examples: \"just now\", \"5 minutes ago\", \"3 hours ago\", \"2 days ago\", \"1 week ago\"\n *\n * Note: Month and year calculations use approximations (30 days/month, 365 days/year)\n * for simplicity. These approximations are acceptable for the \"human-readable\" purpose.\n */\nexport function formatRelativeTime(timestamp: string): string {\n const now = Date.now();\n const then = new Date(timestamp).getTime();\n const diffMs = now - then;\n\n // Handle invalid dates\n if (isNaN(diffMs)) {\n return 'unknown time';\n }\n\n // Future dates\n if (diffMs < 0) {\n return 'in the future';\n }\n\n const seconds = Math.floor(diffMs / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(days / 30); // Approximation for human readability\n const years = Math.floor(days / 365); // Approximation, doesn't account for leap years\n\n if (seconds < 10) {\n return 'just now';\n } else if (seconds < 60) {\n return `${seconds} seconds ago`;\n } else if (minutes < 60) {\n return minutes === 1 ? '1 minute ago' : `${minutes} minutes ago`;\n } else if (hours < 24) {\n return hours === 1 ? '1 hour ago' : `${hours} hours ago`;\n } else if (days < 7) {\n return days === 1 ? '1 day ago' : `${days} days ago`;\n } else if (weeks < 4) {\n return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`;\n } else if (months < 12) {\n return months === 1 ? '1 month ago' : `${months} months ago`;\n } else {\n return years === 1 ? '1 year ago' : `${years} years ago`;\n }\n}\n","import { execFileSync, execFile } from 'child_process';\nimport path from 'node:path';\nimport { promisify } from 'util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Git utilities for extracting context from the workspace.\n * These are intentionally decoupled from VSCode APIs.\n * All sync functions use execFileSync (no shell injection).\n */\n\n/**\n * Returns the git working tree root for the given path.\n * In a worktree, returns the worktree root (not the main repo root).\n * Use `resolveStorageRoot()` to get the main repo root for storage.\n * Falls back to the original path if not inside a git repo.\n */\nexport function findGitRoot(startPath: string): string {\n try {\n const toplevel = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd: startPath,\n encoding: 'utf-8',\n timeout: 5000,\n }).trim();\n return toplevel || startPath;\n } catch {\n return startPath;\n }\n}\n\n/**\n * Cache for resolveStorageRoot results to avoid redundant subprocess calls.\n */\nconst storageRootCache = new Map<string, string>();\n\n/**\n * Clears the resolveStorageRoot cache.\n * Call this in long-lived processes (e.g. VS Code extension) when\n * worktree configuration may have changed.\n */\nexport function clearStorageRootCache(): void {\n storageRootCache.clear();\n}\n\n/**\n * Resolves the main repository root for storage purposes.\n * In a git worktree, `--show-toplevel` returns the worktree root, but\n * `.keepgoing/` should always live in the main repo root so data persists\n * across worktrees. This function uses `--git-common-dir` to find the\n * shared `.git` directory and derives the main repo root from it.\n *\n * Falls back to `startPath` if git commands fail (non-git directories, tests).\n */\nexport function resolveStorageRoot(startPath: string): string {\n const cached = storageRootCache.get(startPath);\n if (cached !== undefined) {\n return cached;\n }\n\n try {\n const toplevel = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd: startPath,\n encoding: 'utf-8',\n timeout: 5000,\n }).trim();\n\n const commonDir = execFileSync('git', ['rev-parse', '--git-common-dir'], {\n cwd: startPath,\n encoding: 'utf-8',\n timeout: 5000,\n }).trim();\n\n // commonDir is relative to the worktree root (e.g. \"../../.git\" or just \".git\")\n const absoluteCommonDir = path.resolve(toplevel, commonDir);\n const mainRoot = path.dirname(absoluteCommonDir);\n\n storageRootCache.set(startPath, mainRoot);\n return mainRoot;\n } catch {\n storageRootCache.set(startPath, startPath);\n return startPath;\n }\n}\n\n/**\n * Returns the current git branch name, or undefined if not in a git repo.\n */\nexport function getCurrentBranch(workspacePath: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Runs git log with the given format since a timestamp.\n * Shared implementation for commit hash and message retrieval.\n */\nfunction getGitLogSince(workspacePath: string, format: string, sinceTimestamp?: string): string[] {\n try {\n const since = sinceTimestamp || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();\n const result = execFileSync(\n 'git',\n ['log', `--since=${since}`, `--format=${format}`],\n {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n },\n );\n\n if (!result.trim()) {\n return [];\n }\n\n return result\n .trim()\n .split('\\n')\n .filter((line: string) => line.length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Returns a list of commit hashes since a given ISO timestamp.\n * If no timestamp is provided, returns recent commits (last 24 hours).\n */\nexport function getCommitsSince(workspacePath: string, sinceTimestamp?: string): string[] {\n return getGitLogSince(workspacePath, '%H', sinceTimestamp);\n}\n\n/**\n * Returns a list of commit subject lines since a given ISO timestamp.\n * If no timestamp is provided, returns commit messages from the last 24 hours.\n * Results are in reverse chronological order (newest first), matching git log default.\n */\nexport function getCommitMessagesSince(workspacePath: string, sinceTimestamp?: string): string[] {\n return getGitLogSince(workspacePath, '%s', sinceTimestamp);\n}\n\n/**\n * Returns the commit message (subject line) for a specific commit hash.\n * Uses hash-based lookup instead of timestamp-based --since to avoid race conditions.\n */\nexport function getCommitMessageByHash(workspacePath: string, commitHash: string): string | undefined {\n try {\n const result = execFileSync('git', ['log', '-1', '--format=%s', commitHash], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns the list of files changed in a specific commit (relative paths).\n * Uses `git diff-tree` to get the actual files touched by that commit,\n * as opposed to `getTouchedFiles` which returns the current workspace dirty state.\n */\nexport function getFilesChangedInCommit(workspacePath: string, commitHash: string): string[] {\n try {\n const result = execFileSync('git', ['diff-tree', '--no-commit-id', '--name-only', '-r', commitHash], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim().split('\\n').filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Returns the current HEAD commit hash, or undefined if not in a git repo.\n */\nexport function getHeadCommitHash(workspacePath: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Async version of getCurrentBranch. Avoids blocking the event loop.\n */\nexport async function getCurrentBranchAsync(workspacePath: string): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Async version of getHeadCommitHash. Avoids blocking the event loop.\n */\nexport async function getHeadCommitHashAsync(workspacePath: string): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns a list of files that have been modified (tracked and untracked)\n * in the workspace. This is best-effort and may not capture all changes.\n */\nexport function getTouchedFiles(workspacePath: string): string[] {\n try {\n const result = execFileSync('git', ['status', '--porcelain'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n\n if (!result.trim()) {\n return [];\n }\n\n return result\n .trim()\n .split('\\n')\n .map((line) => line.substring(3).trim())\n .filter((file) => file.length > 0 && !file.endsWith('/'));\n } catch {\n return [];\n }\n}\n","import type { BriefingTier } from './types';\n\n/**\n * Known model-to-tier mappings. Expandable as new models ship.\n * Keys are normalized (lowercased, trimmed).\n */\nconst MODEL_TIERS: Record<string, BriefingTier> = {\n // Standard: small/fast models (compact is only reachable via explicit tier or context window)\n 'claude-3-haiku': 'standard',\n 'claude-3.5-haiku': 'standard',\n 'claude-haiku-4-5': 'standard',\n 'gpt-4o-mini': 'standard',\n 'gemini-flash': 'standard',\n // Detailed: mid-tier models\n 'claude-3.5-sonnet': 'detailed',\n 'claude-sonnet-4': 'detailed',\n 'claude-sonnet-4-5': 'detailed',\n 'claude-sonnet-4-6': 'detailed',\n 'gpt-4o': 'detailed',\n 'gemini-pro': 'detailed',\n // Full: large context models\n 'claude-opus-4': 'full',\n 'claude-opus-4-5': 'full',\n 'claude-opus-4-6': 'full',\n 'o1': 'full',\n 'o3': 'full',\n 'gemini-ultra': 'full',\n};\n\n/**\n * Resolves the briefing tier from explicit override, model name, or context window.\n *\n * Priority chain:\n * 1. Explicit `tier` param (manual override)\n * 2. Model name lookup in built-in database\n * 3. Context window heuristic\n * 4. Default: `standard`\n */\nexport function resolveTier(opts?: {\n tier?: BriefingTier;\n model?: string;\n contextWindow?: number;\n}): BriefingTier {\n if (opts?.tier) {\n return opts.tier;\n }\n\n if (opts?.model) {\n const fromModel = tierFromModelName(opts.model);\n if (fromModel) return fromModel;\n }\n\n if (opts?.contextWindow !== undefined) {\n return tierFromContextWindow(opts.contextWindow);\n }\n\n return 'standard';\n}\n\n/**\n * Looks up a model name in the built-in tier database.\n * Performs fuzzy matching: normalizes to lowercase and tries progressively\n * shorter prefixes to match versioned model IDs (e.g. \"claude-opus-4-20250514\").\n */\nexport function tierFromModelName(model: string): BriefingTier | undefined {\n const normalized = model.toLowerCase().trim();\n\n // Direct match\n if (MODEL_TIERS[normalized]) {\n return MODEL_TIERS[normalized];\n }\n\n // Try stripping date suffixes and version segments (e.g. \"-20250514\", \"-latest\").\n // Sort keys longest-first so \"claude-sonnet-4-5\" matches before \"claude-sonnet-4\".\n const entries = Object.entries(MODEL_TIERS).sort((a, b) => b[0].length - a[0].length);\n for (const [key, tier] of entries) {\n if (normalized.startsWith(key)) {\n return tier;\n }\n }\n\n return undefined;\n}\n\n/**\n * Maps a context window size (in tokens) to a briefing tier.\n *\n * <16K -> compact\n * <64K -> standard\n * <200K -> detailed\n * 200K+ -> full\n */\nexport function tierFromContextWindow(tokens: number): BriefingTier {\n if (tokens < 16_000) return 'compact';\n if (tokens < 64_000) return 'standard';\n if (tokens < 200_000) return 'detailed';\n return 'full';\n}\n","import type {\n SessionCheckpoint,\n ProjectState,\n ReEntryBriefing,\n DecisionRecord,\n BriefingTier,\n EnrichedBriefing,\n} from './types';\nimport { formatRelativeTime } from './timeUtils';\nimport { resolveTier } from './briefingTier';\n\nconst RECENT_SESSION_COUNT = 5;\n\n/**\n * Generates a synthesized re-entry briefing from stored project data.\n *\n * Uses heuristic rules (no AI) to infer focus and suggest next steps.\n * Designed to be replaced by an LLM-backed implementation in the future\n * while keeping the same input/output contract.\n */\nexport function generateBriefing(\n lastSession: SessionCheckpoint | undefined,\n recentSessions: SessionCheckpoint[],\n projectState: ProjectState,\n gitBranch?: string,\n recentCommitMessages?: string[],\n): ReEntryBriefing | undefined {\n if (!lastSession) {\n return undefined;\n }\n\n return {\n lastWorked: formatRelativeTime(lastSession.timestamp),\n currentFocus: buildCurrentFocus(lastSession, projectState, gitBranch),\n recentActivity: buildRecentActivity(\n lastSession,\n recentSessions,\n recentCommitMessages,\n ),\n suggestedNext: buildSuggestedNext(lastSession, gitBranch),\n smallNextStep: buildSmallNextStep(\n lastSession,\n gitBranch,\n recentCommitMessages,\n ),\n };\n}\n\n/**\n * Returns the most recent N sessions in newest-first order.\n */\nexport function getRecentSessions(\n allSessions: SessionCheckpoint[],\n count: number = RECENT_SESSION_COUNT,\n): SessionCheckpoint[] {\n return allSessions.slice(-count).reverse();\n}\n\n/**\n * Options for generating a tier-aware enriched briefing.\n */\nexport interface EnrichedBriefingOpts {\n // Tier resolution (priority: tier > model > contextWindow > default 'standard')\n tier?: BriefingTier;\n model?: string;\n contextWindow?: number;\n\n // Core briefing inputs (same as generateBriefing)\n lastSession: SessionCheckpoint | undefined;\n recentSessions: SessionCheckpoint[];\n projectState: ProjectState;\n gitBranch?: string;\n recentCommits?: string[];\n\n // Enrichment data: pass what you have, the function trims by tier\n decisions?: DecisionRecord[];\n allTouchedFiles?: string[];\n allSessions?: SessionCheckpoint[];\n fileConflicts?: Array<{ file: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }>;\n branchOverlaps?: Array<{ branch: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }>;\n isWorktree?: boolean;\n}\n\n/**\n * Generates a tier-aware enriched briefing.\n *\n * Calls `generateBriefing()` for the core fields, then selectively populates\n * enrichment fields based on the resolved tier:\n *\n * - compact (~150 tokens): lastWorked + currentFocus + smallNextStep\n * - standard (~400 tokens): all 5 core fields + blocker\n * - detailed (~800 tokens): standard + decisions(3) + touchedFiles(10) + session count\n * - full (~1500 tokens): detailed + sessionHistory(5) + all decisions(10) + all files + commits + cross-session intelligence\n */\nexport function generateEnrichedBriefing(opts: EnrichedBriefingOpts): EnrichedBriefing | undefined {\n const tier = resolveTier({\n tier: opts.tier,\n model: opts.model,\n contextWindow: opts.contextWindow,\n });\n\n const core = generateBriefing(\n opts.lastSession,\n opts.recentSessions,\n opts.projectState,\n opts.gitBranch,\n opts.recentCommits,\n );\n\n if (!core) return undefined;\n\n const enriched: EnrichedBriefing = { tier, core };\n\n // compact: just core (renderer will pick 3 fields)\n if (tier === 'compact') {\n return enriched;\n }\n\n // standard+: blocker, branch, worktree\n enriched.blocker = opts.lastSession?.blocker;\n enriched.gitBranch = opts.gitBranch;\n enriched.isWorktree = opts.isWorktree;\n\n if (tier === 'standard') {\n return enriched;\n }\n\n // detailed+: decisions (3), touched files (10)\n if (opts.decisions && opts.decisions.length > 0) {\n enriched.decisions = opts.decisions.slice(0, tier === 'detailed' ? 3 : 10);\n }\n\n if (opts.allTouchedFiles && opts.allTouchedFiles.length > 0) {\n enriched.touchedFiles = tier === 'detailed'\n ? opts.allTouchedFiles.slice(0, 10)\n : opts.allTouchedFiles;\n } else if (opts.lastSession?.touchedFiles && opts.lastSession.touchedFiles.length > 0) {\n enriched.touchedFiles = tier === 'detailed'\n ? opts.lastSession.touchedFiles.slice(0, 10)\n : opts.lastSession.touchedFiles;\n }\n\n if (tier === 'detailed') {\n return enriched;\n }\n\n // full: session history, all commits, cross-session intelligence\n if (opts.allSessions && opts.allSessions.length > 0) {\n const recent = opts.allSessions.slice(-5).reverse();\n enriched.sessionHistory = recent.map(s => ({\n timestamp: s.timestamp,\n summary: s.summary || '',\n nextStep: s.nextStep || '',\n branch: s.gitBranch,\n }));\n }\n\n if (opts.recentCommits && opts.recentCommits.length > 0) {\n enriched.recentCommits = opts.recentCommits;\n }\n\n if (opts.fileConflicts && opts.fileConflicts.length > 0) {\n enriched.fileConflicts = opts.fileConflicts;\n }\n\n if (opts.branchOverlaps && opts.branchOverlaps.length > 0) {\n enriched.branchOverlaps = opts.branchOverlaps;\n }\n\n return enriched;\n}\n\nfunction buildCurrentFocus(\n lastSession: SessionCheckpoint,\n projectState: ProjectState,\n gitBranch?: string,\n): string {\n if (projectState.derivedCurrentFocus) {\n return projectState.derivedCurrentFocus;\n }\n\n const branchFocus = inferFocusFromBranch(gitBranch);\n if (branchFocus) {\n return branchFocus;\n }\n\n if (lastSession.summary) {\n return lastSession.summary;\n }\n\n if (lastSession.touchedFiles.length > 0) {\n return inferFocusFromFiles(lastSession.touchedFiles);\n }\n\n return 'Unknown, save a checkpoint to set context';\n}\n\nfunction buildRecentActivity(\n lastSession: SessionCheckpoint,\n recentSessions: SessionCheckpoint[],\n recentCommitMessages?: string[],\n): string {\n const parts: string[] = [];\n\n const sessionCount = recentSessions.length;\n if (sessionCount > 1) {\n parts.push(`${sessionCount} recent sessions`);\n } else if (sessionCount === 1) {\n parts.push('1 recent session');\n }\n\n if (lastSession.summary) {\n const brief = lastSession.summary.length > 120\n ? lastSession.summary.slice(0, 117) + '...'\n : lastSession.summary;\n parts.push(`Last: ${brief}`);\n }\n\n if (lastSession.touchedFiles.length > 0) {\n parts.push(`${lastSession.touchedFiles.length} files touched`);\n }\n\n if (recentCommitMessages && recentCommitMessages.length > 0) {\n parts.push(`${recentCommitMessages.length} recent commits`);\n }\n\n return parts.length > 0 ? parts.join('. ') : 'No recent activity recorded';\n}\n\nfunction buildSuggestedNext(\n lastSession: SessionCheckpoint,\n gitBranch?: string,\n): string {\n if (lastSession.nextStep) {\n return lastSession.nextStep;\n }\n\n const branchFocus = inferFocusFromBranch(gitBranch);\n if (branchFocus) {\n return `Continue working on ${branchFocus}`;\n }\n\n if (lastSession.touchedFiles.length > 0) {\n return `Continue working on ${inferFocusFromFiles(lastSession.touchedFiles)}`;\n }\n\n return 'Save a checkpoint to track your next step';\n}\n\nfunction buildSmallNextStep(\n lastSession: SessionCheckpoint,\n gitBranch?: string,\n recentCommitMessages?: string[],\n): string {\n const fallback = 'Review last changed files to resume flow';\n\n if (lastSession.nextStep) {\n const distilled = distillToSmallStep(\n lastSession.nextStep,\n lastSession.touchedFiles,\n );\n if (distilled) {\n return distilled;\n }\n }\n\n if (recentCommitMessages && recentCommitMessages.length > 0) {\n const commitStep = deriveStepFromCommits(recentCommitMessages);\n if (commitStep) {\n return commitStep;\n }\n }\n\n if (lastSession.touchedFiles.length > 0) {\n const fileStep = deriveStepFromFiles(lastSession.touchedFiles);\n if (fileStep) {\n return fileStep;\n }\n }\n\n const branchFocus = inferFocusFromBranch(gitBranch);\n if (branchFocus) {\n return `Check git status for ${branchFocus}`;\n }\n\n return fallback;\n}\n\nfunction distillToSmallStep(\n nextStep: string,\n touchedFiles: string[],\n): string | undefined {\n if (!nextStep.trim()) {\n return undefined;\n }\n\n const words = nextStep.trim().split(/\\s+/);\n if (words.length <= 12) {\n if (touchedFiles.length > 0 && !mentionsFile(nextStep)) {\n const primaryFile = getPrimaryFileName(touchedFiles);\n const enhanced = `${nextStep.trim()} in ${primaryFile}`;\n if (enhanced.split(/\\s+/).length <= 12) {\n return enhanced;\n }\n }\n return nextStep.trim();\n }\n\n return words.slice(0, 12).join(' ');\n}\n\nfunction deriveStepFromCommits(\n commitMessages: string[],\n): string | undefined {\n const lastCommit = commitMessages[0];\n if (!lastCommit || !lastCommit.trim()) {\n return undefined;\n }\n\n const wipPattern =\n /^(?:wip|work in progress|started?|begin|draft)[:\\s]/i;\n if (wipPattern.test(lastCommit)) {\n const topic = lastCommit.replace(wipPattern, '').trim();\n if (topic) {\n const words = topic.split(/\\s+/).slice(0, 8).join(' ');\n return `Continue ${words}`;\n }\n }\n\n return undefined;\n}\n\nfunction deriveStepFromFiles(files: string[]): string | undefined {\n const primaryFile = getPrimaryFileName(files);\n\n if (files.length > 1) {\n return `Open ${primaryFile} and review ${files.length} changed files`;\n }\n\n return `Open ${primaryFile} and pick up where you left off`;\n}\n\nfunction getPrimaryFileName(files: string[]): string {\n const sourceFiles = files.filter((f) => {\n const lower = f.toLowerCase();\n return (\n !lower.includes('test') &&\n !lower.includes('spec') &&\n !lower.includes('.config') &&\n !lower.includes('package.json') &&\n !lower.includes('tsconfig')\n );\n });\n\n const target = sourceFiles.length > 0 ? sourceFiles[0] : files[0];\n const parts = target.replace(/\\\\/g, '/').split('/');\n return parts[parts.length - 1];\n}\n\nfunction mentionsFile(text: string): boolean {\n return /\\w+\\.(?:ts|tsx|js|jsx|py|go|rs|java|rb|css|scss|html|json|yaml|yml|md|sql|sh)\\b/i.test(\n text,\n );\n}\n\nfunction inferFocusFromBranch(branch?: string): string | undefined {\n if (\n !branch ||\n branch === 'main' ||\n branch === 'master' ||\n branch === 'develop' ||\n branch === 'HEAD'\n ) {\n return undefined;\n }\n\n const prefixPattern =\n /^(?:feature|feat|fix|bugfix|hotfix|chore|refactor|docs|test|ci)\\//i;\n const isFix = /^(?:fix|bugfix|hotfix)\\//i.test(branch);\n const stripped = branch.replace(prefixPattern, '');\n\n const cleaned = stripped\n .replace(/[-_/]/g, ' ')\n .replace(/^\\d+\\s*/, '')\n .trim();\n\n if (!cleaned) {\n return undefined;\n }\n\n return isFix ? `${cleaned} fix` : cleaned;\n}\n\nfunction inferFocusFromFiles(files: string[]): string {\n if (files.length === 0) {\n return 'unknown files';\n }\n\n const dirs = files\n .map((f) => {\n const parts = f.replace(/\\\\/g, '/').split('/');\n return parts.length > 1 ? parts.slice(0, -1).join('/') : '';\n })\n .filter((d) => d.length > 0);\n\n if (dirs.length > 0) {\n const counts = new Map<string, number>();\n for (const dir of dirs) {\n counts.set(dir, (counts.get(dir) ?? 0) + 1);\n }\n let topDir = '';\n let topCount = 0;\n for (const [dir, count] of counts) {\n if (count > topCount) {\n topDir = dir;\n topCount = count;\n }\n }\n if (topDir) {\n return `files in ${topDir}`;\n }\n }\n\n const names = files.slice(0, 3).map((f) => {\n const parts = f.replace(/\\\\/g, '/').split('/');\n return parts[parts.length - 1];\n });\n return names.join(', ');\n}\n","import type { EnrichedBriefing } from './types';\nimport { formatRelativeTime } from './timeUtils';\n\n/**\n * Formats an enriched briefing into markdown.\n * Sections are controlled by which fields are present (already tier-gated\n * by `generateEnrichedBriefing()`).\n */\nexport function formatEnrichedBriefing(briefing: EnrichedBriefing): string {\n const { tier, core } = briefing;\n const lines: string[] = [];\n\n // Header\n lines.push('## Re-entry Briefing');\n lines.push('');\n\n // Worktree context\n if (briefing.isWorktree && briefing.gitBranch) {\n lines.push(`**Worktree context:** Scoped to branch \\`${briefing.gitBranch}\\``);\n lines.push('');\n }\n\n // Compact: minimal 3-field output\n if (tier === 'compact') {\n lines.push(`**Last worked:** ${core.lastWorked}`);\n lines.push(`**Focus:** ${core.currentFocus}`);\n lines.push(`**Quick start:** ${core.smallNextStep}`);\n return lines.join('\\n');\n }\n\n // Standard+: all 5 core fields\n lines.push(`**Last worked:** ${core.lastWorked}`);\n lines.push(`**Current focus:** ${core.currentFocus}`);\n lines.push(`**Recent activity:** ${core.recentActivity}`);\n lines.push(`**Suggested next:** ${core.suggestedNext}`);\n lines.push(`**Quick start:** ${core.smallNextStep}`);\n\n if (briefing.blocker) {\n lines.push(`**Blocker:** ${briefing.blocker}`);\n }\n\n // Detailed+: decisions\n if (briefing.decisions && briefing.decisions.length > 0) {\n lines.push('');\n lines.push('### Recent decisions');\n for (const d of briefing.decisions) {\n const rationale = d.rationale ? ` - ${d.rationale}` : '';\n lines.push(`- **${d.classification.category}:** ${d.commitMessage}${rationale}`);\n }\n }\n\n // Detailed+: touched files\n if (briefing.touchedFiles && briefing.touchedFiles.length > 0) {\n lines.push('');\n lines.push(`### Files touched (${briefing.touchedFiles.length})`);\n for (const f of briefing.touchedFiles) {\n lines.push(`- ${f}`);\n }\n }\n\n // Full: session history\n if (briefing.sessionHistory && briefing.sessionHistory.length > 0) {\n lines.push('');\n lines.push('### Session history');\n for (const s of briefing.sessionHistory) {\n const relTime = formatRelativeTime(s.timestamp);\n const branch = s.branch ? ` (${s.branch})` : '';\n lines.push(`- **${relTime}${branch}:** ${s.summary || 'No summary'}. Next: ${s.nextStep || 'Not specified'}`);\n }\n }\n\n // Full: recent commits\n if (briefing.recentCommits && briefing.recentCommits.length > 0) {\n lines.push('');\n lines.push('### Recent commits');\n for (const msg of briefing.recentCommits.slice(0, 10)) {\n lines.push(`- ${msg}`);\n }\n }\n\n // Full: cross-session intelligence\n if (briefing.fileConflicts && briefing.fileConflicts.length > 0) {\n lines.push('');\n lines.push('### File conflicts');\n lines.push('Multiple sessions are editing the same files:');\n for (const c of briefing.fileConflicts) {\n const sessionLabels = c.sessions.map(s => s.agentLabel || s.sessionId).join(', ');\n lines.push(`- \\`${c.file}\\`: ${sessionLabels}`);\n }\n }\n\n if (briefing.branchOverlaps && briefing.branchOverlaps.length > 0) {\n lines.push('');\n lines.push('### Branch overlaps');\n lines.push('Multiple sessions on the same branch:');\n for (const o of briefing.branchOverlaps) {\n const sessionLabels = o.sessions.map(s => s.agentLabel || s.sessionId).join(', ');\n lines.push(`- \\`${o.branch}\\`: ${sessionLabels}`);\n }\n }\n\n return lines.join('\\n');\n}\n","import type {\n SessionCheckpoint,\n ReEntryBriefing,\n DecisionRecord,\n CurrentTask,\n} from './types';\nimport { KeepGoingReader } from './reader';\nimport { generateBriefing } from './reentry';\nimport { formatRelativeTime } from './timeUtils';\nimport { getCommitMessagesSince } from './gitUtils';\nimport path from 'node:path';\n\n/**\n * All context gathered from KeepGoing for export to another AI tool.\n */\nexport interface ContinueOnContext {\n projectName: string;\n gitBranch?: string;\n briefing?: ReEntryBriefing;\n lastCheckpoint?: SessionCheckpoint;\n recentCheckpoints: SessionCheckpoint[];\n recentDecisions: DecisionRecord[];\n currentTasks: CurrentTask[];\n recentCommitMessages: string[];\n}\n\n/**\n * Options controlling how the prompt is formatted.\n */\nexport interface FormatOptions {\n target?: 'chatgpt' | 'gemini' | 'copilot' | 'claude' | 'general';\n includeCommits?: boolean;\n includeFiles?: boolean;\n maxLength?: number;\n}\n\n/**\n * Gathers all available KeepGoing context for a workspace.\n * Uses existing KeepGoingReader methods (worktree-aware).\n */\nexport function gatherContinueOnContext(\n reader: KeepGoingReader,\n workspacePath: string,\n): ContinueOnContext {\n const projectName = path.basename(workspacePath);\n const gitBranch = reader.getCurrentBranch();\n\n if (!reader.exists()) {\n return {\n projectName,\n gitBranch,\n recentCheckpoints: [],\n recentDecisions: [],\n currentTasks: [],\n recentCommitMessages: [],\n };\n }\n\n const { session: lastCheckpoint } = reader.getScopedLastSession();\n const recentCheckpoints = reader.getScopedRecentSessions(5);\n const recentDecisions = reader.getScopedRecentDecisions(3);\n const currentTasks = reader.getCurrentTasks();\n const state = reader.getState() ?? {};\n\n const sinceTimestamp = lastCheckpoint?.timestamp;\n const recentCommitMessages = sinceTimestamp\n ? getCommitMessagesSince(workspacePath, sinceTimestamp)\n : [];\n\n const briefing = generateBriefing(\n lastCheckpoint,\n recentCheckpoints,\n state,\n gitBranch,\n recentCommitMessages,\n );\n\n return {\n projectName,\n gitBranch,\n briefing,\n lastCheckpoint,\n recentCheckpoints,\n recentDecisions,\n currentTasks,\n recentCommitMessages,\n };\n}\n\n/**\n * Formats gathered context into a markdown prompt suitable for pasting\n * into any AI chat tool (ChatGPT, Gemini, Claude, Copilot, etc.).\n */\nexport function formatContinueOnPrompt(\n context: ContinueOnContext,\n options?: FormatOptions,\n): string {\n const opts: FormatOptions = { includeCommits: true, includeFiles: true, ...options };\n const lines: string[] = [];\n\n lines.push(`# Project Context: ${context.projectName}`);\n lines.push('');\n lines.push('I\\'m continuing work on a project. Here is my development context from KeepGoing.');\n\n // Current Status\n lines.push('');\n lines.push('## Current Status');\n if (context.gitBranch) {\n lines.push(`- **Branch:** ${context.gitBranch}`);\n }\n if (context.briefing) {\n lines.push(`- **Last worked:** ${context.briefing.lastWorked}`);\n lines.push(`- **Focus:** ${context.briefing.currentFocus}`);\n } else if (context.lastCheckpoint) {\n lines.push(`- **Last worked:** ${formatRelativeTime(context.lastCheckpoint.timestamp)}`);\n if (context.lastCheckpoint.summary) {\n lines.push(`- **Focus:** ${context.lastCheckpoint.summary}`);\n }\n }\n\n // Last Session\n if (context.lastCheckpoint) {\n lines.push('');\n lines.push('## Last Session');\n if (context.lastCheckpoint.summary) {\n lines.push(`- **Summary:** ${context.lastCheckpoint.summary}`);\n }\n if (context.lastCheckpoint.nextStep) {\n lines.push(`- **Next step:** ${context.lastCheckpoint.nextStep}`);\n }\n if (context.lastCheckpoint.blocker) {\n lines.push(`- **Blocker:** ${context.lastCheckpoint.blocker}`);\n }\n if (opts.includeFiles && context.lastCheckpoint.touchedFiles.length > 0) {\n const files = context.lastCheckpoint.touchedFiles.slice(0, 10).join(', ');\n const extra = context.lastCheckpoint.touchedFiles.length > 10\n ? ` (+${context.lastCheckpoint.touchedFiles.length - 10} more)`\n : '';\n lines.push(`- **Files touched:** ${files}${extra}`);\n }\n }\n\n // Active sessions\n const activeTasks = context.currentTasks.filter(t => t.sessionActive);\n if (activeTasks.length > 0) {\n lines.push('');\n lines.push('## Active Sessions');\n for (const task of activeTasks) {\n const label = task.agentLabel || 'Unknown agent';\n const branch = task.branch ? ` on \\`${task.branch}\\`` : '';\n lines.push(`- **${label}**${branch}: ${task.taskSummary || 'Working'}`);\n }\n }\n\n // Recent decisions\n if (context.recentDecisions.length > 0) {\n lines.push('');\n lines.push('## Recent Decisions');\n for (const d of context.recentDecisions) {\n lines.push(`- ${d.classification.category}: ${d.commitMessage}`);\n }\n }\n\n // Recent commits\n if (opts.includeCommits && context.recentCommitMessages.length > 0) {\n lines.push('');\n lines.push('## Recent Commits');\n const commits = context.recentCommitMessages.slice(0, 10);\n for (const msg of commits) {\n lines.push(`- ${msg}`);\n }\n }\n\n // CTA\n lines.push('');\n lines.push('---');\n const nextStep = context.lastCheckpoint?.nextStep\n || context.briefing?.suggestedNext\n || 'continue where I left off';\n lines.push(`Please help me continue this work. My next step is: ${nextStep}.`);\n\n let result = lines.join('\\n');\n\n // Rough trim if maxLength is set\n if (opts.maxLength && result.length > opts.maxLength) {\n result = result.slice(0, opts.maxLength - 3) + '...';\n }\n\n return result;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { randomUUID, createHash } from 'node:crypto';\nimport type { SessionCheckpoint, ProjectSessions, ProjectState, ProjectMeta, CurrentTask, CurrentTasks } from './types';\nimport { resolveStorageRoot } from './gitUtils';\nimport { registerProject } from './registry';\n\nconst STORAGE_DIR = '.keepgoing';\nconst META_FILE = 'meta.json';\nconst SESSIONS_FILE = 'sessions.json';\nconst STATE_FILE = 'state.json';\nconst CURRENT_TASKS_FILE = 'current-tasks.json';\n\n/** Stale threshold: sessions inactive for longer than this are pruned on read. */\nexport const STALE_SESSION_MS = 2 * 60 * 60 * 1000; // 2 hours\n\n/** Filter out stale sessions (active or finished) inactive longer than STALE_SESSION_MS. */\nexport function pruneStaleTasks(tasks: CurrentTask[]): CurrentTask[] {\n const now = Date.now();\n return tasks.filter(t => {\n const updatedAt = new Date(t.updatedAt).getTime();\n return !isNaN(updatedAt) && (now - updatedAt) < STALE_SESSION_MS;\n });\n}\n\n/**\n * Write layer for .keepgoing/ directory.\n * Creates files if they don't exist yet.\n */\nexport class KeepGoingWriter {\n private readonly storagePath: string;\n private readonly sessionsFilePath: string;\n private readonly stateFilePath: string;\n private readonly metaFilePath: string;\n private readonly currentTasksFilePath: string;\n\n constructor(workspacePath: string) {\n const mainRoot = resolveStorageRoot(workspacePath);\n this.storagePath = path.join(mainRoot, STORAGE_DIR);\n this.sessionsFilePath = path.join(this.storagePath, SESSIONS_FILE);\n this.stateFilePath = path.join(this.storagePath, STATE_FILE);\n this.metaFilePath = path.join(this.storagePath, META_FILE);\n this.currentTasksFilePath = path.join(this.storagePath, CURRENT_TASKS_FILE);\n }\n\n ensureDir(): void {\n if (!fs.existsSync(this.storagePath)) {\n fs.mkdirSync(this.storagePath, { recursive: true });\n }\n }\n\n saveCheckpoint(checkpoint: SessionCheckpoint, projectName: string): void {\n this.ensureDir();\n\n // Read existing sessions\n let sessionsData: ProjectSessions;\n try {\n if (fs.existsSync(this.sessionsFilePath)) {\n const raw = JSON.parse(fs.readFileSync(this.sessionsFilePath, 'utf-8')) as\n | ProjectSessions\n | SessionCheckpoint[];\n if (Array.isArray(raw)) {\n sessionsData = { version: 1, project: projectName, sessions: raw };\n } else {\n sessionsData = raw;\n }\n } else {\n sessionsData = { version: 1, project: projectName, sessions: [] };\n }\n } catch {\n sessionsData = { version: 1, project: projectName, sessions: [] };\n }\n\n sessionsData.sessions.push(checkpoint);\n sessionsData.lastSessionId = checkpoint.id;\n\n // Prune old sessions to keep the file bounded\n const MAX_SESSIONS = 200;\n if (sessionsData.sessions.length > MAX_SESSIONS) {\n sessionsData.sessions = sessionsData.sessions.slice(-MAX_SESSIONS);\n }\n\n fs.writeFileSync(this.sessionsFilePath, JSON.stringify(sessionsData, null, 2), 'utf-8');\n\n // Update state.json\n const state: ProjectState = {\n lastSessionId: checkpoint.id,\n lastKnownBranch: checkpoint.gitBranch,\n lastActivityAt: checkpoint.timestamp,\n };\n fs.writeFileSync(this.stateFilePath, JSON.stringify(state, null, 2), 'utf-8');\n\n this.updateMeta(checkpoint.timestamp);\n\n // Register project in global known-projects registry\n const mainRoot = resolveStorageRoot(this.storagePath);\n registerProject(mainRoot, projectName);\n }\n\n private updateMeta(timestamp: string): void {\n let meta: ProjectMeta;\n try {\n if (fs.existsSync(this.metaFilePath)) {\n meta = JSON.parse(fs.readFileSync(this.metaFilePath, 'utf-8')) as ProjectMeta;\n meta.lastUpdated = timestamp;\n } else {\n meta = {\n projectId: randomUUID(),\n createdAt: timestamp,\n lastUpdated: timestamp,\n };\n }\n } catch {\n meta = {\n projectId: randomUUID(),\n createdAt: timestamp,\n lastUpdated: timestamp,\n };\n }\n fs.writeFileSync(this.metaFilePath, JSON.stringify(meta, null, 2), 'utf-8');\n }\n\n // ---------------------------------------------------------------------------\n // Multi-session API\n // ---------------------------------------------------------------------------\n\n /** Read all current tasks from current-tasks.json. Auto-prunes stale sessions. */\n readCurrentTasks(): CurrentTask[] {\n try {\n if (fs.existsSync(this.currentTasksFilePath)) {\n const raw = JSON.parse(fs.readFileSync(this.currentTasksFilePath, 'utf-8')) as CurrentTasks | CurrentTask[];\n const tasks = Array.isArray(raw) ? raw : (raw.tasks ?? []);\n return this.pruneStale(tasks);\n }\n } catch {\n // Fall through\n }\n\n return [];\n }\n\n /**\n * Upsert a session task by sessionId into current-tasks.json.\n * If no sessionId is present on the task, generates one.\n */\n upsertSession(update: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string }): void {\n this.ensureDir();\n this.upsertSessionCore(update);\n\n // Register project in global known-projects registry\n const mainRoot = resolveStorageRoot(this.storagePath);\n registerProject(mainRoot);\n }\n\n /** Core upsert logic: merges the update into current-tasks.json and returns the pruned task list. */\n private upsertSessionCore(update: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string }): CurrentTask[] {\n this.ensureDir();\n\n const sessionId = update.sessionId || generateSessionId(update);\n const tasks = this.readAllTasksRaw();\n\n const existingIdx = tasks.findIndex(t => t.sessionId === sessionId);\n let merged: CurrentTask;\n\n if (existingIdx >= 0) {\n const existing = tasks[existingIdx];\n merged = { ...existing, ...update, sessionId } as CurrentTask;\n tasks[existingIdx] = merged;\n } else {\n merged = { ...update, sessionId } as CurrentTask;\n tasks.push(merged);\n }\n\n const pruned = this.pruneStale(tasks);\n this.writeTasksFile(pruned);\n return pruned;\n }\n\n /** Remove a specific session by ID. */\n removeSession(sessionId: string): void {\n const tasks = this.readAllTasksRaw().filter(t => t.sessionId !== sessionId);\n this.writeTasksFile(tasks);\n }\n\n /** Get all active sessions (sessionActive=true and within stale threshold). */\n getActiveSessions(): CurrentTask[] {\n return this.readCurrentTasks().filter(t => t.sessionActive);\n }\n\n /** Get a specific session by ID. */\n getSession(sessionId: string): CurrentTask | undefined {\n return this.readCurrentTasks().find(t => t.sessionId === sessionId);\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private readAllTasksRaw(): CurrentTask[] {\n try {\n if (fs.existsSync(this.currentTasksFilePath)) {\n const raw = JSON.parse(fs.readFileSync(this.currentTasksFilePath, 'utf-8')) as CurrentTasks | CurrentTask[];\n return Array.isArray(raw) ? [...raw] : [...(raw.tasks ?? [])];\n }\n } catch {\n // Fall through\n }\n return [];\n }\n\n private pruneStale(tasks: CurrentTask[]): CurrentTask[] {\n return pruneStaleTasks(tasks);\n }\n\n private writeTasksFile(tasks: CurrentTask[]): void {\n const data: CurrentTasks = { version: 1, tasks };\n fs.writeFileSync(this.currentTasksFilePath, JSON.stringify(data, null, 2), 'utf-8');\n }\n}\n\n/**\n * Generates a stable session ID from task context.\n * Combines worktree path, agent label, and branch into a deterministic hash.\n * Falls back to a random UUID if no context is available.\n */\nexport function generateSessionId(context: Partial<CurrentTask>): string {\n const parts = [\n context.worktreePath || context.workspaceRoot || '',\n context.agentLabel || '',\n context.branch || '',\n ].filter(Boolean);\n\n if (parts.length === 0) {\n return randomUUID();\n }\n\n const hash = createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 12);\n return `ses_${hash}`;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface KnownProject {\n path: string;\n name: string;\n lastSeen: string;\n}\n\nexport interface KnownProjectsFile {\n version: number;\n projects: KnownProject[];\n}\n\nconst KEEPGOING_DIR = path.join(os.homedir(), '.keepgoing');\nconst KNOWN_PROJECTS_FILE = path.join(KEEPGOING_DIR, 'known-projects.json');\n\n/** Projects not seen for 90 days are pruned. */\nconst STALE_PROJECT_MS = 90 * 24 * 60 * 60 * 1000;\n\nfunction readKnownProjects(): KnownProjectsFile {\n try {\n if (fs.existsSync(KNOWN_PROJECTS_FILE)) {\n const raw = JSON.parse(fs.readFileSync(KNOWN_PROJECTS_FILE, 'utf-8'));\n if (raw && Array.isArray(raw.projects)) {\n return raw as KnownProjectsFile;\n }\n }\n } catch {\n // Corrupted file, start fresh\n }\n return { version: 1, projects: [] };\n}\n\nfunction writeKnownProjects(data: KnownProjectsFile): void {\n if (!fs.existsSync(KEEPGOING_DIR)) {\n fs.mkdirSync(KEEPGOING_DIR, { recursive: true });\n }\n // Atomic write: temp file + rename\n const tmpFile = KNOWN_PROJECTS_FILE + '.tmp';\n fs.writeFileSync(tmpFile, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n fs.renameSync(tmpFile, KNOWN_PROJECTS_FILE);\n}\n\n/**\n * Register a project in ~/.keepgoing/known-projects.json.\n * Upserts by path, updates lastSeen, prunes stale entries.\n * Safe to call frequently; silently swallows errors.\n */\nexport function registerProject(projectPath: string, projectName?: string): void {\n try {\n const data = readKnownProjects();\n const now = new Date().toISOString();\n const name = projectName || path.basename(projectPath);\n\n const existingIdx = data.projects.findIndex(p => p.path === projectPath);\n if (existingIdx >= 0) {\n data.projects[existingIdx].lastSeen = now;\n if (projectName) {\n data.projects[existingIdx].name = name;\n }\n } else {\n data.projects.push({ path: projectPath, name, lastSeen: now });\n }\n\n // Prune stale projects (not seen for 90 days)\n const cutoff = Date.now() - STALE_PROJECT_MS;\n data.projects = data.projects.filter(p => new Date(p.lastSeen).getTime() > cutoff);\n\n writeKnownProjects(data);\n } catch {\n // Never fail the caller over registry writes\n }\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport type { SessionEvents, SessionCheckpoint, ProjectSessions } from './types';\nimport { resolveStorageRoot } from './gitUtils';\nimport { getCommitsSince, getCommitMessagesSince, getTouchedFiles, getCurrentBranch, getFilesChangedInCommit } from './gitUtils';\nimport { createCheckpoint } from './session';\nimport { KeepGoingWriter } from './storage';\n\n/** Map of conventional commit prefixes to past-tense verbs. */\nconst PREFIX_VERBS: Record<string, string> = {\n feat: 'Added',\n fix: 'Fixed',\n refactor: 'Refactored',\n docs: 'Updated docs for',\n test: 'Added tests for',\n chore: 'Updated',\n style: 'Styled',\n perf: 'Optimized',\n ci: 'Updated CI for',\n build: 'Updated build for',\n revert: 'Reverted',\n};\n\n/** Files/directories to ignore when inferring work areas. */\nconst NOISE_PATTERNS = [\n 'node_modules',\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n '.gitignore',\n '.DS_Store',\n 'dist/',\n 'out/',\n 'build/',\n];\n\n/**\n * Parses conventional commit messages into groups by prefix.\n * Non-conventional messages go into the \"other\" bucket.\n *\n * Returns a map of prefix -> array of cleaned messages (scope stripped).\n */\nexport function categorizeCommits(messages: string[]): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n\n for (const msg of messages) {\n const match = msg.match(/^(\\w+)(?:\\([^)]*\\))?[!]?:\\s*(.+)/);\n if (match) {\n const prefix = match[1].toLowerCase();\n const body = match[2].trim();\n if (!groups.has(prefix)) {\n groups.set(prefix, []);\n }\n groups.get(prefix)!.push(body);\n } else {\n if (!groups.has('other')) {\n groups.set('other', []);\n }\n groups.get('other')!.push(msg.trim());\n }\n }\n\n return groups;\n}\n\n/**\n * Groups files by top-level directory, monorepo-aware.\n * Returns human-readable area names like \"extension\", \"shared storage\", \"web\".\n */\nexport function inferWorkAreas(files: string[]): string[] {\n const areas = new Map<string, number>();\n\n for (const file of files) {\n // Skip noise\n if (NOISE_PATTERNS.some(p => file.includes(p))) {\n continue;\n }\n\n const parts = file.split('/').filter(Boolean);\n let area: string;\n\n // Monorepo-aware: apps/X or packages/X -> use X\n if (parts.length >= 2 && (parts[0] === 'apps' || parts[0] === 'packages')) {\n area = parts[1];\n // Add sub-area for deeper paths in packages (e.g. \"shared/storage\")\n if (parts[0] === 'packages' && parts.length >= 4 && parts[2] === 'src') {\n const subFile = parts[3].replace(/\\.\\w+$/, '');\n area = `${parts[1]} ${subFile}`;\n }\n } else if (parts.length >= 2) {\n area = parts[0];\n } else {\n area = 'root';\n }\n\n areas.set(area, (areas.get(area) ?? 0) + 1);\n }\n\n // Sort by file count descending, return top 3\n return [...areas.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map(([name]) => name);\n}\n\n/**\n * Builds a SessionEvents object from raw git data.\n *\n * Encapsulates the common pattern of mapping commit hashes to full commit\n * objects (with per-commit file lists), computing the committed-file set,\n * and deriving `hasUncommittedChanges`.\n */\nexport function buildSessionEvents(opts: {\n wsPath: string;\n commitHashes: string[];\n commitMessages: string[];\n touchedFiles: string[];\n currentBranch?: string;\n sessionStartTime: string;\n lastActivityTime: string;\n}): SessionEvents {\n const { wsPath, commitHashes, commitMessages, touchedFiles, currentBranch, sessionStartTime, lastActivityTime } = opts;\n const commits = commitHashes.map((hash, i) => ({\n hash,\n message: commitMessages[i] ?? '',\n filesChanged: getFilesChangedInCommit(wsPath, hash),\n timestamp: new Date().toISOString(),\n }));\n const committedFiles = new Set(commits.flatMap(c => c.filesChanged));\n return {\n commits,\n branchSwitches: [],\n touchedFiles,\n currentBranch,\n sessionStartTime,\n lastActivityTime,\n // Normalize rename arrows (\"old -> new\") from git status --porcelain\n // so they match the plain filenames from git diff-tree --name-only.\n hasUncommittedChanges: touchedFiles.some(f => {\n const normalized = f.includes(' -> ') ? f.split(' -> ').pop()! : f;\n return !committedFiles.has(normalized);\n }),\n };\n}\n\n/**\n * Builds a smart summary from accumulated session events.\n *\n * Groups commits by conventional prefix, maps to past-tense verbs,\n * and combines into a readable sentence. Falls back to file-area inference\n * when there are no commits.\n *\n * Returns undefined if nothing meaningful happened (skip checkpoint).\n */\nexport function buildSmartSummary(events: SessionEvents): string | undefined {\n const { commits, branchSwitches, touchedFiles, hasUncommittedChanges } = events;\n\n if (commits.length === 0 && touchedFiles.length === 0 && branchSwitches.length === 0) {\n return undefined;\n }\n\n const parts: string[] = [];\n\n if (commits.length > 0) {\n const messages = commits.map(c => c.message);\n const groups = categorizeCommits(messages);\n\n // Build summary phrases from each group\n const phrases: string[] = [];\n for (const [prefix, bodies] of groups) {\n const verb = PREFIX_VERBS[prefix] ?? (prefix === 'other' ? '' : `${capitalize(prefix)}:`);\n // Take up to 2 items per group\n const items = bodies.slice(0, 2).join(' and ');\n const overflow = bodies.length > 2 ? ` (+${bodies.length - 2} more)` : '';\n if (verb) {\n phrases.push(`${verb} ${items}${overflow}`);\n } else {\n phrases.push(`${items}${overflow}`);\n }\n }\n\n parts.push(phrases.join(', '));\n } else if (touchedFiles.length > 0) {\n // No commits, describe file work\n const areas = inferWorkAreas(touchedFiles);\n const areaStr = areas.length > 0 ? areas.join(' and ') : `${touchedFiles.length} files`;\n const suffix = hasUncommittedChanges ? ' (uncommitted)' : '';\n parts.push(`Worked on ${areaStr}${suffix}`);\n }\n\n // Mention branch switches if they occurred\n if (branchSwitches.length > 0) {\n const last = branchSwitches[branchSwitches.length - 1];\n if (branchSwitches.length === 1) {\n parts.push(`switched to ${last.toBranch}`);\n } else {\n parts.push(`switched branches ${branchSwitches.length} times, ended on ${last.toBranch}`);\n }\n }\n\n const result = parts.join('; ');\n return result || undefined;\n}\n\n/**\n * Builds a smart next-step suggestion from accumulated session events.\n *\n * Priority chain:\n * 1. Uncommitted changes -> \"Review and commit changes in [area]\"\n * 2. Last commit is WIP -> \"Continue [topic]\"\n * 3. Feature branch -> \"Continue [feature name from branch]\"\n * 4. Fallback -> \"Review recent changes in [area]\"\n */\nexport function buildSmartNextStep(events: SessionEvents): string {\n const { commits, touchedFiles, currentBranch, hasUncommittedChanges } = events;\n\n // 1. Uncommitted changes\n if (hasUncommittedChanges && touchedFiles.length > 0) {\n const areas = inferWorkAreas(touchedFiles);\n const areaStr = areas.length > 0 ? areas.join(' and ') : 'working tree';\n return `Review and commit changes in ${areaStr}`;\n }\n\n // 2. Last commit is WIP\n if (commits.length > 0) {\n const lastMsg = commits[commits.length - 1].message;\n const wipMatch = lastMsg.match(/^(?:wip|work in progress|start(?:ed)?|begin|draft)[:\\s]+(.+)/i);\n if (wipMatch) {\n return `Continue ${wipMatch[1].trim()}`;\n }\n }\n\n // 3. Feature branch\n if (currentBranch && !['main', 'master', 'develop', 'HEAD'].includes(currentBranch)) {\n const branchName = currentBranch\n .replace(/^(feat|feature|fix|bugfix|hotfix|chore|refactor)[/-]/i, '')\n .replace(/[-_]/g, ' ')\n .trim();\n if (branchName) {\n return `Continue ${branchName}`;\n }\n }\n\n // 4. Fallback with areas\n if (touchedFiles.length > 0) {\n const areas = inferWorkAreas(touchedFiles);\n if (areas.length > 0) {\n return `Review recent changes in ${areas.join(' and ')}`;\n }\n }\n\n return '';\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n// ---------------------------------------------------------------------------\n// Missed checkpoint recovery\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the last checkpoint from the .keepgoing/sessions.json file.\n * Standalone reader to avoid circular dependencies with storage consumers.\n */\nfunction readLastCheckpoint(wsPath: string): SessionCheckpoint | undefined {\n try {\n const storageRoot = resolveStorageRoot(wsPath);\n const sessionsPath = path.join(storageRoot, '.keepgoing', 'sessions.json');\n if (!fs.existsSync(sessionsPath)) {\n return undefined;\n }\n const raw = JSON.parse(fs.readFileSync(sessionsPath, 'utf-8')) as ProjectSessions | SessionCheckpoint[];\n const sessions = Array.isArray(raw) ? raw : raw.sessions;\n return sessions.length > 0 ? sessions[sessions.length - 1] : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Checks for commits since the last checkpoint that were never recorded\n * (e.g. due to a crash, force-kill, or unclean exit) and writes a\n * recovery checkpoint if needed.\n *\n * Safe to call on every startup. Returns the recovery checkpoint if one\n * was written, or undefined if no recovery was needed.\n */\nexport function recoverMissedCheckpoint(wsPath: string): SessionCheckpoint | undefined {\n try {\n const lastCheckpoint = readLastCheckpoint(wsPath);\n const sinceTimestamp = lastCheckpoint?.timestamp;\n const commitHashes = getCommitsSince(wsPath, sinceTimestamp);\n\n if (commitHashes.length === 0) {\n return undefined;\n }\n\n // Check if these commits were already captured by the last checkpoint\n if (lastCheckpoint?.commitHashes) {\n const captured = new Set(lastCheckpoint.commitHashes);\n const uncaptured = commitHashes.filter(h => !captured.has(h));\n if (uncaptured.length === 0) {\n return undefined;\n }\n }\n\n const commitMessages = getCommitMessagesSince(wsPath, sinceTimestamp);\n const touchedFiles = getTouchedFiles(wsPath);\n const gitBranch = getCurrentBranch(wsPath);\n\n const events = buildSessionEvents({\n wsPath,\n commitHashes,\n commitMessages,\n touchedFiles,\n currentBranch: gitBranch ?? undefined,\n sessionStartTime: sinceTimestamp ?? new Date().toISOString(),\n lastActivityTime: new Date().toISOString(),\n });\n\n const summary = buildSmartSummary(events);\n if (!summary) {\n return undefined;\n }\n\n const nextStep = buildSmartNextStep(events);\n const checkpoint = createCheckpoint({\n summary,\n nextStep,\n gitBranch,\n touchedFiles,\n workspaceRoot: wsPath,\n source: 'auto',\n tags: ['recovery'],\n commitHashes,\n });\n\n const storageRoot = resolveStorageRoot(wsPath);\n const projectName = path.basename(storageRoot);\n const writer = new KeepGoingWriter(wsPath);\n writer.saveCheckpoint(checkpoint, projectName);\n\n console.log(`[KeepGoing] Recovery checkpoint saved: ${summary} (${commitHashes.length} missed commits)`);\n return checkpoint;\n } catch (err) {\n console.error('[KeepGoing] Recovery checkpoint failed:', err);\n return undefined;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { ProjectDecisions, DecisionRecord } from './types';\nimport { createEmptyProjectDecisions } from './session';\nimport { isDecisionsEnabled } from './featureGate';\nimport { resolveStorageRoot } from './gitUtils';\n\nconst STORAGE_DIR = '.keepgoing';\nconst DECISIONS_FILE = 'decisions.json';\nconst MAX_DECISIONS = 100;\n\n/**\n * Storage layer for persisting decision records as JSON files\n * within a .keepgoing/ folder in the workspace.\n *\n * saveDecision() always persists (captures drafts for all users).\n * updateDecision() is gated behind the 'decisions' feature flag (Pro perk).\n * Read operations are always available.\n */\nexport class DecisionStorage {\n private readonly storagePath: string;\n private readonly decisionsFilePath: string;\n\n constructor(workspacePath: string) {\n const mainRoot = resolveStorageRoot(workspacePath);\n this.storagePath = path.join(mainRoot, STORAGE_DIR);\n this.decisionsFilePath = path.join(this.storagePath, DECISIONS_FILE);\n }\n\n private ensureStorageDir(): void {\n if (!fs.existsSync(this.storagePath)) {\n fs.mkdirSync(this.storagePath, { recursive: true });\n }\n }\n\n private getProjectName(): string {\n return path.basename(path.dirname(this.storagePath));\n }\n\n private load(): ProjectDecisions {\n try {\n if (!fs.existsSync(this.decisionsFilePath)) {\n return createEmptyProjectDecisions(this.getProjectName());\n }\n const raw = fs.readFileSync(this.decisionsFilePath, 'utf-8');\n const data = JSON.parse(raw) as ProjectDecisions;\n return data;\n } catch {\n return createEmptyProjectDecisions(this.getProjectName());\n }\n }\n\n private save(decisions: ProjectDecisions): void {\n this.ensureStorageDir();\n const content = JSON.stringify(decisions, null, 2);\n fs.writeFileSync(this.decisionsFilePath, content, 'utf-8');\n }\n\n /**\n * Save a decision record as a draft. Always persists regardless of Pro\n * status so decisions are captured at the correct time. Returns true if\n * saved, false on I/O error.\n */\n saveDecision(decision: DecisionRecord): boolean {\n const data = this.load();\n data.decisions.push(decision);\n data.lastDecisionId = decision.id;\n\n if (data.decisions.length > MAX_DECISIONS) {\n data.decisions = data.decisions.slice(-MAX_DECISIONS);\n }\n\n this.save(data);\n return true;\n }\n\n getLastDecision(): DecisionRecord | undefined {\n const data = this.load();\n if (data.decisions.length === 0) {\n return undefined;\n }\n if (data.lastDecisionId) {\n const found = data.decisions.find(d => d.id === data.lastDecisionId);\n if (found) {\n return found;\n }\n }\n return data.decisions[data.decisions.length - 1];\n }\n\n getAllDecisions(): DecisionRecord[] {\n const data = this.load();\n return data.decisions;\n }\n\n getRecentDecisions(limit: number = 10): DecisionRecord[] {\n const data = this.load();\n return data.decisions.slice(-limit).reverse();\n }\n\n /**\n * Update a decision record. Returns true if updated, false if gated or not found.\n */\n updateDecision(id: string, updates: Partial<Omit<DecisionRecord, 'id'>>): boolean {\n if (!isDecisionsEnabled()) {\n return false;\n }\n\n const data = this.load();\n const index = data.decisions.findIndex(d => d.id === id);\n if (index === -1) {\n return false;\n }\n\n data.decisions[index] = {\n ...data.decisions[index],\n ...updates,\n };\n this.save(data);\n return true;\n }\n\n getStoragePath(): string {\n return this.storagePath;\n }\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { GatedFeature } from './featureGate';\n\nconst LICENSE_FILE = 'license.json';\nconst DEVICE_ID_FILE = 'device-id';\n\n/**\n * Directory for device-wide license storage: `~/.keepgoing/`\n */\nexport function getGlobalLicenseDir(): string {\n return path.join(os.homedir(), '.keepgoing');\n}\n\n/**\n * Full path to the global license cache: `~/.keepgoing/license.json`\n */\nexport function getGlobalLicensePath(): string {\n return path.join(getGlobalLicenseDir(), LICENSE_FILE);\n}\n\n/**\n * Returns a stable, persistent device identifier shared by all consumers\n * (VS Code extension, CLI, MCP server) so each physical machine counts\n * as one activation.\n *\n * The ID is a random UUID stored in `~/.keepgoing/device-id`. Once\n * generated it never changes, even if the hostname or OS is updated.\n */\nexport function getDeviceId(): string {\n const dir = getGlobalLicenseDir();\n const filePath = path.join(dir, DEVICE_ID_FILE);\n\n try {\n const existing = fs.readFileSync(filePath, 'utf-8').trim();\n if (existing) return existing;\n } catch {\n // File doesn't exist yet, generate below\n }\n\n const id = crypto.randomUUID();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, id, 'utf-8');\n return id;\n}\n\nexport type LicenseStatus = 'active' | 'inactive' | 'expired' | 'disabled';\n\n// ---------------------------------------------------------------------------\n// Variant-to-feature mapping\n// ---------------------------------------------------------------------------\n\n// Live store variant IDs\nexport const DECISION_DETECTION_VARIANT_ID = 1361527;\nexport const SESSION_AWARENESS_VARIANT_ID = 1366510;\n\n// Test store variant IDs\nconst TEST_DECISION_DETECTION_VARIANT_ID = 1345647;\nconst TEST_SESSION_AWARENESS_VARIANT_ID = 1365992;\n\n/**\n * Returns the numeric variant ID for the checkout `?enabled=` param.\n * Uses test store IDs in dev mode, live IDs in production.\n */\nexport function getCheckoutVariantId(feature: 'decisions' | 'session-awareness', isDev: boolean): number {\n if (feature === 'decisions') {\n return isDev ? TEST_DECISION_DETECTION_VARIANT_ID : DECISION_DETECTION_VARIANT_ID;\n }\n return isDev ? TEST_SESSION_AWARENESS_VARIANT_ID : SESSION_AWARENESS_VARIANT_ID;\n}\n\n/**\n * Maps each LemonSqueezy variant_id to the features it unlocks.\n * Single source of truth shared by all consumers.\n * Includes both live and test store variants.\n */\nexport const VARIANT_FEATURE_MAP: Record<number, GatedFeature[]> = {\n [DECISION_DETECTION_VARIANT_ID]: ['decisions'],\n [SESSION_AWARENESS_VARIANT_ID]: ['session-awareness'],\n [TEST_DECISION_DETECTION_VARIANT_ID]: ['decisions'],\n [TEST_SESSION_AWARENESS_VARIANT_ID]: ['session-awareness'],\n // Future bundle: [BUNDLE_VARIANT_ID]: ['decisions', 'session-awareness'],\n};\n\n/** Set of all known variant IDs for validation. */\nexport const KNOWN_VARIANT_IDS = new Set(Object.keys(VARIANT_FEATURE_MAP).map(Number));\n\n/**\n * Returns a human-readable label for a variant ID.\n */\nexport function getVariantLabel(variantId: number): string {\n const features = VARIANT_FEATURE_MAP[variantId];\n if (!features) return 'Unknown Add-on';\n if (features.includes('decisions') && features.includes('session-awareness')) return 'Pro Bundle';\n if (features.includes('decisions')) return 'Decision Detection';\n if (features.includes('session-awareness')) return 'Session Awareness';\n return 'Pro Add-on';\n}\n\n// ---------------------------------------------------------------------------\n// Multi-license storage\n// ---------------------------------------------------------------------------\n\nexport interface LicenseEntry {\n licenseKey: string;\n instanceId: string;\n status: LicenseStatus;\n lastValidatedAt: string; // ISO 8601\n activatedAt: string; // ISO 8601\n variantId: number;\n customerName?: string;\n productName?: string;\n variantName?: string;\n}\n\nexport interface LicenseStore {\n version: 2;\n licenses: LicenseEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Multi-license read/write\n// ---------------------------------------------------------------------------\n\n// Short-lived in-memory cache to avoid redundant disk reads within the same\n// call chain (e.g. getLicenseForFeature -> getActiveLicenses -> readLicenseStore).\nlet _cachedStore: LicenseStore | undefined;\nlet _cacheTimestamp = 0;\nconst LICENSE_CACHE_TTL_MS = 2_000; // 2 seconds\n\n/**\n * Read the license store from `~/.keepgoing/license.json`.\n * Uses a 2-second in-memory cache to coalesce repeated reads.\n * Returns an empty store if file is missing or corrupt.\n */\nexport function readLicenseStore(): LicenseStore {\n const now = Date.now();\n if (_cachedStore && now - _cacheTimestamp < LICENSE_CACHE_TTL_MS) {\n return _cachedStore;\n }\n\n const licensePath = getGlobalLicensePath();\n let store: LicenseStore;\n try {\n if (!fs.existsSync(licensePath)) {\n store = { version: 2, licenses: [] };\n } else {\n const raw = fs.readFileSync(licensePath, 'utf-8');\n const data = JSON.parse(raw);\n\n if (data?.version === 2 && Array.isArray(data.licenses)) {\n store = data as LicenseStore;\n } else {\n store = { version: 2, licenses: [] };\n }\n }\n } catch {\n store = { version: 2, licenses: [] };\n }\n\n _cachedStore = store;\n _cacheTimestamp = now;\n return store;\n}\n\n/**\n * Write the license store to `~/.keepgoing/license.json`.\n */\nexport function writeLicenseStore(store: LicenseStore): void {\n const dirPath = getGlobalLicenseDir();\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true });\n }\n const licensePath = path.join(dirPath, LICENSE_FILE);\n fs.writeFileSync(licensePath, JSON.stringify(store, null, 2), 'utf-8');\n // Invalidate read cache so the next read picks up the write immediately\n _cachedStore = store;\n _cacheTimestamp = Date.now();\n}\n\n/**\n * Add or update a license entry (upserts by licenseKey).\n */\nexport function addLicenseEntry(entry: LicenseEntry): void {\n const store = readLicenseStore();\n const idx = store.licenses.findIndex(l => l.licenseKey === entry.licenseKey);\n if (idx >= 0) {\n store.licenses[idx] = entry;\n } else {\n store.licenses.push(entry);\n }\n writeLicenseStore(store);\n}\n\n/**\n * Remove a license entry by licenseKey.\n */\nexport function removeLicenseEntry(licenseKey: string): void {\n const store = readLicenseStore();\n store.licenses = store.licenses.filter(l => l.licenseKey !== licenseKey);\n writeLicenseStore(store);\n}\n\n/**\n * Get all active license entries.\n */\nexport function getActiveLicenses(): LicenseEntry[] {\n return readLicenseStore().licenses.filter(l => l.status === 'active');\n}\n\n/**\n * Find the first active license whose variant maps to the given feature.\n */\nexport function getLicenseForFeature(feature: GatedFeature): LicenseEntry | undefined {\n const active = getActiveLicenses();\n return active.find(l => {\n const features = VARIANT_FEATURE_MAP[l.variantId];\n return features?.includes(feature);\n });\n}\n\n/**\n * Check if any license is currently active.\n */\nexport function isAnyLicenseActive(): boolean {\n return getActiveLicenses().length > 0;\n}\n\n/**\n * Get all active licenses that need revalidation (last validated > 24h ago).\n */\nexport function getAllLicensesNeedingRevalidation(): LicenseEntry[] {\n return getActiveLicenses().filter(l => needsRevalidation(l));\n}\n\nconst REVALIDATION_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * Check if a license entry needs revalidation (last validated > 24 hours ago).\n */\nexport function needsRevalidation(entry: LicenseEntry): boolean {\n const lastValidated = new Date(entry.lastValidatedAt).getTime();\n return Date.now() - lastValidated > REVALIDATION_THRESHOLD_MS;\n}\n","import type { LicenseStore } from './license';\nimport { VARIANT_FEATURE_MAP } from './license';\n\nexport type GatedFeature = 'decisions' | 'session-awareness';\n\nexport interface FeatureGate {\n isEnabled(feature: GatedFeature): boolean;\n}\n\nclass DefaultFeatureGate implements FeatureGate {\n isEnabled(_feature: GatedFeature): boolean {\n return true; // All features enabled until license system exists\n }\n}\n\n/**\n * Feature gate controlled by license status.\n * Tracks which features are enabled via a Set of GatedFeature values,\n * derived from the active licenses and their variant mappings.\n */\nexport class LicenseFeatureGate implements FeatureGate {\n private enabledFeatures: Set<GatedFeature>;\n\n constructor(proEnabled: boolean) {\n // Legacy constructor: enable all or none\n this.enabledFeatures = proEnabled\n ? new Set<GatedFeature>(['decisions', 'session-awareness'])\n : new Set<GatedFeature>();\n }\n\n isEnabled(feature: GatedFeature): boolean {\n return this.enabledFeatures.has(feature);\n }\n\n /**\n * Recompute enabled features from all active licenses in the store.\n */\n refreshFromStore(store: LicenseStore): void {\n const features = new Set<GatedFeature>();\n for (const entry of store.licenses) {\n if (entry.status !== 'active') continue;\n const mapped = VARIANT_FEATURE_MAP[entry.variantId];\n if (mapped) {\n for (const f of mapped) features.add(f);\n }\n }\n this.enabledFeatures = features;\n }\n\n /**\n * Directly set which features are enabled.\n */\n setEnabledFeatures(features: GatedFeature[]): void {\n this.enabledFeatures = new Set(features);\n }\n\n /**\n * @deprecated Use refreshFromStore() or setEnabledFeatures() instead.\n * Enables all features (true) or clears all (false).\n */\n setProEnabled(enabled: boolean): void {\n this.enabledFeatures = enabled\n ? new Set<GatedFeature>(['decisions', 'session-awareness'])\n : new Set<GatedFeature>();\n }\n}\n\nlet currentGate: FeatureGate = new DefaultFeatureGate();\n\nexport function getFeatureGate(): FeatureGate { return currentGate; }\nexport function setFeatureGate(gate: FeatureGate): void { currentGate = gate; }\nexport function isDecisionsEnabled(): boolean { return currentGate.isEnabled('decisions'); }\nexport function isSessionAwarenessEnabled(): boolean { return currentGate.isEnabled('session-awareness'); }\n","import { DecisionClassification, DecisionCategory } from './types';\nimport { createDecisionRecord } from './session';\nimport { DecisionStorage } from './decisionStorage';\nimport { isDecisionsEnabled } from './featureGate';\n\n/**\n * Input describing a git commit for decision classification.\n */\nexport interface CommitInfo {\n /** Full commit message */\n message: string;\n\n /** List of file paths changed in the commit (relative to repo root) */\n filesChanged: string[];\n\n /**\n * Conventional commit type, if known (e.g. 'ci', 'feat').\n * If omitted, it is parsed from the message automatically.\n */\n type?: string;\n\n /**\n * Conventional commit scope, if known (e.g. 'auth', 'db').\n * If omitted, it is parsed from the message automatically.\n */\n scope?: string;\n}\n\n/** Keywords in the commit message that suggest a decision was made, matched with word boundaries. */\nconst MESSAGE_KEYWORDS: RegExp[] = [\n /\\bdeploy\\b/,\n /\\bworkflow\\b/,\n /\\bmigrate\\b/,\n /\\bmigration\\b/,\n /\\bredirect\\b/,\n /\\borigin\\b/,\n /\\btrusted\\b/,\n /\\boidc\\b/,\n /\\boauth\\b/,\n /\\bpostgres\\b/,\n /\\bsupabase\\b/,\n /\\bdocker\\b/,\n /\\bterraform\\b/,\n /\\bk8s\\b/,\n];\n\n/** Conventional commit types/scopes that indicate high-signal changes. */\nconst HIGH_SIGNAL_TYPES = new Set([\n 'ci',\n 'build',\n 'infra',\n 'ops',\n 'auth',\n 'oauth',\n 'oidc',\n 'migration',\n 'db',\n]);\n\n/** Path patterns that reduce confidence (low-signal changes). */\nconst NEGATIVE_PATH_PATTERNS = [/\\.lock$/, /generated/i, /(?:^|\\/)dist\\//];\n\ntype PathTier = 'infra' | 'contextual';\ninterface PathMatch { label: string; tier: PathTier; }\n\n/**\n * Checks whether a file path matches a high-signal pattern.\n * Returns a {@link PathMatch} with the matched label and its tier, or null if no match.\n *\n * - **infra** tier: inherently significant (CI workflows, Dockerfiles, IaC, etc.)\n * - **contextual** tier: needs corroboration from another signal to be meaningful\n */\nfunction matchHighSignalPath(filePath: string): PathMatch | null {\n // Infra tier: .github/workflows is always at repo root\n if (/^\\.github\\/workflows\\//i.test(filePath)) {\n return { label: '.github/workflows', tier: 'infra' };\n }\n // Infra tier: the rest use (?:^|\\/) to support monorepo nesting\n if (/(?:^|\\/)fly\\.toml$/i.test(filePath)) {\n return { label: 'fly.toml', tier: 'infra' };\n }\n if (/(?:^|\\/)Dockerfile/i.test(filePath)) {\n return { label: 'Dockerfile', tier: 'infra' };\n }\n if (/(?:^|\\/)docker-compose/i.test(filePath)) {\n return { label: 'docker-compose', tier: 'infra' };\n }\n if (/(?:^|\\/)terraform\\//i.test(filePath)) {\n return { label: 'terraform/', tier: 'infra' };\n }\n if (/(?:^|\\/)k8s\\//i.test(filePath)) {\n return { label: 'k8s/', tier: 'infra' };\n }\n if (/(?:^|\\/)supabase\\//i.test(filePath)) {\n return { label: 'supabase/', tier: 'infra' };\n }\n if (/(?:^|\\/)migrations?(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*migration*', tier: 'infra' };\n }\n // Contextual tier: need corroboration\n if (/(?:^|\\/)auth(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*auth*', tier: 'contextual' };\n }\n if (/(?:^|\\/)oidc(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*oidc*', tier: 'contextual' };\n }\n if (/(?:^|\\/)oauth(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*oauth*', tier: 'contextual' };\n }\n if (/(?:^|\\/)redirect(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*redirect*', tier: 'contextual' };\n }\n if (/(?:^|\\/)origin(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*origin*', tier: 'contextual' };\n }\n return null;\n}\n\n/**\n * Extracts the conventional commit type, scope, and breaking-change marker\n * from a commit message.\n * Handles formats like `feat(auth): ...`, `ci: ...`, or `feat!: ...`.\n */\nfunction parseConventionalCommit(message: string): { type?: string; scope?: string; breaking?: boolean } {\n const match = /^([a-z]+)(\\(([^)]+)\\))?\\s*(!)?:/.exec(message.trim());\n if (!match) {\n return {};\n }\n return { type: match[1], scope: match[3], breaking: match[4] === '!' };\n}\n\n/**\n * Infers a broad decision category from the signals that were matched.\n */\nfunction inferCategory(\n matchedKeywords: string[],\n matchedTypes: string[],\n matchedPaths: string[],\n): DecisionCategory {\n const all = [...matchedKeywords, ...matchedTypes, ...matchedPaths].join(' ').toLowerCase();\n if (/auth|oidc|oauth|redirect|origin|trusted/.test(all)) {\n return 'auth';\n }\n if (/migrat|postgres|db|supabase/.test(all)) {\n return 'migration';\n }\n if (/ci|workflow|build|deploy/.test(all)) {\n return 'deploy';\n }\n if (/fly|docker|k8s|terraform|infra|ops/.test(all)) {\n return 'infra';\n }\n return 'unknown';\n}\n\n/**\n * Classifies a git commit as a potential decision point using heuristic signals:\n * - Commit message keywords (deploy, migrate, oauth, etc.)\n * - Conventional commit type/scope (ci, auth, db, etc.)\n * - Changed file paths (.github/workflows, fly.toml, migrations, etc.)\n * - Negative filters (lock files, generated code, pure UI asset changes)\n *\n * Returns a classification with a confidence score (0–1) and human-readable reasons.\n */\nexport function classifyCommit(commit: CommitInfo): DecisionClassification {\n const { message, filesChanged } = commit;\n const messageLower = message.toLowerCase();\n\n // Parse conventional commit type/scope from message if not explicitly provided\n const parsed = parseConventionalCommit(message);\n const type = commit.type ?? parsed.type;\n const scope = commit.scope ?? parsed.scope;\n\n const reasons: string[] = [];\n let confidence = 0;\n\n // Signal 1: commit message keywords (word-boundary matching)\n const matchedKeywords = MESSAGE_KEYWORDS.filter(kw => kw.test(messageLower));\n if (matchedKeywords.length > 0) {\n confidence += 0.3;\n const labels = matchedKeywords.slice(0, 3).map(kw => kw.source.replace(/\\\\b/g, ''));\n reasons.push(`commit message contains: ${labels.join(', ')}`);\n }\n\n // Signal 2: conventional commit type\n const matchedTypes: string[] = [];\n if (type && HIGH_SIGNAL_TYPES.has(type)) {\n matchedTypes.push(`type:${type}`);\n confidence += 0.35;\n reasons.push(`conventional commit type '${type}' is high-signal`);\n }\n\n // Signal 3: conventional commit scope\n if (scope && HIGH_SIGNAL_TYPES.has(scope)) {\n matchedTypes.push(`scope:${scope}`);\n confidence += 0.25;\n reasons.push(`conventional commit scope '${scope}' is high-signal`);\n }\n\n // Signal 4: breaking change marker (feat!:, fix(auth)!:, etc.)\n if (parsed.breaking) {\n confidence += 0.4;\n reasons.push('breaking change indicated by ! marker');\n }\n\n // Signal 5: changed file paths (two-tier scoring)\n const matchedPaths: string[] = [];\n let bestTier: PathTier | null = null;\n for (const file of filesChanged) {\n const pm = matchHighSignalPath(file);\n if (pm && !matchedPaths.includes(pm.label)) {\n matchedPaths.push(pm.label);\n if (bestTier !== 'infra') {\n bestTier = pm.tier;\n }\n }\n }\n if (matchedPaths.length > 0) {\n // Infra paths are inherently significant (+0.40).\n // Contextual paths alone need corroboration (+0.20).\n confidence += bestTier === 'infra' ? 0.4 : 0.2;\n reasons.push(`commit touched: ${matchedPaths.slice(0, 3).join(', ')}`);\n }\n\n // Negative filter 1: all files are low-signal (lock, generated, dist)\n if (filesChanged.length > 0 && filesChanged.every(f => NEGATIVE_PATH_PATTERNS.some(p => p.test(f)))) {\n confidence -= 0.5;\n reasons.push('all changed files are low-signal (lock files, generated code, dist)');\n }\n\n // Negative filter 2: only UI asset changes (favicon, SVG, PNG)\n if (filesChanged.length > 0 && filesChanged.every(f => /favicon/i.test(f) || /\\.(svg|png|ico)$/i.test(f))) {\n confidence -= 0.5;\n reasons.push('all changed files are UI assets (favicon, SVG, PNG)');\n }\n\n // Negative filter 3: only CSS changes with Tailwind in message\n if (\n filesChanged.length > 0 &&\n filesChanged.every(f => /\\.css$/i.test(f)) &&\n /tailwind/i.test(messageLower)\n ) {\n confidence -= 0.5;\n reasons.push('Tailwind generated CSS change (low signal)');\n }\n\n const isDecisionCandidate = confidence >= 0.4;\n const keywordLabels = matchedKeywords.map(kw => kw.source.replace(/\\\\b/g, ''));\n const category = isDecisionCandidate\n ? inferCategory(keywordLabels, matchedTypes, matchedPaths)\n : 'unknown';\n\n return {\n isDecisionCandidate,\n confidence: Math.max(0, Math.min(1, confidence)),\n reasons,\n category,\n };\n}\n\n/**\n * Options for {@link tryDetectDecision}.\n */\nexport interface DetectDecisionOpts {\n workspacePath: string;\n checkpointId: string;\n gitBranch?: string;\n commitHash: string;\n commitMessage: string;\n filesChanged: string[];\n}\n\n/**\n * Result returned when a decision is detected.\n */\nexport interface DetectedDecision {\n category: DecisionCategory;\n confidence: number;\n}\n\n/**\n * Classifies a commit, and if it is a decision candidate, persists a\n * {@link DecisionRecord} via {@link DecisionStorage}.\n *\n * Returns the detected category and confidence when a decision was saved,\n * or `undefined` when the feature is gated or the commit is not a candidate.\n */\nexport function tryDetectDecision(opts: DetectDecisionOpts): DetectedDecision | undefined {\n if (!isDecisionsEnabled()) {\n return undefined;\n }\n\n const classification = classifyCommit({\n message: opts.commitMessage,\n filesChanged: opts.filesChanged,\n });\n\n if (!classification.isDecisionCandidate) {\n return undefined;\n }\n\n const decision = createDecisionRecord({\n checkpointId: opts.checkpointId,\n gitBranch: opts.gitBranch,\n commitHash: opts.commitHash,\n commitMessage: opts.commitMessage,\n filesChanged: opts.filesChanged,\n timestamp: new Date().toISOString(),\n classification,\n });\n\n const storage = new DecisionStorage(opts.workspacePath);\n storage.saveDecision(decision);\n\n return {\n category: classification.category,\n confidence: classification.confidence,\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { getRecentSessions } from './reentry';\nimport { readLicenseStore, type LicenseStore } from './license';\nimport { resolveStorageRoot, getCurrentBranch } from './gitUtils';\nimport { pruneStaleTasks } from './storage';\nimport type {\n SessionCheckpoint,\n ProjectSessions,\n ProjectState,\n ProjectMeta,\n DecisionRecord,\n ProjectDecisions,\n CurrentTask,\n CurrentTasks,\n} from './types';\n\nconst STORAGE_DIR = '.keepgoing';\nconst META_FILE = 'meta.json';\nconst SESSIONS_FILE = 'sessions.json';\nconst DECISIONS_FILE = 'decisions.json';\nconst STATE_FILE = 'state.json';\nconst CURRENT_TASKS_FILE = 'current-tasks.json';\n\n/** Result of worktree-aware branch scoping. */\nexport interface BranchScope {\n /** The branch to filter by, or undefined for all branches. */\n effectiveBranch: string | undefined;\n /** Human-readable label for output headers. */\n scopeLabel: string;\n}\n\n/**\n * Read-only reader for .keepgoing/ directory.\n * Does not write or create any files.\n */\nexport class KeepGoingReader {\n private readonly workspacePath: string;\n private readonly storagePath: string;\n private readonly metaFilePath: string;\n private readonly sessionsFilePath: string;\n private readonly decisionsFilePath: string;\n private readonly stateFilePath: string;\n private readonly currentTasksFilePath: string;\n private readonly _isWorktree: boolean;\n private _cachedBranch: string | undefined | null = null; // null = not yet resolved\n\n constructor(workspacePath: string) {\n this.workspacePath = workspacePath;\n const mainRoot = resolveStorageRoot(workspacePath);\n this._isWorktree = mainRoot !== workspacePath;\n this.storagePath = path.join(mainRoot, STORAGE_DIR);\n this.metaFilePath = path.join(this.storagePath, META_FILE);\n this.sessionsFilePath = path.join(this.storagePath, SESSIONS_FILE);\n this.decisionsFilePath = path.join(this.storagePath, DECISIONS_FILE);\n this.stateFilePath = path.join(this.storagePath, STATE_FILE);\n this.currentTasksFilePath = path.join(this.storagePath, CURRENT_TASKS_FILE);\n }\n\n /** Check if .keepgoing/ directory exists. */\n exists(): boolean {\n return fs.existsSync(this.storagePath);\n }\n\n /** Read state.json, returns undefined if missing or corrupt. */\n getState(): ProjectState | undefined {\n return this.readJsonFile<ProjectState>(this.stateFilePath);\n }\n\n /** Read meta.json, returns undefined if missing or corrupt. */\n getMeta(): ProjectMeta | undefined {\n return this.readJsonFile<ProjectMeta>(this.metaFilePath);\n }\n\n /**\n * Read sessions from sessions.json.\n * Handles both formats:\n * - Flat array: SessionCheckpoint[] (from ProjectStorage)\n * - Wrapper object: ProjectSessions (from SessionStorage)\n */\n getSessions(): SessionCheckpoint[] {\n return this.parseSessions().sessions;\n }\n\n /**\n * Get the most recent session checkpoint.\n * Uses state.lastSessionId if available, falls back to last in array.\n */\n getLastSession(): SessionCheckpoint | undefined {\n const { sessions, wrapperLastSessionId } = this.parseSessions();\n if (sessions.length === 0) {\n return undefined;\n }\n\n const state = this.getState();\n if (state?.lastSessionId) {\n const found = sessions.find((s) => s.id === state.lastSessionId);\n if (found) {\n return found;\n }\n }\n\n if (wrapperLastSessionId) {\n const found = sessions.find((s) => s.id === wrapperLastSessionId);\n if (found) {\n return found;\n }\n }\n\n return sessions[sessions.length - 1];\n }\n\n /**\n * Returns the last N sessions, newest first.\n */\n getRecentSessions(count: number): SessionCheckpoint[] {\n return getRecentSessions(this.getSessions(), count);\n }\n\n /** Read all decisions from decisions.json. */\n getDecisions(): DecisionRecord[] {\n return this.parseDecisions().decisions;\n }\n\n /** Returns the last N decisions, newest first. */\n getRecentDecisions(count: number): DecisionRecord[] {\n const all = this.getDecisions();\n return all.slice(-count).reverse();\n }\n\n /** Read the multi-license store from `~/.keepgoing/license.json`. */\n getLicenseStore(): LicenseStore {\n return readLicenseStore();\n }\n\n /**\n * Read all current tasks from current-tasks.json.\n * Automatically filters out stale finished sessions (> 2 hours).\n */\n getCurrentTasks(): CurrentTask[] {\n // Try multi-session file first\n const multiRaw = this.readJsonFile<CurrentTasks | CurrentTask[]>(this.currentTasksFilePath);\n if (multiRaw) {\n const tasks = Array.isArray(multiRaw) ? multiRaw : (multiRaw.tasks ?? []);\n return this.pruneStale(tasks);\n }\n\n return [];\n }\n\n /** Get only active sessions (sessionActive=true and within stale threshold). */\n getActiveTasks(): CurrentTask[] {\n return this.getCurrentTasks().filter(t => t.sessionActive);\n }\n\n /** Get a specific session by ID. */\n getTaskBySessionId(sessionId: string): CurrentTask | undefined {\n return this.getCurrentTasks().find(t => t.sessionId === sessionId);\n }\n\n /**\n * Detect files being edited by multiple sessions simultaneously.\n * Returns pairs of session IDs and the conflicting file paths.\n */\n detectFileConflicts(): Array<{ file: string; sessions: Array<{ sessionId: string; agentLabel?: string; branch?: string }> }> {\n const activeTasks = this.getActiveTasks();\n if (activeTasks.length < 2) return [];\n\n const fileToSessions = new Map<string, Array<{ sessionId: string; agentLabel?: string; branch?: string }>>();\n\n for (const task of activeTasks) {\n if (task.lastFileEdited && task.sessionId) {\n const existing = fileToSessions.get(task.lastFileEdited) ?? [];\n existing.push({\n sessionId: task.sessionId,\n agentLabel: task.agentLabel,\n branch: task.branch,\n });\n fileToSessions.set(task.lastFileEdited, existing);\n }\n }\n\n const conflicts: Array<{ file: string; sessions: Array<{ sessionId: string; agentLabel?: string; branch?: string }> }> = [];\n for (const [file, sessions] of fileToSessions) {\n if (sessions.length > 1) {\n conflicts.push({ file, sessions });\n }\n }\n return conflicts;\n }\n\n /**\n * Detect sessions on the same branch (possible duplicate work).\n */\n detectBranchOverlap(): Array<{ branch: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }> {\n const activeTasks = this.getActiveTasks();\n if (activeTasks.length < 2) return [];\n\n const branchToSessions = new Map<string, Array<{ sessionId: string; agentLabel?: string }>>();\n\n for (const task of activeTasks) {\n if (task.branch && task.sessionId) {\n const existing = branchToSessions.get(task.branch) ?? [];\n existing.push({ sessionId: task.sessionId, agentLabel: task.agentLabel });\n branchToSessions.set(task.branch, existing);\n }\n }\n\n const overlaps: Array<{ branch: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }> = [];\n for (const [branch, sessions] of branchToSessions) {\n if (sessions.length > 1) {\n overlaps.push({ branch, sessions });\n }\n }\n return overlaps;\n }\n\n private pruneStale(tasks: CurrentTask[]): CurrentTask[] {\n return pruneStaleTasks(tasks);\n }\n\n /** Get the last session checkpoint for a specific branch. */\n getLastSessionForBranch(branch: string): SessionCheckpoint | undefined {\n const sessions = this.getSessions().filter(s => s.gitBranch === branch);\n return sessions.length > 0 ? sessions[sessions.length - 1] : undefined;\n }\n\n /** Returns the last N sessions for a specific branch, newest first. */\n getRecentSessionsForBranch(branch: string, count: number): SessionCheckpoint[] {\n const filtered = this.getSessions().filter(s => s.gitBranch === branch);\n return filtered.slice(-count).reverse();\n }\n\n /** Returns the last N decisions for a specific branch, newest first. */\n getRecentDecisionsForBranch(branch: string, count: number): DecisionRecord[] {\n const filtered = this.getDecisions().filter(d => d.gitBranch === branch);\n return filtered.slice(-count).reverse();\n }\n\n /** Whether the workspace is inside a git worktree. */\n get isWorktree(): boolean {\n return this._isWorktree;\n }\n\n /**\n * Returns the current git branch for this workspace.\n * Lazily cached: the branch is resolved once per KeepGoingReader instance.\n */\n getCurrentBranch(): string | undefined {\n if (this._cachedBranch === null) {\n this._cachedBranch = getCurrentBranch(this.workspacePath);\n }\n return this._cachedBranch;\n }\n\n /**\n * Worktree-aware last session lookup.\n * In a worktree, scopes to the current branch with fallback to global.\n * Returns the session and whether it fell back to global.\n */\n getScopedLastSession(): { session: SessionCheckpoint | undefined; isFallback: boolean } {\n const branch = this.getCurrentBranch();\n if (this._isWorktree && branch) {\n const scoped = this.getLastSessionForBranch(branch);\n if (scoped) return { session: scoped, isFallback: false };\n return { session: this.getLastSession(), isFallback: true };\n }\n return { session: this.getLastSession(), isFallback: false };\n }\n\n /** Worktree-aware recent sessions. Scopes to current branch in a worktree. */\n getScopedRecentSessions(count: number): SessionCheckpoint[] {\n const branch = this.getCurrentBranch();\n if (this._isWorktree && branch) {\n return this.getRecentSessionsForBranch(branch, count);\n }\n return this.getRecentSessions(count);\n }\n\n /** Worktree-aware recent decisions. Scopes to current branch in a worktree. */\n getScopedRecentDecisions(count: number): DecisionRecord[] {\n const branch = this.getCurrentBranch();\n if (this._isWorktree && branch) {\n return this.getRecentDecisionsForBranch(branch, count);\n }\n return this.getRecentDecisions(count);\n }\n\n /**\n * Resolves branch scope from an explicit `branch` parameter.\n * Used by tools that accept a `branch` argument (e.g. get_session_history, get_decisions).\n * - `\"all\"` returns no filter.\n * - An explicit branch name uses that.\n * - `undefined` auto-scopes to the current branch in a worktree, or all branches otherwise.\n */\n resolveBranchScope(branch?: string): BranchScope {\n if (branch === 'all') {\n return { effectiveBranch: undefined, scopeLabel: 'all branches' };\n }\n if (branch) {\n return { effectiveBranch: branch, scopeLabel: `branch \\`${branch}\\`` };\n }\n const currentBranch = this.getCurrentBranch();\n if (this._isWorktree && currentBranch) {\n return { effectiveBranch: currentBranch, scopeLabel: `branch \\`${currentBranch}\\` (worktree)` };\n }\n return { effectiveBranch: undefined, scopeLabel: 'all branches' };\n }\n\n /**\n * Parses sessions.json once, returning both the session list\n * and the optional lastSessionId from a ProjectSessions wrapper.\n */\n private parseSessions(): { sessions: SessionCheckpoint[]; wrapperLastSessionId?: string } {\n const raw = this.readJsonFile<ProjectSessions | SessionCheckpoint[]>(\n this.sessionsFilePath,\n );\n if (!raw) {\n return { sessions: [] };\n }\n if (Array.isArray(raw)) {\n return { sessions: raw };\n }\n return { sessions: raw.sessions ?? [], wrapperLastSessionId: raw.lastSessionId };\n }\n\n private parseDecisions(): { decisions: DecisionRecord[]; lastDecisionId?: string } {\n const raw = this.readJsonFile<ProjectDecisions>(this.decisionsFilePath);\n if (!raw) {\n return { decisions: [] };\n }\n return { decisions: raw.decisions ?? [], lastDecisionId: raw.lastDecisionId };\n }\n\n private readJsonFile<T>(filePath: string): T | undefined {\n try {\n if (!fs.existsSync(filePath)) {\n return undefined;\n }\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst KEEPGOING_MARKER = '@keepgoingdev/mcp-server';\n\nexport const SESSION_START_HOOK = {\n matcher: '',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --print-momentum',\n },\n ],\n};\n\nexport const STOP_HOOK = {\n matcher: '',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --save-checkpoint',\n },\n ],\n};\n\nexport const POST_TOOL_USE_HOOK = {\n matcher: 'Edit|Write|MultiEdit',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --update-task-from-hook',\n },\n ],\n};\n\n// Stop fires on mid-conversation interrupts (Ctrl+C, abort).\n// SessionEnd fires on normal conversation end.\n// Both save a checkpoint to ensure no work is lost regardless of exit path.\n// The 2-minute dedup in saveCheckpoint prevents double writes when both fire.\nexport const SESSION_END_HOOK = {\n matcher: '',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --save-checkpoint',\n },\n ],\n};\n\n/** @deprecated Use KEEPGOING_RULES_CONTENT instead. Kept for backward compatibility. */\nexport const CLAUDE_MD_SECTION = `\n## KeepGoing\n\nAfter completing a task or meaningful piece of work, call the \\`save_checkpoint\\` MCP tool with:\n- \\`summary\\`: What you accomplished\n- \\`nextStep\\`: What should be done next\n- \\`blocker\\`: Any blocker (if applicable)\n`;\n\nexport const KEEPGOING_RULES_VERSION = 1;\n\n// Hash of KEEPGOING_RULES_CONTENT (first 8 chars of SHA-256).\n// If you change KEEPGOING_RULES_CONTENT, run `npm run shared:test` to get the new hash,\n// then update this constant AND bump KEEPGOING_RULES_VERSION.\nexport const KEEPGOING_RULES_CONTENT_HASH = '2ca205f2';\n\nexport const KEEPGOING_RULES_CONTENT = `<!-- @keepgoingdev/mcp-server v${KEEPGOING_RULES_VERSION} -->\n## KeepGoing\n\nAfter completing a task or meaningful piece of work, call the \\`save_checkpoint\\` MCP tool with:\n- \\`summary\\`: 1-2 sentences. What changed and why — no file paths, no implementation details (those are captured from git).\n- \\`nextStep\\`: What to do next\n- \\`blocker\\`: Any blocker (if applicable)\n`;\n\nfunction getRulesFileVersion(content: string): number | null {\n const match = content.match(/<!-- @keepgoingdev\\/mcp-server v(\\d+) -->/);\n return match ? parseInt(match[1], 10) : null;\n}\n\nexport const STATUSLINE_CMD = 'npx -y @keepgoingdev/mcp-server --statusline';\n\nexport interface ScopePaths {\n claudeDir: string;\n settingsPath: string;\n claudeMdPath: string;\n rulesPath: string;\n}\n\nexport interface StatuslineConfig {\n /** Check if an existing statusline command is a legacy format that needs migration. */\n isLegacy?: (command: string) => boolean;\n /** Clean up legacy statusline artifacts (e.g. old script files). */\n cleanup?: () => void;\n}\n\nexport interface SetupProjectOptions {\n workspacePath: string;\n scope?: 'project' | 'user';\n sessionHooks?: boolean;\n claudeMd?: boolean;\n /** Override the Claude config directory (user scope only). Defaults to CLAUDE_CONFIG_DIR env var or ~/.claude. */\n claudeDir?: string;\n /** Optional statusline migration helpers. */\n statusline?: StatuslineConfig;\n}\n\nexport interface SetupProjectResult {\n /** Human-readable result messages (one per action taken/skipped). */\n messages: string[];\n /** Whether any files were changed on disk. */\n changed: boolean;\n}\n\n/**\n * Detect the Claude config directory.\n * Priority: CLAUDE_CONFIG_DIR env var (inherited by MCP server subprocess from Claude Code) → ~/.claude\n */\nexport function detectClaudeDir(): string {\n return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');\n}\n\nexport function hasKeepGoingHook(hookEntries: unknown[]): boolean {\n return hookEntries.some((entry: any) =>\n entry?.hooks?.some((h: any) => typeof h?.command === 'string' && h.command.includes(KEEPGOING_MARKER)),\n );\n}\n\n/**\n * Resolve settings, CLAUDE.md, and rules file paths based on scope.\n * - \"project\": <workspacePath>/.claude/settings.json, <workspacePath>/CLAUDE.md, <workspacePath>/.claude/rules/keepgoing.md\n * - \"user\": <claudeDir>/settings.json, <claudeDir>/CLAUDE.md, <claudeDir>/rules/keepgoing.md\n * where claudeDir defaults to CLAUDE_CONFIG_DIR env var or ~/.claude\n */\nexport function resolveScopePaths(\n scope: 'project' | 'user',\n workspacePath: string,\n overrideClaudeDir?: string,\n): ScopePaths {\n if (scope === 'user') {\n const claudeDir = overrideClaudeDir || detectClaudeDir();\n return {\n claudeDir,\n settingsPath: path.join(claudeDir, 'settings.json'),\n claudeMdPath: path.join(claudeDir, 'CLAUDE.md'),\n rulesPath: path.join(claudeDir, 'rules', 'keepgoing.md'),\n };\n }\n const claudeDir = path.join(workspacePath, '.claude');\n const dotClaudeMdPath = path.join(workspacePath, '.claude', 'CLAUDE.md');\n const rootClaudeMdPath = path.join(workspacePath, 'CLAUDE.md');\n return {\n claudeDir,\n settingsPath: path.join(claudeDir, 'settings.json'),\n claudeMdPath: fs.existsSync(dotClaudeMdPath) ? dotClaudeMdPath : rootClaudeMdPath,\n rulesPath: path.join(workspacePath, '.claude', 'rules', 'keepgoing.md'),\n };\n}\n\n/**\n * Write session hooks into a settings object. Returns true if anything changed.\n */\nexport function writeHooksToSettings(settings: any): boolean {\n let changed = false;\n\n if (!settings.hooks) {\n settings.hooks = {};\n }\n\n // SessionStart\n if (!Array.isArray(settings.hooks.SessionStart)) {\n settings.hooks.SessionStart = [];\n }\n if (!hasKeepGoingHook(settings.hooks.SessionStart)) {\n settings.hooks.SessionStart.push(SESSION_START_HOOK);\n changed = true;\n }\n\n // Stop\n if (!Array.isArray(settings.hooks.Stop)) {\n settings.hooks.Stop = [];\n }\n if (!hasKeepGoingHook(settings.hooks.Stop)) {\n settings.hooks.Stop.push(STOP_HOOK);\n changed = true;\n }\n\n // PostToolUse\n if (!Array.isArray(settings.hooks.PostToolUse)) {\n settings.hooks.PostToolUse = [];\n }\n if (!hasKeepGoingHook(settings.hooks.PostToolUse)) {\n settings.hooks.PostToolUse.push(POST_TOOL_USE_HOOK);\n changed = true;\n }\n\n // SessionEnd\n if (!Array.isArray(settings.hooks.SessionEnd)) {\n settings.hooks.SessionEnd = [];\n }\n if (!hasKeepGoingHook(settings.hooks.SessionEnd)) {\n settings.hooks.SessionEnd.push(SESSION_END_HOOK);\n changed = true;\n }\n\n return changed;\n}\n\n/**\n * Check if the \"other\" scope also has KeepGoing hooks configured.\n * Returns a warning string if conflict detected, or null.\n */\nexport function checkHookConflict(scope: 'project' | 'user', workspacePath: string): string | null {\n const otherPaths = resolveScopePaths(scope === 'user' ? 'project' : 'user', workspacePath);\n\n if (!fs.existsSync(otherPaths.settingsPath)) {\n return null;\n }\n\n try {\n const otherSettings = JSON.parse(fs.readFileSync(otherPaths.settingsPath, 'utf-8'));\n const hooks = otherSettings?.hooks;\n if (!hooks) return null;\n\n const hasConflict =\n (Array.isArray(hooks.SessionStart) && hasKeepGoingHook(hooks.SessionStart)) ||\n (Array.isArray(hooks.Stop) && hasKeepGoingHook(hooks.Stop));\n\n if (hasConflict) {\n const otherScope = scope === 'user' ? 'project' : 'user';\n const otherFile = otherPaths.settingsPath;\n return `KeepGoing hooks are also configured at ${otherScope} scope (${otherFile}). ` +\n `Having hooks at both scopes may cause them to fire twice. ` +\n `Consider removing the ${otherScope}-level hooks if you want to use ${scope}-level only.`;\n }\n } catch {\n // Ignore parse errors in the other settings file\n }\n\n return null;\n}\n\n/**\n * Set up KeepGoing in a project or globally.\n * Writes session hooks to settings.json, optionally sets up statusline,\n * and writes KeepGoing instructions to .claude/rules/keepgoing.md.\n */\nexport function setupProject(options: SetupProjectOptions): SetupProjectResult {\n const {\n workspacePath,\n scope = 'project',\n sessionHooks = true,\n claudeMd = true,\n claudeDir: claudeDirOverride,\n statusline,\n } = options;\n\n const messages: string[] = [];\n let changed = false;\n const { claudeDir, settingsPath, claudeMdPath, rulesPath } = resolveScopePaths(\n scope,\n workspacePath,\n claudeDirOverride,\n );\n const scopeLabel = scope === 'user'\n ? path.join('~', path.relative(os.homedir(), claudeDir), 'settings.json').replace(/\\\\/g, '/')\n : '.claude/settings.json';\n\n let settings: any = {};\n if (fs.existsSync(settingsPath)) {\n settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n }\n\n let settingsChanged = false;\n\n // --- Session hooks ---\n if (sessionHooks) {\n const hooksChanged = writeHooksToSettings(settings);\n settingsChanged = hooksChanged;\n\n if (hooksChanged) {\n messages.push(`Session hooks: Added to ${scopeLabel}`);\n } else {\n messages.push('Session hooks: Already present, skipped');\n }\n\n const conflict = checkHookConflict(scope, workspacePath);\n if (conflict) {\n messages.push(`Warning: ${conflict}`);\n }\n }\n\n // --- Statusline (project scope only) ---\n if (scope === 'project') {\n const needsUpdate = settings.statusLine?.command\n && statusline?.isLegacy?.(settings.statusLine.command);\n\n if (!settings.statusLine || needsUpdate) {\n settings.statusLine = {\n type: 'command',\n command: STATUSLINE_CMD,\n };\n settingsChanged = true;\n messages.push(needsUpdate\n ? 'Statusline: Migrated to auto-updating npx command'\n : 'Statusline: Added to .claude/settings.json');\n } else {\n messages.push('Statusline: Already configured in settings, skipped');\n }\n\n statusline?.cleanup?.();\n }\n\n // Write settings once if anything changed\n if (settingsChanged) {\n if (!fs.existsSync(claudeDir)) {\n fs.mkdirSync(claudeDir, { recursive: true });\n }\n fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n changed = true;\n }\n\n // --- Rules file (.claude/rules/keepgoing.md) ---\n if (claudeMd) {\n const rulesDir = path.dirname(rulesPath);\n const rulesLabel = scope === 'user'\n ? path.join(path.relative(os.homedir(), path.dirname(rulesPath)), 'keepgoing.md').replace(/\\\\/g, '/')\n : '.claude/rules/keepgoing.md';\n\n if (fs.existsSync(rulesPath)) {\n const existing = fs.readFileSync(rulesPath, 'utf-8');\n const existingVersion = getRulesFileVersion(existing);\n\n if (existingVersion === null) {\n messages.push(`Rules file: Custom file found at ${rulesLabel}, skipping`);\n } else if (existingVersion >= KEEPGOING_RULES_VERSION) {\n messages.push(`Rules file: Already up to date (v${existingVersion}), skipped`);\n } else {\n if (!fs.existsSync(rulesDir)) {\n fs.mkdirSync(rulesDir, { recursive: true });\n }\n fs.writeFileSync(rulesPath, KEEPGOING_RULES_CONTENT);\n changed = true;\n messages.push(`Rules file: Updated v${existingVersion} → v${KEEPGOING_RULES_VERSION} at ${rulesLabel}`);\n }\n } else {\n // Check for migration hint: CLAUDE.md already has ## KeepGoing\n const existingClaudeMd = fs.existsSync(claudeMdPath)\n ? fs.readFileSync(claudeMdPath, 'utf-8')\n : '';\n\n if (!fs.existsSync(rulesDir)) {\n fs.mkdirSync(rulesDir, { recursive: true });\n }\n fs.writeFileSync(rulesPath, KEEPGOING_RULES_CONTENT);\n changed = true;\n\n if (existingClaudeMd.includes('## KeepGoing')) {\n const mdLabel = scope === 'user' ? '~/.claude/CLAUDE.md' : 'CLAUDE.md';\n messages.push(\n `Rules file: Created ${rulesLabel} (you can now remove the ## KeepGoing section from ${mdLabel})`,\n );\n } else {\n messages.push(`Rules file: Created ${rulesLabel}`);\n }\n }\n }\n\n return { messages, changed };\n}\n","/**\n * HTTP client for the LemonSqueezy license API.\n * Uses Node's built-in fetch (no new dependencies).\n *\n * API docs: https://docs.lemonsqueezy.com/api/license-api\n * These endpoints accept application/x-www-form-urlencoded bodies\n * and do not require an API key header.\n */\n\nimport { KNOWN_VARIANT_IDS } from './license';\n\nconst BASE_URL = 'https://api.lemonsqueezy.com/v1/licenses';\nconst REQUEST_TIMEOUT_MS = 15_000; // 15 seconds\nconst EXPECTED_STORE_ID = 301555;\nconst EXPECTED_PRODUCT_ID = 864311;\n\nfunction fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n return fetch(url, { ...init, signal: controller.signal }).finally(() => clearTimeout(timer));\n}\n\ninterface LemonSqueezyLicenseMeta {\n store_id: number;\n product_id: number;\n product_name: string;\n variant_id: number;\n variant_name: string;\n customer_id: number;\n customer_name: string;\n customer_email: string;\n}\n\ninterface LemonSqueezyInstance {\n id: string;\n name: string;\n created_at: string;\n}\n\nexport interface ActivateResult {\n valid: boolean;\n error?: string;\n licenseKey?: string;\n instanceId?: string;\n customerName?: string;\n productName?: string;\n variantId?: number;\n variantName?: string;\n}\n\nexport interface ValidateResult {\n valid: boolean;\n error?: string;\n /** True when the error was caused by a network/timeout failure, not an API rejection. */\n isNetworkError?: boolean;\n licenseKey?: string;\n customerName?: string;\n productName?: string;\n variantId?: number;\n variantName?: string;\n}\n\nexport interface LicenseClientOptions {\n allowTestMode?: boolean;\n}\n\nexport interface DeactivateResult {\n deactivated: boolean;\n error?: string;\n}\n\nfunction validateProductIdentity(meta: LemonSqueezyLicenseMeta | undefined): string | undefined {\n if (!meta) return 'License response missing product metadata.';\n if (meta.store_id !== EXPECTED_STORE_ID || meta.product_id !== EXPECTED_PRODUCT_ID) {\n return 'This license key does not belong to KeepGoing.';\n }\n return undefined;\n}\n\n/**\n * Safely parse JSON from a fetch response, returning null on failure.\n */\nasync function safeJson(res: Response): Promise<Record<string, unknown> | null> {\n try {\n const text = await res.text();\n return JSON.parse(text) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Activate a license key on a device.\n */\nexport async function activateLicense(\n licenseKey: string,\n instanceName: string,\n options?: LicenseClientOptions,\n): Promise<ActivateResult> {\n try {\n const res = await fetchWithTimeout(`${BASE_URL}/activate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ license_key: licenseKey, instance_name: instanceName }),\n });\n\n const data = await safeJson(res) as {\n activated?: boolean;\n error?: string;\n license_key?: { key: string; test_mode?: boolean };\n instance?: LemonSqueezyInstance;\n meta?: LemonSqueezyLicenseMeta;\n } | null;\n\n if (!res.ok || !data?.activated) {\n return { valid: false, error: (data?.error as string) || `Activation failed (${res.status})` };\n }\n\n if (!options?.allowTestMode && data.license_key?.test_mode) {\n // Release the activation slot we just consumed\n if (data.license_key?.key && data.instance?.id) {\n await deactivateLicense(data.license_key.key, data.instance.id);\n }\n return { valid: false, error: 'This is a test license key. Please use a production license key from your purchase confirmation.' };\n }\n\n if (!options?.allowTestMode) {\n const productError = validateProductIdentity(data.meta);\n if (productError) {\n // Release the activation slot we just consumed\n if (data.license_key?.key && data.instance?.id) {\n await deactivateLicense(data.license_key.key, data.instance.id);\n }\n return { valid: false, error: productError };\n }\n\n // Validate variant_id is a known add-on\n if (data.meta?.variant_id && !KNOWN_VARIANT_IDS.has(data.meta.variant_id)) {\n if (data.license_key?.key && data.instance?.id) {\n await deactivateLicense(data.license_key.key, data.instance.id);\n }\n return { valid: false, error: 'This license key is for an unrecognized add-on variant. Please update KeepGoing or contact support.' };\n }\n }\n\n return {\n valid: true,\n licenseKey: data.license_key?.key,\n instanceId: data.instance?.id,\n customerName: data.meta?.customer_name,\n productName: data.meta?.product_name,\n variantId: data.meta?.variant_id,\n variantName: data.meta?.variant_name,\n };\n } catch (err) {\n const message = err instanceof Error && err.name === 'AbortError'\n ? 'Request timed out. Please check your network connection and try again.'\n : err instanceof Error ? err.message : 'Network error';\n return { valid: false, error: message };\n }\n}\n\n/**\n * Validate a previously activated license key.\n */\nexport async function validateLicense(\n licenseKey: string,\n instanceId: string,\n options?: LicenseClientOptions,\n): Promise<ValidateResult> {\n try {\n const res = await fetchWithTimeout(`${BASE_URL}/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ license_key: licenseKey, instance_id: instanceId }),\n });\n\n const data = await safeJson(res) as {\n valid?: boolean;\n error?: string;\n license_key?: { key: string; test_mode?: boolean };\n meta?: LemonSqueezyLicenseMeta;\n } | null;\n\n if (!res.ok || !data?.valid) {\n return { valid: false, isNetworkError: false, error: (data?.error as string) || `Validation failed (${res.status})` };\n }\n\n if (!options?.allowTestMode && data.license_key?.test_mode) {\n return { valid: false, isNetworkError: false, error: 'This is a test license key. Please use a production license key from your purchase confirmation.' };\n }\n\n if (!options?.allowTestMode) {\n const productError = validateProductIdentity(data.meta);\n if (productError) {\n return { valid: false, isNetworkError: false, error: productError };\n }\n }\n\n return {\n valid: true,\n licenseKey: data.license_key?.key,\n customerName: data.meta?.customer_name,\n productName: data.meta?.product_name,\n variantId: data.meta?.variant_id,\n variantName: data.meta?.variant_name,\n };\n } catch (err) {\n const message = err instanceof Error && err.name === 'AbortError'\n ? 'Request timed out. Please check your network connection and try again.'\n : err instanceof Error ? err.message : 'Network error';\n return { valid: false, isNetworkError: true, error: message };\n }\n}\n\n/**\n * Deactivate a license key from a device.\n */\nexport async function deactivateLicense(\n licenseKey: string,\n instanceId: string,\n): Promise<DeactivateResult> {\n try {\n const res = await fetchWithTimeout(`${BASE_URL}/deactivate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ license_key: licenseKey, instance_id: instanceId }),\n });\n\n const data = await safeJson(res) as {\n deactivated?: boolean;\n error?: string;\n } | null;\n\n if (!res.ok || !data?.deactivated) {\n return { deactivated: false, error: (data?.error as string) || `Deactivation failed (${res.status})` };\n }\n\n return { deactivated: true };\n } catch (err) {\n const message = err instanceof Error && err.name === 'AbortError'\n ? 'Request timed out. Please check your network connection and try again.'\n : err instanceof Error ? err.message : 'Network error';\n return { deactivated: false, error: message };\n }\n}\n","/**\n * Shared license revalidation logic used by all consumers\n * (VS Code extension, CLI, MCP server).\n *\n * Extracts the TOCTOU-safe revalidation pattern from the extension's\n * LicenseService so that every consumer can verify stale licenses\n * against the LemonSqueezy API.\n */\n\nimport type { GatedFeature } from './featureGate';\nimport {\n getAllLicensesNeedingRevalidation,\n getLicenseForFeature,\n needsRevalidation,\n readLicenseStore,\n writeLicenseStore,\n} from './license';\nimport type { LicenseEntry } from './license';\nimport { validateLicense } from './licenseClient';\n\nexport interface RevalidationOptions {\n /** Allow test-mode licenses (dev environments). Default false. */\n allowTestMode?: boolean;\n}\n\nexport interface RevalidationResult {\n /** Number of licenses that were stale and checked. */\n checked: number;\n /** Number of licenses successfully revalidated (timestamp refreshed). */\n refreshed: number;\n /** Number of licenses marked inactive due to server rejection. */\n revoked: number;\n /** Number of licenses skipped due to network errors (kept cached). */\n skippedNetworkError: number;\n}\n\n/**\n * Revalidate all licenses that exceed the 24-hour staleness threshold.\n *\n * For each stale license, calls the LemonSqueezy validate API:\n * - On success: refreshes `lastValidatedAt` and optionally `customerName`\n * - On API rejection (non-network error): marks license as `inactive`\n * - On network/timeout error: keeps cached state (offline tolerance)\n *\n * Uses a TOCTOU-safe merge pattern: re-reads the store from disk before\n * writing, then merges only the changed fields.\n */\nexport async function revalidateStaleLicenses(\n options?: RevalidationOptions,\n): Promise<RevalidationResult> {\n const stale = getAllLicensesNeedingRevalidation();\n const result: RevalidationResult = { checked: stale.length, refreshed: 0, revoked: 0, skippedNetworkError: 0 };\n\n if (stale.length === 0) return result;\n\n // Collect only field-level patches. We avoid mutating stale entries\n // directly so the final write merges into a fresh read, preventing\n // TOCTOU races with concurrent writers (extension, CLI, MCP).\n const updates = new Map<string, { lastValidatedAt?: string; customerName?: string; status?: string }>();\n\n for (const entry of stale) {\n const vResult = await validateLicense(entry.licenseKey, entry.instanceId, {\n allowTestMode: options?.allowTestMode,\n });\n\n if (vResult.valid) {\n const patch: { lastValidatedAt: string; customerName?: string } = {\n lastValidatedAt: new Date().toISOString(),\n };\n if (vResult.customerName) patch.customerName = vResult.customerName;\n updates.set(entry.licenseKey, patch);\n result.refreshed++;\n } else if (vResult.isNetworkError) {\n // Offline tolerance: keep cached state\n result.skippedNetworkError++;\n } else {\n // Definitive rejection from API\n updates.set(entry.licenseKey, { status: 'inactive' });\n result.revoked++;\n }\n }\n\n if (updates.size > 0) {\n // Re-read and merge only the changed fields, preserving any concurrent writes\n const store = readLicenseStore();\n for (const [key, patch] of updates) {\n const idx = store.licenses.findIndex(l => l.licenseKey === key);\n if (idx >= 0) {\n Object.assign(store.licenses[idx], patch);\n }\n }\n writeLicenseStore(store);\n }\n\n return result;\n}\n\n/**\n * Get a license for a feature, revalidating first if stale.\n *\n * Hot path (license fresh, <24h): synchronous lookup, no HTTP call.\n * Cold path (license stale, once per 24h): one HTTP round-trip per stale license.\n *\n * Returns the license entry if active and valid, or undefined if none exists\n * or revalidation revoked it.\n */\nexport async function getLicenseForFeatureWithRevalidation(\n feature: GatedFeature,\n options?: RevalidationOptions,\n): Promise<LicenseEntry | undefined> {\n const license = getLicenseForFeature(feature);\n if (!license) return undefined;\n\n // Only revalidate if this license is stale\n if (needsRevalidation(license)) {\n await revalidateStaleLicenses(options);\n // Re-check after revalidation (may have been revoked)\n return getLicenseForFeature(feature);\n }\n\n return license;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n formatRelativeTime,\n generateEnrichedBriefing,\n formatEnrichedBriefing,\n getCommitMessagesSince,\n} from '@keepgoingdev/shared';\nimport type { BriefingTier } from '@keepgoingdev/shared';\n\nexport function registerGetMomentum(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'get_momentum',\n 'Get current developer momentum: last checkpoint, next step, blockers, and branch context. Use this to understand where the developer left off. Pass tier or model to control detail level.',\n {\n tier: z.enum(['compact', 'standard', 'detailed', 'full']).optional()\n .describe('Briefing detail level. compact (~150 tokens), standard (~400), detailed (~800), full (~1500). Default: standard.'),\n model: z.string().optional()\n .describe('Model name (e.g. \"claude-opus-4\") to auto-resolve tier. Ignored if tier is set.'),\n },\n async ({ tier, model }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found. The developer has not saved any checkpoints yet.',\n },\n ],\n };\n }\n\n // If tier or model is provided, use the enriched briefing path\n if (tier || model) {\n const gitBranch = reader.getCurrentBranch();\n const { session: lastSession } = reader.getScopedLastSession();\n const recentSessions = reader.getScopedRecentSessions(5);\n const state = reader.getState() ?? {};\n\n const sinceTimestamp = lastSession?.timestamp;\n const recentCommits = sinceTimestamp\n ? getCommitMessagesSince(workspacePath, sinceTimestamp)\n : [];\n\n const decisions = reader.getScopedRecentDecisions(10);\n const allSessions = reader.getSessions();\n const fileConflicts = reader.detectFileConflicts();\n const branchOverlaps = reader.detectBranchOverlap();\n\n const briefing = generateEnrichedBriefing({\n tier: tier as BriefingTier | undefined,\n model,\n lastSession,\n recentSessions,\n projectState: state,\n gitBranch,\n recentCommits,\n decisions,\n allTouchedFiles: lastSession?.touchedFiles,\n allSessions,\n fileConflicts,\n branchOverlaps,\n isWorktree: reader.isWorktree,\n });\n\n if (!briefing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'KeepGoing is set up but no session checkpoints exist yet.',\n },\n ],\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: formatEnrichedBriefing(briefing) }],\n };\n }\n\n // Default: existing momentum format (backward compat when no tier/model passed)\n const { session: lastSession, isFallback } = reader.getScopedLastSession();\n const currentBranch = reader.getCurrentBranch();\n\n if (!lastSession) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'KeepGoing is set up but no session checkpoints exist yet.',\n },\n ],\n };\n }\n\n const state = reader.getState();\n const branchChanged =\n lastSession.gitBranch &&\n currentBranch &&\n lastSession.gitBranch !== currentBranch;\n\n const lines: string[] = [\n `## Developer Momentum`,\n '',\n ];\n\n if (reader.isWorktree && currentBranch) {\n lines.push(`**Worktree context:** Scoped to branch \\`${currentBranch}\\``);\n if (isFallback) {\n lines.push(`**Note:** No checkpoints found for branch \\`${currentBranch}\\`. Showing last global checkpoint.`);\n }\n lines.push('');\n }\n\n lines.push(\n `**Last checkpoint:** ${formatRelativeTime(lastSession.timestamp)}`,\n `**Summary:** ${lastSession.summary || 'No summary'}`,\n `**Next step:** ${lastSession.nextStep || 'Not specified'}`,\n );\n\n if (lastSession.blocker) {\n lines.push(`**Blocker:** ${lastSession.blocker}`);\n }\n\n if (lastSession.projectIntent) {\n lines.push(`**Project intent:** ${lastSession.projectIntent}`);\n }\n\n lines.push('');\n\n if (currentBranch) {\n lines.push(`**Current branch:** ${currentBranch}`);\n }\n if (branchChanged && !reader.isWorktree) {\n lines.push(\n `**Note:** Branch changed since last checkpoint (was \\`${lastSession.gitBranch}\\`, now \\`${currentBranch}\\`)`,\n );\n }\n\n if (lastSession.touchedFiles.length > 0) {\n lines.push('');\n lines.push(\n `**Files touched (${lastSession.touchedFiles.length}):** ${lastSession.touchedFiles.slice(0, 10).join(', ')}`,\n );\n if (lastSession.touchedFiles.length > 10) {\n lines.push(\n ` ...and ${lastSession.touchedFiles.length - 10} more`,\n );\n }\n }\n\n if (state?.derivedCurrentFocus) {\n lines.push('');\n lines.push(`**Derived focus:** ${state.derivedCurrentFocus}`);\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport { formatRelativeTime } from '@keepgoingdev/shared';\n\nexport function registerGetSessionHistory(server: McpServer, reader: KeepGoingReader) {\n server.tool(\n 'get_session_history',\n 'Get recent session checkpoints. Returns a chronological list of what the developer worked on.',\n {\n limit: z.number().min(1).max(50).default(5).describe('Number of recent sessions to return (1-50, default 5)'),\n branch: z.string().optional().describe('Filter to a specific branch name, or \"all\" to show all branches. Auto-detected from worktree context by default.'),\n },\n async ({ limit, branch }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found.',\n },\n ],\n };\n }\n\n const { effectiveBranch, scopeLabel } = reader.resolveBranchScope(branch);\n\n const sessions = effectiveBranch\n ? reader.getRecentSessionsForBranch(effectiveBranch, limit)\n : reader.getRecentSessions(limit);\n\n if (sessions.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: effectiveBranch\n ? `No session checkpoints found for branch \\`${effectiveBranch}\\`. Use branch: \"all\" to see all branches.`\n : 'No session checkpoints found.',\n },\n ],\n };\n }\n\n const lines: string[] = [\n `## Session History (last ${sessions.length}, ${scopeLabel})`,\n '',\n ];\n\n for (const session of sessions) {\n lines.push(`### ${formatRelativeTime(session.timestamp)}`);\n lines.push(`- **Summary:** ${session.summary || 'No summary'}`);\n lines.push(`- **Next step:** ${session.nextStep || 'Not specified'}`);\n if (session.blocker) {\n lines.push(`- **Blocker:** ${session.blocker}`);\n }\n if (session.gitBranch) {\n lines.push(`- **Branch:** ${session.gitBranch}`);\n }\n if (session.touchedFiles.length > 0) {\n lines.push(\n `- **Files:** ${session.touchedFiles.slice(0, 5).join(', ')}${session.touchedFiles.length > 5 ? ` (+${session.touchedFiles.length - 5} more)` : ''}`,\n );\n }\n lines.push('');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n getCommitMessagesSince,\n generateEnrichedBriefing,\n formatEnrichedBriefing,\n} from '@keepgoingdev/shared';\nimport type { BriefingTier } from '@keepgoingdev/shared';\n\nexport function registerGetReentryBriefing(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'get_reentry_briefing',\n 'Get a synthesized re-entry briefing that helps a developer understand where they left off. Includes focus, recent activity, and suggested next steps. Pass tier or model to control detail level.',\n {\n tier: z.enum(['compact', 'standard', 'detailed', 'full']).optional()\n .describe('Briefing detail level. compact (~150 tokens), standard (~400), detailed (~800), full (~1500). Default: standard.'),\n model: z.string().optional()\n .describe('Model name (e.g. \"claude-opus-4\") to auto-resolve tier. Ignored if tier is set.'),\n },\n async ({ tier, model }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found. The developer has not saved any checkpoints yet.',\n },\n ],\n };\n }\n\n const gitBranch = reader.getCurrentBranch();\n const { session: lastSession } = reader.getScopedLastSession();\n const recentSessions = reader.getScopedRecentSessions(5);\n const state = reader.getState() ?? {};\n\n const sinceTimestamp = lastSession?.timestamp;\n const recentCommits = sinceTimestamp\n ? getCommitMessagesSince(workspacePath, sinceTimestamp)\n : [];\n\n const decisions = reader.getScopedRecentDecisions(10);\n const allSessions = reader.getSessions();\n const fileConflicts = reader.detectFileConflicts();\n const branchOverlaps = reader.detectBranchOverlap();\n\n const briefing = generateEnrichedBriefing({\n tier: tier as BriefingTier | undefined,\n model,\n lastSession,\n recentSessions,\n projectState: state,\n gitBranch,\n recentCommits,\n decisions,\n allTouchedFiles: lastSession?.touchedFiles,\n allSessions,\n fileConflicts,\n branchOverlaps,\n isWorktree: reader.isWorktree,\n });\n\n if (!briefing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No session data available to generate a briefing.',\n },\n ],\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: formatEnrichedBriefing(briefing) }],\n };\n },\n );\n}\n","import path from 'node:path';\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n KeepGoingWriter,\n createCheckpoint,\n getCurrentBranch,\n getTouchedFiles,\n getCommitsSince,\n getCommitMessagesSince,\n getHeadCommitHash,\n tryDetectDecision,\n resolveStorageRoot,\n generateSessionId,\n} from '@keepgoingdev/shared';\n\nexport function registerSaveCheckpoint(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'save_checkpoint',\n 'Save a development checkpoint. Call this after completing a task or meaningful piece of work, not just at end of session. Each checkpoint helps the next session (or developer) pick up exactly where you left off.',\n {\n summary: z.string().describe('What was accomplished in this session'),\n nextStep: z.string().optional().describe('What to do next'),\n blocker: z.string().optional().describe('Any blocker preventing progress'),\n },\n async ({ summary, nextStep, blocker }) => {\n const lastSession = reader.getLastSession();\n\n const gitBranch = getCurrentBranch(workspacePath);\n const touchedFiles = getTouchedFiles(workspacePath);\n const commitHashes = getCommitsSince(workspacePath, lastSession?.timestamp);\n const projectName = path.basename(resolveStorageRoot(workspacePath));\n\n const sessionId = generateSessionId({ workspaceRoot: workspacePath, branch: gitBranch ?? undefined, worktreePath: workspacePath });\n const checkpoint = createCheckpoint({\n summary,\n nextStep: nextStep || '',\n blocker,\n gitBranch,\n touchedFiles,\n commitHashes,\n workspaceRoot: workspacePath,\n source: 'manual',\n sessionId,\n });\n\n const writer = new KeepGoingWriter(workspacePath);\n writer.saveCheckpoint(checkpoint, projectName);\n\n const lines: string[] = [\n `Checkpoint saved.`,\n `- **ID:** ${checkpoint.id}`,\n `- **Branch:** ${gitBranch || 'unknown'}`,\n `- **Files tracked:** ${touchedFiles.length}`,\n `- **Commits captured:** ${commitHashes.length}`,\n ];\n\n // Decision detection\n if (commitHashes.length > 0) {\n const commitMessages = getCommitMessagesSince(workspacePath, lastSession?.timestamp);\n const headHash = getHeadCommitHash(workspacePath);\n if (commitMessages.length > 0 && headHash) {\n const detected = tryDetectDecision({\n workspacePath,\n checkpointId: checkpoint.id,\n gitBranch,\n commitHash: headHash,\n commitMessage: commitMessages[0],\n filesChanged: touchedFiles,\n });\n if (detected) {\n lines.push(`- **Decision detected:** ${detected.category} (${(detected.confidence * 100).toFixed(0)}% confidence)`);\n }\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport { formatRelativeTime, getLicenseForFeatureWithRevalidation } from '@keepgoingdev/shared';\n\nexport function registerGetDecisions(server: McpServer, reader: KeepGoingReader) {\n server.tool(\n 'get_decisions',\n 'Get recent decision records. Returns detected high-signal commits with their category, confidence, and rationale.',\n {\n limit: z.number().min(1).max(50).default(10).describe('Number of recent decisions to return (1-50, default 10)'),\n branch: z.string().optional().describe('Filter to a specific branch name, or \"all\" to show all branches. Auto-detected from worktree context by default.'),\n },\n async ({ limit, branch }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found.',\n },\n ],\n };\n }\n\n if (process.env.KEEPGOING_PRO_BYPASS !== '1' && !(await getLicenseForFeatureWithRevalidation('decisions'))) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Decision Detection requires a Pro license. Use the activate_license tool, run `keepgoing activate <key>` in your terminal, or visit https://keepgoing.dev/add-ons to purchase.',\n },\n ],\n };\n }\n\n const { effectiveBranch, scopeLabel } = reader.resolveBranchScope(branch);\n\n const decisions = effectiveBranch\n ? reader.getRecentDecisionsForBranch(effectiveBranch, limit)\n : reader.getRecentDecisions(limit);\n\n if (decisions.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: effectiveBranch\n ? `No decision records found for branch \\`${effectiveBranch}\\`. Use branch: \"all\" to see all branches.`\n : 'No decision records found.',\n },\n ],\n };\n }\n\n const lines: string[] = [\n `## Decisions (last ${decisions.length}, ${scopeLabel})`,\n '',\n ];\n\n for (const decision of decisions) {\n lines.push(`### ${decision.commitMessage}`);\n lines.push(`- **When:** ${formatRelativeTime(decision.timestamp)}`);\n lines.push(`- **Category:** ${decision.classification.category}`);\n lines.push(`- **Confidence:** ${(decision.classification.confidence * 100).toFixed(0)}%`);\n if (decision.gitBranch) {\n lines.push(`- **Branch:** ${decision.gitBranch}`);\n }\n if (decision.rationale) {\n lines.push(`- **Rationale:** ${decision.rationale}`);\n }\n if (decision.classification.reasons.length > 0) {\n lines.push(`- **Signals:** ${decision.classification.reasons.join('; ')}`);\n }\n lines.push('');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport { formatRelativeTime, getLicenseForFeatureWithRevalidation } from '@keepgoingdev/shared';\n\nexport function registerGetCurrentTask(server: McpServer, reader: KeepGoingReader) {\n server.tool(\n 'get_current_task',\n \"Get a bird's eye view of all active Claude sessions. See what each session is working on, which branch it is on, and when it last did something. Useful when running multiple parallel sessions across worktrees.\",\n {},\n async () => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found.',\n },\n ],\n };\n }\n\n if (process.env.KEEPGOING_PRO_BYPASS !== '1' && !(await getLicenseForFeatureWithRevalidation('session-awareness'))) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Session Awareness requires a license. Use the activate_license tool, run `keepgoing activate <key>` in your terminal, or visit https://keepgoing.dev/add-ons to purchase.',\n },\n ],\n };\n }\n\n const tasks = reader.getCurrentTasks();\n\n if (tasks.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No current task data found. The agent has not started writing session data yet.',\n },\n ],\n };\n }\n\n const activeTasks = tasks.filter(t => t.sessionActive);\n const finishedTasks = tasks.filter(t => !t.sessionActive);\n\n const lines: string[] = [];\n\n // Summary header\n const totalActive = activeTasks.length;\n const totalFinished = finishedTasks.length;\n if (totalActive > 0 || totalFinished > 0) {\n const parts: string[] = [];\n if (totalActive > 0) parts.push(`${totalActive} active`);\n if (totalFinished > 0) parts.push(`${totalFinished} finished`);\n lines.push(`## Live Sessions (${parts.join(', ')})`);\n lines.push('');\n }\n\n // Render each task\n for (const task of [...activeTasks, ...finishedTasks]) {\n const statusIcon = task.sessionActive ? '🟢' : '✅';\n const statusLabel = task.sessionActive ? 'Active' : 'Finished';\n const sessionLabel = task.sessionLabel || task.agentLabel || task.sessionId || 'Session';\n\n lines.push(`### ${statusIcon} ${sessionLabel} (${statusLabel})`);\n lines.push(`- **Updated:** ${formatRelativeTime(task.updatedAt)}`);\n\n if (task.branch) {\n lines.push(`- **Branch:** ${task.branch}`);\n }\n if (task.agentLabel && task.sessionLabel) {\n lines.push(`- **Agent:** ${task.agentLabel}`);\n }\n if (task.taskSummary) {\n lines.push(`- **Doing:** ${task.taskSummary}`);\n }\n if (task.lastFileEdited) {\n lines.push(`- **Last file:** ${task.lastFileEdited}`);\n }\n if (task.nextStep) {\n lines.push(`- **Next step:** ${task.nextStep}`);\n }\n lines.push('');\n }\n\n // Cross-session intelligence: file conflicts\n const conflicts = reader.detectFileConflicts();\n if (conflicts.length > 0) {\n lines.push('### ⚠️ Potential Conflicts');\n for (const conflict of conflicts) {\n const sessionLabels = conflict.sessions.map(s => s.agentLabel || s.sessionId || 'unknown').join(', ');\n lines.push(`- **${conflict.file}** is being edited by: ${sessionLabels}`);\n }\n lines.push('');\n }\n\n // Cross-session intelligence: branch overlap\n const overlaps = reader.detectBranchOverlap();\n if (overlaps.length > 0) {\n lines.push('### ℹ️ Branch Overlap');\n for (const overlap of overlaps) {\n const sessionLabels = overlap.sessions.map(s => s.agentLabel || s.sessionId || 'unknown').join(', ');\n lines.push(`- **${overlap.branch}**: ${sessionLabels} (possible duplicate work)`);\n }\n lines.push('');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport { setupProject } from '@keepgoingdev/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { isLegacyStatusline, cleanupLegacyScript } from '../cli/migrate.js';\n\nexport function registerSetupProject(server: McpServer, workspacePath: string) {\n server.tool(\n 'setup_project',\n 'Set up KeepGoing hooks and instructions. Use scope \"user\" for global setup (all projects) or \"project\" for per-project setup.',\n {\n sessionHooks: z.boolean().optional().default(true).describe('Add session hooks to settings.json'),\n claudeMd: z.boolean().optional().default(true).describe('Add KeepGoing instructions to .claude/rules/keepgoing.md'),\n scope: z.enum(['project', 'user']).optional().default('project').describe('Where to write config: \"user\" for global (~/.claude/), \"project\" for per-project (.claude/)'),\n claudeDir: z.string().optional().describe('Override the Claude config directory for user scope (defaults to CLAUDE_CONFIG_DIR env var or ~/.claude)'),\n },\n async ({ sessionHooks, claudeMd, scope, claudeDir }) => {\n const result = setupProject({\n workspacePath,\n scope,\n sessionHooks,\n claudeMd,\n claudeDir,\n statusline: {\n isLegacy: isLegacyStatusline,\n cleanup: cleanupLegacyScript,\n },\n });\n\n // Format messages as markdown for MCP consumers\n const formatted = result.messages.map(msg => {\n // Wrap the label portion in bold markdown\n return msg.replace(/^([^:]+:)/, '**$1**');\n });\n\n return {\n content: [{ type: 'text' as const, text: formatted.join('\\n') }],\n };\n },\n );\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst STATUSLINE_CMD = 'npx -y @keepgoingdev/mcp-server --statusline';\n\n/**\n * Check if a statusline command is a legacy keepgoing script that needs migration.\n * Matches the known legacy pattern: a path to keepgoing-statusline.sh copied during setup.\n */\nexport function isLegacyStatusline(command: string): boolean {\n return !command.includes('--statusline') && command.includes('keepgoing-statusline');\n}\n\n/**\n * Migrate legacy statusline (copied script) to npx command.\n * Rewrites .claude/settings.json and removes the old script file.\n * Returns a user-facing message if migration occurred, or undefined if nothing changed.\n */\nexport function migrateStatusline(wsPath: string): string | undefined {\n const settingsPath = path.join(wsPath, '.claude', 'settings.json');\n if (!fs.existsSync(settingsPath)) return undefined;\n\n try {\n const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n const cmd: string | undefined = settings.statusLine?.command;\n if (!cmd || !isLegacyStatusline(cmd)) return undefined;\n\n settings.statusLine = {\n type: 'command',\n command: STATUSLINE_CMD,\n };\n fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n\n cleanupLegacyScript();\n\n return '[KeepGoing] Migrated statusline to auto-updating command (restart Claude Code to apply)';\n } catch {\n return undefined;\n }\n}\n\n/** Remove the legacy ~/.claude/keepgoing-statusline.sh if it exists. */\nexport function cleanupLegacyScript(): void {\n const legacyScript = path.join(os.homedir(), '.claude', 'keepgoing-statusline.sh');\n if (fs.existsSync(legacyScript)) {\n try { fs.unlinkSync(legacyScript); } catch { /* ignore */ }\n }\n}\n\nexport { STATUSLINE_CMD };\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n readLicenseStore,\n addLicenseEntry,\n getDeviceId,\n getVariantLabel,\n activateLicense,\n VARIANT_FEATURE_MAP,\n} from '@keepgoingdev/shared';\n\nexport function registerActivateLicense(server: McpServer) {\n server.tool(\n 'activate_license',\n 'Activate a KeepGoing Pro license on this device. Unlocks add-ons like Decision Detection and Session Awareness.',\n { license_key: z.string().describe('Your KeepGoing Pro license key') },\n async ({ license_key }) => {\n // Check locally first to avoid consuming a remote activation slot unnecessarily\n const store = readLicenseStore();\n const existingForKey = store.licenses.find(\n l => l.status === 'active' && l.licenseKey === license_key,\n );\n if (existingForKey) {\n const label = getVariantLabel(existingForKey.variantId);\n const who = existingForKey.customerName ? ` (${existingForKey.customerName})` : '';\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} is already active${who}. No action needed.`,\n },\n ],\n };\n }\n\n const result = await activateLicense(license_key, getDeviceId());\n\n if (!result.valid) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Activation failed: ${result.error ?? 'unknown error'}`,\n },\n ],\n };\n }\n\n const variantId = result.variantId!;\n\n // Check if a different key already covers this variant\n const existingForVariant = store.licenses.find(\n l => l.status === 'active' && l.variantId === variantId,\n );\n if (existingForVariant) {\n const label = getVariantLabel(variantId);\n const who = existingForVariant.customerName ? ` (${existingForVariant.customerName})` : '';\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} is already active${who}. No action needed.`,\n },\n ],\n };\n }\n\n const now = new Date().toISOString();\n addLicenseEntry({\n licenseKey: result.licenseKey || license_key,\n instanceId: result.instanceId || getDeviceId(),\n status: 'active',\n lastValidatedAt: now,\n activatedAt: now,\n variantId,\n customerName: result.customerName,\n productName: result.productName,\n variantName: result.variantName,\n });\n\n const label = getVariantLabel(variantId);\n const features = VARIANT_FEATURE_MAP[variantId];\n const featureList = features ? features.join(', ') : 'Pro features';\n const who = result.customerName ? ` Welcome, ${result.customerName}!` : '';\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} activated successfully.${who} Enabled: ${featureList}.`,\n },\n ],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n readLicenseStore,\n removeLicenseEntry,\n getVariantLabel,\n deactivateLicense,\n} from '@keepgoingdev/shared';\n\nexport function registerDeactivateLicense(server: McpServer) {\n server.tool(\n 'deactivate_license',\n 'Deactivate the KeepGoing Pro license on this device.',\n {\n license_key: z.string().optional().describe('Specific license key to deactivate. If omitted and only one license is active, deactivates it. If multiple are active, lists them.'),\n },\n async ({ license_key }) => {\n const store = readLicenseStore();\n const activeLicenses = store.licenses.filter(l => l.status === 'active');\n\n if (activeLicenses.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No active license found on this device.',\n },\n ],\n };\n }\n\n // Determine which license to deactivate\n let target;\n if (license_key) {\n target = activeLicenses.find(l => l.licenseKey === license_key);\n if (!target) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No active license found with key \"${license_key}\".`,\n },\n ],\n };\n }\n } else if (activeLicenses.length === 1) {\n target = activeLicenses[0];\n } else {\n // Multiple active licenses, ask user to specify\n const lines = ['Multiple active licenses found. Please specify which to deactivate using the license_key parameter:', ''];\n for (const l of activeLicenses) {\n const label = getVariantLabel(l.variantId);\n const who = l.customerName ? ` (${l.customerName})` : '';\n lines.push(`- ${label}${who}: ${l.licenseKey}`);\n }\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n }\n\n const result = await deactivateLicense(target.licenseKey, target.instanceId);\n removeLicenseEntry(target.licenseKey);\n\n const label = getVariantLabel(target.variantId);\n\n if (!result.deactivated) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} license cleared locally, but remote deactivation failed: ${result.error ?? 'unknown error'}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} license deactivated successfully. The activation slot has been freed.`,\n },\n ],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n gatherContinueOnContext,\n formatContinueOnPrompt,\n} from '@keepgoingdev/shared';\nimport type { FormatOptions } from '@keepgoingdev/shared';\n\nexport function registerContinueOn(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'continue_on',\n 'Export KeepGoing context as a formatted prompt for use in another AI tool (ChatGPT, Gemini, Copilot, etc.). Returns a markdown prompt with project status, last session, decisions, and recent commits.',\n {\n target: z.enum(['chatgpt', 'gemini', 'copilot', 'claude', 'general']).optional()\n .describe('Target AI tool (currently used for future format tuning)'),\n include_commits: z.boolean().default(true)\n .describe('Include recent commit messages in the prompt'),\n include_files: z.boolean().default(true)\n .describe('Include touched file paths in the prompt'),\n },\n async ({ target, include_commits, include_files }) => {\n if (!reader.exists()) {\n return {\n content: [{\n type: 'text' as const,\n text: 'No KeepGoing data found. Save a checkpoint first to use Continue On.',\n }],\n };\n }\n\n const context = gatherContinueOnContext(reader, workspacePath);\n\n if (!context.lastCheckpoint && !context.briefing) {\n return {\n content: [{\n type: 'text' as const,\n text: 'No session data available. Save a checkpoint first.',\n }],\n };\n }\n\n const formatOpts: FormatOptions = {\n target: target as FormatOptions['target'],\n includeCommits: include_commits,\n includeFiles: include_files,\n };\n\n const prompt = formatContinueOnPrompt(context, formatOpts);\n\n return {\n content: [{ type: 'text' as const, text: prompt }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport function registerResumePrompt(server: McpServer) {\n server.prompt(\n 'resume',\n 'Check developer momentum and suggest what to work on next',\n async () => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: [\n 'I just opened this project and want to pick up where I left off.',\n '',\n 'Please use the KeepGoing tools to:',\n '1. Check my current momentum (get_momentum)',\n '2. Get a re-entry briefing (get_reentry_briefing)',\n '3. Based on the results, give me a concise summary of where I left off and suggest what to work on next.',\n '',\n 'Keep your response brief and actionable.',\n ].join('\\n'),\n },\n },\n ],\n }),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport function registerDecisionsPrompt(server: McpServer) {\n server.prompt(\n 'decisions',\n 'Review recent architectural decisions and their rationale',\n async () => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: [\n 'I want to review recent architectural decisions in this project.',\n '',\n 'Please use the KeepGoing tools to:',\n '1. Fetch recent decision records (get_decisions)',\n '2. Get my current branch context (get_momentum)',\n '3. Summarize the decisions, highlighting any that were made on the current branch',\n '',\n 'Keep your response brief and organized.',\n ].join('\\n'),\n },\n },\n ],\n }),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport function registerProgressPrompt(server: McpServer) {\n server.prompt(\n 'progress',\n 'Summarize recent development progress across sessions',\n async () => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: [\n 'I need a summary of recent development progress for this project.',\n '',\n 'Please use the KeepGoing tools to:',\n '1. Fetch session history with a higher limit for broader coverage (get_session_history, limit: 20)',\n '2. Get my current branch context (get_momentum)',\n '3. Synthesize a progress summary grouped by branch or feature, highlighting the current branch',\n '',\n 'Format the summary so it can be used in a standup or sprint review.',\n ].join('\\n'),\n },\n },\n ],\n }),\n );\n}\n","import { findGitRoot } from '@keepgoingdev/shared';\n\n/**\n * Resolve the workspace path from CLI arguments.\n * Takes the first non-flag argument as an explicit path, falling back to CWD.\n */\nexport function resolveWsPath(args: string[] = process.argv.slice(2)): string {\n const explicit = args.find(a => !a.startsWith('--'));\n return findGitRoot(explicit || process.cwd());\n}\n","import { formatRelativeTime, getLicenseForFeatureWithRevalidation } from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\nimport { migrateStatusline } from './migrate.js';\n\nexport async function handlePrintMomentum(): Promise<void> {\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n\n if (!reader.exists()) {\n process.exit(0);\n }\n\n const { session: lastSession } = reader.getScopedLastSession();\n if (!lastSession) {\n process.exit(0);\n }\n\n const touchedCount = lastSession.touchedFiles?.length ?? 0;\n const lines: string[] = [];\n lines.push(`[KeepGoing] Last checkpoint: ${formatRelativeTime(lastSession.timestamp)}`);\n if (lastSession.summary) {\n lines.push(` Summary: ${lastSession.summary}`);\n }\n if (lastSession.nextStep) {\n lines.push(` Next step: ${lastSession.nextStep}`);\n }\n if (lastSession.blocker) {\n lines.push(` Blocker: ${lastSession.blocker}`);\n }\n if (lastSession.gitBranch) {\n lines.push(` Branch: ${lastSession.gitBranch}`);\n }\n if (touchedCount > 0) {\n lines.push(` Worked on ${touchedCount} files on ${lastSession.gitBranch ?? 'unknown branch'}`);\n }\n lines.push(' Tip: Use the get_reentry_briefing tool for a full briefing');\n\n // Auto-migrate legacy statusline if needed\n const migrationMsg = migrateStatusline(wsPath);\n if (migrationMsg) {\n lines.push(migrationMsg);\n }\n\n console.log(lines.join('\\n'));\n process.exit(0);\n}\n\nexport async function handlePrintCurrent(): Promise<void> {\n if (process.env.KEEPGOING_PRO_BYPASS !== '1' && !(await getLicenseForFeatureWithRevalidation('session-awareness'))) {\n process.exit(0);\n }\n\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n const tasks = reader.getCurrentTasks();\n\n if (tasks.length === 0) {\n process.exit(0);\n }\n\n const activeTasks = tasks.filter(t => t.sessionActive);\n const finishedTasks = tasks.filter(t => !t.sessionActive);\n\n // Summary line\n if (tasks.length > 1) {\n const parts: string[] = [];\n if (activeTasks.length > 0) parts.push(`${activeTasks.length} active`);\n if (finishedTasks.length > 0) parts.push(`${finishedTasks.length} finished`);\n console.log(`[KeepGoing] Sessions: ${parts.join(', ')}`);\n }\n\n // Print each task\n for (const task of [...activeTasks, ...finishedTasks]) {\n const prefix = task.sessionActive ? '[KeepGoing] Current task:' : '[KeepGoing] \\u2705 Last task:';\n const sessionLabel = task.agentLabel || task.sessionId || '';\n const labelSuffix = sessionLabel ? ` (${sessionLabel})` : '';\n const lines: string[] = [`${prefix} ${formatRelativeTime(task.updatedAt)}${labelSuffix}`];\n if (task.branch) {\n lines.push(` Branch: ${task.branch}`);\n }\n if (task.taskSummary) {\n lines.push(` Doing: ${task.taskSummary}`);\n }\n if (task.nextStep) {\n lines.push(` Next: ${task.nextStep}`);\n }\n console.log(lines.join('\\n'));\n }\n\n process.exit(0);\n}\n","import {\n resolveStorageRoot,\n KeepGoingWriter,\n createCheckpoint,\n getCurrentBranch,\n getTouchedFiles,\n getCommitsSince,\n getCommitMessagesSince,\n getFilesChangedInCommit,\n tryDetectDecision,\n getLicenseForFeatureWithRevalidation,\n generateSessionId,\n buildSessionEvents,\n buildSmartSummary,\n buildSmartNextStep,\n} from '@keepgoingdev/shared';\nimport path from 'node:path';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\n\nexport async function handleSaveCheckpoint(): Promise<void> {\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n\n const { session: lastSession } = reader.getScopedLastSession();\n\n // Skip if a checkpoint was written within the last 2 minutes (avoid duplicating extension checkpoints)\n if (lastSession?.timestamp) {\n const ageMs = Date.now() - new Date(lastSession.timestamp).getTime();\n if (ageMs < 2 * 60 * 1000) {\n process.exit(0);\n }\n }\n\n const touchedFiles = getTouchedFiles(wsPath);\n const commitHashes = getCommitsSince(wsPath, lastSession?.timestamp);\n\n // Skip if there's nothing to capture\n if (touchedFiles.length === 0 && commitHashes.length === 0) {\n process.exit(0);\n }\n\n const gitBranch = getCurrentBranch(wsPath);\n const commitMessages = getCommitMessagesSince(wsPath, lastSession?.timestamp);\n\n // Build SessionEvents for smart summary\n const now = new Date().toISOString();\n const events = buildSessionEvents({\n wsPath,\n commitHashes,\n commitMessages,\n touchedFiles,\n currentBranch: gitBranch ?? undefined,\n sessionStartTime: lastSession?.timestamp ?? now,\n lastActivityTime: now,\n });\n\n const summary = buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map(f => path.basename(f)).join(', ')}`;\n const nextStep = buildSmartNextStep(events);\n\n const projectName = path.basename(resolveStorageRoot(wsPath));\n const sessionId = generateSessionId({ workspaceRoot: wsPath, branch: gitBranch ?? undefined, worktreePath: wsPath });\n const checkpoint = createCheckpoint({\n summary,\n nextStep,\n gitBranch,\n touchedFiles,\n commitHashes,\n workspaceRoot: wsPath,\n source: 'auto',\n sessionId,\n });\n\n const writer = new KeepGoingWriter(wsPath);\n writer.saveCheckpoint(checkpoint, projectName);\n\n // Mark current task as finished using multi-session API\n writer.upsertSession({\n sessionId,\n sessionActive: false,\n nextStep: checkpoint.nextStep || undefined,\n branch: gitBranch ?? undefined,\n updatedAt: checkpoint.timestamp,\n });\n\n // Decision detection (Pro feature, requires valid Decision Detection license)\n // Loop all commits between checkpoints so none are missed\n if (process.env.KEEPGOING_PRO_BYPASS === '1' || (await getLicenseForFeatureWithRevalidation('decisions'))) {\n for (let i = 0; i < commitHashes.length; i++) {\n const hash = commitHashes[i];\n const message = commitMessages[i];\n if (!hash || !message) continue;\n const files = getFilesChangedInCommit(wsPath, hash);\n const detected = tryDetectDecision({\n workspacePath: wsPath,\n checkpointId: checkpoint.id,\n gitBranch,\n commitHash: hash,\n commitMessage: message,\n filesChanged: files,\n });\n if (detected) {\n console.log(`[KeepGoing] Decision detected: ${detected.category} (${(detected.confidence * 100).toFixed(0)}% confidence)`);\n }\n }\n }\n\n console.log(`[KeepGoing] Auto-checkpoint saved: ${summary}`);\n process.exit(0);\n}\n","import fs from 'node:fs';\n\nconst TAIL_READ_BYTES = 8_192;\n\nconst TOOL_VERB_MAP: Record<string, string> = {\n Edit: 'editing',\n MultiEdit: 'editing',\n Write: 'editing',\n Read: 'researching',\n Glob: 'researching',\n Grep: 'researching',\n Bash: 'running',\n Agent: 'delegating',\n WebFetch: 'browsing',\n WebSearch: 'browsing',\n TodoWrite: 'planning',\n};\n\n/**\n * Truncates text at the last word boundary at or before `max` chars, appending ellipsis if cut.\n */\nexport function truncateAtWord(text: string, max: number): string {\n if (text.length <= max) return text;\n const cut = text.slice(0, max);\n const lastSpace = cut.lastIndexOf(' ');\n return (lastSpace > max / 2 ? cut.slice(0, lastSpace) : cut.slice(0, max - 1)) + '\\u2026';\n}\n\nconst FILLER_PREFIX_RE = /^(i want to|can you|please|let['']?s|could you|help me|i need to|i['']d like to|implement the following plan[:\\s]*|implement this plan[:\\s]*)\\s*/i;\nconst MARKDOWN_HEADING_RE = /^#+\\s+/;\n\n// Claude Code transcript JSONL entry shape (wrapped format)\ninterface TranscriptEntry {\n type?: string;\n message?: {\n role?: string;\n content?: unknown;\n };\n}\n\ninterface ContentPart {\n type?: string;\n text?: string;\n name?: string;\n}\n\nfunction extractTextFromContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n let text = '';\n for (const part of content as ContentPart[]) {\n if (part.type === 'text' && typeof part.text === 'string') {\n text += part.text + ' ';\n }\n }\n return text.trim();\n}\n\nfunction isUserEntry(entry: TranscriptEntry): boolean {\n // Claude Code wraps messages: { type: \"user\", message: { role: \"user\", content: [...] } }\n return entry.type === 'user' && entry.message?.role === 'user';\n}\n\nfunction getToolUseFromEntry(entry: TranscriptEntry): string | null {\n const content = entry.message?.content;\n if (!Array.isArray(content)) return null;\n for (const part of (content as ContentPart[]).slice().reverse()) {\n if (part.type === 'tool_use' && typeof part.name === 'string') {\n return part.name;\n }\n }\n return null;\n}\n\nfunction isAssistantEntry(entry: TranscriptEntry): boolean {\n return entry.message?.role === 'assistant';\n}\n\n/**\n * Extracts a stable session label from the first substantive user message in a transcript.\n * Returns null if no suitable message is found.\n */\nexport function extractSessionLabel(transcriptPath: string): string | null {\n if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;\n\n try {\n const raw = fs.readFileSync(transcriptPath, 'utf-8');\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(trimmed) as TranscriptEntry;\n } catch {\n continue;\n }\n\n if (!isUserEntry(entry)) continue;\n\n let text = extractTextFromContent(entry.message?.content);\n if (!text) continue;\n\n // Skip system-injected messages (bracket-prefixed or XML-tag-prefixed)\n if (text.startsWith('[') || /^<[a-z][\\w-]*>/.test(text)) continue;\n\n // Strip @-file mentions\n text = text.replace(/@[\\w./\\-]+/g, '').trim();\n // Strip filler prefixes\n text = text.replace(FILLER_PREFIX_RE, '').trim();\n // Strip markdown heading markers\n text = text.replace(MARKDOWN_HEADING_RE, '').trim();\n // Collapse whitespace/newlines to single space\n text = text.replace(/\\s+/g, ' ').trim();\n\n if (text.length < 20) continue;\n\n // Cap at 80 chars to bound reads; caller applies display budget\n if (text.length > 80) {\n text = text.slice(0, 80);\n }\n\n return text;\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n\n/**\n * Extracts the current action verb by reading the last tool_use entry from the transcript.\n * Reads only the last TAIL_READ_BYTES for efficiency.\n * Returns null if no tool use is found.\n */\nexport function extractCurrentAction(transcriptPath: string): string | null {\n if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;\n\n try {\n const stat = fs.statSync(transcriptPath);\n const fileSize = stat.size;\n if (fileSize === 0) return null;\n\n const readSize = Math.min(fileSize, TAIL_READ_BYTES);\n const offset = fileSize - readSize;\n\n const buf = Buffer.alloc(readSize);\n const fd = fs.openSync(transcriptPath, 'r');\n try {\n fs.readSync(fd, buf, 0, readSize, offset);\n } finally {\n fs.closeSync(fd);\n }\n\n const tail = buf.toString('utf-8');\n const lines = tail.split('\\n').reverse();\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(trimmed) as TranscriptEntry;\n } catch {\n continue;\n }\n\n if (!isAssistantEntry(entry)) continue;\n\n const toolName = getToolUseFromEntry(entry);\n if (toolName) {\n return TOOL_VERB_MAP[toolName] ?? 'working';\n }\n // Last assistant entry is text-only — AI finished its turn\n return 'done';\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n","import {\n KeepGoingWriter,\n getCurrentBranch,\n generateSessionId,\n type CurrentTask,\n} from '@keepgoingdev/shared';\nimport { resolveWsPath } from './util.js';\nimport { extractSessionLabel } from './transcriptUtils.js';\n\nexport async function handleUpdateTask(): Promise<void> {\n const args = process.argv.slice(2);\n const flagIndex = args.indexOf('--update-task');\n const payloadStr = args[flagIndex + 1];\n\n // Exclude the payload argument when resolving workspace path\n const wsArgs = args.filter((a, i) => !a.startsWith('--') && i !== flagIndex + 1);\n const wsPath = resolveWsPath(wsArgs.length > 0 ? wsArgs : undefined);\n\n if (payloadStr) {\n try {\n const payload = JSON.parse(payloadStr) as Partial<CurrentTask>;\n const writer = new KeepGoingWriter(wsPath);\n const branch = payload.branch ?? getCurrentBranch(wsPath) ?? undefined;\n const task: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string } = {\n ...payload,\n branch,\n worktreePath: wsPath,\n sessionActive: payload.sessionActive !== false,\n updatedAt: new Date().toISOString(),\n };\n\n // Use multi-session API if sessionId is present or can be derived\n const sessionId = payload.sessionId || generateSessionId({ ...task, workspaceRoot: wsPath });\n task.sessionId = sessionId;\n writer.upsertSession(task);\n } catch {\n // Exit silently on parse errors\n }\n }\n process.exit(0);\n}\n\nconst STDIN_TIMEOUT_MS = 5_000;\n\nexport async function handleUpdateTaskFromHook(): Promise<void> {\n const wsPath = resolveWsPath();\n\n const chunks: Buffer[] = [];\n const timeout = setTimeout(() => process.exit(0), STDIN_TIMEOUT_MS);\n process.stdin.on('error', () => { clearTimeout(timeout); process.exit(0); });\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\n process.stdin.on('end', () => {\n clearTimeout(timeout);\n try {\n const raw = Buffer.concat(chunks).toString('utf-8').trim();\n if (!raw) {\n process.exit(0);\n }\n const hookData = JSON.parse(raw) as {\n tool_name?: string;\n tool_input?: { file_path?: string; path?: string };\n session_id?: string;\n transcript_path?: string;\n };\n const toolName = hookData.tool_name ?? 'Edit';\n const filePath = hookData.tool_input?.file_path ?? hookData.tool_input?.path ?? '';\n const fileName = filePath ? filePath.split('/').pop() ?? filePath : '';\n const writer = new KeepGoingWriter(wsPath);\n // Reuse branch from OUR session to avoid spawning git on every edit.\n // Only match by sessionId so we never inherit a stale branch from another session.\n const existing = writer.readCurrentTasks();\n const sessionIdFromHook = hookData.session_id;\n const existingSession = sessionIdFromHook\n ? existing.find(t => t.sessionId === sessionIdFromHook)\n : undefined;\n const cachedBranch = existingSession?.branch;\n const branch = cachedBranch ?? getCurrentBranch(wsPath) ?? undefined;\n\n const task: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string } = {\n taskSummary: fileName ? `${toolName} ${fileName}` : `Used ${toolName}`,\n lastFileEdited: filePath || undefined,\n branch,\n worktreePath: wsPath,\n sessionActive: true,\n updatedAt: new Date().toISOString(),\n };\n\n // Derive session ID from context\n const sessionId = hookData.session_id || generateSessionId({ ...task, workspaceRoot: wsPath });\n task.sessionId = sessionId;\n\n // Cache sessionLabel on first hook fire; never overwrite once set\n if (!existingSession?.sessionLabel && hookData.transcript_path) {\n const label = extractSessionLabel(hookData.transcript_path);\n if (label) task.sessionLabel = label;\n }\n\n writer.upsertSession(task);\n } catch {\n // Exit silently on errors\n }\n process.exit(0);\n });\n process.stdin.resume();\n}\n","import { pruneStaleTasks, findGitRoot } from '@keepgoingdev/shared';\nimport type { CurrentTasks } from '@keepgoingdev/shared';\nimport { extractSessionLabel, extractCurrentAction, truncateAtWord } from './transcriptUtils.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst STDIN_TIMEOUT_MS = 3_000;\n\nexport async function handleStatusline(): Promise<void> {\n const chunks: Buffer[] = [];\n const timeout = setTimeout(() => process.exit(0), STDIN_TIMEOUT_MS);\n process.stdin.on('error', () => { clearTimeout(timeout); process.exit(0); });\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\n process.stdin.on('end', () => {\n clearTimeout(timeout);\n try {\n const raw = Buffer.concat(chunks).toString('utf-8').trim();\n if (!raw) process.exit(0);\n\n const input = JSON.parse(raw) as {\n session_id?: string;\n transcript_path?: string;\n agent?: { name?: string };\n workspace?: { current_dir?: string };\n cwd?: string;\n };\n\n const dir = input.workspace?.current_dir ?? input.cwd;\n if (!dir) process.exit(0);\n\n const transcriptPath = input.transcript_path;\n const sessionId = input.session_id;\n\n // Resolve label: agent.name > cached sessionLabel > transcript > null\n let label: string | null = null;\n\n if (input.agent?.name) {\n label = input.agent.name;\n }\n\n if (!label) {\n // Try to find cached sessionLabel in current-tasks.json\n try {\n const gitRoot = findGitRoot(dir);\n const tasksFile = path.join(gitRoot, '.keepgoing', 'current-tasks.json');\n if (fs.existsSync(tasksFile)) {\n const data = JSON.parse(fs.readFileSync(tasksFile, 'utf-8')) as CurrentTasks;\n const tasks = pruneStaleTasks(data.tasks ?? []);\n const match = sessionId ? tasks.find(t => t.sessionId === sessionId) : undefined;\n if (match?.sessionLabel) {\n label = match.sessionLabel;\n }\n }\n } catch {\n // Ignore; fall through to transcript\n }\n }\n\n if (!label && transcriptPath) {\n label = extractSessionLabel(transcriptPath);\n }\n\n if (!label) process.exit(0);\n\n // Get current action from transcript\n const action = transcriptPath ? extractCurrentAction(transcriptPath) : null;\n\n // Dynamic budget: 40 chars with action verb, 55 chars without\n const budget = action ? 40 : 55;\n const displayLabel = truncateAtWord(label, budget);\n\n if (action) {\n process.stdout.write(`[KG] ${displayLabel} \\u00b7 ${action}\\n`);\n } else {\n process.stdout.write(`[KG] ${displayLabel}\\n`);\n }\n } catch {\n // Exit silently on errors\n }\n process.exit(0);\n });\n process.stdin.resume();\n}\n","import {\n gatherContinueOnContext,\n formatContinueOnPrompt,\n} from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\n\nexport async function handleContinueOn(): Promise<void> {\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n\n if (!reader.exists()) {\n process.exit(0);\n }\n\n const context = gatherContinueOnContext(reader, wsPath);\n\n if (!context.lastCheckpoint && !context.briefing) {\n process.exit(0);\n }\n\n const prompt = formatContinueOnPrompt(context);\n console.log(prompt);\n process.exit(0);\n}\n","import {\n getCurrentBranch,\n getHeadCommitHash,\n getCommitMessageByHash,\n getFilesChangedInCommit,\n tryDetectDecision,\n getLicenseForFeatureWithRevalidation,\n} from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\n\n/**\n * Called by the global post-commit hook after every git commit.\n * Detects high-signal decisions from the HEAD commit and writes\n * them to `.keepgoing/decisions.json`.\n */\nexport async function handleDetectDecisions(): Promise<void> {\n const wsPath = resolveWsPath();\n\n // Respect Pro gate\n if (!(process.env.KEEPGOING_PRO_BYPASS === '1' || (await getLicenseForFeatureWithRevalidation('decisions')))) {\n process.exit(0);\n }\n\n // Only run if .keepgoing/ exists (auto-init is handled by write-triggers\n // like save_checkpoint, not by read-only detection)\n const reader = new KeepGoingReader(wsPath);\n if (!reader.exists()) {\n process.exit(0);\n }\n\n const gitBranch = getCurrentBranch(wsPath);\n const headHash = getHeadCommitHash(wsPath);\n if (!headHash) process.exit(0);\n\n // Get HEAD commit message by hash (avoids loading all recent commits)\n const commitMessage = getCommitMessageByHash(wsPath, headHash);\n if (!commitMessage) process.exit(0);\n\n const files = getFilesChangedInCommit(wsPath, headHash);\n\n const detected = tryDetectDecision({\n workspacePath: wsPath,\n gitBranch,\n commitHash: headHash,\n commitMessage,\n filesChanged: files,\n });\n\n if (detected) {\n console.log(`[KeepGoing] Decision detected: ${detected.category} (${(detected.confidence * 100).toFixed(0)}% confidence)`);\n }\n\n process.exit(0);\n}\n"],"mappings":"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","names":["path","fs","path","randomUUID","path","path","fs","randomUUID","fs","path","fs","os","path","fs","path","STORAGE_DIR","path","fs","fs","path","STORAGE_DIR","META_FILE","SESSIONS_FILE","DECISIONS_FILE","STATE_FILE","CURRENT_TASKS_FILE","path","fs","fs","os","path","path","os","claudeDir","fs","lastSession","state","z","z","z","z","path","z","z","path","z","z","z","fs","os","path","STATUSLINE_CMD","z","z","z","label","who","z","z","label","z","z","path","path","fs","fs","path","STDIN_TIMEOUT_MS"]}
1
+ {"version":3,"sources":["../src/index.ts","../../../packages/shared/src/session.ts","../../../packages/shared/src/timeUtils.ts","../../../packages/shared/src/gitUtils.ts","../../../packages/shared/src/briefingTier.ts","../../../packages/shared/src/reentry.ts","../../../packages/shared/src/briefingFormatter.ts","../../../packages/shared/src/continueOn.ts","../../../packages/shared/src/storage.ts","../../../packages/shared/src/registry.ts","../../../packages/shared/src/smartSummary.ts","../../../packages/shared/src/decisionStorage.ts","../../../packages/shared/src/license.ts","../../../packages/shared/src/featureGate.ts","../../../packages/shared/src/decisionDetection.ts","../../../packages/shared/src/reader.ts","../../../packages/shared/src/setup.ts","../../../packages/shared/src/licenseClient.ts","../../../packages/shared/src/licenseRevalidation.ts","../src/tools/getMomentum.ts","../src/tools/getSessionHistory.ts","../src/tools/getReentryBriefing.ts","../src/tools/saveCheckpoint.ts","../src/tools/getDecisions.ts","../src/tools/getCurrentTask.ts","../src/tools/setupProject.ts","../src/cli/migrate.ts","../src/tools/activateLicense.ts","../src/tools/deactivateLicense.ts","../src/tools/continueOn.ts","../src/prompts/resume.ts","../src/prompts/decisions.ts","../src/prompts/progress.ts","../src/cli/util.ts","../src/cli/print.ts","../src/cli/saveCheckpoint.ts","../src/cli/transcriptUtils.ts","../src/cli/updateTask.ts","../src/cli/statusline.ts","../src/cli/continueOn.ts","../src/cli/detectDecisions.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { findGitRoot } from '@keepgoingdev/shared';\nimport { KeepGoingReader } from './storage.js';\nimport { registerGetMomentum } from './tools/getMomentum.js';\nimport { registerGetSessionHistory } from './tools/getSessionHistory.js';\nimport { registerGetReentryBriefing } from './tools/getReentryBriefing.js';\nimport { registerSaveCheckpoint } from './tools/saveCheckpoint.js';\nimport { registerGetDecisions } from './tools/getDecisions.js';\nimport { registerGetCurrentTask } from './tools/getCurrentTask.js';\nimport { registerSetupProject } from './tools/setupProject.js';\nimport { registerActivateLicense } from './tools/activateLicense.js';\nimport { registerDeactivateLicense } from './tools/deactivateLicense.js';\nimport { registerContinueOn } from './tools/continueOn.js';\nimport { registerResumePrompt } from './prompts/resume.js';\nimport { registerDecisionsPrompt } from './prompts/decisions.js';\nimport { registerProgressPrompt } from './prompts/progress.js';\nimport { handlePrintMomentum, handlePrintCurrent } from './cli/print.js';\nimport { handleSaveCheckpoint } from './cli/saveCheckpoint.js';\nimport { handleUpdateTask, handleUpdateTaskFromHook } from './cli/updateTask.js';\nimport { handleStatusline } from './cli/statusline.js';\nimport { handleContinueOn } from './cli/continueOn.js';\nimport { handleDetectDecisions } from './cli/detectDecisions.js';\n\n// CLI flag dispatch table. Each handler calls process.exit() when done.\nconst CLI_HANDLERS: Record<string, () => Promise<void>> = {\n '--print-momentum': handlePrintMomentum,\n '--save-checkpoint': handleSaveCheckpoint,\n '--update-task': handleUpdateTask,\n '--update-task-from-hook': handleUpdateTaskFromHook,\n '--print-current': handlePrintCurrent,\n '--statusline': handleStatusline,\n '--continue-on': handleContinueOn,\n '--detect-decisions': handleDetectDecisions,\n};\n\nconst flag = process.argv.slice(2).find(a => a in CLI_HANDLERS);\nif (flag) {\n await CLI_HANDLERS[flag]();\n} else {\n // Default: start MCP server\n // Workspace path can be passed as an argument, otherwise defaults to CWD.\n // MCP hosts (Claude Code, etc.) typically launch the server with the project root as CWD.\n const workspacePath = findGitRoot(process.argv[2] || process.cwd());\n const reader = new KeepGoingReader(workspacePath);\n\n const server = new McpServer({\n name: 'keepgoing',\n version: '0.1.0',\n });\n\n // Register tools\n registerGetMomentum(server, reader, workspacePath);\n registerGetSessionHistory(server, reader);\n registerGetReentryBriefing(server, reader, workspacePath);\n registerGetDecisions(server, reader);\n registerGetCurrentTask(server, reader);\n registerSaveCheckpoint(server, reader, workspacePath);\n registerContinueOn(server, reader, workspacePath);\n registerSetupProject(server, workspacePath);\n registerActivateLicense(server);\n registerDeactivateLicense(server);\n\n // Register prompts\n registerResumePrompt(server);\n registerDecisionsPrompt(server);\n registerProgressPrompt(server);\n\n // Connect via stdio\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('KeepGoing MCP server started');\n}\n","import { randomUUID } from 'crypto';\nimport type { SessionCheckpoint, ProjectSessions, ProjectMeta, ProjectState, DecisionRecord, ProjectDecisions } from './types';\n\n/**\n * Generates a UUID v4 unique ID for checkpoints.\n */\nexport function generateCheckpointId(): string {\n return randomUUID();\n}\n\n/**\n * Creates a SessionCheckpoint with auto-generated id and timestamp.\n * Consolidates the repeated checkpoint construction pattern.\n */\nexport function createCheckpoint(\n fields: Omit<SessionCheckpoint, 'id' | 'timestamp'>,\n): SessionCheckpoint {\n return {\n id: generateCheckpointId(),\n timestamp: new Date().toISOString(),\n ...fields,\n };\n}\n\n/**\n * Creates a default empty project sessions container.\n */\nexport function createEmptyProjectSessions(projectName: string): ProjectSessions {\n return {\n version: 1,\n project: projectName,\n sessions: [],\n lastSessionId: undefined,\n };\n}\n\n/**\n * Creates a default project metadata object.\n */\nexport function createProjectMeta(): ProjectMeta {\n const now = new Date().toISOString();\n return {\n projectId: randomUUID(),\n createdAt: now,\n lastUpdated: now,\n };\n}\n\n/**\n * Creates a default empty project state object.\n */\nexport function createEmptyProjectState(): ProjectState {\n return {};\n}\n\n/**\n * Creates a DecisionRecord with an auto-generated UUID id.\n */\nexport function createDecisionRecord(\n fields: Omit<DecisionRecord, 'id'>,\n): DecisionRecord {\n return {\n id: randomUUID(),\n ...fields,\n };\n}\n\n/**\n * Creates a default empty project decisions container.\n */\nexport function createEmptyProjectDecisions(projectName: string): ProjectDecisions {\n return {\n version: 1,\n project: projectName,\n decisions: [],\n lastDecisionId: undefined,\n };\n}\n","/**\n * Formats a timestamp as a human-readable relative time string.\n * Examples: \"just now\", \"5 minutes ago\", \"3 hours ago\", \"2 days ago\", \"1 week ago\"\n *\n * Note: Month and year calculations use approximations (30 days/month, 365 days/year)\n * for simplicity. These approximations are acceptable for the \"human-readable\" purpose.\n */\nexport function formatRelativeTime(timestamp: string): string {\n const now = Date.now();\n const then = new Date(timestamp).getTime();\n const diffMs = now - then;\n\n // Handle invalid dates\n if (isNaN(diffMs)) {\n return 'unknown time';\n }\n\n // Future dates\n if (diffMs < 0) {\n return 'in the future';\n }\n\n const seconds = Math.floor(diffMs / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(days / 30); // Approximation for human readability\n const years = Math.floor(days / 365); // Approximation, doesn't account for leap years\n\n if (seconds < 10) {\n return 'just now';\n } else if (seconds < 60) {\n return `${seconds} seconds ago`;\n } else if (minutes < 60) {\n return minutes === 1 ? '1 minute ago' : `${minutes} minutes ago`;\n } else if (hours < 24) {\n return hours === 1 ? '1 hour ago' : `${hours} hours ago`;\n } else if (days < 7) {\n return days === 1 ? '1 day ago' : `${days} days ago`;\n } else if (weeks < 4) {\n return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`;\n } else if (months < 12) {\n return months === 1 ? '1 month ago' : `${months} months ago`;\n } else {\n return years === 1 ? '1 year ago' : `${years} years ago`;\n }\n}\n","import { execFileSync, execFile } from 'child_process';\nimport path from 'node:path';\nimport { promisify } from 'util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Git utilities for extracting context from the workspace.\n * These are intentionally decoupled from VSCode APIs.\n * All sync functions use execFileSync (no shell injection).\n */\n\n/**\n * Returns the git working tree root for the given path.\n * In a worktree, returns the worktree root (not the main repo root).\n * Use `resolveStorageRoot()` to get the main repo root for storage.\n * Falls back to the original path if not inside a git repo.\n */\nexport function findGitRoot(startPath: string): string {\n try {\n const toplevel = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd: startPath,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n return toplevel || startPath;\n } catch {\n return startPath;\n }\n}\n\n/**\n * Cache for resolveStorageRoot results to avoid redundant subprocess calls.\n */\nconst storageRootCache = new Map<string, string>();\n\n/**\n * Clears the resolveStorageRoot cache.\n * Call this in long-lived processes (e.g. VS Code extension) when\n * worktree configuration may have changed.\n */\nexport function clearStorageRootCache(): void {\n storageRootCache.clear();\n}\n\n/**\n * Resolves the main repository root for storage purposes.\n * In a git worktree, `--show-toplevel` returns the worktree root, but\n * `.keepgoing/` should always live in the main repo root so data persists\n * across worktrees. This function uses `--git-common-dir` to find the\n * shared `.git` directory and derives the main repo root from it.\n *\n * Falls back to `startPath` if git commands fail (non-git directories, tests).\n */\nexport function resolveStorageRoot(startPath: string): string {\n const cached = storageRootCache.get(startPath);\n if (cached !== undefined) {\n return cached;\n }\n\n try {\n const toplevel = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd: startPath,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const commonDir = execFileSync('git', ['rev-parse', '--git-common-dir'], {\n cwd: startPath,\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n // commonDir is relative to the worktree root (e.g. \"../../.git\" or just \".git\")\n const absoluteCommonDir = path.resolve(toplevel, commonDir);\n const mainRoot = path.dirname(absoluteCommonDir);\n\n storageRootCache.set(startPath, mainRoot);\n return mainRoot;\n } catch {\n storageRootCache.set(startPath, startPath);\n return startPath;\n }\n}\n\n/**\n * Returns the current git branch name, or undefined if not in a git repo.\n */\nexport function getCurrentBranch(workspacePath: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Runs git log with the given format since a timestamp.\n * Shared implementation for commit hash and message retrieval.\n */\nfunction getGitLogSince(workspacePath: string, format: string, sinceTimestamp?: string): string[] {\n try {\n const since = sinceTimestamp || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();\n const result = execFileSync(\n 'git',\n ['log', `--since=${since}`, `--format=${format}`],\n {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n },\n );\n\n if (!result.trim()) {\n return [];\n }\n\n return result\n .trim()\n .split('\\n')\n .filter((line: string) => line.length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Returns a list of commit hashes since a given ISO timestamp.\n * If no timestamp is provided, returns recent commits (last 24 hours).\n */\nexport function getCommitsSince(workspacePath: string, sinceTimestamp?: string): string[] {\n return getGitLogSince(workspacePath, '%H', sinceTimestamp);\n}\n\n/**\n * Returns a list of commit subject lines since a given ISO timestamp.\n * If no timestamp is provided, returns commit messages from the last 24 hours.\n * Results are in reverse chronological order (newest first), matching git log default.\n */\nexport function getCommitMessagesSince(workspacePath: string, sinceTimestamp?: string): string[] {\n return getGitLogSince(workspacePath, '%s', sinceTimestamp);\n}\n\n/**\n * Returns the commit message (subject line) for a specific commit hash.\n * Uses hash-based lookup instead of timestamp-based --since to avoid race conditions.\n */\nexport function getCommitMessageByHash(workspacePath: string, commitHash: string): string | undefined {\n try {\n const result = execFileSync('git', ['log', '-1', '--format=%s', commitHash], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns the list of files changed in a specific commit (relative paths).\n * Uses `git diff-tree` to get the actual files touched by that commit,\n * as opposed to `getTouchedFiles` which returns the current workspace dirty state.\n */\nexport function getFilesChangedInCommit(workspacePath: string, commitHash: string): string[] {\n try {\n const result = execFileSync('git', ['diff-tree', '--no-commit-id', '--name-only', '-r', commitHash], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim().split('\\n').filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Returns the current HEAD commit hash, or undefined if not in a git repo.\n */\nexport function getHeadCommitHash(workspacePath: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return result.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Async version of getCurrentBranch. Avoids blocking the event loop.\n */\nexport async function getCurrentBranchAsync(workspacePath: string): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Async version of getHeadCommitHash. Avoids blocking the event loop.\n */\nexport async function getHeadCommitHashAsync(workspacePath: string): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns a list of files that have been modified (tracked and untracked)\n * in the workspace. This is best-effort and may not capture all changes.\n */\nexport function getTouchedFiles(workspacePath: string): string[] {\n try {\n const result = execFileSync('git', ['status', '--porcelain'], {\n cwd: workspacePath,\n encoding: 'utf-8',\n timeout: 5000,\n });\n\n if (!result.trim()) {\n return [];\n }\n\n return result\n .trim()\n .split('\\n')\n .map((line) => line.substring(3).trim())\n .filter((file) => file.length > 0 && !file.endsWith('/'));\n } catch {\n return [];\n }\n}\n","import type { BriefingTier } from './types';\n\n/**\n * Known model-to-tier mappings. Expandable as new models ship.\n * Keys are normalized (lowercased, trimmed).\n */\nconst MODEL_TIERS: Record<string, BriefingTier> = {\n // Standard: small/fast models (compact is only reachable via explicit tier or context window)\n 'claude-3-haiku': 'standard',\n 'claude-3.5-haiku': 'standard',\n 'claude-haiku-4-5': 'standard',\n 'gpt-4o-mini': 'standard',\n 'gemini-flash': 'standard',\n // Detailed: mid-tier models\n 'claude-3.5-sonnet': 'detailed',\n 'claude-sonnet-4': 'detailed',\n 'claude-sonnet-4-5': 'detailed',\n 'claude-sonnet-4-6': 'detailed',\n 'gpt-4o': 'detailed',\n 'gemini-pro': 'detailed',\n // Full: large context models\n 'claude-opus-4': 'full',\n 'claude-opus-4-5': 'full',\n 'claude-opus-4-6': 'full',\n 'o1': 'full',\n 'o3': 'full',\n 'gemini-ultra': 'full',\n};\n\n/**\n * Resolves the briefing tier from explicit override, model name, or context window.\n *\n * Priority chain:\n * 1. Explicit `tier` param (manual override)\n * 2. Model name lookup in built-in database\n * 3. Context window heuristic\n * 4. Default: `standard`\n */\nexport function resolveTier(opts?: {\n tier?: BriefingTier;\n model?: string;\n contextWindow?: number;\n}): BriefingTier {\n if (opts?.tier) {\n return opts.tier;\n }\n\n if (opts?.model) {\n const fromModel = tierFromModelName(opts.model);\n if (fromModel) return fromModel;\n }\n\n if (opts?.contextWindow !== undefined) {\n return tierFromContextWindow(opts.contextWindow);\n }\n\n return 'standard';\n}\n\n/**\n * Looks up a model name in the built-in tier database.\n * Performs fuzzy matching: normalizes to lowercase and tries progressively\n * shorter prefixes to match versioned model IDs (e.g. \"claude-opus-4-20250514\").\n */\nexport function tierFromModelName(model: string): BriefingTier | undefined {\n const normalized = model.toLowerCase().trim();\n\n // Direct match\n if (MODEL_TIERS[normalized]) {\n return MODEL_TIERS[normalized];\n }\n\n // Try stripping date suffixes and version segments (e.g. \"-20250514\", \"-latest\").\n // Sort keys longest-first so \"claude-sonnet-4-5\" matches before \"claude-sonnet-4\".\n const entries = Object.entries(MODEL_TIERS).sort((a, b) => b[0].length - a[0].length);\n for (const [key, tier] of entries) {\n if (normalized.startsWith(key)) {\n return tier;\n }\n }\n\n return undefined;\n}\n\n/**\n * Maps a context window size (in tokens) to a briefing tier.\n *\n * <16K -> compact\n * <64K -> standard\n * <200K -> detailed\n * 200K+ -> full\n */\nexport function tierFromContextWindow(tokens: number): BriefingTier {\n if (tokens < 16_000) return 'compact';\n if (tokens < 64_000) return 'standard';\n if (tokens < 200_000) return 'detailed';\n return 'full';\n}\n","import type {\n SessionCheckpoint,\n ProjectState,\n ReEntryBriefing,\n DecisionRecord,\n BriefingTier,\n EnrichedBriefing,\n} from './types';\nimport { formatRelativeTime } from './timeUtils';\nimport { resolveTier } from './briefingTier';\n\nconst RECENT_SESSION_COUNT = 5;\n\n/**\n * Generates a synthesized re-entry briefing from stored project data.\n *\n * Uses heuristic rules (no AI) to infer focus and suggest next steps.\n * Designed to be replaced by an LLM-backed implementation in the future\n * while keeping the same input/output contract.\n */\nexport function generateBriefing(\n lastSession: SessionCheckpoint | undefined,\n recentSessions: SessionCheckpoint[],\n projectState: ProjectState,\n gitBranch?: string,\n recentCommitMessages?: string[],\n): ReEntryBriefing | undefined {\n if (!lastSession) {\n return undefined;\n }\n\n return {\n lastWorked: formatRelativeTime(lastSession.timestamp),\n currentFocus: buildCurrentFocus(lastSession, projectState, gitBranch),\n recentActivity: buildRecentActivity(\n lastSession,\n recentSessions,\n recentCommitMessages,\n ),\n suggestedNext: buildSuggestedNext(lastSession, gitBranch),\n smallNextStep: buildSmallNextStep(\n lastSession,\n gitBranch,\n recentCommitMessages,\n ),\n };\n}\n\n/**\n * Returns the most recent N sessions in newest-first order.\n */\nexport function getRecentSessions(\n allSessions: SessionCheckpoint[],\n count: number = RECENT_SESSION_COUNT,\n): SessionCheckpoint[] {\n return allSessions.slice(-count).reverse();\n}\n\n/**\n * Options for generating a tier-aware enriched briefing.\n */\nexport interface EnrichedBriefingOpts {\n // Tier resolution (priority: tier > model > contextWindow > default 'standard')\n tier?: BriefingTier;\n model?: string;\n contextWindow?: number;\n\n // Core briefing inputs (same as generateBriefing)\n lastSession: SessionCheckpoint | undefined;\n recentSessions: SessionCheckpoint[];\n projectState: ProjectState;\n gitBranch?: string;\n recentCommits?: string[];\n\n // Enrichment data: pass what you have, the function trims by tier\n decisions?: DecisionRecord[];\n allTouchedFiles?: string[];\n allSessions?: SessionCheckpoint[];\n fileConflicts?: Array<{ file: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }>;\n branchOverlaps?: Array<{ branch: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }>;\n isWorktree?: boolean;\n}\n\n/**\n * Generates a tier-aware enriched briefing.\n *\n * Calls `generateBriefing()` for the core fields, then selectively populates\n * enrichment fields based on the resolved tier:\n *\n * - compact (~150 tokens): lastWorked + currentFocus + smallNextStep\n * - standard (~400 tokens): all 5 core fields + blocker\n * - detailed (~800 tokens): standard + decisions(3) + touchedFiles(10) + session count\n * - full (~1500 tokens): detailed + sessionHistory(5) + all decisions(10) + all files + commits + cross-session intelligence\n */\nexport function generateEnrichedBriefing(opts: EnrichedBriefingOpts): EnrichedBriefing | undefined {\n const tier = resolveTier({\n tier: opts.tier,\n model: opts.model,\n contextWindow: opts.contextWindow,\n });\n\n const core = generateBriefing(\n opts.lastSession,\n opts.recentSessions,\n opts.projectState,\n opts.gitBranch,\n opts.recentCommits,\n );\n\n if (!core) return undefined;\n\n const enriched: EnrichedBriefing = { tier, core };\n\n // compact: just core (renderer will pick 3 fields)\n if (tier === 'compact') {\n return enriched;\n }\n\n // standard+: blocker, branch, worktree\n enriched.blocker = opts.lastSession?.blocker;\n enriched.gitBranch = opts.gitBranch;\n enriched.isWorktree = opts.isWorktree;\n\n if (tier === 'standard') {\n return enriched;\n }\n\n // detailed+: decisions (3), touched files (10)\n if (opts.decisions && opts.decisions.length > 0) {\n enriched.decisions = opts.decisions.slice(0, tier === 'detailed' ? 3 : 10);\n }\n\n if (opts.allTouchedFiles && opts.allTouchedFiles.length > 0) {\n enriched.touchedFiles = tier === 'detailed'\n ? opts.allTouchedFiles.slice(0, 10)\n : opts.allTouchedFiles;\n } else if (opts.lastSession?.touchedFiles && opts.lastSession.touchedFiles.length > 0) {\n enriched.touchedFiles = tier === 'detailed'\n ? opts.lastSession.touchedFiles.slice(0, 10)\n : opts.lastSession.touchedFiles;\n }\n\n if (tier === 'detailed') {\n return enriched;\n }\n\n // full: session history, all commits, cross-session intelligence\n if (opts.allSessions && opts.allSessions.length > 0) {\n const recent = opts.allSessions.slice(-5).reverse();\n enriched.sessionHistory = recent.map(s => ({\n timestamp: s.timestamp,\n summary: s.summary || '',\n nextStep: s.nextStep || '',\n branch: s.gitBranch,\n }));\n }\n\n if (opts.recentCommits && opts.recentCommits.length > 0) {\n enriched.recentCommits = opts.recentCommits;\n }\n\n if (opts.fileConflicts && opts.fileConflicts.length > 0) {\n enriched.fileConflicts = opts.fileConflicts;\n }\n\n if (opts.branchOverlaps && opts.branchOverlaps.length > 0) {\n enriched.branchOverlaps = opts.branchOverlaps;\n }\n\n return enriched;\n}\n\nfunction buildCurrentFocus(\n lastSession: SessionCheckpoint,\n projectState: ProjectState,\n gitBranch?: string,\n): string {\n if (projectState.derivedCurrentFocus) {\n return projectState.derivedCurrentFocus;\n }\n\n const branchFocus = inferFocusFromBranch(gitBranch);\n if (branchFocus) {\n return branchFocus;\n }\n\n if (lastSession.summary) {\n return lastSession.summary;\n }\n\n if (lastSession.touchedFiles.length > 0) {\n return inferFocusFromFiles(lastSession.touchedFiles);\n }\n\n return 'Unknown, save a checkpoint to set context';\n}\n\nfunction buildRecentActivity(\n lastSession: SessionCheckpoint,\n recentSessions: SessionCheckpoint[],\n recentCommitMessages?: string[],\n): string {\n const parts: string[] = [];\n\n const sessionCount = recentSessions.length;\n if (sessionCount > 1) {\n parts.push(`${sessionCount} recent sessions`);\n } else if (sessionCount === 1) {\n parts.push('1 recent session');\n }\n\n if (lastSession.summary) {\n const brief = lastSession.summary.length > 120\n ? lastSession.summary.slice(0, 117) + '...'\n : lastSession.summary;\n parts.push(`Last: ${brief}`);\n }\n\n if (lastSession.touchedFiles.length > 0) {\n parts.push(`${lastSession.touchedFiles.length} files touched`);\n }\n\n if (recentCommitMessages && recentCommitMessages.length > 0) {\n parts.push(`${recentCommitMessages.length} recent commits`);\n }\n\n return parts.length > 0 ? parts.join('. ') : 'No recent activity recorded';\n}\n\nfunction buildSuggestedNext(\n lastSession: SessionCheckpoint,\n gitBranch?: string,\n): string {\n if (lastSession.nextStep) {\n return lastSession.nextStep;\n }\n\n const branchFocus = inferFocusFromBranch(gitBranch);\n if (branchFocus) {\n return `Continue working on ${branchFocus}`;\n }\n\n if (lastSession.touchedFiles.length > 0) {\n return `Continue working on ${inferFocusFromFiles(lastSession.touchedFiles)}`;\n }\n\n return 'Save a checkpoint to track your next step';\n}\n\nfunction buildSmallNextStep(\n lastSession: SessionCheckpoint,\n gitBranch?: string,\n recentCommitMessages?: string[],\n): string {\n const fallback = 'Review last changed files to resume flow';\n\n if (lastSession.nextStep) {\n const distilled = distillToSmallStep(\n lastSession.nextStep,\n lastSession.touchedFiles,\n );\n if (distilled) {\n return distilled;\n }\n }\n\n if (recentCommitMessages && recentCommitMessages.length > 0) {\n const commitStep = deriveStepFromCommits(recentCommitMessages);\n if (commitStep) {\n return commitStep;\n }\n }\n\n if (lastSession.touchedFiles.length > 0) {\n const fileStep = deriveStepFromFiles(lastSession.touchedFiles);\n if (fileStep) {\n return fileStep;\n }\n }\n\n const branchFocus = inferFocusFromBranch(gitBranch);\n if (branchFocus) {\n return `Check git status for ${branchFocus}`;\n }\n\n return fallback;\n}\n\nfunction distillToSmallStep(\n nextStep: string,\n touchedFiles: string[],\n): string | undefined {\n if (!nextStep.trim()) {\n return undefined;\n }\n\n const words = nextStep.trim().split(/\\s+/);\n if (words.length <= 12) {\n if (touchedFiles.length > 0 && !mentionsFile(nextStep)) {\n const primaryFile = getPrimaryFileName(touchedFiles);\n const enhanced = `${nextStep.trim()} in ${primaryFile}`;\n if (enhanced.split(/\\s+/).length <= 12) {\n return enhanced;\n }\n }\n return nextStep.trim();\n }\n\n return words.slice(0, 12).join(' ');\n}\n\nfunction deriveStepFromCommits(\n commitMessages: string[],\n): string | undefined {\n const lastCommit = commitMessages[0];\n if (!lastCommit || !lastCommit.trim()) {\n return undefined;\n }\n\n const wipPattern =\n /^(?:wip|work in progress|started?|begin|draft)[:\\s]/i;\n if (wipPattern.test(lastCommit)) {\n const topic = lastCommit.replace(wipPattern, '').trim();\n if (topic) {\n const words = topic.split(/\\s+/).slice(0, 8).join(' ');\n return `Continue ${words}`;\n }\n }\n\n return undefined;\n}\n\nfunction deriveStepFromFiles(files: string[]): string | undefined {\n const primaryFile = getPrimaryFileName(files);\n\n if (files.length > 1) {\n return `Open ${primaryFile} and review ${files.length} changed files`;\n }\n\n return `Open ${primaryFile} and pick up where you left off`;\n}\n\nfunction getPrimaryFileName(files: string[]): string {\n const sourceFiles = files.filter((f) => {\n const lower = f.toLowerCase();\n return (\n !lower.includes('test') &&\n !lower.includes('spec') &&\n !lower.includes('.config') &&\n !lower.includes('package.json') &&\n !lower.includes('tsconfig')\n );\n });\n\n const target = sourceFiles.length > 0 ? sourceFiles[0] : files[0];\n const parts = target.replace(/\\\\/g, '/').split('/');\n return parts[parts.length - 1];\n}\n\nfunction mentionsFile(text: string): boolean {\n return /\\w+\\.(?:ts|tsx|js|jsx|py|go|rs|java|rb|css|scss|html|json|yaml|yml|md|sql|sh)\\b/i.test(\n text,\n );\n}\n\nfunction inferFocusFromBranch(branch?: string): string | undefined {\n if (\n !branch ||\n branch === 'main' ||\n branch === 'master' ||\n branch === 'develop' ||\n branch === 'HEAD'\n ) {\n return undefined;\n }\n\n const prefixPattern =\n /^(?:feature|feat|fix|bugfix|hotfix|chore|refactor|docs|test|ci)\\//i;\n const isFix = /^(?:fix|bugfix|hotfix)\\//i.test(branch);\n const stripped = branch.replace(prefixPattern, '');\n\n const cleaned = stripped\n .replace(/[-_/]/g, ' ')\n .replace(/^\\d+\\s*/, '')\n .trim();\n\n if (!cleaned) {\n return undefined;\n }\n\n return isFix ? `${cleaned} fix` : cleaned;\n}\n\nfunction inferFocusFromFiles(files: string[]): string {\n if (files.length === 0) {\n return 'unknown files';\n }\n\n const dirs = files\n .map((f) => {\n const parts = f.replace(/\\\\/g, '/').split('/');\n return parts.length > 1 ? parts.slice(0, -1).join('/') : '';\n })\n .filter((d) => d.length > 0);\n\n if (dirs.length > 0) {\n const counts = new Map<string, number>();\n for (const dir of dirs) {\n counts.set(dir, (counts.get(dir) ?? 0) + 1);\n }\n let topDir = '';\n let topCount = 0;\n for (const [dir, count] of counts) {\n if (count > topCount) {\n topDir = dir;\n topCount = count;\n }\n }\n if (topDir) {\n return `files in ${topDir}`;\n }\n }\n\n const names = files.slice(0, 3).map((f) => {\n const parts = f.replace(/\\\\/g, '/').split('/');\n return parts[parts.length - 1];\n });\n return names.join(', ');\n}\n","import type { EnrichedBriefing } from './types';\nimport { formatRelativeTime } from './timeUtils';\n\n/**\n * Formats an enriched briefing into markdown.\n * Sections are controlled by which fields are present (already tier-gated\n * by `generateEnrichedBriefing()`).\n */\nexport function formatEnrichedBriefing(briefing: EnrichedBriefing): string {\n const { tier, core } = briefing;\n const lines: string[] = [];\n\n // Header\n lines.push('## Re-entry Briefing');\n lines.push('');\n\n // Worktree context\n if (briefing.isWorktree && briefing.gitBranch) {\n lines.push(`**Worktree context:** Scoped to branch \\`${briefing.gitBranch}\\``);\n lines.push('');\n }\n\n // Compact: minimal 3-field output\n if (tier === 'compact') {\n lines.push(`**Last worked:** ${core.lastWorked}`);\n lines.push(`**Focus:** ${core.currentFocus}`);\n lines.push(`**Quick start:** ${core.smallNextStep}`);\n return lines.join('\\n');\n }\n\n // Standard+: all 5 core fields\n lines.push(`**Last worked:** ${core.lastWorked}`);\n lines.push(`**Current focus:** ${core.currentFocus}`);\n lines.push(`**Recent activity:** ${core.recentActivity}`);\n lines.push(`**Suggested next:** ${core.suggestedNext}`);\n lines.push(`**Quick start:** ${core.smallNextStep}`);\n\n if (briefing.blocker) {\n lines.push(`**Blocker:** ${briefing.blocker}`);\n }\n\n // Detailed+: decisions\n if (briefing.decisions && briefing.decisions.length > 0) {\n lines.push('');\n lines.push('### Recent decisions');\n for (const d of briefing.decisions) {\n const rationale = d.rationale ? ` - ${d.rationale}` : '';\n lines.push(`- **${d.classification.category}:** ${d.commitMessage}${rationale}`);\n }\n }\n\n // Detailed+: touched files\n if (briefing.touchedFiles && briefing.touchedFiles.length > 0) {\n lines.push('');\n lines.push(`### Files touched (${briefing.touchedFiles.length})`);\n for (const f of briefing.touchedFiles) {\n lines.push(`- ${f}`);\n }\n }\n\n // Full: session history\n if (briefing.sessionHistory && briefing.sessionHistory.length > 0) {\n lines.push('');\n lines.push('### Session history');\n for (const s of briefing.sessionHistory) {\n const relTime = formatRelativeTime(s.timestamp);\n const branch = s.branch ? ` (${s.branch})` : '';\n lines.push(`- **${relTime}${branch}:** ${s.summary || 'No summary'}. Next: ${s.nextStep || 'Not specified'}`);\n }\n }\n\n // Full: recent commits\n if (briefing.recentCommits && briefing.recentCommits.length > 0) {\n lines.push('');\n lines.push('### Recent commits');\n for (const msg of briefing.recentCommits.slice(0, 10)) {\n lines.push(`- ${msg}`);\n }\n }\n\n // Full: cross-session intelligence\n if (briefing.fileConflicts && briefing.fileConflicts.length > 0) {\n lines.push('');\n lines.push('### File conflicts');\n lines.push('Multiple sessions are editing the same files:');\n for (const c of briefing.fileConflicts) {\n const sessionLabels = c.sessions.map(s => s.agentLabel || s.sessionId).join(', ');\n lines.push(`- \\`${c.file}\\`: ${sessionLabels}`);\n }\n }\n\n if (briefing.branchOverlaps && briefing.branchOverlaps.length > 0) {\n lines.push('');\n lines.push('### Branch overlaps');\n lines.push('Multiple sessions on the same branch:');\n for (const o of briefing.branchOverlaps) {\n const sessionLabels = o.sessions.map(s => s.agentLabel || s.sessionId).join(', ');\n lines.push(`- \\`${o.branch}\\`: ${sessionLabels}`);\n }\n }\n\n return lines.join('\\n');\n}\n","import type {\n SessionCheckpoint,\n ReEntryBriefing,\n DecisionRecord,\n CurrentTask,\n} from './types';\nimport { KeepGoingReader } from './reader';\nimport { generateBriefing } from './reentry';\nimport { formatRelativeTime } from './timeUtils';\nimport { getCommitMessagesSince } from './gitUtils';\nimport path from 'node:path';\n\n/**\n * All context gathered from KeepGoing for export to another AI tool.\n */\nexport interface ContinueOnContext {\n projectName: string;\n gitBranch?: string;\n briefing?: ReEntryBriefing;\n lastCheckpoint?: SessionCheckpoint;\n recentCheckpoints: SessionCheckpoint[];\n recentDecisions: DecisionRecord[];\n currentTasks: CurrentTask[];\n recentCommitMessages: string[];\n}\n\n/**\n * Options controlling how the prompt is formatted.\n */\nexport interface FormatOptions {\n target?: 'chatgpt' | 'gemini' | 'copilot' | 'claude' | 'general';\n includeCommits?: boolean;\n includeFiles?: boolean;\n maxLength?: number;\n}\n\n/**\n * Gathers all available KeepGoing context for a workspace.\n * Uses existing KeepGoingReader methods (worktree-aware).\n */\nexport function gatherContinueOnContext(\n reader: KeepGoingReader,\n workspacePath: string,\n): ContinueOnContext {\n const projectName = path.basename(workspacePath);\n const gitBranch = reader.getCurrentBranch();\n\n if (!reader.exists()) {\n return {\n projectName,\n gitBranch,\n recentCheckpoints: [],\n recentDecisions: [],\n currentTasks: [],\n recentCommitMessages: [],\n };\n }\n\n const { session: lastCheckpoint } = reader.getScopedLastSession();\n const recentCheckpoints = reader.getScopedRecentSessions(5);\n const recentDecisions = reader.getScopedRecentDecisions(3);\n const currentTasks = reader.getCurrentTasks();\n const state = reader.getState() ?? {};\n\n const sinceTimestamp = lastCheckpoint?.timestamp;\n const recentCommitMessages = sinceTimestamp\n ? getCommitMessagesSince(workspacePath, sinceTimestamp)\n : [];\n\n const briefing = generateBriefing(\n lastCheckpoint,\n recentCheckpoints,\n state,\n gitBranch,\n recentCommitMessages,\n );\n\n return {\n projectName,\n gitBranch,\n briefing,\n lastCheckpoint,\n recentCheckpoints,\n recentDecisions,\n currentTasks,\n recentCommitMessages,\n };\n}\n\n/**\n * Formats gathered context into a markdown prompt suitable for pasting\n * into any AI chat tool (ChatGPT, Gemini, Claude, Copilot, etc.).\n */\nexport function formatContinueOnPrompt(\n context: ContinueOnContext,\n options?: FormatOptions,\n): string {\n const opts: FormatOptions = { includeCommits: true, includeFiles: true, ...options };\n const lines: string[] = [];\n\n lines.push(`# Project Context: ${context.projectName}`);\n lines.push('');\n lines.push('I\\'m continuing work on a project. Here is my development context from KeepGoing.');\n\n // Current Status\n lines.push('');\n lines.push('## Current Status');\n if (context.gitBranch) {\n lines.push(`- **Branch:** ${context.gitBranch}`);\n }\n if (context.briefing) {\n lines.push(`- **Last worked:** ${context.briefing.lastWorked}`);\n lines.push(`- **Focus:** ${context.briefing.currentFocus}`);\n } else if (context.lastCheckpoint) {\n lines.push(`- **Last worked:** ${formatRelativeTime(context.lastCheckpoint.timestamp)}`);\n if (context.lastCheckpoint.summary) {\n lines.push(`- **Focus:** ${context.lastCheckpoint.summary}`);\n }\n }\n\n // Last Session\n if (context.lastCheckpoint) {\n lines.push('');\n lines.push('## Last Session');\n if (context.lastCheckpoint.summary) {\n lines.push(`- **Summary:** ${context.lastCheckpoint.summary}`);\n }\n if (context.lastCheckpoint.nextStep) {\n lines.push(`- **Next step:** ${context.lastCheckpoint.nextStep}`);\n }\n if (context.lastCheckpoint.blocker) {\n lines.push(`- **Blocker:** ${context.lastCheckpoint.blocker}`);\n }\n if (opts.includeFiles && context.lastCheckpoint.touchedFiles.length > 0) {\n const files = context.lastCheckpoint.touchedFiles.slice(0, 10).join(', ');\n const extra = context.lastCheckpoint.touchedFiles.length > 10\n ? ` (+${context.lastCheckpoint.touchedFiles.length - 10} more)`\n : '';\n lines.push(`- **Files touched:** ${files}${extra}`);\n }\n }\n\n // Active sessions\n const activeTasks = context.currentTasks.filter(t => t.sessionActive);\n if (activeTasks.length > 0) {\n lines.push('');\n lines.push('## Active Sessions');\n for (const task of activeTasks) {\n const label = task.agentLabel || 'Unknown agent';\n const branch = task.branch ? ` on \\`${task.branch}\\`` : '';\n lines.push(`- **${label}**${branch}: ${task.taskSummary || 'Working'}`);\n }\n }\n\n // Recent decisions\n if (context.recentDecisions.length > 0) {\n lines.push('');\n lines.push('## Recent Decisions');\n for (const d of context.recentDecisions) {\n lines.push(`- ${d.classification.category}: ${d.commitMessage}`);\n }\n }\n\n // Recent commits\n if (opts.includeCommits && context.recentCommitMessages.length > 0) {\n lines.push('');\n lines.push('## Recent Commits');\n const commits = context.recentCommitMessages.slice(0, 10);\n for (const msg of commits) {\n lines.push(`- ${msg}`);\n }\n }\n\n // CTA\n lines.push('');\n lines.push('---');\n const nextStep = context.lastCheckpoint?.nextStep\n || context.briefing?.suggestedNext\n || 'continue where I left off';\n lines.push(`Please help me continue this work. My next step is: ${nextStep}.`);\n\n let result = lines.join('\\n');\n\n // Rough trim if maxLength is set\n if (opts.maxLength && result.length > opts.maxLength) {\n result = result.slice(0, opts.maxLength - 3) + '...';\n }\n\n return result;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { randomUUID, createHash } from 'node:crypto';\nimport type { SessionCheckpoint, ProjectSessions, ProjectState, ProjectMeta, CurrentTask, CurrentTasks } from './types';\nimport { resolveStorageRoot } from './gitUtils';\nimport { registerProject } from './registry';\n\nconst STORAGE_DIR = '.keepgoing';\nconst META_FILE = 'meta.json';\nconst SESSIONS_FILE = 'sessions.json';\nconst STATE_FILE = 'state.json';\nconst CURRENT_TASKS_FILE = 'current-tasks.json';\n\n/** Stale threshold: sessions inactive for longer than this are pruned on read. */\nexport const STALE_SESSION_MS = 2 * 60 * 60 * 1000; // 2 hours\n\n/** Filter out stale sessions (active or finished) inactive longer than STALE_SESSION_MS. */\nexport function pruneStaleTasks(tasks: CurrentTask[]): CurrentTask[] {\n const now = Date.now();\n return tasks.filter(t => {\n const updatedAt = new Date(t.updatedAt).getTime();\n return !isNaN(updatedAt) && (now - updatedAt) < STALE_SESSION_MS;\n });\n}\n\n/**\n * Write layer for .keepgoing/ directory.\n * Creates files if they don't exist yet.\n */\nexport class KeepGoingWriter {\n private readonly storagePath: string;\n private readonly sessionsFilePath: string;\n private readonly stateFilePath: string;\n private readonly metaFilePath: string;\n private readonly currentTasksFilePath: string;\n\n constructor(workspacePath: string) {\n const mainRoot = resolveStorageRoot(workspacePath);\n this.storagePath = path.join(mainRoot, STORAGE_DIR);\n this.sessionsFilePath = path.join(this.storagePath, SESSIONS_FILE);\n this.stateFilePath = path.join(this.storagePath, STATE_FILE);\n this.metaFilePath = path.join(this.storagePath, META_FILE);\n this.currentTasksFilePath = path.join(this.storagePath, CURRENT_TASKS_FILE);\n }\n\n ensureDir(): void {\n if (!fs.existsSync(this.storagePath)) {\n fs.mkdirSync(this.storagePath, { recursive: true });\n }\n }\n\n saveCheckpoint(checkpoint: SessionCheckpoint, projectName: string): void {\n this.ensureDir();\n\n // Read existing sessions\n let sessionsData: ProjectSessions;\n try {\n if (fs.existsSync(this.sessionsFilePath)) {\n const raw = JSON.parse(fs.readFileSync(this.sessionsFilePath, 'utf-8')) as\n | ProjectSessions\n | SessionCheckpoint[];\n if (Array.isArray(raw)) {\n sessionsData = { version: 1, project: projectName, sessions: raw };\n } else {\n sessionsData = raw;\n }\n } else {\n sessionsData = { version: 1, project: projectName, sessions: [] };\n }\n } catch {\n sessionsData = { version: 1, project: projectName, sessions: [] };\n }\n\n sessionsData.sessions.push(checkpoint);\n sessionsData.lastSessionId = checkpoint.id;\n\n // Prune old sessions to keep the file bounded\n const MAX_SESSIONS = 200;\n if (sessionsData.sessions.length > MAX_SESSIONS) {\n sessionsData.sessions = sessionsData.sessions.slice(-MAX_SESSIONS);\n }\n\n fs.writeFileSync(this.sessionsFilePath, JSON.stringify(sessionsData, null, 2), 'utf-8');\n\n // Update state.json\n const state: ProjectState = {\n lastSessionId: checkpoint.id,\n lastKnownBranch: checkpoint.gitBranch,\n lastActivityAt: checkpoint.timestamp,\n };\n fs.writeFileSync(this.stateFilePath, JSON.stringify(state, null, 2), 'utf-8');\n\n this.updateMeta(checkpoint.timestamp);\n\n // Register project in global known-projects registry\n const mainRoot = resolveStorageRoot(this.storagePath);\n registerProject(mainRoot, projectName);\n }\n\n private updateMeta(timestamp: string): void {\n let meta: ProjectMeta;\n try {\n if (fs.existsSync(this.metaFilePath)) {\n meta = JSON.parse(fs.readFileSync(this.metaFilePath, 'utf-8')) as ProjectMeta;\n meta.lastUpdated = timestamp;\n } else {\n meta = {\n projectId: randomUUID(),\n createdAt: timestamp,\n lastUpdated: timestamp,\n };\n }\n } catch {\n meta = {\n projectId: randomUUID(),\n createdAt: timestamp,\n lastUpdated: timestamp,\n };\n }\n fs.writeFileSync(this.metaFilePath, JSON.stringify(meta, null, 2), 'utf-8');\n }\n\n // ---------------------------------------------------------------------------\n // Multi-session API\n // ---------------------------------------------------------------------------\n\n /** Read all current tasks from current-tasks.json. Auto-prunes stale sessions. */\n readCurrentTasks(): CurrentTask[] {\n try {\n if (fs.existsSync(this.currentTasksFilePath)) {\n const raw = JSON.parse(fs.readFileSync(this.currentTasksFilePath, 'utf-8')) as CurrentTasks | CurrentTask[];\n const tasks = Array.isArray(raw) ? raw : (raw.tasks ?? []);\n return this.pruneStale(tasks);\n }\n } catch {\n // Fall through\n }\n\n return [];\n }\n\n /**\n * Upsert a session task by sessionId into current-tasks.json.\n * If no sessionId is present on the task, generates one.\n */\n upsertSession(update: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string }): void {\n this.ensureDir();\n this.upsertSessionCore(update);\n\n // Register project in global known-projects registry\n const mainRoot = resolveStorageRoot(this.storagePath);\n registerProject(mainRoot);\n }\n\n /** Core upsert logic: merges the update into current-tasks.json and returns the pruned task list. */\n private upsertSessionCore(update: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string }): CurrentTask[] {\n this.ensureDir();\n\n const sessionId = update.sessionId || generateSessionId(update);\n const tasks = this.readAllTasksRaw();\n\n const existingIdx = tasks.findIndex(t => t.sessionId === sessionId);\n let merged: CurrentTask;\n\n if (existingIdx >= 0) {\n const existing = tasks[existingIdx];\n merged = { ...existing, ...update, sessionId } as CurrentTask;\n tasks[existingIdx] = merged;\n } else {\n merged = { ...update, sessionId } as CurrentTask;\n tasks.push(merged);\n }\n\n const pruned = this.pruneStale(tasks);\n this.writeTasksFile(pruned);\n return pruned;\n }\n\n /** Remove a specific session by ID. */\n removeSession(sessionId: string): void {\n const tasks = this.readAllTasksRaw().filter(t => t.sessionId !== sessionId);\n this.writeTasksFile(tasks);\n }\n\n /** Get all active sessions (sessionActive=true and within stale threshold). */\n getActiveSessions(): CurrentTask[] {\n return this.readCurrentTasks().filter(t => t.sessionActive);\n }\n\n /** Get a specific session by ID. */\n getSession(sessionId: string): CurrentTask | undefined {\n return this.readCurrentTasks().find(t => t.sessionId === sessionId);\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private readAllTasksRaw(): CurrentTask[] {\n try {\n if (fs.existsSync(this.currentTasksFilePath)) {\n const raw = JSON.parse(fs.readFileSync(this.currentTasksFilePath, 'utf-8')) as CurrentTasks | CurrentTask[];\n return Array.isArray(raw) ? [...raw] : [...(raw.tasks ?? [])];\n }\n } catch {\n // Fall through\n }\n return [];\n }\n\n private pruneStale(tasks: CurrentTask[]): CurrentTask[] {\n return pruneStaleTasks(tasks);\n }\n\n private writeTasksFile(tasks: CurrentTask[]): void {\n const data: CurrentTasks = { version: 1, tasks };\n fs.writeFileSync(this.currentTasksFilePath, JSON.stringify(data, null, 2), 'utf-8');\n }\n}\n\n/**\n * Generates a stable session ID from task context.\n * Combines worktree path, agent label, and branch into a deterministic hash.\n * Falls back to a random UUID if no context is available.\n */\nexport function generateSessionId(context: Partial<CurrentTask>): string {\n const parts = [\n context.worktreePath || context.workspaceRoot || '',\n context.agentLabel || '',\n context.branch || '',\n ].filter(Boolean);\n\n if (parts.length === 0) {\n return randomUUID();\n }\n\n const hash = createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 12);\n return `ses_${hash}`;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface KnownProject {\n path: string;\n name: string;\n lastSeen: string;\n}\n\nexport interface KnownProjectsFile {\n version: number;\n projects: KnownProject[];\n}\n\nconst KEEPGOING_DIR = path.join(os.homedir(), '.keepgoing');\nconst KNOWN_PROJECTS_FILE = path.join(KEEPGOING_DIR, 'known-projects.json');\n\n/** Projects not seen for 90 days are pruned. */\nconst STALE_PROJECT_MS = 90 * 24 * 60 * 60 * 1000;\n\nfunction readKnownProjects(): KnownProjectsFile {\n try {\n if (fs.existsSync(KNOWN_PROJECTS_FILE)) {\n const raw = JSON.parse(fs.readFileSync(KNOWN_PROJECTS_FILE, 'utf-8'));\n if (raw && Array.isArray(raw.projects)) {\n return raw as KnownProjectsFile;\n }\n }\n } catch {\n // Corrupted file, start fresh\n }\n return { version: 1, projects: [] };\n}\n\nfunction writeKnownProjects(data: KnownProjectsFile): void {\n if (!fs.existsSync(KEEPGOING_DIR)) {\n fs.mkdirSync(KEEPGOING_DIR, { recursive: true });\n }\n // Atomic write: temp file + rename\n const tmpFile = KNOWN_PROJECTS_FILE + '.tmp';\n fs.writeFileSync(tmpFile, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n fs.renameSync(tmpFile, KNOWN_PROJECTS_FILE);\n}\n\n/**\n * Register a project in ~/.keepgoing/known-projects.json.\n * Upserts by path, updates lastSeen, prunes stale entries.\n * Safe to call frequently; silently swallows errors.\n */\nexport function registerProject(projectPath: string, projectName?: string): void {\n try {\n const data = readKnownProjects();\n const now = new Date().toISOString();\n const name = projectName || path.basename(projectPath);\n\n const existingIdx = data.projects.findIndex(p => p.path === projectPath);\n if (existingIdx >= 0) {\n data.projects[existingIdx].lastSeen = now;\n if (projectName) {\n data.projects[existingIdx].name = name;\n }\n } else {\n data.projects.push({ path: projectPath, name, lastSeen: now });\n }\n\n // Prune stale projects (not seen for 90 days)\n const cutoff = Date.now() - STALE_PROJECT_MS;\n data.projects = data.projects.filter(p => new Date(p.lastSeen).getTime() > cutoff);\n\n writeKnownProjects(data);\n } catch {\n // Never fail the caller over registry writes\n }\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport type { SessionEvents, SessionCheckpoint, ProjectSessions } from './types';\nimport { resolveStorageRoot } from './gitUtils';\nimport { getCommitsSince, getCommitMessagesSince, getTouchedFiles, getCurrentBranch, getFilesChangedInCommit } from './gitUtils';\nimport { createCheckpoint } from './session';\nimport { KeepGoingWriter } from './storage';\n\n/** Map of conventional commit prefixes to past-tense verbs. */\nconst PREFIX_VERBS: Record<string, string> = {\n feat: 'Added',\n fix: 'Fixed',\n refactor: 'Refactored',\n docs: 'Updated docs for',\n test: 'Added tests for',\n chore: 'Updated',\n style: 'Styled',\n perf: 'Optimized',\n ci: 'Updated CI for',\n build: 'Updated build for',\n revert: 'Reverted',\n};\n\n/** Files/directories to ignore when inferring work areas. */\nconst NOISE_PATTERNS = [\n 'node_modules',\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n '.gitignore',\n '.DS_Store',\n 'dist/',\n 'out/',\n 'build/',\n];\n\n/**\n * Parses conventional commit messages into groups by prefix.\n * Non-conventional messages go into the \"other\" bucket.\n *\n * Returns a map of prefix -> array of cleaned messages (scope stripped).\n */\nexport function categorizeCommits(messages: string[]): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n\n for (const msg of messages) {\n const match = msg.match(/^(\\w+)(?:\\([^)]*\\))?[!]?:\\s*(.+)/);\n if (match) {\n const prefix = match[1].toLowerCase();\n const body = match[2].trim();\n if (!groups.has(prefix)) {\n groups.set(prefix, []);\n }\n groups.get(prefix)!.push(body);\n } else {\n if (!groups.has('other')) {\n groups.set('other', []);\n }\n groups.get('other')!.push(msg.trim());\n }\n }\n\n return groups;\n}\n\n/**\n * Groups files by top-level directory, monorepo-aware.\n * Returns human-readable area names like \"extension\", \"shared storage\", \"web\".\n */\nexport function inferWorkAreas(files: string[]): string[] {\n const areas = new Map<string, number>();\n\n for (const file of files) {\n // Skip noise\n if (NOISE_PATTERNS.some(p => file.includes(p))) {\n continue;\n }\n\n const parts = file.split('/').filter(Boolean);\n let area: string;\n\n // Monorepo-aware: apps/X or packages/X -> use X\n if (parts.length >= 2 && (parts[0] === 'apps' || parts[0] === 'packages')) {\n area = parts[1];\n // Add sub-area for deeper paths in packages (e.g. \"shared/storage\")\n if (parts[0] === 'packages' && parts.length >= 4 && parts[2] === 'src') {\n const subFile = parts[3].replace(/\\.\\w+$/, '');\n area = `${parts[1]} ${subFile}`;\n }\n } else if (parts.length >= 2) {\n area = parts[0];\n } else {\n area = 'root';\n }\n\n areas.set(area, (areas.get(area) ?? 0) + 1);\n }\n\n // Sort by file count descending, return top 3\n return [...areas.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map(([name]) => name);\n}\n\n/**\n * Builds a SessionEvents object from raw git data.\n *\n * Encapsulates the common pattern of mapping commit hashes to full commit\n * objects (with per-commit file lists), computing the committed-file set,\n * and deriving `hasUncommittedChanges`.\n */\nexport function buildSessionEvents(opts: {\n wsPath: string;\n commitHashes: string[];\n commitMessages: string[];\n touchedFiles: string[];\n currentBranch?: string;\n sessionStartTime: string;\n lastActivityTime: string;\n}): SessionEvents {\n const { wsPath, commitHashes, commitMessages, touchedFiles, currentBranch, sessionStartTime, lastActivityTime } = opts;\n const commits = commitHashes.map((hash, i) => ({\n hash,\n message: commitMessages[i] ?? '',\n filesChanged: getFilesChangedInCommit(wsPath, hash),\n timestamp: new Date().toISOString(),\n }));\n const committedFiles = new Set(commits.flatMap(c => c.filesChanged));\n return {\n commits,\n branchSwitches: [],\n touchedFiles,\n currentBranch,\n sessionStartTime,\n lastActivityTime,\n // Normalize rename arrows (\"old -> new\") from git status --porcelain\n // so they match the plain filenames from git diff-tree --name-only.\n hasUncommittedChanges: touchedFiles.some(f => {\n const normalized = f.includes(' -> ') ? f.split(' -> ').pop()! : f;\n return !committedFiles.has(normalized);\n }),\n };\n}\n\n/**\n * Builds a smart summary from accumulated session events.\n *\n * Groups commits by conventional prefix, maps to past-tense verbs,\n * and combines into a readable sentence. Falls back to file-area inference\n * when there are no commits.\n *\n * Returns undefined if nothing meaningful happened (skip checkpoint).\n */\nexport function buildSmartSummary(events: SessionEvents): string | undefined {\n const { commits, branchSwitches, touchedFiles, hasUncommittedChanges } = events;\n\n if (commits.length === 0 && touchedFiles.length === 0 && branchSwitches.length === 0) {\n return undefined;\n }\n\n const parts: string[] = [];\n\n if (commits.length > 0) {\n const messages = commits.map(c => c.message);\n const groups = categorizeCommits(messages);\n\n // Build summary phrases from each group\n const phrases: string[] = [];\n for (const [prefix, bodies] of groups) {\n const verb = PREFIX_VERBS[prefix] ?? (prefix === 'other' ? '' : `${capitalize(prefix)}:`);\n // Take up to 2 items per group\n const items = bodies.slice(0, 2).join(' and ');\n const overflow = bodies.length > 2 ? ` (+${bodies.length - 2} more)` : '';\n if (verb) {\n phrases.push(`${verb} ${items}${overflow}`);\n } else {\n phrases.push(`${items}${overflow}`);\n }\n }\n\n parts.push(phrases.join(', '));\n } else if (touchedFiles.length > 0) {\n // No commits, describe file work\n const areas = inferWorkAreas(touchedFiles);\n const areaStr = areas.length > 0 ? areas.join(' and ') : `${touchedFiles.length} files`;\n const suffix = hasUncommittedChanges ? ' (uncommitted)' : '';\n parts.push(`Worked on ${areaStr}${suffix}`);\n }\n\n // Mention branch switches if they occurred\n if (branchSwitches.length > 0) {\n const last = branchSwitches[branchSwitches.length - 1];\n if (branchSwitches.length === 1) {\n parts.push(`switched to ${last.toBranch}`);\n } else {\n parts.push(`switched branches ${branchSwitches.length} times, ended on ${last.toBranch}`);\n }\n }\n\n const result = parts.join('; ');\n return result || undefined;\n}\n\n/**\n * Builds a smart next-step suggestion from accumulated session events.\n *\n * Priority chain:\n * 1. Uncommitted changes -> \"Review and commit changes in [area]\"\n * 2. Last commit is WIP -> \"Continue [topic]\"\n * 3. Feature branch -> \"Continue [feature name from branch]\"\n * 4. Fallback -> \"Review recent changes in [area]\"\n */\nexport function buildSmartNextStep(events: SessionEvents): string {\n const { commits, touchedFiles, currentBranch, hasUncommittedChanges } = events;\n\n // 1. Uncommitted changes\n if (hasUncommittedChanges && touchedFiles.length > 0) {\n const areas = inferWorkAreas(touchedFiles);\n const areaStr = areas.length > 0 ? areas.join(' and ') : 'working tree';\n return `Review and commit changes in ${areaStr}`;\n }\n\n // 2. Last commit is WIP\n if (commits.length > 0) {\n const lastMsg = commits[commits.length - 1].message;\n const wipMatch = lastMsg.match(/^(?:wip|work in progress|start(?:ed)?|begin|draft)[:\\s]+(.+)/i);\n if (wipMatch) {\n return `Continue ${wipMatch[1].trim()}`;\n }\n }\n\n // 3. Feature branch\n if (currentBranch && !['main', 'master', 'develop', 'HEAD'].includes(currentBranch)) {\n const branchName = currentBranch\n .replace(/^(feat|feature|fix|bugfix|hotfix|chore|refactor)[/-]/i, '')\n .replace(/[-_]/g, ' ')\n .trim();\n if (branchName) {\n return `Continue ${branchName}`;\n }\n }\n\n // 4. Fallback with areas\n if (touchedFiles.length > 0) {\n const areas = inferWorkAreas(touchedFiles);\n if (areas.length > 0) {\n return `Review recent changes in ${areas.join(' and ')}`;\n }\n }\n\n return '';\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n// ---------------------------------------------------------------------------\n// Missed checkpoint recovery\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the last checkpoint from the .keepgoing/sessions.json file.\n * Standalone reader to avoid circular dependencies with storage consumers.\n */\nfunction readLastCheckpoint(wsPath: string): SessionCheckpoint | undefined {\n try {\n const storageRoot = resolveStorageRoot(wsPath);\n const sessionsPath = path.join(storageRoot, '.keepgoing', 'sessions.json');\n if (!fs.existsSync(sessionsPath)) {\n return undefined;\n }\n const raw = JSON.parse(fs.readFileSync(sessionsPath, 'utf-8')) as ProjectSessions | SessionCheckpoint[];\n const sessions = Array.isArray(raw) ? raw : raw.sessions;\n return sessions.length > 0 ? sessions[sessions.length - 1] : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Checks for commits since the last checkpoint that were never recorded\n * (e.g. due to a crash, force-kill, or unclean exit) and writes a\n * recovery checkpoint if needed.\n *\n * Safe to call on every startup. Returns the recovery checkpoint if one\n * was written, or undefined if no recovery was needed.\n */\nexport function recoverMissedCheckpoint(wsPath: string): SessionCheckpoint | undefined {\n try {\n const lastCheckpoint = readLastCheckpoint(wsPath);\n const sinceTimestamp = lastCheckpoint?.timestamp;\n const commitHashes = getCommitsSince(wsPath, sinceTimestamp);\n\n if (commitHashes.length === 0) {\n return undefined;\n }\n\n // Check if these commits were already captured by the last checkpoint\n if (lastCheckpoint?.commitHashes) {\n const captured = new Set(lastCheckpoint.commitHashes);\n const uncaptured = commitHashes.filter(h => !captured.has(h));\n if (uncaptured.length === 0) {\n return undefined;\n }\n }\n\n const commitMessages = getCommitMessagesSince(wsPath, sinceTimestamp);\n const touchedFiles = getTouchedFiles(wsPath);\n const gitBranch = getCurrentBranch(wsPath);\n\n const events = buildSessionEvents({\n wsPath,\n commitHashes,\n commitMessages,\n touchedFiles,\n currentBranch: gitBranch ?? undefined,\n sessionStartTime: sinceTimestamp ?? new Date().toISOString(),\n lastActivityTime: new Date().toISOString(),\n });\n\n const summary = buildSmartSummary(events);\n if (!summary) {\n return undefined;\n }\n\n const nextStep = buildSmartNextStep(events);\n const checkpoint = createCheckpoint({\n summary,\n nextStep,\n gitBranch,\n touchedFiles,\n workspaceRoot: wsPath,\n source: 'auto',\n tags: ['recovery'],\n commitHashes,\n });\n\n const storageRoot = resolveStorageRoot(wsPath);\n const projectName = path.basename(storageRoot);\n const writer = new KeepGoingWriter(wsPath);\n writer.saveCheckpoint(checkpoint, projectName);\n\n console.log(`[KeepGoing] Recovery checkpoint saved: ${summary} (${commitHashes.length} missed commits)`);\n return checkpoint;\n } catch (err) {\n console.error('[KeepGoing] Recovery checkpoint failed:', err);\n return undefined;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { ProjectDecisions, DecisionRecord } from './types';\nimport { createEmptyProjectDecisions } from './session';\nimport { isDecisionsEnabled } from './featureGate';\nimport { resolveStorageRoot } from './gitUtils';\n\nconst STORAGE_DIR = '.keepgoing';\nconst DECISIONS_FILE = 'decisions.json';\nconst MAX_DECISIONS = 100;\n\n/**\n * Storage layer for persisting decision records as JSON files\n * within a .keepgoing/ folder in the workspace.\n *\n * saveDecision() always persists (captures drafts for all users).\n * updateDecision() is gated behind the 'decisions' feature flag (Pro perk).\n * Read operations are always available.\n */\nexport class DecisionStorage {\n private readonly storagePath: string;\n private readonly decisionsFilePath: string;\n\n constructor(workspacePath: string) {\n const mainRoot = resolveStorageRoot(workspacePath);\n this.storagePath = path.join(mainRoot, STORAGE_DIR);\n this.decisionsFilePath = path.join(this.storagePath, DECISIONS_FILE);\n }\n\n private ensureStorageDir(): void {\n if (!fs.existsSync(this.storagePath)) {\n fs.mkdirSync(this.storagePath, { recursive: true });\n }\n }\n\n private getProjectName(): string {\n return path.basename(path.dirname(this.storagePath));\n }\n\n private load(): ProjectDecisions {\n try {\n if (!fs.existsSync(this.decisionsFilePath)) {\n return createEmptyProjectDecisions(this.getProjectName());\n }\n const raw = fs.readFileSync(this.decisionsFilePath, 'utf-8');\n const data = JSON.parse(raw) as ProjectDecisions;\n return data;\n } catch {\n return createEmptyProjectDecisions(this.getProjectName());\n }\n }\n\n private save(decisions: ProjectDecisions): void {\n this.ensureStorageDir();\n const content = JSON.stringify(decisions, null, 2);\n fs.writeFileSync(this.decisionsFilePath, content, 'utf-8');\n }\n\n /**\n * Save a decision record as a draft. Always persists regardless of Pro\n * status so decisions are captured at the correct time. Returns true if\n * saved, false on I/O error.\n */\n saveDecision(decision: DecisionRecord): boolean {\n const data = this.load();\n data.decisions.push(decision);\n data.lastDecisionId = decision.id;\n\n if (data.decisions.length > MAX_DECISIONS) {\n data.decisions = data.decisions.slice(-MAX_DECISIONS);\n }\n\n this.save(data);\n return true;\n }\n\n getLastDecision(): DecisionRecord | undefined {\n const data = this.load();\n if (data.decisions.length === 0) {\n return undefined;\n }\n if (data.lastDecisionId) {\n const found = data.decisions.find(d => d.id === data.lastDecisionId);\n if (found) {\n return found;\n }\n }\n return data.decisions[data.decisions.length - 1];\n }\n\n getAllDecisions(): DecisionRecord[] {\n const data = this.load();\n return data.decisions;\n }\n\n getRecentDecisions(limit: number = 10): DecisionRecord[] {\n const data = this.load();\n return data.decisions.slice(-limit).reverse();\n }\n\n /**\n * Update a decision record. Returns true if updated, false if gated or not found.\n */\n updateDecision(id: string, updates: Partial<Omit<DecisionRecord, 'id'>>): boolean {\n if (!isDecisionsEnabled()) {\n return false;\n }\n\n const data = this.load();\n const index = data.decisions.findIndex(d => d.id === id);\n if (index === -1) {\n return false;\n }\n\n data.decisions[index] = {\n ...data.decisions[index],\n ...updates,\n };\n this.save(data);\n return true;\n }\n\n getStoragePath(): string {\n return this.storagePath;\n }\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { GatedFeature } from './featureGate';\n\nconst LICENSE_FILE = 'license.json';\nconst DEVICE_ID_FILE = 'device-id';\n\n/**\n * Directory for device-wide license storage: `~/.keepgoing/`\n */\nexport function getGlobalLicenseDir(): string {\n return path.join(os.homedir(), '.keepgoing');\n}\n\n/**\n * Full path to the global license cache: `~/.keepgoing/license.json`\n */\nexport function getGlobalLicensePath(): string {\n return path.join(getGlobalLicenseDir(), LICENSE_FILE);\n}\n\n/**\n * Returns a stable, persistent device identifier shared by all consumers\n * (VS Code extension, CLI, MCP server) so each physical machine counts\n * as one activation.\n *\n * The ID is a random UUID stored in `~/.keepgoing/device-id`. Once\n * generated it never changes, even if the hostname or OS is updated.\n */\nexport function getDeviceId(): string {\n const dir = getGlobalLicenseDir();\n const filePath = path.join(dir, DEVICE_ID_FILE);\n\n try {\n const existing = fs.readFileSync(filePath, 'utf-8').trim();\n if (existing) return existing;\n } catch {\n // File doesn't exist yet, generate below\n }\n\n const id = crypto.randomUUID();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, id, 'utf-8');\n return id;\n}\n\nexport type LicenseStatus = 'active' | 'inactive' | 'expired' | 'disabled';\n\n// ---------------------------------------------------------------------------\n// Variant-to-feature mapping\n// ---------------------------------------------------------------------------\n\n// Live store variant IDs\nexport const DECISION_DETECTION_VARIANT_ID = 1361527;\nexport const SESSION_AWARENESS_VARIANT_ID = 1366510;\n\n// Test store variant IDs\nconst TEST_DECISION_DETECTION_VARIANT_ID = 1345647;\nconst TEST_SESSION_AWARENESS_VARIANT_ID = 1365992;\n\n/**\n * Returns the numeric variant ID for the checkout `?enabled=` param.\n * Uses test store IDs in dev mode, live IDs in production.\n */\nexport function getCheckoutVariantId(feature: 'decisions' | 'session-awareness', isDev: boolean): number {\n if (feature === 'decisions') {\n return isDev ? TEST_DECISION_DETECTION_VARIANT_ID : DECISION_DETECTION_VARIANT_ID;\n }\n return isDev ? TEST_SESSION_AWARENESS_VARIANT_ID : SESSION_AWARENESS_VARIANT_ID;\n}\n\n/**\n * Maps each LemonSqueezy variant_id to the features it unlocks.\n * Single source of truth shared by all consumers.\n * Includes both live and test store variants.\n */\nexport const VARIANT_FEATURE_MAP: Record<number, GatedFeature[]> = {\n [DECISION_DETECTION_VARIANT_ID]: ['decisions'],\n [SESSION_AWARENESS_VARIANT_ID]: ['session-awareness'],\n [TEST_DECISION_DETECTION_VARIANT_ID]: ['decisions'],\n [TEST_SESSION_AWARENESS_VARIANT_ID]: ['session-awareness'],\n // Future bundle: [BUNDLE_VARIANT_ID]: ['decisions', 'session-awareness'],\n};\n\n/** Set of all known variant IDs for validation. */\nexport const KNOWN_VARIANT_IDS = new Set(Object.keys(VARIANT_FEATURE_MAP).map(Number));\n\n/**\n * Returns a human-readable label for a variant ID.\n */\nexport function getVariantLabel(variantId: number): string {\n const features = VARIANT_FEATURE_MAP[variantId];\n if (!features) return 'Unknown Add-on';\n if (features.includes('decisions') && features.includes('session-awareness')) return 'Pro Bundle';\n if (features.includes('decisions')) return 'Decision Detection';\n if (features.includes('session-awareness')) return 'Session Awareness';\n return 'Pro Add-on';\n}\n\n// ---------------------------------------------------------------------------\n// Multi-license storage\n// ---------------------------------------------------------------------------\n\nexport interface LicenseEntry {\n licenseKey: string;\n instanceId: string;\n status: LicenseStatus;\n lastValidatedAt: string; // ISO 8601\n activatedAt: string; // ISO 8601\n variantId: number;\n customerName?: string;\n productName?: string;\n variantName?: string;\n}\n\nexport interface LicenseStore {\n version: 2;\n licenses: LicenseEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Multi-license read/write\n// ---------------------------------------------------------------------------\n\n// Short-lived in-memory cache to avoid redundant disk reads within the same\n// call chain (e.g. getLicenseForFeature -> getActiveLicenses -> readLicenseStore).\nlet _cachedStore: LicenseStore | undefined;\nlet _cacheTimestamp = 0;\nconst LICENSE_CACHE_TTL_MS = 2_000; // 2 seconds\n\n/**\n * Read the license store from `~/.keepgoing/license.json`.\n * Uses a 2-second in-memory cache to coalesce repeated reads.\n * Returns an empty store if file is missing or corrupt.\n */\nexport function readLicenseStore(): LicenseStore {\n const now = Date.now();\n if (_cachedStore && now - _cacheTimestamp < LICENSE_CACHE_TTL_MS) {\n return _cachedStore;\n }\n\n const licensePath = getGlobalLicensePath();\n let store: LicenseStore;\n try {\n if (!fs.existsSync(licensePath)) {\n store = { version: 2, licenses: [] };\n } else {\n const raw = fs.readFileSync(licensePath, 'utf-8');\n const data = JSON.parse(raw);\n\n if (data?.version === 2 && Array.isArray(data.licenses)) {\n store = data as LicenseStore;\n } else {\n store = { version: 2, licenses: [] };\n }\n }\n } catch {\n store = { version: 2, licenses: [] };\n }\n\n _cachedStore = store;\n _cacheTimestamp = now;\n return store;\n}\n\n/**\n * Write the license store to `~/.keepgoing/license.json`.\n */\nexport function writeLicenseStore(store: LicenseStore): void {\n const dirPath = getGlobalLicenseDir();\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true });\n }\n const licensePath = path.join(dirPath, LICENSE_FILE);\n fs.writeFileSync(licensePath, JSON.stringify(store, null, 2), 'utf-8');\n // Invalidate read cache so the next read picks up the write immediately\n _cachedStore = store;\n _cacheTimestamp = Date.now();\n}\n\n/**\n * Add or update a license entry (upserts by licenseKey).\n */\nexport function addLicenseEntry(entry: LicenseEntry): void {\n const store = readLicenseStore();\n const idx = store.licenses.findIndex(l => l.licenseKey === entry.licenseKey);\n if (idx >= 0) {\n store.licenses[idx] = entry;\n } else {\n store.licenses.push(entry);\n }\n writeLicenseStore(store);\n}\n\n/**\n * Remove a license entry by licenseKey.\n */\nexport function removeLicenseEntry(licenseKey: string): void {\n const store = readLicenseStore();\n store.licenses = store.licenses.filter(l => l.licenseKey !== licenseKey);\n writeLicenseStore(store);\n}\n\n/**\n * Get all active license entries.\n */\nexport function getActiveLicenses(): LicenseEntry[] {\n return readLicenseStore().licenses.filter(l => l.status === 'active');\n}\n\n/**\n * Find the first active license whose variant maps to the given feature.\n */\nexport function getLicenseForFeature(feature: GatedFeature): LicenseEntry | undefined {\n const active = getActiveLicenses();\n return active.find(l => {\n const features = VARIANT_FEATURE_MAP[l.variantId];\n return features?.includes(feature);\n });\n}\n\n/**\n * Check if any license is currently active.\n */\nexport function isAnyLicenseActive(): boolean {\n return getActiveLicenses().length > 0;\n}\n\n/**\n * Get all active licenses that need revalidation (last validated > 24h ago).\n */\nexport function getAllLicensesNeedingRevalidation(): LicenseEntry[] {\n return getActiveLicenses().filter(l => needsRevalidation(l));\n}\n\nconst REVALIDATION_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * Check if a license entry needs revalidation (last validated > 24 hours ago).\n */\nexport function needsRevalidation(entry: LicenseEntry): boolean {\n const lastValidated = new Date(entry.lastValidatedAt).getTime();\n return Date.now() - lastValidated > REVALIDATION_THRESHOLD_MS;\n}\n","import type { LicenseStore } from './license';\nimport { VARIANT_FEATURE_MAP } from './license';\n\nexport type GatedFeature = 'decisions' | 'session-awareness';\n\nexport interface FeatureGate {\n isEnabled(feature: GatedFeature): boolean;\n}\n\nclass DefaultFeatureGate implements FeatureGate {\n isEnabled(_feature: GatedFeature): boolean {\n return true; // All features enabled until license system exists\n }\n}\n\n/**\n * Feature gate controlled by license status.\n * Tracks which features are enabled via a Set of GatedFeature values,\n * derived from the active licenses and their variant mappings.\n */\nexport class LicenseFeatureGate implements FeatureGate {\n private enabledFeatures: Set<GatedFeature>;\n\n constructor(proEnabled: boolean) {\n // Legacy constructor: enable all or none\n this.enabledFeatures = proEnabled\n ? new Set<GatedFeature>(['decisions', 'session-awareness'])\n : new Set<GatedFeature>();\n }\n\n isEnabled(feature: GatedFeature): boolean {\n return this.enabledFeatures.has(feature);\n }\n\n /**\n * Recompute enabled features from all active licenses in the store.\n */\n refreshFromStore(store: LicenseStore): void {\n const features = new Set<GatedFeature>();\n for (const entry of store.licenses) {\n if (entry.status !== 'active') continue;\n const mapped = VARIANT_FEATURE_MAP[entry.variantId];\n if (mapped) {\n for (const f of mapped) features.add(f);\n }\n }\n this.enabledFeatures = features;\n }\n\n /**\n * Directly set which features are enabled.\n */\n setEnabledFeatures(features: GatedFeature[]): void {\n this.enabledFeatures = new Set(features);\n }\n\n /**\n * @deprecated Use refreshFromStore() or setEnabledFeatures() instead.\n * Enables all features (true) or clears all (false).\n */\n setProEnabled(enabled: boolean): void {\n this.enabledFeatures = enabled\n ? new Set<GatedFeature>(['decisions', 'session-awareness'])\n : new Set<GatedFeature>();\n }\n}\n\nlet currentGate: FeatureGate = new DefaultFeatureGate();\n\nexport function getFeatureGate(): FeatureGate { return currentGate; }\nexport function setFeatureGate(gate: FeatureGate): void { currentGate = gate; }\nexport function isDecisionsEnabled(): boolean { return currentGate.isEnabled('decisions'); }\nexport function isSessionAwarenessEnabled(): boolean { return currentGate.isEnabled('session-awareness'); }\n","import { DecisionClassification, DecisionCategory } from './types';\nimport { createDecisionRecord } from './session';\nimport { DecisionStorage } from './decisionStorage';\nimport { isDecisionsEnabled } from './featureGate';\n\n/**\n * Input describing a git commit for decision classification.\n */\nexport interface CommitInfo {\n /** Full commit message */\n message: string;\n\n /** List of file paths changed in the commit (relative to repo root) */\n filesChanged: string[];\n\n /**\n * Conventional commit type, if known (e.g. 'ci', 'feat').\n * If omitted, it is parsed from the message automatically.\n */\n type?: string;\n\n /**\n * Conventional commit scope, if known (e.g. 'auth', 'db').\n * If omitted, it is parsed from the message automatically.\n */\n scope?: string;\n}\n\n/** Keywords in the commit message that suggest a decision was made, matched with word boundaries. */\nconst MESSAGE_KEYWORDS: RegExp[] = [\n /\\bdeploy\\b/,\n /\\bworkflow\\b/,\n /\\bmigrate\\b/,\n /\\bmigration\\b/,\n /\\bredirect\\b/,\n /\\borigin\\b/,\n /\\btrusted\\b/,\n /\\boidc\\b/,\n /\\boauth\\b/,\n /\\bpostgres\\b/,\n /\\bsupabase\\b/,\n /\\bdocker\\b/,\n /\\bterraform\\b/,\n /\\bk8s\\b/,\n];\n\n/** Conventional commit types/scopes that indicate high-signal changes. */\nconst HIGH_SIGNAL_TYPES = new Set([\n 'ci',\n 'build',\n 'infra',\n 'ops',\n 'auth',\n 'oauth',\n 'oidc',\n 'migration',\n 'db',\n]);\n\n/** Path patterns that reduce confidence (low-signal changes). */\nconst NEGATIVE_PATH_PATTERNS = [/\\.lock$/, /generated/i, /(?:^|\\/)dist\\//];\n\ntype PathTier = 'infra' | 'contextual';\ninterface PathMatch { label: string; tier: PathTier; }\n\n/**\n * Checks whether a file path matches a high-signal pattern.\n * Returns a {@link PathMatch} with the matched label and its tier, or null if no match.\n *\n * - **infra** tier: inherently significant (CI workflows, Dockerfiles, IaC, etc.)\n * - **contextual** tier: needs corroboration from another signal to be meaningful\n */\nfunction matchHighSignalPath(filePath: string): PathMatch | null {\n // Infra tier: .github/workflows is always at repo root\n if (/^\\.github\\/workflows\\//i.test(filePath)) {\n return { label: '.github/workflows', tier: 'infra' };\n }\n // Infra tier: the rest use (?:^|\\/) to support monorepo nesting\n if (/(?:^|\\/)fly\\.toml$/i.test(filePath)) {\n return { label: 'fly.toml', tier: 'infra' };\n }\n if (/(?:^|\\/)Dockerfile/i.test(filePath)) {\n return { label: 'Dockerfile', tier: 'infra' };\n }\n if (/(?:^|\\/)docker-compose/i.test(filePath)) {\n return { label: 'docker-compose', tier: 'infra' };\n }\n if (/(?:^|\\/)terraform\\//i.test(filePath)) {\n return { label: 'terraform/', tier: 'infra' };\n }\n if (/(?:^|\\/)k8s\\//i.test(filePath)) {\n return { label: 'k8s/', tier: 'infra' };\n }\n if (/(?:^|\\/)supabase\\//i.test(filePath)) {\n return { label: 'supabase/', tier: 'infra' };\n }\n if (/(?:^|\\/)migrations?(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*migration*', tier: 'infra' };\n }\n // Contextual tier: need corroboration\n if (/(?:^|\\/)auth(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*auth*', tier: 'contextual' };\n }\n if (/(?:^|\\/)oidc(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*oidc*', tier: 'contextual' };\n }\n if (/(?:^|\\/)oauth(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*oauth*', tier: 'contextual' };\n }\n if (/(?:^|\\/)redirect(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*redirect*', tier: 'contextual' };\n }\n if (/(?:^|\\/)origin(?:\\/|\\.)/i.test(filePath)) {\n return { label: '*origin*', tier: 'contextual' };\n }\n return null;\n}\n\n/**\n * Extracts the conventional commit type, scope, and breaking-change marker\n * from a commit message.\n * Handles formats like `feat(auth): ...`, `ci: ...`, or `feat!: ...`.\n */\nfunction parseConventionalCommit(message: string): { type?: string; scope?: string; breaking?: boolean } {\n const match = /^([a-z]+)(\\(([^)]+)\\))?\\s*(!)?:/.exec(message.trim());\n if (!match) {\n return {};\n }\n return { type: match[1], scope: match[3], breaking: match[4] === '!' };\n}\n\n/**\n * Infers a broad decision category from the signals that were matched.\n */\nfunction inferCategory(\n matchedKeywords: string[],\n matchedTypes: string[],\n matchedPaths: string[],\n): DecisionCategory {\n const all = [...matchedKeywords, ...matchedTypes, ...matchedPaths].join(' ').toLowerCase();\n if (/auth|oidc|oauth|redirect|origin|trusted/.test(all)) {\n return 'auth';\n }\n if (/migrat|postgres|db|supabase/.test(all)) {\n return 'migration';\n }\n if (/ci|workflow|build|deploy/.test(all)) {\n return 'deploy';\n }\n if (/fly|docker|k8s|terraform|infra|ops/.test(all)) {\n return 'infra';\n }\n return 'unknown';\n}\n\n/**\n * Classifies a git commit as a potential decision point using heuristic signals:\n * - Commit message keywords (deploy, migrate, oauth, etc.)\n * - Conventional commit type/scope (ci, auth, db, etc.)\n * - Changed file paths (.github/workflows, fly.toml, migrations, etc.)\n * - Negative filters (lock files, generated code, pure UI asset changes)\n *\n * Returns a classification with a confidence score (0–1) and human-readable reasons.\n */\nexport function classifyCommit(commit: CommitInfo): DecisionClassification {\n const { message, filesChanged } = commit;\n const messageLower = message.toLowerCase();\n\n // Parse conventional commit type/scope from message if not explicitly provided\n const parsed = parseConventionalCommit(message);\n const type = commit.type ?? parsed.type;\n const scope = commit.scope ?? parsed.scope;\n\n const reasons: string[] = [];\n let confidence = 0;\n\n // Signal 1: commit message keywords (word-boundary matching)\n const matchedKeywords = MESSAGE_KEYWORDS.filter(kw => kw.test(messageLower));\n if (matchedKeywords.length > 0) {\n confidence += 0.3;\n const labels = matchedKeywords.slice(0, 3).map(kw => kw.source.replace(/\\\\b/g, ''));\n reasons.push(`commit message contains: ${labels.join(', ')}`);\n }\n\n // Signal 2: conventional commit type\n const matchedTypes: string[] = [];\n if (type && HIGH_SIGNAL_TYPES.has(type)) {\n matchedTypes.push(`type:${type}`);\n confidence += 0.35;\n reasons.push(`conventional commit type '${type}' is high-signal`);\n }\n\n // Signal 3: conventional commit scope\n if (scope && HIGH_SIGNAL_TYPES.has(scope)) {\n matchedTypes.push(`scope:${scope}`);\n confidence += 0.25;\n reasons.push(`conventional commit scope '${scope}' is high-signal`);\n }\n\n // Signal 4: breaking change marker (feat!:, fix(auth)!:, etc.)\n if (parsed.breaking) {\n confidence += 0.4;\n reasons.push('breaking change indicated by ! marker');\n }\n\n // Signal 5: changed file paths (two-tier scoring)\n const matchedPaths: string[] = [];\n let bestTier: PathTier | null = null;\n for (const file of filesChanged) {\n const pm = matchHighSignalPath(file);\n if (pm && !matchedPaths.includes(pm.label)) {\n matchedPaths.push(pm.label);\n if (bestTier !== 'infra') {\n bestTier = pm.tier;\n }\n }\n }\n if (matchedPaths.length > 0) {\n // Infra paths are inherently significant (+0.40).\n // Contextual paths alone need corroboration (+0.20).\n confidence += bestTier === 'infra' ? 0.4 : 0.2;\n reasons.push(`commit touched: ${matchedPaths.slice(0, 3).join(', ')}`);\n }\n\n // Negative filter 1: all files are low-signal (lock, generated, dist)\n if (filesChanged.length > 0 && filesChanged.every(f => NEGATIVE_PATH_PATTERNS.some(p => p.test(f)))) {\n confidence -= 0.5;\n reasons.push('all changed files are low-signal (lock files, generated code, dist)');\n }\n\n // Negative filter 2: only UI asset changes (favicon, SVG, PNG)\n if (filesChanged.length > 0 && filesChanged.every(f => /favicon/i.test(f) || /\\.(svg|png|ico)$/i.test(f))) {\n confidence -= 0.5;\n reasons.push('all changed files are UI assets (favicon, SVG, PNG)');\n }\n\n // Negative filter 3: only CSS changes with Tailwind in message\n if (\n filesChanged.length > 0 &&\n filesChanged.every(f => /\\.css$/i.test(f)) &&\n /tailwind/i.test(messageLower)\n ) {\n confidence -= 0.5;\n reasons.push('Tailwind generated CSS change (low signal)');\n }\n\n const isDecisionCandidate = confidence >= 0.4;\n const keywordLabels = matchedKeywords.map(kw => kw.source.replace(/\\\\b/g, ''));\n const category = isDecisionCandidate\n ? inferCategory(keywordLabels, matchedTypes, matchedPaths)\n : 'unknown';\n\n return {\n isDecisionCandidate,\n confidence: Math.max(0, Math.min(1, confidence)),\n reasons,\n category,\n };\n}\n\n/**\n * Options for {@link tryDetectDecision}.\n */\nexport interface DetectDecisionOpts {\n workspacePath: string;\n checkpointId: string;\n gitBranch?: string;\n commitHash: string;\n commitMessage: string;\n filesChanged: string[];\n}\n\n/**\n * Result returned when a decision is detected.\n */\nexport interface DetectedDecision {\n category: DecisionCategory;\n confidence: number;\n}\n\n/**\n * Classifies a commit, and if it is a decision candidate, persists a\n * {@link DecisionRecord} via {@link DecisionStorage}.\n *\n * Returns the detected category and confidence when a decision was saved,\n * or `undefined` when the feature is gated or the commit is not a candidate.\n */\nexport function tryDetectDecision(opts: DetectDecisionOpts): DetectedDecision | undefined {\n if (!isDecisionsEnabled()) {\n return undefined;\n }\n\n const classification = classifyCommit({\n message: opts.commitMessage,\n filesChanged: opts.filesChanged,\n });\n\n if (!classification.isDecisionCandidate) {\n return undefined;\n }\n\n const decision = createDecisionRecord({\n checkpointId: opts.checkpointId,\n gitBranch: opts.gitBranch,\n commitHash: opts.commitHash,\n commitMessage: opts.commitMessage,\n filesChanged: opts.filesChanged,\n timestamp: new Date().toISOString(),\n classification,\n });\n\n const storage = new DecisionStorage(opts.workspacePath);\n storage.saveDecision(decision);\n\n return {\n category: classification.category,\n confidence: classification.confidence,\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { getRecentSessions } from './reentry';\nimport { readLicenseStore, type LicenseStore } from './license';\nimport { resolveStorageRoot, getCurrentBranch } from './gitUtils';\nimport { pruneStaleTasks } from './storage';\nimport type {\n SessionCheckpoint,\n ProjectSessions,\n ProjectState,\n ProjectMeta,\n DecisionRecord,\n ProjectDecisions,\n CurrentTask,\n CurrentTasks,\n} from './types';\n\nconst STORAGE_DIR = '.keepgoing';\nconst META_FILE = 'meta.json';\nconst SESSIONS_FILE = 'sessions.json';\nconst DECISIONS_FILE = 'decisions.json';\nconst STATE_FILE = 'state.json';\nconst CURRENT_TASKS_FILE = 'current-tasks.json';\n\n/** Result of worktree-aware branch scoping. */\nexport interface BranchScope {\n /** The branch to filter by, or undefined for all branches. */\n effectiveBranch: string | undefined;\n /** Human-readable label for output headers. */\n scopeLabel: string;\n}\n\n/**\n * Read-only reader for .keepgoing/ directory.\n * Does not write or create any files.\n */\nexport class KeepGoingReader {\n private readonly workspacePath: string;\n private readonly storagePath: string;\n private readonly metaFilePath: string;\n private readonly sessionsFilePath: string;\n private readonly decisionsFilePath: string;\n private readonly stateFilePath: string;\n private readonly currentTasksFilePath: string;\n private readonly _isWorktree: boolean;\n private _cachedBranch: string | undefined | null = null; // null = not yet resolved\n\n constructor(workspacePath: string) {\n this.workspacePath = workspacePath;\n const mainRoot = resolveStorageRoot(workspacePath);\n this._isWorktree = mainRoot !== workspacePath;\n this.storagePath = path.join(mainRoot, STORAGE_DIR);\n this.metaFilePath = path.join(this.storagePath, META_FILE);\n this.sessionsFilePath = path.join(this.storagePath, SESSIONS_FILE);\n this.decisionsFilePath = path.join(this.storagePath, DECISIONS_FILE);\n this.stateFilePath = path.join(this.storagePath, STATE_FILE);\n this.currentTasksFilePath = path.join(this.storagePath, CURRENT_TASKS_FILE);\n }\n\n /** Check if .keepgoing/ directory exists. */\n exists(): boolean {\n return fs.existsSync(this.storagePath);\n }\n\n /** Read state.json, returns undefined if missing or corrupt. */\n getState(): ProjectState | undefined {\n return this.readJsonFile<ProjectState>(this.stateFilePath);\n }\n\n /** Read meta.json, returns undefined if missing or corrupt. */\n getMeta(): ProjectMeta | undefined {\n return this.readJsonFile<ProjectMeta>(this.metaFilePath);\n }\n\n /**\n * Read sessions from sessions.json.\n * Handles both formats:\n * - Flat array: SessionCheckpoint[] (from ProjectStorage)\n * - Wrapper object: ProjectSessions (from SessionStorage)\n */\n getSessions(): SessionCheckpoint[] {\n return this.parseSessions().sessions;\n }\n\n /**\n * Get the most recent session checkpoint.\n * Uses state.lastSessionId if available, falls back to last in array.\n */\n getLastSession(): SessionCheckpoint | undefined {\n const { sessions, wrapperLastSessionId } = this.parseSessions();\n if (sessions.length === 0) {\n return undefined;\n }\n\n const state = this.getState();\n if (state?.lastSessionId) {\n const found = sessions.find((s) => s.id === state.lastSessionId);\n if (found) {\n return found;\n }\n }\n\n if (wrapperLastSessionId) {\n const found = sessions.find((s) => s.id === wrapperLastSessionId);\n if (found) {\n return found;\n }\n }\n\n return sessions[sessions.length - 1];\n }\n\n /**\n * Returns the last N sessions, newest first.\n */\n getRecentSessions(count: number): SessionCheckpoint[] {\n return getRecentSessions(this.getSessions(), count);\n }\n\n /** Read all decisions from decisions.json. */\n getDecisions(): DecisionRecord[] {\n return this.parseDecisions().decisions;\n }\n\n /** Returns the last N decisions, newest first. */\n getRecentDecisions(count: number): DecisionRecord[] {\n const all = this.getDecisions();\n return all.slice(-count).reverse();\n }\n\n /** Read the multi-license store from `~/.keepgoing/license.json`. */\n getLicenseStore(): LicenseStore {\n return readLicenseStore();\n }\n\n /**\n * Read all current tasks from current-tasks.json.\n * Automatically filters out stale finished sessions (> 2 hours).\n */\n getCurrentTasks(): CurrentTask[] {\n // Try multi-session file first\n const multiRaw = this.readJsonFile<CurrentTasks | CurrentTask[]>(this.currentTasksFilePath);\n if (multiRaw) {\n const tasks = Array.isArray(multiRaw) ? multiRaw : (multiRaw.tasks ?? []);\n return this.pruneStale(tasks);\n }\n\n return [];\n }\n\n /** Get only active sessions (sessionActive=true and within stale threshold). */\n getActiveTasks(): CurrentTask[] {\n return this.getCurrentTasks().filter(t => t.sessionActive);\n }\n\n /** Get a specific session by ID. */\n getTaskBySessionId(sessionId: string): CurrentTask | undefined {\n return this.getCurrentTasks().find(t => t.sessionId === sessionId);\n }\n\n /**\n * Detect files being edited by multiple sessions simultaneously.\n * Returns pairs of session IDs and the conflicting file paths.\n */\n detectFileConflicts(): Array<{ file: string; sessions: Array<{ sessionId: string; agentLabel?: string; branch?: string }> }> {\n const activeTasks = this.getActiveTasks();\n if (activeTasks.length < 2) return [];\n\n const fileToSessions = new Map<string, Array<{ sessionId: string; agentLabel?: string; branch?: string }>>();\n\n for (const task of activeTasks) {\n if (task.lastFileEdited && task.sessionId) {\n const existing = fileToSessions.get(task.lastFileEdited) ?? [];\n existing.push({\n sessionId: task.sessionId,\n agentLabel: task.agentLabel,\n branch: task.branch,\n });\n fileToSessions.set(task.lastFileEdited, existing);\n }\n }\n\n const conflicts: Array<{ file: string; sessions: Array<{ sessionId: string; agentLabel?: string; branch?: string }> }> = [];\n for (const [file, sessions] of fileToSessions) {\n if (sessions.length > 1) {\n conflicts.push({ file, sessions });\n }\n }\n return conflicts;\n }\n\n /**\n * Detect sessions on the same branch (possible duplicate work).\n */\n detectBranchOverlap(): Array<{ branch: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }> {\n const activeTasks = this.getActiveTasks();\n if (activeTasks.length < 2) return [];\n\n const branchToSessions = new Map<string, Array<{ sessionId: string; agentLabel?: string }>>();\n\n for (const task of activeTasks) {\n if (task.branch && task.sessionId) {\n const existing = branchToSessions.get(task.branch) ?? [];\n existing.push({ sessionId: task.sessionId, agentLabel: task.agentLabel });\n branchToSessions.set(task.branch, existing);\n }\n }\n\n const overlaps: Array<{ branch: string; sessions: Array<{ sessionId: string; agentLabel?: string }> }> = [];\n for (const [branch, sessions] of branchToSessions) {\n if (sessions.length > 1) {\n overlaps.push({ branch, sessions });\n }\n }\n return overlaps;\n }\n\n private pruneStale(tasks: CurrentTask[]): CurrentTask[] {\n return pruneStaleTasks(tasks);\n }\n\n /** Get the last session checkpoint for a specific branch. */\n getLastSessionForBranch(branch: string): SessionCheckpoint | undefined {\n const sessions = this.getSessions().filter(s => s.gitBranch === branch);\n return sessions.length > 0 ? sessions[sessions.length - 1] : undefined;\n }\n\n /** Returns the last N sessions for a specific branch, newest first. */\n getRecentSessionsForBranch(branch: string, count: number): SessionCheckpoint[] {\n const filtered = this.getSessions().filter(s => s.gitBranch === branch);\n return filtered.slice(-count).reverse();\n }\n\n /** Returns the last N decisions for a specific branch, newest first. */\n getRecentDecisionsForBranch(branch: string, count: number): DecisionRecord[] {\n const filtered = this.getDecisions().filter(d => d.gitBranch === branch);\n return filtered.slice(-count).reverse();\n }\n\n /** Whether the workspace is inside a git worktree. */\n get isWorktree(): boolean {\n return this._isWorktree;\n }\n\n /**\n * Returns the current git branch for this workspace.\n * Lazily cached: the branch is resolved once per KeepGoingReader instance.\n */\n getCurrentBranch(): string | undefined {\n if (this._cachedBranch === null) {\n this._cachedBranch = getCurrentBranch(this.workspacePath);\n }\n return this._cachedBranch;\n }\n\n /**\n * Worktree-aware last session lookup.\n * In a worktree, scopes to the current branch with fallback to global.\n * Returns the session and whether it fell back to global.\n */\n getScopedLastSession(): { session: SessionCheckpoint | undefined; isFallback: boolean } {\n const branch = this.getCurrentBranch();\n if (this._isWorktree && branch) {\n const scoped = this.getLastSessionForBranch(branch);\n if (scoped) return { session: scoped, isFallback: false };\n return { session: this.getLastSession(), isFallback: true };\n }\n return { session: this.getLastSession(), isFallback: false };\n }\n\n /** Worktree-aware recent sessions. Scopes to current branch in a worktree. */\n getScopedRecentSessions(count: number): SessionCheckpoint[] {\n const branch = this.getCurrentBranch();\n if (this._isWorktree && branch) {\n return this.getRecentSessionsForBranch(branch, count);\n }\n return this.getRecentSessions(count);\n }\n\n /** Worktree-aware recent decisions. Scopes to current branch in a worktree. */\n getScopedRecentDecisions(count: number): DecisionRecord[] {\n const branch = this.getCurrentBranch();\n if (this._isWorktree && branch) {\n return this.getRecentDecisionsForBranch(branch, count);\n }\n return this.getRecentDecisions(count);\n }\n\n /**\n * Resolves branch scope from an explicit `branch` parameter.\n * Used by tools that accept a `branch` argument (e.g. get_session_history, get_decisions).\n * - `\"all\"` returns no filter.\n * - An explicit branch name uses that.\n * - `undefined` auto-scopes to the current branch in a worktree, or all branches otherwise.\n */\n resolveBranchScope(branch?: string): BranchScope {\n if (branch === 'all') {\n return { effectiveBranch: undefined, scopeLabel: 'all branches' };\n }\n if (branch) {\n return { effectiveBranch: branch, scopeLabel: `branch \\`${branch}\\`` };\n }\n const currentBranch = this.getCurrentBranch();\n if (this._isWorktree && currentBranch) {\n return { effectiveBranch: currentBranch, scopeLabel: `branch \\`${currentBranch}\\` (worktree)` };\n }\n return { effectiveBranch: undefined, scopeLabel: 'all branches' };\n }\n\n /**\n * Parses sessions.json once, returning both the session list\n * and the optional lastSessionId from a ProjectSessions wrapper.\n */\n private parseSessions(): { sessions: SessionCheckpoint[]; wrapperLastSessionId?: string } {\n const raw = this.readJsonFile<ProjectSessions | SessionCheckpoint[]>(\n this.sessionsFilePath,\n );\n if (!raw) {\n return { sessions: [] };\n }\n if (Array.isArray(raw)) {\n return { sessions: raw };\n }\n return { sessions: raw.sessions ?? [], wrapperLastSessionId: raw.lastSessionId };\n }\n\n private parseDecisions(): { decisions: DecisionRecord[]; lastDecisionId?: string } {\n const raw = this.readJsonFile<ProjectDecisions>(this.decisionsFilePath);\n if (!raw) {\n return { decisions: [] };\n }\n return { decisions: raw.decisions ?? [], lastDecisionId: raw.lastDecisionId };\n }\n\n private readJsonFile<T>(filePath: string): T | undefined {\n try {\n if (!fs.existsSync(filePath)) {\n return undefined;\n }\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst KEEPGOING_MARKER = '@keepgoingdev/mcp-server';\n\nexport const SESSION_START_HOOK = {\n matcher: '',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --print-momentum',\n },\n ],\n};\n\nexport const STOP_HOOK = {\n matcher: '',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --save-checkpoint',\n },\n ],\n};\n\nexport const POST_TOOL_USE_HOOK = {\n matcher: 'Edit|Write|MultiEdit',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --update-task-from-hook',\n },\n ],\n};\n\n// Stop fires on mid-conversation interrupts (Ctrl+C, abort).\n// SessionEnd fires on normal conversation end.\n// Both save a checkpoint to ensure no work is lost regardless of exit path.\n// The 2-minute dedup in saveCheckpoint prevents double writes when both fire.\nexport const SESSION_END_HOOK = {\n matcher: '',\n hooks: [\n {\n type: 'command',\n command: 'npx -y @keepgoingdev/mcp-server --save-checkpoint',\n },\n ],\n};\n\n/** @deprecated Use KEEPGOING_RULES_CONTENT instead. Kept for backward compatibility. */\nexport const CLAUDE_MD_SECTION = `\n## KeepGoing\n\nAfter completing a task or meaningful piece of work, call the \\`save_checkpoint\\` MCP tool with:\n- \\`summary\\`: What you accomplished\n- \\`nextStep\\`: What should be done next\n- \\`blocker\\`: Any blocker (if applicable)\n`;\n\nexport const KEEPGOING_RULES_VERSION = 2;\n\n// Hash of KEEPGOING_RULES_CONTENT (first 8 chars of SHA-256).\n// If you change KEEPGOING_RULES_CONTENT, run `npm run shared:test` to get the new hash,\n// then update this constant AND bump KEEPGOING_RULES_VERSION.\nexport const KEEPGOING_RULES_CONTENT_HASH = '1c88eb6d';\n\nexport const KEEPGOING_RULES_CONTENT = `<!-- @keepgoingdev/mcp-server v${KEEPGOING_RULES_VERSION} -->\n## KeepGoing\n\nWhen you see KeepGoing momentum data in your session context (from a SessionStart hook), share a brief welcome with the user that includes: what was last worked on, the suggested next step, and any blockers.\n\nAfter completing a task or meaningful piece of work, call the \\`save_checkpoint\\` MCP tool with:\n- \\`summary\\`: 1-2 sentences. What changed and why, no file paths, no implementation details (those are captured from git).\n- \\`nextStep\\`: What to do next\n- \\`blocker\\`: Any blocker (if applicable)\n`;\n\nfunction getRulesFileVersion(content: string): number | null {\n const match = content.match(/<!-- @keepgoingdev\\/mcp-server v(\\d+) -->/);\n return match ? parseInt(match[1], 10) : null;\n}\n\nexport const STATUSLINE_CMD = 'npx -y @keepgoingdev/mcp-server --statusline';\n\nexport interface ScopePaths {\n claudeDir: string;\n settingsPath: string;\n claudeMdPath: string;\n rulesPath: string;\n}\n\nexport interface StatuslineConfig {\n /** Check if an existing statusline command is a legacy format that needs migration. */\n isLegacy?: (command: string) => boolean;\n /** Clean up legacy statusline artifacts (e.g. old script files). */\n cleanup?: () => void;\n}\n\nexport interface SetupProjectOptions {\n workspacePath: string;\n scope?: 'project' | 'user';\n sessionHooks?: boolean;\n claudeMd?: boolean;\n /** Override the Claude config directory (user scope only). Defaults to CLAUDE_CONFIG_DIR env var or ~/.claude. */\n claudeDir?: string;\n /** Optional statusline migration helpers. */\n statusline?: StatuslineConfig;\n}\n\nexport interface SetupProjectResult {\n /** Human-readable result messages (one per action taken/skipped). */\n messages: string[];\n /** Whether any files were changed on disk. */\n changed: boolean;\n}\n\n/**\n * Detect the Claude config directory.\n * Priority: CLAUDE_CONFIG_DIR env var (inherited by MCP server subprocess from Claude Code) → ~/.claude\n */\nexport function detectClaudeDir(): string {\n return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');\n}\n\nexport function hasKeepGoingHook(hookEntries: unknown[]): boolean {\n return hookEntries.some((entry: any) =>\n entry?.hooks?.some((h: any) => typeof h?.command === 'string' && h.command.includes(KEEPGOING_MARKER)),\n );\n}\n\n/**\n * Resolve settings, CLAUDE.md, and rules file paths based on scope.\n * - \"project\": <workspacePath>/.claude/settings.json, <workspacePath>/CLAUDE.md, <workspacePath>/.claude/rules/keepgoing.md\n * - \"user\": <claudeDir>/settings.json, <claudeDir>/CLAUDE.md, <claudeDir>/rules/keepgoing.md\n * where claudeDir defaults to CLAUDE_CONFIG_DIR env var or ~/.claude\n */\nexport function resolveScopePaths(\n scope: 'project' | 'user',\n workspacePath: string,\n overrideClaudeDir?: string,\n): ScopePaths {\n if (scope === 'user') {\n const claudeDir = overrideClaudeDir || detectClaudeDir();\n return {\n claudeDir,\n settingsPath: path.join(claudeDir, 'settings.json'),\n claudeMdPath: path.join(claudeDir, 'CLAUDE.md'),\n rulesPath: path.join(claudeDir, 'rules', 'keepgoing.md'),\n };\n }\n const claudeDir = path.join(workspacePath, '.claude');\n const dotClaudeMdPath = path.join(workspacePath, '.claude', 'CLAUDE.md');\n const rootClaudeMdPath = path.join(workspacePath, 'CLAUDE.md');\n return {\n claudeDir,\n settingsPath: path.join(claudeDir, 'settings.json'),\n claudeMdPath: fs.existsSync(dotClaudeMdPath) ? dotClaudeMdPath : rootClaudeMdPath,\n rulesPath: path.join(workspacePath, '.claude', 'rules', 'keepgoing.md'),\n };\n}\n\n/**\n * Write session hooks into a settings object. Returns true if anything changed.\n */\nexport function writeHooksToSettings(settings: any): boolean {\n let changed = false;\n\n if (!settings.hooks) {\n settings.hooks = {};\n }\n\n // SessionStart\n if (!Array.isArray(settings.hooks.SessionStart)) {\n settings.hooks.SessionStart = [];\n }\n if (!hasKeepGoingHook(settings.hooks.SessionStart)) {\n settings.hooks.SessionStart.push(SESSION_START_HOOK);\n changed = true;\n }\n\n // Stop\n if (!Array.isArray(settings.hooks.Stop)) {\n settings.hooks.Stop = [];\n }\n if (!hasKeepGoingHook(settings.hooks.Stop)) {\n settings.hooks.Stop.push(STOP_HOOK);\n changed = true;\n }\n\n // PostToolUse\n if (!Array.isArray(settings.hooks.PostToolUse)) {\n settings.hooks.PostToolUse = [];\n }\n if (!hasKeepGoingHook(settings.hooks.PostToolUse)) {\n settings.hooks.PostToolUse.push(POST_TOOL_USE_HOOK);\n changed = true;\n }\n\n // SessionEnd\n if (!Array.isArray(settings.hooks.SessionEnd)) {\n settings.hooks.SessionEnd = [];\n }\n if (!hasKeepGoingHook(settings.hooks.SessionEnd)) {\n settings.hooks.SessionEnd.push(SESSION_END_HOOK);\n changed = true;\n }\n\n return changed;\n}\n\n/**\n * Check if the \"other\" scope also has KeepGoing hooks configured.\n * Returns a warning string if conflict detected, or null.\n */\nexport function checkHookConflict(scope: 'project' | 'user', workspacePath: string): string | null {\n const otherPaths = resolveScopePaths(scope === 'user' ? 'project' : 'user', workspacePath);\n\n if (!fs.existsSync(otherPaths.settingsPath)) {\n return null;\n }\n\n try {\n const otherSettings = JSON.parse(fs.readFileSync(otherPaths.settingsPath, 'utf-8'));\n const hooks = otherSettings?.hooks;\n if (!hooks) return null;\n\n const hasConflict =\n (Array.isArray(hooks.SessionStart) && hasKeepGoingHook(hooks.SessionStart)) ||\n (Array.isArray(hooks.Stop) && hasKeepGoingHook(hooks.Stop));\n\n if (hasConflict) {\n const otherScope = scope === 'user' ? 'project' : 'user';\n const otherFile = otherPaths.settingsPath;\n return `KeepGoing hooks are also configured at ${otherScope} scope (${otherFile}). ` +\n `Having hooks at both scopes may cause them to fire twice. ` +\n `Consider removing the ${otherScope}-level hooks if you want to use ${scope}-level only.`;\n }\n } catch {\n // Ignore parse errors in the other settings file\n }\n\n return null;\n}\n\n/**\n * Set up KeepGoing in a project or globally.\n * Writes session hooks to settings.json, optionally sets up statusline,\n * and writes KeepGoing instructions to .claude/rules/keepgoing.md.\n */\nexport function setupProject(options: SetupProjectOptions): SetupProjectResult {\n const {\n workspacePath,\n scope = 'project',\n sessionHooks = true,\n claudeMd = true,\n claudeDir: claudeDirOverride,\n statusline,\n } = options;\n\n const messages: string[] = [];\n let changed = false;\n const { claudeDir, settingsPath, claudeMdPath, rulesPath } = resolveScopePaths(\n scope,\n workspacePath,\n claudeDirOverride,\n );\n const scopeLabel = scope === 'user'\n ? path.join('~', path.relative(os.homedir(), claudeDir), 'settings.json').replace(/\\\\/g, '/')\n : '.claude/settings.json';\n\n let settings: any = {};\n if (fs.existsSync(settingsPath)) {\n settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n }\n\n let settingsChanged = false;\n\n // --- Session hooks ---\n if (sessionHooks) {\n const hooksChanged = writeHooksToSettings(settings);\n settingsChanged = hooksChanged;\n\n if (hooksChanged) {\n messages.push(`Session hooks: Added to ${scopeLabel}`);\n } else {\n messages.push('Session hooks: Already present, skipped');\n }\n\n const conflict = checkHookConflict(scope, workspacePath);\n if (conflict) {\n messages.push(`Warning: ${conflict}`);\n }\n }\n\n // --- Statusline ---\n {\n const needsUpdate = settings.statusLine?.command\n && statusline?.isLegacy?.(settings.statusLine.command);\n\n if (!settings.statusLine || needsUpdate) {\n settings.statusLine = {\n type: 'command',\n command: STATUSLINE_CMD,\n };\n settingsChanged = true;\n messages.push(needsUpdate\n ? 'Statusline: Migrated to auto-updating npx command'\n : `Statusline: Added to ${scopeLabel}`);\n } else {\n messages.push('Statusline: Already configured in settings, skipped');\n }\n\n statusline?.cleanup?.();\n }\n\n // Write settings once if anything changed\n if (settingsChanged) {\n if (!fs.existsSync(claudeDir)) {\n fs.mkdirSync(claudeDir, { recursive: true });\n }\n fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n changed = true;\n }\n\n // --- Rules file (.claude/rules/keepgoing.md) ---\n if (claudeMd) {\n const rulesDir = path.dirname(rulesPath);\n const rulesLabel = scope === 'user'\n ? path.join(path.relative(os.homedir(), path.dirname(rulesPath)), 'keepgoing.md').replace(/\\\\/g, '/')\n : '.claude/rules/keepgoing.md';\n\n if (fs.existsSync(rulesPath)) {\n const existing = fs.readFileSync(rulesPath, 'utf-8');\n const existingVersion = getRulesFileVersion(existing);\n\n if (existingVersion === null) {\n messages.push(`Rules file: Custom file found at ${rulesLabel}, skipping`);\n } else if (existingVersion >= KEEPGOING_RULES_VERSION) {\n messages.push(`Rules file: Already up to date (v${existingVersion}), skipped`);\n } else {\n if (!fs.existsSync(rulesDir)) {\n fs.mkdirSync(rulesDir, { recursive: true });\n }\n fs.writeFileSync(rulesPath, KEEPGOING_RULES_CONTENT);\n changed = true;\n messages.push(`Rules file: Updated v${existingVersion} → v${KEEPGOING_RULES_VERSION} at ${rulesLabel}`);\n }\n } else {\n // Check for migration hint: CLAUDE.md already has ## KeepGoing\n const existingClaudeMd = fs.existsSync(claudeMdPath)\n ? fs.readFileSync(claudeMdPath, 'utf-8')\n : '';\n\n if (!fs.existsSync(rulesDir)) {\n fs.mkdirSync(rulesDir, { recursive: true });\n }\n fs.writeFileSync(rulesPath, KEEPGOING_RULES_CONTENT);\n changed = true;\n\n if (existingClaudeMd.includes('## KeepGoing')) {\n const mdLabel = scope === 'user' ? '~/.claude/CLAUDE.md' : 'CLAUDE.md';\n messages.push(\n `Rules file: Created ${rulesLabel} (you can now remove the ## KeepGoing section from ${mdLabel})`,\n );\n } else {\n messages.push(`Rules file: Created ${rulesLabel}`);\n }\n }\n }\n\n return { messages, changed };\n}\n","/**\n * HTTP client for the LemonSqueezy license API.\n * Uses Node's built-in fetch (no new dependencies).\n *\n * API docs: https://docs.lemonsqueezy.com/api/license-api\n * These endpoints accept application/x-www-form-urlencoded bodies\n * and do not require an API key header.\n */\n\nimport { KNOWN_VARIANT_IDS } from './license';\n\nconst BASE_URL = 'https://api.lemonsqueezy.com/v1/licenses';\nconst REQUEST_TIMEOUT_MS = 15_000; // 15 seconds\nconst EXPECTED_STORE_ID = 301555;\nconst EXPECTED_PRODUCT_ID = 864311;\n\nfunction fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n return fetch(url, { ...init, signal: controller.signal }).finally(() => clearTimeout(timer));\n}\n\ninterface LemonSqueezyLicenseMeta {\n store_id: number;\n product_id: number;\n product_name: string;\n variant_id: number;\n variant_name: string;\n customer_id: number;\n customer_name: string;\n customer_email: string;\n}\n\ninterface LemonSqueezyInstance {\n id: string;\n name: string;\n created_at: string;\n}\n\nexport interface ActivateResult {\n valid: boolean;\n error?: string;\n licenseKey?: string;\n instanceId?: string;\n customerName?: string;\n productName?: string;\n variantId?: number;\n variantName?: string;\n}\n\nexport interface ValidateResult {\n valid: boolean;\n error?: string;\n /** True when the error was caused by a network/timeout failure, not an API rejection. */\n isNetworkError?: boolean;\n licenseKey?: string;\n customerName?: string;\n productName?: string;\n variantId?: number;\n variantName?: string;\n}\n\nexport interface LicenseClientOptions {\n allowTestMode?: boolean;\n}\n\nexport interface DeactivateResult {\n deactivated: boolean;\n error?: string;\n}\n\nfunction validateProductIdentity(meta: LemonSqueezyLicenseMeta | undefined): string | undefined {\n if (!meta) return 'License response missing product metadata.';\n if (meta.store_id !== EXPECTED_STORE_ID || meta.product_id !== EXPECTED_PRODUCT_ID) {\n return 'This license key does not belong to KeepGoing.';\n }\n return undefined;\n}\n\n/**\n * Safely parse JSON from a fetch response, returning null on failure.\n */\nasync function safeJson(res: Response): Promise<Record<string, unknown> | null> {\n try {\n const text = await res.text();\n return JSON.parse(text) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Activate a license key on a device.\n */\nexport async function activateLicense(\n licenseKey: string,\n instanceName: string,\n options?: LicenseClientOptions,\n): Promise<ActivateResult> {\n try {\n const res = await fetchWithTimeout(`${BASE_URL}/activate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ license_key: licenseKey, instance_name: instanceName }),\n });\n\n const data = await safeJson(res) as {\n activated?: boolean;\n error?: string;\n license_key?: { key: string; test_mode?: boolean };\n instance?: LemonSqueezyInstance;\n meta?: LemonSqueezyLicenseMeta;\n } | null;\n\n if (!res.ok || !data?.activated) {\n return { valid: false, error: (data?.error as string) || `Activation failed (${res.status})` };\n }\n\n if (!options?.allowTestMode && data.license_key?.test_mode) {\n // Release the activation slot we just consumed\n if (data.license_key?.key && data.instance?.id) {\n await deactivateLicense(data.license_key.key, data.instance.id);\n }\n return { valid: false, error: 'This is a test license key. Please use a production license key from your purchase confirmation.' };\n }\n\n if (!options?.allowTestMode) {\n const productError = validateProductIdentity(data.meta);\n if (productError) {\n // Release the activation slot we just consumed\n if (data.license_key?.key && data.instance?.id) {\n await deactivateLicense(data.license_key.key, data.instance.id);\n }\n return { valid: false, error: productError };\n }\n\n // Validate variant_id is a known add-on\n if (data.meta?.variant_id && !KNOWN_VARIANT_IDS.has(data.meta.variant_id)) {\n if (data.license_key?.key && data.instance?.id) {\n await deactivateLicense(data.license_key.key, data.instance.id);\n }\n return { valid: false, error: 'This license key is for an unrecognized add-on variant. Please update KeepGoing or contact support.' };\n }\n }\n\n return {\n valid: true,\n licenseKey: data.license_key?.key,\n instanceId: data.instance?.id,\n customerName: data.meta?.customer_name,\n productName: data.meta?.product_name,\n variantId: data.meta?.variant_id,\n variantName: data.meta?.variant_name,\n };\n } catch (err) {\n const message = err instanceof Error && err.name === 'AbortError'\n ? 'Request timed out. Please check your network connection and try again.'\n : err instanceof Error ? err.message : 'Network error';\n return { valid: false, error: message };\n }\n}\n\n/**\n * Validate a previously activated license key.\n */\nexport async function validateLicense(\n licenseKey: string,\n instanceId: string,\n options?: LicenseClientOptions,\n): Promise<ValidateResult> {\n try {\n const res = await fetchWithTimeout(`${BASE_URL}/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ license_key: licenseKey, instance_id: instanceId }),\n });\n\n const data = await safeJson(res) as {\n valid?: boolean;\n error?: string;\n license_key?: { key: string; test_mode?: boolean };\n meta?: LemonSqueezyLicenseMeta;\n } | null;\n\n if (!res.ok || !data?.valid) {\n return { valid: false, isNetworkError: false, error: (data?.error as string) || `Validation failed (${res.status})` };\n }\n\n if (!options?.allowTestMode && data.license_key?.test_mode) {\n return { valid: false, isNetworkError: false, error: 'This is a test license key. Please use a production license key from your purchase confirmation.' };\n }\n\n if (!options?.allowTestMode) {\n const productError = validateProductIdentity(data.meta);\n if (productError) {\n return { valid: false, isNetworkError: false, error: productError };\n }\n }\n\n return {\n valid: true,\n licenseKey: data.license_key?.key,\n customerName: data.meta?.customer_name,\n productName: data.meta?.product_name,\n variantId: data.meta?.variant_id,\n variantName: data.meta?.variant_name,\n };\n } catch (err) {\n const message = err instanceof Error && err.name === 'AbortError'\n ? 'Request timed out. Please check your network connection and try again.'\n : err instanceof Error ? err.message : 'Network error';\n return { valid: false, isNetworkError: true, error: message };\n }\n}\n\n/**\n * Deactivate a license key from a device.\n */\nexport async function deactivateLicense(\n licenseKey: string,\n instanceId: string,\n): Promise<DeactivateResult> {\n try {\n const res = await fetchWithTimeout(`${BASE_URL}/deactivate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ license_key: licenseKey, instance_id: instanceId }),\n });\n\n const data = await safeJson(res) as {\n deactivated?: boolean;\n error?: string;\n } | null;\n\n if (!res.ok || !data?.deactivated) {\n return { deactivated: false, error: (data?.error as string) || `Deactivation failed (${res.status})` };\n }\n\n return { deactivated: true };\n } catch (err) {\n const message = err instanceof Error && err.name === 'AbortError'\n ? 'Request timed out. Please check your network connection and try again.'\n : err instanceof Error ? err.message : 'Network error';\n return { deactivated: false, error: message };\n }\n}\n","/**\n * Shared license revalidation logic used by all consumers\n * (VS Code extension, CLI, MCP server).\n *\n * Extracts the TOCTOU-safe revalidation pattern from the extension's\n * LicenseService so that every consumer can verify stale licenses\n * against the LemonSqueezy API.\n */\n\nimport type { GatedFeature } from './featureGate';\nimport {\n getAllLicensesNeedingRevalidation,\n getLicenseForFeature,\n needsRevalidation,\n readLicenseStore,\n writeLicenseStore,\n} from './license';\nimport type { LicenseEntry } from './license';\nimport { validateLicense } from './licenseClient';\n\nexport interface RevalidationOptions {\n /** Allow test-mode licenses (dev environments). Default false. */\n allowTestMode?: boolean;\n}\n\nexport interface RevalidationResult {\n /** Number of licenses that were stale and checked. */\n checked: number;\n /** Number of licenses successfully revalidated (timestamp refreshed). */\n refreshed: number;\n /** Number of licenses marked inactive due to server rejection. */\n revoked: number;\n /** Number of licenses skipped due to network errors (kept cached). */\n skippedNetworkError: number;\n}\n\n/**\n * Revalidate all licenses that exceed the 24-hour staleness threshold.\n *\n * For each stale license, calls the LemonSqueezy validate API:\n * - On success: refreshes `lastValidatedAt` and optionally `customerName`\n * - On API rejection (non-network error): marks license as `inactive`\n * - On network/timeout error: keeps cached state (offline tolerance)\n *\n * Uses a TOCTOU-safe merge pattern: re-reads the store from disk before\n * writing, then merges only the changed fields.\n */\nexport async function revalidateStaleLicenses(\n options?: RevalidationOptions,\n): Promise<RevalidationResult> {\n const stale = getAllLicensesNeedingRevalidation();\n const result: RevalidationResult = { checked: stale.length, refreshed: 0, revoked: 0, skippedNetworkError: 0 };\n\n if (stale.length === 0) return result;\n\n // Collect only field-level patches. We avoid mutating stale entries\n // directly so the final write merges into a fresh read, preventing\n // TOCTOU races with concurrent writers (extension, CLI, MCP).\n const updates = new Map<string, { lastValidatedAt?: string; customerName?: string; status?: string }>();\n\n for (const entry of stale) {\n const vResult = await validateLicense(entry.licenseKey, entry.instanceId, {\n allowTestMode: options?.allowTestMode,\n });\n\n if (vResult.valid) {\n const patch: { lastValidatedAt: string; customerName?: string } = {\n lastValidatedAt: new Date().toISOString(),\n };\n if (vResult.customerName) patch.customerName = vResult.customerName;\n updates.set(entry.licenseKey, patch);\n result.refreshed++;\n } else if (vResult.isNetworkError) {\n // Offline tolerance: keep cached state\n result.skippedNetworkError++;\n } else {\n // Definitive rejection from API\n updates.set(entry.licenseKey, { status: 'inactive' });\n result.revoked++;\n }\n }\n\n if (updates.size > 0) {\n // Re-read and merge only the changed fields, preserving any concurrent writes\n const store = readLicenseStore();\n for (const [key, patch] of updates) {\n const idx = store.licenses.findIndex(l => l.licenseKey === key);\n if (idx >= 0) {\n Object.assign(store.licenses[idx], patch);\n }\n }\n writeLicenseStore(store);\n }\n\n return result;\n}\n\n/**\n * Get a license for a feature, revalidating first if stale.\n *\n * Hot path (license fresh, <24h): synchronous lookup, no HTTP call.\n * Cold path (license stale, once per 24h): one HTTP round-trip per stale license.\n *\n * Returns the license entry if active and valid, or undefined if none exists\n * or revalidation revoked it.\n */\nexport async function getLicenseForFeatureWithRevalidation(\n feature: GatedFeature,\n options?: RevalidationOptions,\n): Promise<LicenseEntry | undefined> {\n const license = getLicenseForFeature(feature);\n if (!license) return undefined;\n\n // Only revalidate if this license is stale\n if (needsRevalidation(license)) {\n await revalidateStaleLicenses(options);\n // Re-check after revalidation (may have been revoked)\n return getLicenseForFeature(feature);\n }\n\n return license;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n formatRelativeTime,\n generateEnrichedBriefing,\n formatEnrichedBriefing,\n getCommitMessagesSince,\n} from '@keepgoingdev/shared';\nimport type { BriefingTier } from '@keepgoingdev/shared';\n\nexport function registerGetMomentum(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'get_momentum',\n 'Get current developer momentum: last checkpoint, next step, blockers, and branch context. Use this to understand where the developer left off. Pass tier or model to control detail level.',\n {\n tier: z.enum(['compact', 'standard', 'detailed', 'full']).optional()\n .describe('Briefing detail level. compact (~150 tokens), standard (~400), detailed (~800), full (~1500). Default: standard.'),\n model: z.string().optional()\n .describe('Model name (e.g. \"claude-opus-4\") to auto-resolve tier. Ignored if tier is set.'),\n },\n async ({ tier, model }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found. The developer has not saved any checkpoints yet.',\n },\n ],\n };\n }\n\n // If tier or model is provided, use the enriched briefing path\n if (tier || model) {\n const gitBranch = reader.getCurrentBranch();\n const { session: lastSession } = reader.getScopedLastSession();\n const recentSessions = reader.getScopedRecentSessions(5);\n const state = reader.getState() ?? {};\n\n const sinceTimestamp = lastSession?.timestamp;\n const recentCommits = sinceTimestamp\n ? getCommitMessagesSince(workspacePath, sinceTimestamp)\n : [];\n\n const decisions = reader.getScopedRecentDecisions(10);\n const allSessions = reader.getSessions();\n const fileConflicts = reader.detectFileConflicts();\n const branchOverlaps = reader.detectBranchOverlap();\n\n const briefing = generateEnrichedBriefing({\n tier: tier as BriefingTier | undefined,\n model,\n lastSession,\n recentSessions,\n projectState: state,\n gitBranch,\n recentCommits,\n decisions,\n allTouchedFiles: lastSession?.touchedFiles,\n allSessions,\n fileConflicts,\n branchOverlaps,\n isWorktree: reader.isWorktree,\n });\n\n if (!briefing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'KeepGoing is set up but no session checkpoints exist yet.',\n },\n ],\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: formatEnrichedBriefing(briefing) }],\n };\n }\n\n // Default: existing momentum format (backward compat when no tier/model passed)\n const { session: lastSession, isFallback } = reader.getScopedLastSession();\n const currentBranch = reader.getCurrentBranch();\n\n if (!lastSession) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'KeepGoing is set up but no session checkpoints exist yet.',\n },\n ],\n };\n }\n\n const state = reader.getState();\n const branchChanged =\n lastSession.gitBranch &&\n currentBranch &&\n lastSession.gitBranch !== currentBranch;\n\n const lines: string[] = [\n `## Developer Momentum`,\n '',\n ];\n\n if (reader.isWorktree && currentBranch) {\n lines.push(`**Worktree context:** Scoped to branch \\`${currentBranch}\\``);\n if (isFallback) {\n lines.push(`**Note:** No checkpoints found for branch \\`${currentBranch}\\`. Showing last global checkpoint.`);\n }\n lines.push('');\n }\n\n lines.push(\n `**Last checkpoint:** ${formatRelativeTime(lastSession.timestamp)}`,\n `**Summary:** ${lastSession.summary || 'No summary'}`,\n `**Next step:** ${lastSession.nextStep || 'Not specified'}`,\n );\n\n if (lastSession.blocker) {\n lines.push(`**Blocker:** ${lastSession.blocker}`);\n }\n\n if (lastSession.projectIntent) {\n lines.push(`**Project intent:** ${lastSession.projectIntent}`);\n }\n\n lines.push('');\n\n if (currentBranch) {\n lines.push(`**Current branch:** ${currentBranch}`);\n }\n if (branchChanged && !reader.isWorktree) {\n lines.push(\n `**Note:** Branch changed since last checkpoint (was \\`${lastSession.gitBranch}\\`, now \\`${currentBranch}\\`)`,\n );\n }\n\n if (lastSession.touchedFiles.length > 0) {\n lines.push('');\n lines.push(\n `**Files touched (${lastSession.touchedFiles.length}):** ${lastSession.touchedFiles.slice(0, 10).join(', ')}`,\n );\n if (lastSession.touchedFiles.length > 10) {\n lines.push(\n ` ...and ${lastSession.touchedFiles.length - 10} more`,\n );\n }\n }\n\n if (state?.derivedCurrentFocus) {\n lines.push('');\n lines.push(`**Derived focus:** ${state.derivedCurrentFocus}`);\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport { formatRelativeTime } from '@keepgoingdev/shared';\n\nexport function registerGetSessionHistory(server: McpServer, reader: KeepGoingReader) {\n server.tool(\n 'get_session_history',\n 'Get recent session checkpoints. Returns a chronological list of what the developer worked on.',\n {\n limit: z.number().min(1).max(50).default(5).describe('Number of recent sessions to return (1-50, default 5)'),\n branch: z.string().optional().describe('Filter to a specific branch name, or \"all\" to show all branches. Auto-detected from worktree context by default.'),\n },\n async ({ limit, branch }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found.',\n },\n ],\n };\n }\n\n const { effectiveBranch, scopeLabel } = reader.resolveBranchScope(branch);\n\n const sessions = effectiveBranch\n ? reader.getRecentSessionsForBranch(effectiveBranch, limit)\n : reader.getRecentSessions(limit);\n\n if (sessions.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: effectiveBranch\n ? `No session checkpoints found for branch \\`${effectiveBranch}\\`. Use branch: \"all\" to see all branches.`\n : 'No session checkpoints found.',\n },\n ],\n };\n }\n\n const lines: string[] = [\n `## Session History (last ${sessions.length}, ${scopeLabel})`,\n '',\n ];\n\n for (const session of sessions) {\n lines.push(`### ${formatRelativeTime(session.timestamp)}`);\n lines.push(`- **Summary:** ${session.summary || 'No summary'}`);\n lines.push(`- **Next step:** ${session.nextStep || 'Not specified'}`);\n if (session.blocker) {\n lines.push(`- **Blocker:** ${session.blocker}`);\n }\n if (session.gitBranch) {\n lines.push(`- **Branch:** ${session.gitBranch}`);\n }\n if (session.touchedFiles.length > 0) {\n lines.push(\n `- **Files:** ${session.touchedFiles.slice(0, 5).join(', ')}${session.touchedFiles.length > 5 ? ` (+${session.touchedFiles.length - 5} more)` : ''}`,\n );\n }\n lines.push('');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n getCommitMessagesSince,\n generateEnrichedBriefing,\n formatEnrichedBriefing,\n} from '@keepgoingdev/shared';\nimport type { BriefingTier } from '@keepgoingdev/shared';\n\nexport function registerGetReentryBriefing(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'get_reentry_briefing',\n 'Get a synthesized re-entry briefing that helps a developer understand where they left off. Includes focus, recent activity, and suggested next steps. Pass tier or model to control detail level.',\n {\n tier: z.enum(['compact', 'standard', 'detailed', 'full']).optional()\n .describe('Briefing detail level. compact (~150 tokens), standard (~400), detailed (~800), full (~1500). Default: standard.'),\n model: z.string().optional()\n .describe('Model name (e.g. \"claude-opus-4\") to auto-resolve tier. Ignored if tier is set.'),\n },\n async ({ tier, model }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found. The developer has not saved any checkpoints yet.',\n },\n ],\n };\n }\n\n const gitBranch = reader.getCurrentBranch();\n const { session: lastSession } = reader.getScopedLastSession();\n const recentSessions = reader.getScopedRecentSessions(5);\n const state = reader.getState() ?? {};\n\n const sinceTimestamp = lastSession?.timestamp;\n const recentCommits = sinceTimestamp\n ? getCommitMessagesSince(workspacePath, sinceTimestamp)\n : [];\n\n const decisions = reader.getScopedRecentDecisions(10);\n const allSessions = reader.getSessions();\n const fileConflicts = reader.detectFileConflicts();\n const branchOverlaps = reader.detectBranchOverlap();\n\n const briefing = generateEnrichedBriefing({\n tier: tier as BriefingTier | undefined,\n model,\n lastSession,\n recentSessions,\n projectState: state,\n gitBranch,\n recentCommits,\n decisions,\n allTouchedFiles: lastSession?.touchedFiles,\n allSessions,\n fileConflicts,\n branchOverlaps,\n isWorktree: reader.isWorktree,\n });\n\n if (!briefing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No session data available to generate a briefing.',\n },\n ],\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: formatEnrichedBriefing(briefing) }],\n };\n },\n );\n}\n","import path from 'node:path';\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n KeepGoingWriter,\n createCheckpoint,\n getCurrentBranch,\n getTouchedFiles,\n getCommitsSince,\n getCommitMessagesSince,\n getHeadCommitHash,\n tryDetectDecision,\n resolveStorageRoot,\n generateSessionId,\n} from '@keepgoingdev/shared';\n\nexport function registerSaveCheckpoint(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'save_checkpoint',\n 'Save a development checkpoint. Call this after completing a task or meaningful piece of work, not just at end of session. Each checkpoint helps the next session (or developer) pick up exactly where you left off.',\n {\n summary: z.string().describe('What was accomplished in this session'),\n nextStep: z.string().optional().describe('What to do next'),\n blocker: z.string().optional().describe('Any blocker preventing progress'),\n },\n async ({ summary, nextStep, blocker }) => {\n const lastSession = reader.getLastSession();\n\n const gitBranch = getCurrentBranch(workspacePath);\n const touchedFiles = getTouchedFiles(workspacePath);\n const commitHashes = getCommitsSince(workspacePath, lastSession?.timestamp);\n const projectName = path.basename(resolveStorageRoot(workspacePath));\n\n const sessionId = generateSessionId({ workspaceRoot: workspacePath, branch: gitBranch ?? undefined, worktreePath: workspacePath });\n const checkpoint = createCheckpoint({\n summary,\n nextStep: nextStep || '',\n blocker,\n gitBranch,\n touchedFiles,\n commitHashes,\n workspaceRoot: workspacePath,\n source: 'manual',\n sessionId,\n });\n\n const writer = new KeepGoingWriter(workspacePath);\n writer.saveCheckpoint(checkpoint, projectName);\n\n const lines: string[] = [\n `Checkpoint saved.`,\n `- **ID:** ${checkpoint.id}`,\n `- **Branch:** ${gitBranch || 'unknown'}`,\n `- **Files tracked:** ${touchedFiles.length}`,\n `- **Commits captured:** ${commitHashes.length}`,\n ];\n\n // Decision detection\n if (commitHashes.length > 0) {\n const commitMessages = getCommitMessagesSince(workspacePath, lastSession?.timestamp);\n const headHash = getHeadCommitHash(workspacePath);\n if (commitMessages.length > 0 && headHash) {\n const detected = tryDetectDecision({\n workspacePath,\n checkpointId: checkpoint.id,\n gitBranch,\n commitHash: headHash,\n commitMessage: commitMessages[0],\n filesChanged: touchedFiles,\n });\n if (detected) {\n lines.push(`- **Decision detected:** ${detected.category} (${(detected.confidence * 100).toFixed(0)}% confidence)`);\n }\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport { formatRelativeTime, getLicenseForFeatureWithRevalidation } from '@keepgoingdev/shared';\n\nexport function registerGetDecisions(server: McpServer, reader: KeepGoingReader) {\n server.tool(\n 'get_decisions',\n 'Get recent decision records. Returns detected high-signal commits with their category, confidence, and rationale.',\n {\n limit: z.number().min(1).max(50).default(10).describe('Number of recent decisions to return (1-50, default 10)'),\n branch: z.string().optional().describe('Filter to a specific branch name, or \"all\" to show all branches. Auto-detected from worktree context by default.'),\n },\n async ({ limit, branch }) => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found.',\n },\n ],\n };\n }\n\n if (process.env.KEEPGOING_PRO_BYPASS !== '1' && !(await getLicenseForFeatureWithRevalidation('decisions'))) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Decision Detection requires a Pro license. Use the activate_license tool, run `keepgoing activate <key>` in your terminal, or visit https://keepgoing.dev/add-ons to purchase.',\n },\n ],\n };\n }\n\n const { effectiveBranch, scopeLabel } = reader.resolveBranchScope(branch);\n\n const decisions = effectiveBranch\n ? reader.getRecentDecisionsForBranch(effectiveBranch, limit)\n : reader.getRecentDecisions(limit);\n\n if (decisions.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: effectiveBranch\n ? `No decision records found for branch \\`${effectiveBranch}\\`. Use branch: \"all\" to see all branches.`\n : 'No decision records found.',\n },\n ],\n };\n }\n\n const lines: string[] = [\n `## Decisions (last ${decisions.length}, ${scopeLabel})`,\n '',\n ];\n\n for (const decision of decisions) {\n lines.push(`### ${decision.commitMessage}`);\n lines.push(`- **When:** ${formatRelativeTime(decision.timestamp)}`);\n lines.push(`- **Category:** ${decision.classification.category}`);\n lines.push(`- **Confidence:** ${(decision.classification.confidence * 100).toFixed(0)}%`);\n if (decision.gitBranch) {\n lines.push(`- **Branch:** ${decision.gitBranch}`);\n }\n if (decision.rationale) {\n lines.push(`- **Rationale:** ${decision.rationale}`);\n }\n if (decision.classification.reasons.length > 0) {\n lines.push(`- **Signals:** ${decision.classification.reasons.join('; ')}`);\n }\n lines.push('');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport { formatRelativeTime, getLicenseForFeatureWithRevalidation } from '@keepgoingdev/shared';\n\nexport function registerGetCurrentTask(server: McpServer, reader: KeepGoingReader) {\n server.tool(\n 'get_current_task',\n \"Get a bird's eye view of all active Claude sessions. See what each session is working on, which branch it is on, and when it last did something. Useful when running multiple parallel sessions across worktrees.\",\n {},\n async () => {\n if (!reader.exists()) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No KeepGoing data found.',\n },\n ],\n };\n }\n\n if (process.env.KEEPGOING_PRO_BYPASS !== '1' && !(await getLicenseForFeatureWithRevalidation('session-awareness'))) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Session Awareness requires a license. Use the activate_license tool, run `keepgoing activate <key>` in your terminal, or visit https://keepgoing.dev/add-ons to purchase.',\n },\n ],\n };\n }\n\n const tasks = reader.getCurrentTasks();\n\n if (tasks.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No current task data found. The agent has not started writing session data yet.',\n },\n ],\n };\n }\n\n const activeTasks = tasks.filter(t => t.sessionActive);\n const finishedTasks = tasks.filter(t => !t.sessionActive);\n\n const lines: string[] = [];\n\n // Summary header\n const totalActive = activeTasks.length;\n const totalFinished = finishedTasks.length;\n if (totalActive > 0 || totalFinished > 0) {\n const parts: string[] = [];\n if (totalActive > 0) parts.push(`${totalActive} active`);\n if (totalFinished > 0) parts.push(`${totalFinished} finished`);\n lines.push(`## Live Sessions (${parts.join(', ')})`);\n lines.push('');\n }\n\n // Render each task\n for (const task of [...activeTasks, ...finishedTasks]) {\n const statusIcon = task.sessionActive ? '🟢' : '✅';\n const statusLabel = task.sessionActive ? 'Active' : 'Finished';\n const sessionLabel = task.sessionLabel || task.agentLabel || task.sessionId || 'Session';\n\n lines.push(`### ${statusIcon} ${sessionLabel} (${statusLabel})`);\n lines.push(`- **Updated:** ${formatRelativeTime(task.updatedAt)}`);\n\n if (task.branch) {\n lines.push(`- **Branch:** ${task.branch}`);\n }\n if (task.agentLabel && task.sessionLabel) {\n lines.push(`- **Agent:** ${task.agentLabel}`);\n }\n if (task.taskSummary) {\n lines.push(`- **Doing:** ${task.taskSummary}`);\n }\n if (task.lastFileEdited) {\n lines.push(`- **Last file:** ${task.lastFileEdited}`);\n }\n if (task.nextStep) {\n lines.push(`- **Next step:** ${task.nextStep}`);\n }\n lines.push('');\n }\n\n // Cross-session intelligence: file conflicts\n const conflicts = reader.detectFileConflicts();\n if (conflicts.length > 0) {\n lines.push('### ⚠️ Potential Conflicts');\n for (const conflict of conflicts) {\n const sessionLabels = conflict.sessions.map(s => s.agentLabel || s.sessionId || 'unknown').join(', ');\n lines.push(`- **${conflict.file}** is being edited by: ${sessionLabels}`);\n }\n lines.push('');\n }\n\n // Cross-session intelligence: branch overlap\n const overlaps = reader.detectBranchOverlap();\n if (overlaps.length > 0) {\n lines.push('### ℹ️ Branch Overlap');\n for (const overlap of overlaps) {\n const sessionLabels = overlap.sessions.map(s => s.agentLabel || s.sessionId || 'unknown').join(', ');\n lines.push(`- **${overlap.branch}**: ${sessionLabels} (possible duplicate work)`);\n }\n lines.push('');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport { setupProject } from '@keepgoingdev/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { isLegacyStatusline, cleanupLegacyScript } from '../cli/migrate.js';\n\nexport function registerSetupProject(server: McpServer, workspacePath: string) {\n server.tool(\n 'setup_project',\n 'Set up KeepGoing hooks and instructions. Use scope \"user\" for global setup (all projects) or \"project\" for per-project setup.',\n {\n sessionHooks: z.boolean().optional().default(true).describe('Add session hooks to settings.json'),\n claudeMd: z.boolean().optional().default(true).describe('Add KeepGoing instructions to .claude/rules/keepgoing.md'),\n scope: z.enum(['project', 'user']).optional().default('project').describe('Where to write config: \"user\" for global (~/.claude/), \"project\" for per-project (.claude/)'),\n claudeDir: z.string().optional().describe('Override the Claude config directory for user scope (defaults to CLAUDE_CONFIG_DIR env var or ~/.claude)'),\n },\n async ({ sessionHooks, claudeMd, scope, claudeDir }) => {\n const result = setupProject({\n workspacePath,\n scope,\n sessionHooks,\n claudeMd,\n claudeDir,\n statusline: {\n isLegacy: isLegacyStatusline,\n cleanup: cleanupLegacyScript,\n },\n });\n\n // Format messages as markdown for MCP consumers\n const formatted = result.messages.map(msg => {\n // Wrap the label portion in bold markdown\n return msg.replace(/^([^:]+:)/, '**$1**');\n });\n\n return {\n content: [{ type: 'text' as const, text: formatted.join('\\n') }],\n };\n },\n );\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst STATUSLINE_CMD = 'npx -y @keepgoingdev/mcp-server --statusline';\n\n/**\n * Check if a statusline command is a legacy keepgoing script that needs migration.\n * Matches the known legacy pattern: a path to keepgoing-statusline.sh copied during setup.\n */\nexport function isLegacyStatusline(command: string): boolean {\n return !command.includes('--statusline') && command.includes('keepgoing-statusline');\n}\n\n/**\n * Migrate legacy statusline (copied script) to npx command.\n * Rewrites .claude/settings.json and removes the old script file.\n * Returns a user-facing message if migration occurred, or undefined if nothing changed.\n */\nexport function migrateStatusline(wsPath: string): string | undefined {\n const settingsPath = path.join(wsPath, '.claude', 'settings.json');\n if (!fs.existsSync(settingsPath)) return undefined;\n\n try {\n const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n const cmd: string | undefined = settings.statusLine?.command;\n if (!cmd || !isLegacyStatusline(cmd)) return undefined;\n\n settings.statusLine = {\n type: 'command',\n command: STATUSLINE_CMD,\n };\n fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n\n cleanupLegacyScript();\n\n return '[KeepGoing] Migrated statusline to auto-updating command (restart Claude Code to apply)';\n } catch {\n return undefined;\n }\n}\n\n/** Remove the legacy ~/.claude/keepgoing-statusline.sh if it exists. */\nexport function cleanupLegacyScript(): void {\n const legacyScript = path.join(os.homedir(), '.claude', 'keepgoing-statusline.sh');\n if (fs.existsSync(legacyScript)) {\n try { fs.unlinkSync(legacyScript); } catch { /* ignore */ }\n }\n}\n\nexport { STATUSLINE_CMD };\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n readLicenseStore,\n addLicenseEntry,\n getDeviceId,\n getVariantLabel,\n activateLicense,\n VARIANT_FEATURE_MAP,\n} from '@keepgoingdev/shared';\n\nexport function registerActivateLicense(server: McpServer) {\n server.tool(\n 'activate_license',\n 'Activate a KeepGoing Pro license on this device. Unlocks add-ons like Decision Detection and Session Awareness.',\n { license_key: z.string().describe('Your KeepGoing Pro license key') },\n async ({ license_key }) => {\n // Check locally first to avoid consuming a remote activation slot unnecessarily\n const store = readLicenseStore();\n const existingForKey = store.licenses.find(\n l => l.status === 'active' && l.licenseKey === license_key,\n );\n if (existingForKey) {\n const label = getVariantLabel(existingForKey.variantId);\n const who = existingForKey.customerName ? ` (${existingForKey.customerName})` : '';\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} is already active${who}. No action needed.`,\n },\n ],\n };\n }\n\n const result = await activateLicense(license_key, getDeviceId());\n\n if (!result.valid) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Activation failed: ${result.error ?? 'unknown error'}`,\n },\n ],\n };\n }\n\n const variantId = result.variantId!;\n\n // Check if a different key already covers this variant\n const existingForVariant = store.licenses.find(\n l => l.status === 'active' && l.variantId === variantId,\n );\n if (existingForVariant) {\n const label = getVariantLabel(variantId);\n const who = existingForVariant.customerName ? ` (${existingForVariant.customerName})` : '';\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} is already active${who}. No action needed.`,\n },\n ],\n };\n }\n\n const now = new Date().toISOString();\n addLicenseEntry({\n licenseKey: result.licenseKey || license_key,\n instanceId: result.instanceId || getDeviceId(),\n status: 'active',\n lastValidatedAt: now,\n activatedAt: now,\n variantId,\n customerName: result.customerName,\n productName: result.productName,\n variantName: result.variantName,\n });\n\n const label = getVariantLabel(variantId);\n const features = VARIANT_FEATURE_MAP[variantId];\n const featureList = features ? features.join(', ') : 'Pro features';\n const who = result.customerName ? ` Welcome, ${result.customerName}!` : '';\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} activated successfully.${who} Enabled: ${featureList}.`,\n },\n ],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n readLicenseStore,\n removeLicenseEntry,\n getVariantLabel,\n deactivateLicense,\n} from '@keepgoingdev/shared';\n\nexport function registerDeactivateLicense(server: McpServer) {\n server.tool(\n 'deactivate_license',\n 'Deactivate the KeepGoing Pro license on this device.',\n {\n license_key: z.string().optional().describe('Specific license key to deactivate. If omitted and only one license is active, deactivates it. If multiple are active, lists them.'),\n },\n async ({ license_key }) => {\n const store = readLicenseStore();\n const activeLicenses = store.licenses.filter(l => l.status === 'active');\n\n if (activeLicenses.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No active license found on this device.',\n },\n ],\n };\n }\n\n // Determine which license to deactivate\n let target;\n if (license_key) {\n target = activeLicenses.find(l => l.licenseKey === license_key);\n if (!target) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No active license found with key \"${license_key}\".`,\n },\n ],\n };\n }\n } else if (activeLicenses.length === 1) {\n target = activeLicenses[0];\n } else {\n // Multiple active licenses, ask user to specify\n const lines = ['Multiple active licenses found. Please specify which to deactivate using the license_key parameter:', ''];\n for (const l of activeLicenses) {\n const label = getVariantLabel(l.variantId);\n const who = l.customerName ? ` (${l.customerName})` : '';\n lines.push(`- ${label}${who}: ${l.licenseKey}`);\n }\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n }\n\n const result = await deactivateLicense(target.licenseKey, target.instanceId);\n removeLicenseEntry(target.licenseKey);\n\n const label = getVariantLabel(target.variantId);\n\n if (!result.deactivated) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} license cleared locally, but remote deactivation failed: ${result.error ?? 'unknown error'}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `${label} license deactivated successfully. The activation slot has been freed.`,\n },\n ],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { KeepGoingReader } from '../storage.js';\nimport {\n gatherContinueOnContext,\n formatContinueOnPrompt,\n} from '@keepgoingdev/shared';\nimport type { FormatOptions } from '@keepgoingdev/shared';\n\nexport function registerContinueOn(server: McpServer, reader: KeepGoingReader, workspacePath: string) {\n server.tool(\n 'continue_on',\n 'Export KeepGoing context as a formatted prompt for use in another AI tool (ChatGPT, Gemini, Copilot, etc.). Returns a markdown prompt with project status, last session, decisions, and recent commits.',\n {\n target: z.enum(['chatgpt', 'gemini', 'copilot', 'claude', 'general']).optional()\n .describe('Target AI tool (currently used for future format tuning)'),\n include_commits: z.boolean().default(true)\n .describe('Include recent commit messages in the prompt'),\n include_files: z.boolean().default(true)\n .describe('Include touched file paths in the prompt'),\n },\n async ({ target, include_commits, include_files }) => {\n if (!reader.exists()) {\n return {\n content: [{\n type: 'text' as const,\n text: 'No KeepGoing data found. Save a checkpoint first to use Continue On.',\n }],\n };\n }\n\n const context = gatherContinueOnContext(reader, workspacePath);\n\n if (!context.lastCheckpoint && !context.briefing) {\n return {\n content: [{\n type: 'text' as const,\n text: 'No session data available. Save a checkpoint first.',\n }],\n };\n }\n\n const formatOpts: FormatOptions = {\n target: target as FormatOptions['target'],\n includeCommits: include_commits,\n includeFiles: include_files,\n };\n\n const prompt = formatContinueOnPrompt(context, formatOpts);\n\n return {\n content: [{ type: 'text' as const, text: prompt }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport function registerResumePrompt(server: McpServer) {\n server.prompt(\n 'resume',\n 'Check developer momentum and suggest what to work on next',\n async () => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: [\n 'I just opened this project and want to pick up where I left off.',\n '',\n 'Please use the KeepGoing tools to:',\n '1. Check my current momentum (get_momentum)',\n '2. Get a re-entry briefing (get_reentry_briefing)',\n '3. Based on the results, give me a concise summary of where I left off and suggest what to work on next.',\n '',\n 'Keep your response brief and actionable.',\n ].join('\\n'),\n },\n },\n ],\n }),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport function registerDecisionsPrompt(server: McpServer) {\n server.prompt(\n 'decisions',\n 'Review recent architectural decisions and their rationale',\n async () => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: [\n 'I want to review recent architectural decisions in this project.',\n '',\n 'Please use the KeepGoing tools to:',\n '1. Fetch recent decision records (get_decisions)',\n '2. Get my current branch context (get_momentum)',\n '3. Summarize the decisions, highlighting any that were made on the current branch',\n '',\n 'Keep your response brief and organized.',\n ].join('\\n'),\n },\n },\n ],\n }),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport function registerProgressPrompt(server: McpServer) {\n server.prompt(\n 'progress',\n 'Summarize recent development progress across sessions',\n async () => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: [\n 'I need a summary of recent development progress for this project.',\n '',\n 'Please use the KeepGoing tools to:',\n '1. Fetch session history with a higher limit for broader coverage (get_session_history, limit: 20)',\n '2. Get my current branch context (get_momentum)',\n '3. Synthesize a progress summary grouped by branch or feature, highlighting the current branch',\n '',\n 'Format the summary so it can be used in a standup or sprint review.',\n ].join('\\n'),\n },\n },\n ],\n }),\n );\n}\n","import { findGitRoot } from '@keepgoingdev/shared';\n\n/**\n * Resolve the workspace path from CLI arguments.\n * Takes the first non-flag argument as an explicit path, falling back to CWD.\n */\nexport function resolveWsPath(args: string[] = process.argv.slice(2)): string {\n const explicit = args.find(a => !a.startsWith('--'));\n return findGitRoot(explicit || process.cwd());\n}\n","import { formatRelativeTime, getLicenseForFeatureWithRevalidation } from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\nimport { migrateStatusline } from './migrate.js';\n\nexport async function handlePrintMomentum(): Promise<void> {\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n\n if (!reader.exists()) {\n process.exit(0);\n }\n\n const { session: lastSession } = reader.getScopedLastSession();\n if (!lastSession) {\n process.exit(0);\n }\n\n const touchedCount = lastSession.touchedFiles?.length ?? 0;\n const lines: string[] = [];\n lines.push(`[KeepGoing] Last checkpoint: ${formatRelativeTime(lastSession.timestamp)}`);\n if (lastSession.summary) {\n lines.push(` Summary: ${lastSession.summary}`);\n }\n if (lastSession.nextStep) {\n lines.push(` Next step: ${lastSession.nextStep}`);\n }\n if (lastSession.blocker) {\n lines.push(` Blocker: ${lastSession.blocker}`);\n }\n if (lastSession.gitBranch) {\n lines.push(` Branch: ${lastSession.gitBranch}`);\n }\n if (touchedCount > 0) {\n lines.push(` Worked on ${touchedCount} files on ${lastSession.gitBranch ?? 'unknown branch'}`);\n }\n lines.push(' Tip: Use the get_reentry_briefing tool for a full briefing');\n\n // Auto-migrate legacy statusline if needed\n const migrationMsg = migrateStatusline(wsPath);\n if (migrationMsg) {\n lines.push(migrationMsg);\n }\n\n console.log(lines.join('\\n'));\n process.exit(0);\n}\n\nexport async function handlePrintCurrent(): Promise<void> {\n if (process.env.KEEPGOING_PRO_BYPASS !== '1' && !(await getLicenseForFeatureWithRevalidation('session-awareness'))) {\n process.exit(0);\n }\n\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n const tasks = reader.getCurrentTasks();\n\n if (tasks.length === 0) {\n process.exit(0);\n }\n\n const activeTasks = tasks.filter(t => t.sessionActive);\n const finishedTasks = tasks.filter(t => !t.sessionActive);\n\n // Summary line\n if (tasks.length > 1) {\n const parts: string[] = [];\n if (activeTasks.length > 0) parts.push(`${activeTasks.length} active`);\n if (finishedTasks.length > 0) parts.push(`${finishedTasks.length} finished`);\n console.log(`[KeepGoing] Sessions: ${parts.join(', ')}`);\n }\n\n // Print each task\n for (const task of [...activeTasks, ...finishedTasks]) {\n const prefix = task.sessionActive ? '[KeepGoing] Current task:' : '[KeepGoing] \\u2705 Last task:';\n const sessionLabel = task.agentLabel || task.sessionId || '';\n const labelSuffix = sessionLabel ? ` (${sessionLabel})` : '';\n const lines: string[] = [`${prefix} ${formatRelativeTime(task.updatedAt)}${labelSuffix}`];\n if (task.branch) {\n lines.push(` Branch: ${task.branch}`);\n }\n if (task.taskSummary) {\n lines.push(` Doing: ${task.taskSummary}`);\n }\n if (task.nextStep) {\n lines.push(` Next: ${task.nextStep}`);\n }\n console.log(lines.join('\\n'));\n }\n\n process.exit(0);\n}\n","import {\n resolveStorageRoot,\n KeepGoingWriter,\n createCheckpoint,\n getCurrentBranch,\n getTouchedFiles,\n getCommitsSince,\n getCommitMessagesSince,\n getFilesChangedInCommit,\n tryDetectDecision,\n getLicenseForFeatureWithRevalidation,\n generateSessionId,\n buildSessionEvents,\n buildSmartSummary,\n buildSmartNextStep,\n} from '@keepgoingdev/shared';\nimport path from 'node:path';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\n\nexport async function handleSaveCheckpoint(): Promise<void> {\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n\n const { session: lastSession } = reader.getScopedLastSession();\n\n // Skip if a checkpoint was written within the last 2 minutes (avoid duplicating extension checkpoints)\n if (lastSession?.timestamp) {\n const ageMs = Date.now() - new Date(lastSession.timestamp).getTime();\n if (ageMs < 2 * 60 * 1000) {\n process.exit(0);\n }\n }\n\n const touchedFiles = getTouchedFiles(wsPath);\n const commitHashes = getCommitsSince(wsPath, lastSession?.timestamp);\n\n // Skip if there's nothing to capture\n if (touchedFiles.length === 0 && commitHashes.length === 0) {\n process.exit(0);\n }\n\n const gitBranch = getCurrentBranch(wsPath);\n const commitMessages = getCommitMessagesSince(wsPath, lastSession?.timestamp);\n\n // Build SessionEvents for smart summary\n const now = new Date().toISOString();\n const events = buildSessionEvents({\n wsPath,\n commitHashes,\n commitMessages,\n touchedFiles,\n currentBranch: gitBranch ?? undefined,\n sessionStartTime: lastSession?.timestamp ?? now,\n lastActivityTime: now,\n });\n\n const summary = buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map(f => path.basename(f)).join(', ')}`;\n const nextStep = buildSmartNextStep(events);\n\n const projectName = path.basename(resolveStorageRoot(wsPath));\n const sessionId = generateSessionId({ workspaceRoot: wsPath, branch: gitBranch ?? undefined, worktreePath: wsPath });\n const checkpoint = createCheckpoint({\n summary,\n nextStep,\n gitBranch,\n touchedFiles,\n commitHashes,\n workspaceRoot: wsPath,\n source: 'auto',\n sessionId,\n });\n\n const writer = new KeepGoingWriter(wsPath);\n writer.saveCheckpoint(checkpoint, projectName);\n\n // Mark current task as finished using multi-session API\n writer.upsertSession({\n sessionId,\n sessionActive: false,\n nextStep: checkpoint.nextStep || undefined,\n branch: gitBranch ?? undefined,\n updatedAt: checkpoint.timestamp,\n });\n\n // Decision detection (Pro feature, requires valid Decision Detection license)\n // Loop all commits between checkpoints so none are missed\n if (process.env.KEEPGOING_PRO_BYPASS === '1' || (await getLicenseForFeatureWithRevalidation('decisions'))) {\n for (let i = 0; i < commitHashes.length; i++) {\n const hash = commitHashes[i];\n const message = commitMessages[i];\n if (!hash || !message) continue;\n const files = getFilesChangedInCommit(wsPath, hash);\n const detected = tryDetectDecision({\n workspacePath: wsPath,\n checkpointId: checkpoint.id,\n gitBranch,\n commitHash: hash,\n commitMessage: message,\n filesChanged: files,\n });\n if (detected) {\n console.log(`[KeepGoing] Decision detected: ${detected.category} (${(detected.confidence * 100).toFixed(0)}% confidence)`);\n }\n }\n }\n\n console.log(`[KeepGoing] Auto-checkpoint saved: ${summary}`);\n process.exit(0);\n}\n","import fs from 'node:fs';\n\nconst TAIL_READ_BYTES = 32_768;\nconst LATEST_LABEL_READ_BYTES = 65_536;\n\nconst TOOL_VERB_MAP: Record<string, string> = {\n Edit: 'editing',\n MultiEdit: 'editing',\n Write: 'editing',\n Read: 'researching',\n Glob: 'researching',\n Grep: 'researching',\n Bash: 'running',\n Agent: 'delegating',\n WebFetch: 'browsing',\n WebSearch: 'browsing',\n TodoWrite: 'planning',\n AskUserQuestion: 'discussing',\n EnterPlanMode: 'planning',\n ExitPlanMode: 'planning',\n TaskCreate: 'planning',\n TaskUpdate: 'planning',\n};\n\n/**\n * Truncates text at the last word boundary at or before `max` chars, appending ellipsis if cut.\n */\nexport function truncateAtWord(text: string, max: number): string {\n if (text.length <= max) return text;\n const cut = text.slice(0, max);\n const lastSpace = cut.lastIndexOf(' ');\n return (lastSpace > max / 2 ? cut.slice(0, lastSpace) : cut.slice(0, max - 1)) + '\\u2026';\n}\n\nconst FILLER_PREFIX_RE = /^(i want to|can you|please|let['']?s|could you|help me|i need to|i['']d like to|implement the following plan[:\\s]*|implement this plan[:\\s]*)\\s*/i;\nconst MARKDOWN_HEADING_RE = /^#+\\s+/;\n\n// Claude Code transcript JSONL entry shape (wrapped format)\ninterface TranscriptEntry {\n type?: string;\n message?: {\n role?: string;\n content?: unknown;\n };\n}\n\ninterface ContentPart {\n type?: string;\n text?: string;\n name?: string;\n}\n\nfunction extractTextFromContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n let text = '';\n for (const part of content as ContentPart[]) {\n if (part.type === 'text' && typeof part.text === 'string') {\n text += part.text + ' ';\n }\n }\n return text.trim();\n}\n\nfunction isUserEntry(entry: TranscriptEntry): boolean {\n // Claude Code wraps messages: { type: \"user\", message: { role: \"user\", content: [...] } }\n return entry.type === 'user' && entry.message?.role === 'user';\n}\n\nfunction getToolUseFromEntry(entry: TranscriptEntry): string | null {\n const content = entry.message?.content;\n if (!Array.isArray(content)) return null;\n for (const part of (content as ContentPart[]).slice().reverse()) {\n if (part.type === 'tool_use' && typeof part.name === 'string') {\n return part.name;\n }\n }\n return null;\n}\n\nfunction isAssistantEntry(entry: TranscriptEntry): boolean {\n return entry.message?.role === 'assistant';\n}\n\n/**\n * Extracts a stable session label from the first substantive user message in a transcript.\n * Returns null if no suitable message is found.\n */\nexport function extractSessionLabel(transcriptPath: string): string | null {\n if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;\n\n try {\n const raw = fs.readFileSync(transcriptPath, 'utf-8');\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(trimmed) as TranscriptEntry;\n } catch {\n continue;\n }\n\n if (!isUserEntry(entry)) continue;\n\n let text = extractTextFromContent(entry.message?.content);\n if (!text) continue;\n\n // Skip system-injected messages (bracket-prefixed or XML-tag-prefixed)\n if (text.startsWith('[') || /^<[a-z][\\w-]*>/.test(text)) continue;\n\n // Strip @-file mentions\n text = text.replace(/@[\\w./\\-]+/g, '').trim();\n // Strip filler prefixes\n text = text.replace(FILLER_PREFIX_RE, '').trim();\n // Strip markdown heading markers\n text = text.replace(MARKDOWN_HEADING_RE, '').trim();\n // Collapse whitespace/newlines to single space\n text = text.replace(/\\s+/g, ' ').trim();\n\n if (text.length < 20) continue;\n\n // Cap at 80 chars to bound reads; caller applies display budget\n if (text.length > 80) {\n text = text.slice(0, 80);\n }\n\n return text;\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n\n/**\n * Extracts the latest substantive user message from the transcript (tail-read, scan backwards).\n * Returns null if no suitable message is found within the read window.\n */\nexport function extractLatestUserLabel(transcriptPath: string): string | null {\n if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;\n\n try {\n const stat = fs.statSync(transcriptPath);\n const fileSize = stat.size;\n if (fileSize === 0) return null;\n\n const readSize = Math.min(fileSize, LATEST_LABEL_READ_BYTES);\n const offset = fileSize - readSize;\n\n const buf = Buffer.alloc(readSize);\n const fd = fs.openSync(transcriptPath, 'r');\n try {\n fs.readSync(fd, buf, 0, readSize, offset);\n } finally {\n fs.closeSync(fd);\n }\n\n const tail = buf.toString('utf-8');\n const lines = tail.split('\\n').reverse();\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(trimmed) as TranscriptEntry;\n } catch {\n continue;\n }\n\n if (!isUserEntry(entry)) continue;\n\n let text = extractTextFromContent(entry.message?.content);\n if (!text) continue;\n\n // Skip system-injected messages\n if (text.startsWith('[') || /^<[a-z][\\w-]*>/.test(text)) continue;\n\n // Strip @-file mentions\n text = text.replace(/@[\\w./\\-]+/g, '').trim();\n // Strip filler prefixes\n text = text.replace(FILLER_PREFIX_RE, '').trim();\n // Strip markdown heading markers\n text = text.replace(MARKDOWN_HEADING_RE, '').trim();\n // Collapse whitespace/newlines to single space\n text = text.replace(/\\s+/g, ' ').trim();\n\n if (text.length < 20) continue;\n\n if (text.length > 80) {\n text = text.slice(0, 80);\n }\n\n return text;\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n\n/**\n * Extracts the current action verb by reading the last tool_use entry from the transcript.\n * Reads only the last TAIL_READ_BYTES for efficiency.\n * Returns null if no tool use is found.\n */\nexport function extractCurrentAction(transcriptPath: string): string | null {\n if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;\n\n try {\n const stat = fs.statSync(transcriptPath);\n const fileSize = stat.size;\n if (fileSize === 0) return null;\n\n const readSize = Math.min(fileSize, TAIL_READ_BYTES);\n const offset = fileSize - readSize;\n\n const buf = Buffer.alloc(readSize);\n const fd = fs.openSync(transcriptPath, 'r');\n try {\n fs.readSync(fd, buf, 0, readSize, offset);\n } finally {\n fs.closeSync(fd);\n }\n\n const tail = buf.toString('utf-8');\n const lines = tail.split('\\n').reverse();\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let entry: TranscriptEntry;\n try {\n entry = JSON.parse(trimmed) as TranscriptEntry;\n } catch {\n continue;\n }\n\n if (!isAssistantEntry(entry)) continue;\n\n const toolName = getToolUseFromEntry(entry);\n if (toolName) {\n return TOOL_VERB_MAP[toolName] ?? 'working';\n }\n // Last assistant entry is text-only — AI finished its turn\n return 'done';\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n","import {\n KeepGoingWriter,\n getCurrentBranch,\n generateSessionId,\n type CurrentTask,\n} from '@keepgoingdev/shared';\nimport { resolveWsPath } from './util.js';\nimport { extractSessionLabel } from './transcriptUtils.js';\n\nexport async function handleUpdateTask(): Promise<void> {\n const args = process.argv.slice(2);\n const flagIndex = args.indexOf('--update-task');\n const payloadStr = args[flagIndex + 1];\n\n // Exclude the payload argument when resolving workspace path\n const wsArgs = args.filter((a, i) => !a.startsWith('--') && i !== flagIndex + 1);\n const wsPath = resolveWsPath(wsArgs.length > 0 ? wsArgs : undefined);\n\n if (payloadStr) {\n try {\n const payload = JSON.parse(payloadStr) as Partial<CurrentTask>;\n const writer = new KeepGoingWriter(wsPath);\n const branch = payload.branch ?? getCurrentBranch(wsPath) ?? undefined;\n const task: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string } = {\n ...payload,\n branch,\n worktreePath: wsPath,\n sessionActive: payload.sessionActive !== false,\n updatedAt: new Date().toISOString(),\n };\n\n // Use multi-session API if sessionId is present or can be derived\n const sessionId = payload.sessionId || generateSessionId({ ...task, workspaceRoot: wsPath });\n task.sessionId = sessionId;\n writer.upsertSession(task);\n } catch {\n // Exit silently on parse errors\n }\n }\n process.exit(0);\n}\n\nconst STDIN_TIMEOUT_MS = 5_000;\n\nexport async function handleUpdateTaskFromHook(): Promise<void> {\n const wsPath = resolveWsPath();\n\n const chunks: Buffer[] = [];\n const timeout = setTimeout(() => process.exit(0), STDIN_TIMEOUT_MS);\n process.stdin.on('error', () => { clearTimeout(timeout); process.exit(0); });\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\n process.stdin.on('end', () => {\n clearTimeout(timeout);\n try {\n const raw = Buffer.concat(chunks).toString('utf-8').trim();\n if (!raw) {\n process.exit(0);\n }\n const hookData = JSON.parse(raw) as {\n tool_name?: string;\n tool_input?: { file_path?: string; path?: string };\n session_id?: string;\n transcript_path?: string;\n };\n const toolName = hookData.tool_name ?? 'Edit';\n const filePath = hookData.tool_input?.file_path ?? hookData.tool_input?.path ?? '';\n const fileName = filePath ? filePath.split('/').pop() ?? filePath : '';\n const writer = new KeepGoingWriter(wsPath);\n // Reuse branch from OUR session to avoid spawning git on every edit.\n // Only match by sessionId so we never inherit a stale branch from another session.\n const existing = writer.readCurrentTasks();\n const sessionIdFromHook = hookData.session_id;\n const existingSession = sessionIdFromHook\n ? existing.find(t => t.sessionId === sessionIdFromHook)\n : undefined;\n const cachedBranch = existingSession?.branch;\n const branch = cachedBranch ?? getCurrentBranch(wsPath) ?? undefined;\n\n const task: Partial<CurrentTask> & { sessionActive: boolean; updatedAt: string } = {\n taskSummary: fileName ? `${toolName} ${fileName}` : `Used ${toolName}`,\n lastFileEdited: filePath || undefined,\n branch,\n worktreePath: wsPath,\n sessionActive: true,\n updatedAt: new Date().toISOString(),\n };\n\n // Derive session ID from context\n const sessionId = hookData.session_id || generateSessionId({ ...task, workspaceRoot: wsPath });\n task.sessionId = sessionId;\n\n // Cache sessionLabel on first hook fire; never overwrite once set\n if (!existingSession?.sessionLabel && hookData.transcript_path) {\n const label = extractSessionLabel(hookData.transcript_path);\n if (label) task.sessionLabel = label;\n }\n\n writer.upsertSession(task);\n } catch {\n // Exit silently on errors\n }\n process.exit(0);\n });\n process.stdin.resume();\n}\n","import { pruneStaleTasks, findGitRoot, formatRelativeTime } from '@keepgoingdev/shared';\nimport type { CurrentTasks } from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { extractSessionLabel, extractLatestUserLabel, extractCurrentAction, truncateAtWord } from './transcriptUtils.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst STDIN_TIMEOUT_MS = 3_000;\n\nexport async function handleStatusline(): Promise<void> {\n const chunks: Buffer[] = [];\n const timeout = setTimeout(() => process.exit(0), STDIN_TIMEOUT_MS);\n process.stdin.on('error', () => { clearTimeout(timeout); process.exit(0); });\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\n process.stdin.on('end', () => {\n clearTimeout(timeout);\n try {\n const raw = Buffer.concat(chunks).toString('utf-8').trim();\n if (!raw) process.exit(0);\n\n const input = JSON.parse(raw) as {\n session_id?: string;\n transcript_path?: string;\n agent?: { name?: string };\n workspace?: { current_dir?: string };\n cwd?: string;\n };\n\n const dir = input.workspace?.current_dir ?? input.cwd;\n if (!dir) process.exit(0);\n\n const transcriptPath = input.transcript_path;\n const sessionId = input.session_id;\n\n // Resolve label: agent.name > latest user message > cached sessionLabel > first user message > null\n let label: string | null = null;\n\n if (input.agent?.name) {\n label = input.agent.name;\n }\n\n // For active sessions, prefer the latest user message so the label\n // reflects the current topic, not just the initial prompt.\n if (!label && transcriptPath) {\n label = extractLatestUserLabel(transcriptPath);\n }\n\n if (!label) {\n // Try to find cached sessionLabel in current-tasks.json\n try {\n const gitRoot = findGitRoot(dir);\n const tasksFile = path.join(gitRoot, '.keepgoing', 'current-tasks.json');\n if (fs.existsSync(tasksFile)) {\n const data = JSON.parse(fs.readFileSync(tasksFile, 'utf-8')) as CurrentTasks;\n const tasks = pruneStaleTasks(data.tasks ?? []);\n const match = sessionId ? tasks.find(t => t.sessionId === sessionId) : undefined;\n if (match?.sessionLabel) {\n label = match.sessionLabel;\n }\n }\n } catch {\n // Ignore; fall through to transcript\n }\n }\n\n if (!label && transcriptPath) {\n label = extractSessionLabel(transcriptPath);\n }\n\n if (!label) {\n // No active session: show last meaningful checkpoint as fallback.\n // Prefer manual checkpoints over auto-saved ones (auto-saves from session end\n // always show \"just now\" which isn't useful).\n try {\n const gitRoot = findGitRoot(dir);\n const reader = new KeepGoingReader(gitRoot);\n if (reader.exists()) {\n const recent = reader.getScopedRecentSessions(10);\n const last = recent.find(s => s.source !== 'auto') ?? recent[0];\n if (last) {\n const ago = formatRelativeTime(last.timestamp);\n const summary = last.summary ? truncateAtWord(last.summary, 40) : null;\n const next = last.nextStep ? truncateAtWord(last.nextStep, 30) : null;\n const parts = [`[KG] ${ago}`];\n if (summary) parts.push(summary);\n if (next) parts.push(`\\u2192 ${next}`);\n process.stdout.write(`${parts.join(' \\u00b7 ')}\\n`);\n }\n }\n } catch {\n // Ignore\n }\n process.exit(0);\n }\n\n // Get current action from transcript\n const action = transcriptPath ? extractCurrentAction(transcriptPath) : null;\n\n // Dynamic budget: 40 chars with action verb, 55 chars without\n const budget = action ? 40 : 55;\n const displayLabel = truncateAtWord(label, budget);\n\n if (action) {\n process.stdout.write(`[KG] ${displayLabel} \\u00b7 ${action}\\n`);\n } else {\n process.stdout.write(`[KG] ${displayLabel}\\n`);\n }\n } catch {\n // Exit silently on errors\n }\n process.exit(0);\n });\n process.stdin.resume();\n}\n","import {\n gatherContinueOnContext,\n formatContinueOnPrompt,\n} from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\n\nexport async function handleContinueOn(): Promise<void> {\n const wsPath = resolveWsPath();\n const reader = new KeepGoingReader(wsPath);\n\n if (!reader.exists()) {\n process.exit(0);\n }\n\n const context = gatherContinueOnContext(reader, wsPath);\n\n if (!context.lastCheckpoint && !context.briefing) {\n process.exit(0);\n }\n\n const prompt = formatContinueOnPrompt(context);\n console.log(prompt);\n process.exit(0);\n}\n","import {\n getCurrentBranch,\n getHeadCommitHash,\n getCommitMessageByHash,\n getFilesChangedInCommit,\n tryDetectDecision,\n getLicenseForFeatureWithRevalidation,\n} from '@keepgoingdev/shared';\nimport { KeepGoingReader } from '../storage.js';\nimport { resolveWsPath } from './util.js';\n\n/**\n * Called by the global post-commit hook after every git commit.\n * Detects high-signal decisions from the HEAD commit and writes\n * them to `.keepgoing/decisions.json`.\n */\nexport async function handleDetectDecisions(): Promise<void> {\n const wsPath = resolveWsPath();\n\n // Respect Pro gate\n if (!(process.env.KEEPGOING_PRO_BYPASS === '1' || (await getLicenseForFeatureWithRevalidation('decisions')))) {\n process.exit(0);\n }\n\n // Only run if .keepgoing/ exists (auto-init is handled by write-triggers\n // like save_checkpoint, not by read-only detection)\n const reader = new KeepGoingReader(wsPath);\n if (!reader.exists()) {\n process.exit(0);\n }\n\n const gitBranch = getCurrentBranch(wsPath);\n const headHash = getHeadCommitHash(wsPath);\n if (!headHash) process.exit(0);\n\n // Get HEAD commit message by hash (avoids loading all recent commits)\n const commitMessage = getCommitMessageByHash(wsPath, headHash);\n if (!commitMessage) process.exit(0);\n\n const files = getFilesChangedInCommit(wsPath, headHash);\n\n const detected = tryDetectDecision({\n workspacePath: wsPath,\n gitBranch,\n commitHash: headHash,\n commitMessage,\n filesChanged: files,\n });\n\n if (detected) {\n console.log(`[KeepGoing] Decision detected: ${detected.category} (${(detected.confidence * 100).toFixed(0)}% confidence)`);\n }\n\n process.exit(0);\n}\n"],"mappings":"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","names":["path","fs","path","randomUUID","path","path","fs","randomUUID","fs","path","fs","os","path","fs","path","STORAGE_DIR","path","fs","fs","path","STORAGE_DIR","META_FILE","SESSIONS_FILE","DECISIONS_FILE","STATE_FILE","CURRENT_TASKS_FILE","path","fs","fs","os","path","path","os","claudeDir","fs","lastSession","state","z","z","z","z","path","z","z","path","z","z","z","fs","os","path","STATUSLINE_CMD","z","z","z","label","who","z","z","label","z","z","path","path","fs","fs","path","STDIN_TIMEOUT_MS"]}