@workscribe/cli 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/data-dirs.ts","../src/utils/paths.ts","../src/utils/private-directory.ts","../src/db/migrations/001_initial_schema.ts","../src/db/migrations/002_semantic_categories.ts","../src/db/migrations/003_alerts.ts","../src/db/migrations/004_ide_columns.ts","../src/db/migrations/005_session_summaries.ts","../src/config/ai-tools.json","../src/utils/logger.ts","../src/config/registry.ts","../src/summary/event-metadata.ts","../src/summary/commits.ts","../src/capture/tickets.ts","../src/config/index.ts","../src/profiles/schema.ts","../src/utils/fetch.ts","../src/profiles/fetcher.ts","../src/profiles/loader.ts","../src/capture/shell-syntax.ts","../src/capture/command-steps.ts","../src/summary/exclusions.ts","../src/normalize/compound.ts","../src/normalize/git-invocation.ts","../src/normalize/commit-message.ts","../src/normalize/dependency-metadata.ts","../src/normalize/patterns.ts","../src/normalize/reclassify.ts","../src/db/migrations/006_reclassify_other.ts","../src/db/migrations/007_data_jobs.ts","../src/db/migrations/008_dimensions.ts","../src/db/migrations/009_work_blocks.ts","../src/db/migrations/010_summaries.ts","../src/db/migrations/011_events_project_time.ts","../src/db/migrations/012_agent_sessions.ts","../src/db/migrations/013_tickets.ts","../src/db/migrations/index.ts","../src/utils/error-message.ts","../src/db/migrate.ts","../src/db/transaction.ts","../src/db/dimensions.ts","../src/db/agent-sessions.ts","../src/db/tickets.ts","../src/db/event-writer.ts","../src/db/summary-store.ts","../src/db/recent-events.ts","../src/summary/store.ts","../src/summary/anchors.ts","../src/db/index.ts","../src/capture/redact.ts","../src/capture/repo-name.ts","../src/capture/project-identity.ts","../src/capture/git.ts","../src/capture/git-snapshot.ts","../src/capture/aider.ts","../src/capture/workscribe-command.ts","../src/capture/filter.ts","../src/capture/ide.ts","../src/normalize/git-directory.ts","../src/capture/commit-message-file.ts","../src/utils/working-directory.ts","../src/capture/dimensions.ts","../src/capture/index.ts","../src/cli/capture.ts","../src/capture/detect-secrets.ts","../src/db/alerts.ts","../src/cli/hook.ts","../src/utils/completion-scripts.ts","../src/utils/backup.ts","../src/capture/completions-block.ts","../src/capture/shell-hook.ts","../src/capture/hooks/utils.ts","../src/capture/hooks/claude-code-command.ts","../src/capture/hooks/claude-code.ts","../src/capture/hooks/index.ts","../src/cli/completions.ts","../src/utils/memoize.ts","../src/skills/agents.ts","../src/skills/detect.ts","../src/skills/state.ts","../src/skills/installer.ts","../src/skills/package-path.ts","../src/skills/report.ts","../src/skills/actions.ts","../src/summary/legacy-ai.ts","../src/cli/legacy-ai-cleanup.ts","../src/cli/init.ts","../src/utils/format.ts","../src/cli/notices.ts","../src/db/jobs/store.ts","../src/db/jobs/directory-probes.ts","../src/db/jobs/project-resolution.ts","../src/db/jobs/project-dimensions-report.ts","../src/db/jobs/project-dimensions.ts","../src/db/jobs/frozen-old-sessions.ts","../src/db/jobs/summary-anchors.ts","../src/db/jobs/drop-old-sessions.ts","../src/db/jobs/ticket-refs.ts","../src/db/jobs/index.ts","../src/db/jobs/backup.ts","../src/db/jobs/runner.ts","../src/db/jobs/state.ts","../src/cli/data-jobs.ts","../src/cli/status.ts","../src/summary/git-commits.ts","../src/summary/story-commits.ts","../src/normalize/git-move.ts","../src/sessions/grouper.ts","../src/sessions/blocks.ts","../src/summary/heading.ts","../src/summary/payload.ts","../src/summary/contract/constants.ts","../src/summary/contract/utils.ts","../src/summary/contract/build.ts","../src/cli/events.ts","../src/summary/write-back.ts","../src/utils/stdin.ts","../src/db/thread-store.ts","../src/db/context-lookup.ts","../src/db/day-threads.ts","../src/sessions/threads.ts","../src/cli/fail.ts","../src/cli/thread-day.ts","../src/cli/session-summary.ts","../src/cli/sessions.ts","../src/ai/engines/template/constants.ts","../src/ai/engines/template/utils.ts","../src/ai/engines/template/describe.ts","../src/ai/engines/template/branch-moves.ts","../src/ai/engines/template/story.ts","../src/ai/engines/template/week.ts","../src/ai/engines/template/provider.ts","../src/summary/writer.ts","../src/integrations/webhook.ts","../src/integrations/slack.ts","../src/summary/webhook.ts","../src/summary/day-commits.ts","../src/summary/day.ts","../src/cli/day-notes.ts","../src/summary/weekly.ts","../src/cli/week.ts","../src/cli/summary.ts","../src/cli/export.ts","../src/cli/config.ts","../src/cli/pause.ts","../src/cli/undo.ts","../src/cli/clear.ts","../src/cli/uninstall.ts","../src/cli/profile.ts","../src/cli/note.ts","../src/cli/bug-report.ts","../src/cli/upgrade.ts","../src/cli/alerts.ts","../src/summary/file-details.ts","../src/cli/context-file.ts","../src/summary/thread-context.ts","../src/cli/context-threads.ts","../src/cli/context-view.ts","../src/cli/context.ts","../src/cli/skill.ts","../src/utils/update-check.ts","../src/main.ts","../src/utils/node-version.ts","../src/index.ts"],"sourcesContent":["import path from 'path'\n\n// Where Workscribe keeps its data, config and state (pure). Installs from before the XDG layout keep everything in\n// ~/.workscribe; fresh ones follow the XDG Base Directory spec. ~/.workscribe exists in both — the alerts file lives\n// there so shell hooks can find it — so a legacy install is known by its data, never by the folder.\n\nconst LEGACY_DIR_NAME = '.workscribe'\nconst APP_DIR_NAME = 'workscribe'\n/** A legacy install has one of these in ~/.workscribe; an XDG install never writes them there. */\nconst LEGACY_MARKERS = ['workscribe.db', 'config.json']\n\nexport interface DataDirsInput {\n homeDir: string\n env: Partial<Record<'XDG_DATA_HOME' | 'XDG_CONFIG_HOME' | 'XDG_STATE_HOME', string>>\n exists: (filePath: string) => boolean\n}\n\nexport interface DataDirs {\n legacyDir: string\n dataDir: string\n configDir: string\n stateDir: string\n}\n\nexport function resolveDataDirs({ homeDir, env, exists }: DataDirsInput): DataDirs {\n const legacyDir = path.join(homeDir, LEGACY_DIR_NAME)\n const isLegacy = LEGACY_MARKERS.some((marker) => exists(path.join(legacyDir, marker)))\n if (isLegacy) return { legacyDir, dataDir: legacyDir, configDir: legacyDir, stateDir: legacyDir }\n // An empty XDG_*_HOME counts as unset, as the spec says — else the data would land in the current folder\n const xdgDir = (variable: keyof DataDirsInput['env'], fallback: string[]): string =>\n path.join(env[variable] || path.join(homeDir, ...fallback), APP_DIR_NAME)\n return {\n legacyDir,\n dataDir: xdgDir('XDG_DATA_HOME', ['.local', 'share']),\n configDir: xdgDir('XDG_CONFIG_HOME', ['.config']),\n stateDir: xdgDir('XDG_STATE_HOME', ['.local', 'state']),\n }\n}\n","import path from 'path'\nimport os from 'os'\nimport fs from 'fs'\nimport { resolveDataDirs } from './data-dirs'\n\n// Decided once per process: legacy installs keep ~/.workscribe, fresh ones use XDG (see data-dirs.ts)\nconst {\n legacyDir: LEGACY_DIR,\n dataDir: xdgDataDir,\n configDir: xdgConfigDir,\n stateDir: xdgStateDir,\n} = resolveDataDirs({ homeDir: os.homedir(), env: process.env, exists: fs.existsSync })\n\n// All directories that must exist for Workscribe to function (deduplicated for legacy installs).\n// LEGACY_DIR (~/.workscribe) is always included so the alerts signal file has a predictable\n// location that shell hooks can check without knowing the XDG paths.\nexport const REQUIRED_DIRS: string[] = [...new Set([xdgDataDir, xdgConfigDir, xdgStateDir, LEGACY_DIR])]\n\nexport const WORKSCRIBE_DIR = xdgDataDir\nexport const DB_PATH = path.join(xdgDataDir, 'workscribe.db')\nexport const CONFIG_PATH = path.join(xdgConfigDir, 'config.json')\nexport const DEBUG_LOG_PATH = path.join(xdgStateDir, 'debug.log')\nexport const UPDATE_CHECK_PATH = path.join(xdgStateDir, 'update-check.json')\nexport const EXPORTS_DIR = path.join(xdgDataDir, 'exports')\n// Copies of the database taken before a data upgrade (src/db/jobs/backup.ts) — private, newest 3 kept\nexport const BACKUPS_DIR = path.join(xdgDataDir, 'backups')\nexport const PAUSE_FLAG_PATH = path.join(xdgStateDir, 'paused')\nexport const PROFILES_DIR = path.join(xdgDataDir, 'profiles')\n// Fixed at ~/.workscribe/alerts.pending so shell hooks can check it without knowing XDG paths.\n// Content is irrelevant — its presence signals pending alerts.\nexport const ALERTS_PENDING_PATH = path.join(LEGACY_DIR, 'alerts.pending')\n","import fs from 'fs'\n\nconst PRIVATE_DIRECTORY_MODE = 0o700\nconst GROUP_AND_OTHER_BITS = 0o077\n\n/**\n * Creates the folder (and any missing parents) readable only by you, and tightens an existing one that group or\n * others can open — so the database and its `-wal`/`-shm` files never sit in a shared folder.\n */\nexport function ensurePrivateDirectory(directory: string): void {\n fs.mkdirSync(directory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE })\n if ((fs.statSync(directory).mode & GROUP_AND_OTHER_BITS) !== 0) fs.chmodSync(directory, PRIVATE_DIRECTORY_MODE)\n}\n","export default {\n version: 1,\n description: 'initial schema',\n sql: `\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n captured_at TEXT NOT NULL,\n command TEXT NOT NULL,\n cwd TEXT NOT NULL,\n exit_code INTEGER,\n duration_ms INTEGER,\n repo_name TEXT,\n branch TEXT,\n category TEXT NOT NULL DEFAULT 'other',\n metadata TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_captured_at ON events(captured_at);\n CREATE INDEX IF NOT EXISTS idx_events_repo_name ON events(repo_name);\n\n CREATE TABLE IF NOT EXISTS sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n repo_name TEXT,\n event_count INTEGER NOT NULL DEFAULT 0,\n summary TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_sessions_date ON sessions(date);\n\n CREATE TABLE IF NOT EXISTS session_events (\n session_id INTEGER NOT NULL REFERENCES sessions(id),\n event_id INTEGER NOT NULL REFERENCES events(id),\n PRIMARY KEY (session_id, event_id)\n );\n `,\n}\n","const migration = {\n version: 2,\n description: 'Rename granular categories to 13 universal semantic categories',\n sql: `\n UPDATE events SET category = 'shipping' WHERE category IN ('code_commit','code_push','code_merge');\n UPDATE events SET category = 'versioning' WHERE category IN ('code_pull','branch_switch','code_stash','git_other');\n UPDATE events SET category = 'building' WHERE category = 'build';\n UPDATE events SET category = 'running' WHERE category IN ('server_run','script_run');\n UPDATE events SET category = 'testing' WHERE category = 'test_run';\n UPDATE events SET category = 'linting' WHERE category = 'lint_run';\n UPDATE events SET category = 'dependencies' WHERE category IN ('dependency_install','dependency_remove');\n UPDATE events SET category = 'infrastructure' WHERE category = 'infrastructure';\n UPDATE events SET category = 'files' WHERE category IN ('file_ops','environment');\n UPDATE events SET category = 'exploring' WHERE category IN ('code_search','code_read');\n `,\n}\n\nexport default migration\n","const migration = {\n version: 3,\n description: 'Add alerts table for AI session secret detection',\n sql: `\n CREATE TABLE IF NOT EXISTS alerts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at INTEGER NOT NULL,\n tool TEXT NOT NULL,\n session_id TEXT,\n context TEXT NOT NULL,\n cleared INTEGER NOT NULL DEFAULT 0\n );\n CREATE INDEX IF NOT EXISTS alerts_cleared ON alerts (cleared);\n `,\n}\n\nexport default migration\n","const migration = {\n version: 4,\n description: 'Add source and file_path columns to events for IDE integration',\n sql: `\n ALTER TABLE events ADD COLUMN source TEXT;\n ALTER TABLE events ADD COLUMN file_path TEXT;\n CREATE INDEX IF NOT EXISTS idx_events_file_path ON events (file_path, cwd);\n `,\n}\n\nexport default migration\n","// Must produce exactly what sessionKey() in src/summary/store.ts produces — pinned by a test.\nexport const SESSION_KEY_SQL = \"COALESCE(repo_name, '') || '|' || started_at\"\n\nconst migration = {\n version: 5,\n description: 'Store session summaries per engine, keyed by repo and start time so they survive session rebuilds',\n sql: `\n CREATE TABLE IF NOT EXISTS session_summaries (\n session_key TEXT NOT NULL,\n engine TEXT NOT NULL,\n text TEXT NOT NULL,\n created_at TEXT NOT NULL,\n PRIMARY KEY (session_key, engine)\n );\n INSERT OR IGNORE INTO session_summaries (session_key, engine, text, created_at)\n SELECT ${SESSION_KEY_SQL}, 'legacy', summary, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n FROM sessions\n WHERE summary IS NOT NULL;\n `,\n}\n\nexport default migration\n","[\n {\n \"tool\": \"claude\",\n \"displayName\": \"Claude Code\",\n \"pattern\": \"^claude\\\\b\",\n \"hook\": \"claude-code\",\n \"contextSource\": \"hook\",\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"aider\",\n \"displayName\": \"Aider\",\n \"pattern\": \"^aider\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"markdown\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"codex\",\n \"displayName\": \"Codex CLI\",\n \"pattern\": \"^codex\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"jsonl\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"gemini\",\n \"displayName\": \"Gemini CLI\",\n \"pattern\": \"^gemini\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"json\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"gh copilot\",\n \"displayName\": \"GitHub Copilot\",\n \"pattern\": \"^gh\\\\s+copilot\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"jsonl\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"amazon-q\",\n \"displayName\": \"Amazon Q\",\n \"pattern\": \"^q\\\\s+(chat|dev)\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"json\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"opencode\",\n \"displayName\": \"OpenCode\",\n \"pattern\": \"^opencode\\\\b\",\n \"hook\": null,\n \"contextSource\": \"sqlite\",\n \"historyFormat\": \"sqlite\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"kilo\",\n \"displayName\": \"Kilo Code\",\n \"pattern\": \"^kilo\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"grok\",\n \"displayName\": \"Grok Build\",\n \"pattern\": \"^grok\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": \"Grok Build was found to silently upload entire local git repositories — including .env files and untracked secrets — to Google Cloud Storage without user disclosure (July 2026 incident). Workscribe does not read Grok Build session files. Consider whether this tool is appropriate for use in repositories containing sensitive credentials.\"\n },\n {\n \"tool\": \"continue\",\n \"displayName\": \"Continue\",\n \"pattern\": \"^continue\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"cody\",\n \"displayName\": \"Cody\",\n \"pattern\": \"^cody\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"cursor\",\n \"displayName\": \"Cursor\",\n \"pattern\": \"^cursor\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n }\n]\n","import chalk from 'chalk'\nimport fs from 'fs'\nimport { DEBUG_LOG_PATH } from './paths'\n\nconst isDebug = process.env.WORKSCRIBE_DEBUG === '1'\n\nexport const log = {\n info: (msg: string) => console.log(chalk.cyan('→'), msg),\n success: (msg: string) => console.log(chalk.green('✓'), msg),\n warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),\n error: (msg: string) => console.error(chalk.red('✗'), msg),\n /** A warning on stderr — keeps stdout clean for JSON output (summary.format=json) and pipes. */\n notice: (msg: string) => process.stderr.write(`${chalk.yellow('⚠')} ${msg}\\n`),\n plain: (msg: string) => console.log(msg),\n}\n\nexport function debugLog(context: string, error: unknown): void {\n if (!isDebug) return\n const entry = `[${new Date().toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}\\n`\n try {\n fs.appendFileSync(DEBUG_LOG_PATH, entry, { mode: 0o600 })\n } catch {\n // If we can't write to the debug log, silently ignore\n }\n}\n","import fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport BUILTIN_TOOLS from './ai-tools.json'\nimport { debugLog } from '../utils/logger'\n\nexport interface AIToolEntry {\n tool: string\n displayName: string\n pattern: string\n hook: string | null\n contextSource: 'hook' | 'history-file' | 'sqlite' | null\n historyFormat: 'jsonl' | 'json' | 'markdown' | 'sqlite' | null\n securityWarning: string | null\n}\n\nconst USER_REGISTRY_PATH = path.join(os.homedir(), '.workscribe', 'ai-tools.json')\n\nexport function loadAIToolRegistry(): AIToolEntry[] {\n let userEntries: AIToolEntry[] = []\n\n try {\n if (fs.existsSync(USER_REGISTRY_PATH)) {\n const raw = fs.readFileSync(USER_REGISTRY_PATH, 'utf-8')\n const parsed = JSON.parse(raw)\n if (Array.isArray(parsed)) {\n userEntries = parsed\n .filter((entry) => typeof entry.tool === 'string' && typeof entry.pattern === 'string')\n .map((entry) => ({\n tool: entry.tool as string,\n displayName: (entry.displayName as string | undefined) ?? (entry.tool as string),\n pattern: entry.pattern as string,\n hook: (entry.hook as string | null | undefined) ?? null,\n contextSource: (entry.contextSource as AIToolEntry['contextSource'] | undefined) ?? null,\n historyFormat: (entry.historyFormat as AIToolEntry['historyFormat'] | undefined) ?? null,\n securityWarning: (entry.securityWarning as string | null | undefined) ?? null,\n }))\n }\n }\n } catch {\n debugLog('registry', 'Failed to load user AI tool registry — using built-ins only')\n }\n\n if (userEntries.length === 0) return BUILTIN_TOOLS as AIToolEntry[]\n\n const userToolSlugs = new Set(userEntries.map((entry) => entry.tool))\n const filteredBuiltins = (BUILTIN_TOOLS as AIToolEntry[]).filter((entry) => !userToolSlugs.has(entry.tool))\n return [...filteredBuiltins, ...userEntries]\n}\n","// Reads an event row's stored metadata. A leaf module (no imports) so both the payload builder and the commit\n// helpers can use it without importing each other.\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Metadata is best-effort context for the summary — a corrupt or non-object row must not fail it. */\nexport function parseMetadata(raw: string | null): Record<string, unknown> {\n if (!raw) return {}\n try {\n const parsed: unknown = JSON.parse(raw)\n return isPlainObject(parsed) ? parsed : {}\n } catch {\n return {}\n }\n}\n","// Commits Workscribe captured, and how they merge with git's. A leaf module: it imports only types and the metadata\n// reader, so the payload builder and the JSON contract can both use it without an import cycle.\nimport { parseMetadata } from './event-metadata'\nimport type { EventRow } from '../db/types'\nimport type { GitCommit } from './git-commits'\nimport type { CommitJson } from './contract/types'\n\n/** A command that ran and failed. An unknown exit code (`null`, commands agents ran) is not a failure. */\nexport function isFailedExit(exitCode: number | null): boolean {\n return exitCode !== null && exitCode !== 0\n}\n\n/** A `git commit` that failed (rejected by a hook, nothing staged) made no commit. */\nexport function isFailedCommand(row: Pick<EventRow, 'exit_code'>): boolean {\n return isFailedExit(row.exit_code)\n}\n\n/** A commit Workscribe captured itself, and when — null when only the subject is known. */\nexport interface CapturedCommit {\n subject: string\n committedAt: string | null\n /** The branch the capture happened on — the commit's branch, for the story */\n branch: string | null\n}\n\n/** What finding an event's commits reads — a stored row, or an event about to be stored. */\nexport type CommitSource = Pick<EventRow, 'category' | 'metadata' | 'exit_code' | 'captured_at' | 'branch'>\n\n/**\n * Commits Workscribe captured itself: `git commit -m` messages, at the time the commit finished, and AI-session\n * commits (subjects only — the session records which commits reached HEAD, not when). A `git commit` that failed\n * (rejected by a hook, nothing staged) made no commit, so it is skipped. A repeated subject keeps its first place,\n * and the first time any capture of it recorded.\n */\nexport function collectCapturedCommits(rows: CommitSource[]): CapturedCommit[] {\n const commits = rows.flatMap((row): CapturedCommit[] => {\n const metadata = parseMetadata(row.metadata)\n if (row.category === 'shipping' && typeof metadata.message === 'string' && !isFailedCommand(row)) {\n return [{ subject: metadata.message, committedAt: row.captured_at, branch: row.branch }]\n }\n if (row.category === 'ai_session' && Array.isArray(metadata.commits)) {\n return metadata.commits\n .filter((subject): subject is string => typeof subject === 'string')\n .map((subject) => ({ subject, committedAt: null, branch: row.branch }))\n }\n return []\n })\n const bySubject = new Map<string, CapturedCommit>()\n for (const commit of commits) {\n if (commit.subject === '') continue\n const earlier = bySubject.get(commit.subject)\n // An AI session recorded before its `git commit -m` mustn't hide the time (and branch) that capture has\n const isBetterTimed = earlier !== undefined && earlier.committedAt === null && commit.committedAt !== null\n bySubject.set(\n commit.subject,\n isBetterTimed ? { ...earlier, committedAt: commit.committedAt, branch: commit.branch } : (earlier ?? commit)\n )\n }\n return [...bySubject.values()]\n}\n\n/**\n * Whether a captured subject is this git commit. Besides an exact match, it tolerates the two ways\n * events captured before the `-m` parsing fix were cut wrong — and only those, so a short `fix`\n * never matches `fix: something else`:\n * - stopped at an inner quote: `fix: don` for `fix: don't redirect`\n * - ran on into flags: `wip --no-verify` for `wip`\n */\nexport function isSameCommit(captured: string, gitSubject: string): boolean {\n if (captured === gitSubject) return true\n if (gitSubject.startsWith(captured)) return /^['\"]/.test(gitSubject.slice(captured.length))\n if (captured.startsWith(gitSubject)) return /^\\s+-/.test(captured.slice(gitSubject.length))\n return false\n}\n\n/**\n * Git commits first, oldest first and each listed even if subjects repeat (they are distinct commits),\n * then captured subjects git didn't return — amended, rebased or on another branch — with no hash.\n * Captured subjects git attributes only to other people are dropped: an AI session records every\n * commit that reached HEAD, including ones pulled or rebased in.\n */\nexport function mergeCommits(\n captured: CapturedCommit[],\n fromGit: GitCommit[],\n othersSubjects: string[] = []\n): CommitJson[] {\n const others = new Set(othersSubjects)\n const gitCommits: CommitJson[] = fromGit.map(({ hash, subject, committedAt }) => ({\n hash,\n subject,\n committedAt,\n source: 'git',\n }))\n const capturedOnly: CommitJson[] = captured\n .filter(({ subject }) => !others.has(subject))\n .filter(({ subject }) => !fromGit.some((commit) => isSameCommit(subject, commit.subject)))\n .map(({ subject, committedAt }) => ({ hash: null, subject, committedAt, source: 'captured' }))\n return [...gitCommits, ...capturedOnly]\n}\n","// Which tickets an event names (pure): keys matching the ticket pattern in its linked branch's name, and in the\n// subjects of the commits it made. Used by capture and by the `ticket-refs` job, so both derive the same links.\nimport { collectCapturedCommits, type CommitSource } from '../summary/commits'\nimport type { TicketKeys } from '../db/tickets'\n\nexport const DEFAULT_TICKET_PATTERN = '[A-Z][A-Z0-9]+-\\\\d+'\n\n/** Only the start of a long text is searched: ticket keys sit at the start of branch names and subjects. */\nconst MAX_SEARCHED_CHARACTERS = 200\n\nconst REPEATS = new Set(['*', '+', '{'])\nconst VARIES = new Set(['*', '+', '?', '{', '|'])\n\n/** Where a character class `[...]` that opens at `start` ends (its `]`), escapes inside skipped. */\nfunction endOfClass(source: string, start: number): number {\n let index = start + 1\n if (source[index] === '^') index++\n if (source[index] === ']') index++\n while (index < source.length && source[index] !== ']') index += source[index] === '\\\\' ? 2 : 1\n return index\n}\n\n/**\n * Whether a group that repeats (`*`, `+`, `{…}`) holds anything that varies — a quantifier or an `|`, at any depth:\n * `(A+)+`, `((A+))+`, `(A|AA)+`. That is the shape that can backtrack exponentially on a near miss. Deliberately\n * conservative: it also refuses some safe patterns, such as `(?:-[A-Z]+)*`.\n */\nfunction hasNestedRepetition(source: string): boolean {\n const groupsVary: boolean[] = []\n for (let index = 0; index < source.length; index++) {\n const character = source[index]\n if (character === '\\\\') {\n index++\n } else if (character === '[') {\n index = endOfClass(source, index)\n } else if (character === '(') {\n groupsVary.push(false)\n if (source[index + 1] === '?') index++\n } else if (character === ')') {\n const varies = groupsVary.pop() ?? false\n if (varies && REPEATS.has(source[index + 1])) return true\n } else if (VARIES.has(character)) {\n groupsVary.fill(true)\n }\n }\n return false\n}\n\n/** Why a ticket pattern can't be used, or null when it can. */\nexport function ticketPatternProblem(source: string): string | null {\n if (hasNestedRepetition(source)) {\n return 'it repeats a group that itself repeats or has alternatives, which can hang on a near miss'\n }\n let pattern: RegExp\n try {\n pattern = new RegExp(source)\n } catch (error) {\n return `it isn't a valid regular expression (${(error as Error).message})`\n }\n return pattern.test('') ? 'it matches empty text, so it would find a ticket everywhere' : null\n}\n\n/** The pattern to find ticket keys with: the configured one, or the default when it's missing or unusable. */\nexport function ticketPatternOf(source: unknown): RegExp {\n const usable = typeof source === 'string' && ticketPatternProblem(source) === null ? source : DEFAULT_TICKET_PATTERN\n return new RegExp(usable, 'g')\n}\n\n/**\n * The keys in a text's searched start. A match that runs into the cut may be a longer key cut short (`WOR-1234` read\n * as `WOR-1`), so it's dropped; so is an empty match, which a pattern like `[A-Z]*(?=-\\d)` can make inside a text.\n */\nfunction keysIn(text: string, pattern: RegExp): string[] {\n const searched = text.slice(0, MAX_SEARCHED_CHARACTERS)\n const isCut = text.length > searched.length\n return [...searched.matchAll(pattern)]\n .filter((match) => match[0] !== '' && !(isCut && match.index + match[0].length === searched.length))\n .map((match) => match[0])\n}\n\nfunction distinct(keys: string[]): string[] {\n return [...new Set(keys)]\n}\n\n/**\n * The keys an event names. Branch keys come from the branch the event is linked to — never its raw `branch` text,\n * which a save in another repo keeps without a link. A failed commit names nothing.\n */\nexport function ticketKeysOf(\n { linkedBranch, event }: { linkedBranch: string | null; event: CommitSource },\n pattern: RegExp\n): TicketKeys {\n const subjects = collectCapturedCommits([event]).map((commit) => commit.subject)\n return {\n branch: linkedBranch === null ? [] : distinct(keysIn(linkedBranch, pattern)),\n commits: distinct(subjects.flatMap((subject) => keysIn(subject, pattern))),\n }\n}\n","import crypto from 'crypto'\nimport fs from 'fs'\nimport path from 'path'\nimport { CONFIG_PATH } from '../utils/paths'\nimport { debugLog } from '../utils/logger'\nimport { DEFAULT_TICKET_PATTERN } from '../capture/tickets'\n\n/**\n * AI provider settings from Workscribe 1.x. 2.0 removed AI providers, so nothing writes these any more — they're\n * read only to tell 1.x users (see src/summary/legacy-ai.ts) and to keep a stored API key out of bug reports.\n */\nexport interface LegacyAiConfig {\n provider?: string\n apiKey?: string\n model?: string\n baseUrl?: string\n}\n\nexport interface WorkscribeConfig {\n ai?: LegacyAiConfig\n session: {\n idleTimeout: number // minutes\n }\n git: {\n // When true, repo_name is \"owner/repo\" instead of bare \"repo\". Off by\n // default — bare names are nicer for the common case of one repo per\n // name. Turn on if you work across repos whose bare names collide\n // (e.g. an org that names plugin repos after the tool they target:\n // workscribe/vscode, workscribe/nvim, ...).\n qualifyRepoName: boolean\n }\n capture: {\n ignore: string[]\n aiTools: boolean\n includeAiderPrompt: boolean\n profiles: string[]\n }\n redact: {\n extra: string[]\n }\n summary: {\n format: 'markdown' | 'json'\n // Categories re-included in summaries (overrides built-in exclusions).\n // Excluded by default: exploring, other, linting.\n // Example: [\"linting\"] to include lint pass/fail in summaries.\n includeCategories?: string[]\n // 1.x setting (\"template\" | \"ollama\" | \"provider\"). 2.0 always uses the template; read only to detect\n // old configs (src/summary/legacy-ai.ts).\n engine?: string\n }\n projects?: Record<string, string> // absolute path → display name override\n integrations?: {\n slackWebhook?: string\n webhook?: string\n }\n telemetry: {\n enabled: boolean\n installId: string\n }\n tickets: {\n // What a ticket key looks like in branch names and commit messages (a regular expression). Change it with\n // `workscribe config set tickets.pattern`, which also re-links the history.\n pattern: string\n }\n}\n\nconst DEFAULTS: WorkscribeConfig = {\n session: {\n idleTimeout: 30,\n },\n git: {\n qualifyRepoName: false,\n },\n capture: {\n ignore: ['cd', 'ls', 'll', 'la', 'ls -la', 'ls -l', 'ls -a', 'pwd', 'clear', 'exit', 'history', 'man'],\n aiTools: true,\n includeAiderPrompt: true,\n profiles: [],\n },\n redact: {\n extra: [],\n },\n summary: {\n format: 'markdown',\n },\n telemetry: {\n enabled: true,\n installId: '',\n },\n tickets: {\n pattern: DEFAULT_TICKET_PATTERN,\n },\n}\n\nexport function readConfig(): WorkscribeConfig {\n if (!fs.existsSync(CONFIG_PATH)) return deepMerge(DEFAULTS, {})\n try {\n const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')\n const parsed = JSON.parse(raw)\n return deepMerge(DEFAULTS, parsed)\n } catch {\n return deepMerge(DEFAULTS, {})\n }\n}\n\nexport function writeConfig(config: WorkscribeConfig): void {\n // Previously safe to assume — writeConfig was only ever called after `init`\n // created the data directories, or by `config set` where init had already\n // run. CLI-05's fallback can now call this as the very first filesystem\n // touch for a user who skipped init entirely, so it can't assume the dir exists.\n fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true, mode: 0o700 })\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 })\n // Enforce permissions on existing files (mode only applies on creation above)\n try {\n fs.chmodSync(CONFIG_PATH, 0o600)\n } catch {\n /* ignore */\n }\n}\n\n// Generates and persists telemetry.installId on first use. Covers both a\n// fresh install and an existing user upgrading — deepMerge already backfills\n// the telemetry key itself, this just fills in the empty installId.\nexport function ensureInstallId(config: WorkscribeConfig): string {\n if (config.telemetry.installId) return config.telemetry.installId\n\n const installId = crypto.randomUUID()\n writeConfig({ ...config, telemetry: { ...config.telemetry, installId } })\n return installId\n}\n\n// Fallback for users who skip `init` entirely — which is exactly what an\n// existing user upgrading will typically do, since they never re-run it.\n// Presence of installId is the only state tracked; no separate \"notice\n// shown\" flag is needed since this is a no-op once installId is set.\nexport function ensureTelemetryFallback(): void {\n // Unlike init's explicit telemetry step, this runs unannounced ahead of\n // every command — it must never throw, however it fails, or a disk issue\n // (read-only $HOME, full disk, a locked-down CI sandbox) would crash the\n // user's actual command before it even starts.\n try {\n const config = readConfig()\n if (config.telemetry.installId) return\n\n config.telemetry.enabled = true\n config.telemetry.installId = ensureInstallId(config)\n\n process.stderr.write(\n '\\n Workscribe collects anonymous install stats (install ID, CLI version, platform, Node version).\\n Opt out: workscribe config set telemetry.enabled false\\n\\n'\n )\n } catch (error) {\n debugLog('ensureTelemetryFallback', error)\n }\n}\n\nexport function getConfigValue(config: WorkscribeConfig, key: string): unknown {\n const parts = key.split('.')\n let cur: unknown = config\n for (const part of parts) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\nexport function setConfigValue(config: WorkscribeConfig, key: string, value: string): WorkscribeConfig {\n const parts = key.split('.')\n const result = JSON.parse(JSON.stringify(config)) as WorkscribeConfig\n let cur = result as unknown as Record<string, unknown>\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]\n if (cur[part] == null || typeof cur[part] !== 'object') {\n cur[part] = {}\n }\n cur = cur[part] as Record<string, unknown>\n }\n const lastKey = parts[parts.length - 1]\n // Coerce value type\n if (value === 'true') cur[lastKey] = true\n else if (value === 'false') cur[lastKey] = false\n else if (!isNaN(Number(value))) cur[lastKey] = Number(value)\n else cur[lastKey] = value\n return result\n}\n\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n // structuredClone, not a shallow spread — any key overrides doesn't touch\n // (e.g. a config file written before a newer top-level key existed) must\n // still be an independent copy, not a shared reference back into the\n // caller's `defaults` object. A shallow spread here previously let any\n // caller that mutates a nested field on the returned config (e.g.\n // ensureTelemetryFallback) silently corrupt the module-level DEFAULTS\n // singleton for the rest of the process.\n const result = structuredClone(defaults)\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const val = overrides[key]\n if (val !== null && typeof val === 'object' && !Array.isArray(val) && typeof defaults[key] === 'object') {\n result[key] = deepMerge(defaults[key] as object, val as object) as T[keyof T]\n } else if (val !== undefined) {\n result[key] = val as T[keyof T]\n }\n }\n return result\n}\n","export interface ProfileRule {\n pattern: string\n category: string\n metadata?: Record<string, string>\n}\n\nexport interface Profile {\n name: string\n displayName: string\n version: string\n detect?: { files?: string[] }\n rules: ProfileRule[]\n aiContext?: string\n}\n\nexport interface ManifestEntry {\n name: string\n displayName: string\n tags: string[]\n description: string\n keywords: string[]\n detect?: { files?: string[] }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0\n}\n\nfunction isValidRule(rule: unknown): boolean {\n if (!rule || typeof rule !== 'object') return false\n const { pattern, category } = rule as Record<string, unknown>\n return isNonEmptyString(pattern) && isNonEmptyString(category)\n}\n\nexport function isValidProfile(candidate: unknown): candidate is Profile {\n if (!candidate || typeof candidate !== 'object') return false\n const { name, displayName, version, rules } = candidate as Record<string, unknown>\n if (!isNonEmptyString(name) || !isNonEmptyString(displayName) || !isNonEmptyString(version)) return false\n if (!Array.isArray(rules)) return false\n return rules.every(isValidRule)\n}\n\nexport function isValidManifest(m: unknown): m is ManifestEntry[] {\n return (\n Array.isArray(m) &&\n m.every((e) => e && typeof e === 'object' && typeof (e as Record<string, unknown>).name === 'string')\n )\n}\n","const DEFAULT_TIMEOUT_MS = 30_000\n\nconst NETWORK_ERROR_CODES = new Set([\n 'ENOTFOUND',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'ETIMEDOUT',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n])\n\nfunction isNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const cause = (err as Error & { cause?: unknown }).cause\n const code =\n (err as NodeJS.ErrnoException).code ?? (cause instanceof Error ? (cause as NodeJS.ErrnoException).code : undefined)\n return NETWORK_ERROR_CODES.has(code ?? '')\n}\n\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<Response> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...init, signal: controller.signal })\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Request timed out after ${timeoutMs / 1000}s`)\n }\n if (isNetworkError(err)) {\n throw new Error(\"No internet connection — try again when you're online\")\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { PROFILES_DIR } from '../utils/paths'\nimport { isValidProfile, isValidManifest } from './schema'\nimport type { ManifestEntry, Profile } from './schema'\nimport { debugLog } from '../utils/logger'\nimport { fetchWithTimeout } from '../utils/fetch'\n\nconst REGISTRY_BASE = 'https://raw.githubusercontent.com/workscribe/profiles/main'\n\nfunction manifestCachePath(): string {\n return path.join(PROFILES_DIR, 'manifest.json')\n}\n\nexport async function fetchManifest(): Promise<ManifestEntry[]> {\n const response = await fetchWithTimeout(`${REGISTRY_BASE}/manifest.json`, {})\n if (!response.ok) throw new Error(`Registry unreachable (${response.status})`)\n const data: unknown = await response.json()\n if (!isValidManifest(data)) throw new Error('Manifest format unrecognised')\n return data\n}\n\nexport async function fetchProfile(name: string): Promise<Profile> {\n const response = await fetchWithTimeout(`${REGISTRY_BASE}/${name}.json`, {})\n if (response.status === 404) throw new Error(`Profile \"${name}\" not found in registry`)\n if (!response.ok) throw new Error(`Registry unreachable (${response.status})`)\n const data: unknown = await response.json()\n if (!isValidProfile(data)) throw new Error(`Profile \"${name}\" failed schema validation`)\n return data\n}\n\nexport async function installProfile(name: string): Promise<void> {\n ensureProfilesDir()\n const profile = await fetchProfile(name)\n const dest = path.join(PROFILES_DIR, `${name}.json`)\n fs.writeFileSync(dest, JSON.stringify(profile, null, 2), { encoding: 'utf-8', mode: 0o600 })\n debugLog('profiles', `Installed profile \"${name}\" to ${dest}`)\n}\n\nexport async function cacheManifest(): Promise<ManifestEntry[]> {\n ensureProfilesDir()\n const manifest = await fetchManifest()\n fs.writeFileSync(manifestCachePath(), JSON.stringify(manifest, null, 2), { encoding: 'utf-8', mode: 0o600 })\n return manifest\n}\n\nexport function readCachedManifest(): ManifestEntry[] | null {\n if (!fs.existsSync(manifestCachePath())) return null\n try {\n const data: unknown = JSON.parse(fs.readFileSync(manifestCachePath(), 'utf-8'))\n return isValidManifest(data) ? data : null\n } catch {\n return null\n }\n}\n\nfunction ensureProfilesDir(): void {\n if (!fs.existsSync(PROFILES_DIR)) {\n fs.mkdirSync(PROFILES_DIR, { recursive: true, mode: 0o700 })\n }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { PROFILES_DIR } from '../utils/paths'\nimport { readConfig } from '../config'\nimport { isValidProfile } from './schema'\nimport type { Profile, ManifestEntry, ProfileRule } from './schema'\nimport { readCachedManifest } from './fetcher'\nimport { debugLog } from '../utils/logger'\n\n// Matches the Rule interface in patterns.ts — structural compatibility without circular import\ninterface CompiledRule {\n match(cmd: string): { category: string; metadata: Record<string, unknown> } | null\n}\n\nfunction compileRule(profileName: string, rule: ProfileRule): CompiledRule | null {\n let regex: RegExp\n try {\n regex = new RegExp(rule.pattern)\n } catch {\n debugLog('profiles', `Profile \"${profileName}\": invalid pattern \"${rule.pattern}\" — skipping rule`)\n return null\n }\n\n return {\n match(cmd: string) {\n const match = cmd.match(regex)\n if (!match) return null\n const metadata: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(rule.metadata ?? {})) {\n metadata[key] = value.replace(/\\$(\\d+)/g, (_, index: string) => match[Number(index)] ?? '')\n }\n return { category: rule.category, metadata }\n },\n }\n}\n\nfunction loadProfileFromDisk(name: string): Profile | null {\n const filePath = path.join(PROFILES_DIR, `${name}.json`)\n if (!fs.existsSync(filePath)) {\n debugLog('profiles', `Profile \"${name}\" not found in cache — run: workscribe profile add ${name}`)\n return null\n }\n let candidate: unknown\n try {\n candidate = JSON.parse(fs.readFileSync(filePath, 'utf-8'))\n } catch {\n debugLog('profiles', `Profile \"${name}\" contains invalid JSON — skipping`)\n return null\n }\n if (!isValidProfile(candidate)) {\n debugLog('profiles', `Profile \"${name}\" failed schema validation — skipping`)\n return null\n }\n return candidate\n}\n\nfunction loadUserProfiles(): Profile[] {\n if (!fs.existsSync(PROFILES_DIR)) return []\n const activeNames = new Set(readConfig().capture.profiles)\n return fs\n .readdirSync(PROFILES_DIR)\n .filter((file) => file.endsWith('.json') && file !== 'manifest.json')\n .filter((file) => {\n const name = file.replace(/\\.json$/, '')\n return !activeNames.has(name) // avoid double-loading names already in config\n })\n .flatMap((file) => {\n const candidate = loadProfileFromDisk(file.replace(/\\.json$/, ''))\n return candidate ? [candidate] : []\n })\n .filter((profile) => fs.existsSync(path.join(PROFILES_DIR, `${profile.name}.json`)))\n}\n\nexport function getActiveProfileRules(): CompiledRule[] {\n const activeNames = readConfig().capture.profiles\n if (activeNames.length === 0) return []\n\n return activeNames.flatMap((name) => {\n const profile = loadProfileFromDisk(name)\n if (!profile) return []\n return profile.rules.flatMap((rule) => {\n const compiled = compileRule(profile.name, rule)\n return compiled ? [compiled] : []\n })\n })\n}\n\nexport function getCachedProfiles(): Profile[] {\n if (!fs.existsSync(PROFILES_DIR)) return []\n return fs\n .readdirSync(PROFILES_DIR)\n .filter((file) => file.endsWith('.json') && file !== 'manifest.json')\n .flatMap((file) => {\n const profile = loadProfileFromDisk(file.replace(/\\.json$/, ''))\n return profile ? [profile] : []\n })\n}\n\nexport function detectProfilesForDirectory(dir: string): ManifestEntry[] {\n const manifest = readCachedManifest()\n if (!manifest) return []\n\n const activeNames = new Set(readConfig().capture.profiles)\n\n return manifest.filter((entry) => {\n if (activeNames.has(entry.name)) return false\n const indicatorFiles = entry.detect?.files ?? []\n return indicatorFiles.length > 0 && indicatorFiles.every((file) => fs.existsSync(path.join(dir, file)))\n })\n}\n\nexport { loadUserProfiles }\n","// Just enough shell syntax to find command boundaries in captured commands — quotes, escapes,\n// separators and substitutions — without a full parser. Pure and total: never throws.\n\ntype QuoteState = '\"' | \"'\" | null\n\ninterface QuotedCharacter {\n index: number\n quote: QuoteState\n}\n\n// `$(…)` and backticks run a command inside another, also inside double quotes. `<(…)` and `>(…)`\n// run one as a file, but only unquoted — inside double quotes they are plain text.\nconst COMMAND_SUBSTITUTION_OPENER = '$('\nconst PROCESS_SUBSTITUTION_OPENERS = ['<(', '>(']\n\n/**\n * Every character that is content rather than syntax, with the quote it sits in. Quote marks\n * themselves and backslash-escaped characters are skipped — they can't separate or substitute.\n */\nfunction* quotedCharacters(text: string): Generator<QuotedCharacter> {\n let quote: QuoteState = null\n for (let index = 0; index < text.length; index++) {\n const character = text[index]\n if (character === '\\\\' && quote !== \"'\") {\n index++\n continue\n }\n if (character === quote) {\n quote = null\n continue\n }\n if (quote === null && (character === '\"' || character === \"'\")) {\n quote = character\n continue\n }\n yield { index, quote }\n }\n}\n\nexport function isOutsideQuotes(text: string, position: number): boolean {\n for (const { index, quote } of quotedCharacters(text)) {\n if (index >= position) return quote === null\n }\n return true\n}\n\n/** A lone `&` runs the step before it in the background; `2>&1`, `&>` and `>&` are redirections, not separators. */\nfunction isRedirection(text: string, index: number): boolean {\n return text[index] === '&' && (text[index - 1] === '>' || text[index - 1] === '<' || text[index + 1] === '>')\n}\n\n/** Splits on any separator that sits outside single or double quotes. */\nexport function splitOutsideQuotes(text: string, separators: string[]): string[] {\n const parts: string[] = []\n let partStart = 0\n let skipUntil = 0\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote !== null || index < skipUntil || isRedirection(text, index)) continue\n const separator = separators.find((candidate) => text.startsWith(candidate, index))\n if (!separator) continue\n parts.push(text.slice(partStart, index))\n partStart = index + separator.length\n skipUntil = partStart\n }\n parts.push(text.slice(partStart))\n return parts.map((part) => part.trim()).filter((part) => part !== '')\n}\n\n/** Command or process substitution outside single quotes: the shell runs another command inside this one. */\nexport function containsCommandSubstitution(text: string): boolean {\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote === \"'\") continue\n if (text[index] === '`' || text.startsWith(COMMAND_SUBSTITUTION_OPENER, index)) return true\n if (quote === null && PROCESS_SUBSTITUTION_OPENERS.some((opener) => text.startsWith(opener, index))) return true\n }\n return false\n}\n\n// A `#` starts a comment only at the start of a word: `$#`, `${#x}` and `a#b` are not comments.\n// (bash and agents' shells treat it this way; interactive zsh only does with `interactivecomments` set.)\nconst WORD_BOUNDARY = /[\\s;&|()]/\n\n/** Where the first `# …` comment starts (outside quotes, at the start of a word), or -1. */\nexport function commentStart(text: string): number {\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote !== null || text[index] !== '#') continue\n if (index === 0 || WORD_BOUNDARY.test(text[index - 1])) return index\n }\n return -1\n}\n\n/**\n * Removes `# …` comments up to the end of each line. Quote tracking restarts after each comment, so an\n * apostrophe inside one (`# let's check`) can't leave a quote \"open\" for the rest of the command.\n */\nexport function stripComments(text: string): string {\n let result = ''\n let rest = text\n for (let start = commentStart(rest); start !== -1; start = commentStart(rest)) {\n result += rest.slice(0, start)\n const lineEnd = rest.indexOf('\\n', start)\n if (lineEnd === -1) return result\n rest = rest.slice(lineEnd)\n }\n return result + rest\n}\n\n/**\n * The text with everything inside quotes (and the quote marks and escapes themselves) replaced by `_`, keeping\n * every position — so a pattern can be matched against the unquoted parts only and applied to the original.\n */\nexport function maskQuotedText(text: string): string {\n const masked = Array.from({ length: text.length }, () => '_')\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote === null) masked[index] = text[index]\n }\n return masked.join('')\n}\n\nexport function startsWithWord(step: string, word: string): boolean {\n return step === word || step.startsWith(`${word} `) || step.startsWith(`${word}\\t`)\n}\n\n/** A shell word with its quotes and escapes removed, and where it starts in the text. */\nexport interface ShellWord {\n word: string\n start: number\n}\n\n/**\n * The text's words as the shell would pass them to a command: split on whitespace outside quotes, with quote marks\n * and escaping backslashes removed (`\"a b\"` → `a b`, `\\\"` → `\"`). `$(…)` and other expansions are kept as written.\n */\nexport function shellWords(text: string): ShellWord[] {\n const words: ShellWord[] = []\n let quote: QuoteState = null\n let current: ShellWord | null = null\n const append = (character: string, index: number) => {\n current ??= { word: '', start: index }\n current.word += character\n }\n for (let index = 0; index < text.length; index++) {\n const character = text[index]\n if (character === '\\\\' && quote !== \"'\") {\n current ??= { word: '', start: index }\n index++\n if (index < text.length) current.word += text[index]\n continue\n }\n if (quote !== null) {\n if (character === quote) quote = null\n else append(character, index)\n continue\n }\n if (character === '\"' || character === \"'\") {\n current ??= { word: '', start: index }\n quote = character\n continue\n }\n if (/\\s/.test(character)) {\n if (current) words.push(current)\n current = null\n continue\n }\n append(character, index)\n }\n if (current) words.push(current)\n return words\n}\n","// Breaks a captured shell command into the simple commands it runs, so each one can be classified or\n// filtered on its own — agents run things like `cd app && pnpm nx test shell 2>&1 | tail -40; npx tsc`.\n// Works on the raw command (heredocs and newlines are lost once whitespace is collapsed). Pure, never throws.\nimport {\n commentStart,\n containsCommandSubstitution,\n isOutsideQuotes,\n maskQuotedText,\n splitOutsideQuotes,\n startsWithWord,\n stripComments,\n} from './shell-syntax'\n\nconst STEP_SEPARATORS = ['&&', '||', ';', '\\n', '&']\nconst PIPE_SEPARATORS = ['|']\n/** Commands that only produce text. */\nexport const TEXT_SOURCES = ['echo', 'printf']\n// Steps that do no work of their own when they run nothing inside them\nconst GLUE_COMMANDS = ['cd', 'true', ':', ...TEXT_SOURCES]\n// `<<TAG`, `<<-TAG`, `<<'TAG'`, `<<\"TAG\"` — but not a `<<<` here-string\nconst HEREDOC_OPENER = /(?<!<)<<(?!<)-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1/g\n// Words that wrap the command after them: `do npm test`, `if npm test`, `! grep x`, `time make`\nconst KEYWORD_PREFIX = /^(?:do|then|else|elif|if|while|until|time|!)\\s+/\n// Loop and case headers — shell structure, unless they run a command (`for f in $(npm test)`)\nconst STRUCTURE_HEADER = /^(?:for|select|case)\\s/\n// Closing keywords and bare keywords left over once a step is split\nconst STRUCTURE_KEYWORD = /^(?:done|fi|esac|\\}|\\)|do|then|else)$/\n// `NAME=value` before a command (or on its own); the value may be quoted or a `$(…)` substitution\nconst LEADING_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=(?:\"[^\"]*\"|'[^']*'|\\$\\([^)]*\\)|\\S*)(?:\\s+|$)/\n// `2>&1`, `> file`, `2>/dev/null`, `&> log`, `>> file`, `< input`\nconst REDIRECTION = /(?:^|\\s)(?:\\d*>>?|&>>?|<)\\s*(?:&\\d+|[^\\s;|&]+)/g\n// Package-runner forms that mean \"run this tool\", rewritten to `npx …` so the npx rules recognise them\nconst TOOL_RUNNER = /^(?:(?:pnpm|yarn|npm)\\s+exec|pnpm\\s+dlx|bunx)\\s+/\nconst FIRST_SUBSTITUTION = /\\$\\(([^()]*)\\)/\n// `$(…)` and backtick substitutions, for the commands inside them\nconst SUBSTITUTIONS = /\\$\\(([^()]*)\\)|`([^`]*)`/g\n\n/** The commands a line's `$(…)`/backtick substitutions run — what the shell executes from heredoc text. */\nfunction substitutionCommands(line: string): string[] {\n return [...line.matchAll(SUBSTITUTIONS)]\n .map((match) => (match[1] ?? match[2] ?? '').trim())\n .filter((command) => command !== '')\n}\n\n/**\n * Removes heredoc bodies, keeping the line that opens each one, so their text can't look like commands.\n * The shell still runs `$(…)` inside an unquoted heredoc (`<<EOF`, not `<<'EOF'`), so the commands inside\n * those substitutions stay — each on its own line, without the surrounding text (whose apostrophes and\n * other characters would otherwise be read as shell syntax).\n */\nexport function stripHeredocBodies(command: string): string {\n const kept: string[] = []\n const pending: { terminator: string; runsSubstitutions: boolean }[] = []\n for (const line of command.split('\\n')) {\n if (pending.length > 0) {\n if (line.trim() === pending[0].terminator) pending.shift()\n else if (pending[0].runsSubstitutions && containsCommandSubstitution(line))\n kept.push(...substitutionCommands(line))\n continue\n }\n kept.push(line)\n const comment = commentStart(line)\n for (const match of line.matchAll(HEREDOC_OPENER)) {\n // `\"a<<b\"` is text inside quotes, `\\<<` an escaped `<` and `# see <<EOF` a comment — none opens a heredoc\n const isEscaped = line[match.index - 1] === '\\\\'\n const isInComment = comment !== -1 && match.index > comment\n if (isEscaped || isInComment || !isOutsideQuotes(line, match.index)) continue\n pending.push({ terminator: match[2], runsSubstitutions: match[1] === '' })\n }\n }\n return kept.join('\\n')\n}\n\n/** A heredoc in a command: its terminator, its body, and the line that opened it. */\nexport interface HeredocBody {\n terminator: string\n body: string\n openerLine: string\n}\n\n/**\n * Every heredoc in a command, in order — for text a command reads from its input (`git commit -F - <<EOF`'s\n * message). The same openers `stripHeredocBodies` recognises.\n */\nexport function heredocBodies(command: string): HeredocBody[] {\n const heredocs: HeredocBody[] = []\n const pending: { terminator: string; lines: string[]; openerLine: string }[] = []\n for (const line of command.split('\\n')) {\n if (pending.length > 0) {\n const [current] = pending\n if (line.trim() === current.terminator) {\n heredocs.push({\n terminator: current.terminator,\n body: current.lines.join('\\n'),\n openerLine: current.openerLine,\n })\n pending.shift()\n } else {\n current.lines.push(line)\n }\n continue\n }\n const comment = commentStart(line)\n for (const match of line.matchAll(HEREDOC_OPENER)) {\n const isEscaped = line[match.index - 1] === '\\\\'\n const isInComment = comment !== -1 && match.index > comment\n if (isEscaped || isInComment || !isOutsideQuotes(line, match.index)) continue\n pending.push({ terminator: match[2], lines: [], openerLine: line })\n }\n }\n return heredocs\n}\n\n/** The steps a shell runs: split on newlines, `&&`, `||`, `;` and a background `&`, outside quotes. */\nexport function splitSteps(command: string): string[] {\n return splitOutsideQuotes(command, STEP_SEPARATORS)\n}\n\nfunction countOf(text: string, character: string): number {\n return text.split(character).length - 1\n}\n\n/** `(cd app && npm test)` splits into `(cd app` and `npm test)` — drop the unbalanced group brackets. */\nfunction withoutGroupBrackets(step: string): string {\n let unwrapped = step\n while (unwrapped.startsWith('(') && countOf(unwrapped, '(') > countOf(unwrapped, ')')) {\n unwrapped = unwrapped.slice(1).trim()\n }\n while (unwrapped.endsWith(')') && countOf(unwrapped, ')') > countOf(unwrapped, '(')) {\n unwrapped = unwrapped.slice(0, -1).trim()\n }\n if (unwrapped.startsWith('{ ')) unwrapped = unwrapped.slice(2).trim()\n return unwrapped\n}\n\nfunction withoutKeywordPrefixes(step: string): string {\n let unwrapped = step\n while (KEYWORD_PREFIX.test(unwrapped)) unwrapped = unwrapped.replace(KEYWORD_PREFIX, '')\n return unwrapped\n}\n\nfunction withoutLeadingAssignments(step: string): string {\n let unwrapped = step\n while (LEADING_ASSIGNMENT.test(unwrapped)) unwrapped = unwrapped.replace(LEADING_ASSIGNMENT, '')\n return unwrapped\n}\n\n/** `d=node_modules/x` does nothing on its own; `f=$(npm test)` still runs `npm test`, so it stays. */\nfunction isPlainAssignment(step: string): boolean {\n return withoutLeadingAssignments(step) === '' && !containsCommandSubstitution(step)\n}\n\n/** Loop headers and closing keywords do no work — but a header that runs a command (`for f in $(ls)`) does. */\nfunction isShellStructure(step: string): boolean {\n if (STRUCTURE_KEYWORD.test(step)) return true\n return STRUCTURE_HEADER.test(step) && !containsCommandSubstitution(step)\n}\n\n/** Removes redirections (`2>&1`, `> out.txt`) outside quotes — a `>` inside a commit message is kept. */\nfunction withoutRedirections(command: string): string {\n const masked = maskQuotedText(command)\n let result = ''\n let copiedUpTo = 0\n for (const match of masked.matchAll(REDIRECTION)) {\n result += command.slice(copiedUpTo, match.index)\n copiedUpTo = match.index + match[0].length\n }\n return result + command.slice(copiedUpTo)\n}\n\n/**\n * The commands a captured shell line runs, in order: comments and heredoc bodies removed, split into steps,\n * group brackets and wrapping keywords (`do`, `then`, `if`…) removed, and loop headers, closing keywords and\n * plain assignments dropped.\n */\nexport function commandSteps(rawCommand: string): string[] {\n return splitSteps(stripComments(stripHeredocBodies(rawCommand)))\n .map((step) => withoutKeywordPrefixes(withoutGroupBrackets(step)))\n .filter((step) => step !== '' && !isShellStructure(step) && !isPlainAssignment(step))\n}\n\n/**\n * The command a step mainly runs, in a form the normalizer rules can match: leading assignments, everything\n * after the first `|`, and redirections removed; `pnpm exec tsc` read as `npx tsc`. For an assignment or a\n * loop header that runs a command (`f=$(find . -name x)`, `for f in $(npm test)`), the command inside.\n */\nexport function simpleCommandOf(step: string): string {\n const command = withoutLeadingAssignments(step)\n if (command === '' || STRUCTURE_HEADER.test(command)) return step.match(FIRST_SUBSTITUTION)?.[1]?.trim() ?? ''\n const [firstStage = ''] = splitOutsideQuotes(command, PIPE_SEPARATORS)\n return withoutRedirections(firstStage).replace(/\\s+/g, ' ').trim().replace(TOOL_RUNNER, 'npx ')\n}\n\n/**\n * Shell glue that does no work of its own — `cd`, `true`, `:`, or printing text — as long as it runs\n * nothing inside it and pipes nothing. Agents wrap commands in these (`… || true`, `; echo \"exit=$?\"`).\n */\nexport function isGlueStep(step: string): boolean {\n if (containsCommandSubstitution(step) || splitOutsideQuotes(step, PIPE_SEPARATORS).length > 1) return false\n return GLUE_COMMANDS.some((command) => startsWithWord(step, command))\n}\n","// Categories excluded from summary input by default:\n// - exploring/note: handled separately (notes rendered before event list; exploring is raw file reads)\n// - linting: lint/format runs are support noise, not meaningful work outcomes\n// `other` is intentionally kept — hints give the summary context for commands that don't fit known patterns\n// (e.g. docker compose up, make deploy, terraform apply).\n// Only a type import, so the template engine and the --json contract can use this without loading profiles or the DB.\nimport type { SessionPayload } from '../ai/types'\n\nexport const DEFAULT_EXCLUDED_CATEGORIES = new Set(['exploring', 'note', 'linting'])\n\nexport function buildExcludedSet(includeCategories: string[] = []): Set<string> {\n const excluded = new Set(DEFAULT_EXCLUDED_CATEGORIES)\n for (const category of includeCategories) excluded.delete(category)\n return excluded\n}\n\n/** How many events of each excluded category the summary left out — for the footer under `workscribe summary`. */\nexport function countExcludedEvents(sessions: SessionPayload[], excluded: Set<string>): Map<string, number> {\n const counts = new Map<string, number>()\n for (const session of sessions) {\n for (const event of session.events) {\n if (excluded.has(event.category)) counts.set(event.category, (counts.get(event.category) ?? 0) + 1)\n }\n }\n return counts\n}\n","// Classifies compound shell commands — `cd app && pnpm nx test shell 2>&1 | tail -40; npx tsc` — by their most\n// telling step. Pure: the rule matcher is passed in, so this module doesn't load the rules (or the profiles).\nimport { commandSteps, isGlueStep, simpleCommandOf } from '../capture/command-steps'\nimport { DEFAULT_EXCLUDED_CATEGORIES } from '../summary/exclusions'\nimport type { NormalizedEvent } from './patterns'\n\n/** Matches a step: `command` is its simple form (one line, redirections gone), `raw` the step as written. */\nexport type MatchCommand = (command: string, raw: string) => NormalizedEvent\n\nexport const UNCLASSIFIED: NormalizedEvent = { category: 'other', metadata: {} }\n\n// When a compound command runs several things, the event takes the step that says most about the work:\n// outcomes first, then routine work. `git status` (versioning) must never hide `npm test` next to it.\nexport const STEP_CATEGORY_PRIORITY = [\n 'testing',\n 'building',\n 'releasing',\n 'shipping',\n 'dependencies',\n 'database',\n 'infrastructure',\n 'running',\n 'files',\n 'versioning',\n]\n\n// Ranks after the priority list, lower is more telling: any other category, then ones summaries leave out, then `other`\nconst OTHER_CATEGORY_RANK = STEP_CATEGORY_PRIORITY.length\nconst EXCLUDED_CATEGORY_RANK = OTHER_CATEGORY_RANK + 1\nconst UNCLASSIFIED_RANK = EXCLUDED_CATEGORY_RANK + 1\n\nfunction rankOf(category: string): number {\n const index = STEP_CATEGORY_PRIORITY.indexOf(category)\n if (index !== -1) return index\n if (category === UNCLASSIFIED.category) return UNCLASSIFIED_RANK\n if (DEFAULT_EXCLUDED_CATEGORIES.has(category)) return EXCLUDED_CATEGORY_RANK\n return OTHER_CATEGORY_RANK\n}\n\n/** The most telling step's event, and that step; ties go to the earlier step. */\nfunction primaryStepEvent(steps: string[], matchCommand: MatchCommand): NormalizedEvent {\n const events = steps.map((step) => ({ ...matchCommand(simpleCommandOf(step), step), matchedStep: step }))\n return events.reduce<NormalizedEvent>(\n (best, event) => (rankOf(event.category) < rankOf(best.category) ? event : best),\n UNCLASSIFIED\n )\n}\n\n/**\n * A command the rules already recognise as it is, with nothing else to run, keeps that result. Otherwise it's\n * split into the commands it runs, shell glue (`cd`, `echo`, assignments, loop keywords) is set aside, and the\n * most telling step names the event — falling back to the whole command when no step is recognised either.\n */\nexport function classifyCompound(command: string, matchCommand: MatchCommand): NormalizedEvent {\n const workSteps = commandSteps(command).filter((step) => !isGlueStep(step))\n // Matched as a whole (so heredoc text inside it is still there), but it's the one work step that ran\n const wholeCommandEvent = {\n ...matchCommand(command, command),\n matchedStep: workSteps.length === 1 ? workSteps[0] : command,\n }\n const isRecognisedSingleCommand = workSteps.length <= 1 && wholeCommandEvent.category !== UNCLASSIFIED.category\n if (isRecognisedSingleCommand || workSteps.length === 0) return wholeCommandEvent\n\n const primaryEvent = primaryStepEvent(workSteps, matchCommand)\n return primaryEvent.category === UNCLASSIFIED.category ? wholeCommandEvent : primaryEvent\n}\n","// Reads `git [global options] <subcommand> …` — git's own options (`-C <dir>`, `-c k=v`, `--work-tree=…`) apart from\n// the subcommand — so the rules see the subcommand. Pure: no file system or git calls.\nimport { type ShellWord, shellWords } from '../capture/shell-syntax'\n\nexport interface GitInvocation {\n /** `-C` directories, in order — each relative to the previous one */\n directories: string[]\n workTree: string | null\n gitDir: string | null\n /** The subcommand (`commit`, `push`, …), or null for a bare `git` or one with only options */\n subcommand: string | null\n /** The words after the subcommand */\n args: ShellWord[]\n /** Where the subcommand starts in the text, so the original (quotes and all) can be kept from there */\n subcommandStart: number\n}\n\n// Global options that take the next word as their value\nconst OPTIONS_WITH_VALUE = new Set([\n '-C',\n '-c',\n '--git-dir',\n '--work-tree',\n '--namespace',\n '--exec-path',\n '--config-env',\n])\n\n/** Reads `git [global options] <subcommand> …`, or null if the text isn't a git command. */\nexport function parseGitInvocation(command: string): GitInvocation | null {\n const words = shellWords(command.trim())\n if (words[0]?.word !== 'git') return null\n\n const invocation: GitInvocation = {\n directories: [],\n workTree: null,\n gitDir: null,\n subcommand: null,\n args: [],\n subcommandStart: -1,\n }\n for (let index = 1; index < words.length; index++) {\n const { word } = words[index]\n if (!word.startsWith('-')) {\n invocation.subcommand = word\n invocation.subcommandStart = words[index].start\n invocation.args = words.slice(index + 1)\n return invocation\n }\n const [name, inlineValue] = word.startsWith('--') ? splitLongOption(word) : [word, undefined]\n const takesValue = OPTIONS_WITH_VALUE.has(name)\n const value = inlineValue ?? (takesValue ? words[++index]?.word : undefined)\n if (name === '-C' && value !== undefined) invocation.directories.push(value)\n if (name === '--work-tree' && value !== undefined) invocation.workTree = value\n if (name === '--git-dir' && value !== undefined) invocation.gitDir = value\n }\n return invocation\n}\n\nfunction splitLongOption(word: string): [string, string | undefined] {\n const equals = word.indexOf('=')\n return equals === -1 ? [word, undefined] : [word.slice(0, equals), word.slice(equals + 1)]\n}\n\n/**\n * `git -C \"/a b\" -c x=y commit -m \"…\"` as `git commit -m \"…\"`: the global options dropped, the rest kept as typed —\n * so every git rule matches on the subcommand. Anything that isn't a git command comes back unchanged.\n */\nexport function withoutGitGlobalOptions(command: string): string {\n const invocation = parseGitInvocation(command)\n if (!invocation || invocation.subcommandStart === -1) return command\n const trimmed = command.trim()\n return `git ${trimmed.slice(invocation.subcommandStart)}`\n}\n","// A `git commit`'s message, from however it was given: `-m`, `-am`, `--message=`, `-F <file>`, `-F -` with a heredoc,\n// or the `-m \"$(cat <<'EOF' … EOF)\"` form coding agents use. Pure: a message file is only named, for capture to read.\nimport { heredocBodies } from '../capture/command-steps'\nimport { isOutsideQuotes } from '../capture/shell-syntax'\nimport { parseGitInvocation } from './git-invocation'\n\n// The first line of `$(cat <<'EOF'` — anchored to one line, so there's nothing for the engine to backtrack over\nconst SUBSTITUTION_OPENER = /^\\$\\(\\s*cat\\s*<<-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1\\s*$/\n// A heredoc opener (`<<TAG`, `<<-'TAG'`), not a `<<<` here-string\nconst HEREDOC_OPENER = /(?<!<)<<(?!<)-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1/g\n\n/** What a `git commit` says about its message: the subject line, or the file it reads it from. */\nexport interface CommitMessage {\n subject: string | null\n /** `-F <file>` — read by capture, which can check the file safely; never stored */\n messageFile?: string\n}\n\n/** The first non-empty line — what `git log --format=%s` calls the subject. */\nfunction firstLineOf(text: string | null | undefined): string | null {\n const line = text\n ?.split('\\n')\n .map((part) => part.trim())\n .find((part) => part !== '')\n return line || null\n}\n\n/** A `$(cat <<'TAG' … TAG)` value's body, found line by line — or null if the value isn't one. */\nfunction substitutionHeredocBody(value: string): string | null {\n const lines = value.split('\\n')\n const terminator = lines[0]?.match(SUBSTITUTION_OPENER)?.[2]\n if (!terminator) return null\n const end = lines.findIndex((line, index) => index > 0 && line.trim() === terminator)\n return end === -1 ? null : lines.slice(1, end).join('\\n')\n}\n\nfunction messageFromValue(value: string | undefined): CommitMessage {\n if (value === undefined) return { subject: null }\n return { subject: firstLineOf(substitutionHeredocBody(value) ?? value) }\n}\n\n/** The heredoc tag a `-F -` step reads from — quoted or not — if its opener sits outside quotes. */\nfunction inputHeredocTag(step: string): string | null {\n for (const match of step.matchAll(HEREDOC_OPENER)) {\n if (isOutsideQuotes(step, match.index)) return match[2]\n }\n return null\n}\n\nfunction messageFromFile(file: string | undefined, step: string, original: string): CommitMessage {\n if (file === undefined) return { subject: null }\n if (file !== '-') return { subject: null, messageFile: file }\n // `-F -` reads standard input: a heredoc whose body is gone from the step, so it's taken from the whole command —\n // the one opened on this step's line, in case an earlier heredoc used the same terminator\n const tag = inputHeredocTag(step)\n if (!tag) return { subject: null }\n const stepLine = step.split('\\n')[0].trim()\n const heredocs = heredocBodies(original).filter(({ terminator }) => terminator === tag)\n const heredoc = heredocs.find(({ openerLine }) => openerLine.includes(stepLine)) ?? heredocs[0]\n return { subject: firstLineOf(heredoc?.body) }\n}\n\n/**\n * A `git commit` step's message: `-m`, `-m<msg>`, `--message[=]`, short flags ending in `m` (`-am`), `-F`/`--file`\n * (a file, or `-` for a heredoc). The first message given is the subject. Null if the step isn't a commit.\n * `original` is the whole captured command, where heredoc bodies for `-F -` still are.\n */\nexport function commitMessageOf(step: string, original: string): CommitMessage | null {\n const invocation = parseGitInvocation(step)\n if (invocation?.subcommand !== 'commit') return null\n const args = invocation.args.map(({ word }) => word)\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]\n const next = args[index + 1]\n if (arg === '--') break\n if (arg === '-m' || arg === '--message') return messageFromValue(next)\n if (arg.startsWith('--message=')) return messageFromValue(arg.slice('--message='.length))\n if (arg === '-F' || arg === '--file') return messageFromFile(next, step, original)\n if (arg.startsWith('--file=')) return messageFromFile(arg.slice('--file='.length), step, original)\n if (!/^-[A-Za-z]/.test(arg) || arg.startsWith('--')) continue\n // Short options: `-m<msg>` / `-F<file>` carry their value; a cluster like `-am` or `-sF` takes the next word\n if (arg.startsWith('-m') && arg.length > 2) return messageFromValue(arg.slice(2))\n if (arg.startsWith('-F') && arg.length > 2) return messageFromFile(arg.slice(2), step, original)\n if (arg.endsWith('m')) return messageFromValue(next)\n if (arg.endsWith('F')) return messageFromFile(next, step, original)\n }\n return { subject: null }\n}\n","// What a package-manager command installs or removes, and for which workspace — read word by word, so flags and\n// their values (`--filter web`, `--registry <url>`) are never taken for package names. Pure.\n\n// Package-manager flags whose next word is their value, not a package.\n// (`-w` isn't here: npm's takes a value but pnpm's — workspace root — doesn't.)\nconst FLAGS_WITH_VALUE = new Set([\n '--filter',\n '-F',\n '--registry',\n '--workspace',\n '--dir',\n '-C',\n '--prefix',\n '--tag',\n '--cwd',\n])\n\nexport const INSTALL_WORDS = ['install', 'i', 'add']\nexport const REMOVE_WORDS = ['remove', 'rm', 'uninstall', 'un']\n\n/**\n * The package names after a command word — `npm install foo bar` → foo, bar. Flags are skipped, and so is the value\n * of a flag that takes one (`pnpm add --filter web zod` → zod).\n */\nexport function extractPackages(command: string, commandWords: string[]): string[] {\n const words = command.trim().split(/\\s+/)\n const start = words.findIndex((word, index) => index > 0 && commandWords.includes(word))\n if (start === -1) return []\n const packages: string[] = []\n for (let index = start + 1; index < words.length; index++) {\n const word = words[index]\n if (FLAGS_WITH_VALUE.has(word)) index++\n else if (word && !word.startsWith('-')) packages.push(word)\n }\n return packages\n}\n\n/** The workspace a pnpm `--filter` / `-F` command targets, if any. */\nfunction workspaceOf(command: string): string | null {\n const words = command.trim().split(/\\s+/)\n const filter = words.findIndex((word) => word === '--filter' || word === '-F')\n if (filter !== -1) return words[filter + 1] ?? null\n return words.find((word) => word.startsWith('--filter='))?.slice('--filter='.length) ?? null\n}\n\n/** Dependency metadata, with `action: 'remove'` for removals and the workspace when the command targets one. */\nexport function dependencyMetadata(\n tool: string,\n command: string,\n commandWords: string[],\n action?: 'remove'\n): Record<string, unknown> {\n const workspace = workspaceOf(command)\n return {\n tool,\n packages: extractPackages(command, commandWords),\n ...(action ? { action } : {}),\n ...(workspace ? { workspace } : {}),\n }\n}\n","import { loadAIToolRegistry } from '../config/registry'\nimport { getActiveProfileRules } from '../profiles/loader'\nimport { debugLog } from '../utils/logger'\nimport { classifyCompound, UNCLASSIFIED } from './compound'\nimport { commitMessageOf } from './commit-message'\nimport { dependencyMetadata, extractPackages, INSTALL_WORDS, REMOVE_WORDS } from './dependency-metadata'\nimport { withoutGitGlobalOptions } from './git-invocation'\n\nexport interface NormalizedEvent {\n category: string\n metadata: Record<string, unknown>\n /** The step that set the category, as written (or the whole command) — for capture, never stored */\n matchedStep?: string\n}\n\n/** What a rule can read besides the one-line command it matched: the step as written, and the whole command. */\ninterface RuleContext {\n /** The step with its line breaks and quoting — `-m \"$(cat <<'EOF' …)\"` survives here */\n raw: string\n /** The whole captured command, where heredoc bodies (`-F - <<EOF`) still are */\n original: string\n}\n\ninterface Rule {\n match(cmd: string, context: RuleContext): NormalizedEvent | null\n}\n\nfunction makeRule(\n pattern: RegExp,\n category: string,\n extract: (match: RegExpMatchArray, cmd: string, context: RuleContext) => Record<string, unknown> = () => ({})\n): Rule {\n return {\n match(cmd, context) {\n const m = cmd.match(pattern)\n if (!m) return null\n return { category, metadata: extract(m, cmd, context) }\n },\n }\n}\n\n/** A package-manager rule: `tool`'s packages after one of `commandWords`, with `action: 'remove'` for removals. */\nfunction dependencyRule(pattern: RegExp, tool: string, commandWords: string[], action?: 'remove'): Rule {\n return makeRule(pattern, 'dependencies', (_match, command) => dependencyMetadata(tool, command, commandWords, action))\n}\n\n// Nx targets → categories: `nx build app`, `nx run app:test`, `nx run-many -t lint`, `nx affected --target=e2e`\nconst NX_TARGET_CATEGORIES: Record<string, string> = {\n build: 'building',\n test: 'testing',\n e2e: 'testing',\n lint: 'linting',\n serve: 'running',\n dev: 'running',\n}\nconst NX_COMMAND =\n /^(?:(?:npx|pnpm|yarn|bunx)\\s+)?nx\\s+(?:(?:run-many|affected)\\b.*?(?:-t|--targets?)[=\\s]+([\\w-]+)|run\\s+[\\w@./-]+:([\\w-]+)|([\\w-]+)\\b)/\n\nconst nxRule: Rule = {\n match(cmd) {\n const match = cmd.match(NX_COMMAND)\n const target = match?.[1] ?? match?.[2] ?? match?.[3]\n const category = target ? NX_TARGET_CATEGORIES[target] : undefined\n return category ? { category, metadata: { tool: 'nx', target } } : null\n },\n}\n\nconst BASE_RULES: Rule[] = [\n // ─── Git operations ───────────────────────────────────────────────────────\n makeRule(/^git\\s+commit\\b/, 'shipping', (_m, _cmd, { raw, original }) => {\n // Read from the step as written, so quoting and multi-line heredoc messages survive (see git-invocation.ts)\n const commitMessage = commitMessageOf(raw, original)\n const message = commitMessage?.subject ?? null\n return commitMessage?.messageFile ? { message, messageFile: commitMessage.messageFile } : { message }\n }),\n\n makeRule(/^git\\s+push\\b/, 'shipping', (_m, cmd) => {\n // Flags (-u, --force, …) aren't the remote or branch: `git push -u origin main` → origin/main\n const positional = cmd\n .split(/\\s+/)\n .slice(2)\n .filter((part) => !part.startsWith('-'))\n return { remote: positional[0] ?? 'origin', branch: positional[1] ?? null }\n }),\n\n makeRule(/^git\\s+(pull|fetch)\\b/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], remote: parts[2] ?? 'origin' }\n }),\n\n makeRule(/^git\\s+(checkout|switch)\\b/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n const branch = parts.find((part) => !part.startsWith('-') && part !== 'git' && part !== parts[1])\n return { branch: branch ?? null }\n }),\n\n makeRule(/^git\\s+(merge|rebase)\\b/, 'shipping', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], target: parts[2] ?? null }\n }),\n\n makeRule(/^git\\s+stash\\b/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[2] ?? 'push' }\n }),\n\n makeRule(/^git\\s+/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1] ?? null }\n }),\n\n // ─── Releasing ────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+publish\\b/, 'releasing', () => ({ tool: 'npm' })),\n makeRule(/^cargo\\s+publish\\b/, 'releasing', () => ({ tool: 'cargo' })),\n makeRule(/^gem\\s+push\\b/, 'releasing', () => ({ tool: 'gem' })),\n makeRule(/^docker\\s+push\\b/, 'releasing', (_m, cmd) => ({ tool: 'docker', image: cmd.split(/\\s+/)[2] ?? null })),\n makeRule(/^gh\\s+release\\b/, 'releasing', (_m, cmd) => ({ tool: 'gh', subcommand: cmd.split(/\\s+/)[2] ?? null })),\n makeRule(/^fly\\s+(deploy|launch)\\b/, 'releasing', (match) => ({ tool: 'fly', subcommand: match[1] })),\n makeRule(/^vercel\\s+(deploy|build|--prod)\\b/, 'releasing', () => ({ tool: 'vercel' })),\n makeRule(/^netlify\\s+deploy\\b/, 'releasing', () => ({ tool: 'netlify' })),\n makeRule(/^railway\\s+up\\b/, 'releasing', () => ({ tool: 'railway' })),\n makeRule(/^wrangler\\s+deploy\\b/, 'releasing', () => ({ tool: 'wrangler' })),\n\n // ─── Dependencies ─────────────────────────────────────────────────────────\n dependencyRule(/^npm\\s+(install|i|add)\\b/, 'npm', INSTALL_WORDS),\n\n // `yarn workspace web add zod` — the workspace is the word after `workspace`\n makeRule(/^yarn\\s+workspace\\s+(\\S+)\\s+(add|remove)\\b/, 'dependencies', (match, command) => ({\n ...dependencyMetadata('yarn', command, [match[2]], match[2] === 'remove' ? 'remove' : undefined),\n workspace: match[1],\n })),\n\n // `pnpm --filter web add zod`, `pnpm -F web add zod`, `pnpm -r add zod` — options before the subcommand\n makeRule(\n /^pnpm\\s+(?:(?:--filter|-F)\\s+\\S+|--filter=\\S+|-r|--recursive)\\s+(add|install|i|remove|rm|uninstall|un)\\b/,\n 'dependencies',\n (match, command) =>\n REMOVE_WORDS.includes(match[1])\n ? dependencyMetadata('pnpm', command, REMOVE_WORDS, 'remove')\n : dependencyMetadata('pnpm', command, INSTALL_WORDS)\n ),\n\n // `yarn install` takes no packages; `pnpm install <pkg>` / `pnpm i <pkg>` adds them, like `add`\n dependencyRule(/^yarn\\s+(install|add)\\b/, 'yarn', ['add']),\n dependencyRule(/^pnpm\\s+(install|i|add)\\b/, 'pnpm', INSTALL_WORDS),\n dependencyRule(/^bun\\s+(install|i|add)\\b/, 'bun', INSTALL_WORDS),\n\n makeRule(/^pip\\s+(install)\\b/, 'dependencies', (_match, command) => ({\n tool: 'pip',\n packages: extractPackages(command, ['install']),\n })),\n\n makeRule(/^cargo\\s+add\\b/, 'dependencies', (_match, command) => ({\n tool: 'cargo',\n packages: extractPackages(command, ['add']),\n })),\n\n makeRule(/^(brew)\\s+install\\b/, 'dependencies', (_match, command) => ({\n tool: 'brew',\n packages: extractPackages(command, ['install']),\n })),\n\n makeRule(/^go\\s+get\\b/, 'dependencies', (_match, command) => ({\n tool: 'go',\n packages: extractPackages(command, ['get']),\n })),\n\n dependencyRule(/^npm\\s+(uninstall|remove|rm|un)\\b/, 'npm', REMOVE_WORDS, 'remove'),\n\n makeRule(/^(yarn|pnpm)\\s+(remove|rm|uninstall|un)\\b/, 'dependencies', (match, command) =>\n dependencyMetadata(match[1], command, REMOVE_WORDS, 'remove')\n ),\n\n dependencyRule(/^bun\\s+(remove|rm)\\b/, 'bun', REMOVE_WORDS, 'remove'),\n\n makeRule(/^pip\\s+uninstall\\b/, 'dependencies', (_match, command) => ({\n tool: 'pip',\n packages: extractPackages(command, ['uninstall']),\n action: 'remove',\n })),\n\n // Runtime version managers\n makeRule(/^nvm\\b/, 'dependencies', (_m, cmd) => ({ tool: 'nvm', subcommand: cmd.split(/\\s+/)[1] ?? null })),\n makeRule(/^(fnm|volta)\\b/, 'dependencies', (m, cmd) => ({ tool: m[1], subcommand: cmd.split(/\\s+/)[1] ?? null })),\n makeRule(/^(rbenv|pyenv|asdf)\\b/, 'dependencies', (m, cmd) => ({\n tool: m[1],\n subcommand: cmd.split(/\\s+/)[1] ?? null,\n })),\n\n // ─── Nx (before the package-manager rules, which would read `pnpm nx` as `pnpm`) ─\n nxRule,\n\n // ─── Tests ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+(test|run\\s+test)\\b/, 'testing', () => ({ tool: 'npm' })),\n makeRule(/^yarn\\s+test\\b/, 'testing', () => ({ tool: 'yarn' })),\n makeRule(/^pnpm\\s+test\\b/, 'testing', () => ({ tool: 'pnpm' })),\n makeRule(/^bun\\s+test\\b/, 'testing', () => ({ tool: 'bun' })),\n makeRule(/^(jest|vitest)\\b/, 'testing', (match) => ({ tool: match[1] })),\n makeRule(/^pytest\\b/, 'testing', () => ({ tool: 'pytest' })),\n makeRule(/^go\\s+test\\b/, 'testing', () => ({ tool: 'go' })),\n makeRule(/^cargo\\s+test\\b/, 'testing', () => ({ tool: 'cargo' })),\n makeRule(/^rspec\\b/, 'testing', () => ({ tool: 'rspec' })),\n makeRule(/^(php\\s+)?phpunit\\b/, 'testing', () => ({ tool: 'phpunit' })),\n\n // ─── Build ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+build\\b/, 'building', () => ({ tool: 'npm' })),\n makeRule(/^(yarn|pnpm)\\s+build\\b/, 'building', (match) => ({ tool: match[1] })),\n makeRule(/^bun\\s+run\\s+build\\b/, 'building', () => ({ tool: 'bun' })),\n makeRule(/^(make|gmake)\\b/, 'building', (match) => ({ tool: match[1] })),\n makeRule(/^cargo\\s+build\\b/, 'building', () => ({ tool: 'cargo' })),\n makeRule(/^go\\s+build\\b/, 'building', () => ({ tool: 'go' })),\n makeRule(/^docker\\s+build\\b/, 'building', () => ({ tool: 'docker' })),\n makeRule(/^(mvn|gradle)\\b/, 'building', (match) => ({ tool: match[1] })),\n\n // ─── Dev server / running ─────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+(dev|start|serve)\\b/, 'running', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^(yarn|pnpm)\\s+(dev|start)\\b/, 'running', (match) => ({ tool: match[1], script: match[2] })),\n makeRule(/^bun\\s+run\\s+(dev|start|serve)\\b/, 'running', (_m, cmd) => ({\n tool: 'bun',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^python\\s+.*manage\\.py\\s+runserver\\b/, 'running', () => ({ tool: 'django' })),\n\n // ─── Infrastructure ───────────────────────────────────────────────────────\n makeRule(/^ssh\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'ssh', target: parts[parts.length - 1] }\n }),\n\n makeRule(/^kubectl\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'kubectl', subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^(helm|terraform|pulumi)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^docker\\s+(run|compose|ps|stop|start|rm|exec)\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'docker', subcommand: parts[1] }\n }),\n\n makeRule(/^(aws|gcloud|az)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n // ─── Database ─────────────────────────────────────────────────────────────\n makeRule(/^(psql|pgcli)\\b/, 'database', (m) => ({ tool: m[1] })),\n makeRule(/^(mysql|mycli)\\b/, 'database', (m) => ({ tool: m[1] })),\n makeRule(/^sqlite3\\b/, 'database', () => ({ tool: 'sqlite3' })),\n makeRule(/^(mongosh|mongo)\\b/, 'database', (m) => ({ tool: m[1] })),\n makeRule(/^redis-cli\\b/, 'database', () => ({ tool: 'redis-cli' })),\n\n // ─── npx ─────────────────────────────────────────────────────────────────\n makeRule(/^npx\\s+(jest|vitest|mocha|ava|tap)\\b/, 'testing', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n makeRule(/^(?:npx\\s+)?tsc\\b/, 'building', () => ({ tool: 'tsc' })),\n makeRule(/^npx\\s+(eslint|tslint|biome)\\b/, 'linting', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n makeRule(/^npx\\s+(prettier|dprint)\\b/, 'linting', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n makeRule(/^npx\\s+\\S+/, 'running', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n\n // ─── Scripts ──────────────────────────────────────────────────────────────\n makeRule(/^(bash|sh|zsh|fish)\\s+\\S+/, 'running', (match) => ({ shell: match[1] })),\n makeRule(/^node\\s+\\S+\\.(?:js|mjs|cjs)\\b/, 'running', () => ({ runtime: 'node' })),\n makeRule(/^python3?\\s+\\S+\\.py\\b/, 'running', () => ({ runtime: 'python' })),\n\n // ─── Linting / code quality ───────────────────────────────────────────────\n makeRule(/^(eslint|tslint|biome)\\b/, 'linting', (match) => ({ tool: match[1] })),\n makeRule(/^(prettier|dprint)\\b/, 'linting', (match) => ({ tool: match[1] })),\n\n // ─── generic run scripts ──────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+\\S+/, 'running', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n\n makeRule(/^bun\\s+run\\s+\\S+/, 'running', (_m, cmd) => ({\n tool: 'bun',\n script: cmd.split(/\\s+/)[2],\n })),\n\n // ─── Code search ─────────────────────────────────────────────────────────\n makeRule(/^(grep|egrep|fgrep)\\b/, 'exploring', (match) => ({ tool: match[1] })),\n makeRule(/^(rg|ag|ack|ripgrep)\\b/, 'exploring', (match) => ({ tool: match[1] })),\n makeRule(/^find\\s/, 'exploring', () => ({ tool: 'find' })),\n\n // ─── Code read ────────────────────────────────────────────────────────────\n makeRule(/^(cat|bat|less|more)\\s+\\S+/, 'exploring', (match) => ({ tool: match[1] })),\n makeRule(/^(head|tail)\\b/, 'exploring', (match) => ({ tool: match[1] })),\n\n // ─── File operations ──────────────────────────────────────────────────────\n makeRule(/^(cp|rsync)\\b/, 'files', (match) => ({ tool: match[1] })),\n makeRule(/^(mv)\\b/, 'files', () => ({ tool: 'mv' })),\n makeRule(/^(rm|rmdir)\\b/, 'files', (match) => ({ tool: match[1] })),\n makeRule(/^(mkdir|touch|ln)\\b/, 'files', (match) => ({ tool: match[1] })),\n makeRule(/^(chmod|chown|chgrp)\\b/, 'files', (match) => ({ tool: match[1] })),\n\n // ─── Environment ──────────────────────────────────────────────────────────\n makeRule(/^export\\s+\\w+=/, 'files', (_m, cmd) => {\n const varName = cmd.match(/^export\\s+(\\w+)=/)?.[1] ?? null\n return { variable: varName }\n }),\n makeRule(/^source\\s+/, 'files', () => ({})),\n]\n\nfunction aiToolRules(): Rule[] {\n return loadAIToolRegistry().flatMap((entry) => {\n try {\n return [makeRule(new RegExp(entry.pattern), 'ai_session', () => ({ tool: entry.tool }))]\n } catch {\n debugLog('registry', `Skipping tool \"${entry.tool}\" — invalid pattern: ${entry.pattern}`)\n return []\n }\n })\n}\n\nexport function buildRules(profileRules: Array<{ match(cmd: string): NormalizedEvent | null }> = []): Rule[] {\n return [...BASE_RULES, ...profileRules, ...aiToolRules()]\n}\n\n// Built on first use, not at import: the active profiles come from the config and profile files, and\n// importing this module (via capture, or the database's migrations) shouldn't read either\nlet activeRules: Rule[] | null = null\n\nfunction getRules(): Rule[] {\n activeRules ??= buildRules(getActiveProfileRules())\n return activeRules\n}\n\nfunction matchRules(command: string, context: RuleContext): NormalizedEvent {\n // git's own options (`-C <dir>`, `-c k=v`, `--work-tree=…`) are dropped, so the rules see `git <subcommand> …`\n const flattened = withoutGitGlobalOptions(command.trim()).replace(/\\s+/g, ' ')\n for (const rule of getRules()) {\n const result = rule.match(flattened, context)\n if (result) return result\n }\n return UNCLASSIFIED\n}\n\n/**\n * Classifies a captured command — compound ones by their most telling step (see `./compound`). `matchedStep` says\n * which step that was, as written, so capture can tell which repo a `git -C …` step worked in.\n */\nexport function normalizeCommand(command: string): NormalizedEvent {\n return classifyCompound(command, (simple, raw) => matchRules(simple, { raw, original: command }))\n}\n","import type { NormalizedEvent } from './patterns'\n\nexport interface ReclassifyOptions {\n classify: (command: string) => NormalizedEvent\n /** `capture.aiTools` — when it's off, Workscribe doesn't record AI sessions, so none are created here either. */\n aiToolsEnabled: boolean\n}\n\n/**\n * The new category and metadata for a stored `other` event, or null to leave it alone. The metadata\n * replaces the old one entirely, so the `hint`/`tool` kept for unclassified commands doesn't linger.\n */\nexport function reclassifiedEvent(\n command: string,\n { classify, aiToolsEnabled }: ReclassifyOptions\n): NormalizedEvent | null {\n const { category, metadata } = classify(command)\n if (category === 'other') return null\n if (category === 'ai_session' && !aiToolsEnabled) return null\n // A `-F <file>` path is only for capture to read at the time; a stored event never keeps it\n const { messageFile: _messageFile, ...storedMetadata } = metadata\n return { category, metadata: storedMetadata }\n}\n","// Reclassifies stored `other` events with the classifier as it is when this runs — so on an install that first\n// opens its database with a later release, later rules and the user's active profiles apply too. That's fine:\n// it only ever improves `other` rows, never touches any other row or a stored command, and skips what it can't classify.\nimport { normalizeCommand } from '../../normalize/patterns'\nimport { reclassifiedEvent } from '../../normalize/reclassify'\nimport { readConfig } from '../../config'\nimport { debugLog } from '../../utils/logger'\nimport type { Migration, MigrationDatabase } from '../migration-types'\n\ninterface UnclassifiedRow {\n id: number\n command: string\n}\n\nfunction classifyOrSkip(row: UnclassifiedRow, aiToolsEnabled: boolean): ReturnType<typeof reclassifiedEvent> {\n try {\n return reclassifiedEvent(row.command, { classify: normalizeCommand, aiToolsEnabled })\n } catch (error) {\n // Best-effort: this runs the first time any command opens the database after upgrading (even the\n // background capture), so one odd command must never fail the migration and lock the database out\n debugLog('migration-006', error)\n return null\n }\n}\n\nconst migration: Migration = {\n version: 6,\n description: 'Reclassify unclassified events now that compound commands are classified by their main step',\n sql: '',\n run(db: MigrationDatabase) {\n const aiToolsEnabled = readConfig().capture.aiTools !== false\n const rows = db.prepare(\"SELECT id, command FROM events WHERE category = 'other'\").all() as UnclassifiedRow[]\n const update = db.prepare('UPDATE events SET category = ?, metadata = ? WHERE id = ?')\n for (const row of rows) {\n const event = classifyOrSkip(row, aiToolsEnabled)\n if (!event) continue\n const metadata = Object.keys(event.metadata).length > 0 ? JSON.stringify(event.metadata) : null\n update.run(event.category, metadata, row.id)\n }\n },\n}\n\nexport default migration\n","// Strictly additive, like every migration from here on: data work belongs in a data job (src/db/jobs/), which is\n// batched, resumable and backed up, never in a migration that runs in whichever process opens the database first.\nconst migration = {\n version: 7,\n description: 'Add data job state, the issues jobs record, and a meta key-value table',\n sql: `\n CREATE TABLE IF NOT EXISTS data_jobs (\n name TEXT PRIMARY KEY,\n version INTEGER NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'pending',\n started_at TEXT,\n finished_at TEXT,\n verification TEXT,\n error TEXT\n );\n CREATE TABLE IF NOT EXISTS data_job_issues (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_name TEXT NOT NULL,\n job_version INTEGER NOT NULL,\n reference TEXT NOT NULL,\n reason TEXT NOT NULL,\n recorded_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_data_job_issues_job ON data_job_issues (job_name, job_version);\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `,\n}\n\nexport default migration\n","// Strictly additive: the tables and the two nullable `events` columns start empty. Capture fills them for new events\n// and the `project-dimensions` data job (src/db/jobs/project-dimensions.ts) fills them for the history.\nconst migration = {\n version: 8,\n description: 'Add project, branch and file dimensions, and link events to them',\n sql: `\n CREATE TABLE IF NOT EXISTS project (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n identity TEXT NOT NULL UNIQUE,\n kind TEXT NOT NULL,\n name TEXT\n );\n CREATE TABLE IF NOT EXISTS project_alias (\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n PRIMARY KEY (project_id, name)\n );\n CREATE INDEX IF NOT EXISTS idx_project_alias_name ON project_alias (name);\n CREATE TABLE IF NOT EXISTS project_path (\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n path TEXT NOT NULL,\n PRIMARY KEY (project_id, path)\n );\n CREATE INDEX IF NOT EXISTS idx_project_path_path ON project_path (path);\n CREATE TABLE IF NOT EXISTS branch (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n UNIQUE (project_id, name)\n );\n CREATE TABLE IF NOT EXISTS file (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n relative_path TEXT NOT NULL,\n UNIQUE (project_id, relative_path)\n );\n CREATE TABLE IF NOT EXISTS event_file (\n event_id INTEGER NOT NULL REFERENCES events (id) ON DELETE CASCADE,\n file_id INTEGER NOT NULL REFERENCES file (id) ON DELETE CASCADE,\n action TEXT NOT NULL,\n PRIMARY KEY (event_id, file_id)\n );\n CREATE INDEX IF NOT EXISTS idx_event_file_file ON event_file (file_id);\n CREATE TABLE IF NOT EXISTS directory_probe (\n path TEXT PRIMARY KEY,\n outcome TEXT NOT NULL,\n root TEXT,\n common_dir TEXT,\n remote_url TEXT\n );\n CREATE TABLE IF NOT EXISTS project_resolution (\n cwd TEXT NOT NULL,\n repo_name TEXT NOT NULL,\n project_id INTEGER REFERENCES project (id) ON DELETE CASCADE,\n method TEXT NOT NULL,\n event_count INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (cwd, repo_name)\n );\n ALTER TABLE events ADD COLUMN project_id INTEGER REFERENCES project (id);\n ALTER TABLE events ADD COLUMN branch_id INTEGER REFERENCES branch (id);\n CREATE INDEX IF NOT EXISTS idx_events_project_id ON events (project_id);\n CREATE INDEX IF NOT EXISTS idx_events_branch_id ON events (branch_id);\n `,\n}\n\nexport default migration\n","// Strictly additive. Everything here is derived from `events` and rebuilt per day (src/sessions/threads.ts), so the\n// `project_id` columns carry no foreign key: a project merged or deleted later can't block a write, and the day's\n// next rebuild replaces the rows.\nconst migration = {\n version: 9,\n description: 'Add work blocks, project threads and activity slots, built per day alongside sessions',\n sql: `\n CREATE TABLE IF NOT EXISTS work_block (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n event_count INTEGER NOT NULL,\n active_slots INTEGER NOT NULL,\n grouping_version INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_work_block_date ON work_block (date, started_at);\n CREATE TABLE IF NOT EXISTS thread (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n block_id INTEGER NOT NULL REFERENCES work_block (id) ON DELETE CASCADE,\n project_id INTEGER,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n event_count INTEGER NOT NULL,\n active_slots INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_thread_block ON thread (block_id);\n CREATE INDEX IF NOT EXISTS idx_thread_project ON thread (project_id, started_at);\n CREATE TABLE IF NOT EXISTS thread_event (\n thread_id INTEGER NOT NULL REFERENCES thread (id) ON DELETE CASCADE,\n event_id INTEGER NOT NULL REFERENCES events (id) ON DELETE CASCADE,\n PRIMARY KEY (thread_id, event_id)\n );\n CREATE INDEX IF NOT EXISTS idx_thread_event_event ON thread_event (event_id);\n CREATE TABLE IF NOT EXISTS activity_slot (\n date TEXT NOT NULL,\n slot_start TEXT NOT NULL,\n project_id INTEGER,\n event_count INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_activity_slot_date ON activity_slot (date, slot_start);\n CREATE TABLE IF NOT EXISTS grouping_day (\n date TEXT PRIMARY KEY,\n grouping_version INTEGER NOT NULL,\n idle_ms INTEGER NOT NULL,\n event_count INTEGER NOT NULL,\n link_checksum INTEGER NOT NULL,\n link_spread INTEGER NOT NULL,\n max_event_id INTEGER\n );\n `,\n}\n\nexport default migration\n","// Strictly additive. A summary is kept against the events it describes rather than a derived session id or key, so\n// regrouping never orphans it. No foreign keys: a summary is never deleted because its events or project went away.\nconst migration = {\n version: 10,\n description: 'Add summaries anchored to the events they describe',\n sql: `\n CREATE TABLE IF NOT EXISTS summary (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL,\n project_id INTEGER,\n first_event_id INTEGER,\n last_event_id INTEGER,\n engine TEXT NOT NULL,\n text TEXT NOT NULL,\n created_at TEXT NOT NULL,\n source_key TEXT,\n origin TEXT NOT NULL,\n anchor_source TEXT NOT NULL,\n is_mixed INTEGER NOT NULL DEFAULT 0\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_summary_source ON summary (source_key, engine);\n CREATE INDEX IF NOT EXISTS idx_summary_first_event ON summary (first_event_id);\n `,\n}\n\nexport default migration\n","// Strictly additive: an index, so `context` finds a project's latest event without sorting all of them.\nconst migration = {\n version: 11,\n description: \"Index events by project and time, for a project's latest event\",\n sql: `\n CREATE INDEX IF NOT EXISTS idx_events_project_time ON events (project_id, captured_at);\n `,\n}\n\nexport default migration\n","// Strictly additive. The agent conversation each hook event came from (Claude Code's `session_id`), so one agent's\n// work across several repos can be linked. WOR-136 extends this table with how a session ended and its recaps.\nconst migration = {\n version: 12,\n description: 'Add agent sessions, and link each agent event to its session',\n sql: `\n CREATE TABLE IF NOT EXISTS agent_sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n source TEXT NOT NULL,\n session_id TEXT NOT NULL,\n cwd TEXT,\n started_at TEXT NOT NULL,\n ended_at TEXT,\n last_seen_at TEXT NOT NULL,\n UNIQUE (source, session_id)\n );\n ALTER TABLE events ADD COLUMN agent_session_id INTEGER REFERENCES agent_sessions (id);\n CREATE INDEX IF NOT EXISTS idx_events_agent_session ON events (agent_session_id);\n `,\n}\n\nexport default migration\n","// Strictly additive. Tickets named by branches and commit messages (WOR-144): a ticket links to a branch, or to the\n// commit event that named it. Capture fills them for new events and the `ticket-refs` data job for the history; an\n// event's links go with it (undo, clear) and a branch's with the branch.\nconst migration = {\n version: 13,\n description: 'Add tickets, linked to the branches and commits that name them',\n sql: `\n CREATE TABLE IF NOT EXISTS ticket (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT NOT NULL UNIQUE\n );\n CREATE TABLE IF NOT EXISTS ticket_ref (\n ticket_id INTEGER NOT NULL REFERENCES ticket (id) ON DELETE CASCADE,\n branch_id INTEGER REFERENCES branch (id) ON DELETE CASCADE,\n event_id INTEGER REFERENCES events (id) ON DELETE CASCADE,\n CHECK ((branch_id IS NULL) <> (event_id IS NULL))\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_ref_branch ON ticket_ref (branch_id, ticket_id) WHERE branch_id IS NOT NULL;\n CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_ref_event ON ticket_ref (event_id, ticket_id) WHERE event_id IS NOT NULL;\n `,\n}\n\nexport default migration\n","import initialSchema from './001_initial_schema'\nimport semanticCategories from './002_semantic_categories'\nimport alerts from './003_alerts'\nimport ideColumns from './004_ide_columns'\nimport sessionSummaries from './005_session_summaries'\nimport reclassifyOther from './006_reclassify_other'\nimport dataJobs from './007_data_jobs'\nimport dimensions from './008_dimensions'\nimport workBlocks from './009_work_blocks'\nimport summaries from './010_summaries'\nimport eventsProjectTime from './011_events_project_time'\nimport agentSessions from './012_agent_sessions'\nimport tickets from './013_tickets'\nimport type { Migration } from '../migration-types'\n\nexport type { Migration } from '../migration-types'\n\n// Ordered list — append only. Never modify or remove an existing entry.\nexport const MIGRATIONS: Migration[] = [\n initialSchema,\n semanticCategories,\n alerts,\n ideColumns,\n sessionSummaries,\n reclassifyOther,\n dataJobs,\n dimensions,\n workBlocks,\n summaries,\n eventsProjectTime,\n agentSessions,\n tickets,\n]\n","/** The message from anything thrown — an Error's message, or the thrown value itself as text. */\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import type { Migration, MigrationDatabase } from './migration-types'\nimport { errorMessage } from '../utils/error-message'\n\n/** An install from before the migration system already has the events table — record version 1 as applied. */\nfunction seedVersionOneForOldInstalls(db: MigrationDatabase): void {\n const hasEvents = db.prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='events'\").get()\n const hasVersion1 = db.prepare('SELECT version FROM schema_migrations WHERE version = 1').get()\n if (hasEvents && !hasVersion1) {\n db.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES ($version, datetime('now'))\").run({\n version: 1,\n })\n }\n}\n\nfunction isApplied(db: MigrationDatabase, version: number): boolean {\n return db.prepare('SELECT version FROM schema_migrations WHERE version = ?').get(version) !== undefined\n}\n\n/**\n * Applies each pending migration in order, each in its own transaction, so a failure leaves the database at\n * the previous version and the next run retries it. `BEGIN IMMEDIATE` takes the write lock up front and the\n * version is checked inside the transaction, so two processes starting at once (parallel captures from an\n * agent) can't both run a migration. Kept free of `node:sqlite` so tests can run it on any database.\n */\nexport function applyMigrations(db: MigrationDatabase, migrations: Migration[]): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_migrations (\n version INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n seedVersionOneForOldInstalls(db)\n\n for (const migration of migrations) {\n if (isApplied(db, migration.version)) continue\n\n db.exec('BEGIN IMMEDIATE')\n try {\n if (isApplied(db, migration.version)) {\n db.exec('COMMIT')\n continue\n }\n if (migration.sql.trim()) db.exec(migration.sql)\n migration.run?.(db)\n db.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES ($version, datetime('now'))\").run({\n version: migration.version,\n })\n db.exec('COMMIT')\n } catch (error) {\n try {\n db.exec('ROLLBACK')\n } catch {\n /* already rolled back or connection lost */\n }\n throw new Error(`Migration ${migration.version} (${migration.description}) failed: ${errorMessage(error)}`, {\n cause: error,\n })\n }\n }\n}\n","import type { MigrationDatabase } from './migration-types'\n\n/** How long a connection waits for another process's write lock before failing (`PRAGMA busy_timeout`). */\nexport const BUSY_TIMEOUT_MS = 5000\n\n// SQLite's SQLITE_BUSY (\"database is locked\"): another process held the write lock for longer than we waited\nconst SQLITE_BUSY = 5\n\nexport interface TransactionOptions {\n /** Wait at most this long for the write lock, instead of BUSY_TIMEOUT_MS. Restored afterwards. */\n busyTimeoutMs?: number\n}\n\n/** Whether an error is SQLite giving up on a write lock another process holds. */\nexport function isDatabaseLocked(error: unknown): boolean {\n return (error as { errcode?: unknown } | null)?.errcode === SQLITE_BUSY\n}\n\n/** Undoes an open transaction. Never throws: the error that made us roll back is the one worth reporting. */\nfunction rollbackQuietly(db: MigrationDatabase): void {\n try {\n db.exec('ROLLBACK')\n } catch {\n // SQLite already rolled back (some errors end the transaction themselves), or the connection is gone\n }\n}\n\n/**\n * Runs `read` in a read transaction: one consistent snapshot across its queries, without the write lock — in WAL\n * mode a reader never blocks a writer, however long it reads. An error from `read` is the one thrown, never one from\n * ending the transaction.\n */\nexport function withReadTransaction<T>(db: MigrationDatabase, read: () => T): T {\n db.exec('BEGIN')\n let result: T\n try {\n result = read()\n } catch (error) {\n rollbackQuietly(db)\n throw error\n }\n db.exec('COMMIT')\n return result\n}\n\n/**\n * Runs `work` holding the database's write lock (`BEGIN IMMEDIATE`), so no other process can write in between —\n * `work` should recheck whatever made it start, since another process may have done the job while it waited.\n * Commits on success; rolls back and rethrows on error. Throws a locked error (see `isDatabaseLocked`) if the lock\n * isn't free within the busy timeout. Kept free of `node:sqlite`, so tests run it on any database.\n */\nexport function withImmediateTransaction<T>(db: MigrationDatabase, work: () => T, options: TransactionOptions = {}): T {\n const { busyTimeoutMs } = options\n if (busyTimeoutMs !== undefined) db.exec(`PRAGMA busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`)\n try {\n db.exec('BEGIN IMMEDIATE')\n try {\n const result = work()\n db.exec('COMMIT')\n return result\n } catch (error) {\n rollbackQuietly(db)\n throw error\n }\n } finally {\n if (busyTimeoutMs !== undefined) db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`)\n }\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { ProjectKind } from '../capture/project-identity'\n\n// The project, branch and file rows events link to (migration 008). Insert-or-ignore throughout, so capture and the\n// backfill job can both write them, in any order, any number of times. `node:sqlite`-free, so tests run it in memory.\n\n/** What an event did to a file. An editor save is the one file action captured today. */\nexport type FileAction = 'save'\n\nexport const FILE_SAVE_ACTION: FileAction = 'save'\n\nexport interface ProjectRecord {\n identity: string\n kind: ProjectKind\n /** The name the event was stored under: kept as an alias, and as the project's latest name */\n name: string | null\n /** The checkout or worktree root the event came from, when git said */\n root: string | null\n}\n\n/** What an event links to. Resolved before the event is written; every part may be missing. */\nexport interface EventDimensions {\n project: ProjectRecord | null\n /** The event's branch, when it's a branch of `project` */\n branch: string | null\n /** The file the event was about, relative to the project's root */\n file: { relativePath: string; action: FileAction } | null\n}\n\n/** The project's id, creating it if needed, and records the name and root it was seen under. */\nexport function upsertProject(db: MigrationDatabase, project: ProjectRecord): number {\n const row = db\n .prepare(\n `INSERT INTO project (identity, kind, name) VALUES ($identity, $kind, $name)\n ON CONFLICT (identity) DO UPDATE SET name = COALESCE(excluded.name, project.name)\n RETURNING id`\n )\n .get({ identity: project.identity, kind: project.kind, name: project.name }) as { id: number }\n if (project.name !== null) {\n db.prepare('INSERT OR IGNORE INTO project_alias (project_id, name) VALUES (?, ?)').run(row.id, project.name)\n }\n if (project.root !== null) {\n db.prepare('INSERT OR IGNORE INTO project_path (project_id, path) VALUES (?, ?)').run(row.id, project.root)\n }\n return row.id\n}\n\nexport function upsertBranch(db: MigrationDatabase, projectId: number, name: string): number {\n db.prepare('INSERT OR IGNORE INTO branch (project_id, name) VALUES (?, ?)').run(projectId, name)\n const row = db.prepare('SELECT id FROM branch WHERE project_id = ? AND name = ?').get(projectId, name) as {\n id: number\n }\n return row.id\n}\n\nexport function upsertFile(db: MigrationDatabase, projectId: number, relativePath: string): number {\n db.prepare('INSERT OR IGNORE INTO file (project_id, relative_path) VALUES (?, ?)').run(projectId, relativePath)\n const row = db\n .prepare('SELECT id FROM file WHERE project_id = ? AND relative_path = ?')\n .get(projectId, relativePath) as { id: number }\n return row.id\n}\n\nexport function linkEventFile(\n db: MigrationDatabase,\n link: { eventId: number; fileId: number; action: FileAction }\n): void {\n db.prepare('INSERT OR IGNORE INTO event_file (event_id, file_id, action) VALUES (?, ?, ?)').run(\n link.eventId,\n link.fileId,\n link.action\n )\n}\n\n/** The ids an event was linked to. */\nexport interface EventLinks {\n projectId: number\n branchId: number | null\n}\n\n/** Links a stored event to its project, branch and file. Without a project, nothing is linked (null). */\nexport function writeEventDimensions(\n db: MigrationDatabase,\n eventId: number,\n dimensions: EventDimensions\n): EventLinks | null {\n if (dimensions.project === null) return null\n const projectId = upsertProject(db, dimensions.project)\n const branchId = dimensions.branch === null ? null : upsertBranch(db, projectId, dimensions.branch)\n db.prepare('UPDATE events SET project_id = ?, branch_id = ? WHERE id = ?').run(projectId, branchId, eventId)\n if (dimensions.file !== null) {\n const fileId = upsertFile(db, projectId, dimensions.file.relativePath)\n linkEventFile(db, { eventId, fileId, action: dimensions.file.action })\n }\n return { projectId, branchId }\n}\n","import type { MigrationDatabase } from './migration-types'\n\n// `agent_sessions` (migration 012): the agent conversations hook events came from. `node:sqlite`-free, so tests run\n// it on an in-memory database.\n\n/** Which conversation an agent event came from: the agent, and the id it gives the conversation. */\nexport interface AgentSessionRef {\n source: string\n sessionId: string\n}\n\n/**\n * The session's id, creating it on first sight with the event's folder and time. After that only `last_seen_at`\n * moves, and only forward.\n */\nexport function upsertAgentSession(\n db: MigrationDatabase,\n session: AgentSessionRef & { cwd: string; capturedAt: string }\n): number {\n const row = db\n .prepare(\n `INSERT INTO agent_sessions (source, session_id, cwd, started_at, last_seen_at)\n VALUES ($source, $session_id, $cwd, $captured_at, $captured_at)\n ON CONFLICT (source, session_id) DO UPDATE SET last_seen_at = MAX(last_seen_at, excluded.last_seen_at)\n RETURNING id`\n )\n .get({\n source: session.source,\n session_id: session.sessionId,\n cwd: session.cwd,\n captured_at: session.capturedAt,\n }) as { id: number }\n return row.id\n}\n\n/** What `sessions --json` says about an agent session: never its working folder. */\nexport interface AgentSessionInfo {\n source: string\n sessionId: string\n startedAt: string\n lastSeenAt: string\n}\n\n/** The distinct agent sessions these events came from, oldest first. */\nexport function getAgentSessionsOfEvents(db: MigrationDatabase, eventIds: number[]): AgentSessionInfo[] {\n return db\n .prepare(\n `SELECT source, session_id AS sessionId, started_at AS startedAt, last_seen_at AS lastSeenAt FROM agent_sessions\n WHERE id IN (SELECT agent_session_id FROM events WHERE id IN (SELECT value FROM json_each(?)))\n ORDER BY started_at, id`\n )\n .all(JSON.stringify(eventIds)) as unknown as AgentSessionInfo[]\n}\n\n/** The agent sessions these events belong to — read before deleting them. */\nexport function agentSessionIdsOfEvents(db: MigrationDatabase, eventIds: number[]): number[] {\n const rows = db\n .prepare(\n `SELECT DISTINCT agent_session_id AS id FROM events\n WHERE id IN (SELECT value FROM json_each(?)) AND agent_session_id IS NOT NULL`\n )\n .all(JSON.stringify(eventIds)) as { id: number }[]\n return rows.map((row) => row.id)\n}\n\n/** The agent sessions of a local day's events — read before `clear` deletes them. */\nexport function agentSessionIdsOfDate(db: MigrationDatabase, date: string): number[] {\n const rows = db\n .prepare(\n `SELECT DISTINCT agent_session_id AS id FROM events\n WHERE date(captured_at, 'localtime') = ? AND agent_session_id IS NOT NULL`\n )\n .all(date) as { id: number }[]\n return rows.map((row) => row.id)\n}\n\n/**\n * Deletes those of these sessions no event belongs to any more — after their events were deleted, so `undo` and\n * `clear` leave no conversation id behind. Only these: a session recorded with no event yet is never touched.\n */\nexport function deleteEmptyAgentSessions(db: MigrationDatabase, sessionIds: number[]): void {\n if (sessionIds.length === 0) return\n db.prepare(\n `DELETE FROM agent_sessions\n WHERE id IN (SELECT value FROM json_each(?))\n AND NOT EXISTS (SELECT 1 FROM events WHERE events.agent_session_id = agent_sessions.id)`\n ).run(JSON.stringify(sessionIds))\n}\n","import type { MigrationDatabase } from './migration-types'\n\n// The `ticket` and `ticket_ref` rows (migration 013). Insert-or-ignore throughout, so capture and the `ticket-refs`\n// job can both write them, in any order, any number of times. `node:sqlite`-free, so tests run it in memory.\n\n/** Ticket keys an event names: in its linked branch's name, and in the commits it made. */\nexport interface TicketKeys {\n branch: string[]\n commits: string[]\n}\n\nfunction upsertTicket(db: MigrationDatabase, key: string): number {\n db.prepare('INSERT OR IGNORE INTO ticket (key) VALUES (?)').run(key)\n return (db.prepare('SELECT id FROM ticket WHERE key = ?').get(key) as { id: number }).id\n}\n\n/**\n * Links the keys to the event's branch and to the event itself. Branch keys need the branch the event is linked to:\n * without one (outside a project, or a save in another repo), they link nothing.\n */\nexport function linkEventTickets(\n db: MigrationDatabase,\n link: { eventId: number; branchId: number | null; keys: TicketKeys }\n): void {\n if (link.branchId !== null) {\n const linkBranch = db.prepare('INSERT OR IGNORE INTO ticket_ref (ticket_id, branch_id) VALUES (?, ?)')\n for (const key of link.keys.branch) linkBranch.run(upsertTicket(db, key), link.branchId)\n }\n const linkEvent = db.prepare('INSERT OR IGNORE INTO ticket_ref (ticket_id, event_id) VALUES (?, ?)')\n for (const key of link.keys.commits) linkEvent.run(upsertTicket(db, key), link.eventId)\n}\n\n/** The keys linked to these events, or to the branches they're on — sorted numerically (WOR-9 before WOR-10). */\nexport function ticketsOfEvents(db: MigrationDatabase, eventIds: number[]): string[] {\n const rows = db\n .prepare(\n `WITH chosen AS (SELECT value AS id FROM json_each(?))\n SELECT DISTINCT ticket.key FROM ticket_ref JOIN ticket ON ticket.id = ticket_ref.ticket_id\n WHERE ticket_ref.event_id IN (SELECT id FROM chosen)\n OR ticket_ref.branch_id IN (SELECT branch_id FROM events WHERE id IN (SELECT id FROM chosen))`\n )\n .all(JSON.stringify(eventIds)) as { key: string }[]\n return rows.map((row) => row.key).sort((first, second) => first.localeCompare(second, 'en', { numeric: true }))\n}\n","import type { MigrationDatabase } from './migration-types'\nimport { isDatabaseLocked, withImmediateTransaction } from './transaction'\nimport { writeEventDimensions, type EventDimensions } from './dimensions'\nimport { upsertAgentSession, type AgentSessionRef } from './agent-sessions'\nimport { linkEventTickets, type TicketKeys } from './tickets'\nimport { debugLog } from '../utils/logger'\n\nexport interface NewEvent {\n captured_at: string\n command: string\n cwd: string\n exit_code: number | null\n duration_ms: number | null\n repo_name: string | null\n branch: string | null\n category: string\n metadata: string | null\n source: string | null\n file_path: string | null\n /** What the event links to. The text columns above are stored exactly as given either way */\n dimensions?: EventDimensions\n /** The agent conversation a hook event came from */\n agentSession?: AgentSessionRef\n /** The ticket keys it names, linked along with its project (`ticketKeysOf`) */\n tickets?: TicketKeys\n}\n\nfunction insertEventRow(db: MigrationDatabase, event: NewEvent, agentSessionId: number | null = null): number {\n const { dimensions: _dimensions, agentSession: _agentSession, tickets: _tickets, ...columns } = event\n const result = db\n .prepare(\n `INSERT INTO events (captured_at, command, cwd, exit_code, duration_ms, repo_name, branch, category, metadata, source, file_path, agent_session_id)\n VALUES ($captured_at, $command, $cwd, $exit_code, $duration_ms, $repo_name, $branch, $category, $metadata, $source, $file_path, $agent_session_id)`\n )\n .run({ ...columns, agent_session_id: agentSessionId }) as { lastInsertRowid: number | bigint }\n return Number(result.lastInsertRowid)\n}\n\n/** The event, its agent session, its project links and its tickets, written in order inside the caller's transaction. */\nfunction insertLinkedEvent(db: MigrationDatabase, event: NewEvent): void {\n const agentSessionId = event.agentSession\n ? upsertAgentSession(db, { ...event.agentSession, cwd: event.cwd, capturedAt: event.captured_at })\n : null\n const eventId = insertEventRow(db, event, agentSessionId)\n const links = event.dimensions?.project ? writeEventDimensions(db, eventId, event.dimensions) : null\n if (links && event.tickets) linkEventTickets(db, { eventId, branchId: links.branchId, keys: event.tickets })\n}\n\n/**\n * Stores an event and its links — its project, its tickets and its agent session — in one transaction. If linking fails, the\n * event is stored without links rather than lost. A lock that never came free is thrown, as a plain insert would\n * have been: no second wait. An event with nothing to link is one plain insert.\n */\nexport function insertEventRecord(db: MigrationDatabase, event: NewEvent): void {\n if (!event.dimensions?.project && !event.agentSession) {\n insertEventRow(db, event)\n return\n }\n try {\n withImmediateTransaction(db, () => insertLinkedEvent(db, event))\n } catch (error) {\n if (isDatabaseLocked(error)) throw error\n debugLog('capture: linking the event', error)\n insertEventRow(db, event)\n }\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { StoredEngine } from '../ai/engine'\nimport type { AnchorRange } from '../summary/anchors'\n\n// The `summary` table (migration 010): summaries anchored to the events they describe. `sessions summary set` writes\n// here only; the `summary-anchors` job mapped the old `session_summaries` store into it.\n// `node:sqlite`-free, so tests run it on an in-memory database.\n\n/** `saved`: written by `sessions summary set`. `mapped`: rebuilt by the job from the old store. */\nexport type SummaryOrigin = 'saved' | 'mapped'\nexport const SAVED_ORIGIN: SummaryOrigin = 'saved'\nexport const MAPPED_ORIGIN: SummaryOrigin = 'mapped'\n\n/**\n * Where a summary's range came from: the thread it was saved for, the live old session it was saved from (before\n * WOR-147), the cached old session, the old session regrouped from its day, or nowhere (kept without a range).\n */\nexport type AnchorSource = 'thread' | 'live' | 'cached' | 'regrouped' | 'none'\n\nexport interface SummaryRow {\n id: number\n scope: string\n project_id: number | null\n first_event_id: number | null\n last_event_id: number | null\n engine: StoredEngine\n text: string\n created_at: string\n source_key: string | null\n origin: SummaryOrigin\n anchor_source: AnchorSource\n /** 1 when its session mixed projects, so it matches no thread */\n is_mixed: number\n}\n\n/** A summary of a thread or an old session, by the key it was saved under. */\nexport interface SessionSummaryRecord {\n sourceKey: string\n engine: StoredEngine\n text: string\n createdAt: string\n anchor: AnchorRange & { isMixed: boolean }\n anchorSource: AnchorSource\n}\n\nconst SESSION_SCOPE = 'session'\n\nconst INSERT_SUMMARY = `INSERT INTO summary\n (scope, project_id, first_event_id, last_event_id, engine, text, created_at, source_key, origin, anchor_source, is_mixed)\n VALUES ('${SESSION_SCOPE}', $project_id, $first_event_id, $last_event_id, $engine, $text, $created_at, $source_key,\n $origin, $anchor_source, $is_mixed)\n ON CONFLICT (source_key, engine) DO UPDATE SET\n project_id = excluded.project_id, first_event_id = excluded.first_event_id, last_event_id = excluded.last_event_id,\n text = excluded.text, created_at = excluded.created_at, origin = excluded.origin,\n anchor_source = excluded.anchor_source, is_mixed = excluded.is_mixed`\n\n/** A saved summary replaces whatever row its key and engine had. */\nconst SAVE_SUMMARY = INSERT_SUMMARY\n/** A mapped summary replaces only a row the job wrote itself, never one `sessions summary set` saved. */\nconst MAP_SUMMARY = `${INSERT_SUMMARY} WHERE summary.origin = '${MAPPED_ORIGIN}'`\n\nfunction parametersOf(record: SessionSummaryRecord, origin: SummaryOrigin): Record<string, unknown> {\n return {\n project_id: record.anchor.projectId,\n first_event_id: record.anchor.firstEventId,\n last_event_id: record.anchor.lastEventId,\n engine: record.engine,\n text: record.text,\n created_at: record.createdAt,\n source_key: record.sourceKey,\n origin,\n anchor_source: record.anchorSource,\n is_mixed: record.anchor.isMixed ? 1 : 0,\n }\n}\n\n/**\n * Saves a summary the agent skill wrote — overwriting whatever row its key and engine had, one the job mapped\n * included. Never writes `session_summaries`.\n */\nexport function saveAnchoredSummary(db: MigrationDatabase, record: SessionSummaryRecord): void {\n db.prepare(SAVE_SUMMARY).run(parametersOf(record, SAVED_ORIGIN))\n}\n\n/**\n * The job's write: inserts or rebuilds a mapped row. A row `sessions summary set` saved keeps its range and text —\n * the live session it came from beats anything rebuilt — and only gets its project when it had none.\n */\nexport function mapSummary(db: MigrationDatabase, record: SessionSummaryRecord): void {\n db.prepare(MAP_SUMMARY).run(parametersOf(record, MAPPED_ORIGIN))\n if (record.anchor.projectId === null) return\n db.prepare(\n `UPDATE summary SET project_id = ?\n WHERE source_key = ? AND engine = ? AND origin = '${SAVED_ORIGIN}' AND project_id IS NULL`\n ).run(record.anchor.projectId, record.sourceKey, record.engine)\n}\n\nexport function getSummaryBySource(db: MigrationDatabase, sourceKey: string, engine: string): SummaryRow | null {\n const row = db.prepare('SELECT * FROM summary WHERE source_key = ? AND engine = ?').get(sourceKey, engine) as\n | SummaryRow\n | undefined\n return row ?? null\n}\n","import type { MigrationDatabase } from './migration-types'\n\n// Counts over the last few days of events, for `status` and `profile`. The window starts at an ISO timestamp, the\n// same format `captured_at` is stored in: compared with SQLite's `datetime()` text (\"2026-11-02 10:00:00\"), every\n// event of the boundary day would pass, since 'T' sorts after ' '. `node:sqlite`-free, so tests run it in memory.\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/** The start of the last `days` days, as `captured_at` is stored. */\nexport function sinceIso(days: number, now: number = Date.now()): string {\n return new Date(now - days * MS_PER_DAY).toISOString()\n}\n\nexport function countOtherEventTools(db: MigrationDatabase, since: string): { tool: string; count: number }[] {\n return db\n .prepare(\n `SELECT json_extract(metadata, '$.tool') as tool, COUNT(*) as count\n FROM events\n WHERE category = 'other'\n AND captured_at >= ?\n AND json_extract(metadata, '$.tool') IS NOT NULL\n GROUP BY tool\n ORDER BY count DESC`\n )\n .all(since) as { tool: string; count: number }[]\n}\n\nexport function countOtherEventsSince(db: MigrationDatabase, since: string): { total: number; otherCount: number } {\n return db\n .prepare(\n `SELECT COUNT(*) as total,\n SUM(CASE WHEN category = 'other' THEN 1 ELSE 0 END) as otherCount\n FROM events\n WHERE captured_at >= ?`\n )\n .get(since) as { total: number; otherCount: number }\n}\n","import type { StoredEngine } from '../ai/engine'\n\n/** Engine name for summaries migrated from the 1.x `sessions.summary` column (written by a provider or Ollama). */\nexport const LEGACY_ENGINE = 'legacy' satisfies StoredEngine\n\n/** Engine name for summaries the Workscribe agent skill saved with `sessions summary set`. */\nexport const SKILL_ENGINE = 'skill' satisfies StoredEngine\n\n// Higher wins when picking what to show: richer sources first. Template rows rank 0 — templates are\n// rendered live, so any stored copy is a stale leftover and must never beat a fresh render.\nconst ENGINE_RANK: Record<StoredEngine, number> = { skill: 3, ollama: 2, provider: 2, legacy: 2, template: 0 }\n\n/** Summaries a 1.x AI provider or Ollama wrote. 2.0 no longer writes these, but still shows them for their sessions. */\nconst LEGACY_AI_ENGINES = new Set<string>(['provider', 'ollama', LEGACY_ENGINE])\n\n/** Identifies a session across rebuilds. Must match SESSION_KEY_SQL in migration 005. */\nexport function sessionKey(repoName: string | null, startedAt: string): string {\n return `${repoName ?? ''}|${startedAt}`\n}\n\nexport interface ParsedSessionKey {\n repoName: string | null\n startedAt: string\n}\n\n/** Reverses sessionKey(). Null for a malformed key: no `|`, or a start that isn't a timestamp. */\nexport function parseSessionKey(key: string): ParsedSessionKey | null {\n const separator = key.lastIndexOf('|')\n if (separator === -1) return null\n const startedAt = key.slice(separator + 1)\n if (startedAt === '' || Number.isNaN(new Date(startedAt).getTime())) return null\n const repoName = key.slice(0, separator)\n return { repoName: repoName === '' ? null : repoName, startedAt }\n}\n\n/** What picking a summary reads from a stored one — `summary` rows fit. */\nexport interface RankableSummary {\n engine: string\n text: string\n created_at: string\n}\n\nfunction newestFirst(first: RankableSummary, second: RankableSummary): number {\n return second.created_at.localeCompare(first.created_at)\n}\n\nexport interface SavedSummary {\n text: string\n fromSkill: boolean\n}\n\n/**\n * What `workscribe summary` can show for a session instead of rendering the template: a summary the agent\n * skill saved wins (it was written with more context), else one a 1.x AI provider wrote for that session.\n */\nexport function pickSavedSummary(rows: RankableSummary[]): SavedSummary | null {\n const [newestSkill] = rows.filter((row) => row.engine === SKILL_ENGINE).sort(newestFirst)\n if (newestSkill) return { text: newestSkill.text, fromSkill: true }\n const [newestLegacy] = rows.filter((row) => LEGACY_AI_ENGINES.has(row.engine)).sort(newestFirst)\n return newestLegacy ? { text: newestLegacy.text, fromSkill: false } : null\n}\n\n/** The best stored summary for a session regardless of the configured engine (used by `context`). */\nexport function pickBestSummary(rows: RankableSummary[]): string | null {\n // Rows come from the DB, so an engine name this build doesn't know is ignored like a template row\n const rank = (row: RankableSummary): number => ENGINE_RANK[row.engine as StoredEngine] ?? 0\n const [best] = rows\n .filter((row) => rank(row) > 0)\n .sort((first, second) => rank(second) - rank(first) || newestFirst(first, second))\n return best?.text ?? null\n}\n","import type { EventRow } from '../db/types'\nimport { parseSessionKey } from './store'\n\n// Which events a summary describes, and which thread it belongs to (pure). A summary is anchored to a project and\n// the first and last events of its range; the thread it attaches to is worked out when it's read, since threads are\n// rebuilt whenever a day changes.\n\n/** The `anchor_source` of a summary `sessions summary set` saved for a thread. */\nexport const THREAD_ANCHOR_SOURCE = 'thread'\n\n/** A summary belongs to a project only when that project holds more than this share of its events. */\nexport const MAJORITY_SHARE = 0.5\n\n/** What a stored summary is anchored to. */\nexport interface AnchorRange {\n /** Null for \"elsewhere\", or when the events aren't linked to projects yet */\n projectId: number | null\n firstEventId: number | null\n lastEventId: number | null\n}\n\nexport interface SummaryAnchor extends AnchorRange {\n /** How much of the range the project holds, 0–1 */\n share: number\n /** The range mixed projects: the project held half of it or less, so the summary may describe other work */\n isMixed: boolean\n /** The key names a repo, but none of its events is linked to a project yet */\n isUnlinked: boolean\n}\n\nconst UNANCHORED: SummaryAnchor = {\n projectId: null,\n firstEventId: null,\n lastEventId: null,\n share: 0,\n isMixed: false,\n isUnlinked: false,\n}\n\nfunction inSessionOrder(events: EventRow[]): EventRow[] {\n return [...events].sort(\n (first, second) => first.captured_at.localeCompare(second.captured_at) || first.id - second.id\n )\n}\n\n/** The project most of these events link to; a tie goes to the lowest id. Null when none is linked. */\nfunction mostLinkedProject(events: EventRow[]): number | null {\n const counts = new Map<number, number>()\n for (const event of events) {\n const projectId = event.project_id ?? null\n if (projectId === null) continue\n counts.set(projectId, (counts.get(projectId) ?? 0) + 1)\n }\n const [best] = [...counts.entries()].sort(\n ([firstId, firstCount], [secondId, secondCount]) => secondCount - firstCount || firstId - secondId\n )\n return best?.[0] ?? null\n}\n\n/**\n * The anchor of an old session's summary: its first and last events in session order, and the project most of\n * its events named by the key link to (null for a key with no repo name: \"elsewhere\").\n */\nexport function anchorOfSession(events: EventRow[], keyRepoName: string | null): SummaryAnchor {\n if (events.length === 0) return UNANCHORED\n const ordered = inSessionOrder(events)\n const range = { firstEventId: ordered[0].id, lastEventId: ordered[ordered.length - 1].id }\n const projectId =\n keyRepoName === null ? null : mostLinkedProject(ordered.filter((event) => event.repo_name === keyRepoName))\n if (keyRepoName !== null && projectId === null) {\n return { ...UNANCHORED, ...range, isUnlinked: true }\n }\n const held = ordered.filter((event) => (event.project_id ?? null) === projectId).length\n const share = held / ordered.length\n return { ...range, projectId, share, isMixed: share <= MAJORITY_SHARE, isUnlinked: false }\n}\n\n/** The anchor of the session a `repo|started_at` key names, from that session's events. */\nexport function anchorOfKeyedSession(events: EventRow[], key: string): SummaryAnchor {\n return anchorOfSession(events, parseSessionKey(key)?.repoName ?? null)\n}\n\n/** A thread's anchor: one project's events, so never mixed. */\nexport interface ThreadAnchor extends AnchorRange {\n isMixed: false\n}\n\n/** The anchor of a summary saved for a thread: its project, and its first and last events in grouping order. */\nexport function anchorOfThread(events: EventRow[], projectId: number | null): ThreadAnchor {\n return {\n projectId,\n firstEventId: events[0]?.id ?? null,\n lastEventId: events.at(-1)?.id ?? null,\n isMixed: false,\n }\n}\n\n/** What attaching a stored summary to a thread reads. */\nexport interface StoredAnchor extends AnchorRange {\n /** Saved for a thread by `sessions summary set`, rather than for an old session */\n isThreadSaved: boolean\n}\n\nexport interface BlockEvent {\n id: number\n threadId: number\n}\n\nexport interface CandidateThread {\n id: number\n projectId: number | null\n}\n\n/**\n * The thread a summary belongs to, in the block holding its first event: its project's thread, when that thread\n * holds more than half of the block's events from the first event to the last (or to the block's end, if the last\n * is outside it). A summary saved for a thread needs only its first event to still be in that thread: a small\n * thread worked in between a busier project's events never holds most of its own range. Null for an unanchored\n * summary, a day with no threads built, or a range that mixed projects.\n */\nexport function findSummaryThread(\n anchor: StoredAnchor,\n block: { threads: CandidateThread[]; events: BlockEvent[] }\n): CandidateThread | null {\n const start = block.events.findIndex((event) => event.id === anchor.firstEventId)\n if (start === -1) return null\n const thread = block.threads.find((candidate) => candidate.projectId === anchor.projectId)\n if (!thread) return null\n if (anchor.isThreadSaved) return block.events[start].threadId === thread.id ? thread : null\n const end = block.events.findIndex((event) => event.id === anchor.lastEventId)\n const rangeEvents = block.events.slice(start, end === -1 ? block.events.length : end + 1)\n const held = rangeEvents.filter((event) => event.threadId === thread.id).length\n return held / rangeEvents.length > MAJORITY_SHARE ? thread : null\n}\n\n/** What attaching a stored summary reads from its row — `summary` rows fit. */\nexport interface AttachableSummary {\n project_id: number | null\n first_event_id: number | null\n last_event_id: number | null\n anchor_source: string\n}\n\n/** The stored summaries that belong to this thread of the block, by `findSummaryThread`. */\nexport function summariesOfThread<Summary extends AttachableSummary>(\n threadId: number,\n block: { threads: CandidateThread[]; events: BlockEvent[] },\n summaries: Summary[]\n): Summary[] {\n return summaries.filter((summary) => {\n const anchor = {\n projectId: summary.project_id,\n firstEventId: summary.first_event_id,\n lastEventId: summary.last_event_id,\n isThreadSaved: summary.anchor_source === THREAD_ANCHOR_SOURCE,\n }\n return findSummaryThread(anchor, block)?.id === threadId\n })\n}\n","import { DatabaseSync } from 'node:sqlite'\nimport fs from 'fs'\nimport { WORKSCRIBE_DIR, DB_PATH } from '../utils/paths'\nimport { ensurePrivateDirectory } from '../utils/private-directory'\nimport type { EventRow } from './types'\nimport { MIGRATIONS } from './migrations'\nimport { applyMigrations } from './migrate'\nimport { BUSY_TIMEOUT_MS, withImmediateTransaction, type TransactionOptions } from './transaction'\nimport type { StoredEngine } from '../ai/engine'\nimport { insertEventRecord, type NewEvent } from './event-writer'\nimport { saveAnchoredSummary } from './summary-store'\nimport { countOtherEventsSince, countOtherEventTools, sinceIso } from './recent-events'\nimport { agentSessionIdsOfDate, agentSessionIdsOfEvents, deleteEmptyAgentSessions } from './agent-sessions'\nimport { THREAD_ANCHOR_SOURCE, type ThreadAnchor } from '../summary/anchors'\nexport type { EventRow } from './types'\n\nlet _db: DatabaseSync | null = null\n\nexport function getDb(): DatabaseSync {\n if (_db) return _db\n\n // The data folder exists and is private before SQLite creates anything in it\n try {\n ensurePrivateDirectory(WORKSCRIBE_DIR)\n } catch (error) {\n // Rethrown as a clean, actionable message — callers reaching this\n // (explicit commands, or _capture/_hook's own try/catch) otherwise\n // only see a raw native stack trace with the real cause buried in it.\n throw new Error(`Could not create data directory ${WORKSCRIBE_DIR}: ${(error as Error).message}`)\n }\n\n // Don't assign to _db until after migrations succeed — if applyMigrations\n // throws, _db stays null so the next call retries cleanly instead of\n // returning a partially-initialised singleton.\n const db = new DatabaseSync(DB_PATH)\n try {\n fs.chmodSync(DB_PATH, 0o600)\n } catch {\n /* ignore if already correct */\n }\n db.exec('PRAGMA journal_mode = WAL')\n db.exec('PRAGMA foreign_keys = ON')\n // Parallel captures (an agent running several commands at once) wait for the write lock instead of failing\n db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`)\n applyMigrations(db, MIGRATIONS)\n _db = db\n return _db\n}\n\n// ─── Typed query helpers ──────────────────────────────────────────────────────\n\nexport function insertEvent(event: NewEvent): void {\n insertEventRecord(getDb(), event)\n}\n\nexport function getLastEventForFile(filePath: string, cwd: string): EventRow | undefined {\n const db = getDb()\n return db\n .prepare('SELECT * FROM events WHERE file_path = $file_path AND cwd = $cwd ORDER BY id DESC LIMIT 1')\n .get({ file_path: filePath, cwd }) as EventRow | undefined\n}\n\nexport function getLastEvent(): EventRow | undefined {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY id DESC LIMIT 1')\n return stmt.get() as EventRow | undefined\n}\n\nexport function getLastEventForCwd(cwd: string): EventRow | undefined {\n const db = getDb()\n return db.prepare('SELECT * FROM events WHERE cwd = $cwd ORDER BY id DESC LIMIT 1').get({ cwd }) as\n | EventRow\n | undefined\n}\n\nexport function getEventsForDate(date: string): EventRow[] {\n const db = getDb()\n const stmt = db.prepare(`\n SELECT * FROM events\n WHERE date(captured_at, 'localtime') = $date\n ORDER BY captured_at ASC, id ASC\n `)\n return stmt.all({ date }) as unknown as EventRow[]\n}\n\n/** Runs `work` holding the database's write lock — see `withImmediateTransaction`. */\nexport function runInImmediateTransaction<T>(work: () => T, options?: TransactionOptions): T {\n return withImmediateTransaction(getDb(), work, options)\n}\n\nexport function getRecentEvents(limit = 20): EventRow[] {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY captured_at DESC LIMIT $limit')\n return stmt.all({ limit }) as unknown as EventRow[]\n}\n\nexport function getEventCount(): number {\n const db = getDb()\n const stmt = db.prepare('SELECT COUNT(*) as count FROM events')\n const row = stmt.get() as { count: number }\n return row.count\n}\n\n/** Saves a thread's summary to `summary`, anchored to the thread's events, under the thread's key. */\nexport function saveThreadSummary(\n threadKey: string,\n summary: { engine: StoredEngine; text: string; anchor: ThreadAnchor }\n): void {\n saveAnchoredSummary(getDb(), {\n sourceKey: threadKey,\n engine: summary.engine,\n text: summary.text,\n createdAt: new Date().toISOString(),\n anchor: summary.anchor,\n anchorSource: THREAD_ANCHOR_SOURCE,\n })\n}\n\nexport function getLastEventIds(n: number): number[] {\n const db = getDb()\n const rows = db.prepare('SELECT id FROM events ORDER BY id DESC LIMIT $n').all({ n }) as { id: number }[]\n return rows.map((row) => row.id)\n}\n\nexport function getEventsByIds(ids: number[]): EventRow[] {\n if (ids.length === 0) return []\n const db = getDb()\n const placeholders = ids.map(() => '?').join(', ')\n return db\n .prepare(`SELECT * FROM events WHERE id IN (${placeholders}) ORDER BY id DESC`)\n .all(...ids) as unknown as EventRow[]\n}\n\n// The old sessions' links to events (migration 001), until the drop-old-sessions data job removes them: their foreign\n// key would refuse to delete an event they point at, so undo and clear remove those links first\nconst OLD_SESSION_LINKS = 'session_events'\n\nfunction hasOldSessionLinks(db: DatabaseSync): boolean {\n return (\n db.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?\").get(OLD_SESSION_LINKS) !== undefined\n )\n}\n\nexport function deleteEventsByIds(ids: number[]): void {\n if (ids.length === 0) return\n const db = getDb()\n const placeholders = ids.map(() => '?').join(', ')\n\n runInImmediateTransaction(() => {\n const agentSessionIds = agentSessionIdsOfEvents(db, ids)\n if (hasOldSessionLinks(db)) {\n db.prepare(`DELETE FROM ${OLD_SESSION_LINKS} WHERE event_id IN (${placeholders})`).run(...ids)\n }\n db.prepare(`DELETE FROM events WHERE id IN (${placeholders})`).run(...ids)\n deleteEmptyAgentSessions(db, agentSessionIds)\n })\n}\n\nexport function deleteEventsForDate(date: string): number {\n const db = getDb()\n return runInImmediateTransaction(() => {\n const agentSessionIds = agentSessionIdsOfDate(db, date)\n if (hasOldSessionLinks(db)) {\n db.prepare(\n `DELETE FROM ${OLD_SESSION_LINKS}\n WHERE event_id IN (SELECT id FROM events WHERE date(captured_at, 'localtime') = $date)`\n ).run({ date })\n }\n const result = db.prepare(`DELETE FROM events WHERE date(captured_at, 'localtime') = $date`).run({ date }) as {\n changes: number\n }\n deleteEmptyAgentSessions(db, agentSessionIds)\n return result.changes\n })\n}\n\nexport function getOtherEventToolCounts(days: number): { tool: string; count: number }[] {\n return countOtherEventTools(getDb(), sinceIso(days))\n}\n\nexport function getRecentOtherRate(days: number): { total: number; otherCount: number } {\n return countOtherEventsSince(getDb(), sinceIso(days))\n}\n","// Patterns that match known sensitive values\nconst BUILTIN_PATTERNS: RegExp[] = [\n // AWS access keys\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access keys (40 char base64-ish after common env vars)\n /(?<=AWS_SECRET_ACCESS_KEY[=\\s]+)\\S+/gi,\n // GitHub tokens\n /ghp_[a-zA-Z0-9]{36}/g,\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // GitLab tokens\n /glpat-[a-zA-Z0-9\\-]{20}/g,\n // Generic JWTs (three base64url segments)\n /eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n // Slack tokens and incoming webhook URLs\n /xox[baprs]-[a-zA-Z0-9\\-]+/g,\n /https:\\/\\/hooks\\.slack\\.com\\/services\\/[A-Z0-9]+\\/[A-Z0-9]+\\/[a-zA-Z0-9]+/g,\n // Stripe keys\n /sk_(live|test)_[a-zA-Z0-9]{24,}/g,\n // Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)\n /(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\\s+)\\S+/gi,\n // export VAR=VALUE where VAR looks sensitive\n /(?<=export\\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\\S+/gi,\n // Inline assignment: VAR_WITH_SENSITIVE_KEYWORD=value cmd (e.g. API_KEY=secret npm start)\n // Lookbehind matches only the variable-name= prefix, so only the value is replaced\n /(?<=[A-Z_][A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z0-9_]*=)\\S+/gi,\n // Credentials embedded in connection string URLs (e.g. DATABASE_URL=postgres://user:pass@host/db)\n // Matches the password segment between :// user : password @ — preserves scheme and host\n /(?<=[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/[^:@\\s/]+:)[^@\\s]+(?=@)/g,\n]\n\nexport function redact(command: string, extraPatterns: string[] = []): string {\n let result = command\n\n const allPatterns = [...BUILTIN_PATTERNS]\n\n for (const pattern of extraPatterns) {\n try {\n allPatterns.push(new RegExp(pattern, 'g'))\n } catch {\n // Invalid regex — skip silently\n }\n }\n\n for (const pattern of allPatterns) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0\n result = result.replace(pattern, '[REDACTED]')\n }\n\n return result\n}\n","// How a repo's `origin` remote becomes a repo name, in both forms `git.qualifyRepoName` chooses between. Pure.\n\n/** The owner and repo parsed from an `origin` remote URL. */\nexport interface RemoteRepo {\n owner: string\n repo: string\n}\n\n// Handles SSH (git@host:owner/repo.git), HTTPS (https://host/owner/repo.git),\n// and bare git:// / ssh:// forms uniformly by normalizing the SSH scp-like\n// syntax's first ':' to '/' (but not the '://' in URLs with a real scheme),\n// then splitting on '/'.\nexport function parseOwnerAndRepo(url: string): RemoteRepo | null {\n const normalized = url.trim().replace(/^[^/]+:(?!\\/\\/)/, (match) => match.replace(':', '/'))\n const segments = normalized.split('/').filter(Boolean)\n if (segments.length < 2) return null\n const repo = segments[segments.length - 1].replace(/\\.git$/, '')\n const owner = segments[segments.length - 2]\n return owner && repo ? { owner, repo } : null\n}\n\n/** `owner/repo` when qualified, bare `repo` otherwise. */\nexport function formatRepoName(remote: RemoteRepo, qualify: boolean): string {\n return qualify ? `${remote.owner}/${remote.repo}` : remote.repo\n}\n","// What makes two events the same project, whatever the repo is called. Pure: shared by capture and the backfill job.\n\n/** `remote`: keyed by the canonical `origin` remote. `local`: no remote, keyed by the git folder. `name`: only a\n * stored name was left to go on (a backfilled event whose folder is gone). */\nexport type ProjectKind = 'remote' | 'local' | 'name'\n\nexport interface ProjectIdentity {\n identity: string\n kind: ProjectKind\n}\n\nconst SCHEME_URL = /^([a-z][a-z0-9+.-]*):\\/\\/(.*)$/i\n// `user@host:path`, git's scp-like form. The host can't hold a `/`, so a local path never matches\nconst SCP_LIKE = /^(?:[^@/]+@)?([^:/]+):(.+)$/\n\nfunction hostAndPath(url: string): { host: string; path: string } | null {\n const schemeMatch = SCHEME_URL.exec(url)\n if (schemeMatch) {\n if (schemeMatch[1].toLowerCase() === 'file') return null\n const rest = schemeMatch[2].replace(/^[^@/]*@/, '')\n const slash = rest.indexOf('/')\n if (slash <= 0) return null\n return { host: rest.slice(0, slash).replace(/:\\d*$/, ''), path: rest.slice(slash + 1) }\n }\n const scpMatch = SCP_LIKE.exec(url)\n return scpMatch ? { host: scpMatch[1], path: scpMatch[2] } : null\n}\n\n/**\n * The remote URL with any user name and password or token removed (`https://user:token@host/…` → `https://host/…`),\n * so nothing stored can hold a credential. The scp-like form (`git@host:path`) can't carry one and is kept as is.\n */\nexport function withoutCredentials(url: string): string {\n const schemeMatch = SCHEME_URL.exec(url.trim())\n if (!schemeMatch) return url.trim()\n const [, scheme, rest] = schemeMatch\n const slash = rest.indexOf('/')\n const authority = slash === -1 ? rest : rest.slice(0, slash)\n const host = authority.slice(authority.lastIndexOf('@') + 1)\n return `${scheme}://${host}${slash === -1 ? '' : rest.slice(slash)}`\n}\n\n/**\n * `host/owner/repo` for any form of the same remote — SSH, scp-like, HTTPS, `git://` — with the user, port and `.git`\n * dropped and every group kept (`gitlab.com/group/sub/repo`). Lower-cased: hosts and the common forges ignore case.\n * Null for a remote with no host, such as a local path.\n */\nexport function canonicalRemote(url: string): string | null {\n const parts = hostAndPath(url.trim())\n if (!parts) return null\n const repoPath = parts.path\n .replace(/^\\/+|\\/+$/g, '')\n .replace(/\\.git$/i, '')\n .replace(/\\/+$/, '')\n if (!parts.host || !repoPath) return null\n return `${parts.host}/${repoPath}`.toLowerCase()\n}\n\n/** The project a repo belongs to: its remote when it has one, else its git folder, which all its worktrees share. */\nexport function projectIdentity(repo: { remoteUrl: string | null; commonDir: string | null }): ProjectIdentity | null {\n const remote = repo.remoteUrl === null ? null : canonicalRemote(repo.remoteUrl)\n if (remote !== null) return { identity: `remote:${remote}`, kind: 'remote' }\n if (repo.commonDir !== null) return { identity: `local:${repo.commonDir}`, kind: 'local' }\n return null\n}\n\n/** The project for a stored name alone, used only when nothing else is left to go on. */\nexport function nameIdentity(name: string): ProjectIdentity {\n return { identity: `name:${name.toLowerCase()}`, kind: 'name' }\n}\n","import { execSync } from 'child_process'\nimport path from 'path'\nimport fs from 'fs'\nimport type { WorkscribeConfig } from '../config'\nimport { formatRepoName, parseOwnerAndRepo, type RemoteRepo } from './repo-name'\nimport { projectIdentity, withoutCredentials, type ProjectIdentity } from './project-identity'\n\n/** Everything known about the repo at a path — `root` and `remote` let `context` find the other name form too. */\nexport interface RepoContext {\n repoName: string | null\n branch: string | null\n root: string | null\n /** The `origin` remote, set only when `repoName` came from it (not an override, package.json or the folder) */\n remote: RemoteRepo | null\n /** The project the repo belongs to, however it's named — null outside a repo */\n project: ProjectIdentity | null\n}\n\n/** What git says about a folder: gone, not in a repo, or the repo's root, git folder and `origin` remote. */\nexport type DirectoryProbe =\n | { outcome: 'missing' | 'not-a-repo' }\n | { outcome: 'repo'; root: string; commonDir: string | null; remoteUrl: string | null }\n\n/** The settings that decide what a repo is called. */\nexport type RepoNamingConfig = Pick<WorkscribeConfig, 'projects' | 'git'>\n\nconst NOT_A_REPO: RepoContext = { repoName: null, branch: null, root: null, remote: null, project: null }\n\nfunction gitOutput(command: string, cwd: string): string | null {\n try {\n return execSync(command, { cwd, timeout: 2000, stdio: 'pipe' }).toString().trim() || null\n } catch {\n return null\n }\n}\n\n/** The `origin` remote, never with a credential in it — the backfill stores it */\nfunction remoteUrlOf(root: string): string | null {\n const url = gitOutput('git remote get-url origin', root)\n return url === null ? null : withoutCredentials(url)\n}\n\n/** The git folder every worktree of the repo shares, resolved, so two paths to one repo give one answer. */\nfunction commonDirOf(directory: string): string | null {\n const commonDir = gitOutput('git rev-parse --git-common-dir', directory)\n if (commonDir === null) return null\n const absolute = path.resolve(directory, commonDir)\n try {\n return fs.realpathSync(absolute)\n } catch {\n return absolute\n }\n}\n\n/** The repo's identity — the git folder is only asked for when there's no usable remote. */\nfunction identityOf(directory: string, remoteUrl: string | null): ProjectIdentity | null {\n return (\n projectIdentity({ remoteUrl, commonDir: null }) ?? projectIdentity({ remoteUrl, commonDir: commonDirOf(directory) })\n )\n}\n\nfunction nameFromPackageJson(root: string): string | null {\n try {\n const raw = fs.readFileSync(path.join(root, 'package.json'), 'utf-8')\n const pkg = JSON.parse(raw) as { name?: string }\n if (!pkg.name) return null\n return pkg.name.replace(/^@[^/]+\\//, '')\n } catch {\n return null\n }\n}\n\nfunction currentBranch(cwd: string): string | null {\n try {\n return (\n execSync('git branch --show-current', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim() || null\n )\n } catch {\n return null // Detached HEAD or not a git repo\n }\n}\n\n/** The repo's name, and the remote it came from — set only when the name did come from the remote. */\nfunction nameRepo(repo: {\n root: string\n nameOverride: string | null\n remoteUrl: string | null\n qualifyRepoName: boolean\n}): Pick<RepoContext, 'repoName' | 'remote'> {\n const { root, nameOverride, remoteUrl, qualifyRepoName } = repo\n if (nameOverride !== null) return { repoName: nameOverride, remote: null }\n const remote = remoteUrl === null ? null : parseOwnerAndRepo(remoteUrl)\n if (remote) return { repoName: formatRepoName(remote, qualifyRepoName), remote }\n return { repoName: nameFromPackageJson(root) ?? path.basename(root), remote: null }\n}\n\n/**\n * The repo at `cwd`, named the way the config says: the `projects.<cwd>` override, else the `origin` remote in the\n * `git.qualifyRepoName` form, else package.json or the folder. Everything that stores or looks up a repo name uses\n * this, so shell, IDE, notes and `context` always agree.\n */\nexport function getRepoContext(cwd: string, config: RepoNamingConfig): RepoContext {\n const root = gitOutput('git rev-parse --show-toplevel', cwd)\n if (root === null) return NOT_A_REPO\n // Read even when an override names the repo: the name may come from config, the identity never does\n const remoteUrl = remoteUrlOf(root)\n const naming = nameRepo({\n root,\n nameOverride: config.projects?.[cwd] ?? null,\n remoteUrl,\n qualifyRepoName: config.git.qualifyRepoName,\n })\n return { ...naming, branch: currentBranch(cwd), root, project: identityOf(cwd, remoteUrl) }\n}\n\n/** What git says about a folder now — the backfill job asks once per folder and keeps the answer. */\nexport function probeDirectory(directory: string): DirectoryProbe {\n if (!fs.existsSync(directory)) return { outcome: 'missing' }\n // A bare repo or a path inside `.git` has no work tree, so it isn't a repo here — as for capture\n const root = gitOutput('git rev-parse --show-toplevel', directory)\n if (root === null) return { outcome: 'not-a-repo' }\n return { outcome: 'repo', root, commonDir: commonDirOf(directory), remoteUrl: remoteUrlOf(root) }\n}\n","import { execSync } from 'child_process'\n\n/**\n * Returns commit subject lines for all commits made between `sha` and HEAD.\n * Returns an empty array if sha is empty, HEAD hasn't moved, or git fails.\n */\nexport function getCommitsSince(sha: string, cwd: string): string[] {\n if (!sha) return []\n try {\n const out = execSync(`git log ${sha}..HEAD --format=%s`, { cwd, timeout: 3000 }).toString().trim()\n return out ? out.split('\\n').filter(Boolean) : []\n } catch {\n return []\n }\n}\n","import fs from 'fs'\nimport path from 'path'\n\nconst HISTORY_FILE = '.aider.chat.history.md'\nconst PROMPT_MAX_LENGTH = 200\n\nexport function readLastAiderPrompt(cwd: string): string | null {\n try {\n const histPath = path.join(cwd, HISTORY_FILE)\n const content = fs.readFileSync(histPath, 'utf-8')\n const matches = [...content.matchAll(/^#### (.+)$/gm)]\n if (!matches.length) return null\n return matches[matches.length - 1][1].trim().slice(0, PROMPT_MAX_LENGTH)\n } catch {\n return null\n }\n}\n","// Recognises Workscribe's own commands inside compound shell input, so a coding agent running the\n// Workscribe skill (`cd repo && workscribe …`, `… | workscribe …`, heredocs) isn't captured as work.\n// Works on the raw command: heredocs and newlines are lost once whitespace is collapsed.\nimport { containsCommandSubstitution, splitOutsideQuotes, startsWithWord } from './shell-syntax'\nimport { commandSteps, isGlueStep, TEXT_SOURCES } from './command-steps'\n\nconst PIPE_SEPARATORS = ['|']\n\n/**\n * A step is Workscribe's when it starts with `workscribe` (whatever it's piped into), or when it only\n * pipes text into it (`echo … | workscribe …`). `npm test | workscribe note done` still ran the tests,\n * and so does `workscribe note \"$(npm test)\"`, so neither is.\n */\nfunction isWorkscribeStep(step: string): boolean {\n if (containsCommandSubstitution(step)) return false\n const stages = splitOutsideQuotes(step, PIPE_SEPARATORS)\n if (startsWithWord(stages[0] ?? '', 'workscribe')) return true\n const isTextOrWorkscribe = (stage: string): boolean =>\n startsWithWord(stage, 'workscribe') || TEXT_SOURCES.some((source) => startsWithWord(stage, source))\n return stages.every(isTextOrWorkscribe) && stages.some((stage) => startsWithWord(stage, 'workscribe'))\n}\n\n/**\n * True when the command does nothing but run Workscribe — possibly wrapped in shell glue (`cd`,\n * `|| true`, `; echo $?`), piped into, or with a heredoc. A command that also does real work\n * (`npm test && workscribe note …`) is not Workscribe-only.\n */\nexport function isWorkscribeOnlyCommand(rawCommand: string): boolean {\n const steps = commandSteps(rawCommand)\n const workSteps = steps.filter((step) => !isGlueStep(step))\n return workSteps.length > 0 && workSteps.every(isWorkscribeStep)\n}\n","import type { EventRow } from '../db/types'\nimport { isWorkscribeOnlyCommand } from './workscribe-command'\nimport { commandSteps, isGlueStep } from './command-steps'\n\nfunction normalizeForFilter(cmd: string): string {\n return cmd.trim().replace(/\\s+/g, ' ')\n}\n\nfunction isIgnoredStep(step: string, ignoreList: string[]): boolean {\n const normalized = normalizeForFilter(step)\n return ignoreList.includes(normalized) || ignoreList.includes(normalized.split(' ')[0])\n}\n\n/**\n * Returns true if the command should be dropped (noise / ignore list).\n *\n * The ignore list applies to each step of a compound command: `cd app && npm test` is kept because\n * `npm test` is real work, while `cd app && ls` is dropped. A step that does nothing on its own (`cd`,\n * `echo`) can't keep a command, but a command made only of such steps is dropped only if one of them is\n * on the ignore list — so `echo hi` on its own is still captured, as before.\n */\nexport function shouldFilter(command: string, ignoreList: string[]): boolean {\n const normalized = normalizeForFilter(command)\n if (!normalized) return true\n\n // Workscribe's own commands — always excluded, not user-configurable. Checked on the raw command so\n // compound ones an agent runs are caught too, while `workscribe note \"$(npm test)\"` still counts as work.\n if (isWorkscribeOnlyCommand(command)) return true\n\n const normalizedIgnore = ignoreList.map(normalizeForFilter)\n if (normalizedIgnore.includes(normalized)) return true\n\n const steps = commandSteps(command)\n const isQuiet = (step: string): boolean => isIgnoredStep(step, normalizedIgnore) || isGlueStep(step)\n return steps.every(isQuiet) && steps.some((step) => isIgnoredStep(step, normalizedIgnore))\n}\n\n/**\n * Returns true if this command is a duplicate of the last event\n * (same command + same cwd within the dedup window).\n */\nexport function isDuplicate(\n command: string,\n cwd: string,\n lastEvent: Pick<EventRow, 'command' | 'cwd' | 'captured_at'> | undefined,\n dedupWindowMs = 5000,\n now = Date.now()\n): boolean {\n if (!lastEvent) return false\n const lastTime = new Date(lastEvent.captured_at).getTime()\n return lastEvent.command === normalizeForFilter(command) && lastEvent.cwd === cwd && now - lastTime < dedupWindowMs\n}\n","// Per-file dedup window for IDE file-save events.\n// Prevents duplicate captures when an IDE auto-saves the same file repeatedly.\nexport const IDE_DEDUP_WINDOW_MS = 30_000\n\n// task_run can double-capture: a shell task running in a hooked terminal\n// fires both this generic event and a richer shell-sourced one, with no\n// shared ID linking them. Shell events land first (precmd fires as the\n// command returns), so a short window here catches the same real action.\nexport const TASK_RUN_DEDUP_WINDOW_MS = 5_000\n\n// Valid event type strings sent by IDE integrations via `workscribe _capture --type <type>`.\nexport const IDE_EVENT_TYPES = new Set(['code_edit', 'debug_start', 'debug_end', 'task_run'])\n\nexport function isIdeEventType(type: string): boolean {\n return IDE_EVENT_TYPES.has(type)\n}\n","// The folder a captured command's work happened in — the repo to credit it to. Pure: paths are only resolved, never\n// looked at on disk.\nimport path from 'path'\nimport { type GitInvocation, parseGitInvocation } from './git-invocation'\n\n/** A literal directory that can be followed — not `~`, `$VAR`, `-` or a substitution, which only the shell expands. */\nfunction isLiteralDirectory(directory: string | null | undefined): directory is string {\n return typeof directory === 'string' && directory !== '' && directory !== '-' && !/[~$`]/.test(directory)\n}\n\n/**\n * The directory a git invocation works in, from `base`: its `-C` chain (each relative to the one before), then its\n * `--work-tree` relative to that — or, without either, the repo a `--git-dir …/.git` belongs to.\n */\nexport function gitDirectoryOf(invocation: GitInvocation, base: string): string {\n if (!invocation.directories.every(isLiteralDirectory)) return base\n const directory = invocation.directories.reduce((current, next) => path.resolve(current, next), base)\n if (invocation.workTree !== null) {\n return isLiteralDirectory(invocation.workTree) ? path.resolve(directory, invocation.workTree) : base\n }\n const { gitDir } = invocation\n if (invocation.directories.length === 0 && isLiteralDirectory(gitDir) && path.basename(gitDir) === '.git') {\n return path.dirname(path.resolve(directory, gitDir))\n }\n return directory\n}\n\n/**\n * Where a captured command's work happened: `cwd`, moved by the matched step's own git options (`-C`,\n * `--work-tree`, `--git-dir`). A `cd` before the step is never followed: every source reports `cwd` as where the\n * shell ended up — the shell hook's `$PWD`, and Claude Code's `cwd`, which keeps a `cd` inside the project (checked\n * against Claude Code 2.1.311). Following it again would apply it twice.\n */\nexport function effectiveDirectory(options: { matchedStep: string; cwd: string }): string {\n const invocation = parseGitInvocation(options.matchedStep)\n return invocation ? gitDirectoryOf(invocation, options.cwd) : options.cwd\n}\n","// Reads the message of a `git commit -F <file>`. Capture runs synchronously on every command and must never hang or\n// throw, so only a small, regular file is read — never a pipe, a device or standard input.\nimport fs from 'fs'\nimport path from 'path'\n\nconst MAX_MESSAGE_FILE_BYTES = 4096\n\n/** The first line git keeps from a message file — non-empty, and not a `#` comment (git strips those). */\nfunction subjectOfMessageFile(text: string): string | null {\n return (\n text\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line !== '' && !line.startsWith('#')) ?? null\n )\n}\n\n/**\n * The subject of a commit message file, resolved against the directory the commit ran in — or null if the file\n * can't be read safely: gone already (an agent's temporary file), not a regular file, or a path with `~`, `$` or a\n * substitution in it.\n */\nexport function readCommitMessageFile(file: string, directory: string): string | null {\n if (/[~$`]/.test(file)) return null\n const resolved = path.resolve(directory, file)\n try {\n if (!fs.lstatSync(resolved).isFile()) return null\n const descriptor = fs.openSync(resolved, 'r')\n try {\n const buffer = Buffer.alloc(MAX_MESSAGE_FILE_BYTES)\n const bytesRead = fs.readSync(descriptor, buffer, 0, MAX_MESSAGE_FILE_BYTES, 0)\n return subjectOfMessageFile(buffer.subarray(0, bytesRead).toString('utf-8'))\n } finally {\n fs.closeSync(descriptor)\n }\n } catch {\n return null // the file vanished between checks, or can't be opened — the git log merge fills the message in\n }\n}\n","import fs from 'fs'\nimport path from 'path'\n\n// The shell hook stores `--cwd \"$PWD\"`: the folder as you reached it, through any symlink (a symlinked `~/code`, or\n// macOS's `/tmp` → `/private/tmp`). Node's `process.cwd()` and `git rev-parse --show-toplevel` give the resolved\n// path. These helpers let `note` and `context` see paths the way capture stored them.\n\nfunction realPath(target: string): string | null {\n try {\n return fs.realpathSync(target)\n } catch {\n return null // the folder is gone, or never existed\n }\n}\n\n/** The path with symlinks resolved, or as given if it doesn't exist (any more). */\nexport function resolvedPath(target: string): string {\n return realPath(target) ?? target\n}\n\n/** The folder you're in, as you reached it: `$PWD` when it's the same folder as `process.cwd()`, else that. */\nexport function currentDirectory(where = { pwd: process.env.PWD, cwd: process.cwd() }): string {\n if (where.pwd && realPath(where.pwd) === realPath(where.cwd)) return where.pwd\n return where.cwd\n}\n\n/**\n * The repo root in every form a stored event's `cwd` may use: git's resolved `root`, and the same folder as reached\n * from `cwd` — so a repo opened through a symlink still matches its own activity.\n */\nexport function rootForms(cwd: string, root: string): string[] {\n const resolvedCwd = realPath(cwd)\n if (!resolvedCwd) return [root]\n const reached = path.resolve(cwd, path.relative(resolvedCwd, root))\n return [...new Set([root, reached])]\n}\n\n/** `target` relative to `root` — `''` for the root itself — or null when it's outside it. Pure: compares paths only. */\nexport function relativeInside(root: string, target: string): string | null {\n const relative = path.relative(root, target)\n const isOutside = relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)\n return isOutside ? null : relative\n}\n\n/**\n * The file's path relative to the repo root, or null when it isn't inside it. The root is tried in each form the\n * file's own folder reaches it by, so a file opened through a symlink still matches.\n */\nexport function pathInsideRoot(filePath: string, root: string): string | null {\n for (const form of rootForms(path.dirname(filePath), root)) {\n const relative = relativeInside(form, filePath)\n if (relative) return relative\n }\n return null\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { getRepoContext, type RepoContext, type RepoNamingConfig } from './git'\nimport { FILE_SAVE_ACTION, type EventDimensions, type ProjectRecord } from '../db/dimensions'\nimport { pathInsideRoot } from '../utils/working-directory'\n\nfunction projectRecord(context: RepoContext): ProjectRecord | null {\n if (!context.project) return null\n return { ...context.project, name: context.repoName, root: context.root }\n}\n\n/** What an event links to, from the repo context capture already found for it. */\nexport function eventDimensions(context: RepoContext): EventDimensions {\n const project = projectRecord(context)\n return { project, branch: project === null ? null : context.branch, file: null }\n}\n\n/**\n * Whether a folder between the file and the root it was found under is a repo of its own — a nested repo or a\n * submodule, which owns the file instead. The root itself isn't checked.\n */\nfunction isInNestedRepo(filePath: string, relativePath: string): boolean {\n const foldersBelowRoot = relativePath.split(path.sep).length - 1\n let folder = path.dirname(filePath)\n for (let level = 0; level < foldersBelowRoot; level++) {\n if (fs.existsSync(path.join(folder, '.git'))) return true\n folder = path.dirname(folder)\n }\n return false\n}\n\n/**\n * What a save links to. The project is the file's own repo, which may not be the folder the editor has open (a\n * workspace above several repos, or a nested repo or submodule inside the open one): git is asked again only then.\n * The branch is the open folder's, so it's only kept when the file is in that repo.\n */\nexport function fileEventDimensions(context: RepoContext, filePath: string, config: RepoNamingConfig): EventDimensions {\n const pathInWorkspaceRepo = context.root === null ? null : pathInsideRoot(filePath, context.root)\n if (pathInWorkspaceRepo !== null && context.project && !isInNestedRepo(filePath, pathInWorkspaceRepo)) {\n return { ...eventDimensions(context), file: { relativePath: pathInWorkspaceRepo, action: FILE_SAVE_ACTION } }\n }\n const fileContext = getRepoContext(path.dirname(filePath), config)\n const project = projectRecord(fileContext)\n if (project === null || fileContext.root === null) return eventDimensions(context)\n const relativePath = pathInsideRoot(filePath, fileContext.root)\n return {\n project,\n branch: null,\n file: relativePath === null ? null : { relativePath, action: FILE_SAVE_ACTION },\n }\n}\n","import fs from 'fs'\nimport { insertEvent, getLastEvent, getLastEventForFile, getLastEventForCwd } from '../db'\nimport { redact } from './redact'\nimport { getRepoContext } from './git'\nimport { getCommitsSince } from './git-snapshot'\nimport { readLastAiderPrompt } from './aider'\nimport { normalizeCommand } from '../normalize/patterns'\nimport { readConfig } from '../config'\nimport { shouldFilter, isDuplicate } from './filter'\nimport { IDE_DEDUP_WINDOW_MS, TASK_RUN_DEDUP_WINDOW_MS } from './ide'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { loadAIToolRegistry } from '../config/registry'\nimport { effectiveDirectory } from '../normalize/git-directory'\nimport { readCommitMessageFile } from './commit-message-file'\nimport { eventDimensions, fileEventDimensions } from './dimensions'\nimport type { AgentSessionRef } from '../db/agent-sessions'\nimport type { EventDimensions } from '../db/dimensions'\nimport type { TicketKeys } from '../db/tickets'\nimport type { WorkscribeConfig } from '../config'\nimport { ticketKeysOf, ticketPatternOf } from './tickets'\nimport type { CommitSource } from '../summary/commits'\n\n/**\n * The ticket keys an event names, with the branch it's linked to — none when it isn't linked to one. A config.json\n * edited to drop `tickets` means the default pattern, never a failed capture.\n */\nfunction ticketsOf(event: CommitSource, dimensions: EventDimensions, config: WorkscribeConfig): TicketKeys {\n return ticketKeysOf({ linkedBranch: dimensions.branch, event }, ticketPatternOf(config.tickets?.pattern))\n}\n\n/**\n * `git commit -F <file>`: the message, read now while the file may still exist and redacted like the command — and\n * never the path, which isn't stored.\n */\nfunction withMessageFromFile(\n metadata: Record<string, unknown>,\n workDirectory: string,\n extraRedactions: string[]\n): Record<string, unknown> {\n const { messageFile, ...rest } = metadata\n if (typeof messageFile !== 'string') return metadata\n const message = readCommitMessageFile(messageFile, workDirectory)\n return { ...rest, message: message ? redact(message, extraRedactions) : null }\n}\n\nexport function captureCommand(opts: {\n command?: string\n cwd: string\n exitCode: number | null\n durationMs: number | null\n gitBefore?: string\n source?: string\n type?: string\n file?: string\n /** The agent conversation a hook command ran in; only shell commands from a hook have one */\n agentSession?: AgentSessionRef\n}): void {\n // Check pause flag before any I/O\n if (fs.existsSync(PAUSE_FLAG_PATH)) return\n\n const config = readConfig()\n const { cwd, exitCode, durationMs } = opts\n\n // ─── IDE event path ──────────────────────────────────────────────────────────\n // When --type is provided, the caller has already classified the event.\n // Skip normalization and noise filtering — these are first-party signals.\n if (opts.type) {\n // For file-save events, deduplicate per-file with a longer window (30s) to\n // absorb rapid auto-saves of the same file.\n if (opts.type === 'code_edit' && opts.file) {\n const lastForFile = getLastEventForFile(opts.file, cwd)\n if (lastForFile) {\n const lastTime = new Date(lastForFile.captured_at).getTime()\n if (Date.now() - lastTime < IDE_DEDUP_WINDOW_MS) return\n }\n }\n\n // A shell task running in a hooked terminal already got captured with a\n // real category by the shell hook — skip the generic duplicate.\n if (opts.type === 'task_run') {\n const lastForCwd = getLastEventForCwd(cwd)\n if (lastForCwd && !lastForCwd.source) {\n const lastTime = new Date(lastForCwd.captured_at).getTime()\n if (Date.now() - lastTime < TASK_RUN_DEDUP_WINDOW_MS) return\n }\n }\n\n const command = opts.command ?? opts.file ?? opts.type\n const context = getRepoContext(cwd, config)\n const { repoName, branch } = context\n const ideEvent = {\n captured_at: new Date().toISOString(),\n command,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category: opts.type,\n metadata: null,\n source: opts.source ?? null,\n file_path: opts.file ?? null,\n }\n const dimensions = opts.file ? fileEventDimensions(context, opts.file, config) : eventDimensions(context)\n insertEvent({ ...ideEvent, dimensions, tickets: ticketsOf(ideEvent, dimensions, config) })\n return\n }\n\n // ─── Shell event path ─────────────────────────────────────────────────────────\n const command = opts.command as string\n\n // 1. Filter noise commands\n if (shouldFilter(command, config.capture.ignore)) return\n\n // 2. Deduplicate: same command + same cwd within 5 seconds\n const last = getLastEvent()\n if (isDuplicate(command, cwd, last ?? undefined)) return\n\n // 3. Redact sensitive patterns\n const safeCommand = redact(command, config.redact.extra)\n\n // 4. Normalize to typed event — first, because the repo depends on which step did the work\n const { category, metadata: normalizedMetadata, matchedStep } = normalizeCommand(safeCommand)\n\n if (category === 'ai_session' && config.capture.aiTools === false) return\n\n // 5. Extract git context from where the work happened — `git -C ../web commit` belongs to web, not here — named\n // exactly as the IDE path, notes and `context` name it\n const workDirectory = effectiveDirectory({ matchedStep: matchedStep ?? safeCommand, cwd })\n const context = getRepoContext(workDirectory, config)\n const { repoName, branch } = context\n const metadata = withMessageFromFile(normalizedMetadata, workDirectory, config.redact.extra)\n\n // Hint passthrough: preserve context for unrecognised commands so the AI can describe them\n if (category === 'other') {\n const firstToken = safeCommand.trim().split(/\\s+/)[0]\n if (firstToken) metadata.tool = firstToken\n metadata.hint = safeCommand.trim().slice(0, 120)\n }\n\n // 6. For ai_session events, enrich metadata\n if (category === 'ai_session') {\n // 6a. Commits made during the session\n if (opts.gitBefore) {\n const commits = getCommitsSince(opts.gitBefore, workDirectory)\n if (commits.length > 0) metadata.commits = commits\n }\n\n // 6b. Last user prompt from history file (e.g. Aider)\n if (config.capture.includeAiderPrompt !== false) {\n const toolEntry = loadAIToolRegistry().find((entry) => entry.tool === metadata.tool)\n if (toolEntry?.contextSource === 'history-file') {\n const prompt = readLastAiderPrompt(cwd)\n if (prompt) metadata.prompt = prompt\n }\n }\n }\n\n // 7. Persist\n const shellEvent = {\n captured_at: new Date().toISOString(),\n command: safeCommand,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category,\n metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,\n source: opts.source ?? null,\n file_path: null,\n }\n const dimensions = eventDimensions(context)\n insertEvent({\n ...shellEvent,\n dimensions,\n tickets: ticketsOf(shellEvent, dimensions, config),\n agentSession: opts.agentSession,\n })\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { debugLog } from '../utils/logger'\n\n// Ends a capture that is still alive this long after starting. The capture itself is synchronous, so the timer can\n// only fire once it has finished: it never cuts short a wait for the database lock (busy_timeout, also 5 s).\nconst CAPTURE_HANG_TIMEOUT_MS = 5_000\n\nexport function registerCapture(program: Command): void {\n // Internal command — called by the shell hook and IDE integrations, not by the user\n program\n .command('_capture')\n .description('Internal: capture a shell command or IDE event (called by shell hook or IDE plugin)')\n .option('--cmd <command>', 'command that was executed (shell events)')\n .option('--cwd <path>', 'working directory')\n .option('--exit <code>', 'exit code', parseInt)\n .option('--duration <ms>', 'duration in milliseconds', parseInt)\n .option('--git-before <sha>', 'git HEAD SHA before this command ran')\n .option('--type <type>', 'event type override — skips normalization (IDE integrations)')\n .option('--source <source>', 'event source identifier (e.g. vscode, neovim, jupyter)')\n .option('--file <path>', 'file path for code_edit events')\n .action((opts) => {\n // Prevent background process accumulation if the process hangs (e.g. SQLite locked).\n // unref() allows a natural exit to happen immediately without waiting for the timer.\n const hangGuard = setTimeout(() => process.exit(0), CAPTURE_HANG_TIMEOUT_MS)\n hangGuard.unref()\n\n // Never surface errors to the terminal — this runs silently in the background\n try {\n if (!opts.cwd) return\n // Require either a shell command or a typed IDE event\n if (!opts.cmd && !opts.type) return\n captureCommand({\n command: opts.cmd,\n cwd: opts.cwd,\n exitCode: opts.exit ?? null,\n durationMs: opts.duration ?? null,\n gitBefore: opts.gitBefore ?? undefined,\n source: opts.source ?? undefined,\n type: opts.type ?? undefined,\n file: opts.file ?? undefined,\n })\n } catch (err) {\n debugLog('_capture', err)\n }\n })\n}\n","import { redact } from './redact'\n\nexport function containsSecrets(text: string): boolean {\n return redact(text) !== text\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { getDb } from './index'\nimport { ALERTS_PENDING_PATH } from '../utils/paths'\nimport { ensurePrivateDirectory } from '../utils/private-directory'\n\nexport interface AlertRow {\n id: number\n created_at: number\n tool: string\n session_id: string | null\n context: string\n cleared: number\n}\n\nexport function insertAlert(tool: string, sessionId: string | null, context: string): void {\n getDb()\n .prepare(\n `INSERT INTO alerts (created_at, tool, session_id, context)\n VALUES ($created_at, $tool, $session_id, $context)`\n )\n .run({ created_at: Date.now(), tool, session_id: sessionId ?? null, context })\n\n ensurePrivateDirectory(path.dirname(ALERTS_PENDING_PATH))\n fs.writeFileSync(ALERTS_PENDING_PATH, '', { flag: 'w' })\n}\n\nexport function getPendingAlerts(): AlertRow[] {\n return getDb()\n .prepare('SELECT * FROM alerts WHERE cleared = 0 ORDER BY created_at DESC')\n .all() as unknown as AlertRow[]\n}\n\nexport function hasPendingAlerts(): boolean {\n const row = getDb().prepare('SELECT COUNT(*) as count FROM alerts WHERE cleared = 0').get() as {\n count: number | bigint\n }\n return Number(row.count) > 0\n}\n\nexport function clearAlerts(): number {\n const result = getDb().prepare('UPDATE alerts SET cleared = 1 WHERE cleared = 0').run()\n try {\n fs.unlinkSync(ALERTS_PENDING_PATH)\n } catch {\n // File may already be absent\n }\n return Number(result.changes)\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { readConfig } from '../config'\nimport { debugLog } from '../utils/logger'\nimport { containsSecrets } from '../capture/detect-secrets'\nimport { insertAlert } from '../db/alerts'\n\n/** The Claude Code events that report a finished Bash command — the success and the failure halves. */\nexport const TOOL_EVENTS = ['PostToolUse', 'PostToolUseFailure'] as const\nexport type ToolEvent = (typeof TOOL_EVENTS)[number]\n\nexport interface HookCommand {\n command: string\n cwd: string\n /** 0 for a success, the real code for a failure, null when it's unknown (an interrupted call) */\n exitCode: number | null\n durationMs: number | null\n /** The agent conversation the command ran in — the same across every folder it worked in. Null if not sent */\n sessionId: string | null\n}\n\n// PostToolUseFailure's `error` starts with this for a Bash command that ran and exited\nconst EXIT_CODE_LINE = /^Exit code (\\d+)/\n\n/**\n * The exit code an event implies. Claude Code fires PostToolUse only when a Bash call succeeds and\n * PostToolUseFailure only when it fails (checked against Claude Code 2.1.311 — see\n * tests/fixtures/claude-code-hooks/). Only the first line of `error` is read; its output never is.\n */\nfunction exitCodeOf(event: ToolEvent, payload: Record<string, unknown>): number | null {\n if (event === 'PostToolUse') return 0\n if (payload.is_interrupt === true || typeof payload.error !== 'string') return null\n const exitCode = payload.error.match(EXIT_CODE_LINE)?.[1]\n return exitCode === undefined ? null : Number(exitCode)\n}\n\n/** Parses a Claude Code PostToolUse or PostToolUseFailure stdin payload. Returns null if required fields are missing. */\nexport function parseHookPayload(raw: string, event: ToolEvent = 'PostToolUse'): HookCommand | null {\n if (!raw.trim()) return null\n const payload = JSON.parse(raw) as Record<string, unknown>\n // The hook's matcher is `Bash`, but settings can be edited: exit codes only mean something for shell commands\n if (payload.tool_name !== undefined && payload.tool_name !== 'Bash') return null\n const toolInput = payload.tool_input as Record<string, unknown> | undefined\n const command = typeof toolInput?.command === 'string' ? toolInput.command : null\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : null\n if (!command || !cwd) return null\n const durationMs = typeof payload.duration_ms === 'number' ? payload.duration_ms : null\n const sessionId = typeof payload.session_id === 'string' && payload.session_id !== '' ? payload.session_id : null\n return { command, cwd, exitCode: exitCodeOf(event, payload), durationMs, sessionId }\n}\n\nfunction isToolEvent(event: string): event is ToolEvent {\n return (TOOL_EVENTS as readonly string[]).includes(event)\n}\n\ninterface UserPromptPayload {\n session_id: string\n prompt: string\n cwd: string\n}\n\n/** Parses a Claude Code UserPromptSubmit stdin payload. Returns null if required fields are missing. */\nexport function parseUserPromptPayload(raw: string): UserPromptPayload | null {\n if (!raw.trim()) return null\n const payload = JSON.parse(raw) as Record<string, unknown>\n const prompt = typeof payload.prompt === 'string' ? payload.prompt : null\n const sessionId = typeof payload.session_id === 'string' ? payload.session_id : ''\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : ''\n if (!prompt) return null\n return { session_id: sessionId, prompt, cwd }\n}\n\nexport function registerHook(program: Command): void {\n // Internal command — called by AI tool hooks (e.g. Claude Code PostToolUse), not by the user\n program\n .command('_hook')\n .description('Internal: capture events from AI tool hooks (stdin JSON payload)')\n .option('--source <source>', 'hook source identifier (e.g. claude-code)')\n .option('--event <event>', 'hook event type (PostToolUse, PostToolUseFailure or UserPromptSubmit)', 'PostToolUse')\n .action((opts: { source?: string; event: string }) => {\n let raw = ''\n process.stdin.setEncoding('utf-8')\n process.stdin.on('data', (chunk: string) => {\n raw += chunk\n })\n process.stdin.on('end', () => {\n try {\n if (opts.event === 'UserPromptSubmit') {\n const parsed = parseUserPromptPayload(raw)\n if (!parsed) return\n if (containsSecrets(parsed.prompt)) {\n insertAlert(\n opts.source ?? 'unknown',\n parsed.session_id || null,\n 'Possible credential detected in prompt submitted to AI tool'\n )\n // Claude Code reads this JSON and injects systemMessage into Claude's context\n process.stdout.write(\n JSON.stringify({\n systemMessage:\n 'Workscribe detected a possible credential in your prompt. ' +\n 'Run `workscribe alerts` for details.',\n })\n )\n }\n return\n }\n\n // A finished Bash command, successful or not — anything else is an event this version doesn't handle\n if (!isToolEvent(opts.event)) return\n if (readConfig().capture.aiTools === false) return\n const parsed = parseHookPayload(raw, opts.event)\n if (!parsed) return\n const { sessionId, ...command } = parsed\n // Only with a source, so the event and its session always name the same agent\n const agentSession = sessionId && opts.source ? { source: opts.source, sessionId } : undefined\n captureCommand({ ...command, source: opts.source, agentSession })\n } catch (err) {\n debugLog('_hook', err)\n }\n })\n })\n}\n","export interface CompletionCommand {\n name: string\n description: string\n}\n\nexport function bashCompletion(commands: CompletionCommand[]): string {\n const names = commands.map((command) => command.name).join(' ')\n return `\\\n_workscribe_completion() {\n local cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n local prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n local commands=\"${names}\"\n case \"$prev\" in\n completions) COMPREPLY=( $(compgen -W \"bash zsh fish\" -- \"$cur\") ); return ;;\n config) COMPREPLY=( $(compgen -W \"get set list\" -- \"$cur\") ); return ;;\n profile) COMPREPLY=( $(compgen -W \"list add remove update detect suggest\" -- \"$cur\") ); return ;;\n esac\n COMPREPLY=( $(compgen -W \"$commands\" -- \"$cur\") )\n}\ncomplete -F _workscribe_completion workscribe`\n}\n\nexport function zshCompletion(commands: CompletionCommand[]): string {\n const commandList = commands.map((command) => ` '${command.name}:${command.description}'`).join('\\n')\n return `\\\n#compdef workscribe\n_workscribe() {\n local state\n _arguments '1: :->command' '*: :->args'\n case $state in\n command)\n local -a commands\n commands=(\n${commandList}\n )\n _describe 'command' commands\n ;;\n args)\n case $words[2] in\n completions) local -a shells; shells=('bash' 'zsh' 'fish'); _describe 'shell' shells ;;\n config) local -a sub; sub=('get:Get a value' 'set:Set a value' 'list:List all values'); _describe 'subcommand' sub ;;\n profile) local -a sub; sub=('list' 'add' 'remove' 'update' 'detect' 'suggest'); _describe 'subcommand' sub ;;\n esac\n ;;\n esac\n}\ncompdef _workscribe workscribe`\n}\n\nexport function fishCompletion(commands: CompletionCommand[]): string {\n const lines = [\n 'complete -c workscribe -f',\n ...commands.map(\n (command) =>\n `complete -c workscribe -n \"__fish_use_subcommand\" -a ${command.name.padEnd(12)} -d \"${command.description}\"`\n ),\n 'complete -c workscribe -n \"__fish_seen_subcommand_from completions\" -a \"bash zsh fish\"',\n 'complete -c workscribe -n \"__fish_seen_subcommand_from config\" -a \"get set list\"',\n 'complete -c workscribe -n \"__fish_seen_subcommand_from profile\" -a \"list add remove update detect suggest\"',\n ]\n return lines.join('\\n')\n}\n","import fs from 'fs'\n\n// Far more than two edits in one millisecond ever need; a bound so a broken file system can't loop forever\nconst MAX_BACKUP_ATTEMPTS = 100\n\n/**\n * Copies a file the user owns to `<file>.workscribe-backup-<time>` before Workscribe edits it, and returns the\n * backup's path. Never overwrites an earlier backup — two edits in the same millisecond (say the shell hook and\n * the completions block, one after the other) each keep their own copy.\n */\nexport function backUpFile(file: string): string {\n const base = `${file}.workscribe-backup-${Date.now()}`\n for (let attempt = 1; attempt <= MAX_BACKUP_ATTEMPTS; attempt++) {\n const backup = attempt === 1 ? base : `${base}-${attempt}`\n try {\n fs.copyFileSync(file, backup, fs.constants.COPYFILE_EXCL)\n return backup\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\n }\n }\n throw new Error(`Couldn't back up ${file}: too many backups named ${base}`)\n}\n","// The `# Workscribe completions` block `init` adds to ~/.zshrc / ~/.bashrc. Pure text handling, so the rc-file\n// I/O in shell-hook.ts stays thin. The block guards its `source` line, so it does nothing — and prints nothing —\n// once the completion script is gone (e.g. after the package and its data folder are removed).\n//\n// This code edits the user's own rc files, so it only ever touches lines in the exact shapes Workscribe wrote:\n// a marker line directly followed by the line(s) below, sourcing Workscribe's own `completion.<shell>` script.\n// A marker on its own, or followed by anything else, is left alone.\n\nexport const COMPLETIONS_MARKER = '# Workscribe completions'\n\nconst ZSH_COMPINIT = 'autoload -Uz compinit && compinit'\n// Workscribe's own script: `<data folder>/completion.zsh` or `completion.bash`\nconst OWN_SCRIPT = '\"[^\"]*completion\\\\.(?:zsh|bash)\"'\n// The line(s) under the marker, in every shape Workscribe has written\nconst GUARDED_ZSH = new RegExp(`^if \\\\[ -f ${OWN_SCRIPT} \\\\]; then ${ZSH_COMPINIT}; source ${OWN_SCRIPT}; fi$`)\nconst GUARDED_BASH = new RegExp(`^\\\\[ -f ${OWN_SCRIPT} \\\\] && source ${OWN_SCRIPT}$`)\nconst UNGUARDED_SOURCE = new RegExp(`^source ${OWN_SCRIPT}$`)\n\n/** The block for a shell; `file` is the generated completion script it sources. */\nexport function completionsBlock(shellName: string, file: string): string {\n const guardedSource =\n shellName === 'zsh'\n ? `if [ -f \"${file}\" ]; then ${ZSH_COMPINIT}; source \"${file}\"; fi`\n : `[ -f \"${file}\" ] && source \"${file}\"`\n return `${COMPLETIONS_MARKER}\\n${guardedSource}`\n}\n\n/** Lines of a block: `first` is the marker line, `last` the line that sources the script (inclusive). */\ninterface BlockLines {\n first: number\n last: number\n}\n\n/** A file's lines, compared without a Windows `\\r` but written back with it. */\ninterface RcLines {\n lines: string[]\n lineEnding: '' | '\\r'\n}\n\nfunction splitLines(content: string): RcLines {\n return { lines: content.split('\\n'), lineEnding: content.includes('\\r\\n') ? '\\r' : '' }\n}\n\nfunction bare(line: string | undefined): string {\n return (line ?? '').replace(/\\r$/, '')\n}\n\n/** How many lines after the marker belong to the block, or 0 if what follows isn't a Workscribe block. */\nfunction blockLengthAfterMarker(lines: string[], markerIndex: number): number {\n const next = bare(lines[markerIndex + 1])\n if (GUARDED_ZSH.test(next) || GUARDED_BASH.test(next) || UNGUARDED_SOURCE.test(next)) return 1\n if (next === ZSH_COMPINIT && UNGUARDED_SOURCE.test(bare(lines[markerIndex + 2]))) return 2\n return 0\n}\n\n/** Every Workscribe completions block — older versions of `init` could append more than one. */\nfunction findBlocks(lines: string[]): BlockLines[] {\n const blocks: BlockLines[] = []\n for (let index = 0; index < lines.length; index++) {\n if (bare(lines[index]) !== COMPLETIONS_MARKER) continue\n const length = blockLengthAfterMarker(lines, index)\n if (length === 0) continue\n blocks.push({ first: index, last: index + length })\n index += length\n }\n return blocks\n}\n\nfunction blockText(lines: string[], block: BlockLines): string {\n return lines\n .slice(block.first, block.last + 1)\n .map(bare)\n .join('\\n')\n}\n\nexport function hasCompletionsBlock(content: string): boolean {\n return findBlocks(splitLines(content).lines).length > 0\n}\n\n/** There's a block that isn't the current one (typically an unguarded block from an older version), or several. */\nexport function isCompletionsBlockOutdated(content: string, currentBlock: string): boolean {\n const { lines } = splitLines(content)\n const blocks = findBlocks(lines)\n if (blocks.length === 0) return false\n return blocks.length > 1 || blockText(lines, blocks[0]) !== currentBlock\n}\n\n/**\n * Rebuilds the file from its lines: `replacement` takes the place of the first block, the other blocks go — each\n * with the one blank line `installCompletions` put before it — and every other line stays exactly as it was.\n */\nfunction rebuild(content: string, replacement: string | null): string {\n const { lines, lineEnding } = splitLines(content)\n const blocks = findBlocks(lines)\n if (blocks.length === 0) return content\n const dropped = new Set<number>()\n blocks.forEach((block, blockIndex) => {\n const isReplaced = blockIndex === 0 && replacement !== null\n for (let index = block.first; index <= block.last; index++) dropped.add(index)\n if (!isReplaced && block.first > 0 && bare(lines[block.first - 1]) === '') dropped.add(block.first - 1)\n })\n const replacementLines = replacement === null ? [] : replacement.split('\\n').map((line) => line + lineEnding)\n return lines\n .flatMap((line, index) => {\n if (replacement !== null && index === blocks[0].first) return replacementLines\n return dropped.has(index) ? [] : [line]\n })\n .join('\\n')\n}\n\n/** Replaces the first block with `newBlock` and removes any duplicates. */\nexport function replaceCompletionsBlock(content: string, newBlock: string): string {\n return rebuild(content, newBlock)\n}\n\n/** Removes every block (and the blank line before each), leaving the rest of the file as it was. */\nexport function removeCompletionsBlock(content: string): string {\n return rebuild(content, null)\n}\n","import fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { execFileSync } from 'child_process'\nimport { log } from '../utils/logger'\nimport { CompletionCommand, bashCompletion, zshCompletion, fishCompletion } from '../utils/completion-scripts'\nimport { WORKSCRIBE_DIR } from '../utils/paths'\nimport { ensurePrivateDirectory } from '../utils/private-directory'\nimport { backUpFile } from '../utils/backup'\nimport {\n COMPLETIONS_MARKER,\n completionsBlock,\n hasCompletionsBlock,\n isCompletionsBlockOutdated,\n removeCompletionsBlock,\n replaceCompletionsBlock,\n} from './completions-block'\n\nconst FISH_COMPLETIONS_DIR = path.join(os.homedir(), '.config', 'fish', 'completions')\n\nconst HOOK_START = '# workscribe:hook:start'\nconst HOOK_END = '# workscribe:hook:end'\nconst HOOK_PREAMBLE = '# Workscribe — developer work journal'\n\n/** A shell and the rc file `init` writes its hook (and completions) into. */\nexport type ShellTarget = { name: string; rcFile: string }\n\n/** Backs up an rc file before editing it, never overwriting an earlier backup, and says where it went. */\nfunction backUp(rcFile: string): void {\n log.info(`Backed up ${rcFile} → ${path.basename(backUpFile(rcFile))}`)\n}\n\nexport function getInstalledHookBlock(rcFile: string): string | null {\n if (!fs.existsSync(rcFile)) return null\n const content = fs.readFileSync(rcFile, 'utf-8')\n const startIdx = content.indexOf(HOOK_START)\n const endIdx = content.indexOf(HOOK_END)\n if (startIdx === -1 || endIdx === -1) return null\n return content.slice(startIdx, endIdx + HOOK_END.length)\n}\n\nexport function isHookInstalled(rcFile: string): boolean {\n return getInstalledHookBlock(rcFile) !== null\n}\n\nexport function isHookOutdated(rcFile: string, hookContent: string): boolean {\n const installed = getInstalledHookBlock(rcFile)\n if (!installed) return false\n return installed.trim() !== hookContent.trim()\n}\n\nexport function installHook(rcFile: string, hookContent: string): void {\n backUp(rcFile)\n fs.appendFileSync(rcFile, `\\n${hookContent}\\n`)\n}\n\nexport function updateHook(rcFile: string, hookContent: string): void {\n backUp(rcFile)\n const content = fs.readFileSync(rcFile, 'utf-8')\n const startIdx = content.indexOf(HOOK_START)\n const endIdx = content.indexOf(HOOK_END) + HOOK_END.length\n fs.writeFileSync(rcFile, content.slice(0, startIdx) + hookContent + content.slice(endIdx))\n}\n\nexport function removeHook(rcFile: string): void {\n backUp(rcFile)\n const content = fs.readFileSync(rcFile, 'utf-8')\n const startIdx = content.indexOf(HOOK_START)\n const endIdx = content.indexOf(HOOK_END) + HOOK_END.length\n // Also remove the preamble comment lines that installHook writes before HOOK_START\n const preambleIdx = content.lastIndexOf(HOOK_PREAMBLE, startIdx)\n const blockStart = preambleIdx !== -1 ? preambleIdx : startIdx\n const before = content.slice(0, blockStart).replace(/\\n+$/, '\\n')\n const after = content.slice(endIdx).replace(/^\\n/, '')\n fs.writeFileSync(rcFile, before + after)\n}\n\nexport function resolveWorkscribeBinary(): string | null {\n try {\n const resolved = execFileSync('which', ['workscribe'], { encoding: 'utf-8' }).trim()\n return resolved || null\n } catch {\n return null\n }\n}\n\n/**\n * nodePath pins the Node interpreter alongside the workscribe script path.\n * Without it, workscribe's own shebang (`#!/usr/bin/env -S node ...`) still\n * dynamically resolves `node` via whatever PATH is active in the shell at\n * capture time — so a hardcoded binaryPath alone doesn't protect against a\n * stale/wrong default Node version in that shell, only against the\n * workscribe script itself moving. nodePath should be process.execPath,\n * captured at `workscribe init` time when it's guaranteed to be 22+.\n */\nexport function injectBinaryPath(hookContent: string, binaryPath: string, nodePath: string): string {\n // Invoking node directly (rather than executing workscribe's own shebang)\n // bypasses the `--disable-warning=ExperimentalWarning` flag postbuild.js\n // bakes into dist/index.js's shebang — shebang flags only apply when the\n // OS execs the file via its shebang line, not when node is invoked\n // explicitly. Without repeating the flag here, every pinned-node-path\n // invocation (the default whenever a version manager is detected) leaks\n // the node:sqlite ExperimentalWarning to stderr.\n const pinnedInvocation = `\"${nodePath}\" --disable-warning=ExperimentalWarning \"${binaryPath}\"`\n return hookContent\n .replace('command -v workscribe > /dev/null 2>&1 || return 0', `[[ -x \"${binaryPath}\" ]] || return 0`)\n .replace('command -v workscribe &>/dev/null || return 0', `[[ -x \"${binaryPath}\" ]] || return 0`)\n .replace('type -q workscribe\\n or return 0', `test -x \"${binaryPath}\"\\n or return 0`)\n .replaceAll('command workscribe _capture', `${pinnedInvocation} _capture`)\n .replaceAll('command workscribe alerts', `${pinnedInvocation} alerts`)\n}\n\nexport function isCompletionsInstalled(shell: ShellTarget): boolean {\n if (shell.name === 'fish') {\n return fs.existsSync(path.join(FISH_COMPLETIONS_DIR, 'workscribe.fish'))\n }\n try {\n return fs.readFileSync(shell.rcFile, 'utf-8').includes(COMPLETIONS_MARKER)\n } catch {\n return false\n }\n}\n\nconst FISH_COMPLETIONS_FILE = path.join(FISH_COMPLETIONS_DIR, 'workscribe.fish')\n\nfunction completionScriptFile(shellName: string): string {\n return path.join(WORKSCRIBE_DIR, `completion.${shellName}`)\n}\n\nfunction readRcFile(rcFile: string): string | null {\n try {\n return fs.readFileSync(rcFile, 'utf-8')\n } catch {\n return null // a missing rc file simply has no completions block\n }\n}\n\n/** (Re)writes the completion script itself — safe to run on every `init`, so a deleted data folder is repaired. */\nexport function writeCompletionScript(shell: ShellTarget, commands: CompletionCommand[]): void {\n if (shell.name === 'fish') {\n fs.mkdirSync(FISH_COMPLETIONS_DIR, { recursive: true })\n fs.writeFileSync(FISH_COMPLETIONS_FILE, fishCompletion(commands), { mode: 0o644 })\n return\n }\n const script = shell.name === 'zsh' ? zshCompletion(commands) : bashCompletion(commands)\n ensurePrivateDirectory(WORKSCRIBE_DIR)\n fs.writeFileSync(completionScriptFile(shell.name), script, { mode: 0o644 })\n}\n\nexport function installCompletions(shell: ShellTarget, commands: CompletionCommand[]): void {\n writeCompletionScript(shell, commands)\n // installCompletions can be called more than once (e.g. isCompletionsInstalled raced with a concurrent init).\n // The rcFile append must stay idempotent so repeated calls never grow duplicate blocks.\n if (shell.name !== 'fish' && !isCompletionsInstalled(shell)) {\n fs.appendFileSync(shell.rcFile, `\\n${completionsBlock(shell.name, completionScriptFile(shell.name))}\\n`)\n }\n}\n\n/** The rc file has a completions block from an older version — one that errors once its script is deleted. */\nexport function isCompletionsOutdated(shell: ShellTarget): boolean {\n if (shell.name === 'fish') return false // fish completions are a standalone file, not an rc-file line\n const content = readRcFile(shell.rcFile)\n return (\n content !== null &&\n isCompletionsBlockOutdated(content, completionsBlock(shell.name, completionScriptFile(shell.name)))\n )\n}\n\nexport function updateCompletions(shell: ShellTarget): void {\n const content = readRcFile(shell.rcFile)\n if (content === null || !hasCompletionsBlock(content)) return\n backUp(shell.rcFile)\n fs.writeFileSync(\n shell.rcFile,\n replaceCompletionsBlock(content, completionsBlock(shell.name, completionScriptFile(shell.name)))\n )\n}\n\n/** Removes the completions block from an rc file, if it has one. Returns whether anything was removed. */\nexport function removeCompletions(rcFile: string): boolean {\n const content = readRcFile(rcFile)\n if (content === null || !hasCompletionsBlock(content)) return false\n backUp(rcFile)\n fs.writeFileSync(rcFile, removeCompletionsBlock(content))\n return true\n}\n\n/** Removes the fish completion file, if present. Returns whether anything was removed. */\nexport function removeFishCompletions(): boolean {\n if (!fs.existsSync(FISH_COMPLETIONS_FILE)) return false\n fs.rmSync(FISH_COMPLETIONS_FILE, { force: true })\n return true\n}\n","import { execSync } from 'child_process'\n\n/** Returns true if the given CLI command is available on PATH */\nexport function isCommandInstalled(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'ignore' })\n return true\n } catch {\n return false\n }\n}\n","// The command strings Workscribe writes into ~/.claude/settings.json, and how to read them back. Pure.\n//\n// Every command is guarded: when the binary (or the Node it's pinned to) is gone — say the package was removed\n// without `workscribe uninstall` — the hook exits 0 silently instead of erroring on every prompt and tool call.\n// `_hook` itself never exits non-zero, so the trailing `|| true` only ever hides a missing binary or a crash.\n\n// Identifies a workscribe hook entry in any of its forms — legacy bare, pinned, guarded — since all contain it\nexport const HOOK_MARKER = '_hook --source claude-code'\n\nconst GUARD_SUFFIX = ' || true'\n// The `test -x \"<path>\" && ` guards in front of a pinned command\nconst LEADING_PATH_GUARDS = /^(?:test -x \"[^\"]*\" && )+/\nconst BARE_GUARD = 'command -v workscribe >/dev/null 2>&1 && '\n\n/**\n * The command for one hook. Pinned (node and script paths known at install time) when possible, so it's immune to\n * PATH and Node-version drift; bare (`workscribe …`) otherwise. `args` follow the binary, e.g. `_hook --source …`.\n */\nexport function buildHookCommand(options: { nodePath: string; binaryPath: string | null; args: string }): string {\n if (!options.binaryPath) return `${BARE_GUARD}workscribe ${options.args}${GUARD_SUFFIX}`\n const { nodePath, binaryPath, args } = options\n return `test -x \"${nodePath}\" && test -x \"${binaryPath}\" && \"${nodePath}\" \"${binaryPath}\" ${args}${GUARD_SUFFIX}`\n}\n\nfunction withoutLeadingGuards(command: string): string {\n return command.trimStart().replace(LEADING_PATH_GUARDS, '')\n}\n\n/**\n * Pinned: invokes an absolute path (a Node interpreter) rather than the bare `workscribe` string — immune to\n * PATH/Node-version drift between install time and whatever environment the hook fires in.\n */\nexport function isPinnedCommand(command: string): boolean {\n const invocation = withoutLeadingGuards(command)\n return invocation.startsWith('\"') || invocation.startsWith('/')\n}\n\n/** Guarded: does nothing, silently, when the binary is gone. */\nexport function isGuardedCommand(command: string): boolean {\n const trimmed = command.trim()\n const hasLeadingGuard = LEADING_PATH_GUARDS.test(trimmed) || trimmed.startsWith(BARE_GUARD)\n return hasLeadingGuard && trimmed.endsWith(GUARD_SUFFIX)\n}\n\n/** The Node interpreter a pinned command runs (`\"<node>\" \"<workscribe>\" …`), or null for a bare command. */\nexport function pinnedNodePath(command: string): string | null {\n return withoutLeadingGuards(command).match(/^\"([^\"]+)\"/)?.[1] ?? null\n}\n","import fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { execFileSync } from 'child_process'\nimport type { AIHookIntegration, HookUninstallOutcome } from './types'\nimport { isCommandInstalled } from './utils'\nimport { resolveWorkscribeBinary } from '../shell-hook'\nimport { backUpFile } from '../../utils/backup'\nimport { buildHookCommand, HOOK_MARKER, isGuardedCommand, isPinnedCommand, pinnedNodePath } from './claude-code-command'\n\nconst CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json')\n\nfunction readSettings(): Record<string, unknown> {\n if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) return {}\n try {\n return JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8')) as Record<string, unknown>\n } catch {\n return {}\n }\n}\n\nfunction commandsForEvent(hooks: Record<string, unknown>, eventName: string): string[] {\n const entries = (hooks[eventName] ?? []) as unknown[]\n const commands: string[] = []\n for (const entry of entries) {\n const entryHooks = ((entry as Record<string, unknown>).hooks ?? []) as unknown[]\n for (const hookItem of entryHooks) {\n const hook = hookItem as Record<string, unknown>\n if (typeof hook.command === 'string' && hook.command.includes(HOOK_MARKER)) {\n commands.push(hook.command)\n }\n }\n }\n return commands\n}\n\nfunction findWorkscribeHookCommands(settings: Record<string, unknown>): {\n postToolUse: string[]\n postToolUseFailure: string[]\n userPromptSubmit: string[]\n} {\n const hooks = (settings.hooks ?? {}) as Record<string, unknown>\n return {\n postToolUse: commandsForEvent(hooks, 'PostToolUse'),\n postToolUseFailure: commandsForEvent(hooks, 'PostToolUseFailure'),\n userPromptSubmit: commandsForEvent(hooks, 'UserPromptSubmit'),\n }\n}\n\n/**\n * Strips any existing workscribe hook entries out of a hooks array for one\n * event type, so install() can be called repeatedly — fresh install or\n * repairing a stale/unpinned one — without ever accumulating duplicates.\n */\nfunction withoutWorkscribeEntries(entries: unknown[]): unknown[] {\n return entries\n .map((entry) => {\n const record = entry as Record<string, unknown>\n const entryHooks = (record.hooks ?? []) as unknown[]\n const filteredHooks = entryHooks.filter((hookItem) => {\n const hook = hookItem as Record<string, unknown>\n return !(typeof hook.command === 'string' && hook.command.includes(HOOK_MARKER))\n })\n return { ...record, hooks: filteredHooks }\n })\n .filter((entry) => ((entry as Record<string, unknown>).hooks as unknown[]).length > 0)\n}\n\nexport interface ClaudeCodeHookHealth {\n installed: boolean\n pinned: boolean\n /** Every Workscribe hook exits silently when the binary is gone (false for hooks from Workscribe ≤ 2.0.0). */\n guarded: boolean\n /** A pinned hook's Node interpreter no longer exists (e.g. removed by a version manager) — capture is off. */\n nodeMissing: boolean\n /** Only meaningful when installed && !pinned; null otherwise. */\n confirmedBroken: boolean | null\n}\n\nfunction isExecutable(filePath: string): boolean {\n try {\n fs.accessSync(filePath, fs.constants.X_OK)\n return true\n } catch {\n return false\n }\n}\n\nfunction allWorkscribeCommands(): string[] {\n const { postToolUse, postToolUseFailure, userPromptSubmit } = findWorkscribeHookCommands(readSettings())\n return [...postToolUse, ...postToolUseFailure, ...userPromptSubmit]\n}\n\n/**\n * Checks whether an installed Claude Code hook uses the pinned (safe) or\n * bare (vulnerable to Node-version/PATH drift) command form, and — for bare\n * commands only — attempts a live probe to confirm whether it's actually\n * broken right now. The probe is best-effort: it inherits this process's own\n * environment, which can't perfectly replicate whatever environment Claude\n * Code invokes hooks in, but a failure here is still strong evidence of a\n * real, currently-happening problem rather than a theoretical risk.\n */\nexport function checkHookHealth(): ClaudeCodeHookHealth {\n const allCommands = allWorkscribeCommands()\n if (allCommands.length === 0) {\n return { installed: false, pinned: true, guarded: true, nodeMissing: false, confirmedBroken: null }\n }\n\n const guarded = allCommands.every(isGuardedCommand)\n const nodePaths = allCommands.map(pinnedNodePath).filter((nodePath): nodePath is string => nodePath !== null)\n const nodeMissing = nodePaths.some((nodePath) => !isExecutable(nodePath))\n if (allCommands.every(isPinnedCommand)) {\n return { installed: true, pinned: true, guarded, nodeMissing, confirmedBroken: null }\n }\n\n let confirmedBroken: boolean\n try {\n execFileSync('workscribe', ['--version'], { stdio: 'ignore', timeout: 2000 })\n confirmedBroken = false\n } catch {\n confirmedBroken = true\n }\n\n return { installed: true, pinned: false, guarded, nodeMissing, confirmedBroken }\n}\n\n/** Reads settings.json without forgiving a parse error — uninstall must never overwrite a file it couldn't read. */\nfunction readSettingsStrictly(): Record<string, unknown> | 'missing' | 'unreadable' {\n if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) return 'missing'\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8'))\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : 'unreadable'\n } catch {\n return 'unreadable'\n }\n}\n\n/** The hooks object with Workscribe's entries removed, empty event arrays dropped — or null if there were none. */\nfunction hooksWithoutWorkscribe(hooks: Record<string, unknown>): Record<string, unknown> | null {\n let removedAny = false\n const remaining: Record<string, unknown> = {}\n for (const [eventName, entries] of Object.entries(hooks)) {\n if (!Array.isArray(entries)) {\n remaining[eventName] = entries\n continue\n }\n const kept = withoutWorkscribeEntries(entries)\n if (commandsForEvent(hooks, eventName).length > 0) removedAny = true\n if (kept.length > 0) remaining[eventName] = kept\n }\n return removedAny ? remaining : null\n}\n\n/**\n * Both events have a Workscribe hook, and every one is pinned, guarded, and pinned to a Node.js that still exists.\n * The guard keeps a hook whose Node a version manager removed quiet — and so quietly broken — unless `init` sees it.\n */\nfunction isHookCurrent(): boolean {\n const commandsByEvent = findWorkscribeHookCommands(readSettings())\n // An install from before the failure hook (WOR-127) is outdated: its commands never record a failing exit code\n if (Object.values(commandsByEvent).some((commands) => commands.length === 0)) return false\n return Object.values(commandsByEvent)\n .flat()\n .every((command) => {\n const nodePath = pinnedNodePath(command)\n return isPinnedCommand(command) && isGuardedCommand(command) && nodePath !== null && isExecutable(nodePath)\n })\n}\n\nexport const claudeCodeIntegration: AIHookIntegration = {\n displayName: 'Claude Code',\n\n isInstalled() {\n return isCommandInstalled('claude')\n },\n\n // \"Installed\" means a *current* hook is present, not just any workscribe hook — so `workscribe init` offers to\n // update a stale one (unpinned, unguarded, or pinned to a Node that's gone) through its outdated-hook prompt\n isHookInstalled() {\n return isHookCurrent()\n },\n\n install() {\n const settings = readSettings()\n const existingHooks = (settings.hooks ?? {}) as Record<string, unknown>\n const existingPostToolUse = withoutWorkscribeEntries((existingHooks.PostToolUse ?? []) as unknown[])\n const existingPostToolUseFailure = withoutWorkscribeEntries((existingHooks.PostToolUseFailure ?? []) as unknown[])\n const existingUserPromptSubmit = withoutWorkscribeEntries((existingHooks.UserPromptSubmit ?? []) as unknown[])\n\n const binaryPath = resolveWorkscribeBinary()\n const command = (args: string) => buildHookCommand({ nodePath: process.execPath, binaryPath, args })\n\n settings.hooks = {\n ...existingHooks,\n PostToolUse: [\n ...existingPostToolUse,\n {\n matcher: 'Bash',\n hooks: [{ type: 'command', command: command(HOOK_MARKER), async: true }],\n },\n ],\n // Failed Bash calls only fire this one, with the exit code in `error` — PostToolUse alone never sees a failure\n PostToolUseFailure: [\n ...existingPostToolUseFailure,\n {\n matcher: 'Bash',\n hooks: [{ type: 'command', command: command(`${HOOK_MARKER} --event PostToolUseFailure`), async: true }],\n },\n ],\n UserPromptSubmit: [\n ...existingUserPromptSubmit,\n {\n hooks: [\n {\n type: 'command',\n command: command(`${HOOK_MARKER} --event UserPromptSubmit`),\n },\n ],\n },\n ],\n }\n fs.mkdirSync(path.dirname(CLAUDE_SETTINGS_PATH), { recursive: true })\n fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2))\n },\n\n // Some workscribe hook is present, but not all of them are current (pinned and guarded) — including a\n // half-installed one where only PostToolUse or only UserPromptSubmit exists\n isHookOutdated() {\n return allWorkscribeCommands().length > 0 && !isHookCurrent()\n },\n\n uninstall(): HookUninstallOutcome {\n const settings = readSettingsStrictly()\n if (settings === 'missing') return 'not-installed'\n if (settings === 'unreadable') return 'unreadable'\n const hooks = settings.hooks\n if (hooks === null || typeof hooks !== 'object' || Array.isArray(hooks)) return 'not-installed'\n const remainingHooks = hooksWithoutWorkscribe(hooks as Record<string, unknown>)\n if (!remainingHooks) return 'not-installed'\n\n const updated: Record<string, unknown> = { ...settings, hooks: remainingHooks }\n if (Object.keys(remainingHooks).length === 0) delete updated.hooks\n backUpFile(CLAUDE_SETTINGS_PATH)\n fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(updated, null, 2))\n return 'removed'\n },\n}\n","import type { AIHookIntegration } from './types'\nimport { claudeCodeIntegration } from './claude-code'\n\n/**\n * Registry of AI tool hook integrations, keyed by the \"hook\" field in src/config/ai-tools.json.\n * Adding support for a new tool = add a new entry here and a corresponding integration file.\n */\nexport const AI_HOOK_INTEGRATIONS: Record<string, AIHookIntegration> = {\n 'claude-code': claudeCodeIntegration,\n}\n","import { Command } from 'commander'\nimport { log } from '../utils/logger'\nimport { CompletionCommand, bashCompletion, zshCompletion, fishCompletion } from '../utils/completion-scripts'\n\nexport type { CompletionCommand }\nexport { bashCompletion, zshCompletion, fishCompletion }\n\nexport const HIDDEN_COMMANDS = new Set(['_capture', '_hook'])\n\n// _capture/_hook fire on every shell command as a detached background process\n// that must never add latency — startup checks (update check, telemetry) skip\n// those outright rather than relying on a cache alone.\nexport function shouldRunStartupChecks(invokedCommand: string | undefined): boolean {\n if (invokedCommand === undefined) return true\n return !HIDDEN_COMMANDS.has(invokedCommand)\n}\n\n// Data jobs (src/db/jobs/) run only before these: commands people type that read the data. Never the background\n// _capture/_hook, never `context` (the editor calls it with a 5 s timeout), never `sessions` (the agent skill calls\n// it), and never commands that remove or reset the database (`uninstall`, `clear`, `init`).\nexport const DATA_JOB_COMMANDS = new Set(['status', 'summary', 'week', 'export', 'events', 'upgrade'])\n// The extension detects the CLI with `--version` on a 2 s timeout; help output should never wait on a backup either\nconst INFORMATION_FLAGS = new Set(['--version', '-V', '--help', '-h'])\n\nexport function shouldRunDataJobs(args: readonly string[]): boolean {\n const [command] = args\n if (command === undefined || !DATA_JOB_COMMANDS.has(command)) return false\n return !args.some((arg) => INFORMATION_FLAGS.has(arg))\n}\n\nexport function getPublicCommands(program: Command): CompletionCommand[] {\n return program.commands\n .filter((command) => !HIDDEN_COMMANDS.has(command.name()))\n .map((command) => ({ name: command.name(), description: command.description() }))\n}\n\nexport function registerCompletions(program: Command): void {\n program\n .command('completions [shell]')\n .description('Print shell completion script (bash, zsh, fish)')\n .action((shell?: string) => {\n const detected = shell ?? (process.env.SHELL ?? '').split('/').at(-1)\n\n if (!detected || !['bash', 'zsh', 'fish'].includes(detected)) {\n log.error(`Unknown shell: \"${detected ?? ''}\". Supported: bash, zsh, fish`)\n log.plain(` Usage: workscribe completions <bash|zsh|fish>`)\n process.exit(1)\n }\n\n const commands = getPublicCommands(program)\n const script =\n detected === 'bash'\n ? bashCompletion(commands)\n : detected === 'zsh'\n ? zshCompletion(commands)\n : fishCompletion(commands)\n\n process.stdout.write(script + '\\n')\n })\n}\n","/** Caches a single-argument lookup by its string key for the lifetime of the returned function. */\nexport function memoize<Value>(lookup: (key: string) => Value): (key: string) => Value {\n // Boxed so a cached null/undefined result is told apart from a missing entry\n const cache = new Map<string, { value: Value }>()\n return (key) => {\n const cached = cache.get(key)\n if (cached) return cached.value\n const value = lookup(key)\n cache.set(key, { value })\n return value\n }\n}\n","import type { SkillAgent } from './types'\n\n/** Folder name the skill is installed under, inside each agent's skills folder. */\nexport const SKILL_NAME = 'workscribe'\n\n/** Marker file that identifies a Workscribe-installed copy of the skill. */\nexport const MARKER_FILE = '.workscribe-skill.json'\n\n// Skills folders checked against each vendor's docs (2026-10-26). If a vendor moves its folder,\n// only this table needs to change.\nexport const SKILL_AGENTS: SkillAgent[] = [\n {\n id: 'claude-code',\n name: 'Claude Code',\n skillsFolder: '.claude/skills',\n commands: ['claude'],\n homeFolders: ['.claude'],\n vscodeExtensionPrefixes: [],\n docsUrl: 'https://code.claude.com/docs/en/skills',\n },\n {\n id: 'codex',\n name: 'Codex',\n skillsFolder: '.agents/skills',\n commands: ['codex'],\n homeFolders: ['.codex'],\n vscodeExtensionPrefixes: [],\n docsUrl: 'https://learn.chatgpt.com/docs/build-skills',\n },\n {\n id: 'copilot',\n name: 'GitHub Copilot',\n skillsFolder: '.agents/skills',\n commands: ['copilot'],\n homeFolders: ['.copilot'],\n vscodeExtensionPrefixes: ['github.copilot-chat'],\n docsUrl: 'https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills',\n },\n {\n id: 'cursor',\n name: 'Cursor',\n skillsFolder: '.cursor/skills',\n commands: ['cursor', 'cursor-agent'],\n homeFolders: ['.cursor'],\n vscodeExtensionPrefixes: [],\n docsUrl: 'https://cursor.com/docs/skills',\n },\n]\n\nexport const SKILL_AGENT_IDS = SKILL_AGENTS.map((agent) => agent.id)\n","import fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport { isCommandInstalled } from '../capture/hooks/utils'\nimport { memoize } from '../utils/memoize'\nimport { SKILL_AGENTS } from './agents'\nimport type { SkillAgent } from './types'\n\n/** The I/O detection needs — injected so tests can describe a machine without touching the real one. */\nexport interface DetectionEnvironment {\n homeDir: string\n isCommandInstalled: (command: string) => boolean\n pathExists: (absolutePath: string) => boolean\n listDirectory: (absolutePath: string) => string[]\n}\n\nexport function defaultDetectionEnvironment(): DetectionEnvironment {\n return {\n homeDir: os.homedir(),\n // `which` spawns a process, so each command is looked up at most once per run\n isCommandInstalled: memoize(isCommandInstalled),\n pathExists: (absolutePath) => fs.existsSync(absolutePath),\n listDirectory: (absolutePath) => {\n try {\n return fs.readdirSync(absolutePath)\n } catch {\n return [] // a missing extensions folder just means no extensions\n }\n },\n }\n}\n\nfunction hasHomeFolder(agent: SkillAgent, environment: DetectionEnvironment): boolean {\n return agent.homeFolders.some((folder) => environment.pathExists(path.join(environment.homeDir, folder)))\n}\n\nfunction hasVscodeExtension(agent: SkillAgent, environment: DetectionEnvironment): boolean {\n if (agent.vscodeExtensionPrefixes.length === 0) return false\n const extensions = environment.listDirectory(path.join(environment.homeDir, '.vscode', 'extensions'))\n return extensions.some((extension) => agent.vscodeExtensionPrefixes.some((prefix) => extension.startsWith(prefix)))\n}\n\n/** Folder signals are checked first; `which` is only spawned when none of them found the agent. */\nexport function isAgentPresent(agent: SkillAgent, environment: DetectionEnvironment): boolean {\n return (\n hasHomeFolder(agent, environment) ||\n hasVscodeExtension(agent, environment) ||\n agent.commands.some((command) => environment.isCommandInstalled(command))\n )\n}\n\nexport function detectAgents(environment: DetectionEnvironment = defaultDetectionEnvironment()): SkillAgent[] {\n return SKILL_AGENTS.filter((agent) => isAgentPresent(agent, environment))\n}\n","import type { InstallOutcome, SkillAgent, SkillInstallState, SkillMarker, SkillsFolderGroup } from './types'\n\n/** One entry per distinct skills folder, in registry order — Codex and Copilot share `.agents/skills`. */\nexport function groupBySkillsFolder(agents: SkillAgent[]): SkillsFolderGroup[] {\n const groups = new Map<string, SkillAgent[]>()\n for (const agent of agents) groups.set(agent.skillsFolder, [...(groups.get(agent.skillsFolder) ?? []), agent])\n return [...groups.entries()].map(([skillsFolder, groupAgents]) => ({ skillsFolder, agents: groupAgents }))\n}\n\n/**\n * Names the agents a folder serves, noting any other agent that reads the same folder — installing or\n * removing for Codex also changes it for GitHub Copilot, and the message should say so.\n */\nexport function describeFolderAgents(group: SkillsFolderGroup, allAgents: SkillAgent[]): string {\n const sharers = allAgents.filter(\n (agent) => agent.skillsFolder === group.skillsFolder && !group.agents.includes(agent)\n )\n const names = joinAgentNames(group.agents)\n return sharers.length === 0 ? names : `${names} (folder shared with ${joinAgentNames(sharers)})`\n}\n\n/** The result `init` can report without asking: up to date, or a folder it doesn't own. Null means ask. */\nexport function outcomeWithoutPrompt(state: SkillInstallState): InstallOutcome | null {\n if (state.kind === 'installed') return 'up-to-date'\n if (state.kind === 'not-ours') return 'skipped-not-ours'\n return null\n}\n\n/** \"Codex\", \"Codex and GitHub Copilot\", \"Claude Code, Codex and Cursor\". */\nexport function joinAgentNames(agents: SkillAgent[]): string {\n const names = agents.map((agent) => agent.name)\n if (names.length <= 1) return names[0] ?? ''\n return `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n}\n\n/** What's in an agent's `workscribe` skill folder, judged by its marker. */\nexport function describeInstallState(input: {\n folderExists: boolean\n marker: SkillMarker | null\n currentVersion: string\n}): SkillInstallState {\n if (!input.folderExists) return { kind: 'not-installed' }\n if (!input.marker) return { kind: 'not-ours' }\n if (input.marker.version === input.currentVersion) return { kind: 'installed', version: input.marker.version }\n return { kind: 'outdated', version: input.marker.version }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { MARKER_FILE, SKILL_NAME } from './agents'\nimport { describeInstallState } from './state'\nimport type { InstallOutcome, InstallSkillOptions, SkillInstallState, SkillMarker, UninstallOutcome } from './types'\n\nfunction isSkillMarker(value: unknown): value is SkillMarker {\n const candidate = value as Partial<SkillMarker> | null\n return typeof candidate?.version === 'string' && typeof candidate?.installedAt === 'string'\n}\n\nfunction readMarker(skillDir: string): SkillMarker | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(path.join(skillDir, MARKER_FILE), 'utf-8'))\n return isSkillMarker(parsed) ? parsed : null\n } catch {\n return null // no marker, or not ours to read — either way it isn't a Workscribe install\n }\n}\n\n/** State of the Workscribe skill inside one agent skills folder (e.g. `~/.claude/skills`). */\nexport function readInstallState(skillsFolder: string, currentVersion: string): SkillInstallState {\n const skillDir = path.join(skillsFolder, SKILL_NAME)\n return describeInstallState({\n folderExists: fs.existsSync(skillDir),\n marker: readMarker(skillDir),\n currentVersion,\n })\n}\n\nfunction copyDirectory(from: string, to: string): void {\n fs.mkdirSync(to, { recursive: true })\n for (const entry of fs.readdirSync(from, { withFileTypes: true })) {\n const source = path.join(from, entry.name)\n const destination = path.join(to, entry.name)\n if (entry.isDirectory()) copyDirectory(source, destination)\n else fs.copyFileSync(source, destination)\n }\n}\n\n/**\n * Swaps a fully prepared copy into place: move any existing copy aside, rename the new one in, then\n * delete the old one — or put it back if the rename fails. A plain rename over a non-empty folder\n * fails on POSIX, and this way an interrupted install never leaves a half-written skill.\n */\nfunction swapInto(skillDir: string, preparedDir: string): void {\n const previousDir = path.join(path.dirname(skillDir), `.${SKILL_NAME}.old-${process.pid}`)\n fs.rmSync(previousDir, { recursive: true, force: true }) // left behind by a run that was killed\n const hadPrevious = fs.existsSync(skillDir)\n if (hadPrevious) fs.renameSync(skillDir, previousDir)\n try {\n fs.renameSync(preparedDir, skillDir)\n } catch (error) {\n if (hadPrevious) fs.renameSync(previousDir, skillDir)\n throw error\n }\n if (hadPrevious) fs.rmSync(previousDir, { recursive: true, force: true })\n}\n\nexport function installSkill(options: InstallSkillOptions): InstallOutcome {\n const state = readInstallState(options.skillsFolder, options.version)\n if (state.kind === 'installed') return 'up-to-date'\n if (state.kind === 'not-ours' && !options.force) return 'skipped-not-ours'\n\n fs.mkdirSync(options.skillsFolder, { recursive: true })\n const preparedDir = path.join(options.skillsFolder, `.${SKILL_NAME}.tmp-${process.pid}`)\n fs.rmSync(preparedDir, { recursive: true, force: true }) // left behind by a run that was killed\n try {\n copyDirectory(options.sourceDir, preparedDir)\n const marker: SkillMarker = { version: options.version, installedAt: new Date().toISOString() }\n fs.writeFileSync(path.join(preparedDir, MARKER_FILE), JSON.stringify(marker, null, 2))\n swapInto(path.join(options.skillsFolder, SKILL_NAME), preparedDir)\n } finally {\n fs.rmSync(preparedDir, { recursive: true, force: true })\n }\n return state.kind === 'not-installed' ? 'installed' : 'updated'\n}\n\n/** Removes the skill only if Workscribe installed it — a user's own `workscribe` skill is left alone. */\nexport function uninstallSkill(skillsFolder: string): UninstallOutcome {\n const skillDir = path.join(skillsFolder, SKILL_NAME)\n if (!fs.existsSync(skillDir)) return 'not-installed'\n if (!readMarker(skillDir)) return 'skipped-not-ours'\n fs.rmSync(skillDir, { recursive: true, force: true })\n return 'removed'\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { SKILL_NAME } from './agents'\n\nconst SKILL_ENTRY_FILE = 'SKILL.md'\n\n/**\n * Where the skill shipped in this npm package lives. The bundle runs from `dist/`, so it's one level\n * up; the TypeScript sources run from `src/<folder>/`, so it's two. The first with a SKILL.md wins.\n */\nexport function packagedSkillCandidates(fromDirectory: string): string[] {\n return [\n path.join(fromDirectory, '..', 'skills', SKILL_NAME),\n path.join(fromDirectory, '..', '..', 'skills', SKILL_NAME),\n ]\n}\n\nexport function resolvePackagedSkillDir(fromDirectory: string = __dirname): string {\n const found = packagedSkillCandidates(fromDirectory).find((candidate) =>\n fs.existsSync(path.join(candidate, SKILL_ENTRY_FILE))\n )\n if (!found) {\n throw new Error(\n 'The Workscribe skill files are missing from this install. Reinstall it: npm install -g @workscribe/cli'\n )\n }\n return found\n}\n","// Friendly wording for skill install results and status, shared by `workscribe skill`, `init` and `status`.\nimport type { InstallOutcome, SkillInstallState, UninstallOutcome } from './types'\n\nexport type MessageLevel = 'success' | 'info' | 'warn'\n\nexport interface Message {\n level: MessageLevel\n text: string\n}\n\n/** `/Users/dev/.claude/skills` → `~/.claude/skills`, so paths read the way users type them. */\nexport function displayPath(absolutePath: string, homeDir: string): string {\n return absolutePath.startsWith(homeDir) ? `~${absolutePath.slice(homeDir.length)}` : absolutePath\n}\n\nexport function describeInstallOutcome(input: {\n outcome: InstallOutcome\n agents: string\n location: string\n version: string\n}): Message {\n switch (input.outcome) {\n case 'installed':\n return { level: 'success', text: `Installed the Workscribe skill for ${input.agents} (${input.location})` }\n case 'updated':\n return { level: 'success', text: `Updated the Workscribe skill for ${input.agents} to ${input.version}` }\n case 'up-to-date':\n return { level: 'info', text: `The Workscribe skill is already up to date for ${input.agents}` }\n case 'skipped-not-ours':\n return {\n level: 'warn',\n text:\n `Skipped ${input.agents} — ${input.location} already holds a workscribe skill that Workscribe didn't install. ` +\n 'Run workscribe skill install --force to replace it.',\n }\n }\n}\n\nexport function describeUninstallOutcome(input: {\n outcome: UninstallOutcome\n agents: string\n location: string\n}): Message {\n switch (input.outcome) {\n case 'removed':\n return { level: 'success', text: `Removed the Workscribe skill from ${input.agents} (${input.location})` }\n case 'not-installed':\n return { level: 'info', text: `The Workscribe skill isn't installed for ${input.agents}` }\n case 'skipped-not-ours':\n return {\n level: 'warn',\n text: `Left ${input.location} alone — that workscribe skill wasn't installed by Workscribe.`,\n }\n }\n}\n\n/** One status phrase per state, with the command that fixes it when something needs doing. */\nexport function describeSkillStatus(state: SkillInstallState, currentVersion: string): Message {\n switch (state.kind) {\n case 'installed':\n return { level: 'success', text: `installed (${state.version})` }\n case 'outdated':\n return {\n level: 'warn',\n text: `outdated (${state.version}; this CLI is ${currentVersion}) — run workscribe skill install`,\n }\n case 'not-installed':\n return { level: 'info', text: 'not installed — run workscribe skill install' }\n case 'not-ours':\n return {\n level: 'warn',\n text: 'a different workscribe skill is there — run workscribe skill install --force to replace it',\n }\n }\n}\n\n/** The reason from anything thrown — fs errors carry useful text like \"EACCES: permission denied\". */\nexport function reasonOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nexport function describeInstallFailure(input: { agents: string; location: string; error: unknown }): Message {\n return {\n level: 'warn',\n text:\n `Couldn't install the Workscribe skill for ${input.agents} (${reasonOf(input.error)}). ` +\n `Check you can write to ${input.location}, then run: workscribe skill install`,\n }\n}\n\nexport function describeUninstallFailure(input: { agents: string; location: string; error: unknown }): Message {\n return {\n level: 'warn',\n text:\n `Couldn't remove the Workscribe skill for ${input.agents} (${reasonOf(input.error)}). ` +\n `Delete it yourself: rm -rf ${input.location}`,\n }\n}\n\nexport const AFTER_INSTALL_HINT =\n 'Restart any agent that is already open, then ask it to \"summarize my work today\" (or type /workscribe in Claude Code).'\n","// Install, remove and report the skill one skills folder at a time, with friendly messages. Shared by\n// `workscribe skill`, `init`, `status` and `uninstall`; a failure in one folder is reported, never thrown,\n// so the rest of those commands carries on.\nimport os from 'os'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { SKILL_AGENTS, SKILL_NAME } from './agents'\nimport { installSkill, readInstallState, uninstallSkill } from './installer'\nimport {\n describeInstallFailure,\n describeInstallOutcome,\n describeSkillStatus,\n describeUninstallFailure,\n describeUninstallOutcome,\n displayPath,\n type Message,\n type MessageLevel,\n} from './report'\nimport { describeFolderAgents, groupBySkillsFolder } from './state'\nimport type { SkillAgent, SkillsFolderGroup } from './types'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\n/** Width of the agent-name column in `skill status` and `workscribe status`. */\nexport const AGENT_NAME_COLUMN_WIDTH = 16\n\nconst STATUS_COLOURS: Record<MessageLevel, (text: string) => string> = {\n success: chalk.green,\n warn: chalk.yellow,\n info: chalk.dim,\n}\n\n/** Where a group's skill lives, and how messages should name the agents it serves. */\nexport interface FolderTarget {\n skillsFolder: string\n location: string\n agents: string\n}\n\nexport function describeTarget(group: SkillsFolderGroup, homeDir: string = os.homedir()): FolderTarget {\n const skillsFolder = path.join(homeDir, group.skillsFolder)\n return {\n skillsFolder,\n location: displayPath(path.join(skillsFolder, SKILL_NAME), homeDir),\n agents: describeFolderAgents(group, SKILL_AGENTS),\n }\n}\n\nexport function printMessage(message: Message): void {\n log[message.level](message.text)\n}\n\nexport function installIntoFolder(options: { target: FolderTarget; sourceDir: string; force?: boolean }): Message {\n const { target } = options\n try {\n const outcome = installSkill({\n sourceDir: options.sourceDir,\n skillsFolder: target.skillsFolder,\n version: __WORKSCRIBE_VERSION__,\n force: options.force,\n })\n return describeInstallOutcome({ ...target, outcome, version: __WORKSCRIBE_VERSION__ })\n } catch (error) {\n return describeInstallFailure({ ...target, error })\n }\n}\n\n/**\n * Removes Workscribe-installed copies from each agent's skills folder. `workscribe uninstall` passes\n * `reportMissing: false` so folders that never had the skill don't add noise.\n */\nexport function removeSkills(agents: SkillAgent[], options: { reportMissing: boolean }): void {\n for (const group of groupBySkillsFolder(agents)) {\n const target = describeTarget(group)\n try {\n const outcome = uninstallSkill(target.skillsFolder)\n if (outcome === 'not-installed' && !options.reportMissing) continue\n printMessage(describeUninstallOutcome({ ...target, outcome }))\n } catch (error) {\n printMessage(describeUninstallFailure({ ...target, error }))\n }\n }\n}\n\n/** \"Claude Code installed (2.0.0) ~/.claude/skills/workscribe\" */\nexport function formatAgentSkillLine(agent: SkillAgent, homeDir: string = os.homedir()): string {\n const location = displayPath(path.join(homeDir, agent.skillsFolder, SKILL_NAME), homeDir)\n const installState = readInstallState(path.join(homeDir, agent.skillsFolder), __WORKSCRIBE_VERSION__)\n const status = describeSkillStatus(installState, __WORKSCRIBE_VERSION__)\n return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${STATUS_COLOURS[status.level](status.text)} ${chalk.dim(location)}`\n}\n\nexport function formatUndetectedAgentLine(agent: SkillAgent): string {\n return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${chalk.dim('not detected')}`\n}\n","// Workscribe 2.0 removed AI providers (cloud and Ollama). These helpers spot 1.x settings so users hear about\n// it once per run, can't set them again by accident, and can have them (and any stored API key) removed.\nimport type { WorkscribeConfig } from '../config'\n\n/** The only `summary.engine` value 2.0 still means — setting it is harmless, so it isn't a leftover. */\nexport const TEMPLATE_ENGINE = 'template'\n\nconst LEGACY_AI_FIELDS = ['provider', 'apiKey', 'model', 'baseUrl'] as const\n\nconst REMOVED_HEADLINE = 'AI providers were removed in Workscribe 2.0 — summaries now use the offline template engine.'\n\nconst SKILL_HINT =\n 'For AI-written summaries, install the Workscribe skill and ask your coding agent:\\n workscribe skill install'\n\n/** Why `config set` refuses `ai.*` and `summary.engine`, and what to do instead. */\nexport const REMOVED_AI_SETTING_MESSAGE = `${REMOVED_HEADLINE}\\n${SKILL_HINT}`\n\nexport function hasLegacyAiSettings(config: WorkscribeConfig): boolean {\n const hasAiField = LEGACY_AI_FIELDS.some((field) => Boolean(config.ai?.[field]))\n const engine = config.summary?.engine\n return hasAiField || (engine !== undefined && engine !== TEMPLATE_ENGINE)\n}\n\n/** The once-per-run notice for a 1.x config, or null when there's nothing left over. `configPath` is shown as-is. */\nexport function describeLegacyAiSettings(config: WorkscribeConfig, configPath: string): string | null {\n if (!hasLegacyAiSettings(config)) return null\n return (\n `${REMOVED_HEADLINE}\\n${SKILL_HINT}\\n` +\n `Your old AI settings (including any API key) are still in ${configPath}. To remove them, run:\\n workscribe init`\n )\n}\n\n/** `ai`, `ai.<anything>`, `summary.engine` and anything under it, in any case — settings `config set` refuses. */\nexport function isRemovedAiSetting(key: string): boolean {\n const normalizedKey = key.toLowerCase()\n return ['ai', 'summary.engine'].some(\n (removed) => normalizedKey === removed || normalizedKey.startsWith(`${removed}.`)\n )\n}\n\n/** A copy of the config without the 1.x AI settings — what `init` saves when the user agrees to remove them. */\nexport function withoutLegacyAiSettings(config: WorkscribeConfig): WorkscribeConfig {\n const cleaned = structuredClone(config)\n delete cleaned.ai\n delete cleaned.summary.engine\n return cleaned\n}\n","import prompts from 'prompts'\nimport { log } from '../utils/logger'\nimport { writeConfig, type WorkscribeConfig } from '../config'\nimport { hasLegacyAiSettings, withoutLegacyAiSettings } from '../summary/legacy-ai'\n\n/** Part of `init`: 2.0 removed AI providers, so offer to delete the 1.x settings — they can include a stored API key. */\nexport async function offerLegacyAiCleanup(config: WorkscribeConfig): Promise<WorkscribeConfig> {\n if (!hasLegacyAiSettings(config)) return config\n log.plain('')\n const { removeLegacyAi } = await prompts({\n type: 'confirm',\n name: 'removeLegacyAi',\n message:\n 'Your config has AI provider settings from Workscribe 1.x (including any API key) that 2.0 no longer uses. Remove them?',\n initial: true,\n })\n if (!removeLegacyAi) {\n log.info('Kept the old AI settings — they are ignored, and workscribe init can remove them later')\n return config\n }\n const cleaned = withoutLegacyAiSettings(config)\n writeConfig(cleaned)\n log.success('Removed the old AI provider settings from your config')\n return cleaned\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport prompts from 'prompts'\nimport chalk from 'chalk'\nimport { CONFIG_PATH, PROFILES_DIR, REQUIRED_DIRS } from '../utils/paths'\nimport { readConfig, writeConfig, ensureInstallId, WorkscribeConfig } from '../config'\nimport { detectProfilesForDirectory } from '../profiles/loader'\nimport { installProfile, cacheManifest } from '../profiles/fetcher'\nimport { getDb } from '../db'\nimport { log } from '../utils/logger'\nimport { loadAIToolRegistry } from '../config/registry'\nimport {\n isHookInstalled,\n isHookOutdated,\n installHook,\n updateHook,\n isCompletionsInstalled,\n installCompletions,\n isCompletionsOutdated,\n updateCompletions,\n type ShellTarget,\n writeCompletionScript,\n resolveWorkscribeBinary,\n injectBinaryPath,\n} from '../capture/shell-hook'\nimport { AI_HOOK_INTEGRATIONS } from '../capture/hooks'\nimport { getPublicCommands } from './completions'\nimport { detectAgents } from '../skills/detect'\nimport { readInstallState } from '../skills/installer'\nimport { resolvePackagedSkillDir } from '../skills/package-path'\nimport { describeInstallOutcome, reasonOf } from '../skills/report'\nimport { groupBySkillsFolder, outcomeWithoutPrompt } from '../skills/state'\nimport { describeTarget, installIntoFolder, printMessage } from '../skills/actions'\nimport { offerLegacyAiCleanup } from './legacy-ai-cleanup'\nimport type { SkillsFolderGroup } from '../skills/types'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\ntype Shell = ShellTarget\n\nconst TOTAL_STEPS = 5\n\nfunction detectShell(): Shell | null {\n const shell = process.env.SHELL ?? ''\n if (shell.includes('zsh')) return { name: 'zsh', rcFile: path.join(os.homedir(), '.zshrc') }\n if (shell.includes('bash')) return { name: 'bash', rcFile: path.join(os.homedir(), '.bashrc') }\n if (shell.includes('fish')) {\n const confDir = path.join(os.homedir(), '.config', 'fish', 'conf.d')\n return { name: 'fish', rcFile: path.join(confDir, 'workscribe.fish') }\n }\n return null\n}\n\nfunction step(current: number, total: number, label: string): void {\n log.plain(chalk.bold(`\\n [${current}/${total}] ${label}`))\n}\n\n// ── Step 1: Data directories ──────────────────────────────────────────────\nfunction runDataDirectoriesStep(): void {\n step(1, TOTAL_STEPS, 'Creating data directories')\n\n for (const dir of REQUIRED_DIRS) {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n log.success(`Created ${dir}`)\n } else {\n try {\n fs.chmodSync(dir, 0o700)\n } catch {\n /* ignore */\n }\n log.info(`${dir} already exists`)\n }\n }\n\n if (!fs.existsSync(PROFILES_DIR)) {\n fs.mkdirSync(PROFILES_DIR, { recursive: true, mode: 0o700 })\n }\n}\n\n// ── Step 2: Database ──────────────────────────────────────────────────────\nasync function initDatabaseAndConfig(): Promise<WorkscribeConfig> {\n step(2, TOTAL_STEPS, 'Initialising database')\n const config = readConfig()\n getDb()\n log.success('Database ready')\n return offerLegacyAiCleanup(config)\n}\n\n// ── Telemetry (part of setup, no separate step) ────────────────────────────\nasync function runTelemetryStep(config: WorkscribeConfig): Promise<void> {\n log.plain('')\n log.plain(chalk.dim(' Workscribe can send anonymous install stats: a random install ID,'))\n log.plain(chalk.dim(' CLI version, OS platform, and Node.js version. No command, event, or'))\n log.plain(chalk.dim(' summary data is ever sent. Opt out anytime:'))\n log.plain(chalk.dim(' workscribe config set telemetry.enabled false'))\n log.plain('')\n const { telemetryEnabled } = await prompts({\n type: 'confirm',\n name: 'telemetryEnabled',\n message: 'Send anonymous install stats?',\n initial: true,\n })\n config.telemetry.enabled = telemetryEnabled\n config.telemetry.installId = ensureInstallId(config)\n}\n\n// ── Step 3: Shell hook + completions ──────────────────────────────────────\nasync function runShellHookStep(program: Command): Promise<Shell | null> {\n step(3, TOTAL_STEPS, 'Installing shell hook')\n\n const shell = detectShell()\n if (!shell) {\n log.warn('Could not detect shell. Supported: zsh, bash, fish')\n log.plain(\n ` Manually add the hook from: ${chalk.cyan('shell/hook.zsh')}, ${chalk.cyan('shell/hook.bash')}, or ${chalk.cyan('shell/hook.fish')}`\n )\n return null\n }\n\n const hookFile = path.join(__dirname, '..', 'shell', `hook.${shell.name}`)\n if (!fs.existsSync(hookFile)) {\n log.warn(`Hook template not found at ${hookFile}`)\n return shell\n }\n\n const hookContent = fs.readFileSync(hookFile, 'utf-8')\n const binaryPath = resolveWorkscribeBinary()\n const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent\n\n // For fish: ensure conf.d directory exists before touching the file\n if (shell.name === 'fish') {\n const confDir = path.dirname(shell.rcFile)\n if (!fs.existsSync(confDir)) fs.mkdirSync(confDir, { recursive: true })\n }\n\n if (!fs.existsSync(shell.rcFile)) fs.writeFileSync(shell.rcFile, '')\n\n if (!isHookInstalled(shell.rcFile)) {\n log.plain('')\n log.plain(chalk.dim(' Workscribe adds a lightweight hook to your shell that captures each'))\n log.plain(chalk.dim(' command you run — the text, exit code, duration, and working directory.'))\n log.plain(chalk.dim(' It runs in the background and adds no latency to your terminal.'))\n log.plain(chalk.dim(' Credentials and secrets are redacted before anything is stored.'))\n log.plain('')\n installHook(shell.rcFile, hookWithPath)\n log.success(`Shell hook installed in ${shell.rcFile}`)\n } else if (isHookOutdated(shell.rcFile, hookWithPath)) {\n const { update } = await prompts({\n type: 'confirm',\n name: 'update',\n message: `Shell hook in ${shell.rcFile} is outdated. Update?`,\n initial: true,\n })\n if (update) {\n updateHook(shell.rcFile, hookWithPath)\n log.success(`Shell hook updated in ${shell.rcFile}`)\n } else {\n log.info('Shell hook update skipped')\n }\n } else {\n log.info(`Shell hook up to date in ${shell.rcFile}`)\n }\n\n // Offer completions install — or repair: refresh the script, and guard an older unguarded rc block\n if (isCompletionsInstalled(shell)) {\n await refreshCompletions(shell, program)\n } else {\n const { installComps } = await prompts({\n type: 'confirm',\n name: 'installComps',\n message: 'Install shell completions (tab-complete workscribe commands)?',\n initial: true,\n })\n if (installComps) {\n try {\n installCompletions(shell, getPublicCommands(program))\n log.success('Shell completions installed')\n } catch (error) {\n log.warn(`Completions install failed: ${(error as Error).message}`)\n }\n }\n }\n\n return shell\n}\n\nasync function refreshCompletions(shell: Shell, program: Command): Promise<void> {\n try {\n writeCompletionScript(shell, getPublicCommands(program))\n } catch (error) {\n log.warn(`Could not refresh shell completions: ${(error as Error).message}`)\n }\n if (!isCompletionsOutdated(shell)) {\n log.info('Shell completions already installed')\n return\n }\n const { updateShellCompletions } = await prompts({\n type: 'confirm',\n name: 'updateShellCompletions',\n message: `Shell completions in ${shell.rcFile} are outdated (they error in new terminals if Workscribe is removed). Update?`,\n initial: true,\n })\n if (!updateShellCompletions) {\n log.info('Shell completions update skipped')\n return\n }\n updateCompletions(shell)\n log.success(`Shell completions updated in ${shell.rcFile}`)\n}\n\n// ── Step 4: AI tools ──────────────────────────────────────────────────────\nasync function runAIToolsStep(): Promise<void> {\n step(4, TOTAL_STEPS, 'Detecting AI coding tools')\n\n const installedTools = loadAIToolRegistry().filter(\n (entry) => entry.hook !== null && AI_HOOK_INTEGRATIONS[entry.hook!]?.isInstalled()\n )\n\n if (installedTools.length === 0) {\n log.info('No AI coding tools detected — re-run workscribe init after installing one')\n return\n }\n\n for (const tool of installedTools) {\n const integration = AI_HOOK_INTEGRATIONS[tool.hook!]!\n\n if (integration.isHookInstalled()) {\n log.info(`${integration.displayName} session hook already installed`)\n } else if (integration.isHookOutdated?.()) {\n // You opted in when you first installed it, so updating it defaults to yes\n const { updateAiHook } = await prompts({\n type: 'confirm',\n name: 'updateAiHook',\n message: `${integration.displayName} session hook is outdated. Update?`,\n initial: true,\n })\n if (updateAiHook) {\n integration.install()\n log.success(`${integration.displayName} session hook updated`)\n } else {\n log.info(`${integration.displayName} hook update skipped — re-run workscribe init to update later`)\n }\n } else {\n const { install } = await prompts({\n type: 'confirm',\n name: 'install',\n message: `${integration.displayName} detected. Install session capture hook?`,\n initial: false,\n })\n if (install) {\n integration.install()\n log.success(`${integration.displayName} session hook installed`)\n } else {\n log.info(`${integration.displayName} hook skipped — re-run workscribe init to install later`)\n }\n }\n }\n}\n\n// ── Step 5: Agent skill ───────────────────────────────────────────────────\nasync function offerSkillInstall(group: SkillsFolderGroup, sourceDir: string): Promise<void> {\n const target = describeTarget(group)\n const state = readInstallState(target.skillsFolder, __WORKSCRIBE_VERSION__)\n const knownOutcome = outcomeWithoutPrompt(state)\n if (knownOutcome) {\n printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: __WORKSCRIBE_VERSION__ }))\n return\n }\n\n const { installSkill: shouldInstall } = await prompts({\n type: 'confirm',\n name: 'installSkill',\n message: `${state.kind === 'outdated' ? 'Update' : 'Install'} the Workscribe skill for ${target.agents}? Lets ${target.agents} summarize your work when you ask`,\n initial: true,\n })\n if (!shouldInstall) {\n log.info(`Skill skipped for ${target.agents} — run workscribe skill install any time`)\n return\n }\n printMessage(installIntoFolder({ target, sourceDir }))\n}\n\nasync function runAgentSkillStep(): Promise<void> {\n step(5, TOTAL_STEPS, 'Installing the Workscribe agent skill')\n\n const agents = detectAgents()\n if (agents.length === 0) {\n log.info('No coding agents detected — run workscribe skill install after installing one')\n return\n }\n\n let sourceDir: string\n try {\n sourceDir = resolvePackagedSkillDir()\n } catch (error) {\n log.warn(reasonOf(error)) // setup can carry on; the skill is optional\n return\n }\n for (const group of groupBySkillsFolder(agents)) {\n await offerSkillInstall(group, sourceDir)\n }\n}\n\n// Profile detection (part of environment scan, no separate step)\nasync function runProfileDetectionStep(config: WorkscribeConfig): Promise<void> {\n try {\n await cacheManifest()\n const detected = detectProfilesForDirectory(process.cwd())\n const alreadyActive = new Set(config.capture.profiles)\n const toPrompt = detected.filter((entry) => !alreadyActive.has(entry.name))\n\n if (toPrompt.length > 0) {\n log.plain('')\n for (const entry of toPrompt) {\n const { install } = await prompts({\n type: 'confirm',\n name: 'install',\n message: `${entry.displayName} project detected. Enable the ${entry.displayName} profile?`,\n initial: true,\n })\n if (install) {\n await installProfile(entry.name)\n config.capture.profiles = [...new Set([...config.capture.profiles, entry.name])]\n log.success(`${entry.displayName} profile enabled`)\n }\n }\n writeConfig(config)\n } else if (detected.length === 0) {\n log.info('No project profiles detected — run workscribe profile detect from your project directory')\n }\n } catch {\n log.info('Could not reach profile registry — run workscribe profile detect after connecting')\n }\n}\n\n// ── What to do next ───────────────────────────────────────────────────────\nfunction printNextSteps(shell: Shell | null): void {\n log.plain('')\n log.plain(chalk.bold(' Setup complete!'))\n log.plain('')\n\n if (shell) {\n log.plain(` Activate capture now:`)\n if (shell.name === 'fish') {\n log.plain(` Restart your terminal`)\n log.plain(` ${chalk.dim(`or run: source ${shell.rcFile}`)}`)\n } else {\n log.plain(` ${chalk.cyan(`source ${shell.rcFile}`)}`)\n log.plain(` Or restart your terminal.`)\n }\n log.plain('')\n }\n\n log.plain(` Then run a few commands and generate your first summary:`)\n log.plain(` ${chalk.cyan('workscribe summary')}`)\n log.plain('')\n log.plain(` For AI-written summaries, ask your coding agent to \"summarize my work today\"`)\n log.plain(` ${chalk.dim('(needs the Workscribe skill: workscribe skill install)')}`)\n log.plain('')\n log.plain(` Check capture is working:`)\n log.plain(` ${chalk.cyan('workscribe status')}`)\n\n log.plain('')\n}\n\nexport function registerInit(program: Command): void {\n program\n .command('init')\n .description('Set up Workscribe and install or update shell hooks')\n .action(async () => {\n log.plain(chalk.bold('\\n Workscribe — Developer Work Journal\\n'))\n\n runDataDirectoriesStep()\n const config = await initDatabaseAndConfig()\n await runTelemetryStep(config)\n const shell = await runShellHookStep(program)\n await runAIToolsStep()\n await runAgentSkillStep()\n await runProfileDetectionStep(config)\n\n if (!fs.existsSync(CONFIG_PATH)) {\n writeConfig(config)\n }\n\n printNextSteps(shell)\n })\n}\n","export function formatDuration(minutes: number): string {\n if (minutes < 60) return `${Math.round(minutes)}m`\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return m > 0 ? `${h}h ${m}m` : `${h}h`\n}\n\nexport function formatDate(isoString: string): string {\n return new Date(isoString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\nexport function todayDate(): string {\n return localDateStr(new Date())\n}\n\nexport function parseDate(input: string): string {\n // Accepts YYYY-MM-DD, returns YYYY-MM-DD. Throws on invalid.\n const d = new Date(input + 'T00:00:00')\n if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`)\n return input\n}\n\nfunction localDateStr(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\n\n/** The local YYYY-MM-DD an ISO timestamp falls on — the same day rule sessions are grouped by. */\nexport function localDateOf(isoString: string): string {\n return localDateStr(new Date(isoString))\n}\n\n/**\n * A local day's bounds as UTC ISO strings, [start, end) — the same day `date(captured_at, 'localtime')` picks,\n * but comparable against `captured_at` directly, so a query can use its index. Handles DST-length days.\n */\nexport function localDayBounds(date: string): { start: string; end: string } {\n const start = new Date(`${date}T00:00:00`)\n const end = new Date(start)\n end.setDate(end.getDate() + 1)\n return { start: start.toISOString(), end: end.toISOString() }\n}\n\n// Returns Monday of the ISO week containing dateStr (YYYY-MM-DD)\nexport function startOfWeek(dateStr: string): string {\n const d = new Date(dateStr + 'T00:00:00')\n const day = d.getDay() // 0=Sun\n const diff = day === 0 ? -6 : 1 - day\n d.setDate(d.getDate() + diff)\n return localDateStr(d)\n}\n\n// Returns all YYYY-MM-DD strings from start to end inclusive\nexport function eachDayOfRange(start: string, end: string): string[] {\n const days: string[] = []\n const cur = new Date(start + 'T00:00:00')\n const last = new Date(end + 'T00:00:00')\n while (cur <= last) {\n days.push(localDateStr(cur))\n cur.setDate(cur.getDate() + 1)\n }\n return days\n}\n\nexport function timeAgo(isoString: string, now = Date.now()): string {\n const elapsedMs = now - new Date(isoString).getTime()\n const minutes = Math.floor(elapsedMs / 60000)\n if (minutes < 2) return 'just now'\n if (minutes < 60) return `${minutes} minutes ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return hours === 1 ? '1 hour ago' : `${hours} hours ago`\n const days = Math.floor(hours / 24)\n if (days === 1) return 'yesterday'\n return `${days} days ago`\n}\n\n/** A local date (`2026-11-03`) as the week shows it: `Mon 3 Nov`. */\nexport function formatWeekday(date: string): string {\n const [year, month, day] = date.split('-').map(Number)\n // Noon UTC, formatted in UTC: the same calendar day whatever the machine's time zone\n return new Date(Date.UTC(year, month - 1, day, 12))\n .toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short', timeZone: 'UTC' })\n .replace(',', '')\n}\n","import os from 'os'\nimport { log } from '../utils/logger'\nimport { CONFIG_PATH } from '../utils/paths'\nimport { describeLegacyAiSettings } from '../summary/legacy-ai'\nimport { displayPath } from '../skills/report'\nimport type { WorkscribeConfig } from '../config'\n\n/** Tells a 1.x user, once per run, that their AI provider settings no longer do anything (stderr, so JSON output stays clean). */\nexport function printLegacyAiNotice(config: WorkscribeConfig): void {\n const notice = describeLegacyAiSettings(config, displayPath(CONFIG_PATH, os.homedir()))\n if (notice) log.notice(notice)\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { DataJob, JobIssue, JobVerification } from './types'\n\nexport type JobStatus = 'pending' | 'running' | 'done' | 'failed'\n\nexport interface JobRow {\n name: string\n version: number\n cursor: number\n status: JobStatus\n started_at: string | null\n finished_at: string | null\n verification: string | null\n error: string | null\n}\n\nexport function getMetaValue(db: MigrationDatabase, key: string): string | null {\n const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined\n return row?.value ?? null\n}\n\nexport function setMetaValue(db: MigrationDatabase, key: string, value: string): void {\n db.prepare(\n 'INSERT INTO meta (key, value) VALUES ($key, $value) ON CONFLICT (key) DO UPDATE SET value = excluded.value'\n ).run({ key, value })\n}\n\nexport function deleteMetaValue(db: MigrationDatabase, key: string): void {\n db.prepare('DELETE FROM meta WHERE key = ?').run(key)\n}\n\n/** Meta values whose key starts with `prefix`, keyed by the rest of the key. */\nexport function getMetaValuesWithPrefix(db: MigrationDatabase, prefix: string): Map<string, string> {\n const rows = db\n .prepare(\"SELECT key, value FROM meta WHERE key LIKE ? ESCAPE '\\\\'\")\n .all(`${prefix.replace(/[\\\\%_]/g, '\\\\$&')}%`) as { key: string; value: string }[]\n return new Map(rows.map((row) => [row.key.slice(prefix.length), row.value]))\n}\n\nexport function getJobRow(db: MigrationDatabase, name: string): JobRow | null {\n return (db.prepare('SELECT * FROM data_jobs WHERE name = ?').get(name) as JobRow | undefined) ?? null\n}\n\n/** Done at this version or a later one — a later version means a newer CLI already ran it; never run it backwards. */\nexport function isJobDone(row: JobRow | null, job: DataJob): boolean {\n return row !== null && row.status === 'done' && row.version >= job.version\n}\n\n/** Starts the job, or restarts it from scratch when its version moved on since it last ran. */\nexport function startJob(db: MigrationDatabase, job: DataJob, startedAt: string): void {\n const row = getJobRow(db, job.name)\n if (row !== null && row.version >= job.version) {\n db.prepare(\"UPDATE data_jobs SET status = 'running', error = NULL WHERE name = ?\").run(job.name)\n return\n }\n job.prepare(db)\n db.prepare(\n `INSERT INTO data_jobs (name, version, cursor, status, started_at)\n VALUES ($name, $version, 0, 'running', $started_at)\n ON CONFLICT (name) DO UPDATE SET version = excluded.version, cursor = 0, status = 'running',\n started_at = excluded.started_at, finished_at = NULL, verification = NULL, error = NULL`\n ).run({ name: job.name, version: job.version, started_at: startedAt })\n}\n\nexport function setJobCursor(db: MigrationDatabase, name: string, cursor: number): void {\n db.prepare('UPDATE data_jobs SET cursor = $cursor WHERE name = $name').run({ name, cursor })\n}\n\nexport function recordJobIssues(db: MigrationDatabase, job: DataJob, issues: JobIssue[], recordedAt: string): void {\n const insert = db.prepare(\n `INSERT INTO data_job_issues (job_name, job_version, reference, reason, recorded_at)\n VALUES ($job_name, $job_version, $reference, $reason, $recorded_at)`\n )\n for (const issue of issues) {\n insert.run({\n job_name: job.name,\n job_version: job.version,\n reference: issue.reference,\n reason: issue.reason,\n recorded_at: recordedAt,\n })\n }\n}\n\n/**\n * Marks the job done — only at this job's version. Returns false when the row moved on to another version meanwhile\n * (a newer CLI restarted it), so older code never marks newer work done.\n */\nexport function finishJob(\n db: MigrationDatabase,\n job: DataJob,\n verification: JobVerification,\n finishedAt: string\n): boolean {\n const result = db\n .prepare(\n `UPDATE data_jobs SET status = 'done', finished_at = $finished_at, verification = $verification\n WHERE name = $name AND version = $version`\n )\n .run({ name: job.name, version: job.version, finished_at: finishedAt, verification: JSON.stringify(verification) })\n return (result as { changes: number | bigint }).changes > 0\n}\n\n/** Records the failure, creating the job's row if it failed before its first write (say, in `prepare`). */\nexport function failJob(db: MigrationDatabase, job: DataJob, error: string): void {\n db.prepare(\n `INSERT INTO data_jobs (name, version, status, error) VALUES ($name, $version, 'failed', $error)\n ON CONFLICT (name) DO UPDATE SET status = 'failed', error = excluded.error`\n ).run({ name: job.name, version: job.version, error })\n}\n","import path from 'path'\nimport type { MigrationDatabase } from '../migration-types'\nimport type { DirectoryProbe } from '../../capture/git'\nimport { withImmediateTransaction, withReadTransaction } from '../transaction'\nimport { rootForms } from '../../utils/working-directory'\n\n// `directory_probe`: what git said about each folder stored events mention, asked once and kept, so a job that\n// reruns gets the same answer without asking again.\n\ninterface ProbeRow {\n path: string\n outcome: DirectoryProbe['outcome']\n root: string | null\n common_dir: string | null\n remote_url: string | null\n}\n\nfunction toProbe(row: ProbeRow): DirectoryProbe {\n if (row.outcome !== 'repo' || row.root === null) {\n return { outcome: row.outcome === 'missing' ? 'missing' : 'not-a-repo' }\n }\n return { outcome: 'repo', root: row.root, commonDir: row.common_dir, remoteUrl: row.remote_url }\n}\n\nexport function loadProbes(db: MigrationDatabase): Map<string, DirectoryProbe> {\n const rows = db.prepare('SELECT * FROM directory_probe').all() as ProbeRow[]\n return new Map(rows.map((row) => [row.path, toProbe(row)]))\n}\n\n/** Every folder an event was captured in or saved a file in that git hasn't been asked about yet. */\nexport function foldersToProbe(db: MigrationDatabase): string[] {\n return withReadTransaction(db, () => {\n const probed = new Set(loadProbes(db).keys())\n const cwds = db.prepare('SELECT DISTINCT cwd FROM events').all() as { cwd: string }[]\n const files = db.prepare('SELECT DISTINCT file_path FROM events WHERE file_path IS NOT NULL').all() as {\n file_path: string\n }[]\n const folders = new Set([...cwds.map((row) => row.cwd), ...files.map((row) => path.dirname(row.file_path))])\n return [...folders].filter((folder) => !probed.has(folder)).sort()\n })\n}\n\n/**\n * Stores the probe in a transaction of its own, with the root as the folder reaches it — the form stored `cwd`s and\n * file paths use. The remote comes without credentials (`probeDirectory` strips them).\n */\nexport function saveProbe(db: MigrationDatabase, folder: string, probe: DirectoryProbe): void {\n const isRepo = probe.outcome === 'repo'\n const root = isRepo ? (rootForms(folder, probe.root).at(-1) ?? probe.root) : null\n withImmediateTransaction(db, () =>\n db\n .prepare(\n `INSERT OR REPLACE INTO directory_probe (path, outcome, root, common_dir, remote_url)\n VALUES ($path, $outcome, $root, $common_dir, $remote_url)`\n )\n .run({\n path: folder,\n outcome: probe.outcome,\n root,\n common_dir: isRepo ? probe.commonDir : null,\n remote_url: isRepo ? probe.remoteUrl : null,\n })\n )\n}\n","import type { DirectoryProbe } from '../../capture/git'\nimport { nameIdentity, projectIdentity, type ProjectIdentity } from '../../capture/project-identity'\nimport type { ProjectRecord } from '../dimensions'\nimport { relativeInside } from '../../utils/working-directory'\n\n// Which project each stored (folder, name) pair belongs to. Pure: the backfill job feeds it the pairs it found and\n// what git said about each folder, and stores the answer.\n\n/** How a pair found its project. Everything but `git` and `none` is a fallback, and is recorded. */\nexport type ResolutionMethod = 'git' | 'folder' | 'qualified-name' | 'bare-name' | 'name' | 'none'\n\nexport const FALLBACK_METHODS: ResolutionMethod[] = ['folder', 'qualified-name', 'bare-name', 'name']\n\nexport interface ObservedPair {\n cwd: string\n repoName: string | null\n}\n\nexport interface PairResolution extends ObservedPair {\n method: ResolutionMethod\n project: ProjectRecord | null\n}\n\n/** A project git found at a folder that still exists, with every root and bare name it was stored under. */\ninterface KnownProject extends ProjectIdentity {\n roots: Set<string>\n bareNames: Set<string>\n}\n\nfunction isQualified(name: string): boolean {\n return name.includes('/')\n}\n\nfunction gitProject(probe: DirectoryProbe | undefined): (ProjectIdentity & { root: string }) | null {\n if (probe?.outcome !== 'repo') return null\n const identity = projectIdentity(probe)\n return identity && { ...identity, root: probe.root }\n}\n\n/**\n * Whether a stored name could have come from this project's remote, as `repo` or `owner/repo`. A name that doesn't\n * is a `projects` override, or a `git -C ../other …` stored with the shell's folder but the other repo's name. A\n * project without a remote has nothing to check the name against.\n */\nfunction nameFitsRemote(project: ProjectIdentity, name: string): boolean {\n return project.kind !== 'remote' || project.identity.endsWith(`/${name.toLowerCase()}`)\n}\n\nfunction knownProjects(pairs: ObservedPair[], probes: Map<string, DirectoryProbe>): KnownProject[] {\n const known = new Map<string, KnownProject>()\n for (const pair of pairs) {\n const found = gitProject(probes.get(pair.cwd))\n if (found === null || pair.repoName === null) continue\n const project = known.get(found.identity) ?? {\n identity: found.identity,\n kind: found.kind,\n roots: new Set<string>(),\n bareNames: new Set<string>(),\n }\n project.roots.add(found.root)\n const isBareFittingName = !isQualified(pair.repoName) && nameFitsRemote(found, pair.repoName)\n if (isBareFittingName) project.bareNames.add(pair.repoName.toLowerCase())\n known.set(found.identity, project)\n }\n return [...known.values()]\n}\n\nfunction singleOrNull<T>(candidates: T[]): T | null {\n return candidates.length === 1 ? candidates[0] : null\n}\n\n/** The project whose root holds the folder, the deepest root first. Null when two projects share that root. */\nfunction byFolder(cwd: string, known: KnownProject[]): KnownProject | null {\n const holders = known.flatMap((project) =>\n [...project.roots]\n .filter((root) => relativeInside(root, cwd) !== null)\n .map((root) => ({ project, depth: root.length }))\n )\n const deepest = Math.max(...holders.map((holder) => holder.depth))\n return singleOrNull(holders.filter((holder) => holder.depth === deepest).map((holder) => holder.project))\n}\n\ntype NamedPair = ObservedPair & { repoName: string }\n\nfunction asFallback(pair: NamedPair, project: KnownProject | null, method: ResolutionMethod): PairResolution | null {\n if (project === null) return null\n return {\n ...pair,\n method,\n project: { identity: project.identity, kind: project.kind, name: pair.repoName, root: null },\n }\n}\n\n/** For `owner/repo`, the one live remote ending in it; for a bare `repo`, the one live project also stored as it. */\nfunction byName(pair: NamedPair, known: KnownProject[]): PairResolution | null {\n const name = pair.repoName.toLowerCase()\n if (isQualified(name)) {\n const remotes = known.filter((project) => project.kind === 'remote' && project.identity.endsWith(`/${name}`))\n return asFallback(pair, singleOrNull(remotes), 'qualified-name')\n }\n const sameBareName = known.filter((project) => project.bareNames.has(name))\n return asFallback(pair, singleOrNull(sameBareName), 'bare-name')\n}\n\n/**\n * For a folder that's gone or no longer a repo, the one live project that fits, if any: the project whose checkout\n * holds the folder, else the one the name fits (`byName`). Two candidates are never guessed between.\n */\nfunction byFallback(pair: NamedPair, known: KnownProject[]): PairResolution | null {\n return asFallback(pair, byFolder(pair.cwd, known), 'folder') ?? byName(pair, known)\n}\n\n/**\n * The folder's own repo — unless the stored name doesn't fit it and does fit exactly one other live project: then\n * the command worked in that other repo (`git -C`). A name that fits neither is kept as an override, but never as an\n * alias, so it can't rename the project or match other history to it.\n */\nfunction byGit(pair: NamedPair, found: ProjectIdentity & { root: string }, known: KnownProject[]): PairResolution {\n const fits = nameFitsRemote(found, pair.repoName)\n const elsewhere = fits\n ? null\n : byName(\n pair,\n known.filter((project) => project.identity !== found.identity)\n )\n if (elsewhere !== null) return elsewhere\n const project = { identity: found.identity, kind: found.kind, name: fits ? pair.repoName : null, root: found.root }\n return { ...pair, method: 'git', project }\n}\n\nfunction resolvePair(pair: ObservedPair, probes: Map<string, DirectoryProbe>, known: KnownProject[]): PairResolution {\n // No name means the folder wasn't in a repo when the event was captured: those events stay unlinked\n if (pair.repoName === null) return { ...pair, method: 'none', project: null }\n // The same pair, with TypeScript told its name is set\n const named: NamedPair = { ...pair, repoName: pair.repoName }\n const found = gitProject(probes.get(pair.cwd))\n if (found !== null) return byGit(named, found, known)\n return (\n byFallback(named, known) ?? {\n ...pair,\n method: 'name',\n project: { ...nameIdentity(pair.repoName), name: pair.repoName, root: null },\n }\n )\n}\n\n/** A bare `x` left on its own joins the one name-only `owner/x`, when there's exactly one. */\nfunction mergeBareNames(resolutions: PairResolution[]): PairResolution[] {\n const qualifiedByRepo = new Map<string, Set<string>>()\n for (const resolution of resolutions) {\n if (resolution.method !== 'name' || !resolution.repoName || !isQualified(resolution.repoName)) continue\n const repo = resolution.repoName.toLowerCase().split('/').at(-1) ?? ''\n qualifiedByRepo.set(repo, (qualifiedByRepo.get(repo) ?? new Set()).add(resolution.project?.identity ?? ''))\n }\n return resolutions.map((resolution) => {\n if (resolution.method !== 'name' || !resolution.repoName || isQualified(resolution.repoName)) return resolution\n const identities = [...(qualifiedByRepo.get(resolution.repoName.toLowerCase()) ?? [])]\n const identity = singleOrNull(identities)\n if (identity === null || resolution.project === null) return resolution\n return { ...resolution, project: { ...resolution.project, identity } }\n })\n}\n\n/**\n * Each pair's project, in order: what git says about the folder now; else a live project whose checkout holds the\n * folder; else the one live project the name fits (`owner/repo` by its remote, a bare `repo` by the bare names it\n * was stored under); else a project of its own, by name.\n */\nexport function resolvePairs(pairs: ObservedPair[], probes: Map<string, DirectoryProbe>): PairResolution[] {\n const known = knownProjects(pairs, probes)\n return mergeBareNames(pairs.map((pair) => resolvePair(pair, probes, known)))\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport { FALLBACK_METHODS, type ResolutionMethod } from './project-resolution'\nimport type { JobIssue, JobVerification } from './types'\n\n// What the project-dimensions job reports: the guesses it made, as issues, and its verification.\n\n/** The events there were when the job resolved every pair: the events it must link, and the count it must keep. */\nexport interface GatherMark {\n throughId: number\n events: number\n}\n\nconst METHOD_LABELS: Record<ResolutionMethod, string> = {\n git: 'git, at a folder that still exists',\n folder: 'a live checkout above a folder that is gone',\n 'qualified-name': 'their owner/repo name',\n 'bare-name': 'a bare name a live repo was also stored under',\n name: 'their name alone (no live repo matched)',\n none: 'nothing: captured outside a repo',\n}\n\nfunction eventCount(count: number): string {\n return `${count} event${count === 1 ? '' : 's'}`\n}\n\nexport function fallbackReason(method: ResolutionMethod, count: number, identity: string | null): string {\n if (method === 'name') return `${eventCount(count)}: no live repo matched, so kept as a project of its own by name`\n return `${eventCount(count)}: joined ${identity ?? 'a project'} by ${METHOD_LABELS[method]}`\n}\n\n/** One issue per pair that found its project by a fallback, so the upgrade's results show every guess it made. */\nexport function fallbackIssues(db: MigrationDatabase): JobIssue[] {\n const rows = db\n .prepare(\n `SELECT resolution.cwd, resolution.repo_name, resolution.method, resolution.event_count, project.identity\n FROM project_resolution resolution LEFT JOIN project ON project.id = resolution.project_id\n ORDER BY resolution.cwd, resolution.repo_name`\n )\n .all() as {\n cwd: string\n repo_name: string\n method: ResolutionMethod\n event_count: number\n identity: string | null\n }[]\n return rows\n .filter((row) => FALLBACK_METHODS.includes(row.method))\n .map((row) => ({\n reference: `${row.cwd} (${row.repo_name})`,\n reason: fallbackReason(row.method, row.event_count, row.identity),\n }))\n}\n\nfunction countRows(db: MigrationDatabase, sql: string, ...parameters: unknown[]): number {\n return (db.prepare(sql).get(...parameters) as { count: number }).count\n}\n\nfunction methodNotes(db: MigrationDatabase): string[] {\n const byMethod = db\n .prepare(\n `SELECT resolution.method, COUNT(*) AS count FROM events\n JOIN project_resolution resolution\n ON resolution.cwd = events.cwd AND resolution.repo_name = COALESCE(events.repo_name, '')\n GROUP BY resolution.method ORDER BY count DESC`\n )\n .all() as { method: ResolutionMethod; count: number }[]\n return byMethod.map((row) => `${eventCount(row.count)} matched by ${METHOD_LABELS[row.method]}`)\n}\n\n/**\n * Passes when every event with a name found a project, which the name-only fallback should guarantee. The job never\n * deletes an event, so events gone since it started were removed on purpose (`undo`, `clear`): noted, not a failure.\n */\nexport function verifyProjectDimensions(db: MigrationDatabase, mark: GatherMark | null): JobVerification {\n const checked = countRows(db, 'SELECT COUNT(*) AS count FROM events')\n const unresolved = countRows(\n db,\n 'SELECT COUNT(*) AS count FROM events WHERE repo_name IS NOT NULL AND project_id IS NULL'\n )\n const unlinkedSaves = countRows(\n db,\n 'SELECT COUNT(*) AS count FROM events WHERE file_path IS NOT NULL AND id NOT IN (SELECT event_id FROM event_file)'\n )\n const keptEvents = countRows(db, 'SELECT COUNT(*) AS count FROM events WHERE id <= ?', mark?.throughId ?? 0)\n const removedEvents = (mark?.events ?? 0) - keptEvents\n const notes = methodNotes(db)\n if (unlinkedSaves > 0) {\n notes.push(`${unlinkedSaves} saved file${unlinkedSaves === 1 ? ' is' : 's are'} in no repo git could find`)\n }\n if (removedEvents > 0) notes.push(`${eventCount(removedEvents)} removed while the upgrade ran (undo or clear)`)\n return {\n checked,\n resolved: checked - unresolved,\n unresolved,\n hasPassed: unresolved === 0,\n notes,\n }\n}\n","import path from 'path'\nimport type { MigrationDatabase } from '../migration-types'\nimport type { DirectoryProbe } from '../../capture/git'\nimport { projectIdentity, type ProjectIdentity } from '../../capture/project-identity'\nimport { withImmediateTransaction, withReadTransaction } from '../transaction'\nimport { FILE_SAVE_ACTION, linkEventFile, upsertBranch, upsertFile, upsertProject } from '../dimensions'\nimport { relativeInside } from '../../utils/working-directory'\nimport { deleteMetaValue, getMetaValue, setMetaValue } from './store'\nimport { foldersToProbe, loadProbes, saveProbe } from './directory-probes'\nimport {\n FALLBACK_METHODS,\n resolvePairs,\n type ObservedPair,\n type PairResolution,\n type ResolutionMethod,\n} from './project-resolution'\nimport { fallbackIssues, fallbackReason, verifyProjectDimensions, type GatherMark } from './project-dimensions-report'\nimport type { BatchResult, DataJob, GatherOutcome, JobIssue } from './types'\n\n// Links every stored event to its project, branch and file (migration 008), for the events captured before capture\n// wrote the links itself. Git is asked once per folder, outside the write lock, and the answer is kept in\n// `directory_probe`: a rerun after a version bump asks nothing and gives the same answer.\n\nconst GATHER_MARK_KEY = 'project-dimensions:gathered'\n\ninterface EventToLink {\n id: number\n cwd: string\n repo_name: string | null\n branch: string | null\n file_path: string | null\n project_id: number | null\n}\n\n/** A pair's project as stored in `project_resolution`. */\ninterface StoredResolution {\n projectId: number | null\n method: ResolutionMethod\n}\n\nfunction pairKey(cwd: string, repoName: string | null): string {\n return `${cwd}\\u0000${repoName ?? ''}`\n}\n\nfunction readGatherMark(db: MigrationDatabase): GatherMark | null {\n const value = getMetaValue(db, GATHER_MARK_KEY)\n return value === null ? null : (JSON.parse(value) as GatherMark)\n}\n\nfunction storeResolution(db: MigrationDatabase, resolution: PairResolution, eventCount: number): StoredResolution {\n const projectId = resolution.project === null ? null : upsertProject(db, resolution.project)\n db.prepare(\n `INSERT OR REPLACE INTO project_resolution (cwd, repo_name, project_id, method, event_count)\n VALUES (?, ?, ?, ?, ?)`\n ).run(resolution.cwd, resolution.repoName ?? '', projectId, resolution.method, eventCount)\n return { projectId, method: resolution.method }\n}\n\n/**\n * Resolves every stored pair at once — the rules compare pairs with each other, so they need the whole set — and\n * marks how far the events went, so batches can tell events captured since (which capture linked itself).\n */\nfunction resolveAllPairs(db: MigrationDatabase): void {\n const { counted, mark } = withReadTransaction(db, () => ({\n counted: db.prepare('SELECT cwd, repo_name, COUNT(*) AS events FROM events GROUP BY cwd, repo_name').all() as {\n cwd: string\n repo_name: string | null\n events: number\n }[],\n mark: db.prepare('SELECT COALESCE(MAX(id), 0) AS throughId, COUNT(*) AS events FROM events').get() as GatherMark,\n }))\n const resolutions = resolvePairs(\n counted.map((row) => ({ cwd: row.cwd, repoName: row.repo_name })),\n loadProbes(db)\n )\n withImmediateTransaction(db, () => {\n resolutions.forEach((resolution, index) => storeResolution(db, resolution, counted[index].events))\n setMetaValue(db, GATHER_MARK_KEY, JSON.stringify(mark))\n })\n}\n\nfunction storedPairs(db: MigrationDatabase): ObservedPair[] {\n const rows = db.prepare('SELECT cwd, repo_name FROM project_resolution').all() as { cwd: string; repo_name: string }[]\n return rows.map((row) => ({ cwd: row.cwd, repoName: row.repo_name === '' ? null : row.repo_name }))\n}\n\n/**\n * A pair first seen after gathering, on an event capture couldn't link. Resolved with the same rules over the folders\n * already probed; a folder never probed counts as unknown, so only the name rules can place it. Batches never call git.\n */\nfunction resolveLatePair(db: MigrationDatabase, pair: ObservedPair, issues: JobIssue[]): StoredResolution {\n const resolutions = resolvePairs([...storedPairs(db), pair], loadProbes(db))\n const resolution = resolutions[resolutions.length - 1]\n const stored = storeResolution(db, resolution, 1)\n if (FALLBACK_METHODS.includes(resolution.method)) {\n const reason = fallbackReason(resolution.method, 1, resolution.project?.identity ?? null)\n issues.push({\n reference: `${pair.cwd} (${pair.repoName ?? ''})`,\n reason: `captured while the upgrade ran; ${reason}`,\n })\n }\n return stored\n}\n\nfunction storedResolution(db: MigrationDatabase, cwd: string, repoName: string | null): StoredResolution | null {\n const row = db\n .prepare('SELECT project_id, method FROM project_resolution WHERE cwd = ? AND repo_name = ?')\n .get(cwd, repoName ?? '') as { project_id: number | null; method: ResolutionMethod } | undefined\n return row === undefined ? null : { projectId: row.project_id, method: row.method }\n}\n\n/** Where a saved file sits — its repo and its path in it — from what git said about its folder. Null: in no repo. */\nfunction locateFile(\n filePath: string,\n probes: Map<string, DirectoryProbe>\n): { identity: ProjectIdentity; root: string; relativePath: string } | null {\n const probe = probes.get(path.dirname(filePath))\n if (probe?.outcome !== 'repo') return null\n const identity = projectIdentity(probe)\n const relativePath = relativeInside(probe.root, filePath)\n if (identity === null || !relativePath) return null\n return { identity, root: probe.root, relativePath }\n}\n\nfunction placeFile(\n db: MigrationDatabase,\n filePath: string,\n probes: Map<string, DirectoryProbe>\n): { projectId: number; relativePath: string } | null {\n const location = locateFile(filePath, probes)\n if (location === null) return null\n const projectId = upsertProject(db, { ...location.identity, name: null, root: location.root })\n return { projectId, relativePath: location.relativePath }\n}\n\n/** One issue per folder whose saves the job can't place, with how many there are. */\nfunction unplacedSaveIssues(\n db: MigrationDatabase,\n probes: Map<string, DirectoryProbe>,\n mark: GatherMark | null\n): JobIssue[] {\n const saves = db\n .prepare('SELECT file_path FROM events WHERE file_path IS NOT NULL AND id <= ?')\n .all(mark?.throughId ?? 0) as { file_path: string }[]\n const unplacedByFolder = new Map<string, number>()\n for (const save of saves) {\n if (locateFile(save.file_path, probes) !== null) continue\n const folder = path.dirname(save.file_path)\n unplacedByFolder.set(folder, (unplacedByFolder.get(folder) ?? 0) + 1)\n }\n return [...unplacedByFolder.entries()]\n .sort(([first], [second]) => first.localeCompare(second))\n .map(([folder, count]) => ({\n reference: folder,\n reason: `${count} saved file${count === 1 ? '' : 's'} here, in no repo git could find`,\n }))\n}\n\ninterface BatchContext {\n mark: GatherMark | null\n probes: Map<string, DirectoryProbe>\n resolutions: Map<string, StoredResolution>\n issues: JobIssue[]\n}\n\nfunction resolutionFor(db: MigrationDatabase, event: EventToLink, context: BatchContext): StoredResolution {\n const key = pairKey(event.cwd, event.repo_name)\n const cached = context.resolutions.get(key)\n if (cached !== undefined) return cached\n const pair = { cwd: event.cwd, repoName: event.repo_name }\n const resolution = storedResolution(db, event.cwd, event.repo_name) ?? resolveLatePair(db, pair, context.issues)\n context.resolutions.set(key, resolution)\n return resolution\n}\n\n/** Captured after the job resolved every pair, and already linked by capture: capture's links are the answer. */\nfunction isLinkedByCapture(event: EventToLink, mark: GatherMark | null): boolean {\n return event.project_id !== null && event.id > (mark?.throughId ?? 0)\n}\n\n/** Links one event. A save takes the project of its file's repo; the branch is kept only for the folder's own repo. */\nfunction linkEvent(db: MigrationDatabase, event: EventToLink, context: BatchContext): void {\n if (isLinkedByCapture(event, context.mark)) return\n const resolution = resolutionFor(db, event, context)\n const file = event.file_path === null ? null : placeFile(db, event.file_path, context.probes)\n const projectId = file?.projectId ?? resolution.projectId\n const isFoldersProject = projectId !== null && projectId === resolution.projectId\n const branchId = event.branch !== null && isFoldersProject ? upsertBranch(db, projectId, event.branch) : null\n db.prepare('UPDATE events SET project_id = ?, branch_id = ? WHERE id = ?').run(projectId, branchId, event.id)\n // A save that can't be placed is recorded once per folder, by the first batch\n if (file === null) return\n const fileId = upsertFile(db, file.projectId, file.relativePath)\n linkEventFile(db, { eventId: event.id, fileId, action: FILE_SAVE_ACTION })\n}\n\n/**\n * After the last batch: projects no event links to any more go, and each project takes the name of its newest event\n * stored under one of its own names — never a `git -C` event's name for another repo.\n */\nfunction finishProjects(db: MigrationDatabase): void {\n db.exec(`\n DELETE FROM project\n WHERE id NOT IN (SELECT project_id FROM events WHERE project_id IS NOT NULL)\n AND id NOT IN (SELECT project_id FROM project_resolution WHERE project_id IS NOT NULL);\n UPDATE project SET name = COALESCE(\n (SELECT repo_name FROM events\n WHERE project_id = project.id\n AND repo_name IN (SELECT name FROM project_alias WHERE project_alias.project_id = project.id)\n ORDER BY id DESC LIMIT 1),\n name\n );\n `)\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const events = db\n .prepare('SELECT id, cwd, repo_name, branch, file_path, project_id FROM events WHERE id > ? ORDER BY id LIMIT ?')\n .all(afterId, limit) as EventToLink[]\n if (events.length === 0) {\n finishProjects(db)\n return { lastId: null, issues: [] }\n }\n const mark = readGatherMark(db)\n const probes = loadProbes(db)\n const issues = afterId === 0 ? [...fallbackIssues(db), ...unplacedSaveIssues(db, probes, mark)] : []\n const context: BatchContext = { mark, probes, resolutions: new Map(), issues }\n for (const event of events) linkEvent(db, event, context)\n return { lastId: events[events.length - 1].id, issues: context.issues }\n}\n\nexport interface ProjectDimensionsOptions {\n probeDirectory: (directory: string) => DirectoryProbe\n}\n\nexport function createProjectDimensionsJob(options: ProjectDimensionsOptions): DataJob {\n return {\n name: 'project-dimensions',\n version: 1,\n description: 'Link stored events to their project, branch and file',\n // Batches rewrite every link up to the gather mark, so only the work tables go; the probes stay, so a rerun never\n // asks git again\n prepare: (db) => {\n db.exec('DELETE FROM event_file; DELETE FROM project_resolution;')\n deleteMetaValue(db, GATHER_MARK_KEY)\n },\n gather: (db, { isOverBudget }): GatherOutcome => {\n for (const folder of foldersToProbe(db)) {\n if (isOverBudget()) return 'incomplete'\n saveProbe(db, folder, options.probeDirectory(folder))\n }\n if (readGatherMark(db) === null) resolveAllPairs(db)\n return 'complete'\n },\n runBatch,\n verify: (db) => verifyProjectDimensions(db, readGatherMark(db)),\n }\n}\n","import type { EventRow } from '../types'\n\n// A frozen copy of how 1.x and 2.0 grouped a day into sessions (src/sessions/grouper.ts and detector.ts at the time\n// of this job), so the summary-anchors job can find an old session's events after the live grouper is gone. Never\n// change it: a summary's key was made by exactly these rules. Pure.\n\nconst IDLE_MS = 30 * 60 * 1000 // the default `session.idleTimeout`: jobs never read the config\nconst LONG_RUNNING_MS = 5 * 60 * 1000\nconst REPO_SWITCH_GAP_MS = 10 * 60 * 1000\n\nfunction splitByIdleGap(events: EventRow[]): EventRow[][] {\n const groups: EventRow[][] = []\n for (const [index, event] of events.entries()) {\n const completedMs = Date.parse(event.captured_at)\n const durationMs = event.duration_ms ?? 0\n const effectiveMs = durationMs >= LONG_RUNNING_MS ? completedMs - durationMs : completedMs\n const isAfterGap = index > 0 && effectiveMs - Date.parse(events[index - 1].captured_at) >= IDLE_MS\n if (index === 0 || isAfterGap) groups.push([event])\n else groups[groups.length - 1].push(event)\n }\n return groups\n}\n\nfunction splitByRepo(events: EventRow[]): EventRow[][] {\n const groups: EventRow[][] = []\n for (const [index, event] of events.entries()) {\n const previous = events[index - 1]\n const isRepoSwitch =\n index > 0 &&\n event.repo_name !== previous.repo_name &&\n Date.parse(event.captured_at) - Date.parse(previous.captured_at) >= REPO_SWITCH_GAP_MS\n if (index === 0 || isRepoSwitch) groups.push([event])\n else groups[groups.length - 1].push(event)\n }\n return groups\n}\n\nexport interface OldSession {\n /** The repo the session was named after: its last event's, else its first's */\n repoName: string | null\n startedAt: string\n events: EventRow[]\n}\n\n/** A day's sessions as the old grouper made them, from its events in `captured_at, id` order. */\nexport function groupOldSessions(dayEvents: EventRow[]): OldSession[] {\n return splitByIdleGap(dayEvents)\n .flatMap(splitByRepo)\n .map((events) => ({\n repoName: events[events.length - 1].repo_name ?? events[0].repo_name ?? null,\n startedAt: events[0].captured_at,\n events,\n }))\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { EventRow } from '../types'\nimport { SESSION_KEY_SQL } from '../migrations/005_session_summaries'\nimport { MAPPED_ORIGIN, mapSummary, type AnchorSource } from '../summary-store'\nimport { anchorOfKeyedSession, type SummaryAnchor } from '../../summary/anchors'\nimport { parseSessionKey, sessionKey } from '../../summary/store'\nimport { localDateOf } from '../../utils/format'\nimport { groupOldSessions, type OldSession } from './frozen-old-sessions'\nimport type { StoredEngine } from '../../ai/engine'\nimport type { BatchResult, DataJob, JobIssue, JobVerification } from './types'\n\n// Maps every summary in `session_summaries` onto the events it describes (the `summary` table, migration 010), so it\n// survives regrouping. Nothing is deleted: a summary whose session can't be found is kept without a range, and\n// every doubtful match is recorded.\n\ninterface OldSummaryRow {\n id: number\n session_key: string\n engine: StoredEngine\n text: string\n created_at: string\n}\n\n/** A batch's regrouped days, so a day several summaries share is read and regrouped once. */\ntype RegroupedDays = Map<string, OldSession[]>\n\n/** The cached old session under the key — looked up within its day, so `idx_sessions_date` narrows the search. */\nfunction eventsOfCachedSession(db: MigrationDatabase, key: string, date: string): EventRow[] | null {\n const session = db\n .prepare(`SELECT id FROM sessions WHERE date = ? AND ${SESSION_KEY_SQL} = ? ORDER BY id LIMIT 1`)\n .get(date, key) as { id: number } | undefined\n if (session === undefined) return null\n return db\n .prepare(\n `SELECT events.* FROM events JOIN session_events ON session_events.event_id = events.id\n WHERE session_events.session_id = ? ORDER BY events.captured_at, events.id`\n )\n .all(session.id) as EventRow[]\n}\n\nfunction regroupDay(db: MigrationDatabase, date: string, days: RegroupedDays): OldSession[] {\n const known = days.get(date)\n if (known !== undefined) return known\n const dayEvents = db\n .prepare(`SELECT * FROM events WHERE date(captured_at, 'localtime') = ? ORDER BY captured_at, id`)\n .all(date) as EventRow[]\n const sessions = groupOldSessions(dayEvents)\n days.set(date, sessions)\n return sessions\n}\n\nfunction findSessionEvents(\n db: MigrationDatabase,\n key: string,\n days: RegroupedDays\n): { source: AnchorSource; events: EventRow[] } {\n const parsed = parseSessionKey(key)\n if (parsed === null) return { source: 'none', events: [] }\n const date = localDateOf(parsed.startedAt)\n const cached = eventsOfCachedSession(db, key, date)\n if (cached !== null && cached.length > 0) return { source: 'cached', events: cached }\n // No longer cached: the old session regrouped from its day with the frozen rules\n const match = regroupDay(db, date, days).find((session) => sessionKey(session.repoName, session.startedAt) === key)\n if (match !== undefined) return { source: 'regrouped', events: match.events }\n return { source: 'none', events: [] }\n}\n\nfunction issuesOf(row: OldSummaryRow, source: AnchorSource, anchor: SummaryAnchor): JobIssue[] {\n const reference = `${row.session_key} (${row.engine})`\n const issues: JobIssue[] = []\n if (source === 'none') {\n issues.push({ reference, reason: 'no events match this session any more; the summary is kept without a range' })\n }\n if (source === 'regrouped') {\n issues.push({\n reference,\n reason:\n 'the session was no longer cached; its range comes from regrouping its day with the default 30-minute idle timeout',\n })\n }\n if (anchor.isUnlinked) {\n issues.push({ reference, reason: \"the session's events aren't linked to a project yet\" })\n }\n if (anchor.isMixed) {\n const percent = Math.round(anchor.share * 100)\n issues.push({\n reference,\n reason: `the session mixed projects: its own project held ${percent}% of its events, so the summary matches no thread`,\n })\n }\n return issues\n}\n\nfunction mapRow(db: MigrationDatabase, row: OldSummaryRow, days: RegroupedDays): JobIssue[] {\n const { source, events } = findSessionEvents(db, row.session_key, days)\n const anchor = anchorOfKeyedSession(events, row.session_key)\n mapSummary(db, {\n sourceKey: row.session_key,\n engine: row.engine,\n text: row.text,\n createdAt: row.created_at,\n anchor,\n anchorSource: source,\n })\n return issuesOf(row, source, anchor)\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const rows = db\n .prepare(\n `SELECT rowid AS id, session_key, engine, text, created_at FROM session_summaries\n WHERE rowid > ? ORDER BY rowid LIMIT ?`\n )\n .all(afterId, limit) as OldSummaryRow[]\n const regroupedDays: RegroupedDays = new Map()\n return {\n lastId: rows.length === 0 ? null : rows[rows.length - 1].id,\n issues: rows.flatMap((row) => mapRow(db, row, regroupedDays)),\n }\n}\n\nfunction countRows(db: MigrationDatabase, sql: string): number {\n return (db.prepare(sql).get() as { count: number }).count\n}\n\nconst SOURCE_LABELS: Record<AnchorSource, string> = {\n thread: 'saved for a thread by the agent skill',\n live: 'from the live session, saved by the agent skill since this upgrade',\n cached: \"from the old session's cached events\",\n regrouped: \"from the old session's day, regrouped\",\n none: 'kept without a range (their events are gone)',\n}\n\n/** How many of the old store's summaries came from each source, and how many mixed projects. */\nfunction sourceNotes(db: MigrationDatabase): string[] {\n const rows = db\n .prepare(\n `SELECT summary.anchor_source AS source, COUNT(*) AS count, SUM(summary.is_mixed) AS mixed\n FROM session_summaries old\n JOIN summary ON summary.source_key = old.session_key AND summary.engine = old.engine\n GROUP BY summary.anchor_source ORDER BY count DESC`\n )\n .all() as { source: AnchorSource; count: number; mixed: number }[]\n const notes = rows.map((row) => `${row.count} ${SOURCE_LABELS[row.source]}`)\n const mixed = rows.reduce((total, row) => total + row.mixed, 0)\n if (mixed > 0) notes.push(`${mixed} described a session that mixed projects, so they match no thread`)\n return notes\n}\n\n/** Passes when every old summary has a row in `summary`: none was lost, whether or not it found its events. */\nfunction verify(db: MigrationDatabase): JobVerification {\n const checked = countRows(db, 'SELECT COUNT(*) AS count FROM session_summaries')\n const joined = `FROM session_summaries old\n LEFT JOIN summary ON summary.source_key = old.session_key AND summary.engine = old.engine`\n const missing = countRows(db, `SELECT COUNT(*) AS count ${joined} WHERE summary.id IS NULL`)\n const unanchored = countRows(\n db,\n `SELECT COUNT(*) AS count ${joined} WHERE summary.id IS NOT NULL AND summary.first_event_id IS NULL`\n )\n const notes = sourceNotes(db)\n if (missing > 0) notes.push(`${missing} summaries have no anchored copy`)\n return { checked, resolved: checked - missing - unanchored, unresolved: unanchored, hasPassed: missing === 0, notes }\n}\n\nexport const summaryAnchorsJob: DataJob = {\n name: 'summary-anchors',\n version: 1,\n description: 'Anchor saved summaries to the events they describe',\n // Only rows the job wrote and can write again; a summary `sessions summary set` saved is never deleted\n prepare: (db) =>\n db.exec(`DELETE FROM summary WHERE origin = '${MAPPED_ORIGIN}' AND EXISTS (\n SELECT 1 FROM session_summaries old WHERE old.session_key = summary.source_key AND old.engine = summary.engine\n )`),\n runBatch,\n verify,\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { BatchResult, DataJob, JobVerification } from './types'\nimport { getJobRow, getMetaValue, isJobDone, setMetaValue } from './store'\nimport { summaryAnchorsJob } from './summary-anchors'\n\n// Drops the linear sessions (WOR-151): nothing has read them since every reader moved onto threads, and their\n// saved summaries live on in `summary`. It only runs once `summary-anchors` copied every old summary there and its\n// check passed, after the runner's backup of this version — so dropping can never lose a summary.\n\n/**\n * In drop order: `session_events` references `sessions`. `sessions.summary` goes with its table. The queries below\n * interpolate these names: they're constants, and SQLite can't bind a table name.\n */\nexport const OLD_TABLES = ['session_events', 'sessions', 'session_summaries']\nconst DROPPED_ROWS_KEY = 'drop-old-sessions:dropped'\n\nexport const OLD_SUMMARIES_NOT_COPIED =\n \"Old summaries aren't all copied yet — the old session tables are kept. Run workscribe bug-report to report it.\"\n\ninterface DroppedTable {\n name: string\n rows: number\n}\n\nfunction existingOldTables(db: MigrationDatabase): string[] {\n return OLD_TABLES.filter(\n (name) => db.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?\").get(name) !== undefined\n )\n}\n\n/** `summary-anchors` is done at its current version, and its check found every old summary copied. */\nfunction areOldSummariesCopied(db: MigrationDatabase): boolean {\n const row = getJobRow(db, summaryAnchorsJob.name)\n if (!isJobDone(row, summaryAnchorsJob) || !row?.verification) return false\n return (JSON.parse(row.verification) as JobVerification).hasPassed\n}\n\n/** The one batch: refuses — failing the job, which retries next run — until the summaries are safe. */\nfunction runBatch(db: MigrationDatabase): BatchResult {\n if (!areOldSummariesCopied(db)) throw new Error(OLD_SUMMARIES_NOT_COPIED)\n const dropped: DroppedTable[] = existingOldTables(db).map((name) => ({\n name,\n rows: (db.prepare(`SELECT COUNT(*) AS rows FROM ${name}`).get() as { rows: number }).rows,\n }))\n for (const name of OLD_TABLES) db.exec(`DROP TABLE IF EXISTS ${name}`)\n if (dropped.length > 0) setMetaValue(db, DROPPED_ROWS_KEY, JSON.stringify(dropped))\n return { lastId: null, issues: [] }\n}\n\nfunction verify(db: MigrationDatabase): JobVerification {\n const dropped = JSON.parse(getMetaValue(db, DROPPED_ROWS_KEY) ?? '[]') as DroppedTable[]\n const checked = dropped.reduce((total, table) => total + table.rows, 0)\n const left = existingOldTables(db)\n const notes = dropped.map((table) => `dropped ${table.name} (${table.rows} rows)`)\n if (left.length > 0) notes.push(`still there: ${left.join(', ')}`)\n return { checked, resolved: checked, unresolved: 0, hasPassed: left.length === 0, notes }\n}\n\nexport const dropOldSessionsJob: DataJob = {\n name: 'drop-old-sessions',\n version: 1,\n description: 'Remove the old session tables, now that their summaries are anchored',\n // Nothing to clear: the job drops tables, and a rerun drops what's left\n prepare: () => undefined,\n runBatch,\n verify,\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { BatchResult, DataJob, JobVerification } from './types'\nimport { getMetaValue, setMetaValue } from './store'\nimport { withImmediateTransaction } from '../transaction'\nimport { linkEventTickets } from '../tickets'\nimport { ticketKeysOf, ticketPatternOf } from '../../capture/tickets'\nimport type { EventRow } from '../types'\n\n// Links the history's events to the tickets their branch names and commit messages name (WOR-144), as capture does\n// for new events. It reads the pattern stored by `config set tickets.pattern`, never the live config, so a rerun\n// derives the same links; changing the pattern through `config set` reruns it from `prepare`.\n\nexport const TICKET_REFS_JOB_NAME = 'ticket-refs'\nconst PATTERN_KEY = 'tickets:pattern'\n\ntype EventToLink = Pick<EventRow, 'id' | 'category' | 'metadata' | 'exit_code' | 'captured_at' | 'branch'> & {\n branch_id: number | null\n linked_branch: string | null\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const events = db\n .prepare(\n `SELECT events.id, events.category, events.metadata, events.exit_code, events.captured_at, events.branch,\n events.branch_id, branch.name AS linked_branch\n FROM events LEFT JOIN branch ON branch.id = events.branch_id\n WHERE events.id > ? AND events.project_id IS NOT NULL\n ORDER BY events.id LIMIT ?`\n )\n .all(afterId, limit) as EventToLink[]\n if (events.length === 0) return { lastId: null, issues: [] }\n const pattern = ticketPatternOf(getMetaValue(db, PATTERN_KEY) ?? undefined)\n for (const event of events) {\n const keys = ticketKeysOf({ linkedBranch: event.linked_branch, event }, pattern)\n linkEventTickets(db, { eventId: event.id, branchId: event.branch_id, keys })\n }\n return { lastId: events[events.length - 1].id, issues: [] }\n}\n\nfunction countOf(db: MigrationDatabase, sql: string): number {\n return (db.prepare(sql).get() as { count: number }).count\n}\n\n/** Links are derived from the events, so none can go missing: the counts are for `status` and `upgrade`. */\nfunction verify(db: MigrationDatabase): JobVerification {\n const checked = countOf(db, 'SELECT COUNT(*) AS count FROM events WHERE project_id IS NOT NULL')\n const tickets = countOf(db, 'SELECT COUNT(*) AS count FROM ticket')\n const branchLinks = countOf(db, 'SELECT COUNT(*) AS count FROM ticket_ref WHERE branch_id IS NOT NULL')\n const commitLinks = countOf(db, 'SELECT COUNT(*) AS count FROM ticket_ref WHERE event_id IS NOT NULL')\n return {\n checked,\n resolved: checked,\n unresolved: 0,\n hasPassed: true,\n notes: [`tickets: ${tickets}; branch links: ${branchLinks}; commit links: ${commitLinks}`],\n }\n}\n\n/**\n * Stores the pattern the history is linked with and restarts the job, so the next data command relinks every event\n * from `prepare`. One transaction: the pattern and the restart never disagree.\n */\nexport function requestTicketRelink(db: MigrationDatabase, pattern: string): void {\n withImmediateTransaction(db, () => {\n setMetaValue(db, PATTERN_KEY, pattern)\n db.prepare('DELETE FROM data_jobs WHERE name = ?').run(TICKET_REFS_JOB_NAME)\n })\n}\n\nexport const ticketRefsJob: DataJob = {\n name: TICKET_REFS_JOB_NAME,\n version: 1,\n description: 'Link stored events to the tickets their branches and commits name',\n // Every link is derived again, so a rerun starts from none\n prepare: (db) => db.exec('DELETE FROM ticket_ref; DELETE FROM ticket'),\n runBatch,\n verify,\n}\n","import type { DataJob } from './types'\nimport { createProjectDimensionsJob } from './project-dimensions'\nimport { probeDirectory } from '../../capture/git'\nimport { summaryAnchorsJob } from './summary-anchors'\nimport { dropOldSessionsJob } from './drop-old-sessions'\nimport { ticketRefsJob } from './ticket-refs'\n\n// Ordered list — append only. Jobs run in this order and a job only starts once every job before it is done, so a\n// later job can rely on an earlier one. To change what a job does for rows it already handled, bump its version:\n// the runner calls its `prepare` and runs it again over every row. Never bump `summary-anchors`: it reads the old\n// session tables, which `drop-old-sessions` removes once it has run. Append new jobs before `drop-old-sessions`, not\n// after: in the stuck state it accepts (a failed `summary-anchors` check) it fails on every run, and the runner never\n// starts a job after a failed one. `ticket-refs` reads `events.branch_id`: bump it whenever `project-dimensions` is bumped.\nexport const JOBS: DataJob[] = [\n createProjectDimensionsJob({ probeDirectory }),\n summaryAnchorsJob,\n ticketRefsJob,\n dropOldSessionsJob,\n]\n","import fs from 'fs'\nimport path from 'path'\nimport type { MigrationDatabase } from '../migration-types'\nimport { isDatabaseLocked, withImmediateTransaction } from '../transaction'\nimport { debugLog } from '../../utils/logger'\nimport { errorMessage } from '../../utils/error-message'\nimport { deleteMetaValue, getMetaValue, setMetaValue } from './store'\n\nexport const BACKUP_KEY_PREFIX = 'backup:'\nexport const BACKUPS_KEPT = 3\n// A claim this old belongs to a process that died mid-backup\nexport const STALE_CLAIM_MS = 10 * 60 * 1000\n\nconst FINISHED_BACKUP = /^workscribe-.+\\.db$/\nconst TEMP_SUFFIX = '.tmp'\nconst PRIVATE_DIRECTORY_MODE = 0o700\nconst PRIVATE_FILE_MODE = 0o600\n\n/** What `meta['backup:<version>']` holds: a claim while a backup is being taken, then where it went. */\nexport type BackupRecord = { state: 'claimed'; at: string } | { state: 'done'; at: string; path: string }\n\nexport type BackupOutcome =\n | { status: 'exists'; path: string }\n | { status: 'created'; path: string }\n | { status: 'busy' }\n | { status: 'failed'; error: string }\n\nexport interface BackupOptions {\n cliVersion: string\n directory: string\n now?: () => Date\n /** Called just before a new backup is taken — the slow part, worth telling the user about. */\n onCreate?: () => void\n}\n\nexport function parseBackupRecord(value: string | null): BackupRecord | null {\n if (value === null) return null\n try {\n return JSON.parse(value) as BackupRecord\n } catch {\n // An unreadable record is treated as no record: the worst case is one extra backup\n return null\n }\n}\n\ntype Claim = { status: 'exists'; path: string } | { status: 'busy' } | { status: 'claimed' }\n\n/** Claims the version's backup under the write lock, so two processes never both take it. */\nfunction claimBackup(db: MigrationDatabase, key: string, now: Date): Claim {\n return withImmediateTransaction(db, () => {\n const record = parseBackupRecord(getMetaValue(db, key))\n if (record?.state === 'done') return { status: 'exists', path: record.path }\n const isClaimedElsewhere = record?.state === 'claimed' && now.getTime() - Date.parse(record.at) < STALE_CLAIM_MS\n if (isClaimedElsewhere) return { status: 'busy' }\n setMetaValue(db, key, JSON.stringify({ state: 'claimed', at: now.toISOString() }))\n return { status: 'claimed' }\n })\n}\n\n/** The backups folder, private to the user: `VACUUM INTO` creates world-readable files. */\nfunction preparePrivateDirectory(directory: string): void {\n fs.mkdirSync(directory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE })\n fs.chmodSync(directory, PRIVATE_DIRECTORY_MODE)\n for (const file of fs.readdirSync(directory)) {\n if (file.endsWith(TEMP_SUFFIX)) fs.rmSync(path.join(directory, file), { force: true })\n }\n}\n\n/** Keeps the newest finished backups; temp files and anything else in the folder are never counted. */\nexport function pruneBackups(directory: string, keep: number = BACKUPS_KEPT): void {\n const backups = fs\n .readdirSync(directory)\n .filter((file) => FINISHED_BACKUP.test(file))\n .map((file) => ({ file, modifiedAt: fs.statSync(path.join(directory, file)).mtimeMs }))\n .sort((first, second) => second.modifiedAt - first.modifiedAt)\n for (const backup of backups.slice(keep)) fs.rmSync(path.join(directory, backup.file), { force: true })\n}\n\nfunction backupKey(cliVersion: string): string {\n return `${BACKUP_KEY_PREFIX}${cliVersion}`\n}\n\n/**\n * The copy was taken while this version's claim was in progress, so it holds that claim. Restored as is, it would\n * make every command think a backup is under way for up to STALE_CLAIM_MS.\n */\nfunction clearClaimInCopy(db: MigrationDatabase, copyPath: string, key: string): void {\n db.prepare('ATTACH DATABASE ? AS backup_copy').run(copyPath)\n try {\n db.prepare('DELETE FROM backup_copy.meta WHERE key = ?').run(key)\n } finally {\n db.exec('DETACH DATABASE backup_copy')\n }\n}\n\n/** Copies the live database (WAL included) to a private file, appearing under its final name only once it's 0600. */\nfunction copyDatabase(db: MigrationDatabase, options: BackupOptions, now: Date): string {\n preparePrivateDirectory(options.directory)\n const name = `workscribe-${options.cliVersion}-${now.toISOString().replace(/[:.]/g, '-')}.db`\n const finalPath = path.join(options.directory, name)\n const tempPath = `${finalPath}${TEMP_SUFFIX}`\n try {\n // VACUUM can't run inside a transaction, so this runs on its own; SQLite gives it a consistent snapshot\n db.prepare('VACUUM INTO ?').run(tempPath)\n fs.chmodSync(tempPath, PRIVATE_FILE_MODE)\n clearClaimInCopy(db, tempPath, backupKey(options.cliVersion))\n fs.renameSync(tempPath, finalPath)\n } catch (error) {\n fs.rmSync(tempPath, { force: true })\n throw error\n }\n pruneBackups(options.directory)\n return finalPath\n}\n\n/**\n * Makes sure this CLI version has a backup of the database before any data job changes it. Never call it from\n * `_capture` or `_hook`: the copy takes as long as the database is big.\n */\nexport function ensureBackup(db: MigrationDatabase, options: BackupOptions): BackupOutcome {\n const now = options.now?.() ?? new Date()\n const key = backupKey(options.cliVersion)\n const claim = tryClaimBackup(db, key, now)\n if (claim.status !== 'claimed') return claim\n\n try {\n options.onCreate?.()\n const backupPath = copyDatabase(db, options, now)\n setMetaValue(db, key, JSON.stringify({ state: 'done', at: now.toISOString(), path: backupPath }))\n return { status: 'created', path: backupPath }\n } catch (error) {\n releaseClaim(db, key)\n return { status: 'failed', error: errorMessage(error) }\n }\n}\n\n/** Another process holding the write lock (a capture, or another command claiming the backup) means try later. */\nfunction tryClaimBackup(db: MigrationDatabase, key: string, now: Date): Claim {\n try {\n return claimBackup(db, key, now)\n } catch (error) {\n if (isDatabaseLocked(error)) return { status: 'busy' }\n throw error\n }\n}\n\n/** Lets the next run try again. If the database refuses even this, the claim goes stale after STALE_CLAIM_MS. */\nfunction releaseClaim(db: MigrationDatabase, key: string): void {\n try {\n deleteMetaValue(db, key)\n } catch (error) {\n debugLog('data job backup: release claim', error)\n }\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport { isDatabaseLocked, withImmediateTransaction, withReadTransaction } from '../transaction'\nimport { debugLog } from '../../utils/logger'\nimport { errorMessage } from '../../utils/error-message'\nimport type { DataJob, JobVerification } from './types'\nimport { failJob, finishJob, getJobRow, isJobDone, recordJobIssues, setJobCursor, startJob } from './store'\n\n// Small enough that one batch holds the write lock for far less than a capture's 5 s busy timeout\nexport const DEFAULT_BATCH_SIZE = 500\n\n// A batch waits at most this long for another process's write lock before the run gives up until next time\nconst BATCH_LOCK_WAIT_MS = 1000\n\nexport type JobOutcome = 'done' | 'paused' | 'failed'\n\nexport interface JobRunResult {\n name: string\n version: number\n outcome: JobOutcome\n verification?: JobVerification\n error?: string\n}\n\n/** Whether this version has a backup: `busy` while another process is taking it, `unavailable` when it failed. */\nexport type BackupReadiness = 'ready' | 'busy' | 'unavailable'\n\n/** `not-needed`: nothing was pending. `busy` or `unavailable`: no backup for this version yet, so nothing ran. */\nexport type BackupState = 'not-needed' | BackupReadiness\n\nexport interface RunResult {\n backup: BackupState\n jobs: JobRunResult[]\n}\n\nexport interface RunOptions {\n /** Makes sure a backup exists for this version, taking one if needed. */\n ensureBackup: () => BackupReadiness\n /** Stop starting new batches after this long. Omitted: run every pending job to completion. */\n budgetMs?: number\n batchSize?: number\n now?: () => Date\n /** Called after each batch, and when a job finishes. */\n onProgress?: (progress: { job: DataJob; elapsedMs: number }) => void\n}\n\ninterface RunContext {\n batchSize: number\n now: () => Date\n isOverBudget: () => boolean\n onProgress: (job: DataJob) => void\n}\n\nconst NEWER_VERSION_ERROR = 'started by a newer version of Workscribe — update Workscribe to finish it'\n\n/**\n * A newer CLI restarted this job with newer code. Finishing it with this code would mix the two, and the newer CLI\n * would then see it done and never redo it.\n */\nclass SupersededError extends Error {\n constructor() {\n super(NEWER_VERSION_ERROR)\n }\n}\n\n/**\n * Runs one batch holding the write lock. Rereads the job's row inside it, so two processes never redo each other's\n * work, and stops if a newer CLI took the job over since the last batch.\n */\nfunction runLockedBatch(db: MigrationDatabase, job: DataJob, context: RunContext): number | null {\n return withImmediateTransaction(\n db,\n () => {\n const row = getJobRow(db, job.name)\n if (row !== null && row.version !== job.version) throw new SupersededError()\n if (row === null || row.status === 'done') return null\n const batch = job.runBatch(db, row.cursor, context.batchSize)\n recordJobIssues(db, job, batch.issues, context.now().toISOString())\n if (batch.lastId !== null) setJobCursor(db, job.name, batch.lastId)\n return batch.lastId\n },\n { busyTimeoutMs: BATCH_LOCK_WAIT_MS }\n )\n}\n\n/** Checks the job's work without the write lock — a full-table check never blocks a capture. */\nfunction finish(db: MigrationDatabase, job: DataJob, context: RunContext): JobRunResult {\n const verification = withReadTransaction(db, () => job.verify(db))\n const isFinished = withImmediateTransaction(db, () => finishJob(db, job, verification, context.now().toISOString()))\n if (!isFinished) throw new SupersededError()\n context.onProgress(job)\n return { name: job.name, version: job.version, outcome: 'done', verification }\n}\n\nfunction runJob(db: MigrationDatabase, job: DataJob, context: RunContext): JobRunResult {\n const summary = { name: job.name, version: job.version }\n // Checked here before `startJob` would touch the row, and again in every batch and when finishing\n const storedVersion = getJobRow(db, job.name)?.version ?? 0\n if (storedVersion > job.version) return { ...summary, outcome: 'paused', error: NEWER_VERSION_ERROR }\n try {\n withImmediateTransaction(db, () => startJob(db, job, context.now().toISOString()))\n // After `startJob`, so a version bump's `prepare` clears the old work before `gather` builds on it\n if (job.gather?.(db, { isOverBudget: context.isOverBudget }) === 'incomplete')\n return { ...summary, outcome: 'paused' }\n while (runLockedBatch(db, job, context) !== null) {\n context.onProgress(job)\n if (context.isOverBudget()) return { ...summary, outcome: 'paused' }\n }\n return finish(db, job, context)\n } catch (error) {\n if (error instanceof SupersededError) return { ...summary, outcome: 'paused', error: error.message }\n // Another process holding the lock (a capture, or the same job elsewhere) isn't a failure: try again next time\n if (isDatabaseLocked(error)) return { ...summary, outcome: 'paused' }\n const message = errorMessage(error)\n recordFailure(db, job, message)\n return { ...summary, outcome: 'failed', error: message }\n }\n}\n\n/** Stores the failure for `status`. If even that write fails, the run still reports the original error. */\nfunction recordFailure(db: MigrationDatabase, job: DataJob, message: string): void {\n try {\n failJob(db, job, message)\n } catch (error) {\n debugLog(`data job ${job.name}: couldn't record the failure`, error)\n }\n}\n\n/**\n * Runs the pending jobs in order, stopping at the first one that doesn't finish, so a job can rely on every job\n * before it. Nothing runs unless `ensureBackup` confirms a backup. Each batch commits on its own: an interrupted run\n * resumes from the last batch it committed.\n */\nexport function runPendingJobs(db: MigrationDatabase, jobs: DataJob[], options: RunOptions): RunResult {\n const pending = jobs.filter((job) => !isJobDone(getJobRow(db, job.name), job))\n if (pending.length === 0) return { backup: 'not-needed', jobs: [] }\n const backup = options.ensureBackup()\n if (backup !== 'ready') return { backup, jobs: [] }\n\n const now = options.now ?? (() => new Date())\n const startedAt = now().getTime()\n const context: RunContext = {\n batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,\n now,\n isOverBudget: () => options.budgetMs !== undefined && now().getTime() - startedAt >= options.budgetMs,\n onProgress: (job) => options.onProgress?.({ job, elapsedMs: now().getTime() - startedAt }),\n }\n\n const results: JobRunResult[] = []\n for (const job of pending) {\n const result = runJob(db, job, context)\n results.push(result)\n if (result.outcome !== 'done') break\n }\n return { backup: 'ready', jobs: results }\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { DataJob, JobVerification } from './types'\nimport { BACKUP_KEY_PREFIX, parseBackupRecord } from './backup'\nimport { getJobRow, getMetaValue, getMetaValuesWithPrefix, isJobDone, setMetaValue, type JobRow } from './store'\n\nconst CLI_VERSION_KEY = 'cliVersion'\n\nexport interface DataStatus {\n pending: string[]\n failed: { name: string; error: string } | null\n /** Finished jobs whose check didn't pass: their counts show data went missing. Never cleared by a rerun. */\n failedChecks: string[]\n /** Rows the finished jobs couldn't resolve, across every job (kept in `data_job_issues`). */\n unresolvedRows: number\n latestBackup: string | null\n}\n\nfunction readVerification(row: JobRow | null): JobVerification | null {\n if (row?.status !== 'done' || !row.verification) return null\n return JSON.parse(row.verification) as JobVerification\n}\n\nfunction readLatestBackup(db: MigrationDatabase): string | null {\n const paths = [...getMetaValuesWithPrefix(db, BACKUP_KEY_PREFIX).values()]\n .map(parseBackupRecord)\n .flatMap((record) => (record?.state === 'done' ? [record] : []))\n .sort((first, second) => second.at.localeCompare(first.at))\n .map((record) => record.path)\n return paths[0] ?? null\n}\n\n/** What `status` and `upgrade` say about the data jobs: pending, failed, checks that didn't pass, the last backup. */\nexport function readDataStatus(db: MigrationDatabase, jobs: DataJob[]): DataStatus {\n const rows = jobs.map((job) => ({ job, row: getJobRow(db, job.name) }))\n const verifications = rows.map(({ job, row }) => ({ name: job.name, verification: readVerification(row) }))\n const failedRow = rows.find(({ row }) => row?.status === 'failed')?.row\n return {\n pending: rows.filter(({ job, row }) => !isJobDone(row, job)).map(({ job }) => job.name),\n failed: failedRow ? { name: failedRow.name, error: failedRow.error ?? 'unknown error' } : null,\n failedChecks: verifications.filter(({ verification }) => verification?.hasPassed === false).map(({ name }) => name),\n unresolvedRows: verifications.reduce((total, { verification }) => total + (verification?.unresolved ?? 0), 0),\n latestBackup: readLatestBackup(db),\n }\n}\n\n/** What a command that reads data only after a job has run says while that job hasn't. */\nexport const UPGRADE_NEEDED_MESSAGE =\n \"Workscribe hasn't finished upgrading your data — run workscribe upgrade, then try again.\"\n\n/**\n * Whether every job named is in `jobs` and done. A name missing from `jobs` counts as not done, so a reader gated on\n * a job never answers before it ran.\n */\nexport function areJobsDone(db: MigrationDatabase, jobs: DataJob[], names: ReadonlySet<string>): boolean {\n const named = jobs.filter((job) => names.has(job.name))\n return named.length === names.size && named.every((job) => isJobDone(getJobRow(db, job.name), job))\n}\n\n/** Remembers the last CLI version that ran a data command — the upgrade signal the CLI had no record of before. */\nexport function recordCliVersion(db: MigrationDatabase, version: string): void {\n if (getMetaValue(db, CLI_VERSION_KEY) === version) return\n setMetaValue(db, CLI_VERSION_KEY, version)\n}\n","import { getDb } from '../db'\nimport { JOBS } from '../db/jobs'\nimport { ensureBackup } from '../db/jobs/backup'\nimport { runPendingJobs, type RunOptions, type RunResult } from '../db/jobs/runner'\nimport { readDataStatus, recordCliVersion, type DataStatus } from '../db/jobs/state'\nimport { BACKUPS_DIR } from '../utils/paths'\nimport { log } from '../utils/logger'\nimport { errorMessage } from '../utils/error-message'\n\n// A command people typed waits at most this long for data jobs before it does its own work; the rest runs next time\nexport const IMPLICIT_RUN_BUDGET_MS = 2000\n// Say something only when a run is slow enough to notice\nconst PROGRESS_NOTICE_AFTER_MS = 1000\n\nconst BACKUP_NOTICE = 'Backing up your Workscribe data before upgrading it (once per version)…'\nconst PROGRESS_NOTICE = 'Upgrading your Workscribe data… (run `workscribe upgrade` to finish it in one go)'\n\nexport function getDataStatus(): DataStatus {\n return readDataStatus(getDb(), JOBS)\n}\n\n/** Runs the pending data jobs on the real database, taking this version's backup first. */\nexport function runDataJobs(cliVersion: string, options: Omit<RunOptions, 'ensureBackup'> = {}): RunResult {\n const db = getDb()\n return runPendingJobs(db, JOBS, {\n ...options,\n ensureBackup: () => {\n const outcome = ensureBackup(db, {\n cliVersion,\n directory: BACKUPS_DIR,\n onCreate: () => log.notice(BACKUP_NOTICE),\n })\n if (outcome.status === 'busy') return 'busy'\n if (outcome.status === 'failed') {\n log.notice(`Couldn't back up your Workscribe data: ${outcome.error}`)\n return 'unavailable'\n }\n return 'ready'\n },\n })\n}\n\n/**\n * Before an allowlisted command (see `shouldRunDataJobs`): records the version and gives pending data jobs a short\n * budget. Never fails the command — a job that couldn't run leaves every reader on its current path.\n */\nexport function runDataJobsBeforeCommand(command: string | undefined, cliVersion: string): void {\n try {\n recordCliVersion(getDb(), cliVersion)\n // `upgrade` runs the jobs itself, with no budget\n if (command === 'upgrade') return\n let hasNoticed = false\n runDataJobs(cliVersion, {\n budgetMs: IMPLICIT_RUN_BUDGET_MS,\n onProgress: ({ elapsedMs }) => {\n if (hasNoticed || elapsedMs < PROGRESS_NOTICE_AFTER_MS) return\n hasNoticed = true\n log.notice(PROGRESS_NOTICE)\n },\n })\n } catch (error) {\n log.notice(`Couldn't upgrade your Workscribe data: ${errorMessage(error)}`)\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport chalk from 'chalk'\nimport { getEventCount, getLastEvent, getRecentOtherRate } from '../db'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { formatDate, todayDate } from '../utils/format'\nimport { getEventsForDate } from '../db'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { getPendingAlerts } from '../db/alerts'\nimport { checkHookHealth } from '../capture/hooks/claude-code'\nimport { detectAgents } from '../skills/detect'\nimport { formatAgentSkillLine } from '../skills/actions'\nimport { printLegacyAiNotice } from './notices'\nimport { getDataStatus } from './data-jobs'\nimport type { DataStatus } from '../db/jobs/state'\n\n// Labelled on the first agent's row only, like the other status sections\nconst AGENT_SKILL_LABEL = 'Agent skill'\n\nfunction describeDataState(status: DataStatus): string {\n if (status.failed) {\n return `${chalk.red(`upgrade failed: ${status.failed.name}`)} ${chalk.dim('(run: workscribe upgrade)')}`\n }\n if (status.failedChecks.length > 0) {\n return `${chalk.red(`upgrade check failed: ${status.failedChecks.join(', ')}`)} ${chalk.dim('(run: workscribe bug-report)')}`\n }\n if (status.pending.length > 0) {\n const count = status.pending.length\n return `${chalk.yellow(`${count} upgrade${count === 1 ? '' : 's'} pending`)} ${chalk.dim('(run: workscribe upgrade)')}`\n }\n return chalk.green('up to date')\n}\n\n/** The \"Data\" line: data jobs pending, failed or failing their check, unresolved rows, and the latest backup. */\nexport function formatDataLines(status: DataStatus): string[] {\n const lines = [` Data ${describeDataState(status)}`]\n const { unresolvedRows } = status\n if (unresolvedRows > 0) {\n lines.push(chalk.dim(` ${unresolvedRows} row${unresolvedRows === 1 ? '' : 's'} left unresolved`))\n }\n if (status.latestBackup) lines.push(chalk.dim(` backup: ${status.latestBackup}`))\n return lines\n}\n\nexport function registerStatus(program: Command): void {\n program\n .command('status')\n .description('Show capture status and event counts')\n .action(() => {\n const config = readConfig()\n printLegacyAiNotice(config)\n const total = getEventCount()\n const todayEvents = getEventsForDate(todayDate())\n const last = getLastEvent()\n const isPaused = fs.existsSync(PAUSE_FLAG_PATH)\n\n log.plain('')\n log.plain(chalk.bold(' Workscribe Status'))\n log.plain('')\n\n if (isPaused) {\n log.plain(` Capture ${chalk.yellow('paused')} ${chalk.dim('(run: workscribe resume)')}`)\n } else {\n log.plain(` Capture ${chalk.green('active')}`)\n }\n\n log.plain(` Events today ${chalk.cyan(String(todayEvents.length))}`)\n log.plain(` Events total ${chalk.cyan(String(total))}`)\n\n if (last) {\n log.plain(` Last event ${formatDate(last.captured_at)} ${chalk.dim(last.command.slice(0, 60))}`)\n }\n\n log.plain('')\n const agents = detectAgents()\n const skillHint = agents.length > 0 ? ' · AI-written ones via the agent skill below' : ''\n log.plain(` Summaries ${chalk.cyan('template')} ${chalk.dim(`(offline)${skillHint}`)}`)\n\n const activeProfiles = config.capture.profiles\n log.plain(\n ` Profiles ${activeProfiles.length > 0 ? chalk.cyan(activeProfiles.join(', ')) : chalk.dim('none')}`\n )\n\n for (const line of formatDataLines(getDataStatus())) log.plain(line)\n\n agents.forEach((agent, index) => {\n const label = index === 0 ? AGENT_SKILL_LABEL : ''\n log.plain(` ${label.padEnd(AGENT_SKILL_LABEL.length)} ${formatAgentSkillLine(agent)}`)\n })\n\n const pendingAlerts = getPendingAlerts()\n if (pendingAlerts.length > 0) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('!')} ${chalk.yellow(String(pendingAlerts.length))} pending security alert${pendingAlerts.length === 1 ? '' : 's'}` +\n chalk.dim(' (run: workscribe alerts)')\n )\n }\n\n const hookHealth = checkHookHealth()\n if (hookHealth.installed && !hookHealth.pinned) {\n log.plain('')\n if (hookHealth.confirmedBroken) {\n log.plain(\n ` ${chalk.yellow('⚠')} Claude Code hook is using an unpinned command and is failing right now — Claude Code activity is not being captured.`\n )\n } else {\n log.plain(\n ` ${chalk.yellow('⚠')} Claude Code hook is using an unpinned command, which can silently break if your default Node version changes.`\n )\n }\n log.plain(chalk.dim(' Run workscribe init to repair it'))\n } else if (hookHealth.installed && hookHealth.nodeMissing) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} The Node.js version the Claude Code hook uses is gone — Claude Code activity is not being captured.`\n )\n log.plain(chalk.dim(' Run workscribe init to repair it'))\n } else if (hookHealth.installed && !hookHealth.guarded) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} Claude Code hook is from an older version — it will show errors in Claude Code if Workscribe is ever removed.`\n )\n log.plain(chalk.dim(' Run workscribe init to update it'))\n }\n\n const { total: weekTotal, otherCount } = getRecentOtherRate(7)\n if (weekTotal > 0) {\n const otherPct = Math.round((otherCount / weekTotal) * 100)\n if (otherPct >= 25) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} ${otherCount} of ${weekTotal} events (${otherPct}%) are uncategorised this week.`\n )\n log.plain(\n chalk.dim(\n ' Run workscribe profile detect — scan current directory for matching profiles\\n' +\n ' Run workscribe profile suggest — analyse your command history'\n )\n )\n }\n }\n\n log.plain('')\n })\n}\n","import { execFileSync } from 'child_process'\nimport path from 'path'\n\nconst GIT_TIMEOUT_MS = 3000\n// Commands are captured when they finish, so a `git commit` that opens a session is stamped after\n// its committer time — by however long the commit hooks ran. A commit closing a session can land\n// just after its event.\nexport const WINDOW_LEAD_MS = 120_000\nexport const WINDOW_TRAIL_MS = 60_000\n// How far back to look when checking who made a captured commit (pulled commits can be old)\nconst AUTHORSHIP_HISTORY_DEPTH = 500\n\nexport interface GitCommit {\n hash: string\n subject: string\n /** When it was committed (ISO 8601, committer date) — to place it on the session's branch timeline */\n committedAt: string\n}\n\ninterface CommitWindow {\n repoRoot: string\n since: string\n until: string\n /** Only commits committed by this email (case-insensitive); all commits when null. */\n author: string | null\n /** Only commits that changed this file */\n filePath?: string\n /** This call's git timeout (default GIT_TIMEOUT_MS) — `getFileCommits` passes what's left of its budget */\n timeoutMs?: number\n /** Look on every local branch, not just HEAD — a commit made on `feat/x` still counts after you're back on `main` */\n allBranches?: boolean\n}\n\n/**\n * Runs git without a shell. Returns trimmed stdout, or null on any failure (no git, not a repo, timeout).\n * `timeoutMs` must be at least 1: Node treats a timeout of 0 as no timeout at all.\n */\nfunction runGit(cwd: string, args: string[], timeoutMs = GIT_TIMEOUT_MS): string | null {\n try {\n return execFileSync('git', args, { cwd, timeout: timeoutMs, stdio: ['ignore', 'pipe', 'ignore'] })\n .toString()\n .trim()\n } catch {\n return null\n }\n}\n\n/** `timeoutMs`: this call's git timeout (default GIT_TIMEOUT_MS). */\nexport function getRepoRoot(cwd: string, timeoutMs?: number): string | null {\n return runGit(cwd, ['rev-parse', '--show-toplevel'], timeoutMs) || null\n}\n\n/** `timeoutMs`: this call's git timeout (default GIT_TIMEOUT_MS). */\nexport function getUserEmail(repoRoot: string, timeoutMs?: number): string | null {\n return runGit(repoRoot, ['config', 'user.email'], timeoutMs) || null\n}\n\n/**\n * Subjects in the last AUTHORSHIP_HISTORY_DEPTH commits on HEAD that only other people committed.\n * Empty without an author to compare against, or when git fails.\n */\nexport function getOthersSubjects(repoRoot: string, author: string | null): string[] {\n if (!author) return []\n const output = runGit(repoRoot, ['log', `-n${AUTHORSHIP_HISTORY_DEPTH}`, '--format=%ce%x09%s'])\n if (!output) return []\n\n const wantedAuthor = author.toLowerCase()\n const mine = new Set<string>()\n const theirs = new Set<string>()\n for (const line of output.split('\\n')) {\n const [email, ...subjectParts] = line.split('\\t')\n const subject = subjectParts.join('\\t')\n if (!subject) continue\n if (email?.toLowerCase() === wantedAuthor) mine.add(subject)\n else theirs.add(subject)\n }\n return [...theirs].filter((subject) => !mine.has(subject))\n}\n\n/**\n * Commits made during a session, oldest first. Window and author both use the *committer* (who made\n * the commit then — including cherry-picks of someone else's work) and only commits reachable from\n * HEAD are seen. `--since` also stops at the first commit older than the window, so after a rebase\n * that leaves dates out of order a few in-window commits can be missed. Captured commit messages\n * cover what this misses.\n */\nexport function getCommitsInWindow(window: CommitWindow): GitCommit[] {\n return findCommitsInWindow(window) ?? []\n}\n\n/** Like `getCommitsInWindow`, but null when git couldn't answer (missing, not a repo, timed out), not just found none. */\nexport function findCommitsInWindow({\n repoRoot,\n since,\n until,\n author,\n filePath,\n timeoutMs,\n allBranches = false,\n}: CommitWindow): GitCommit[] | null {\n const shiftedBy = (iso: string, milliseconds: number): string =>\n new Date(new Date(iso).getTime() + milliseconds).toISOString()\n const output = runGit(\n repoRoot,\n [\n 'log',\n `--since=${shiftedBy(since, -WINDOW_LEAD_MS)}`,\n `--until=${shiftedBy(until, WINDOW_TRAIL_MS)}`,\n '--format=%h%x09%ce%x09%cI%x09%s',\n '--reverse',\n ...(allBranches ? ['--branches'] : []),\n ...(filePath ? ['--', filePath] : []),\n ],\n timeoutMs\n )\n if (output === null) return null\n if (output === '') return []\n\n const wantedAuthor = author?.toLowerCase() ?? null\n return output.split('\\n').flatMap((line) => {\n // The subject goes last, since only it can contain a tab\n const [hash, email, committedAt, ...subjectParts] = line.split('\\t')\n const subject = subjectParts.join('\\t')\n if (!hash || !committedAt || !subject) return []\n if (wantedAuthor && email?.toLowerCase() !== wantedAuthor) return []\n return [{ hash, subject, committedAt }]\n })\n}\n\nexport interface FileCommitsOptions {\n /** The file, with symlinks resolved — git reports the resolved repo root */\n filePath: string\n /** The session window */\n since: string\n until: string\n /** All of the git calls together get at most this long */\n budgetMs: number\n /** The clock, for tests */\n now?: () => number\n}\n\n/** The repo a file is in, and the user's commits in a session window that changed it. */\nexport interface FileCommits {\n repoRoot: string | null\n commits: GitCommit[]\n}\n\n/**\n * The commits in a session window that changed `filePath`, made by the repo's configured user. All three git calls\n * share one `budgetMs` deadline — `context --file` answers the VS Code extension, which gives up after 5 s — and\n * whatever is left when it runs out is skipped, giving no commits rather than a late answer. Pass a resolved path:\n * git reports the resolved repo root.\n */\nexport function getFileCommits(options: FileCommitsOptions): FileCommits {\n const now = options.now ?? Date.now\n const deadline = now() + options.budgetMs\n // Read once per git call and passed on as is, so a clock tick can't turn a checked 1 ms into a timeout of 0\n const timeLeft = (): number | null => {\n const milliseconds = deadline - now()\n return milliseconds >= 1 ? milliseconds : null\n }\n\n const rootTimeout = timeLeft()\n if (rootTimeout === null) return { repoRoot: null, commits: [] }\n const repoRoot = getRepoRoot(path.dirname(options.filePath), rootTimeout)\n if (!repoRoot) return { repoRoot: null, commits: [] }\n\n const emailTimeout = timeLeft()\n if (emailTimeout === null) return { repoRoot, commits: [] }\n const author = getUserEmail(repoRoot, emailTimeout)\n\n const logTimeout = timeLeft()\n if (logTimeout === null) return { repoRoot, commits: [] }\n const commits = getCommitsInWindow({\n repoRoot,\n since: options.since,\n until: options.until,\n author,\n filePath: options.filePath,\n timeoutMs: logTimeout,\n })\n return { repoRoot, commits }\n}\n","// Which commits a session's story tells, and the branch each belongs to. Pure: the git lookups happen elsewhere.\nimport { collectCapturedCommits, isFailedCommand, isSameCommit, mergeCommits } from './commits'\nimport { parseMetadata } from './event-metadata'\nimport { WINDOW_LEAD_MS, WINDOW_TRAIL_MS } from './git-commits'\nimport type { EventRow } from '../db/types'\nimport type { GitCommit } from './git-commits'\n\n/** A commit as the story shows it: under the branch it was made on. */\nexport interface StoryCommit {\n subject: string\n hash: string | null\n committedAt: string | null\n branch: string | null\n}\n\ntype TimedBranch = Pick<EventRow, 'branch' | 'captured_at'>\n\n/**\n * The branch the session was on at `time`: that of its latest event at or before it — or, for a time before every\n * event, the first event's. `git log` doesn't record which branch a commit was made on, so the timeline decides.\n */\nexport function branchAt(events: TimedBranch[], time: string): string | null {\n const moment = Date.parse(time)\n const onBranch = events.filter((event) => event.branch !== null)\n const atOrBefore = onBranch.filter((event) => Date.parse(event.captured_at) <= moment)\n return (atOrBefore.at(-1) ?? onBranch[0])?.branch ?? null\n}\n\n/**\n * The `git commit` capture that recorded this git commit: same subject, captured when it finished — at or after its\n * whole-second commit time, within the hook allowance. Its branch is the one the commit was made on, even when the\n * same command switched branch first (`git switch -c feat/x && git commit -m …`), which the timeline can't see.\n */\nfunction captureOf(events: EventRow[], commit: GitCommit): EventRow | undefined {\n const committed = Date.parse(commit.committedAt)\n return events\n .filter((event) => {\n if (event.category !== 'shipping' || isFailedCommand(event)) return false\n const message = parseMetadata(event.metadata).message\n if (typeof message !== 'string' || !isSameCommit(message, commit.subject)) return false\n const captured = Date.parse(event.captured_at)\n return captured >= committed && captured <= committed + WINDOW_LEAD_MS\n })\n .sort((first, second) => Date.parse(first.captured_at) - Date.parse(second.captured_at))[0]\n}\n\n/**\n * The session's commits — its git commits, then captured ones git didn't return — each with its branch:\n * - a git commit goes under the branch of its own `git commit` capture, or else by its time on the timeline. An AI\n * session's list can't place it (that's the branch the session ended on), nor can a repeated subject's first\n * capture (`wip` on two branches).\n * - a captured-only commit keeps the branch it was captured on.\n *\n * `gitCommits` is null when git wasn't asked. When it was, an AI session's commit that git doesn't confirm is left\n * out: the session lists every commit that reached HEAD, pulled ones too, while git's window holds only yours.\n */\nexport function storyCommitsOf(events: EventRow[], gitCommits: GitCommit[] | null): StoryCommit[] {\n const confirmedByGit = (subject: string): boolean =>\n gitCommits?.some((commit) => isSameCommit(subject, commit.subject)) ?? false\n const captured = collectCapturedCommits(events).filter(\n (commit) => gitCommits === null || commit.committedAt !== null || confirmedByGit(commit.subject)\n )\n const capturedBranches = new Map(captured.map((commit) => [commit.subject, commit.branch]))\n const gitBranchOf = (commit: GitCommit): string | null =>\n captureOf(events, commit)?.branch ?? branchAt(events, commit.committedAt)\n const gitCommitsByHash = new Map((gitCommits ?? []).map((commit) => [commit.hash, commit]))\n return mergeCommits(captured, gitCommits ?? []).map(({ hash, subject, committedAt, source }) => {\n const gitCommit = source === 'git' && hash !== null ? gitCommitsByHash.get(hash) : undefined\n return {\n hash,\n subject,\n committedAt,\n branch: gitCommit ? gitBranchOf(gitCommit) : (capturedBranches.get(subject) ?? null),\n }\n })\n}\n\n/** A session's time span, as `getCommitsInWindow` widens it. */\nexport interface SessionWindow {\n startedAt: string\n endedAt: string\n}\n\n/**\n * Hands each commit of a repo's day to the first session whose window (widened like `getCommitsInWindow`'s)\n * contains its time, so one lookup per repo serves all its sessions and no commit is told twice.\n */\nexport function assignCommitsToSessions(windows: SessionWindow[], commits: GitCommit[]): GitCommit[][] {\n const assigned: GitCommit[][] = windows.map(() => [])\n for (const commit of commits) {\n const moment = Date.parse(commit.committedAt)\n const index = windows.findIndex(\n (window) =>\n moment >= Date.parse(window.startedAt) - WINDOW_LEAD_MS &&\n moment <= Date.parse(window.endedAt) + WINDOW_TRAIL_MS\n )\n if (index !== -1) assigned[index].push(commit)\n }\n return assigned\n}\n","// Reads the git commands that move work between branches — creating a branch, stashing, rebasing, merging and\n// discarding changes — from a stored (already redacted) command, so a summary can say what happened in plain\n// language. Pure: no git calls. Reading the stored command covers past days too, with no change to capture.\nimport { commandSteps, simpleCommandOf } from '../capture/command-steps'\nimport { parseGitInvocation } from './git-invocation'\n\nexport type GitMove =\n | { kind: 'create-branch'; branch: string }\n // A plain switch tells no story of its own, but says which branch the next step of the command ran on.\n // `-` is git's \"the branch you were on before\" (`git checkout -`).\n | { kind: 'switch'; branch: string }\n | { kind: 'stash-push' }\n // `pop` restores the stash and removes it; `apply` restores it and keeps it in `git stash list`\n | { kind: 'stash-pop'; command: 'git stash pop' | 'git stash apply' }\n // Dropping a stash throws its changes away: `drop` the latest, `clear` all of them\n | { kind: 'stash-drop'; command: 'git stash drop' | 'git stash clear' }\n | { kind: 'rebase'; onto: string }\n | { kind: 'merge'; from: string }\n | { kind: 'discard'; command: DiscardCommand }\n\n/** How changes were thrown away, named the way you'd look it up again. */\nexport type DiscardCommand = 'git reset --hard' | 'git checkout -- …' | 'git restore' | 'git clean'\n\n// Ways out of an operation in progress — they move nothing themselves\nconst OPERATION_CONTROL_FLAGS = new Set(['--continue', '--abort', '--skip', '--quit'])\n\n// Flags whose value is the next word, so it isn't read as a branch: `git merge -m \"Merge feat\" feat/x`\nconst MERGE_VALUE_FLAGS = ['-m', '-F', '--file', '-s', '--strategy', '-X', '--strategy-option']\nconst REBASE_VALUE_FLAGS = ['-s', '--strategy', '-X', '--strategy-option', '-x', '--exec', '--onto']\nconst STASH_VALUE_FLAGS = ['-m', '--message', '--pathspec-from-file']\n\n// `git stash <word>`: anything else after `git stash` is a push with options or paths (`git stash -m wip`)\nconst STASH_SUBCOMMANDS = new Set([\n 'push',\n 'save',\n 'pop',\n 'apply',\n 'list',\n 'show',\n 'drop',\n 'clear',\n 'branch',\n 'create',\n 'store',\n])\n\n/** The value after a flag (`-b feat/x`) or attached to it (`--create=feat/x`). */\nfunction flagValue(words: string[], flags: string[]): string | null {\n for (const [index, word] of words.entries()) {\n if (flags.includes(word)) return words[index + 1] ?? null\n const attached = flags.find((flag) => flag.startsWith('--') && word.startsWith(`${flag}=`))\n if (attached) return word.slice(attached.length + 1)\n }\n return null\n}\n\n/** The words that aren't flags or a flag's value; everything after `--` is a path, not a positional. */\nfunction positionals(words: string[], valueFlags: string[] = []): string[] {\n const found: string[] = []\n for (let index = 0; index < words.length; index++) {\n const word = words[index]\n if (word === '--') break\n if (valueFlags.includes(word)) index++\n else if (!word.startsWith('-')) found.push(word)\n }\n return found\n}\n\nfunction isOperationControl(words: string[]): boolean {\n return words.some((word) => OPERATION_CONTROL_FLAGS.has(word))\n}\n\n/** `--track origin/x` (or `-t`, or `--track=direct origin/x`) creates the local branch `x` tracking it. */\nfunction trackedBranch(words: string[]): string | null {\n const isTracking = words.some((word) => word === '--track' || word === '-t' || word.startsWith('--track='))\n const [upstream] = positionals(words)\n if (!isTracking || !upstream) return null\n const slash = upstream.indexOf('/')\n return slash === -1 ? upstream : upstream.slice(slash + 1)\n}\n\n/** A branch created with one of `createFlags` (`-b`, `-c`, …) or by `--track`, or null. */\nfunction createdBranchMove(words: string[], createFlags: string[]): GitMove | null {\n const created = flagValue(words, createFlags) ?? trackedBranch(words)\n return created && !created.startsWith('-') ? { kind: 'create-branch', branch: created } : null\n}\n\n/** `git checkout main`, or `-` for the branch before (a lone `-` isn't a flag). */\nfunction switchTarget(words: string[]): string | undefined {\n return words.includes('-') ? '-' : positionals(words)[0]\n}\n\n/**\n * `git checkout -- file` and `git checkout .` throw away the file's changes. `git checkout main` switches — and so\n * does `git checkout src/a.ts` as far as the words go; the story tells the two apart by whether the branch changed.\n */\nfunction checkoutMove(words: string[]): GitMove | null {\n const created = createdBranchMove(words, ['-b', '-B'])\n if (created) return created\n if (words.includes('--') || positionals(words)[0] === '.') return { kind: 'discard', command: 'git checkout -- …' }\n const target = switchTarget(words)\n return target ? { kind: 'switch', branch: target } : null\n}\n\nfunction switchMove(words: string[]): GitMove | null {\n const created = createdBranchMove(words, ['-c', '-C', '--create', '--force-create'])\n if (created) return created\n const target = switchTarget(words)\n return target ? { kind: 'switch', branch: target } : null\n}\n\nfunction stashMove(words: string[]): GitMove | null {\n const [first] = positionals(words, STASH_VALUE_FLAGS)\n const action = first && STASH_SUBCOMMANDS.has(first) ? first : 'push'\n if (action === 'push' || action === 'save') return { kind: 'stash-push' }\n if (action === 'pop' || action === 'apply') return { kind: 'stash-pop', command: `git stash ${action}` }\n if (action === 'drop' || action === 'clear') return { kind: 'stash-drop', command: `git stash ${action}` }\n return null\n}\n\nfunction rebaseMove(words: string[]): GitMove | null {\n if (isOperationControl(words)) return null\n const onto = flagValue(words, ['--onto']) ?? positionals(words, REBASE_VALUE_FLAGS)[0]\n return onto ? { kind: 'rebase', onto } : null\n}\n\nfunction mergeMove(words: string[]): GitMove | null {\n if (isOperationControl(words)) return null\n const [from] = positionals(words, MERGE_VALUE_FLAGS)\n return from ? { kind: 'merge', from } : null\n}\n\nfunction resetMove(words: string[]): GitMove | null {\n return words.includes('--hard') ? { kind: 'discard', command: 'git reset --hard' } : null\n}\n\n/** `git restore --staged` only unstages; anything that touches the working tree throws changes away. */\nfunction restoreMove(words: string[]): GitMove | null {\n const isUnstageOnly =\n (words.includes('--staged') || words.includes('-S')) && !words.includes('--worktree') && !words.includes('-W')\n return isUnstageOnly ? null : { kind: 'discard', command: 'git restore' }\n}\n\n/** `git clean` deletes untracked files only when forced (`-f`, `-fd`, `--force`); a dry run deletes nothing. */\nfunction cleanMove(words: string[]): GitMove | null {\n const isForced = words.some((word) => word === '--force' || /^-[a-zA-Z]*f/.test(word))\n const isDryRun = words.some((word) => word === '--dry-run' || /^-[a-zA-Z]*n/.test(word))\n return isForced && !isDryRun ? { kind: 'discard', command: 'git clean' } : null\n}\n\nconst MOVE_READERS: Record<string, (words: string[]) => GitMove | null> = {\n checkout: checkoutMove,\n switch: switchMove,\n stash: stashMove,\n rebase: rebaseMove,\n merge: mergeMove,\n reset: resetMove,\n restore: restoreMove,\n clean: cleanMove,\n}\n\n/**\n * The branch-moving work in a command, in the order it ran: every step of a compound command is read\n * (`git stash && git switch -c feat/x && git stash pop`), with git's global options (`-C dir`) set aside.\n */\nexport function gitMovesOf(command: string): GitMove[] {\n return commandSteps(command).flatMap((step) => {\n const invocation = parseGitInvocation(simpleCommandOf(step))\n const readMove = invocation?.subcommand ? MOVE_READERS[invocation.subcommand] : undefined\n const move = readMove?.(invocation?.args.map((arg) => arg.word) ?? [])\n return move ? [move] : []\n })\n}\n","import type { EventRow } from '../db/types'\n\n// captured_at is set at command completion (precmd fires after the command exits).\n// A long-running command's captured_at is its end time, so the gap between it and\n// the prior event equals the command's full duration — falsely triggering the idle\n// threshold. We use its start time (completion − duration) for gap calculation instead.\nexport const LONG_RUNNING_THRESHOLD_MS = 5 * 60 * 1000 // 5 minutes\n\n/**\n * Split a sorted list of events into groups separated by idle gaps.\n */\nexport function splitByIdleGap(events: EventRow[], idleMs: number): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prev = new Date(events[i - 1].captured_at).getTime()\n const currCompleted = new Date(events[i].captured_at).getTime()\n const currDuration = events[i].duration_ms ?? 0\n const currEffective = currDuration >= LONG_RUNNING_THRESHOLD_MS ? currCompleted - currDuration : currCompleted\n\n if (currEffective - prev >= idleMs) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\nexport function minutesBetween(start: string, end: string): number {\n return (new Date(end).getTime() - new Date(start).getTime()) / 60000\n}\n","import type { EventRow } from '../db/types'\nimport { LONG_RUNNING_THRESHOLD_MS, splitByIdleGap } from './grouper'\n\n// A day's work as work blocks, project threads and activity slots (pure). A block is an idle-gap stretch of every\n// event; a thread is one project's events inside it, whatever their order. Switching repos never starts, splits or\n// names anything.\n\n/** Bump when the rules below change: every day is then rebuilt the next time it's read. */\nexport const GROUPING_VERSION = 1\n\nexport const MS_PER_MINUTE = 60 * 1000\nexport const SLOT_MINUTES = 5\nconst SLOT_MS = SLOT_MINUTES * MS_PER_MINUTE\n\n/** A local day as UTC milliseconds, end exclusive. */\nexport interface DayBounds {\n startMs: number\n endMs: number\n}\n\nexport interface ThreadOfBlock {\n /** Null for the block's \"elsewhere\" thread: the events with no project */\n projectId: number | null\n startedAt: string\n endedAt: string\n eventIds: number[]\n activeSlots: number\n}\n\nexport interface WorkBlock {\n startedAt: string\n endedAt: string\n eventCount: number\n activeSlots: number\n threads: ThreadOfBlock[]\n}\n\nexport interface SlotCount {\n slotStart: string\n projectId: number | null\n eventCount: number\n}\n\nexport interface DayBlocks {\n blocks: WorkBlock[]\n slots: SlotCount[]\n}\n\nexport function floorToSlot(ms: number): number {\n return Math.floor(ms / SLOT_MS) * SLOT_MS\n}\n\nexport function toActiveMinutes(activeSlots: number): number {\n return activeSlots * SLOT_MINUTES\n}\n\nfunction projectOf(event: EventRow): number | null {\n return event.project_id ?? null\n}\n\n/**\n * The slots an event occupies: the one its `captured_at` falls in, or for a long-running event every slot from its\n * start (captured time minus duration, as `splitByIdleGap` measures it) to its end. Clamped to the day.\n */\nexport function findSlotsOf(event: EventRow, bounds: DayBounds): number[] {\n const endMs = Date.parse(event.captured_at)\n const durationMs = event.duration_ms ?? 0\n const startMs = durationMs >= LONG_RUNNING_THRESHOLD_MS ? endMs - durationMs : endMs\n const firstSlot = floorToSlot(Math.max(startMs, bounds.startMs))\n const lastSlot = floorToSlot(Math.min(endMs, bounds.endMs - 1))\n const slots: number[] = []\n for (let slot = firstSlot; slot <= lastSlot; slot += SLOT_MS) slots.push(slot)\n return slots\n}\n\nfunction countDistinctSlots(events: EventRow[], bounds: DayBounds): number {\n return new Set(events.flatMap((event) => findSlotsOf(event, bounds))).size\n}\n\n/** One thread per project, in the order each project first appears, holding all of that project's events. */\nfunction buildThreads(events: EventRow[], bounds: DayBounds): ThreadOfBlock[] {\n const byProject = new Map<number | null, EventRow[]>()\n for (const event of events) {\n const projectEvents = byProject.get(projectOf(event))\n if (projectEvents) projectEvents.push(event)\n else byProject.set(projectOf(event), [event])\n }\n return [...byProject.entries()].map(([projectId, projectEvents]) => ({\n projectId,\n startedAt: projectEvents[0].captured_at,\n endedAt: projectEvents[projectEvents.length - 1].captured_at,\n eventIds: projectEvents.map((event) => event.id),\n activeSlots: countDistinctSlots(projectEvents, bounds),\n }))\n}\n\nfunction buildBlock(events: EventRow[], bounds: DayBounds): WorkBlock {\n return {\n startedAt: events[0].captured_at,\n endedAt: events[events.length - 1].captured_at,\n eventCount: events.length,\n // Distinct slots across every project: time worked, never the sum of the threads' times\n activeSlots: countDistinctSlots(events, bounds),\n threads: buildThreads(events, bounds),\n }\n}\n\n/** How many of each project's events fall in each slot of the day. */\nfunction countSlots(events: EventRow[], bounds: DayBounds): SlotCount[] {\n const counts = new Map<string, SlotCount>()\n for (const event of events) {\n const projectId = projectOf(event)\n for (const slot of findSlotsOf(event, bounds)) {\n const key = `${slot}|${projectId ?? ''}`\n const count = counts.get(key) ?? { slotStart: new Date(slot).toISOString(), projectId, eventCount: 0 }\n counts.set(key, { ...count, eventCount: count.eventCount + 1 })\n }\n }\n return [...counts.values()].sort((first, second) => first.slotStart.localeCompare(second.slotStart))\n}\n\n/** The day's blocks, threads and slots, from its events in `captured_at, id` order. */\nexport function buildDayBlocks(events: EventRow[], idleMs: number, bounds: DayBounds): DayBlocks {\n const blocks = splitByIdleGap(events, idleMs)\n .filter((group) => group.length > 0)\n .map((group) => buildBlock(group, bounds))\n return { blocks, slots: countSlots(events, bounds) }\n}\n","import { formatDuration } from '../utils/format'\nimport type { SessionPayload } from '../ai/types'\n\n/** Shown when a session has neither a repo nor any events to name it by. */\nexport const UNKNOWN_LOCATION = 'unknown'\n\ntype HeadingFields = Pick<SessionPayload, 'project' | 'branch' | 'durationMinutes'> &\n Partial<Pick<SessionPayload, 'branches' | 'activeMinutes'>>\n\n/** Every branch the session touched, in the order first seen — or its one branch when that's all that's known. */\nfunction headingBranches(session: HeadingFields): string[] {\n if (session.branches && session.branches.length > 0) return session.branches\n return session.branch ? [session.branch] : []\n}\n\n/**\n * `project (feat/auth, main) — duration`, as used in headings and the --verbose event listing. A thread shows its\n * active time: its span can include other projects' work.\n */\nexport function formatSessionLabel(session: HeadingFields): string {\n const branches = headingBranches(session)\n const branchList = branches.length > 0 ? ` (${branches.join(', ')})` : ''\n const minutes = session.activeMinutes ?? session.durationMinutes\n return `${session.project ?? UNKNOWN_LOCATION}${branchList} — ${formatDuration(minutes)}`\n}\n\n/** The `## project (branches) — duration` line every session summary starts with. */\nexport function formatSessionHeading(session: HeadingFields): string {\n return `## ${formatSessionLabel(session)}`\n}\n","import os from 'os'\nimport path from 'path'\nimport { formatDate } from '../utils/format'\nimport { parseMetadata } from './event-metadata'\nimport { storyCommitsOf } from './story-commits'\nimport { isFailedCommand } from './commits'\nimport { gitMovesOf } from '../normalize/git-move'\nimport type { EventPayload, SessionPayload } from '../ai/types'\nimport type { EventRow } from '../db/types'\nimport type { WorkscribeConfig } from '../config'\nimport type { GitCommit } from './git-commits'\nimport type { DayBlock, DayThread } from '../db/day-threads'\nimport { minutesBetween } from '../sessions/grouper'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { UNKNOWN_LOCATION } from './heading'\n\n/** What sessionLocation needs to name a session that isn't in a git repo. */\nexport interface LocationContext {\n projects: Record<string, string>\n homeDir: string\n}\n\nexport function locationContextFrom(config: Pick<WorkscribeConfig, 'projects'>): LocationContext {\n return { projects: config.projects ?? {}, homeDir: os.homedir() }\n}\n\nconst DEFAULT_LOCATION_CONTEXT: LocationContext = { projects: {}, homeDir: os.homedir() }\n\n// Only git commands can move work between branches; anything else isn't worth parsing\nconst MENTIONS_GIT = /(^|[\\s;&|(])git\\s/\n\n/**\n * The command's branch moves, read from the stored (redacted) command. When the command failed, there's no telling\n * which step did — except that a new branch the session is now on was created (`git switch -c x && npm test` with\n * failing tests), so only that move is kept.\n */\nfunction gitMovesField(row: EventRow): Pick<EventPayload, 'gitMoves'> {\n if (!MENTIONS_GIT.test(row.command)) return {}\n const moves = gitMovesOf(row.command)\n const gitMoves = isFailedCommand(row)\n ? moves.filter((move) => move.kind === 'create-branch' && move.branch === row.branch)\n : moves\n return gitMoves.length > 0 ? { gitMoves } : {}\n}\n\nexport function eventRowToPayload(row: EventRow): EventPayload {\n const metadata = parseMetadata(row.metadata)\n const payload: EventPayload = {\n category: row.category,\n metadata,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n branch: row.branch,\n capturedAt: row.captured_at,\n ...(row.source ? { source: row.source } : {}),\n ...gitMovesField(row),\n }\n if (row.category !== 'other' || typeof metadata.hint !== 'string') return payload\n\n // Unclassified commands carry their raw hint separately so the prompt can show it on its own\n const { hint, tool: _tool, ...rest } = metadata\n return { ...payload, hint, metadata: rest }\n}\n\nfunction branchesOnly(events: Pick<EventRow, 'branch'>[]): string[] {\n return events.map((event) => event.branch).filter((branch): branch is string => branch !== null)\n}\n\n/** The branches a session's events happened on: distinct, in the order first seen, events outside a repo skipped. */\nexport function branchesOf(events: Pick<EventRow, 'branch'>[]): string[] {\n return [...new Set(branchesOnly(events))]\n}\n\n/** The session's branch: the latest it was on — where the work was heading by the end. Null outside a repo. */\nexport function sessionBranch(events: Pick<EventRow, 'branch'>[]): string | null {\n return branchesOnly(events).at(-1) ?? null\n}\n\nfunction mostFrequentCwdOf(events: EventRow[]): string | null {\n const counts = new Map<string, number>()\n for (const event of events) counts.set(event.cwd, (counts.get(event.cwd) ?? 0) + 1)\n let best: string | null = null\n for (const [cwd, count] of counts) {\n if (best === null || count > (counts.get(best) ?? 0)) best = cwd\n }\n return best\n}\n\n/** What naming a stretch of work by its folders reads: its project's name, null outside any project. */\ninterface WorkEvents {\n repoName: string | null\n events: EventRow[]\n}\n\n/** The directory most of a stretch of work's events ran in, or null when it has no events. */\nexport function mostFrequentCwd(work: Pick<WorkEvents, 'events'>): string | null {\n return mostFrequentCwdOf(work.events)\n}\n\n/**\n * A directory inside a thread's repo, for git lookups: the most-used one among its events captured inside a repo,\n * else its most-used one. It doesn't match `repo_name` against a name — a project's name can differ from the text\n * its events were stored under. (Agents often run `cd repo && …` from a parent folder, so the most-used folder\n * overall can miss the repo.)\n */\nexport function threadRepoCwd(events: EventRow[]): string | null {\n return mostFrequentCwdOf(events.filter((event) => event.repo_name !== null)) ?? mostFrequentCwdOf(events)\n}\n\nfunction homeRelative(directory: string, homeDir: string): string {\n if (directory === homeDir) return '~'\n if (directory.startsWith(homeDir + path.sep)) return `~${directory.slice(homeDir.length)}`\n return directory\n}\n\n/**\n * The name a session is shown under: its repo, or — for work outside any git repo — the directory\n * most of its commands ran in (a config.projects name if one is set, else the home-relative path),\n * so the user can always tell where the work happened.\n */\nexport function sessionLocation(work: WorkEvents, context: LocationContext = DEFAULT_LOCATION_CONTEXT): string | null {\n if (work.repoName) return work.repoName\n const cwd = mostFrequentCwd(work)\n if (!cwd) return null\n return context.projects[cwd] ?? homeRelative(cwd, context.homeDir)\n}\n\nexport interface SessionPayloadOptions {\n /** How to name a session outside git */\n context?: LocationContext\n /** The session's commits from `git log`, or null when git wasn't asked — captured ones are always used */\n gitCommits?: GitCommit[] | null\n}\n\n/** A stretch of work as the template tells it: the thread `context` answers with, or a thread of the day. */\nexport interface PayloadSpan extends WorkEvents {\n startedAt: string\n endedAt: string\n durationMinutes: number\n}\n\nexport function spanToPayload(\n span: PayloadSpan,\n date: string,\n { context = DEFAULT_LOCATION_CONTEXT, gitCommits = null }: SessionPayloadOptions = {}\n): SessionPayload {\n return {\n date,\n project: sessionLocation(span, context),\n branch: sessionBranch(span.events),\n branches: branchesOf(span.events),\n startTime: formatDate(span.startedAt),\n endTime: formatDate(span.endedAt),\n durationMinutes: span.durationMinutes,\n events: span.events.map(eventRowToPayload),\n commits: storyCommitsOf(span.events, gitCommits),\n }\n}\n\n/** One thread of a block as the template tells it: its own span and active time, and the projects alongside it. */\nexport function threadToPayload(\n thread: DayThread,\n block: DayBlock,\n options: SessionPayloadOptions = {}\n): SessionPayload {\n const durationMinutes = minutesBetween(thread.startedAt, thread.endedAt)\n return {\n ...spanToPayload({ ...thread, durationMinutes }, block.date, options),\n activeMinutes: toActiveMinutes(thread.activeSlots),\n projectId: thread.projectId,\n alongside: block.threads\n .filter((other) => other.id !== thread.id)\n .map((other) => sessionLocation(other, options.context) ?? UNKNOWN_LOCATION),\n }\n}\n","/**\n * Versions of the `--json` documents read by coding agents and other tools, one per document. Fields may be added\n * within a version; renaming, removing or retyping a field — or changing what it means — bumps that document's\n * version. Tests pin the key sets.\n */\n/** 2: sessions are project threads inside work blocks (WOR-147). */\nexport const SESSIONS_SCHEMA_VERSION = 2\nexport const EVENTS_SCHEMA_VERSION = 1\n","import { eventRowToPayload } from '../payload'\nimport { buildExcludedSet } from '../exclusions'\nimport type { EventRow } from '../../db/types'\n\n/** Events the contract reports: the AI engines' exclusions apply, and notes go to `notes` instead. */\nexport function contractEvents(rows: EventRow[], includeCategories: string[]): EventRow[] {\n const excluded = buildExcludedSet(includeCategories)\n return rows.filter((row) => row.category !== 'note' && !excluded.has(row.category))\n}\n\nexport function collectNotes(rows: EventRow[]): string[] {\n return rows.flatMap((row) => {\n if (row.category !== 'note') return []\n const { text } = eventRowToPayload(row).metadata\n return typeof text === 'string' && text !== '' ? [text] : []\n })\n}\n","import { branchesOf, eventRowToPayload, mostFrequentCwd, sessionBranch, sessionLocation } from '../payload'\nimport { EVENTS_SCHEMA_VERSION, SESSIONS_SCHEMA_VERSION } from './constants'\nimport { collectNotes, contractEvents } from './utils'\nimport { collectCapturedCommits, mergeCommits } from '../commits'\nimport { minutesBetween } from '../../sessions/grouper'\nimport { toActiveMinutes } from '../../sessions/blocks'\nimport type { EventRow } from '../../db/types'\nimport type { DayBlock, DayThread } from '../../db/day-threads'\nimport type {\n BlockJson,\n EventJson,\n EventsJson,\n EventsJsonOptions,\n SessionJson,\n SessionJsonOptions,\n SessionsJson,\n} from './types'\n\n/** Never includes the full command, the event's cwd or an IDE file path. */\nexport function eventToJson(row: EventRow): EventJson {\n const { category, metadata, hint, exitCode, durationMs } = eventRowToPayload(row)\n return {\n capturedAt: row.captured_at,\n category,\n metadata,\n ...(hint === undefined ? {} : { hint }),\n exitCode,\n durationMs,\n source: row.source,\n }\n}\n\n/** One thread of a block as a session of the contract. */\nexport function threadToJson(\n thread: DayThread,\n block: DayBlock,\n { location, git, includeCategories }: SessionJsonOptions\n): SessionJson {\n return {\n key: thread.key,\n project: sessionLocation(thread, location),\n repoName: thread.repoName,\n repoPath: git.repoPath,\n cwd: mostFrequentCwd(thread),\n branch: sessionBranch(thread.events),\n branches: branchesOf(thread.events),\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n durationMinutes: minutesBetween(thread.startedAt, thread.endedAt),\n activeMinutes: toActiveMinutes(thread.activeSlots),\n eventCount: thread.eventCount,\n blockId: block.id,\n alongside: block.threads.filter((other) => other.id !== thread.id).map((other) => other.key),\n // Field by field, so a field added to `AgentSessionInfo` (WOR-136 adds more) never reaches the contract unasked\n agentSessions: thread.agentSessions.map(({ source, sessionId, startedAt, lastSeenAt }) => ({\n source,\n sessionId,\n startedAt,\n lastSeenAt,\n })),\n tickets: thread.tickets,\n notes: collectNotes(thread.events),\n commits: mergeCommits(collectCapturedCommits(thread.events), git.commits, git.othersSubjects),\n events: contractEvents(thread.events, includeCategories).map(eventToJson),\n }\n}\n\nexport function blockToJson(block: DayBlock): BlockJson {\n return {\n id: block.id,\n startedAt: block.startedAt,\n endedAt: block.endedAt,\n eventCount: block.eventCount,\n activeMinutes: toActiveMinutes(block.activeSlots),\n threadKeys: block.threads.map((thread) => thread.key),\n }\n}\n\nexport function buildSessionsJson(date: string, blocks: BlockJson[], sessions: SessionJson[]): SessionsJson {\n return { schemaVersion: SESSIONS_SCHEMA_VERSION, date, blocks, sessions }\n}\n\nexport function buildEventsJson(\n date: string,\n rows: EventRow[],\n { limit, includeCategories }: EventsJsonOptions\n): EventsJson {\n const events = contractEvents(rows, includeCategories)\n return {\n schemaVersion: EVENTS_SCHEMA_VERSION,\n date,\n total: events.length,\n events: events.slice(0, limit).map(eventToJson),\n }\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport { loadAIToolRegistry } from '../config/registry'\nimport { readConfig } from '../config'\nimport { buildEventsJson } from '../summary/contract/build'\n\nconst CATEGORY_COLORS: Record<string, (s: string) => string> = {\n shipping: chalk.green,\n versioning: chalk.blue,\n building: chalk.cyan,\n running: chalk.white,\n testing: chalk.magenta,\n linting: chalk.yellow,\n dependencies: chalk.yellow,\n infrastructure: chalk.red,\n releasing: chalk.bold.green,\n database: chalk.bold.blue,\n files: chalk.dim.yellow,\n exploring: chalk.dim.cyan,\n ai_session: chalk.bold.magenta,\n other: chalk.gray,\n}\n\nconst AI_TOOL_DISPLAY_NAMES: Record<string, string> = Object.fromEntries(\n loadAIToolRegistry().map((entry) => [entry.tool, entry.displayName])\n)\n\nfunction isPositiveWholeNumber(value: number): boolean {\n return Number.isInteger(value) && value > 0\n}\n\nexport function registerEvents(program: Command): void {\n program\n .command('events')\n .description('List captured events')\n .option('--date <YYYY-MM-DD>', 'date to list events for (default: today)')\n .option('--limit <n>', 'max events to show', parseInt)\n .option('--json', 'output the versioned JSON contract (structured events, no raw commands)')\n .action((options) => {\n let date = todayDate()\n if (options.date) {\n try {\n date = parseDate(options.date)\n } catch (error) {\n log.error((error as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n\n if (options.json) {\n // Scripts call the JSON contract, so a bad --limit is an error rather than a silently odd list\n if (options.limit !== undefined && !isPositiveWholeNumber(options.limit)) {\n log.error('--limit must be a positive whole number')\n process.exit(1)\n }\n const includeCategories = readConfig().summary?.includeCategories ?? []\n log.plain(JSON.stringify(buildEventsJson(date, events, { limit: options.limit, includeCategories })))\n return\n }\n\n const limit = options.limit ?? events.length\n\n if (events.length === 0) {\n log.plain(`No events captured for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n for (const event of events.slice(0, limit)) {\n const colorFn = CATEGORY_COLORS[event.category] ?? chalk.gray\n const time = formatDate(event.captured_at)\n const category = colorFn(event.category.padEnd(20))\n const repo = event.repo_name ? chalk.dim(` [${event.repo_name}]`) : ''\n const exitBadge = event.exit_code && event.exit_code !== 0 ? chalk.red(` ✗${event.exit_code}`) : ''\n\n let cmdDisplay: string\n if (event.category === 'ai_session') {\n let meta: Record<string, unknown> = {}\n try {\n meta = event.metadata ? (JSON.parse(event.metadata) as Record<string, unknown>) : {}\n } catch {\n /* ignore */\n }\n const toolKey = typeof meta.tool === 'string' ? meta.tool : ''\n const toolName = AI_TOOL_DISPLAY_NAMES[toolKey] ?? toolKey\n cmdDisplay = chalk.white(toolName.padEnd(20)) + chalk.magenta(' ✦')\n } else {\n cmdDisplay = chalk.dim(event.command.slice(0, 55))\n }\n\n log.plain(` ${chalk.dim(time)} ${category} ${cmdDisplay}${repo}${exitBadge}`)\n }\n\n if (limit < events.length) {\n log.plain('')\n log.plain(chalk.dim(` … and ${events.length - limit} more (remove --limit to see all)`))\n }\n\n log.plain('')\n })\n}\n","// Rules and messages for `workscribe sessions summary set` — pure; the command does the I/O.\n\nconst BYTES_PER_KB = 1024\nconst MAX_SKILL_SUMMARY_KB = 8\n\n/** Largest summary the agent skill may save for one session, in bytes. */\nexport const MAX_SKILL_SUMMARY_BYTES = MAX_SKILL_SUMMARY_KB * BYTES_PER_KB\n\n// Shown whenever no summary text arrives, so the fix is a copy-paste away\nconst SEND_TEXT_EXAMPLE = [\n 'Send the summary text into the command, for example:',\n '',\n \" workscribe sessions summary set --key '<key from workscribe sessions --json>' <<'EOF'\",\n ' ## my-project (main) — 1h 30m',\n ' - What you got done',\n ' EOF',\n].join('\\n')\n\nexport const INTERACTIVE_STDIN_MESSAGE = `This command saves summary text that is sent to it — it can't be typed in here.\\n${SEND_TEXT_EXAMPLE}`\n\nexport function stdinReadFailedMessage(error: unknown): string {\n return `Couldn't read the summary text that was sent in: ${(error as Error).message}`\n}\n\n/** `16384` → `16,384` — thousands separators only, no unit scaling. */\nfunction formatCount(count: number): string {\n return count.toLocaleString('en-US')\n}\n\n/** A friendly error for a summary that can't be saved, or null when it's fine. */\nexport function validateSkillSummary(text: string): string | null {\n if (text.trim() === '') return `Nothing to save — no summary text was received.\\n${SEND_TEXT_EXAMPLE}`\n const bytes = Buffer.byteLength(text, 'utf-8')\n if (bytes > MAX_SKILL_SUMMARY_BYTES) {\n return (\n `This summary is too long to save (${formatCount(bytes)} bytes; the limit is ` +\n `${formatCount(MAX_SKILL_SUMMARY_BYTES)}, about ${MAX_SKILL_SUMMARY_KB} KB). Shorten it and try again.`\n )\n }\n return null\n}\n","import fs from 'fs'\n\nconst STDIN_FILE_DESCRIPTOR = 0\n\n/** True when stdin is a terminal — reading it would wait for typing rather than piped input. */\nexport function isStdinInteractive(): boolean {\n return Boolean(process.stdin.isTTY)\n}\n\n/** Everything piped into the process. Throws if stdin can't be read. */\nexport function readAllStdin(): string {\n return fs.readFileSync(STDIN_FILE_DESCRIPTOR, 'utf-8')\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { EventRow } from './types'\nimport type { DayBlocks } from '../sessions/blocks'\nimport { withReadTransaction } from './transaction'\n\n// The stored work blocks, threads and slots (migration 009), and the stamp that says what a day was built from.\n// `node:sqlite`-free: it takes the connection, so tests run it on an in-memory database.\n\n/** What a day's blocks were built from. Any difference from the day as it is now means they're out of date. */\nexport interface DayStamp {\n groupingVersion: number\n idleMs: number\n eventCount: number\n /** `SUM(project_id × id)`: weighted by the event, so two events swapping projects still changes it */\n linkChecksum: number\n /**\n * `SUM(project_id² × id)`, each term reduced by a prime modulus so it can't overflow. Squaring isn't linear, so changes that cancel out in\n * `linkChecksum` (one event losing project 1 while another gains project 2) still change this one\n */\n linkSpread: number\n /** Null for a day with no events */\n maxEventId: number | null\n}\n\n/** What a day is grouped with: the rules' version and the idle timeout from the config. */\nexport type GroupingBasis = Pick<DayStamp, 'groupingVersion' | 'idleMs'>\n\n/** A day's start and end as stored `captured_at` strings, end exclusive (`localDayBounds`). */\nexport interface DayRange {\n start: string\n end: string\n}\n\nexport interface StoredThread {\n id: number\n blockId: number\n projectId: number | null\n startedAt: string\n endedAt: string\n eventCount: number\n activeSlots: number\n}\n\nexport interface StoredBlock {\n id: number\n date: string\n startedAt: string\n endedAt: string\n eventCount: number\n activeSlots: number\n groupingVersion: number\n threads: StoredThread[]\n}\n\nconst THREADS_OF_BLOCK_SQL = `SELECT id, block_id AS blockId, project_id AS projectId, started_at AS startedAt,\n ended_at AS endedAt, event_count AS eventCount, active_slots AS activeSlots\n FROM thread WHERE block_id = ? ORDER BY id`\n\nconst IN_DAY = 'captured_at >= $start AND captured_at < $end'\n// A prime near 10⁹: each factor is reduced by it before multiplying, so no term or sum overflows 64-bit integers\nconst SPREAD_MODULUS = 1_000_000_007\n\n/** The day's stamp as its events are now — one indexed range query. */\nexport function currentDayStamp(db: MigrationDatabase, range: DayRange, basis: GroupingBasis): DayStamp {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS eventCount, COALESCE(SUM(COALESCE(project_id, 0) * id), 0) AS linkChecksum,\n COALESCE(SUM(COALESCE(project_id, 0) * COALESCE(project_id, 0) % ${SPREAD_MODULUS}\n * (id % ${SPREAD_MODULUS}) % ${SPREAD_MODULUS}), 0) AS linkSpread,\n MAX(id) AS maxEventId\n FROM events WHERE ${IN_DAY}`\n )\n .get(range) as Omit<DayStamp, keyof GroupingBasis>\n return { ...basis, ...row }\n}\n\nexport function readDayStamp(db: MigrationDatabase, date: string): DayStamp | null {\n const row = db\n .prepare(\n `SELECT grouping_version AS groupingVersion, idle_ms AS idleMs, event_count AS eventCount,\n link_checksum AS linkChecksum, link_spread AS linkSpread, max_event_id AS maxEventId\n FROM grouping_day WHERE date = ?`\n )\n .get(date) as DayStamp | undefined\n return row ?? null\n}\n\n/** Compared field by field in JS, so an empty day's null `maxEventId` matches itself. */\nexport function isSameStamp(stored: DayStamp | null, current: DayStamp): boolean {\n return (\n stored !== null &&\n stored.groupingVersion === current.groupingVersion &&\n stored.idleMs === current.idleMs &&\n stored.eventCount === current.eventCount &&\n stored.linkChecksum === current.linkChecksum &&\n stored.linkSpread === current.linkSpread &&\n stored.maxEventId === current.maxEventId\n )\n}\n\n/** The day's events in grouping order — the same range the stamp counts, so the two always agree. */\nexport function getEventsInRange(db: MigrationDatabase, range: DayRange): EventRow[] {\n return db.prepare(`SELECT * FROM events WHERE ${IN_DAY} ORDER BY captured_at ASC, id ASC`).all(range) as EventRow[]\n}\n\n/** What `replaceDayBlocks` stores for a day: its blocks and slots, and the stamp they were built from. */\nexport interface DayBuild {\n date: string\n dayBlocks: DayBlocks\n stamp: DayStamp\n}\n\nfunction insertBlocks(db: MigrationDatabase, { date, dayBlocks, stamp }: DayBuild): void {\n const insertBlock = db.prepare(\n `INSERT INTO work_block (date, started_at, ended_at, event_count, active_slots, grouping_version)\n VALUES (?, ?, ?, ?, ?, ?) RETURNING id`\n )\n const insertThread = db.prepare(\n `INSERT INTO thread (block_id, project_id, started_at, ended_at, event_count, active_slots)\n VALUES (?, ?, ?, ?, ?, ?) RETURNING id`\n )\n const insertThreadEvent = db.prepare('INSERT INTO thread_event (thread_id, event_id) VALUES (?, ?)')\n for (const block of dayBlocks.blocks) {\n const { id: blockId } = insertBlock.get(\n date,\n block.startedAt,\n block.endedAt,\n block.eventCount,\n block.activeSlots,\n stamp.groupingVersion\n ) as { id: number }\n for (const thread of block.threads) {\n const { id: threadId } = insertThread.get(\n blockId,\n thread.projectId,\n thread.startedAt,\n thread.endedAt,\n thread.eventIds.length,\n thread.activeSlots\n ) as { id: number }\n for (const eventId of thread.eventIds) insertThreadEvent.run(threadId, eventId)\n }\n }\n}\n\n/** Replaces everything stored for the day — blocks (with their threads and links), slots and stamp. Call it in a transaction. */\nexport function replaceDayBlocks(db: MigrationDatabase, build: DayBuild): void {\n const { date, dayBlocks, stamp } = build\n db.prepare('DELETE FROM work_block WHERE date = ?').run(date)\n db.prepare('DELETE FROM activity_slot WHERE date = ?').run(date)\n insertBlocks(db, build)\n const insertSlot = db.prepare(\n 'INSERT INTO activity_slot (date, slot_start, project_id, event_count) VALUES (?, ?, ?, ?)'\n )\n for (const slot of dayBlocks.slots) insertSlot.run(date, slot.slotStart, slot.projectId, slot.eventCount)\n db.prepare(\n `INSERT OR REPLACE INTO grouping_day\n (date, grouping_version, idle_ms, event_count, link_checksum, link_spread, max_event_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)`\n ).run(\n date,\n stamp.groupingVersion,\n stamp.idleMs,\n stamp.eventCount,\n stamp.linkChecksum,\n stamp.linkSpread,\n stamp.maxEventId\n )\n}\n\n/** The day's blocks in time order, each with its threads in the order their projects first appear. */\nexport function getBlocksForDate(db: MigrationDatabase, date: string): StoredBlock[] {\n // One snapshot: a rebuild by another process between the two reads would otherwise leave blocks without threads\n return withReadTransaction(db, () => readBlocksForDate(db, date))\n}\n\n/** `getBlocksForDate` without a transaction of its own, for a caller already inside one. */\nexport function readBlocksForDate(db: MigrationDatabase, date: string): StoredBlock[] {\n const blocks = db\n .prepare(\n `SELECT id, date, started_at AS startedAt, ended_at AS endedAt, event_count AS eventCount,\n active_slots AS activeSlots, grouping_version AS groupingVersion\n FROM work_block WHERE date = ? ORDER BY started_at, id`\n )\n .all(date) as Omit<StoredBlock, 'threads'>[]\n const selectThreads = db.prepare(THREADS_OF_BLOCK_SQL)\n return blocks.map((block) => ({ ...block, threads: selectThreads.all(block.id) as StoredThread[] }))\n}\n\nexport function getThreadEvents(db: MigrationDatabase, threadId: number): EventRow[] {\n return db\n .prepare(\n `SELECT events.* FROM events JOIN thread_event ON thread_event.event_id = events.id\n WHERE thread_event.thread_id = ? ORDER BY events.captured_at, events.id`\n )\n .all(threadId) as EventRow[]\n}\n\n/** A built block's threads, and every event of the block in grouping order with its thread. */\nexport interface BlockWithEvents {\n threads: StoredThread[]\n events: { id: number; threadId: number }[]\n}\n\n/**\n * The block holding an event — what `findSummaryThread` needs — read without a transaction of its own, for a caller\n * already inside one. Null when no built block holds the event (its day isn't built yet, or it was deleted).\n */\nexport function readBlockHoldingEvent(db: MigrationDatabase, eventId: number): BlockWithEvents | null {\n const holder = db\n .prepare(\n `SELECT thread.block_id AS blockId FROM thread_event JOIN thread ON thread.id = thread_event.thread_id\n WHERE thread_event.event_id = ?`\n )\n .get(eventId) as { blockId: number } | undefined\n if (holder === undefined) return null\n const threads = db.prepare(THREADS_OF_BLOCK_SQL).all(holder.blockId) as StoredThread[]\n const events = db\n .prepare(\n `SELECT events.id, thread_event.thread_id AS threadId FROM thread_event\n JOIN thread ON thread.id = thread_event.thread_id JOIN events ON events.id = thread_event.event_id\n WHERE thread.block_id = ? ORDER BY events.captured_at, events.id`\n )\n .all(holder.blockId) as BlockWithEvents['events']\n return { threads, events }\n}\n\n/** `readBlockHoldingEvent` in one snapshot of its own, so a rebuild by another process can't split the read. */\nexport function getBlockHoldingEvent(db: MigrationDatabase, eventId: number): BlockWithEvents | null {\n return withReadTransaction(db, () => readBlockHoldingEvent(db, eventId))\n}\n","import path from 'path'\nimport type { MigrationDatabase } from './migration-types'\nimport type { SummaryRow } from './summary-store'\nimport { relativeInside, resolvedPath } from '../utils/working-directory'\n\n// What `context` looks up on the thread path: the event to answer from, the block it's in, and what's stored about\n// that block. `node:sqlite`-free, so tests run it on an in-memory database.\n\nexport interface EventPoint {\n id: number\n capturedAt: string\n}\n\nexport interface WorkBlockRow {\n id: number\n date: string\n startedAt: string\n endedAt: string\n activeSlots: number\n}\n\nfunction latestOf(points: (EventPoint | null | undefined)[]): EventPoint | null {\n const found = points.filter((point): point is EventPoint => point !== null && point !== undefined)\n found.sort((first, second) => second.capturedAt.localeCompare(first.capturedAt) || second.id - first.id)\n return found[0] ?? null\n}\n\n/** The latest save of the file as its path was stored. */\nfunction latestSaveByPath(db: MigrationDatabase, filePath: string): EventPoint | undefined {\n return db\n .prepare(\n `SELECT id, captured_at AS capturedAt FROM events\n WHERE file_path = ? AND category = 'code_edit' ORDER BY captured_at DESC, id DESC LIMIT 1`\n )\n .get(filePath) as EventPoint | undefined\n}\n\n// How many of a project file's saves to look through for one made in this very file\nconst PROJECT_FILE_SAVES_SCANNED = 200\n\n/** Every project file the path could be: the file's path inside each stored checkout root that holds it. */\nfunction projectFilesAt(db: MigrationDatabase, filePath: string): { projectId: number; relativePath: string }[] {\n const roots = db.prepare('SELECT project_id AS projectId, path FROM project_path').all() as {\n projectId: number\n path: string\n }[]\n return roots.flatMap((root) => {\n const relativePath = relativeInside(root.path, filePath)\n return relativePath ? [{ projectId: root.projectId, relativePath }] : []\n })\n}\n\n/**\n * The latest save linked to the file through its project's file — so a save stored under another path to the same\n * file (a symlink) still counts. A project spans every clone and worktree of its repo, so a save only counts when its\n * stored path is the same file on disk: `README.md` saved in another worktree is another file.\n */\nfunction latestSaveByProjectFile(db: MigrationDatabase, filePath: string): EventPoint | null {\n const savesOfFile = db.prepare(\n `SELECT events.id, events.captured_at AS capturedAt, events.file_path AS filePath FROM file\n JOIN event_file ON event_file.file_id = file.id JOIN events ON events.id = event_file.event_id\n WHERE file.project_id = ? AND file.relative_path = ?\n ORDER BY events.captured_at DESC, events.id DESC LIMIT ${PROJECT_FILE_SAVES_SCANNED}`\n )\n const fileOnDisk = resolvedPath(filePath)\n const isThisFile = (storedPath: string | null) => storedPath !== null && resolvedPath(storedPath) === fileOnDisk\n return latestOf(\n projectFilesAt(db, filePath).map((projectFile) => {\n const rows = savesOfFile.all(projectFile.projectId, projectFile.relativePath) as (EventPoint & {\n filePath: string | null\n })[]\n const save = rows.find((row) => isThisFile(row.filePath))\n return save && { id: save.id, capturedAt: save.capturedAt }\n })\n )\n}\n\n/** The file's latest save: by the path it was stored under, or through its project's file. Null when never saved. */\nexport function findLatestFileSave(db: MigrationDatabase, filePath: string): EventPoint | null {\n return latestOf([latestSaveByPath(db, filePath), latestSaveByProjectFile(db, filePath)])\n}\n\n/** The latest event of the project with this identity (`idx_events_project_time`). Null when it has none. */\nexport function findLatestProjectEvent(db: MigrationDatabase, identity: string): EventPoint | null {\n const row = db\n .prepare(\n `SELECT events.id, events.captured_at AS capturedAt FROM events\n WHERE events.project_id = (SELECT id FROM project WHERE identity = ?)\n ORDER BY events.captured_at DESC, events.id DESC LIMIT 1`\n )\n .get(identity) as EventPoint | undefined\n return row ?? null\n}\n\nexport function getWorkBlock(db: MigrationDatabase, blockId: number): WorkBlockRow | null {\n const row = db\n .prepare(\n `SELECT id, date, started_at AS startedAt, ended_at AS endedAt, active_slots AS activeSlots\n FROM work_block WHERE id = ?`\n )\n .get(blockId) as WorkBlockRow | undefined\n return row ?? null\n}\n\n/** The anchored summaries whose range starts in this block — the ones that can belong to one of its threads. */\nexport function getAnchoredSummariesInBlock(db: MigrationDatabase, eventIds: number[]): SummaryRow[] {\n return db\n .prepare('SELECT * FROM summary WHERE first_event_id IN (SELECT value FROM json_each(?)) ORDER BY id')\n .all(JSON.stringify(eventIds)) as SummaryRow[]\n}\n\n/**\n * Each project's name, by id: its latest name, else the newest name its events were stored under, else an alias,\n * else its checkout's folder — a project the upgrade only reached through a file save has no name of its own.\n */\nexport function getProjectNames(db: MigrationDatabase, projectIds: number[]): Map<number, string | null> {\n const rows = db\n .prepare(\n `SELECT project.id,\n COALESCE(\n project.name,\n (SELECT repo_name FROM events WHERE events.project_id = project.id AND repo_name IS NOT NULL\n ORDER BY captured_at DESC LIMIT 1),\n (SELECT name FROM project_alias WHERE project_alias.project_id = project.id ORDER BY name LIMIT 1)\n ) AS name,\n (SELECT path FROM project_path WHERE project_path.project_id = project.id ORDER BY path LIMIT 1) AS root\n FROM project WHERE project.id IN (SELECT value FROM json_each(?))`\n )\n .all(JSON.stringify(projectIds)) as { id: number; name: string | null; root: string | null }[]\n return new Map(rows.map((row) => [row.id, row.name ?? (row.root === null ? null : path.basename(row.root))]))\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { EventRow } from './types'\nimport { withReadTransaction } from './transaction'\nimport { getThreadEvents, readBlocksForDate, type StoredBlock, type StoredThread } from './thread-store'\nimport { getAnchoredSummariesInBlock, getProjectNames } from './context-lookup'\nimport { getAgentSessionsOfEvents, type AgentSessionInfo } from './agent-sessions'\nimport { ticketsOfEvents } from './tickets'\nimport { sessionKey } from '../summary/store'\nimport { areJobsDone } from './jobs/state'\nimport type { DataJob } from './jobs/types'\nimport type { SummaryRow } from './summary-store'\n\n// A day's built work blocks with what `sessions` says about each thread, read in one snapshot so another process\n// rebuilding the day can't split the read. `node:sqlite`-free, so tests run it on an in-memory database.\n\n/**\n * The jobs readers of threads wait for: until old events are linked to projects, threads would put them all\n * \"elsewhere\"; until old summaries are anchored, a thread would miss its summary.\n */\nexport const THREAD_READ_JOBS: ReadonlySet<string> = new Set(['project-dimensions', 'summary-anchors'])\n\n/**\n * Whether threads can be read: once `THREAD_READ_JOBS` are done, or straight away for a database with no events\n * yet — a fresh install has nothing to upgrade, and its jobs only run before the data commands.\n */\nexport function isReadyForThreads(db: MigrationDatabase, jobs: DataJob[]): boolean {\n const hasEvents = db.prepare('SELECT 1 FROM events LIMIT 1').get() !== undefined\n return !hasEvents || areJobsDone(db, jobs, THREAD_READ_JOBS)\n}\n\nexport interface DayThread extends StoredThread {\n /** `project|startedAt` — the same key `context` gives this thread */\n key: string\n /** The project's name; null for the block's \"elsewhere\" thread */\n repoName: string | null\n /** In grouping order */\n events: EventRow[]\n agentSessions: AgentSessionInfo[]\n /** The ticket keys its events' branches and commits name, sorted (WOR-9 before WOR-10) */\n tickets: string[]\n}\n\nexport interface DayBlock extends Omit<StoredBlock, 'threads'> {\n /** In the order their projects first appear in the block */\n threads: DayThread[]\n /** The anchored summaries whose range starts in this block — the ones that can belong to one of its threads */\n summaries: SummaryRow[]\n}\n\nfunction projectIdsOf(blocks: StoredBlock[]): number[] {\n const projectIds = blocks.flatMap((block) => block.threads.map((thread) => thread.projectId))\n return [...new Set(projectIds)].filter((projectId): projectId is number => projectId !== null)\n}\n\nfunction toDayThread(db: MigrationDatabase, thread: StoredThread, projectNames: Map<number, string | null>): DayThread {\n const repoName = thread.projectId === null ? null : (projectNames.get(thread.projectId) ?? null)\n const events = getThreadEvents(db, thread.id)\n const eventIds = events.map((event) => event.id)\n return {\n ...thread,\n key: sessionKey(repoName, thread.startedAt),\n repoName,\n events,\n agentSessions: getAgentSessionsOfEvents(db, eventIds),\n tickets: ticketsOfEvents(db, eventIds),\n }\n}\n\n/** The day's blocks as last built (`ensureDayThreads` builds them), in time order. */\nexport function readDayThreads(db: MigrationDatabase, date: string): DayBlock[] {\n return withReadTransaction(db, () => {\n const blocks = readBlocksForDate(db, date)\n const projectNames = getProjectNames(db, projectIdsOf(blocks))\n return blocks.map((block) => {\n const threads = block.threads.map((thread) => toDayThread(db, thread, projectNames))\n const eventIds = threads.flatMap((thread) => thread.events.map((event) => event.id))\n return { ...block, threads, summaries: getAnchoredSummariesInBlock(db, eventIds) }\n })\n })\n}\n","import { getDb } from '../db'\nimport { withImmediateTransaction, type TransactionOptions } from '../db/transaction'\nimport {\n currentDayStamp,\n getBlocksForDate,\n getEventsInRange,\n isSameStamp,\n readDayStamp,\n replaceDayBlocks,\n type DayRange,\n type GroupingBasis,\n type StoredBlock,\n} from '../db/thread-store'\nimport { readConfig } from '../config'\nimport { localDayBounds } from '../utils/format'\nimport { buildDayBlocks, GROUPING_VERSION, MS_PER_MINUTE } from './blocks'\n\n// Builds a day's work blocks, threads and slots, under the write lock and rechecked first, so two processes never\n// build the same day twice. Every reader of a day's work — `sessions`, `summary`, `export`, the week and `context` —\n// reads them.\n\nexport interface ThreadGroupingOptions {\n /** Wait at most this long for another process's write lock (default: the connection's 5 s). */\n lockWaitMs?: number\n}\n\nfunction groupingBasis(): GroupingBasis {\n return { groupingVersion: GROUPING_VERSION, idleMs: readConfig().session.idleTimeout * MS_PER_MINUTE }\n}\n\nfunction isDayUpToDate(date: string, range: DayRange, basis: GroupingBasis): boolean {\n const db = getDb()\n return isSameStamp(readDayStamp(db, date), currentDayStamp(db, range, basis))\n}\n\n/** Reads the day's events and replaces its blocks, threads, slots and stamp. Runs inside the write lock. */\nfunction rebuildDay(date: string, range: DayRange, basis: GroupingBasis): void {\n const db = getDb()\n const stamp = currentDayStamp(db, range, basis)\n const bounds = { startMs: Date.parse(range.start), endMs: Date.parse(range.end) }\n const dayBlocks = buildDayBlocks(getEventsInRange(db, range), basis.idleMs, bounds)\n replaceDayBlocks(db, { date, dayBlocks, stamp })\n}\n\n/**\n * Builds the day's blocks and threads unless they're up to date with its events, their project links, the idle\n * timeout and `GROUPING_VERSION`. The rebuild holds the write lock and rechecks first, so two processes never build\n * the same day twice. Throws a locked error (`isDatabaseLocked`) if another process keeps the lock past `lockWaitMs`.\n */\nexport function ensureDayThreads(date: string, options: ThreadGroupingOptions = {}): void {\n const range = localDayBounds(date)\n const basis = groupingBasis()\n if (isDayUpToDate(date, range, basis)) return\n const transactionOptions: TransactionOptions = { busyTimeoutMs: options.lockWaitMs }\n withImmediateTransaction(\n getDb(),\n () => {\n // Another process may have built the day while this one waited for the lock\n if (isDayUpToDate(date, range, basis)) return\n rebuildDay(date, range, basis)\n },\n transactionOptions\n )\n}\n\nexport function getOrBuildBlocks(date: string, options: ThreadGroupingOptions = {}): StoredBlock[] {\n ensureDayThreads(date, options)\n return getBlocksForDate(getDb(), date)\n}\n","import { log } from '../utils/logger'\n\n/** Prints the error and exits 1 — for a command that can't go on. */\nexport function fail(message: string): never {\n log.error(message)\n process.exit(1)\n}\n","// The day's threads as `sessions` and `sessions summary set` read them. They never run data jobs (the agent skill\n// calls them), and there's no older path to fall back to: until the thread jobs have run, threads would put old\n// events \"elsewhere\" and `context` couldn't show a summary saved for one, so the commands ask for\n// `workscribe upgrade` instead. The gate is the one `context` uses.\nimport { getDb } from '../db'\nimport { JOBS } from '../db/jobs'\nimport { UPGRADE_NEEDED_MESSAGE } from '../db/jobs/state'\nimport { isReadyForThreads, readDayThreads, type DayBlock } from '../db/day-threads'\nimport { ensureDayThreads } from '../sessions/threads'\nimport { fail } from './fail'\n\n/** Exits with the upgrade message unless threads can be read yet. */\nexport function requireThreadData(): void {\n if (!isReadyForThreads(getDb(), JOBS)) fail(UPGRADE_NEEDED_MESSAGE)\n}\n\n/** The day's blocks and threads, built first if the day changed since they were. */\nexport function loadDayThreads(date: string): DayBlock[] {\n ensureDayThreads(date)\n return readDayThreads(getDb(), date)\n}\n","import { Command } from 'commander'\nimport { saveThreadSummary } from '../db'\nimport { readConfig } from '../config'\nimport { redact } from '../capture/redact'\nimport { log } from '../utils/logger'\nimport { localDateOf } from '../utils/format'\nimport { SKILL_ENGINE, parseSessionKey } from '../summary/store'\nimport { anchorOfThread } from '../summary/anchors'\nimport { INTERACTIVE_STDIN_MESSAGE, stdinReadFailedMessage, validateSkillSummary } from '../summary/write-back'\nimport { isStdinInteractive, readAllStdin } from '../utils/stdin'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { fail } from './fail'\nimport type { DayThread } from '../db/day-threads'\n\n// `workscribe sessions summary set` — how the Workscribe agent skill saves the summary it wrote.\n\n/** The thread a key from `sessions --json` names, after rebuilding its day so the lookup is current. */\nfunction findThreadByKey(key: string): DayThread | null {\n const parsed = parseSessionKey(key)\n if (!parsed) return null\n const threads = loadDayThreads(localDateOf(parsed.startedAt)).flatMap((block) => block.threads)\n return threads.find((thread) => thread.key === key) ?? null\n}\n\n/** The summary text piped in, failing with a friendly message for a terminal or a read error. */\nfunction readSummaryText(): string {\n if (isStdinInteractive()) fail(INTERACTIVE_STDIN_MESSAGE)\n try {\n return readAllStdin()\n } catch (error) {\n fail(stdinReadFailedMessage(error))\n }\n}\n\nfunction saveSkillSummary(options: { key: string }): void {\n if (!parseSessionKey(options.key)) {\n fail(`That session key isn't valid: '${options.key}'. Copy a session's \"key\" from workscribe sessions --json.`)\n }\n requireThreadData()\n\n const summaryText = readSummaryText()\n const problem = validateSkillSummary(summaryText)\n if (problem) fail(problem)\n\n const thread = findThreadByKey(options.key)\n if (!thread) {\n fail(\n 'No session matches this key — your activity may have changed since the sessions were listed. ' +\n 'Run workscribe sessions --json again and use the new key.'\n )\n }\n\n const text = redact(summaryText.trim(), readConfig().redact.extra)\n saveThreadSummary(options.key, {\n engine: SKILL_ENGINE,\n text,\n anchor: anchorOfThread(thread.events, thread.projectId),\n })\n log.success(\n `Saved the summary for ${thread.repoName ?? 'this session'} — workscribe summary, workscribe context and the VS Code status bar now show it.`\n )\n}\n\nexport function registerSummarySet(sessionsCommand: Command): void {\n sessionsCommand\n .command('summary')\n .description('Manage the summaries stored for sessions')\n .command('set')\n .description('Save a summary written by the Workscribe agent skill; the text is read from stdin')\n .requiredOption('--key <key>', 'the session key, from workscribe sessions --json')\n .action(saveSkillSummary)\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, formatDuration } from '../utils/format'\nimport { registerSummarySet } from './session-summary'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { fail } from './fail'\nimport { readConfig } from '../config'\nimport { locationContextFrom, sessionLocation, threadRepoCwd, type LocationContext } from '../summary/payload'\nimport { blockToJson, buildSessionsJson, threadToJson } from '../summary/contract/build'\nimport { collectNotes } from '../summary/contract/utils'\nimport { getCommitsInWindow, getOthersSubjects, getRepoRoot, getUserEmail } from '../summary/git-commits'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { memoize } from '../utils/memoize'\nimport type { DayBlock, DayThread } from '../db/day-threads'\nimport type { SessionGitInfo } from '../summary/contract/types'\n\n/**\n * Looks up git facts for the thread's repo — empty outside a git repo or when git fails.\n * Repo roots and emails are cached for the run so a day of threads in one repo asks git once.\n */\nfunction createThreadGitLookup(): (thread: DayThread) => SessionGitInfo {\n const repoRootOf = memoize(getRepoRoot)\n const emailOf = memoize(getUserEmail)\n const othersSubjectsOf = memoize((repoPath) => getOthersSubjects(repoPath, emailOf(repoPath)))\n return (thread) => {\n const cwd = threadRepoCwd(thread.events)\n const repoPath = cwd ? repoRootOf(cwd) : null\n if (!repoPath) return { repoPath: null, commits: [], othersSubjects: [] }\n const author = emailOf(repoPath)\n const commits = getCommitsInWindow({ repoRoot: repoPath, since: thread.startedAt, until: thread.endedAt, author })\n return { repoPath, commits, othersSubjects: othersSubjectsOf(repoPath) }\n }\n}\n\nfunction printSessionsJson(date: string, blocks: DayBlock[]): void {\n const config = readConfig()\n const options = {\n location: locationContextFrom(config),\n includeCategories: config.summary?.includeCategories ?? [],\n }\n const lookupGit = createThreadGitLookup()\n const sessionJsons = blocks.flatMap((block) =>\n block.threads.map((thread) => threadToJson(thread, block, { ...options, git: lookupGit(thread) }))\n )\n log.plain(JSON.stringify(buildSessionsJson(date, blocks.map(blockToJson), sessionJsons)))\n}\n\nfunction plural(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`\n}\n\nfunction printThread(thread: DayThread, location: LocationContext): void {\n const name = sessionLocation(thread, location)\n const project = name ? chalk.cyan(name) : chalk.gray('no repo')\n const active = formatDuration(toActiveMinutes(thread.activeSlots))\n log.plain(` ${project} ${chalk.dim(`${active} active`)} ${chalk.dim(plural(thread.eventCount, 'event'))}`)\n for (const note of collectNotes(thread.events)) log.plain(` ${chalk.yellow('note')} ${chalk.dim(note)}`)\n}\n\nfunction printBlocks(date: string, blocks: DayBlock[]): void {\n if (blocks.length === 0) {\n log.plain(`No sessions detected for ${date}`)\n return\n }\n const threadCount = blocks.reduce((total, block) => total + block.threads.length, 0)\n const location = locationContextFrom(readConfig())\n log.plain('')\n log.plain(\n chalk.bold(` Sessions for ${date} (${plural(threadCount, 'thread')} in ${plural(blocks.length, 'block')})`)\n )\n for (const block of blocks) {\n const span = `${formatDate(block.startedAt)} – ${formatDate(block.endedAt)}`\n log.plain('')\n log.plain(` ${chalk.dim(span)} ${chalk.dim(`${formatDuration(toActiveMinutes(block.activeSlots))} active`)}`)\n for (const thread of block.threads) printThread(thread, location)\n }\n log.plain('')\n}\n\nfunction parseDateOption(value: string | undefined): string {\n if (!value) return todayDate()\n try {\n return parseDate(value)\n } catch (error) {\n fail((error as Error).message)\n }\n}\n\nexport function registerSessions(program: Command): void {\n const sessionsCommand = program\n .command('sessions')\n .description(\"List the day's work: each block, and each project's thread inside it\")\n .option('--date <YYYY-MM-DD>', 'date to show sessions for (default: today)')\n .option('--json', 'output the versioned JSON contract (threads with events, notes and commits, and their blocks)')\n .action((options) => {\n const date = parseDateOption(options.date)\n requireThreadData()\n const blocks = loadDayThreads(date)\n if (options.json) {\n printSessionsJson(date, blocks)\n return\n }\n printBlocks(date, blocks)\n })\n\n registerSummarySet(sessionsCommand)\n}\n","import type { OutcomeWording } from './types'\n\n// The week digest's caps — a day's story tells everything (story.ts)\nexport const MAX_SESSION_BULLETS = 7\nexport const MAX_COMMIT_BULLETS = 3\nexport const MAX_DAY_BULLETS = 5\nexport const MAX_LISTED_PACKAGES = 6\n\n// Event sources that mean a coding agent ran the command (its hook captured it). Summaries count these,\n// but never name the tool.\nexport const AGENT_SOURCES = ['claude-code']\n\nexport const TEST_WORDING: OutcomeWording = {\n category: 'testing',\n resolved: 'Resolved failing tests',\n passing: 'Tests passing',\n failing: 'Tests still failing',\n ran: 'Ran tests',\n}\n\nexport const BUILD_WORDING: OutcomeWording = {\n category: 'building',\n resolved: 'Fixed a failing build',\n passing: 'Builds passing',\n failing: 'Build still failing',\n ran: 'Ran builds',\n}\n\n// A day's story says how runs turned out, not how many there were (the week digest above still counts them)\nexport const STORY_TEST_WORDING: OutcomeWording = {\n ...TEST_WORDING,\n resolved: 'Tests now passing',\n failing: 'Tests failing',\n}\n\nexport const STORY_BUILD_WORDING: OutcomeWording = {\n ...BUILD_WORDING,\n resolved: 'Build now passing',\n passing: 'Build passing',\n failing: 'Build failing',\n}\n","import type { EventPayload } from '../../types'\n\nexport function pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`\n}\n\n/** The non-empty strings in a metadata value — event metadata is untyped JSON. */\nexport function stringsIn(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string' && item !== '') : []\n}\n\nexport function unique(values: string[]): string[] {\n return [...new Set(values)]\n}\n\nexport function eventsOf(events: EventPayload[], category: string): EventPayload[] {\n return events.filter((event) => event.category === category)\n}\n\nexport function toBulletList(bullets: string[]): string {\n return bullets.map((bullet) => `- ${bullet}`).join('\\n')\n}\n","import { isFailedExit } from '../../../summary/commits'\nimport { buildExcludedSet } from '../../../summary/exclusions'\nimport {\n AGENT_SOURCES,\n BUILD_WORDING,\n MAX_COMMIT_BULLETS,\n MAX_LISTED_PACKAGES,\n MAX_SESSION_BULLETS,\n TEST_WORDING,\n} from './constants'\nimport { eventsOf, pluralize, stringsIn, unique } from './utils'\nimport type { EventPayload, SessionPayload } from '../../types'\nimport type { Outcome, OutcomeWording } from './types'\n\nexport function describeNotes(events: EventPayload[]): string[] {\n return eventsOf(events, 'note').flatMap((event) => stringsIn([event.metadata.text]).map((text) => `Note: ${text}`))\n}\n\n/** Commit messages from `git commit -m` and from AI sessions, in order, without repeats. A failed commit made none. */\nexport function collectCommits(events: EventPayload[]): string[] {\n return unique(\n events.flatMap((event) => {\n if (event.category === 'shipping' && !isFailedExit(event.exitCode)) return stringsIn([event.metadata.message])\n if (event.category === 'ai_session') return stringsIn(event.metadata.commits)\n return []\n })\n )\n}\n\nexport function describeCommits(commits: string[]): string[] {\n const bullets = commits.slice(0, MAX_COMMIT_BULLETS).map((message) => `Committed: ${message}`)\n const hiddenCount = commits.length - MAX_COMMIT_BULLETS\n if (hiddenCount > 0) bullets[MAX_COMMIT_BULLETS - 1] += `, plus ${pluralize(hiddenCount, 'more change')}`\n return bullets\n}\n\n/**\n * How test or build runs turned out. Commands captured through AI-tool hooks have no exit code, so they never decide\n * the outcome; with no exit code known at all, the runs just ran. Null when there were none.\n */\nexport function outcomeOf(runs: EventPayload[]): Outcome | null {\n if (runs.length === 0) return null\n const knownExitCodes = runs.map((run) => run.exitCode).filter((exitCode): exitCode is number => exitCode !== null)\n if (knownExitCodes.length === 0) return 'ran'\n if (knownExitCodes[knownExitCodes.length - 1] !== 0) return 'failing'\n if (knownExitCodes.some((exitCode) => exitCode !== 0)) return 'resolved'\n return 'passing'\n}\n\n/** Pass/fail line for test or build runs, with the run count — as the week digest words it. */\nexport function describeOutcome(events: EventPayload[], wording: OutcomeWording): string | null {\n const runs = eventsOf(events, wording.category)\n const outcome = outcomeOf(runs)\n if (outcome === null) return null\n const runCount = pluralize(runs.length, 'run')\n return outcome === 'resolved' ? `${wording.resolved} (${runCount}, now passing)` : `${wording[outcome]} (${runCount})`\n}\n\nfunction listPackages(packages: string[]): string {\n const listed = packages.slice(0, MAX_LISTED_PACKAGES).join(', ')\n const hiddenCount = packages.length - MAX_LISTED_PACKAGES\n return `${listed}${hiddenCount > 0 ? `, plus ${hiddenCount} more` : ''}`\n}\n\ninterface DependencyOptions {\n /** Say how many installs ran when no package was named — the week digest does; the story doesn't */\n withRunCount?: boolean\n}\n\n/** Removals (`npm uninstall`, `yarn remove`, …) carry action: 'remove' and are worded separately from additions. */\nexport function describeDependencies(\n events: EventPayload[],\n { withRunCount = true }: DependencyOptions = {}\n): string[] {\n const runs = eventsOf(events, 'dependencies')\n if (runs.length === 0) return []\n const isRemoval = (event: EventPayload): boolean => event.metadata.action === 'remove'\n const added = unique(runs.filter((event) => !isRemoval(event)).flatMap((event) => stringsIn(event.metadata.packages)))\n const removed = unique(runs.filter(isRemoval).flatMap((event) => stringsIn(event.metadata.packages)))\n if (added.length === 0 && removed.length === 0) {\n return [withRunCount ? `Installed dependencies (${pluralize(runs.length, 'run')})` : 'Installed dependencies']\n }\n\n return [\n ...(added.length > 0 ? [`Added dependencies: ${listPackages(added)}`] : []),\n ...(removed.length > 0 ? [`Removed dependencies: ${listPackages(removed)}`] : []),\n ]\n}\n\nexport function describeReleases(events: EventPayload[]): string | null {\n const releases = eventsOf(events, 'releasing')\n if (releases.length === 0) return null\n const tools = unique(releases.flatMap((event) => stringsIn([event.metadata.tool])))\n return tools.length > 0 ? `Released / deployed (${tools.join(', ')})` : 'Released / deployed'\n}\n\nexport function describePushes(events: EventPayload[]): string | null {\n const targets = unique(\n eventsOf(events, 'shipping').flatMap((event) => {\n const [remote] = stringsIn([event.metadata.remote])\n if (!remote) return []\n const [branch] = stringsIn([event.metadata.branch])\n return [branch ? `${remote}/${branch}` : remote]\n })\n )\n return targets.length > 0 ? `Pushed to ${targets.join(', ')}` : null\n}\n\n/** Commands a coding agent ran in the session — counted from all of them, including ones the summary leaves out. */\nfunction describeAgentWork(events: EventPayload[]): string | null {\n const agentCommands = events.filter((event) => event.source !== undefined && AGENT_SOURCES.includes(event.source))\n return agentCommands.length > 0\n ? `Worked with an AI coding assistant (${pluralize(agentCommands.length, 'command')})`\n : null\n}\n\nfunction describeEdits(events: EventPayload[]): string | null {\n const saves = eventsOf(events, 'code_edit').length\n return saves > 0 ? `Edited files in the IDE (${pluralize(saves, 'save')})` : null\n}\n\n/** Notes are always kept; everything else follows the same exclusions as the AI payload. */\nexport function includedEvents(events: EventPayload[], includeCategories: string[]): EventPayload[] {\n const excluded = buildExcludedSet(includeCategories)\n return events.filter((event) => event.category === 'note' || !excluded.has(event.category))\n}\n\n/**\n * The week digest's bullets for a location's day, most important first, capped at MAX_SESSION_BULLETS — empty if\n * nothing notable happened. A day's own summary tells the story instead (`story.ts`).\n */\nexport function describeActivity(session: SessionPayload, includeCategories: string[] = []): string[] {\n const events = includedEvents(session.events, includeCategories)\n return [\n ...describeNotes(events),\n ...describeCommits(collectCommits(events)),\n describeOutcome(events, TEST_WORDING),\n describeOutcome(events, BUILD_WORDING),\n ...describeDependencies(events),\n describeReleases(events),\n describePushes(events),\n describeAgentWork(session.events),\n describeEdits(events),\n ]\n .filter((bullet): bullet is string => bullet !== null)\n .slice(0, MAX_SESSION_BULLETS)\n}\n\nexport function countIncludedEvents(session: SessionPayload, includeCategories: string[] = []): number {\n return includedEvents(session.events, includeCategories).length\n}\n","// A session's branch-moving git work, in plain language — so you can retrace what you did, or undo it, without\n// remembering the commands. Pure: it reads the moves `eventRowToPayload` found in each event's command.\nimport type { EventPayload } from '../../types'\nimport type { DiscardCommand, GitMove } from '../../../normalize/git-move'\n\n/** A line of the story, and the branch it tells under. */\nexport interface BranchMoveLine {\n branch: string | null\n line: string\n}\n\ninterface PendingStash {\n branch: string | null\n /** Restored with `git stash apply`, which keeps it in `git stash list` */\n isApplied: boolean\n}\n\nconst DISCARD_LINES: Record<DiscardCommand, string> = {\n 'git reset --hard': 'Discarded local changes (git reset --hard)',\n 'git checkout -- …': 'Discarded changes to files (git checkout -- …)',\n 'git restore': 'Discarded changes to files (git restore)',\n 'git clean': 'Deleted untracked files (git clean)',\n}\n\nfunction describeStashRestore(stash: PendingStash | undefined, branch: string | null, command: string): string {\n if (!stash) return `Restored stashed changes (${command})`\n if (stash.branch !== null && branch !== null && stash.branch !== branch) {\n return `Moved uncommitted changes from ${stash.branch} to ${branch} (git stash, then ${command})`\n }\n return `Set changes aside and restored them (git stash, then ${command})`\n}\n\nconst isBranchMove = (move: GitMove): move is Extract<GitMove, { kind: 'create-branch' | 'switch' }> =>\n move.kind === 'create-branch' || move.kind === 'switch'\n\n/**\n * Whether the event's one branch move was really `git checkout <file>`: it reads like a switch, but the command\n * left the branch as it was. A command with several branch moves (`git checkout main && git pull && git checkout\n * feat/x`) can come back to where it started without having discarded anything, so it's followed step by step.\n */\nfunction isFileCheckout(event: EventPayload, previousBranch: string | null): boolean {\n const branchMoves = (event.gitMoves ?? []).filter(isBranchMove)\n const [only] = branchMoves\n return (\n branchMoves.length === 1 &&\n only.kind === 'switch' &&\n only.branch !== '-' &&\n // On a detached HEAD (`git checkout v1.2.3`, a manual bisect) there's no branch to compare, so it's no discard\n event.branch !== null &&\n event.branch === previousBranch &&\n only.branch !== event.branch\n )\n}\n\n/**\n * The branch each of an event's moves ran on. The event records the branch after the whole command ran, which is\n * right for a single git command; a compound one that switches part-way (`git stash && git switch -c feat/x &&\n * git stash pop`) is followed step by step from the branch before it, `git checkout -` included.\n */\nfunction branchesDuring(event: EventPayload, previousBranch: string | null): (string | null)[] {\n const moves = event.gitMoves ?? []\n if (!moves.some(isBranchMove) || isFileCheckout(event, previousBranch)) return moves.map(() => event.branch)\n let current = previousBranch ?? event.branch\n let before: string | null = null\n return moves.map((move) => {\n if (isBranchMove(move)) {\n // `git checkout -` with no switch before it in the command goes where the command ended up\n const target = move.branch === '-' ? (before ?? event.branch) : move.branch\n before = current\n current = target\n }\n return current\n })\n}\n\ninterface MoveContext {\n move: GitMove\n /** The branch the move ran on */\n branch: string | null\n /** The branch before it: what a new branch was created from */\n fromBranch: string | null\n /** The event's one branch move was really `git checkout <file>` */\n wasFileCheckout: boolean\n}\n\n/** A move that isn't about the stash, as a line — or null for one that needs none. */\nfunction describeMove({ move, branch, fromBranch, wasFileCheckout }: MoveContext): BranchMoveLine | null {\n switch (move.kind) {\n case 'create-branch': {\n const from = fromBranch !== null && fromBranch !== move.branch ? ` from ${fromBranch}` : ''\n return { branch: move.branch, line: `Created ${move.branch}${from}` }\n }\n case 'switch':\n return wasFileCheckout ? { branch, line: DISCARD_LINES['git checkout -- …'] } : null\n case 'rebase': {\n const rebased = branch === null ? 'Rebased' : `Rebased ${branch}`\n return { branch, line: `${rebased} onto ${move.onto}` }\n }\n case 'merge': {\n const into = branch === null ? '' : ` into ${branch}`\n return { branch, line: `Merged ${move.from}${into}` }\n }\n case 'discard':\n return { branch, line: DISCARD_LINES[move.command] }\n default:\n return null\n }\n}\n\ninterface StashStep {\n stashes: PendingStash[]\n line: BranchMoveLine | null\n}\n\n/**\n * One stash move against the stashes still waiting: a push adds one, a pop restores and removes the latest, an\n * apply restores it and keeps it, a drop throws the latest away and a clear all of them. Pure: returns new state.\n */\nfunction applyStashMove(stashes: PendingStash[], move: GitMove, branch: string | null): StashStep {\n const latest = stashes.at(-1)\n switch (move.kind) {\n case 'stash-push':\n return { stashes: [...stashes, { branch, isApplied: false }], line: null }\n case 'stash-pop': {\n const line = { branch, line: describeStashRestore(latest, branch, move.command) }\n if (move.command === 'git stash pop') return { stashes: stashes.slice(0, -1), line }\n return { stashes: stashes.map((stash) => (stash === latest ? { ...stash, isApplied: true } : stash)), line }\n }\n case 'stash-drop': {\n const isClear = move.command === 'git stash clear'\n const line = { branch, line: `Dropped ${isClear ? 'all ' : ''}stashed changes (${move.command})` }\n return { stashes: isClear ? [] : stashes.slice(0, -1), line }\n }\n default:\n return { stashes, line: null }\n }\n}\n\n/**\n * Every branch move in the session, in order, each under the branch it happened on: a stash restored on another\n * branch reads as uncommitted changes moved between them, and a new branch says what it was created from (the\n * branch you were on before). A plain switch gets no line — the story's `On <branch>:` lines already say it.\n */\nexport function describeBranchMoves(events: EventPayload[]): BranchMoveLine[] {\n const lines: BranchMoveLine[] = []\n let stashes: PendingStash[] = []\n let previousBranch: string | null = null\n for (const event of events) {\n const branches = branchesDuring(event, previousBranch)\n const wasFileCheckout = isFileCheckout(event, previousBranch)\n for (const [index, move] of (event.gitMoves ?? []).entries()) {\n const fromBranch = index === 0 ? previousBranch : branches[index - 1]\n const stashStep = applyStashMove(stashes, move, branches[index])\n stashes = stashStep.stashes\n const line = stashStep.line ?? describeMove({ move, branch: branches[index], fromBranch, wasFileCheckout })\n if (line) lines.push(line)\n }\n if (event.branch !== null) previousBranch = event.branch\n }\n // A stash nobody restored is still waiting in `git stash list`; an applied one is there too, but you used it\n const leftStashed = stashes.filter((pending) => !pending.isApplied)\n return [...lines, ...leftStashed.map((stash) => ({ branch: stash.branch, line: 'Left changes stashed (git stash)' }))]\n}\n","// A day's session as a story: notes first, then what was done on each branch — commits, then outcomes, then what\n// shipped and how you moved between branches — and the agent and editor activity in one closing line. Nothing is\n// cut or folded: every commit and every move is told, so the story is a full record you can retrace. Pure.\nimport { formatSessionHeading } from '../../../summary/heading'\nimport { AGENT_SOURCES, STORY_BUILD_WORDING, STORY_TEST_WORDING } from './constants'\nimport {\n describeDependencies,\n describeNotes,\n describePushes,\n describeReleases,\n includedEvents,\n outcomeOf,\n} from './describe'\nimport { describeBranchMoves } from './branch-moves'\nimport { eventsOf, pluralize, toBulletList } from './utils'\nimport type { EventPayload, SessionPayload } from '../../types'\nimport type { StoryCommit } from '../../../summary/story-commits'\nimport type { OutcomeWording } from './types'\n\n/** One branch's share of a session: its events, the commits made on it and the branch moves that happened on it. */\nexport interface BranchGroup {\n branch: string | null\n events: EventPayload[]\n commits: StoryCommit[]\n /** Branch moves (a branch created, a stash moved, a rebase, a discard) that happened on this branch */\n moves: string[]\n}\n\ninterface BranchBullets {\n branch: string | null\n bullets: string[]\n}\n\n/**\n * The session's events, commits and branch moves, by branch in the order first seen. An event with no branch\n * (outside a repo, a detached HEAD) stays with the branch before it; a commit or move on no known branch goes to the\n * first. Moves are read from every event, whatever the summary leaves out.\n */\nfunction groupByBranch(session: SessionPayload, events: EventPayload[]): BranchGroup[] {\n const order = session.branches.length > 0 ? session.branches : [null]\n const groups = new Map<string | null, BranchGroup>(\n order.map((branch) => [branch, { branch, events: [], commits: [], moves: [] }])\n )\n const groupOf = (branch: string | null): BranchGroup | undefined =>\n groups.get(branch !== null && groups.has(branch) ? branch : order[0])\n let current = order[0]\n for (const event of events) {\n if (event.branch !== null && groups.has(event.branch)) current = event.branch\n groups.get(current)?.events.push(event)\n }\n for (const commit of session.commits) groupOf(commit.branch)?.commits.push(commit)\n for (const move of describeBranchMoves(session.events)) groupOf(move.branch)?.moves.push(move.line)\n return [...groups.values()]\n}\n\n/** How the runs turned out, without counting them: \"Tests now passing\", not \"Ran tests (20 runs)\". */\nfunction describeResult(events: EventPayload[], wording: OutcomeWording): string | null {\n const outcome = outcomeOf(eventsOf(events, wording.category))\n return outcome === null ? null : wording[outcome]\n}\n\n/** What was done on one branch, most telling first: commits, outcomes, releases and pushes, then branch moves. */\nfunction branchBullets(group: BranchGroup): string[] {\n return [\n ...group.commits.map((commit) => `Committed: ${commit.subject}`),\n describeResult(group.events, STORY_TEST_WORDING),\n describeResult(group.events, STORY_BUILD_WORDING),\n ...describeDependencies(group.events, { withRunCount: false }),\n describeReleases(group.events),\n describePushes(group.events),\n ...group.moves,\n ].filter((bullet): bullet is string => bullet !== null)\n}\n\n/**\n * \"With an AI coding assistant (126 commands) · 3 file saves in the editor\" — how, not what, so it comes last. Agent\n * commands are counted over every event (`allEvents`), excluded categories too; saves over the included `events`.\n */\nfunction describeActivityLine(allEvents: EventPayload[], events: EventPayload[]): string | null {\n const agentCommands = allEvents.filter(\n (event) => event.source !== undefined && AGENT_SOURCES.includes(event.source)\n ).length\n const saves = eventsOf(events, 'code_edit').length\n const parts = [\n agentCommands > 0 ? `With an AI coding assistant (${pluralize(agentCommands, 'command')})` : null,\n saves > 0 ? `${pluralize(saves, 'file save')} in the editor` : null,\n ].filter((part): part is string => part !== null)\n return parts.length > 0 ? parts.join(' · ') : null\n}\n\n/** A session with nothing to tell but activity says so plainly — the heading already says where and how long. */\nfunction describeQuietSession(session: SessionPayload, events: EventPayload[]): string {\n const ranCommands = session.events.some((event) => event.category !== 'code_edit' && event.category !== 'note')\n const hasOnlySaves = !ranCommands && eventsOf(events, 'code_edit').length > 0\n return hasOnlySaves ? 'Edited files' : 'Explored and ran commands'\n}\n\nfunction branchLines(groups: BranchBullets[], isMultiBranch: boolean): string[] {\n return groups.flatMap((group) => {\n const bullets = toBulletList(group.bullets).split('\\n')\n return isMultiBranch && group.branch !== null ? [`On ${group.branch}:`, ...bullets] : bullets\n })\n}\n\n/** The story's lines under the heading: `- ` bullets, plain `On <branch>:` lines and a plain activity line. */\nexport function storyLines(session: SessionPayload, includeCategories: string[] = []): string[] {\n const events = includedEvents(session.events, includeCategories)\n const notes = describeNotes(events)\n const groups = groupByBranch(session, events)\n .map((group) => ({ branch: group.branch, bullets: branchBullets(group) }))\n .filter((group) => group.bullets.length > 0)\n const activityLine = describeActivityLine(session.events, events)\n const closing = activityLine === null ? [] : [activityLine]\n\n if (notes.length === 0 && groups.length === 0) {\n return [`- ${describeQuietSession(session, events)}`, ...closing]\n }\n const noteLines = notes.length > 0 ? toBulletList(notes).split('\\n') : []\n return [...noteLines, ...branchLines(groups, session.branches.length > 1), ...closing]\n}\n\n/**\n * A session's finished summary, heading included — the one renderer `summary`, `export` and `context` share, so\n * every consumer (the terminal, Slack, the VS Code tab and tooltip) gets the same `## `, `- ` and plain lines.\n */\nexport function renderSessionStory(session: SessionPayload, includeCategories: string[] = []): string {\n return [formatSessionHeading(session), ...storyLines(session, includeCategories)].join('\\n')\n}\n\n/**\n * A session's branches with what happened on each — every event, included or not, so a branch's activity can be\n * counted like a day's. The week splits each thread this way before merging its branches across days, so a thread's\n * events with no branch stay with that thread's branch.\n */\nexport function branchGroupsOf(session: SessionPayload): BranchGroup[] {\n return groupByBranch(session, session.events)\n}\n\n/** What one branch's story says: its notes, then what was done on it, and how — told like a day's branch. */\nexport function storyOfBranch(\n group: BranchGroup,\n includeCategories: string[] = []\n): { bullets: string[]; activity: string | null } {\n const events = includedEvents(group.events, includeCategories)\n return {\n bullets: [...describeNotes(events), ...branchBullets({ ...group, events })],\n activity: describeActivityLine(group.events, events),\n }\n}\n","// The week, in both its layouts (pure). By project (the default): each project, then each of its branches, with the\n// dates it was worked on and what was done — told by the day's own branch story. Each thread is split by branch first\n// and its branches merged across the week after, so a thread's events with no branch stay with that thread's branch.\n// By day: the day-by-day digest, a few bullets a day taking turns across locations.\nimport { UNKNOWN_LOCATION } from '../../../summary/heading'\nimport { formatWeekday, localDateOf } from '../../../utils/format'\nimport { MAX_DAY_BULLETS } from './constants'\nimport { countIncludedEvents, describeActivity } from './describe'\nimport { branchGroupsOf, storyOfBranch, type BranchGroup } from './story'\nimport { pluralize, toBulletList } from './utils'\nimport type { SessionPayload, WeekSummaryPayload } from '../../types'\n\n/** At most this many bullets per branch; the rest fold into \"…plus N more\". */\nexport const MAX_BRANCH_BULLETS = 6\nconst NO_BRANCH_LABEL = '(no branch)'\n// A branch with nothing to tell but excluded activity still says it was worked on\nconst QUIET_BRANCH_BULLET = 'Explored and ran commands'\n\nexport interface BranchWeek {\n /** Null for work on no branch */\n branch: string | null\n /** The local days it had activity, in order */\n dates: string[]\n bullets: string[]\n /** How many bullets were folded away */\n moreCount: number\n activity: string | null\n}\n\nexport interface ProjectWeek {\n project: string\n branches: BranchWeek[]\n}\n\nexport interface DayWeek {\n date: string\n bullets: string[]\n}\n\n/** The week as laid out: what `summary.format=json` prints after `weekStart` and `weekEnd`, and what's rendered. */\nexport type WeekLayoutData = { by: 'project'; projects: ProjectWeek[] } | { by: 'day'; days: DayWeek[] }\n\n/** A branch's merged group across the week, and the days it was worked on */\ninterface BranchAccumulator {\n group: BranchGroup\n dates: Set<string>\n}\n\ninterface ProjectAccumulator {\n project: string\n /** Its branches, in the order first seen */\n branches: Map<string | null, BranchAccumulator>\n}\n\n/** The week groups by project identity; work outside any project (no id) by the folder it's named after. */\nfunction projectKeyOf(session: SessionPayload): string {\n return session.projectId === undefined || session.projectId === null\n ? `name:${session.project ?? UNKNOWN_LOCATION}`\n : `id:${session.projectId}`\n}\n\n/**\n * A commit told twice: the same hash, or — for a captured commit git didn't return — the same subject at the same\n * time. Never by subject alone: two \"wip\" commits on different days are two commits.\n */\nfunction commitKeyOf(commit: BranchGroup['commits'][number]): string {\n return commit.hash ?? `${commit.subject}@${commit.committedAt}`\n}\n\nfunction distinctCommits(commits: BranchGroup['commits']): BranchGroup['commits'] {\n const seen = new Set<string>()\n return commits.filter((commit) => {\n const key = commitKeyOf(commit)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n}\n\n/** A thread's branch group merged into what the week has for that branch so far. */\nfunction mergeBranch(existing: BranchAccumulator | undefined, group: BranchGroup, date: string): BranchAccumulator {\n const dates = new Set(group.events.map((event) => localDateOf(event.capturedAt)))\n if (dates.size === 0) dates.add(date)\n if (!existing) return { group, dates }\n return {\n group: {\n branch: group.branch,\n events: [...existing.group.events, ...group.events],\n commits: distinctCommits([...existing.group.commits, ...group.commits]),\n moves: [...existing.group.moves, ...group.moves],\n },\n dates: new Set([...existing.dates, ...dates]),\n }\n}\n\nfunction toBranchWeek({ group, dates }: BranchAccumulator, includeCategories: string[]): BranchWeek {\n const story = storyOfBranch(group, includeCategories)\n const bullets = story.bullets.length === 0 && story.activity === null ? [QUIET_BRANCH_BULLET] : story.bullets\n return {\n branch: group.branch,\n dates: [...dates].sort(),\n bullets: bullets.slice(0, MAX_BRANCH_BULLETS),\n moreCount: Math.max(0, bullets.length - MAX_BRANCH_BULLETS),\n activity: story.activity,\n }\n}\n\n/** Each project of the week, in the order of its first activity, with its branches in the order first seen. */\nexport function weekByProject(payload: WeekSummaryPayload, includeCategories: string[] = []): ProjectWeek[] {\n const projects = new Map<string, ProjectAccumulator>()\n for (const day of payload.days) {\n for (const session of day.sessions) {\n const key = projectKeyOf(session)\n const project = projects.get(key) ?? { project: session.project ?? UNKNOWN_LOCATION, branches: new Map() }\n projects.set(key, project)\n for (const group of branchGroupsOf(session)) {\n project.branches.set(group.branch, mergeBranch(project.branches.get(group.branch), group, day.date))\n }\n }\n }\n return [...projects.values()].map((project) => ({\n project: project.project,\n branches: [...project.branches.values()].map((branch) => toBranchWeek(branch, includeCategories)),\n }))\n}\n\nfunction renderBranch(branch: BranchWeek): string {\n const heading = `### ${branch.branch ?? NO_BRANCH_LABEL} — ${branch.dates.map(formatWeekday).join(', ')}`\n const more = branch.moreCount > 0 ? [`…plus ${branch.moreCount} more`] : []\n const bullets = [...branch.bullets, ...more]\n // A branch with only an activity line has no bullet list, and so no blank line where one would be\n const lines = [heading, ...(bullets.length === 0 ? [] : [toBulletList(bullets)])]\n return (branch.activity === null ? lines : [...lines, branch.activity]).join('\\n')\n}\n\n/** The week as text: a `## project` section per project, a `### branch — dates` block per branch. */\nexport function renderWeekByProject(projects: ProjectWeek[]): string {\n return projects\n .map((project) => [`## ${project.project}`, ...project.branches.map(renderBranch)].join('\\n\\n'))\n .join('\\n\\n')\n}\n\n/** One entry per location for the day — several sessions in the same repo read as one block of work. */\nexport function mergeByLocation(sessions: SessionPayload[]): SessionPayload[] {\n const merged = new Map<string, SessionPayload>()\n for (const session of sessions) {\n const location = session.project ?? UNKNOWN_LOCATION\n const existing = merged.get(location)\n merged.set(\n location,\n existing\n ? {\n ...existing,\n events: [...existing.events, ...session.events],\n branches: [...new Set([...existing.branches, ...session.branches])],\n durationMinutes: existing.durationMinutes + session.durationMinutes,\n }\n : { ...session, project: location }\n )\n }\n return [...merged.values()]\n}\n\nfunction locationBullets(session: SessionPayload, includeCategories: string[]): string[] {\n const bullets = describeActivity(session, includeCategories)\n const lines = bullets.length > 0 ? bullets : [pluralize(countIncludedEvents(session, includeCategories), 'command')]\n return lines.map((line) => `${session.project}: ${line}`)\n}\n\n/** Takes the top bullet of every location, then the second, and so on — so one busy repo can't fill the day. */\nfunction pickDayBullets(sessions: SessionPayload[], includeCategories: string[]): string[] {\n const perLocation = mergeByLocation(sessions).map((session) => locationBullets(session, includeCategories))\n const longest = Math.max(0, ...perLocation.map((bullets) => bullets.length))\n const interleaved = Array.from({ length: longest }, (_unused, index) =>\n perLocation.flatMap((bullets) => (index < bullets.length ? [bullets[index]] : []))\n ).flat()\n return interleaved.slice(0, MAX_DAY_BULLETS)\n}\n\n/** The week day by day (`--by day`): each day's bullets, taking turns across locations, at most 5 a day. */\nexport function weekByDay(payload: WeekSummaryPayload, includeCategories: string[] = []): DayWeek[] {\n return payload.days.map((day) => ({ date: day.date, bullets: pickDayBullets(day.sessions, includeCategories) }))\n}\n\n/** The week laid out as `payload.by` asks — the one place the two layouts are chosen between. */\nexport function weekLayoutOf(payload: WeekSummaryPayload, includeCategories: string[] = []): WeekLayoutData {\n if (payload.by === 'project') return { by: 'project', projects: weekByProject(payload, includeCategories) }\n return { by: 'day', days: weekByDay(payload, includeCategories) }\n}\n\n/** The laid-out week as text. */\nexport function renderWeek(layout: WeekLayoutData): string {\n if (layout.by === 'project') return renderWeekByProject(layout.projects)\n return layout.days.map((day) => `### ${day.date}\\n${toBulletList(day.bullets)}`).join('\\n\\n')\n}\n","import { renderSessionStory } from './story'\nimport { renderWeek, weekLayoutOf } from './week'\nimport type { AIProvider, DaySummaryPayload, WeekSummaryPayload } from '../../types'\n\n/** Offline, deterministic summaries built from captured events — the default when no AI is configured. */\nexport class TemplateProvider implements AIProvider {\n constructor(private includeCategories: string[] = []) {}\n\n /** Each session's story, heading included, separated by a blank line. */\n async summarize(payload: DaySummaryPayload): Promise<string> {\n return payload.sessions.map((session) => renderSessionStory(session, this.includeCategories)).join('\\n\\n')\n }\n\n /** The week by project then branch (the default), or day by day. */\n async summarizeWeek(payload: WeekSummaryPayload): Promise<string> {\n return renderWeek(weekLayoutOf(payload, this.includeCategories))\n }\n\n async validate(): Promise<void> {\n // Nothing to reach — the template engine runs entirely offline\n }\n}\n","import { TemplateProvider } from '../ai/engines/template/provider'\nimport type { AIProvider } from '../ai/types'\nimport type { WorkscribeConfig } from '../config'\n\n/** What writes summaries since 2.0: the offline template engine, honouring summary.includeCategories. */\nexport function createSummaryWriter(config: WorkscribeConfig): AIProvider {\n return new TemplateProvider(config.summary?.includeCategories ?? [])\n}\n","import { fetchWithTimeout } from '../utils/fetch'\n\nexport async function postToWebhook(url: string, text: string): Promise<void> {\n const response = await fetchWithTimeout(\n url,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text }),\n },\n 10_000\n )\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Webhook POST failed ${response.status}: ${body}`)\n }\n}\n","/**\n * Converts workscribe markdown summary output to Slack mrkdwn format.\n *\n * Rules:\n * ## heading → *heading* (bold)\n * - bullet → • bullet\n *\n * An optional username is prepended as a dim header line so recipients\n * can tell who sent the summary in shared channels.\n */\nexport function formatForSlack(markdown: string, username?: string): string {\n // Slack has no headings: the day's `## ` and the week's `### ` both become bold lines\n const body = markdown.replace(/^#{2,3} (.+)$/gm, '*$1*').replace(/^- /gm, '• ')\n return username ? `_${username}_\\n${body}` : body\n}\n\nexport function isSlackWebhookUrl(url: string): boolean {\n return url.startsWith('https://hooks.slack.com/')\n}\n","import os from 'os'\nimport { log } from '../utils/logger'\nimport { postToWebhook } from '../integrations/webhook'\nimport { formatForSlack, isSlackWebhookUrl } from '../integrations/slack'\nimport type { WorkscribeConfig } from '../config'\n\nexport interface WebhookOptions {\n slack?: boolean\n webhook?: string\n}\n\ninterface PostSummaryInput {\n options: WebhookOptions\n config: WorkscribeConfig\n summary: string\n /** True when stdout carries JSON (summary.format=json): status lines then go to stderr so the JSON can be piped. */\n statusToStderr?: boolean\n}\n\n/** Posts a finished summary to --webhook, or to the configured Slack webhook with --slack. Never throws. */\nexport async function postSummary({\n options,\n config,\n summary,\n statusToStderr = false,\n}: PostSummaryInput): Promise<void> {\n const status = statusToStderr ? { info: log.notice, success: log.notice, warn: log.notice } : log\n const webhookUrl = options.webhook ?? (options.slack ? config.integrations?.slackWebhook : undefined)\n\n if (!webhookUrl) {\n if (options.slack && !config.integrations?.slackWebhook) {\n status.warn('--slack requires integrations.slackWebhook to be set in config')\n }\n return\n }\n\n try {\n status.info('Posting to webhook…')\n const body = isSlackWebhookUrl(webhookUrl) ? formatForSlack(summary, os.userInfo().username) : summary\n await postToWebhook(webhookUrl, body)\n status.success('Posted.')\n } catch (error) {\n log.error(`Webhook failed: ${(error as Error).message}`)\n }\n}\n","// The day's commits from git for the threads `summary` and `export` render. One `git log` per repo covers all of\n// its threads, and every git call shares one budget, so a slow repo can't hold the summary up.\nimport { findCommitsInWindow, getRepoRoot, getUserEmail } from './git-commits'\nimport { assignCommitsToSessions } from './story-commits'\nimport type { GitCommit } from './git-commits'\n\nexport const DAY_GIT_BUDGET_MS = 3000\n\nexport interface DayCommitOptions {\n /** All of the run's git calls together get at most this long */\n budgetMs: number\n /** The clock, for tests */\n now?: () => number\n}\n\n/** A stretch of work to find commits for: a folder inside its repo (null outside one) and its time span. */\nexport interface CommitWindow {\n cwd: string | null\n startedAt: string\n endedAt: string\n}\n\ninterface RepoWindows {\n repoRoot: string\n indexes: number[]\n}\n\n/** Groups the windows by the repo they ran in; windows outside a repo, or past the budget, aren't looked up. */\nfunction groupByRepo(windows: CommitWindow[], timeLeft: () => number | null): RepoWindows[] {\n const rootOfCwd = new Map<string, string | null>()\n const byRoot = new Map<string, number[]>()\n for (const [index, { cwd }] of windows.entries()) {\n if (!cwd) continue\n if (!rootOfCwd.has(cwd)) {\n const timeout = timeLeft()\n if (timeout === null) break\n rootOfCwd.set(cwd, getRepoRoot(cwd, timeout))\n }\n const repoRoot = rootOfCwd.get(cwd)\n if (repoRoot) byRoot.set(repoRoot, [...(byRoot.get(repoRoot) ?? []), index])\n }\n return [...byRoot].map(([repoRoot, indexes]) => ({ repoRoot, indexes }))\n}\n\n/** One `git log` for the repo, from its windows' earliest start to their latest end — null past the budget, or if git can't answer. */\nfunction repoCommits(repo: RepoWindows, windows: CommitWindow[], timeLeft: () => number | null): GitCommit[] | null {\n const emailTimeout = timeLeft()\n if (emailTimeout === null) return null\n const author = getUserEmail(repo.repoRoot, emailTimeout)\n // Without your committer email git can't tell your commits from pulled ones, so the story keeps to captured ones\n if (author === null) return null\n const logTimeout = timeLeft()\n if (logTimeout === null) return null\n const repoWindows = repo.indexes.map((index) => windows[index])\n const byTime = (first: string, second: string): number => Date.parse(first) - Date.parse(second)\n const startTimes = repoWindows.map((window) => window.startedAt).sort(byTime)\n const endTimes = repoWindows.map((window) => window.endedAt).sort(byTime)\n return findCommitsInWindow({\n repoRoot: repo.repoRoot,\n since: startTimes[0],\n until: endTimes[endTimes.length - 1],\n author,\n timeoutMs: logTimeout,\n allBranches: true,\n })\n}\n\n/**\n * Each window's commits from `git log`, by your committer email — or null for a window git wasn't asked about\n * (outside a repo, git missing or slow, the budget spent). The story then tells the commits Workscribe captured.\n * Windows of one repo that overlap (two projects in one git root) share its commits: each goes to the first window,\n * in the order given, whose span holds it — `renderDay` passes threads in block order, so the one that started first.\n */\nexport function lookupDayCommits(windows: CommitWindow[], options: DayCommitOptions): (GitCommit[] | null)[] {\n const now = options.now ?? Date.now\n const deadline = now() + options.budgetMs\n // Read once per git call and passed on as is, so a clock tick can't turn a checked 1 ms into a timeout of 0\n const timeLeft = (): number | null => {\n const milliseconds = deadline - now()\n return milliseconds >= 1 ? milliseconds : null\n }\n\n const perWindow: (GitCommit[] | null)[] = windows.map(() => null)\n for (const repo of groupByRepo(windows, timeLeft)) {\n const commits = repoCommits(repo, windows, timeLeft)\n if (commits === null) continue\n const assigned = assignCommitsToSessions(\n repo.indexes.map((index) => windows[index]),\n commits\n )\n for (const [position, index] of repo.indexes.entries()) perWindow[index] = assigned[position]\n }\n return perWindow\n}\n","// A day's summary, the way `summary` and `export` both tell it: a section per project thread — a saved summary where\n// the agent skill (or a 1.x AI provider) wrote one for it, and the template's story for the rest, with the day's git\n// commits looked up just for those — each followed by the projects worked alongside it, then the day's time worked.\nimport { DAY_GIT_BUDGET_MS, lookupDayCommits } from './day-commits'\nimport { locationContextFrom, threadRepoCwd, threadToPayload } from './payload'\nimport { pickSavedSummary, type SavedSummary } from './store'\nimport { summariesOfThread, type BlockEvent } from './anchors'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { formatDuration } from '../utils/format'\nimport type { AIProvider, SessionPayload } from '../ai/types'\nimport type { WorkscribeConfig } from '../config'\nimport type { DayBlock, DayThread } from '../db/day-threads'\nimport type { GitCommit } from './git-commits'\n\nconst ALONGSIDE_LABEL = 'Alongside:'\nconst TIME_WORKED_LABEL = 'Time worked:'\n\nexport interface RenderDayOptions {\n date: string\n /** The day's blocks and threads, as `loadDayThreads` reads them */\n blocks: DayBlock[]\n config: WorkscribeConfig\n /** What writes the summaries the skill didn't (the template) */\n provider: AIProvider\n /** Ignore saved summaries and tell every thread with the template */\n fresh?: boolean\n}\n\nexport interface RenderedDay {\n /** Per thread, in block order */\n sessionPayloads: SessionPayload[]\n /** Per thread: the saved summary shown, or null where the template told it */\n saved: (SavedSummary | null)[]\n /** Per thread: its finished text, heading included */\n summaries: string[]\n /** Every thread's text with the projects alongside it, separated by a blank line, then the day's time worked */\n fullSummary: string\n /** Whether the template told any thread — the left-out events footer only applies then */\n needsGeneration: boolean\n /** Time worked: the blocks' active minutes — never the sum of the threads' */\n activeMinutes: number\n}\n\n/** The block's events in grouping order, each with its thread — what `findSummaryThread` reads. */\nfunction blockEventsOf(block: DayBlock): BlockEvent[] {\n return block.threads\n .flatMap((thread) => thread.events.map((event) => ({ event, threadId: thread.id })))\n .sort(\n (first, second) =>\n first.event.captured_at.localeCompare(second.event.captured_at) || first.event.id - second.event.id\n )\n .map(({ event, threadId }) => ({ id: event.id, threadId }))\n}\n\n/** Each thread's best summary stored in `summary`, in block order. */\nfunction savedSummariesOf(blocks: DayBlock[]): (SavedSummary | null)[] {\n return blocks.flatMap((block) => {\n const attachable = { threads: block.threads, events: blockEventsOf(block) }\n return block.threads.map((thread) => pickSavedSummary(summariesOfThread(thread.id, attachable, block.summaries)))\n })\n}\n\n/** git's commits for the threads the template tells, within one budget for the run; null for the others. */\nfunction gitCommitsFor(threads: DayThread[], saved: (SavedSummary | null)[]): (GitCommit[] | null)[] {\n const toRender = threads.filter((_thread, index) => saved[index] === null)\n const windows = toRender.map((thread) => ({\n cwd: threadRepoCwd(thread.events),\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n }))\n const commits = lookupDayCommits(windows, { budgetMs: DAY_GIT_BUDGET_MS })\n const commitsByThread = new Map(toRender.map((thread, index) => [thread, commits[index]]))\n return threads.map((thread) => commitsByThread.get(thread) ?? null)\n}\n\nfunction withAlongside(text: string, alongside: string[] = []): string {\n return alongside.length === 0 ? text : `${text}\\n${ALONGSIDE_LABEL} ${alongside.join(', ')}`\n}\n\nfunction joinDay(summaries: string[], sessionPayloads: SessionPayload[], activeMinutes: number): string {\n const sections = summaries.map((text, index) => withAlongside(text, sessionPayloads[index].alongside))\n return [...sections, `${TIME_WORKED_LABEL} ${formatDuration(activeMinutes)}`].join('\\n\\n')\n}\n\n/** Each thread's summary for the day. Throws when the writer fails, so the command can say so and stop. */\nexport async function renderDay({\n date,\n blocks,\n config,\n provider,\n fresh = false,\n}: RenderDayOptions): Promise<RenderedDay> {\n const threads = blocks.flatMap((block) => block.threads.map((thread) => ({ thread, block })))\n const saved = fresh ? threads.map(() => null) : savedSummariesOf(blocks)\n const gitCommits = gitCommitsFor(\n threads.map(({ thread }) => thread),\n saved\n )\n const context = locationContextFrom(config)\n const sessionPayloads = threads.map(({ thread, block }, index) =>\n threadToPayload(thread, block, { context, gitCommits: gitCommits[index] })\n )\n\n const summaries: string[] = []\n for (const [index, sessionPayload] of sessionPayloads.entries()) {\n const savedSummary = saved[index]\n // The writer returns the thread's finished story, heading included\n summaries.push(\n savedSummary ? savedSummary.text : (await provider.summarize({ date, sessions: [sessionPayload] })).trim()\n )\n }\n const activeMinutes = blocks.reduce((total, block) => total + toActiveMinutes(block.activeSlots), 0)\n return {\n sessionPayloads,\n saved,\n summaries,\n fullSummary: joinDay(summaries, sessionPayloads, activeMinutes),\n needsGeneration: saved.some((entry) => entry === null),\n activeMinutes,\n }\n}\n","// What `summary` and `export` print under a day's summary: which sessions came from saved summaries, and which\n// events the template left out. Both go to the console — never into an exported file.\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { countExcludedEvents } from '../summary/exclusions'\nimport type { SessionPayload } from '../ai/types'\nimport type { SavedSummary } from '../summary/store'\n\nexport function printExcludedFooter(sessionPayloads: SessionPayload[], excluded: Set<string>): void {\n const excludedCounts = countExcludedEvents(sessionPayloads, excluded)\n if (excludedCounts.size === 0) return\n const total = [...excludedCounts.values()].reduce((sum, count) => sum + count, 0)\n const breakdown = [...excludedCounts.entries()].map(([category, count]) => `${category} ×${count}`).join(', ')\n log.plain(chalk.dim(` ℹ ${total} event${total === 1 ? '' : 's'} not included in summary (${breakdown})`))\n log.plain(chalk.dim(' Use --verbose to inspect · workscribe config set summary.includeCategories to opt in'))\n log.plain('')\n}\n\n/** Says which sessions came from saved summaries, so it's clear why --fresh would change the output. */\nexport function printSavedNote(saved: (SavedSummary | null)[]): void {\n const fromSkill = saved.filter((entry) => entry?.fromSkill).length\n const fromLegacyProvider = saved.filter((entry) => entry && !entry.fromSkill).length\n const parts = [\n fromSkill > 0 ? `${fromSkill} written by the Workscribe agent skill` : null,\n fromLegacyProvider > 0 ? `${fromLegacyProvider} written by an AI provider in Workscribe 1.x` : null,\n ].filter((part): part is string => part !== null)\n if (parts.length === 0) return\n log.plain(chalk.dim(` (${parts.join(', ')} — use --fresh for the template summary)`))\n}\n","import { log } from '../utils/logger'\nimport { todayDate, startOfWeek, eachDayOfRange } from '../utils/format'\nimport { locationContextFrom, threadRepoCwd, threadToPayload, type LocationContext } from './payload'\nimport { postSummary, type WebhookOptions } from './webhook'\nimport { DAY_GIT_BUDGET_MS, lookupDayCommits } from './day-commits'\nimport { weekLayoutOf } from '../ai/engines/template/week'\nimport type { AIProvider, WeekLayout, WeekSummaryPayload } from '../ai/types'\nimport type { WorkscribeConfig } from '../config'\nimport type { DayBlock, DayThread } from '../db/day-threads'\n\n/** All of the week's git lookups together get at most the day's budget: one `git log` per repo covers the week. */\nexport const WEEK_GIT_BUDGET_MS = DAY_GIT_BUDGET_MS\n\nexport interface WeeklySummaryOptions extends WebhookOptions {\n /** Accepted for compatibility with 1.x scripts — weekly digests are always rendered fresh now. */\n fresh?: boolean\n /** Each project then each branch (the default), or day by day */\n by?: WeekLayout\n}\n\ninterface RunWeeklySummaryInput {\n options: WeeklySummaryOptions\n config: WorkscribeConfig\n provider: AIProvider\n /** A day's blocks and threads — `loadDayThreads`, after the caller checked the data is upgraded */\n loadDay: (date: string) => DayBlock[]\n}\n\ninterface WeekRange {\n weekStart: string\n today: string\n}\n\ninterface ThreadOfWeek {\n date: string\n thread: DayThread\n block: DayBlock\n}\n\n/** git's commits for every thread of the week: one `git log` per repo over the week, within one budget. */\nfunction weekCommits(threads: ThreadOfWeek[]): ReturnType<typeof lookupDayCommits> {\n const windows = threads.map(({ thread }) => ({\n cwd: threadRepoCwd(thread.events),\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n }))\n return lookupDayCommits(windows, { budgetMs: WEEK_GIT_BUDGET_MS })\n}\n\nfunction collectWeekDays(\n { weekStart, today }: WeekRange,\n loadDay: RunWeeklySummaryInput['loadDay'],\n context: LocationContext\n): WeekSummaryPayload['days'] {\n const threads = eachDayOfRange(weekStart, today).flatMap((date) =>\n loadDay(date).flatMap((block) => block.threads.map((thread) => ({ date, thread, block })))\n )\n const commits = weekCommits(threads)\n const days = new Map<string, WeekSummaryPayload['days'][number]>()\n for (const [index, { date, thread, block }] of threads.entries()) {\n const day = days.get(date) ?? { date, sessions: [] }\n day.sessions.push(threadToPayload(thread, block, { context, gitCommits: commits[index] }))\n days.set(date, day)\n }\n return [...days.values()]\n}\n\n/** The week as one JSON document (`summary.format=json`): projects → branches, or days → bullets. */\nfunction weekJson(payload: WeekSummaryPayload, includeCategories: string[]): string {\n const { weekStart, weekEnd } = payload\n return JSON.stringify({ weekStart, weekEnd, ...weekLayoutOf(payload, includeCategories) }, null, 2)\n}\n\nasync function writeWeeklySummary(provider: AIProvider, payload: WeekSummaryPayload): Promise<string> {\n if (provider.summarizeWeek) return provider.summarizeWeek(payload)\n return provider.summarize({ date: payload.weekEnd, sessions: payload.days.flatMap((day) => day.sessions) })\n}\n\nfunction printSummary(summary: string): void {\n log.plain('')\n log.plain(summary)\n log.plain('')\n}\n\n/** Shared flow for `summary --week` and `week`: collect Mon–today's threads, render the week, post it. */\nexport async function runWeeklySummary({ options, config, provider, loadDay }: RunWeeklySummaryInput): Promise<void> {\n const today = todayDate()\n const weekStart = startOfWeek(today)\n const days = collectWeekDays({ weekStart, today }, loadDay, locationContextFrom(config))\n if (days.length === 0) {\n log.plain('No activity captured this week.')\n return\n }\n\n const payload: WeekSummaryPayload = { weekStart, weekEnd: today, by: options.by ?? 'project', days }\n const summary = await writeWeeklySummary(provider, payload)\n const isJson = config.summary?.format === 'json'\n // Only the JSON on stdout, so it can be piped; posting still sends the text, its status on stderr\n if (isJson) log.plain(weekJson(payload, config.summary?.includeCategories ?? []))\n else printSummary(summary)\n await postSummary({ options, config, summary, statusToStderr: isJson })\n}\n","import { Command } from 'commander'\nimport { readConfig } from '../config'\nimport { runWeeklySummary, type WeeklySummaryOptions } from '../summary/weekly'\nimport { createSummaryWriter } from '../summary/writer'\nimport { printLegacyAiNotice } from './notices'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { fail } from './fail'\nimport type { WeekLayout } from '../ai/types'\n\nconst WEEK_LAYOUTS: readonly WeekLayout[] = ['project', 'day']\n\n/** `--by`, which both `week` and `summary --week` take. */\nexport function addWeekLayoutOption(command: Command): Command {\n return command.option('--by <layout>', 'lay the week out by project then branch (default), or by day')\n}\n\nfunction parseWeekLayout(value: string | undefined): WeekLayout {\n if (value === undefined) return 'project'\n const layout = WEEK_LAYOUTS.find((candidate) => candidate === value)\n if (!layout) fail('--by must be project or day')\n return layout\n}\n\n/** `week` and `summary --week`: one handler, so both print the same thing for the same options. */\nexport async function runWeekCommand(options: Omit<WeeklySummaryOptions, 'by'> & { by?: string }): Promise<void> {\n const by = parseWeekLayout(options.by)\n const config = readConfig()\n printLegacyAiNotice(config)\n requireThreadData()\n try {\n await runWeeklySummary({\n options: { ...options, by },\n config,\n provider: createSummaryWriter(config),\n loadDay: loadDayThreads,\n })\n } catch (error) {\n fail((error as Error).message)\n }\n}\n\nexport function registerWeek(program: Command): void {\n const weekCommand = program\n .command('week')\n .description('Summarize the current week (Mon–today) — the same as summary --week')\n .option('--fresh', 'accepted for 1.x scripts — the week is always rendered fresh')\n .option('--slack', 'post to Slack webhook from config')\n .option('--webhook <url>', 'post to a custom webhook URL')\n addWeekLayoutOption(weekCommand).action(runWeekCommand)\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate } from '../utils/format'\nimport type { SessionPayload } from '../ai/types'\nimport { buildExcludedSet } from '../summary/exclusions'\nimport { formatSessionLabel } from '../summary/heading'\nimport { createSummaryWriter } from '../summary/writer'\nimport { printLegacyAiNotice } from './notices'\nimport { postSummary } from '../summary/webhook'\nimport { renderDay, type RenderedDay } from '../summary/day'\nimport { printExcludedFooter, printSavedNote } from './day-notes'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { addWeekLayoutOption, runWeekCommand } from './week'\nimport { fail } from './fail'\n\ninterface SessionSummaryJson {\n project: string | null\n branch: string | null\n /** The thread's span, first event to last */\n durationMinutes: number\n /** The thread's own active time */\n activeMinutes: number | null\n /** The other projects worked in its block */\n alongside: string[]\n bullets: string[]\n}\n\n/** Parse bullet output into a clean string array. Handles both - and • prefixes. */\nexport function parseBullets(text: string): string[] {\n return text\n .split('\\n')\n .map((line) => line.replace(/^[•\\-]\\s*/, '').trim())\n .filter(Boolean)\n}\n\nfunction stripHeading(summary: string): string {\n return summary.replace(/^## .+\\n/, '')\n}\n\nfunction toJsonOutput(day: RenderedDay, date: string): string {\n const sessions: SessionSummaryJson[] = day.sessionPayloads.map((sessionPayload, index) => ({\n project: sessionPayload.project,\n branch: sessionPayload.branch,\n durationMinutes: sessionPayload.durationMinutes,\n activeMinutes: sessionPayload.activeMinutes ?? null,\n alongside: sessionPayload.alongside ?? [],\n bullets: parseBullets(stripHeading(day.summaries[index])),\n }))\n return JSON.stringify({ date, activeMinutes: day.activeMinutes, sessions }, null, 2)\n}\n\nfunction printCapturedEvents(sessionPayloads: SessionPayload[], excluded: Set<string>): void {\n log.plain('')\n log.plain(chalk.bold(' Captured events (--verbose):'))\n for (const sessionPayload of sessionPayloads) {\n log.plain(chalk.dim(`\\n ${formatSessionLabel(sessionPayload)}`))\n for (const event of sessionPayload.events) {\n const tag = excluded.has(event.category) ? chalk.dim(` [excluded] ${event.category}`) : ` ${event.category}`\n const hint = event.hint ? chalk.dim(` [${event.hint}]`) : ''\n const meta = Object.keys(event.metadata).length > 0 ? chalk.dim(` ${JSON.stringify(event.metadata)}`) : ''\n log.plain(` ${tag}${hint}${meta}`)\n }\n }\n log.plain('')\n}\n\nexport function registerSummary(program: Command): void {\n const summaryCommand = program\n .command('summary')\n .description('Generate a work summary')\n .option('--date <YYYY-MM-DD>', 'date to summarize (default: today)')\n .option('--week', 'summarize the current week')\n .option('--fresh', 'ignore saved summaries and render the template')\n .option('--verbose', 'show all captured events, including those left out of the summary')\n .option('--slack', 'post summary to Slack webhook from config')\n .option('--webhook <url>', 'post summary to a custom webhook URL')\n addWeekLayoutOption(summaryCommand).action(async (options) => {\n if (options.by !== undefined && !options.week) fail('--by only applies to --week')\n // ── Weekly branch: the same handler as `workscribe week` ──────────────────\n if (options.week) {\n await runWeekCommand(options)\n return\n }\n\n const config = readConfig()\n printLegacyAiNotice(config)\n const provider = createSummaryWriter(config)\n\n requireThreadData()\n\n // ── Daily branch ─────────────────────────────────────────────────────────\n let date = todayDate()\n if (options.date) {\n try {\n date = parseDate(options.date)\n } catch (error) {\n log.error((error as Error).message)\n process.exit(1)\n }\n }\n\n const blocks = loadDayThreads(date)\n\n if (blocks.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n const jsonMode = config.summary?.format === 'json'\n let day: RenderedDay\n try {\n day = await renderDay({ date, blocks, config, provider, fresh: options.fresh })\n } catch (error) {\n log.error(`Summary failed: ${(error as Error).message}`)\n process.exit(1)\n }\n const { sessionPayloads, saved, fullSummary, needsGeneration } = day\n const excluded = buildExcludedSet(config.summary?.includeCategories ?? [])\n if (needsGeneration && options.verbose) {\n if (jsonMode) log.notice('--verbose is skipped when summary.format is json, to keep the JSON output clean')\n else printCapturedEvents(sessionPayloads, excluded)\n }\n\n if (jsonMode) {\n // Only the JSON on stdout, so it can be piped straight into another tool\n log.plain(toJsonOutput(day, date))\n } else {\n log.plain('')\n log.plain(fullSummary)\n log.plain('')\n printSavedNote(saved)\n if (needsGeneration) printExcludedFooter(sessionPayloads, excluded)\n }\n\n await postSummary({ options, config, summary: fullSummary, statusToStderr: jsonMode })\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate } from '../utils/format'\nimport { EXPORTS_DIR } from '../utils/paths'\nimport { renderDay, type RenderedDay } from '../summary/day'\nimport { buildExcludedSet } from '../summary/exclusions'\nimport { printExcludedFooter, printSavedNote } from './day-notes'\nimport { createSummaryWriter } from '../summary/writer'\nimport { printLegacyAiNotice } from './notices'\nimport { loadDayThreads, requireThreadData } from './thread-day'\n\nexport function registerExport(program: Command): void {\n program\n .command('export')\n .description('Export work summary as a markdown file')\n .option('--date <YYYY-MM-DD>', 'date to export (default: today)')\n .option('--output <path>', 'output file path (default: ~/.workscribe/exports/)')\n .option('--fresh', 'ignore saved summaries and render the template')\n .action(async (options) => {\n const config = readConfig()\n printLegacyAiNotice(config)\n\n let date = todayDate()\n if (options.date) {\n try {\n date = parseDate(options.date)\n } catch (error) {\n log.error((error as Error).message)\n process.exit(1)\n }\n }\n\n requireThreadData()\n const blocks = loadDayThreads(date)\n if (blocks.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n // The same day `workscribe summary` shows, saved summaries included — always as markdown, whatever summary.format\n let day: RenderedDay\n try {\n day = await renderDay({ date, blocks, config, provider: createSummaryWriter(config), fresh: options.fresh })\n } catch (error) {\n log.error(`Export failed: ${(error as Error).message}`)\n process.exit(1)\n }\n\n const outputPath = options.output ?? path.join(EXPORTS_DIR, `workscribe-${date}.md`)\n\n if (!fs.existsSync(EXPORTS_DIR)) fs.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 0o700 })\n\n fs.writeFileSync(outputPath, `# Work Summary — ${date}\\n\\n${day.fullSummary}\\n`, {\n encoding: 'utf-8',\n mode: 0o600,\n })\n log.success(`Saved: ${outputPath}`)\n // Notes about the summary stay on the console, out of the file\n printSavedNote(day.saved)\n if (day.needsGeneration) {\n printExcludedFooter(day.sessionPayloads, buildExcludedSet(config.summary?.includeCategories ?? []))\n }\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { readConfig, writeConfig, getConfigValue, setConfigValue } from '../config'\nimport { REMOVED_AI_SETTING_MESSAGE, isRemovedAiSetting } from '../summary/legacy-ai'\nimport { log } from '../utils/logger'\nimport { getDb } from '../db'\nimport { requestTicketRelink } from '../db/jobs/ticket-refs'\nimport { ticketPatternProblem } from '../capture/tickets'\n\nconst TICKET_PATTERN_KEY = 'tickets.pattern'\n\n/**\n * Sets the ticket pattern: the database first — the pattern the history is linked with, and a restart of the\n * `ticket-refs` job — so a locked database refuses before config.json changes. Stored as the string it is: going\n * through setConfigValue would turn a pattern like `123` into a number.\n */\nfunction setTicketPattern(pattern: string): void {\n const problem = ticketPatternProblem(pattern)\n if (problem) {\n log.error(`That ticket pattern can't be used: ${problem}.`)\n process.exit(1)\n }\n requestTicketRelink(getDb(), pattern)\n const config = readConfig()\n writeConfig({ ...config, tickets: { ...config.tickets, pattern } })\n log.success(`Set ${chalk.cyan(TICKET_PATTERN_KEY)} = ${chalk.yellow(pattern)}`)\n log.plain(\n chalk.dim(\n ' New captures use it now; your history is re-linked the next time a data command runs (or: workscribe upgrade).'\n )\n )\n}\n\nexport function registerConfig(program: Command): void {\n const configCmd = program\n .command('config')\n .description('View or update Workscribe configuration')\n .action(() => {\n const config = readConfig()\n log.plain('')\n log.plain(chalk.bold(' Workscribe Config'))\n log.plain('')\n printConfig(config, ' ')\n log.plain('')\n })\n\n configCmd\n .command('set <key> <value>')\n .description('Set a config value (e.g. workscribe config set session.idleTimeout 45)')\n .action((key: string, value: string) => {\n // Checked before reading or writing, so a refused key never touches config.json\n if (isRemovedAiSetting(key)) {\n log.error(REMOVED_AI_SETTING_MESSAGE)\n process.exit(1)\n }\n if (key === TICKET_PATTERN_KEY) {\n setTicketPattern(value)\n return\n }\n const config = readConfig()\n const updated = setConfigValue(config, key, value)\n writeConfig(updated)\n const isSensitive =\n key.toLowerCase().includes('key') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')\n const displayValue = isSensitive ? '[set]' : chalk.yellow(value)\n log.success(`Set ${chalk.cyan(key)} = ${displayValue}`)\n })\n\n configCmd\n .command('get <key>')\n .description('Get a config value')\n .action((key: string) => {\n const config = readConfig()\n const value = getConfigValue(config, key)\n if (value === undefined) {\n log.warn(`Key not found: ${key}`)\n } else {\n const lastSegment = key.split('.').pop() ?? ''\n if (SENSITIVE_KEYS.has(lastSegment) && typeof value === 'string' && value.length > 0) {\n log.plain(`${value.slice(0, 4)}${'*'.repeat(8)} (${value.length} chars)`)\n } else {\n log.plain(String(value))\n }\n }\n })\n}\n\nconst SENSITIVE_KEYS = new Set([\n 'apiKey',\n 'api_key',\n 'secret',\n 'token',\n 'password',\n 'passwd',\n 'webhook',\n 'slackWebhook',\n])\n\nfunction maskValue(key: string, value: unknown): string {\n if (SENSITIVE_KEYS.has(key) && typeof value === 'string' && value.length > 0) {\n return chalk.dim(`${value.slice(0, 4)}${'*'.repeat(8)} (${value.length} chars)`)\n }\n if (Array.isArray(value)) return `[${(value as unknown[]).join(', ')}]`\n return String(value) || chalk.dim('(not set)')\n}\n\nfunction printConfig(obj: unknown, indent: string): void {\n if (typeof obj !== 'object' || obj === null) return\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n log.plain(`${indent}${chalk.bold(key)}:`)\n printConfig(value, indent + ' ')\n } else {\n log.plain(`${indent}${chalk.dim(key)}: ${chalk.yellow(maskValue(key, value))}`)\n }\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { log } from '../utils/logger'\n\nexport function registerPause(program: Command): void {\n program\n .command('pause')\n .description('Pause capture temporarily')\n .action(() => {\n if (fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is already paused. Run workscribe resume to restart.')\n return\n }\n fs.writeFileSync(PAUSE_FLAG_PATH, '', { mode: 0o600 })\n log.success('Capture paused. Run workscribe resume to restart.')\n })\n\n program\n .command('resume')\n .description('Resume capture after a pause')\n .action(() => {\n if (!fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is not paused.')\n return\n }\n fs.rmSync(PAUSE_FLAG_PATH)\n log.success('Capture resumed.')\n })\n}\n","import { Command } from 'commander'\nimport { getLastEventIds, getEventsByIds, deleteEventsByIds } from '../db'\nimport { log } from '../utils/logger'\n\nexport function registerUndo(program: Command): void {\n program\n .command('undo')\n .description('Remove the last N captured events')\n .option('-n, --count <number>', 'number of events to remove', '1')\n .option('--dry-run', 'preview what would be deleted without deleting')\n .action((opts) => {\n const count = parseInt(opts.count, 10)\n if (isNaN(count) || count < 1) {\n log.error('--count must be a positive integer')\n process.exit(1)\n }\n\n const ids = getLastEventIds(count)\n if (ids.length === 0) {\n log.plain('No events to undo.')\n return\n }\n\n const events = getEventsByIds(ids)\n log.plain('')\n log.plain(`Events to remove (${events.length}):`)\n for (const e of events) {\n log.plain(` ${e.captured_at} ${e.category} ${e.command}`)\n }\n log.plain('')\n\n if (opts.dryRun) {\n log.plain('Dry run — nothing deleted.')\n return\n }\n\n deleteEventsByIds(ids)\n log.success(`Removed ${ids.length} event${ids.length > 1 ? 's' : ''}.`)\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport prompts from 'prompts'\nimport { getEventsForDate, deleteEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate } from '../utils/format'\n\nexport function registerClear(program: Command): void {\n program\n .command('clear')\n .description('Delete all captured events for a date')\n .option('--date <YYYY-MM-DD>', 'date to clear (default: today)')\n .option('--dry-run', 'preview what would be deleted without deleting')\n .option('--yes', 'skip confirmation prompt')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n\n if (events.length === 0) {\n log.plain(`No events found for ${date}.`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n // Show a compact summary by category\n const counts = new Map<string, number>()\n for (const e of events) counts.set(e.category, (counts.get(e.category) ?? 0) + 1)\n for (const [cat, n] of counts) {\n log.plain(` ${chalk.dim(String(n).padStart(4))} ${cat}`)\n }\n log.plain('')\n\n if (opts.dryRun) {\n log.plain(chalk.dim(' Dry run — nothing deleted.'))\n log.plain('')\n return\n }\n\n if (!opts.yes) {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `Delete all ${events.length} events for ${date}?`,\n initial: false,\n })\n if (!confirm) {\n log.plain('Cancelled.')\n return\n }\n }\n\n const deleted = deleteEventsForDate(date)\n log.success(`Cleared ${deleted} event${deleted !== 1 ? 's' : ''} for ${date}.`)\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport prompts from 'prompts'\nimport chalk from 'chalk'\nimport { WORKSCRIBE_DIR, REQUIRED_DIRS } from '../utils/paths'\nimport { isHookInstalled, removeCompletions, removeFishCompletions, removeHook } from '../capture/shell-hook'\nimport { claudeCodeIntegration } from '../capture/hooks/claude-code'\nimport { log } from '../utils/logger'\nimport { SKILL_AGENTS } from '../skills/agents'\nimport { removeSkills } from '../skills/actions'\n\n// The rc files `init` may have added a completions block to — checked on their own, since a file can have the\n// completions block without the hook block\nfunction completionRcFiles(): string[] {\n return ['.zshrc', '.bashrc', '.bash_profile'].map((file) => path.join(os.homedir(), file))\n}\n\n/** Removes everything else `init` adds to your shell and tools: completions and the Claude Code session hook. */\nfunction removeCompletionsAndToolHooks(): void {\n for (const rcFile of completionRcFiles()) {\n if (removeCompletions(rcFile)) log.success(`Shell completions removed from ${rcFile}`)\n }\n if (removeFishCompletions()) log.success('Fish completions removed')\n\n // Not only when Claude Code is on PATH — someone who removed Claude Code can still have the hook in its settings\n const outcome = claudeCodeIntegration.uninstall?.()\n if (outcome === 'removed') log.success('Claude Code session hook removed from ~/.claude/settings.json')\n if (outcome === 'unreadable') {\n log.warn(\n \"Couldn't read ~/.claude/settings.json, so the Claude Code session hook was left in place. Fix the file's JSON, then run: workscribe uninstall --hooks-only\"\n )\n }\n}\n\nfunction detectShellRcFiles(): { name: string; rcFile: string }[] {\n const candidates = [\n { name: 'zsh', rcFile: path.join(os.homedir(), '.zshrc') },\n { name: 'bash', rcFile: path.join(os.homedir(), '.bashrc') },\n { name: 'bash', rcFile: path.join(os.homedir(), '.bash_profile') },\n { name: 'fish', rcFile: path.join(os.homedir(), '.config', 'fish', 'conf.d', 'workscribe.fish') },\n ]\n return candidates.filter((candidate) => fs.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile))\n}\n\nexport function registerUninstall(program: Command): void {\n program\n .command('uninstall')\n .description(\n 'Remove shell hooks, completions, the Claude Code hook and the agent skill; optionally delete all data'\n )\n .option(\n '--hooks-only',\n 'remove shell hooks, completions and the Claude Code hook only — keep the agent skill and skip the data prompt'\n )\n .action(async (options) => {\n log.plain(chalk.bold('\\n Workscribe — Uninstall\\n'))\n\n // 1. Remove shell hooks from any rc files that have them\n const hooked = detectShellRcFiles()\n if (hooked.length === 0) {\n log.info('No shell hooks found')\n } else {\n for (const { name, rcFile } of hooked) {\n if (name === 'fish') {\n // The conf.d file is ours entirely — delete it rather than leaving an empty file\n fs.unlinkSync(rcFile)\n log.success(`Shell hook removed: ${rcFile}`)\n log.plain(` ${chalk.dim('Restart your terminal to take effect')}`)\n } else {\n removeHook(rcFile)\n log.success(`Shell hook removed from ${rcFile}`)\n log.plain(` ${chalk.dim(`Run: source ${rcFile}`)} (or restart your terminal to take effect)`)\n log.plain(\n ` ${chalk.dim(`${name === 'zsh' ? 'unfunction' : 'unset -f'} _workscribe_preexec _workscribe_precmd`)}`\n )\n }\n }\n }\n\n removeCompletionsAndToolHooks()\n\n // Agent skills are only removed on a full uninstall: `--hooks-only` is for clearing what runs in your\n // shell and tools, and the skill is something you chose to install into your coding agents\n if (options.hooksOnly) return\n\n removeSkills(SKILL_AGENTS, { reportMissing: false })\n\n // 2. Optionally delete all Workscribe data directories\n const existingDirs = [...new Set(REQUIRED_DIRS)].filter((dir) => fs.existsSync(dir))\n\n if (existingDirs.length > 0) {\n log.plain('')\n const { deleteData } = await prompts({\n type: 'confirm',\n name: 'deleteData',\n message: `Delete all Workscribe data in ${WORKSCRIBE_DIR}? (database, config, exports, backups)`,\n initial: false,\n })\n\n if (deleteData) {\n for (const dir of existingDirs) {\n fs.rmSync(dir, { recursive: true, force: true })\n log.success(`Deleted ${dir}`)\n }\n } else {\n log.info(`Data kept at ${WORKSCRIBE_DIR}`)\n }\n }\n\n log.plain('')\n log.plain(` ${chalk.dim('npm uninstall -g @workscribe/cli')} — then remove the CLI itself`)\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { readConfig, writeConfig } from '../config'\nimport { PROFILES_DIR } from '../utils/paths'\nimport { getCachedProfiles, detectProfilesForDirectory } from '../profiles/loader'\nimport { installProfile, cacheManifest, readCachedManifest } from '../profiles/fetcher'\nimport { getOtherEventToolCounts } from '../db'\nimport { log } from '../utils/logger'\n\nconst MIN_SUGGEST_COUNT = 5\n\nexport function registerProfile(program: Command): void {\n const profile = program.command('profile').description('Manage stack profiles for better command tracking')\n\n // ─── list ─────────────────────────────────────────────────────────────────\n profile\n .command('list')\n .description('List available profiles from the registry')\n .action(async () => {\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n let manifest = readCachedManifest()\n let offline = false\n\n if (!manifest) {\n try {\n manifest = await cacheManifest()\n } catch {\n offline = true\n manifest = getCachedProfiles().map((p) => ({\n name: p.name,\n displayName: p.displayName,\n tags: [],\n description: '',\n keywords: [],\n detect: p.detect,\n }))\n }\n } else {\n // Refresh in background — best effort, don't block output\n cacheManifest().catch(() => undefined)\n }\n\n if (offline) {\n log.warn('Registry unreachable — showing installed profiles only')\n }\n\n if (manifest.length === 0) {\n log.plain('No profiles available. Check your connection and try again.')\n return\n }\n\n // Group by tag\n const grouped = new Map<string, typeof manifest>()\n for (const entry of manifest) {\n const tag = entry.tags[0] ?? 'other'\n if (!grouped.has(tag)) grouped.set(tag, [])\n grouped.get(tag)!.push(entry)\n }\n\n log.plain('')\n for (const [tag, entries] of grouped) {\n log.plain(chalk.bold(` ${tag.charAt(0).toUpperCase() + tag.slice(1)} profiles`))\n log.plain(chalk.dim(' ' + '─'.repeat(60)))\n for (const entry of entries) {\n const badge = activeNames.has(entry.name) ? chalk.green(' [active]') : ''\n const installed = fs.existsSync(path.join(PROFILES_DIR, `${entry.name}.json`)) ? chalk.dim(' installed') : ''\n log.plain(` ${chalk.cyan(entry.name.padEnd(18))} ${entry.displayName.padEnd(28)}${badge}${installed}`)\n if (entry.description) log.plain(` ${chalk.dim(' '.repeat(18) + entry.description)}`)\n }\n log.plain('')\n }\n })\n\n // ─── add ──────────────────────────────────────────────────────────────────\n profile\n .command('add <names...>')\n .description('Install one or more profiles from the registry')\n .action(async (names: string[]) => {\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n for (const name of names) {\n if (activeNames.has(name)) {\n log.info(`Profile \"${name}\" is already active`)\n continue\n }\n try {\n log.plain(`Fetching ${chalk.cyan(name)}…`)\n await installProfile(name)\n activeNames.add(name)\n log.success(`Profile \"${name}\" installed`)\n } catch (err) {\n log.error(`Failed to install \"${name}\": ${(err as Error).message}`)\n }\n }\n\n config.capture.profiles = [...activeNames]\n writeConfig(config)\n })\n\n // ─── remove ───────────────────────────────────────────────────────────────\n profile\n .command('remove <names...>')\n .description('Uninstall one or more profiles')\n .action((names: string[]) => {\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n for (const name of names) {\n if (!activeNames.has(name)) {\n log.warn(`Profile \"${name}\" is not active`)\n continue\n }\n activeNames.delete(name)\n const cachePath = path.join(PROFILES_DIR, `${name}.json`)\n if (fs.existsSync(cachePath)) fs.unlinkSync(cachePath)\n log.success(`Profile \"${name}\" removed`)\n }\n\n config.capture.profiles = [...activeNames]\n writeConfig(config)\n })\n\n // ─── update ───────────────────────────────────────────────────────────────\n profile\n .command('update [name]')\n .description('Re-fetch installed profiles from the registry')\n .action(async (name?: string) => {\n const config = readConfig()\n const toUpdate = name ? [name] : config.capture.profiles\n\n if (toUpdate.length === 0) {\n log.plain('No profiles installed. Run workscribe profile add <name> first.')\n return\n }\n\n for (const profileName of toUpdate) {\n try {\n log.plain(`Updating ${chalk.cyan(profileName)}…`)\n await installProfile(profileName)\n log.success(`${profileName} updated`)\n } catch (err) {\n log.error(`Failed to update \"${profileName}\": ${(err as Error).message}`)\n }\n }\n })\n\n // ─── detect ───────────────────────────────────────────────────────────────\n profile\n .command('detect')\n .description('Scan the current directory and suggest matching profiles')\n .action(() => {\n const cwd = process.cwd()\n const matches = detectProfilesForDirectory(cwd)\n\n if (matches.length === 0) {\n const cached = readCachedManifest()\n if (!cached) {\n log.plain('No manifest cached. Run workscribe profile list to fetch it first.')\n } else {\n log.plain(`No profiles detected for ${chalk.dim(cwd)}`)\n log.plain(chalk.dim('Run workscribe profile list to browse available profiles.'))\n }\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(' Profiles detected for this directory:'))\n log.plain('')\n for (const entry of matches) {\n const files = entry.detect?.files?.join(', ') ?? ''\n log.plain(` ${chalk.cyan(entry.name.padEnd(18))} ${entry.displayName} ${chalk.dim('(' + files + ')')}`)\n }\n log.plain('')\n log.plain(` Run: ${chalk.cyan('workscribe profile add ' + matches.map((e) => e.name).join(' '))}`)\n log.plain('')\n })\n\n // ─── suggest ──────────────────────────────────────────────────────────────\n profile\n .command('suggest')\n .description('Analyse recent unclassified commands and suggest profiles')\n .option('--days <n>', 'number of days to analyse', '30')\n .action((opts) => {\n const days = Math.max(1, parseInt(opts.days as string, 10) || 30)\n const toolCounts = getOtherEventToolCounts(days)\n\n if (toolCounts.length === 0) {\n log.plain('No unclassified commands found in the last ' + days + ' days.')\n return\n }\n\n const manifest = readCachedManifest()\n if (!manifest) {\n log.plain('No manifest cached. Run workscribe profile list first.')\n return\n }\n\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n // Match tool counts against manifest keywords\n const suggestions: { entry: (typeof manifest)[number]; count: number }[] = []\n for (const entry of manifest) {\n if (activeNames.has(entry.name)) continue\n const matchedCount = toolCounts\n .filter((toolCount) =>\n entry.keywords.some((keyword) => toolCount.tool.toLowerCase().includes(keyword.toLowerCase()))\n )\n .reduce((total, toolCount) => total + toolCount.count, 0)\n if (matchedCount >= MIN_SUGGEST_COUNT) {\n suggestions.push({ entry, count: matchedCount })\n }\n }\n\n suggestions.sort((a, b) => b.count - a.count)\n\n if (suggestions.length === 0) {\n const activeEntries = manifest.filter((entry) => activeNames.has(entry.name))\n const isToolCoveredByActiveProfile = (tool: string) =>\n activeEntries.some((entry) =>\n entry.keywords.some((keyword) => tool.toLowerCase().includes(keyword.toLowerCase()))\n )\n const uncoveredTools = toolCounts.filter((toolCount) => !isToolCoveredByActiveProfile(toolCount.tool))\n\n if (uncoveredTools.length === 0) {\n log.plain('')\n log.plain('Your active profiles cover all unclassified commands.')\n log.plain(chalk.dim(' Future captures will be classified correctly.'))\n log.plain('')\n } else {\n log.plain('')\n log.plain('No profile matches found for your unclassified commands.')\n log.plain('')\n log.plain(chalk.dim(' Top unclassified tools:'))\n for (const { tool, count } of uncoveredTools.slice(0, 5)) {\n log.plain(chalk.dim(` ${tool.padEnd(24)} ${count} event${count === 1 ? '' : 's'}`))\n }\n log.plain('')\n log.plain(chalk.dim(' Run workscribe events --category other to review them.'))\n log.plain('')\n }\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Analysed last ${days} days of unclassified commands:`))\n log.plain('')\n for (const { entry, count } of suggestions) {\n log.plain(\n ` ${chalk.cyan(entry.name.padEnd(18))} ${String(count).padStart(4)} commands → ${entry.displayName}`\n )\n }\n log.plain('')\n log.plain(\n ` Run: ${chalk.cyan('workscribe profile add ' + suggestions.map((suggestion) => suggestion.entry.name).join(' '))}`\n )\n log.plain('')\n\n const allCoveredEntries = [\n ...manifest.filter((entry) => activeNames.has(entry.name)),\n ...suggestions.map((suggestion) => suggestion.entry),\n ]\n const isToolCoveredAfterInstall = (tool: string) =>\n allCoveredEntries.some((entry) =>\n entry.keywords.some((keyword) => tool.toLowerCase().includes(keyword.toLowerCase()))\n )\n const stillUncovered = toolCounts.filter((toolCount) => !isToolCoveredAfterInstall(toolCount.tool)).slice(0, 5)\n if (stillUncovered.length > 0) {\n log.plain(chalk.dim(' Still uncovered after installing:'))\n for (const { tool, count } of stillUncovered) {\n log.plain(chalk.dim(` ${tool.padEnd(24)} ${count} event${count === 1 ? '' : 's'}`))\n }\n log.plain('')\n }\n })\n}\n","import { Command } from 'commander'\nimport { insertEvent } from '../db'\nimport { getRepoContext } from '../capture/git'\nimport { eventDimensions } from '../capture/dimensions'\nimport { readConfig } from '../config'\nimport { currentDirectory } from '../utils/working-directory'\nimport { log } from '../utils/logger'\n\nexport function registerNote(program: Command): void {\n program\n .command('note <text>')\n .description('Attach a free-text annotation to the current session')\n .action((text: string) => {\n // As you reached it, like the shell hook's `$PWD`, so a `projects.<path>` override and `context` match it\n const directory = currentDirectory()\n const context = getRepoContext(directory, readConfig())\n const { repoName, branch } = context\n insertEvent({\n captured_at: new Date().toISOString(),\n command: 'workscribe note',\n cwd: directory,\n exit_code: null,\n duration_ms: null,\n repo_name: repoName,\n branch,\n category: 'note',\n metadata: JSON.stringify({ text }),\n source: null,\n file_path: null,\n dimensions: eventDimensions(context),\n })\n log.success(`Note saved${repoName ? ` to ${repoName}` : ''}`)\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { WORKSCRIBE_DIR, DEBUG_LOG_PATH, CONFIG_PATH } from '../utils/paths'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\nconst GITHUB_ISSUES_URL = 'https://github.com/workscribe/workscribe/issues/new'\nconst DEBUG_LOG_TAIL_LINES = 100\n\nfunction tailFile(filePath: string, lines: number): string {\n if (!fs.existsSync(filePath)) return '(no debug log found — set WORKSCRIBE_DEBUG=1 to enable)'\n const content = fs.readFileSync(filePath, 'utf-8')\n const allLines = content.split('\\n')\n return allLines.slice(-lines).join('\\n')\n}\n\nfunction redactConfig(): string {\n try {\n const config = readConfig()\n if (config.ai?.apiKey) config.ai.apiKey = '<redacted>' // left over from a 1.x config\n if (config.integrations?.slackWebhook) config.integrations.slackWebhook = '<redacted>'\n if (config.integrations?.webhook) config.integrations.webhook = '<redacted>'\n return JSON.stringify(config, null, 2)\n } catch {\n return '(could not read config)'\n }\n}\n\nexport function registerBugReport(program: Command): void {\n program\n .command('bug-report')\n .description('Generate a diagnostic report for filing a bug')\n .action(() => {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-')\n const reportPath = path.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`)\n\n const systemInfo = [\n `Workscribe version : ${__WORKSCRIBE_VERSION__}`,\n `Node.js version : ${process.version}`,\n `Platform : ${process.platform}`,\n `Architecture : ${process.arch}`,\n `OS release : ${os.release()}`,\n `Config path : ${CONFIG_PATH}`,\n `Data directory : ${WORKSCRIBE_DIR}`,\n ].join('\\n')\n\n const report = [\n '=== Workscribe Bug Report ===',\n `Generated: ${new Date().toISOString()}`,\n '',\n '--- System ---',\n systemInfo,\n '',\n '--- Config (secrets redacted) ---',\n redactConfig(),\n '',\n `--- Debug log (last ${DEBUG_LOG_TAIL_LINES} lines) ---`,\n tailFile(DEBUG_LOG_PATH, DEBUG_LOG_TAIL_LINES),\n ].join('\\n')\n\n try {\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 0o700 })\n }\n fs.writeFileSync(reportPath, report, { mode: 0o600 })\n } catch (error) {\n log.error(`Could not write report: ${(error as Error).message}`)\n process.exit(1)\n }\n\n const params = new URLSearchParams({\n title: `Bug report — workscribe ${__WORKSCRIBE_VERSION__}`,\n body: `<!-- Attach ${path.basename(reportPath)} or paste the relevant sections below -->\\n\\n**Describe the issue:**\\n\\n**Steps to reproduce:**\\n\\n**Expected behaviour:**`,\n })\n\n log.success(`Report written to: ${chalk.cyan(reportPath)}`)\n log.plain('')\n log.plain(` Open a GitHub issue:`)\n log.plain(` ${chalk.cyan(`${GITHUB_ISSUES_URL}?${params}`)}`)\n log.plain('')\n log.plain(chalk.dim(` Attach the report or paste the relevant sections into the issue.`))\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport type { JobRunResult, RunResult } from '../db/jobs/runner'\nimport { getDataStatus, runDataJobs } from './data-jobs'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\nfunction printVerification(result: JobRunResult): void {\n const { verification } = result\n if (!verification) return\n const counts = `checked ${verification.checked}, resolved ${verification.resolved}, unresolved ${verification.unresolved}`\n const line = `${result.name} (v${result.version}) — ${counts}`\n if (verification.hasPassed) log.success(line)\n else log.error(`${line} — check failed`)\n for (const note of verification.notes) log.plain(` ${chalk.dim(note)}`)\n}\n\nfunction printResult(result: JobRunResult): void {\n if (result.outcome === 'failed') {\n log.error(`${result.name} failed: ${result.error ?? 'unknown error'} — run \\`workscribe upgrade\\` again to retry`)\n return\n }\n if (result.outcome === 'paused') {\n const reason = result.error ?? 'another Workscribe process is using the database — run `workscribe upgrade` again'\n log.warn(`${result.name} paused: ${reason}`)\n return\n }\n printVerification(result)\n}\n\nfunction hasSucceeded(result: JobRunResult): boolean {\n return result.outcome === 'done' && result.verification?.hasPassed !== false\n}\n\n/** A check that failed on an earlier run stays failed: rerunning can't bring missing data back, the backup can. */\nfunction reportFailedChecks(failedChecks: string[], latestBackup: string | null): void {\n log.error(`The check after upgrading ${failedChecks.join(', ')} didn't pass — some data may not have carried over.`)\n if (latestBackup) log.plain(chalk.dim(` Your data from before the upgrade is in ${latestBackup}`))\n log.plain(chalk.dim(' Run `workscribe bug-report` so it can be fixed.'))\n}\n\n/** Prints what the run did and returns whether the data is now fully up to date, with every check passing. */\nfunction reportRun(result: RunResult): boolean {\n if (result.backup === 'busy') {\n log.warn(\n 'Another Workscribe command is backing up your data right now. Run `workscribe upgrade` again in a moment.'\n )\n return false\n }\n if (result.backup === 'unavailable') {\n log.error(\"Couldn't back up your data, so nothing was changed. Run `workscribe upgrade` again to retry.\")\n return false\n }\n\n result.jobs.forEach(printResult)\n const { latestBackup, pending, failedChecks } = getDataStatus()\n if (failedChecks.length > 0) {\n reportFailedChecks(failedChecks, latestBackup)\n return false\n }\n if (result.backup === 'not-needed') {\n log.success('Your Workscribe data is up to date.')\n return true\n }\n if (latestBackup) log.plain(chalk.dim(` Backup: ${latestBackup}`))\n return pending.length === 0 && result.jobs.every(hasSucceeded)\n}\n\nexport function registerUpgrade(program: Command): void {\n program\n .command('upgrade')\n .description('Bring your stored data up to date after updating Workscribe (backs it up first)')\n .action(() => {\n const announced = new Set<string>()\n const result = runDataJobs(__WORKSCRIBE_VERSION__, {\n onProgress: ({ job }) => {\n if (announced.has(job.name)) return\n announced.add(job.name)\n log.info(`Upgrading: ${job.description}`)\n },\n })\n if (!reportRun(result)) process.exit(1)\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getPendingAlerts, clearAlerts, hasPendingAlerts } from '../db/alerts'\nimport { log } from '../utils/logger'\n\nfunction formatTimestamp(ms: number): string {\n return new Date(ms).toLocaleString([], {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })\n}\n\nexport function registerAlerts(program: Command): void {\n const alertsCmd = program\n .command('alerts')\n .description('Show and manage security alerts from AI session monitoring')\n .option('--check', 'Print warning if alerts are pending (used by shell hooks)')\n .action((opts: { check?: boolean }) => {\n if (opts.check) {\n if (hasPendingAlerts()) {\n log.plain(\n chalk.yellow(' [workscribe] Possible credentials detected in an AI session.') +\n chalk.dim(' Run workscribe alerts for details.')\n )\n }\n return\n }\n\n const alerts = getPendingAlerts()\n if (alerts.length === 0) {\n log.plain('')\n log.plain(' No pending security alerts.')\n log.plain('')\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(' Security Alerts') + chalk.dim(` (${alerts.length} pending)`))\n log.plain('')\n\n for (const alert of alerts) {\n log.plain(` ${chalk.yellow('!')} ${chalk.dim(formatTimestamp(alert.created_at))} ${chalk.cyan(alert.tool)}`)\n log.plain(` ${alert.context}`)\n if (alert.session_id) log.plain(` ${chalk.dim('Session: ' + alert.session_id)}`)\n log.plain('')\n }\n\n log.plain(chalk.dim(' Run workscribe alerts clear to dismiss all alerts'))\n log.plain('')\n })\n\n alertsCmd\n .command('clear')\n .description('Dismiss all pending security alerts')\n .action(() => {\n const count = clearAlerts()\n if (count === 0) {\n log.plain(' No pending alerts to clear.')\n } else {\n log.plain(` ${count} alert${count === 1 ? '' : 's'} cleared.`)\n }\n })\n}\n","// What `context --file --json` says about the file itself — for a tooltip about the file rather than the whole\n// session. Pure: the session's events, the commits and how to display a path are passed in.\nimport path from 'path'\nimport type { EventRow } from '../db/types'\nimport type { GitCommit } from './git-commits'\n\n// How many of the session's other files to name; the rest are only counted\nconst EDITED_ALONGSIDE_LIMIT = 5\nconst CODE_EDIT = 'code_edit'\n\nexport interface FileDetails {\n path: string\n relativePath: string\n firstSavedAt: string\n lastSavedAt: string\n /** Saves recorded — capture keeps one save per file per 30 s, so not every keystroke save */\n saveCount: number\n branch: string | null\n commits: GitCommit[]\n editedAlongside: { files: string[]; total: number }\n}\n\n/** A file relative to its repo root when it's inside it, else its name. Pass resolved paths on both sides. */\nexport function displayPath(absoluteFilePath: string, repoRoot: string | null): string {\n if (repoRoot) {\n const relative = path.relative(repoRoot, absoluteFilePath)\n if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) return relative\n }\n return path.basename(absoluteFilePath)\n}\n\n/** Whether a stored path is the file being asked about — the same path, or (when the caller can tell) the same file. */\nexport type FileMatcher = (storedPath: string) => boolean\n\nfunction samePathAs(filePath: string): FileMatcher {\n return (storedPath) => storedPath === filePath\n}\n\n/** The session's other saved files, most-saved first, then most recently saved. */\nfunction otherFilesBySaves(sessionEvents: EventRow[], isThisFile: FileMatcher): string[] {\n const saves = new Map<string, { count: number; lastSavedAt: string }>()\n for (const event of sessionEvents) {\n if (event.category !== CODE_EDIT || !event.file_path || isThisFile(event.file_path)) continue\n const seen = saves.get(event.file_path)\n saves.set(event.file_path, {\n count: (seen?.count ?? 0) + 1,\n lastSavedAt: seen && seen.lastSavedAt > event.captured_at ? seen.lastSavedAt : event.captured_at,\n })\n }\n return [...saves.entries()]\n .sort(([, first], [, second]) => second.count - first.count || second.lastSavedAt.localeCompare(first.lastSavedAt))\n .map(([otherPath]) => otherPath)\n}\n\nexport interface FileDetailsOptions {\n filePath: string\n sessionEvents: EventRow[]\n sessionBranch: string | null\n commits: GitCommit[]\n /** How to show a stored path — relative to the repo root, resolving symlinks (see `displayPath`) */\n displayPathOf: (storedPath: string) => string\n /** Which stored paths are this file; by default only `filePath` itself */\n isThisFile?: FileMatcher\n}\n\n/** The session's saves of this file, oldest first. */\nexport function savesOf(sessionEvents: EventRow[], isThisFile: FileMatcher): EventRow[] {\n return sessionEvents.filter(\n (event) => event.category === CODE_EDIT && event.file_path !== null && isThisFile(event.file_path)\n )\n}\n\n/** The file's saves in the session, or null if it has none (it was found through a non-save event). */\nexport function buildFileDetails(options: FileDetailsOptions): FileDetails | null {\n const { filePath, sessionEvents, displayPathOf } = options\n const isThisFile = options.isThisFile ?? samePathAs(filePath)\n const saves = savesOf(sessionEvents, isThisFile)\n if (saves.length === 0) return null\n\n const savedTimes = saves.map((save) => save.captured_at).sort()\n // Saves are oldest first, so the last one with a branch is the latest\n const latestBranch = [...saves].reverse().find((save) => save.branch)?.branch ?? null\n const otherFiles = otherFilesBySaves(sessionEvents, isThisFile)\n\n return {\n path: filePath,\n relativePath: displayPathOf(filePath),\n firstSavedAt: savedTimes[0],\n lastSavedAt: savedTimes.at(-1) ?? savedTimes[0],\n saveCount: saves.length,\n branch: latestBranch ?? options.sessionBranch,\n commits: options.commits,\n editedAlongside: {\n files: otherFiles.slice(0, EDITED_ALONGSIDE_LIMIT).map(displayPathOf),\n total: otherFiles.length,\n },\n }\n}\n","// The I/O behind `context --file --json`'s file details, and the time limits the whole command answers within.\nimport type { EventRow } from '../db/types'\nimport { buildFileDetails, displayPath, savesOf, type FileDetails, type FileMatcher } from '../summary/file-details'\nimport { getFileCommits } from '../summary/git-commits'\nimport { resolvedPath } from '../utils/working-directory'\n\n// The VS Code extension gives up on `context --file` after 5 s and then shows nothing, so the whole command has to\n// answer well within that: about 1 s waiting for another process's lock, at most 2 s of git\nexport const LOCK_WAIT_MS = 1000\nexport const GIT_BUDGET_MS = 2000\n\n/** The stretch of work `context --file` answers with: the thread's events and span. */\nexport interface FileSpan {\n events: EventRow[]\n branch: string | null\n startedAt: string\n endedAt: string\n}\n\n/** What the span says about this file: its saves, branch, commits and the files edited alongside it. */\n/** The stored path is this file: the same path, or another path to the same file on disk (through a symlink). */\nfunction sameFileAs(filePath: string): FileMatcher {\n const fileOnDisk = resolvedPath(filePath)\n return (storedPath) => storedPath === filePath || resolvedPath(storedPath) === fileOnDisk\n}\n\nexport function getFileDetails(filePath: string, span: FileSpan): FileDetails | null {\n const isThisFile = sameFileAs(filePath)\n // Found through a non-save event: nothing to say about the file, so don't spend the git budget\n if (savesOf(span.events, isThisFile).length === 0) return null\n\n // git reports the resolved repo root, and the editor may have opened the file through a symlink\n const { repoRoot, commits } = getFileCommits({\n filePath: resolvedPath(filePath),\n since: span.startedAt,\n until: span.endedAt,\n budgetMs: GIT_BUDGET_MS,\n })\n return buildFileDetails({\n filePath,\n sessionEvents: span.events,\n sessionBranch: span.branch,\n commits,\n isThisFile,\n displayPathOf: (storedPath) => displayPath(resolvedPath(storedPath), repoRoot),\n })\n}\n","import type { EventRow } from '../db/types'\nimport type { StoredThread } from '../db/thread-store'\nimport type { SummaryRow } from '../db/summary-store'\nimport type { WorkBlockRow } from '../db/context-lookup'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { summariesOfThread, type BlockEvent } from './anchors'\nimport { minutesBetween } from '../sessions/grouper'\nimport { pickBestSummary, sessionKey } from './store'\n\n// What `context` says on the thread path (pure): the thread an event belongs to, in its block, with the projects\n// worked alongside it, and the summary that belongs to that thread.\n\nexport interface ThreadSpanJson {\n repoName: string | null\n startedAt: string\n endedAt: string\n eventCount: number\n activeMinutes: number\n}\n\n/** The fields every `context` answer has. */\nexport interface WorkSpan {\n repoName: string | null\n date: string\n startedAt: string\n endedAt: string\n durationMinutes: number\n branch: string | null\n eventCount: number\n}\n\nexport interface ThreadContext extends WorkSpan {\n /** The best summary stored for this thread, or null: the caller renders the template from `events` */\n savedSummary: string | null\n thread: { key: string; projectId: number | null; activeMinutes: number }\n block: { startedAt: string; endedAt: string; activeMinutes: number }\n /** The block's other threads, in the order their projects first appear */\n alongside: ThreadSpanJson[]\n}\n\nexport interface ThreadContextInput {\n block: WorkBlockRow\n threads: StoredThread[]\n blockEvents: BlockEvent[]\n thread: StoredThread\n /** The thread's own events, in grouping order */\n threadEvents: EventRow[]\n projectNames: Map<number, string | null>\n summaries: SummaryRow[]\n}\n\nfunction nameOf(thread: StoredThread, projectNames: Map<number, string | null>): string | null {\n return thread.projectId === null ? null : (projectNames.get(thread.projectId) ?? null)\n}\n\nfunction latestBranch(events: EventRow[]): string | null {\n return [...events].reverse().find((event) => event.branch)?.branch ?? null\n}\n\n/** The best stored summary that belongs to this thread, if any. */\nfunction summaryOfThread(input: ThreadContextInput): string | null {\n const block = { threads: input.threads, events: input.blockEvents }\n return pickBestSummary(summariesOfThread(input.thread.id, block, input.summaries))\n}\n\nexport function buildThreadContext(input: ThreadContextInput): ThreadContext {\n const { thread, block, projectNames } = input\n const repoName = nameOf(thread, projectNames)\n return {\n repoName,\n date: block.date,\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n durationMinutes: minutesBetween(thread.startedAt, thread.endedAt),\n branch: latestBranch(input.threadEvents),\n eventCount: thread.eventCount,\n savedSummary: summaryOfThread(input),\n thread: {\n key: sessionKey(repoName, thread.startedAt),\n projectId: thread.projectId,\n activeMinutes: toActiveMinutes(thread.activeSlots),\n },\n block: { startedAt: block.startedAt, endedAt: block.endedAt, activeMinutes: toActiveMinutes(block.activeSlots) },\n alongside: input.threads\n .filter((other) => other.id !== thread.id)\n .map((other) => ({\n repoName: nameOf(other, projectNames),\n startedAt: other.startedAt,\n endedAt: other.endedAt,\n eventCount: other.eventCount,\n activeMinutes: toActiveMinutes(other.activeSlots),\n })),\n }\n}\n","// The I/O behind `context`: finds the event to answer from, builds its day's threads (waiting ~1 s at most for the\n// lock) and reads the thread holding it, with its block. When another process holds the lock, it answers from the day\n// as last built — it never waits twice; a day never built, or data not upgraded yet, finds nothing.\nimport { getDb } from '../db'\nimport type { EventRow } from '../db/types'\nimport { isDatabaseLocked, withReadTransaction } from '../db/transaction'\nimport { getThreadEvents, readBlockHoldingEvent } from '../db/thread-store'\nimport { isReadyForThreads } from '../db/day-threads'\nimport {\n findLatestFileSave,\n findLatestProjectEvent,\n getAnchoredSummariesInBlock,\n getProjectNames,\n getWorkBlock,\n type EventPoint,\n} from '../db/context-lookup'\nimport { JOBS } from '../db/jobs'\nimport { ensureDayThreads } from '../sessions/threads'\nimport { buildThreadContext, type ThreadContext } from '../summary/thread-context'\nimport { localDateOf } from '../utils/format'\nimport { LOCK_WAIT_MS } from './context-file'\n\nexport interface ThreadAnswer {\n threadId: number\n context: ThreadContext\n /** The thread's events, for the template summary and the file details */\n events: EventRow[]\n}\n\n/** The thread holding the event, from one snapshot — so another process rebuilding the day can't split the read. */\nfunction readBuiltThread(eventId: number): ThreadAnswer | null {\n const db = getDb()\n return withReadTransaction(db, () => {\n const holding = readBlockHoldingEvent(db, eventId)\n const threadId = holding?.events.find((blockEvent) => blockEvent.id === eventId)?.threadId\n const thread = holding?.threads.find((candidate) => candidate.id === threadId)\n const block = thread ? getWorkBlock(db, thread.blockId) : null\n if (!holding || !thread || !block) return null\n const projectIds = holding.threads.flatMap((other) => (other.projectId === null ? [] : [other.projectId]))\n const events = getThreadEvents(db, thread.id)\n const context = buildThreadContext({\n block,\n threads: holding.threads,\n blockEvents: holding.events,\n thread,\n threadEvents: events,\n projectNames: getProjectNames(db, projectIds),\n summaries: getAnchoredSummariesInBlock(\n db,\n holding.events.map((blockEvent) => blockEvent.id)\n ),\n })\n return { threadId: thread.id, context, events }\n })\n}\n\nfunction lookUpThreadOf(findEvent: () => EventPoint | null): ThreadAnswer | null {\n if (!isReadyForThreads(getDb(), JOBS)) return null\n const event = findEvent()\n if (event === null) return null\n try {\n ensureDayThreads(localDateOf(event.capturedAt), { lockWaitMs: LOCK_WAIT_MS })\n } catch (error) {\n // Another process is writing: the day as last built answers, and the next call catches up\n if (!isDatabaseLocked(error)) throw error\n }\n return readBuiltThread(event.id)\n}\n\n/** `context --file`: the thread holding the file's latest save. */\nexport function lookUpFileThread(filePath: string): ThreadAnswer | null {\n return lookUpThreadOf(() => findLatestFileSave(getDb(), filePath))\n}\n\n/** `context`: the thread holding the latest event of the project with this identity. */\nexport function lookUpProjectThread(identity: string): ThreadAnswer | null {\n return lookUpThreadOf(() => findLatestProjectEvent(getDb(), identity))\n}\n","// What `context` tells about the thread it answers with.\nimport type { EventRow } from '../db/types'\nimport { readConfig } from '../config'\nimport { renderSessionStory } from '../ai/engines/template/story'\nimport { spanToPayload, locationContextFrom } from '../summary/payload'\nimport type { ThreadContext, WorkSpan } from '../summary/thread-context'\nimport type { ThreadAnswer } from './context-threads'\n\nexport type { WorkSpan } from '../summary/thread-context'\n\nexport interface ContextView extends WorkSpan {\n summary: string\n events: EventRow[]\n threaded: Pick<ThreadContext, 'thread' | 'block' | 'alongside'>\n}\n\n/**\n * A template summary rendered from the span's events — templates are never stored, so it can't go stale. It tells\n * only the commits Workscribe captured: no git lookups, so the VS Code extension's 5 s answer holds.\n */\nfunction renderTemplateSummary(span: WorkSpan, events: EventRow[]): string {\n const config = readConfig()\n const payload = spanToPayload({ ...span, events }, span.date, { context: locationContextFrom(config) })\n return renderSessionStory(payload, config.summary?.includeCategories ?? [])\n}\n\nexport function threadViewOf({ context, events }: ThreadAnswer): ContextView {\n const { savedSummary, thread, block, alongside, ...span } = context\n return {\n ...span,\n // The best stored summary for the thread, or the template's so the VS Code status bar is never empty\n summary: savedSummary ?? renderTemplateSummary(span, events),\n events,\n threaded: { thread, block, alongside },\n }\n}\n","import path from 'path'\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getRepoContext } from '../capture/git'\nimport { currentDirectory } from '../utils/working-directory'\nimport { readConfig } from '../config'\nimport { formatDuration, timeAgo } from '../utils/format'\nimport { log } from '../utils/logger'\nimport { getFileDetails, type FileSpan } from './context-file'\nimport { lookUpFileThread, lookUpProjectThread } from './context-threads'\nimport { threadViewOf, type ContextView, type WorkSpan } from './context-view'\nimport type { FileDetails } from '../summary/file-details'\nimport type { ThreadContext } from '../summary/thread-context'\n\n/** The first version of the `context --json` document; the fields before it are kept exactly as they were. */\nexport const CONTEXT_SCHEMA_VERSION = 1\n\nexport interface SessionContextJson extends WorkSpan {\n timeAgo: string\n summary: string | null\n schemaVersion: number\n // Added after the first fields; an answer always has them now (before WOR-151 an old session's answer had null)\n thread: ThreadContext['thread']\n block: ThreadContext['block']\n alongside: ThreadContext['alongside']\n}\n\nfunction toContextJson(view: ContextView): SessionContextJson {\n return {\n repoName: view.repoName,\n date: view.date,\n startedAt: view.startedAt,\n endedAt: view.endedAt,\n timeAgo: timeAgo(view.startedAt),\n durationMinutes: view.durationMinutes,\n branch: view.branch,\n eventCount: view.eventCount,\n summary: view.summary,\n schemaVersion: CONTEXT_SCHEMA_VERSION,\n thread: view.threaded.thread,\n block: view.threaded.block,\n alongside: view.threaded.alongside,\n }\n}\n\n/** `context --file --json`: the answer, plus what it says about this file (`file` is null without saves of it). */\nfunction toFileContextJson(view: ContextView, filePath: string): SessionContextJson & { file: FileDetails | null } {\n const span: FileSpan = { events: view.events, branch: view.branch, startedAt: view.startedAt, endedAt: view.endedAt }\n return { ...toContextJson(view), file: getFileDetails(filePath, span) }\n}\n\nfunction printContext(view: ContextView, heading: string): void {\n log.plain('')\n log.plain(` ${chalk.bold(heading)}: ${chalk.cyan(timeAgo(view.startedAt))}`)\n log.plain('')\n log.plain(` Date ${view.date}`)\n log.plain(` Duration ${formatDuration(view.durationMinutes)}`)\n if (view.branch) log.plain(` Branch ${chalk.dim(view.branch)}`)\n log.plain(` Events ${view.eventCount}`)\n const alongside = view.threaded.alongside.map((other) => other.repoName ?? 'elsewhere')\n if (alongside.length > 0) log.plain(` Alongside ${chalk.dim(alongside.join(', '))}`)\n\n log.plain('')\n for (const line of view.summary.split('\\n')) {\n log.plain(` ${line}`)\n }\n\n log.plain('')\n}\n\nfunction printNothing(isJson: boolean, message: string): void {\n if (isJson) log.plain(JSON.stringify(null))\n else log.plain(` ${message}`)\n}\n\nfunction runFileContext(file: string, isJson: boolean): void {\n const filePath = path.normalize(file)\n const answer = lookUpFileThread(filePath)\n if (!answer) {\n printNothing(isJson, `No recent work found touching ${filePath}`)\n return\n }\n const view = threadViewOf(answer)\n if (isJson) log.plain(JSON.stringify(toFileContextJson(view, filePath)))\n else printContext(view, `Last worked on ${path.basename(filePath)}`)\n}\n\nfunction runRepoContext(isJson: boolean): void {\n const repo = getRepoContext(currentDirectory(), readConfig())\n if (!repo.repoName) {\n printNothing(isJson, 'Not in a git repository. Run workscribe context from a project directory.')\n return\n }\n const answer = repo.project ? lookUpProjectThread(repo.project.identity) : null\n if (!answer) {\n printNothing(isJson, `No recent work found for ${repo.repoName}`)\n return\n }\n const view = threadViewOf(answer)\n if (isJson) log.plain(JSON.stringify(toContextJson(view)))\n else printContext(view, `Last worked on ${repo.repoName}`)\n}\n\nexport function registerContext(program: Command): void {\n program\n .command('context')\n .description('Show the last work on the current repo')\n .option('--file <path>', 'narrow to the last work that touched this file')\n .option('--json', 'output as JSON instead of formatted text — null when nothing is found')\n .action((opts) => {\n if (opts.file) runFileContext(opts.file, Boolean(opts.json))\n else runRepoContext(Boolean(opts.json))\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { SKILL_AGENTS, SKILL_AGENT_IDS } from '../skills/agents'\nimport { detectAgents } from '../skills/detect'\nimport { resolvePackagedSkillDir } from '../skills/package-path'\nimport { groupBySkillsFolder, joinAgentNames } from '../skills/state'\nimport { AFTER_INSTALL_HINT, reasonOf } from '../skills/report'\nimport {\n describeTarget,\n formatAgentSkillLine,\n formatUndetectedAgentLine,\n installIntoFolder,\n printMessage,\n removeSkills,\n} from '../skills/actions'\nimport type { SkillAgent } from '../skills/types'\nimport { fail } from './fail'\n\ninterface AgentOptions {\n agent?: string[]\n force?: boolean\n}\n\n/** The agents named with --agent (all must be known), or the given default. */\nfunction chooseAgents(options: {\n requested: string[] | undefined\n fallback: () => SkillAgent[]\n subcommand: 'install' | 'uninstall'\n}): SkillAgent[] {\n const { requested } = options\n if (!requested || requested.length === 0) return options.fallback()\n const unknown = requested.filter((id) => !SKILL_AGENT_IDS.includes(id))\n if (unknown.length > 0) {\n fail(\n `Unknown agent: ${unknown.join(', ')}. Choose from: ${SKILL_AGENT_IDS.join(', ')}. ` +\n `For example: workscribe skill ${options.subcommand} --agent claude-code`\n )\n }\n return SKILL_AGENTS.filter((agent) => requested.includes(agent.id))\n}\n\nfunction packagedSkillDir(): string {\n try {\n return resolvePackagedSkillDir()\n } catch (error) {\n fail(reasonOf(error))\n }\n}\n\nfunction installCommand(options: AgentOptions): void {\n const agents = chooseAgents({ requested: options.agent, fallback: detectAgents, subcommand: 'install' })\n if (agents.length === 0) {\n log.info(\n `No coding agents found. Workscribe can install its skill for ${joinAgentNames(SKILL_AGENTS)}. ` +\n \"If yours is installed somewhere Workscribe didn't look, name it: workscribe skill install --agent claude-code\"\n )\n return\n }\n const sourceDir = packagedSkillDir()\n for (const group of groupBySkillsFolder(agents)) {\n printMessage(installIntoFolder({ target: describeTarget(group), sourceDir, force: options.force }))\n }\n log.plain(chalk.dim(` ${AFTER_INSTALL_HINT}`))\n}\n\nfunction statusCommand(): void {\n const detected = new Set(detectAgents().map((agent) => agent.id))\n log.plain('')\n log.plain(chalk.bold(' Workscribe agent skill'))\n for (const agent of SKILL_AGENTS) {\n const line = detected.has(agent.id) ? formatAgentSkillLine(agent) : formatUndetectedAgentLine(agent)\n log.plain(` ${line}`)\n }\n log.plain('')\n}\n\nfunction uninstallCommand(options: AgentOptions): void {\n const agents = chooseAgents({ requested: options.agent, fallback: () => SKILL_AGENTS, subcommand: 'uninstall' })\n removeSkills(agents, { reportMissing: true })\n}\n\nexport function registerSkill(program: Command): void {\n const skill = program.command('skill').description('Install the Workscribe agent skill into your coding agents')\n const agentOption = `limit to these agents (${SKILL_AGENT_IDS.join(', ')})`\n\n skill\n .command('install')\n .description('Install or update the skill for every detected coding agent')\n .option('--agent <ids...>', agentOption)\n .option('--force', \"replace a workscribe skill that Workscribe didn't install\")\n .action(installCommand)\n\n skill\n .command('status')\n .description('Show which agents have the skill and whether it is up to date')\n .action(statusCommand)\n\n skill\n .command('uninstall')\n .description('Remove the skill Workscribe installed (never one you added yourself)')\n .option('--agent <ids...>', agentOption)\n .action(uninstallCommand)\n}\n","import fs from 'fs'\nimport os from 'os'\nimport { UPDATE_CHECK_PATH } from './paths'\nimport { readConfig, WorkscribeConfig } from '../config'\nimport { debugLog } from './logger'\n\nexport interface TelemetryPayload {\n installId: string\n cliVersion: string\n platform: string\n nodeVersion: string\n}\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000\n\ninterface UpdateCache {\n lastChecked: number\n latestVersion: string\n lastTelemetryPing?: number\n}\n\nfunction readCache(): UpdateCache | null {\n try {\n return JSON.parse(fs.readFileSync(UPDATE_CHECK_PATH, 'utf-8')) as UpdateCache\n } catch {\n return null\n }\n}\n\n// Merges with whatever's already on disk rather than overwriting — checkForUpdate()\n// and pingTelemetry() each own one field in the same cache file and must not\n// silently wipe out the other's value when they write.\nfunction writeCache(updates: Partial<UpdateCache>): void {\n try {\n const current = readCache() ?? {}\n fs.writeFileSync(UPDATE_CHECK_PATH, JSON.stringify({ ...current, ...updates }), { mode: 0o600 })\n } catch {\n // Not critical — silently skip if the data dir doesn't exist yet\n }\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n const parse = (version: string) => version.replace(/^v/, '').split('.').map(Number)\n const [latestMajor, latestMinor, latestPatch] = parse(latest)\n const [currentMajor, currentMinor, currentPatch] = parse(current)\n if (latestMajor !== currentMajor) return latestMajor > currentMajor\n if (latestMinor !== currentMinor) return latestMinor > currentMinor\n return latestPatch > currentPatch\n}\n\nexport function checkForUpdate(): void {\n const current = __WORKSCRIBE_VERSION__\n const cache = readCache()\n\n if (cache && isNewer(cache.latestVersion, current)) {\n process.stderr.write(\n `\\n Update available ${current} → ${cache.latestVersion}\\n Run: npm install -g @workscribe/cli\\n\\n`\n )\n }\n\n const now = Date.now()\n if (!cache || now - cache.lastChecked > CHECK_INTERVAL_MS) {\n fetch('https://registry.npmjs.org/@workscribe/cli/latest')\n .then((response) => response.json())\n .then((data: unknown) => {\n const latest = (data as { version?: string }).version\n if (latest) writeCache({ lastChecked: now, latestVersion: latest })\n })\n .catch(() => {\n // Network unavailable — will retry next run\n })\n }\n}\n\nexport function buildTelemetryPayload(config: WorkscribeConfig): TelemetryPayload {\n return {\n installId: config.telemetry.installId,\n cliVersion: __WORKSCRIBE_VERSION__,\n platform: os.platform(),\n nodeVersion: process.version,\n }\n}\n\nexport function pingTelemetry(): void {\n const config = readConfig()\n if (!config.telemetry.enabled || !config.telemetry.installId) return\n\n const cache = readCache()\n const now = Date.now()\n if (cache?.lastTelemetryPing && now - cache.lastTelemetryPing <= CHECK_INTERVAL_MS) return\n\n fetch('https://workscribe.co/api/telemetry/ping', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(buildTelemetryPayload(config)),\n })\n .then((response) => {\n // fetch() only rejects on network failure — a 4xx/5xx still resolves,\n // so without this check a server-side outage would silently look like\n // a successful ping and get throttled for a full day.\n if (!response.ok) {\n debugLog('pingTelemetry', new Error(`Ping failed with HTTP ${response.status}`))\n return\n }\n writeCache({ lastTelemetryPing: now })\n })\n .catch((error) => {\n debugLog('pingTelemetry', error)\n })\n}\n","import { Command } from 'commander'\nimport { registerCapture } from './cli/capture'\nimport { registerHook } from './cli/hook'\n\ndeclare const __WORKSCRIBE_VERSION__: string\nimport { registerInit } from './cli/init'\nimport { registerStatus } from './cli/status'\nimport { registerEvents } from './cli/events'\nimport { registerSessions } from './cli/sessions'\nimport { registerSummary } from './cli/summary'\nimport { registerExport } from './cli/export'\nimport { registerConfig } from './cli/config'\nimport { registerPause } from './cli/pause'\nimport { registerUndo } from './cli/undo'\nimport { registerClear } from './cli/clear'\nimport { registerUninstall } from './cli/uninstall'\nimport { registerProfile } from './cli/profile'\nimport { registerNote } from './cli/note'\nimport { registerWeek } from './cli/week'\nimport { registerBugReport } from './cli/bug-report'\nimport { registerCompletions, shouldRunDataJobs, shouldRunStartupChecks } from './cli/completions'\nimport { registerUpgrade } from './cli/upgrade'\nimport { runDataJobsBeforeCommand } from './cli/data-jobs'\nimport { registerAlerts } from './cli/alerts'\nimport { registerContext } from './cli/context'\nimport { registerSkill } from './cli/skill'\nimport { checkForUpdate, pingTelemetry } from './utils/update-check'\nimport { ensureTelemetryFallback } from './config'\nimport { log } from './utils/logger'\n\nconst invokedCommand = process.argv[2]\nif (shouldRunStartupChecks(invokedCommand)) {\n checkForUpdate()\n // init handles its own telemetry disclosure via an interactive prompt —\n // the silent fallback is only for users who skip init entirely. Must run\n // before pingTelemetry() — otherwise a user's very first command would\n // generate the installId but the ping already read the old empty config\n // moments earlier, delaying the first ping to the next invocation.\n if (invokedCommand !== 'init') {\n ensureTelemetryFallback()\n }\n pingTelemetry()\n}\n\n// Before a data command people typed: record the version and give pending data jobs a short budget (never in the\n// background capture, the editor's `context` calls or the agent skill's `sessions` calls — see shouldRunDataJobs)\nif (shouldRunDataJobs(process.argv.slice(2))) {\n runDataJobsBeforeCommand(invokedCommand, __WORKSCRIBE_VERSION__)\n}\n\nconst program = new Command()\n\nprogram.name('workscribe').description('Automatic developer work journal').version(__WORKSCRIBE_VERSION__)\n\nregisterCapture(program)\nregisterHook(program)\nregisterInit(program)\nregisterStatus(program)\nregisterEvents(program)\nregisterSessions(program)\nregisterSummary(program)\nregisterExport(program)\nregisterConfig(program)\nregisterPause(program)\nregisterUndo(program)\nregisterClear(program)\nregisterUninstall(program)\nregisterProfile(program)\nregisterNote(program)\nregisterWeek(program)\nregisterBugReport(program)\nregisterCompletions(program)\nregisterAlerts(program)\nregisterContext(program)\nregisterSkill(program)\nregisterUpgrade(program)\n\n// parseAsync (not parse) so a throw from either a sync or an async action\n// handler — e.g. getDb() failing to create its data directory — surfaces\n// here as a rejection instead of either an uncaught-exception stack trace\n// (sync actions) or an unhandled-rejection crash (async actions). _capture\n// and _hook already catch everything internally and never throw this far,\n// so this only ever engages for explicit user commands.\nprogram.parseAsync().catch((error: unknown) => {\n log.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n})\n","export type VersionManager = 'nvm' | 'volta' | 'fnm' | 'mise' | 'asdf' | 'nodenv'\n\nexport function detectVersionManager(): VersionManager | null {\n if (process.env.NVM_DIR) return 'nvm'\n if (process.env.VOLTA_HOME) return 'volta'\n if (process.env.FNM_DIR) return 'fnm'\n if (process.env.MISE_DATA_DIR || process.env.MISE_SHELL) return 'mise'\n if (process.env.ASDF_DIR || process.env.ASDF_DATA_DIR) return 'asdf'\n if (process.env.NODENV_ROOT) return 'nodenv'\n return null\n}\n\nconst UPGRADE_COMMANDS: Record<VersionManager, string> = {\n nvm: 'nvm install 22 && nvm use 22',\n volta: 'volta install node@22',\n fnm: 'fnm install 22 && fnm use 22',\n mise: 'mise use --global node@22',\n asdf: 'asdf install nodejs 22 && asdf global nodejs 22',\n nodenv: 'nodenv install 22 && nodenv global 22',\n}\n\nexport function assertNodeVersion(nodeVersion: string): void {\n const major = Number(nodeVersion.split('.')[0])\n if (major < 22) {\n const manager = detectVersionManager()\n const upgradeHint = manager\n ? `Upgrade with ${manager}:\\n ${UPGRADE_COMMANDS[manager]}`\n : 'Download the latest version from https://nodejs.org'\n process.stderr.write(`workscribe requires Node.js 22 or later (you have v${nodeVersion}).\\n\\n${upgradeHint}\\n`)\n process.exit(1)\n }\n}\n","// This file must import nothing beyond assertNodeVersion. ES import\n// statements are hoisted and execute before any of a module's own top-level\n// code, regardless of source order — so `import`-ing anything that\n// transitively loads node:sqlite (e.g. src/cli/capture.ts -> src/db/index.ts)\n// would load and crash on Node < 22 before assertNodeVersion ever runs.\n// require() is not hoisted, so the rest of the CLI is only loaded after the\n// version check passes.\nimport { assertNodeVersion } from './utils/node-version'\n\nassertNodeVersion(process.versions.node)\n\n// eslint-disable-next-line @typescript-eslint/no-require-imports -- must not be a static import (see file comment above)\nrequire('./main')\n"],"mappings":"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","names":["path","import_path","os","fs","path","fs","import_fs","migration","migration","migration","fs","import_fs","chalk","fs","import_fs","import_os","import_path","path","os","fs","path","crypto","import_fs","import_path","path","fs","import_fs","import_path","path","fs","import_fs","import_path","step","step","step","step","HEREDOC_OPENER","migration","migration","migration","migration","migration","migration","migration","migration","init_tickets","init_tickets","migration","init_dimensions","init_agent_sessions","linkEvent","init_tickets","init_dimensions","init_agent_sessions","init_tickets","fs","import_fs","init_agent_sessions","path","fs","import_path","import_fs","import_child_process","path","fs","import_fs","import_path","step","PIPE_SEPARATORS","step","path","import_path","path","fs","import_fs","import_path","fs","path","import_fs","import_path","path","fs","import_fs","import_path","init_dimensions","fs","command","context","repoName","branch","dimensions","import_fs","init_dimensions","init_capture","path","fs","import_fs","import_path","init_alerts","parsed","init_alerts","fs","import_fs","path","fs","import_fs","import_path","import_os","import_child_process","os","import_child_process","fs","import_fs","import_path","import_os","import_child_process","path","os","os","fs","path","import_fs","import_os","import_path","fs","path","import_fs","import_path","path","fs","import_fs","import_path","os","path","chalk","import_os","import_path","import_chalk","prompts","path","os","chalk","fs","prompts","import_fs","import_path","import_os","import_prompts","import_chalk","os","import_os","init_store","path","import_path","project","eventCount","path","import_path","init_dimensions","init_store","runBatch","countRows","runBatch","verify","init_store","runBatch","countOf","verify","init_store","init_tickets","fs","PRIVATE_DIRECTORY_MODE","path","import_fs","import_path","init_backup","init_store","init_store","init_state","init_backup","init_store","init_data_jobs","init_backup","init_state","chalk","fs","import_fs","import_chalk","init_alerts","init_data_jobs","path","import_child_process","import_path","step","splitByIdleGap","splitByIdleGap","os","path","import_os","import_path","init_utils","init_utils","chalk","import_chalk","fs","import_fs","path","import_path","init_agent_sessions","init_tickets","init_state","init_state","chalk","import_chalk","init_utils","init_constants","init_utils","init_constants","init_utils","init_constants","init_utils","init_constants","init_utils","os","import_os","init_webhook","chalk","import_chalk","init_webhook","init_week","chalk","import_chalk","init_webhook","init_week","path","fs","import_fs","import_path","chalk","import_chalk","init_config","fs","import_fs","chalk","prompts","import_chalk","import_prompts","path","os","fs","chalk","prompts","import_fs","import_path","import_os","import_prompts","import_chalk","chalk","fs","path","import_fs","import_path","import_chalk","init_dimensions","fs","path","os","chalk","import_fs","import_os","import_path","import_chalk","chalk","import_chalk","init_data_jobs","chalk","import_chalk","init_alerts","displayPath","path","latestBranch","import_path","displayPath","chalk","path","import_path","import_chalk","chalk","import_chalk","fs","os","import_fs","import_os","init_capture","init_config","init_week","init_data_jobs","init_alerts"]}
1
+ {"version":3,"sources":["../src/utils/data-dirs.ts","../src/utils/paths.ts","../src/utils/private-directory.ts","../src/db/migrations/001_initial_schema.ts","../src/db/migrations/002_semantic_categories.ts","../src/db/migrations/003_alerts.ts","../src/db/migrations/004_ide_columns.ts","../src/db/migrations/005_session_summaries.ts","../src/config/ai-tools.json","../src/utils/logger.ts","../src/config/registry.ts","../src/summary/event-metadata.ts","../src/summary/commits.ts","../src/capture/tickets.ts","../src/config/index.ts","../src/profiles/schema.ts","../src/utils/fetch.ts","../src/profiles/fetcher.ts","../src/profiles/loader.ts","../src/capture/shell-syntax.ts","../src/capture/command-steps.ts","../src/summary/exclusions.ts","../src/normalize/compound.ts","../src/normalize/git-invocation.ts","../src/normalize/commit-message.ts","../src/normalize/dependency-metadata.ts","../src/normalize/patterns.ts","../src/normalize/reclassify.ts","../src/db/migrations/006_reclassify_other.ts","../src/db/migrations/007_data_jobs.ts","../src/db/migrations/008_dimensions.ts","../src/db/migrations/009_work_blocks.ts","../src/db/migrations/010_summaries.ts","../src/db/migrations/011_events_project_time.ts","../src/db/migrations/012_agent_sessions.ts","../src/db/migrations/013_tickets.ts","../src/db/migrations/index.ts","../src/utils/error-message.ts","../src/db/migrate.ts","../src/db/transaction.ts","../src/db/dimensions.ts","../src/db/agent-sessions.ts","../src/db/tickets.ts","../src/db/event-writer.ts","../src/db/summary-store.ts","../src/db/recent-events.ts","../src/summary/store.ts","../src/summary/anchors.ts","../src/db/index.ts","../src/capture/redact.ts","../src/capture/hint.ts","../src/capture/repo-name.ts","../src/capture/project-identity.ts","../src/capture/git.ts","../src/capture/git-snapshot.ts","../src/capture/aider.ts","../src/capture/workscribe-command.ts","../src/capture/filter.ts","../src/capture/ide.ts","../src/normalize/git-directory.ts","../src/capture/commit-message-file.ts","../src/utils/working-directory.ts","../src/capture/dimensions.ts","../src/capture/index.ts","../src/cli/capture.ts","../src/capture/detect-secrets.ts","../src/db/alerts.ts","../src/cli/hook.ts","../src/utils/completion-scripts.ts","../src/utils/backup.ts","../src/capture/completions-block.ts","../src/capture/shell-hook.ts","../src/capture/hooks/utils.ts","../src/capture/hooks/claude-code-command.ts","../src/capture/hooks/claude-code.ts","../src/capture/hooks/index.ts","../src/cli/completions.ts","../src/utils/memoize.ts","../src/skills/agents.ts","../src/skills/detect.ts","../src/skills/state.ts","../src/skills/installer.ts","../src/skills/package-path.ts","../src/skills/report.ts","../src/skills/actions.ts","../src/summary/legacy-ai.ts","../src/cli/legacy-ai-cleanup.ts","../src/cli/init.ts","../src/utils/format.ts","../src/cli/notices.ts","../src/db/jobs/store.ts","../src/db/jobs/directory-probes.ts","../src/db/jobs/project-resolution.ts","../src/db/jobs/project-dimensions-report.ts","../src/db/jobs/project-dimensions.ts","../src/db/jobs/frozen-old-sessions.ts","../src/db/jobs/summary-anchors.ts","../src/db/jobs/drop-old-sessions.ts","../src/db/jobs/ticket-refs.ts","../src/db/jobs/scrub-secrets.ts","../src/db/jobs/index.ts","../src/db/jobs/backup.ts","../src/db/jobs/runner.ts","../src/db/jobs/state.ts","../src/cli/data-jobs.ts","../src/cli/status.ts","../src/summary/git-commits.ts","../src/summary/story-commits.ts","../src/normalize/git-move.ts","../src/sessions/grouper.ts","../src/sessions/blocks.ts","../src/summary/heading.ts","../src/summary/payload.ts","../src/summary/contract/constants.ts","../src/summary/contract/utils.ts","../src/summary/contract/build.ts","../src/cli/events.ts","../src/summary/write-back.ts","../src/utils/stdin.ts","../src/db/thread-store.ts","../src/db/context-lookup.ts","../src/db/day-threads.ts","../src/sessions/threads.ts","../src/cli/fail.ts","../src/cli/thread-day.ts","../src/cli/session-summary.ts","../src/cli/sessions.ts","../src/ai/engines/template/constants.ts","../src/ai/engines/template/utils.ts","../src/ai/engines/template/describe.ts","../src/ai/engines/template/branch-moves.ts","../src/ai/engines/template/story.ts","../src/ai/engines/template/week.ts","../src/ai/engines/template/provider.ts","../src/summary/writer.ts","../src/integrations/webhook.ts","../src/integrations/slack.ts","../src/summary/webhook.ts","../src/summary/day-commits.ts","../src/summary/day.ts","../src/summary/output-format.ts","../src/summary/plain-text.ts","../src/cli/day-notes.ts","../src/summary/weekly.ts","../src/cli/week.ts","../src/cli/summary.ts","../src/cli/export.ts","../src/cli/config.ts","../src/cli/pause.ts","../src/cli/undo.ts","../src/cli/clear.ts","../src/cli/uninstall.ts","../src/cli/profile.ts","../src/cli/note.ts","../src/cli/bug-report.ts","../src/cli/upgrade.ts","../src/cli/alerts.ts","../src/summary/file-details.ts","../src/cli/context-file.ts","../src/summary/thread-context.ts","../src/cli/context-threads.ts","../src/cli/context-view.ts","../src/cli/context.ts","../src/cli/skill.ts","../src/utils/update-check.ts","../src/main.ts","../src/utils/node-version.ts","../src/index.ts"],"sourcesContent":["import path from 'path'\n\n// Where Workscribe keeps its data, config and state (pure). Installs from before the XDG layout keep everything in\n// ~/.workscribe; fresh ones follow the XDG Base Directory spec. ~/.workscribe exists in both — the alerts file lives\n// there so shell hooks can find it — so a legacy install is known by its data, never by the folder.\n\nconst LEGACY_DIR_NAME = '.workscribe'\nconst APP_DIR_NAME = 'workscribe'\n/** A legacy install has one of these in ~/.workscribe; an XDG install never writes them there. */\nconst LEGACY_MARKERS = ['workscribe.db', 'config.json']\n\nexport interface DataDirsInput {\n homeDir: string\n env: Partial<Record<'XDG_DATA_HOME' | 'XDG_CONFIG_HOME' | 'XDG_STATE_HOME', string>>\n exists: (filePath: string) => boolean\n}\n\nexport interface DataDirs {\n legacyDir: string\n dataDir: string\n configDir: string\n stateDir: string\n}\n\nexport function resolveDataDirs({ homeDir, env, exists }: DataDirsInput): DataDirs {\n const legacyDir = path.join(homeDir, LEGACY_DIR_NAME)\n const isLegacy = LEGACY_MARKERS.some((marker) => exists(path.join(legacyDir, marker)))\n if (isLegacy) return { legacyDir, dataDir: legacyDir, configDir: legacyDir, stateDir: legacyDir }\n // An empty XDG_*_HOME counts as unset, as the spec says — else the data would land in the current folder\n const xdgDir = (variable: keyof DataDirsInput['env'], fallback: string[]): string =>\n path.join(env[variable] || path.join(homeDir, ...fallback), APP_DIR_NAME)\n return {\n legacyDir,\n dataDir: xdgDir('XDG_DATA_HOME', ['.local', 'share']),\n configDir: xdgDir('XDG_CONFIG_HOME', ['.config']),\n stateDir: xdgDir('XDG_STATE_HOME', ['.local', 'state']),\n }\n}\n","import path from 'path'\nimport os from 'os'\nimport fs from 'fs'\nimport { resolveDataDirs } from './data-dirs'\n\n// Decided once per process: legacy installs keep ~/.workscribe, fresh ones use XDG (see data-dirs.ts)\nconst {\n legacyDir: LEGACY_DIR,\n dataDir: xdgDataDir,\n configDir: xdgConfigDir,\n stateDir: xdgStateDir,\n} = resolveDataDirs({ homeDir: os.homedir(), env: process.env, exists: fs.existsSync })\n\n// All directories that must exist for Workscribe to function (deduplicated for legacy installs).\n// LEGACY_DIR (~/.workscribe) is always included so the alerts signal file has a predictable\n// location that shell hooks can check without knowing the XDG paths.\nexport const REQUIRED_DIRS: string[] = [...new Set([xdgDataDir, xdgConfigDir, xdgStateDir, LEGACY_DIR])]\n\nexport const WORKSCRIBE_DIR = xdgDataDir\nexport const DB_PATH = path.join(xdgDataDir, 'workscribe.db')\nexport const CONFIG_PATH = path.join(xdgConfigDir, 'config.json')\nexport const DEBUG_LOG_PATH = path.join(xdgStateDir, 'debug.log')\nexport const UPDATE_CHECK_PATH = path.join(xdgStateDir, 'update-check.json')\nexport const EXPORTS_DIR = path.join(xdgDataDir, 'exports')\n// Copies of the database taken before a data upgrade (src/db/jobs/backup.ts) — private, newest 3 kept\nexport const BACKUPS_DIR = path.join(xdgDataDir, 'backups')\nexport const PAUSE_FLAG_PATH = path.join(xdgStateDir, 'paused')\nexport const PROFILES_DIR = path.join(xdgDataDir, 'profiles')\n// Fixed at ~/.workscribe/alerts.pending so shell hooks can check it without knowing XDG paths.\n// Content is irrelevant — its presence signals pending alerts.\nexport const ALERTS_PENDING_PATH = path.join(LEGACY_DIR, 'alerts.pending')\n","import fs from 'fs'\n\nconst PRIVATE_DIRECTORY_MODE = 0o700\nconst GROUP_AND_OTHER_BITS = 0o077\n\n/**\n * Creates the folder (and any missing parents) readable only by you, and tightens an existing one that group or\n * others can open — so the database and its `-wal`/`-shm` files never sit in a shared folder.\n */\nexport function ensurePrivateDirectory(directory: string): void {\n fs.mkdirSync(directory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE })\n if ((fs.statSync(directory).mode & GROUP_AND_OTHER_BITS) !== 0) fs.chmodSync(directory, PRIVATE_DIRECTORY_MODE)\n}\n","export default {\n version: 1,\n description: 'initial schema',\n sql: `\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n captured_at TEXT NOT NULL,\n command TEXT NOT NULL,\n cwd TEXT NOT NULL,\n exit_code INTEGER,\n duration_ms INTEGER,\n repo_name TEXT,\n branch TEXT,\n category TEXT NOT NULL DEFAULT 'other',\n metadata TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_captured_at ON events(captured_at);\n CREATE INDEX IF NOT EXISTS idx_events_repo_name ON events(repo_name);\n\n CREATE TABLE IF NOT EXISTS sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n repo_name TEXT,\n event_count INTEGER NOT NULL DEFAULT 0,\n summary TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_sessions_date ON sessions(date);\n\n CREATE TABLE IF NOT EXISTS session_events (\n session_id INTEGER NOT NULL REFERENCES sessions(id),\n event_id INTEGER NOT NULL REFERENCES events(id),\n PRIMARY KEY (session_id, event_id)\n );\n `,\n}\n","const migration = {\n version: 2,\n description: 'Rename granular categories to 13 universal semantic categories',\n sql: `\n UPDATE events SET category = 'shipping' WHERE category IN ('code_commit','code_push','code_merge');\n UPDATE events SET category = 'versioning' WHERE category IN ('code_pull','branch_switch','code_stash','git_other');\n UPDATE events SET category = 'building' WHERE category = 'build';\n UPDATE events SET category = 'running' WHERE category IN ('server_run','script_run');\n UPDATE events SET category = 'testing' WHERE category = 'test_run';\n UPDATE events SET category = 'linting' WHERE category = 'lint_run';\n UPDATE events SET category = 'dependencies' WHERE category IN ('dependency_install','dependency_remove');\n UPDATE events SET category = 'infrastructure' WHERE category = 'infrastructure';\n UPDATE events SET category = 'files' WHERE category IN ('file_ops','environment');\n UPDATE events SET category = 'exploring' WHERE category IN ('code_search','code_read');\n `,\n}\n\nexport default migration\n","const migration = {\n version: 3,\n description: 'Add alerts table for AI session secret detection',\n sql: `\n CREATE TABLE IF NOT EXISTS alerts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at INTEGER NOT NULL,\n tool TEXT NOT NULL,\n session_id TEXT,\n context TEXT NOT NULL,\n cleared INTEGER NOT NULL DEFAULT 0\n );\n CREATE INDEX IF NOT EXISTS alerts_cleared ON alerts (cleared);\n `,\n}\n\nexport default migration\n","const migration = {\n version: 4,\n description: 'Add source and file_path columns to events for IDE integration',\n sql: `\n ALTER TABLE events ADD COLUMN source TEXT;\n ALTER TABLE events ADD COLUMN file_path TEXT;\n CREATE INDEX IF NOT EXISTS idx_events_file_path ON events (file_path, cwd);\n `,\n}\n\nexport default migration\n","// Must produce exactly what sessionKey() in src/summary/store.ts produces — pinned by a test.\nexport const SESSION_KEY_SQL = \"COALESCE(repo_name, '') || '|' || started_at\"\n\nconst migration = {\n version: 5,\n description: 'Store session summaries per engine, keyed by repo and start time so they survive session rebuilds',\n sql: `\n CREATE TABLE IF NOT EXISTS session_summaries (\n session_key TEXT NOT NULL,\n engine TEXT NOT NULL,\n text TEXT NOT NULL,\n created_at TEXT NOT NULL,\n PRIMARY KEY (session_key, engine)\n );\n INSERT OR IGNORE INTO session_summaries (session_key, engine, text, created_at)\n SELECT ${SESSION_KEY_SQL}, 'legacy', summary, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n FROM sessions\n WHERE summary IS NOT NULL;\n `,\n}\n\nexport default migration\n","[\n {\n \"tool\": \"claude\",\n \"displayName\": \"Claude Code\",\n \"pattern\": \"^claude\\\\b\",\n \"hook\": \"claude-code\",\n \"contextSource\": \"hook\",\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"aider\",\n \"displayName\": \"Aider\",\n \"pattern\": \"^aider\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"markdown\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"codex\",\n \"displayName\": \"Codex CLI\",\n \"pattern\": \"^codex\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"jsonl\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"gemini\",\n \"displayName\": \"Gemini CLI\",\n \"pattern\": \"^gemini\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"json\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"gh copilot\",\n \"displayName\": \"GitHub Copilot\",\n \"pattern\": \"^gh\\\\s+copilot\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"jsonl\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"amazon-q\",\n \"displayName\": \"Amazon Q\",\n \"pattern\": \"^q\\\\s+(chat|dev)\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\",\n \"historyFormat\": \"json\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"opencode\",\n \"displayName\": \"OpenCode\",\n \"pattern\": \"^opencode\\\\b\",\n \"hook\": null,\n \"contextSource\": \"sqlite\",\n \"historyFormat\": \"sqlite\",\n \"securityWarning\": null\n },\n {\n \"tool\": \"kilo\",\n \"displayName\": \"Kilo Code\",\n \"pattern\": \"^kilo\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"grok\",\n \"displayName\": \"Grok Build\",\n \"pattern\": \"^grok\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": \"Grok Build was found to silently upload entire local git repositories — including .env files and untracked secrets — to Google Cloud Storage without user disclosure (July 2026 incident). Workscribe does not read Grok Build session files. Consider whether this tool is appropriate for use in repositories containing sensitive credentials.\"\n },\n {\n \"tool\": \"continue\",\n \"displayName\": \"Continue\",\n \"pattern\": \"^continue\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"cody\",\n \"displayName\": \"Cody\",\n \"pattern\": \"^cody\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n },\n {\n \"tool\": \"cursor\",\n \"displayName\": \"Cursor\",\n \"pattern\": \"^cursor\\\\b\",\n \"hook\": null,\n \"contextSource\": null,\n \"historyFormat\": null,\n \"securityWarning\": null\n }\n]\n","import chalk from 'chalk'\nimport fs from 'fs'\nimport { DEBUG_LOG_PATH } from './paths'\n\nconst isDebug = process.env.WORKSCRIBE_DEBUG === '1'\n\nexport const log = {\n info: (msg: string) => console.log(chalk.cyan('→'), msg),\n success: (msg: string) => console.log(chalk.green('✓'), msg),\n warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),\n error: (msg: string) => console.error(chalk.red('✗'), msg),\n /** A warning on stderr — keeps stdout clean for JSON and plain text output, and pipes. */\n notice: (msg: string) => process.stderr.write(`${chalk.yellow('⚠')} ${msg}\\n`),\n plain: (msg: string) => console.log(msg),\n}\n\nexport function debugLog(context: string, error: unknown): void {\n if (!isDebug) return\n const entry = `[${new Date().toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}\\n`\n try {\n fs.appendFileSync(DEBUG_LOG_PATH, entry, { mode: 0o600 })\n } catch {\n // If we can't write to the debug log, silently ignore\n }\n}\n","import fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport BUILTIN_TOOLS from './ai-tools.json'\nimport { debugLog } from '../utils/logger'\n\nexport interface AIToolEntry {\n tool: string\n displayName: string\n pattern: string\n hook: string | null\n contextSource: 'hook' | 'history-file' | 'sqlite' | null\n historyFormat: 'jsonl' | 'json' | 'markdown' | 'sqlite' | null\n securityWarning: string | null\n}\n\nconst USER_REGISTRY_PATH = path.join(os.homedir(), '.workscribe', 'ai-tools.json')\n\nexport function loadAIToolRegistry(): AIToolEntry[] {\n let userEntries: AIToolEntry[] = []\n\n try {\n if (fs.existsSync(USER_REGISTRY_PATH)) {\n const raw = fs.readFileSync(USER_REGISTRY_PATH, 'utf-8')\n const parsed = JSON.parse(raw)\n if (Array.isArray(parsed)) {\n userEntries = parsed\n .filter((entry) => typeof entry.tool === 'string' && typeof entry.pattern === 'string')\n .map((entry) => ({\n tool: entry.tool as string,\n displayName: (entry.displayName as string | undefined) ?? (entry.tool as string),\n pattern: entry.pattern as string,\n hook: (entry.hook as string | null | undefined) ?? null,\n contextSource: (entry.contextSource as AIToolEntry['contextSource'] | undefined) ?? null,\n historyFormat: (entry.historyFormat as AIToolEntry['historyFormat'] | undefined) ?? null,\n securityWarning: (entry.securityWarning as string | null | undefined) ?? null,\n }))\n }\n }\n } catch {\n debugLog('registry', 'Failed to load user AI tool registry — using built-ins only')\n }\n\n if (userEntries.length === 0) return BUILTIN_TOOLS as AIToolEntry[]\n\n const userToolSlugs = new Set(userEntries.map((entry) => entry.tool))\n const filteredBuiltins = (BUILTIN_TOOLS as AIToolEntry[]).filter((entry) => !userToolSlugs.has(entry.tool))\n return [...filteredBuiltins, ...userEntries]\n}\n","// Reads an event row's stored metadata. A leaf module (no imports) so both the payload builder and the commit\n// helpers can use it without importing each other.\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Metadata is best-effort context for the summary — a corrupt or non-object row must not fail it. */\nexport function parseMetadata(raw: string | null): Record<string, unknown> {\n if (!raw) return {}\n try {\n const parsed: unknown = JSON.parse(raw)\n return isPlainObject(parsed) ? parsed : {}\n } catch {\n return {}\n }\n}\n","// Commits Workscribe captured, and how they merge with git's. A leaf module: it imports only types and the metadata\n// reader, so the payload builder and the JSON contract can both use it without an import cycle.\nimport { parseMetadata } from './event-metadata'\nimport type { EventRow } from '../db/types'\nimport type { GitCommit } from './git-commits'\nimport type { CommitJson } from './contract/types'\n\n/** A command that ran and failed. An unknown exit code (`null`, commands agents ran) is not a failure. */\nexport function isFailedExit(exitCode: number | null): boolean {\n return exitCode !== null && exitCode !== 0\n}\n\n/** A `git commit` that failed (rejected by a hook, nothing staged) made no commit. */\nexport function isFailedCommand(row: Pick<EventRow, 'exit_code'>): boolean {\n return isFailedExit(row.exit_code)\n}\n\n/** A commit Workscribe captured itself, and when — null when only the subject is known. */\nexport interface CapturedCommit {\n subject: string\n committedAt: string | null\n /** The branch the capture happened on — the commit's branch, for the story */\n branch: string | null\n}\n\n/** What finding an event's commits reads — a stored row, or an event about to be stored. */\nexport type CommitSource = Pick<EventRow, 'category' | 'metadata' | 'exit_code' | 'captured_at' | 'branch'>\n\n/**\n * Commits Workscribe captured itself: `git commit -m` messages, at the time the commit finished, and AI-session\n * commits (subjects only — the session records which commits reached HEAD, not when). A `git commit` that failed\n * (rejected by a hook, nothing staged) made no commit, so it is skipped. A repeated subject keeps its first place,\n * and the first time any capture of it recorded.\n */\nexport function collectCapturedCommits(rows: CommitSource[]): CapturedCommit[] {\n const commits = rows.flatMap((row): CapturedCommit[] => {\n const metadata = parseMetadata(row.metadata)\n if (row.category === 'shipping' && typeof metadata.message === 'string' && !isFailedCommand(row)) {\n return [{ subject: metadata.message, committedAt: row.captured_at, branch: row.branch }]\n }\n if (row.category === 'ai_session' && Array.isArray(metadata.commits)) {\n return metadata.commits\n .filter((subject): subject is string => typeof subject === 'string')\n .map((subject) => ({ subject, committedAt: null, branch: row.branch }))\n }\n return []\n })\n const bySubject = new Map<string, CapturedCommit>()\n for (const commit of commits) {\n if (commit.subject === '') continue\n const earlier = bySubject.get(commit.subject)\n // An AI session recorded before its `git commit -m` mustn't hide the time (and branch) that capture has\n const isBetterTimed = earlier !== undefined && earlier.committedAt === null && commit.committedAt !== null\n bySubject.set(\n commit.subject,\n isBetterTimed ? { ...earlier, committedAt: commit.committedAt, branch: commit.branch } : (earlier ?? commit)\n )\n }\n return [...bySubject.values()]\n}\n\n/**\n * Whether a captured subject is this git commit. Besides an exact match, it tolerates the two ways\n * events captured before the `-m` parsing fix were cut wrong — and only those, so a short `fix`\n * never matches `fix: something else`:\n * - stopped at an inner quote: `fix: don` for `fix: don't redirect`\n * - ran on into flags: `wip --no-verify` for `wip`\n */\nexport function isSameCommit(captured: string, gitSubject: string): boolean {\n if (captured === gitSubject) return true\n if (gitSubject.startsWith(captured)) return /^['\"]/.test(gitSubject.slice(captured.length))\n if (captured.startsWith(gitSubject)) return /^\\s+-/.test(captured.slice(gitSubject.length))\n return false\n}\n\n/**\n * Git commits first, oldest first and each listed even if subjects repeat (they are distinct commits),\n * then captured subjects git didn't return — amended, rebased or on another branch — with no hash.\n * Captured subjects git attributes only to other people are dropped: an AI session records every\n * commit that reached HEAD, including ones pulled or rebased in.\n */\nexport function mergeCommits(\n captured: CapturedCommit[],\n fromGit: GitCommit[],\n othersSubjects: string[] = []\n): CommitJson[] {\n const others = new Set(othersSubjects)\n const gitCommits: CommitJson[] = fromGit.map(({ hash, subject, committedAt }) => ({\n hash,\n subject,\n committedAt,\n source: 'git',\n }))\n const capturedOnly: CommitJson[] = captured\n .filter(({ subject }) => !others.has(subject))\n .filter(({ subject }) => !fromGit.some((commit) => isSameCommit(subject, commit.subject)))\n .map(({ subject, committedAt }) => ({ hash: null, subject, committedAt, source: 'captured' }))\n return [...gitCommits, ...capturedOnly]\n}\n","// Which tickets an event names (pure): keys matching the ticket pattern in its linked branch's name, and in the\n// subjects of the commits it made. Used by capture and by the `ticket-refs` job, so both derive the same links.\nimport { collectCapturedCommits, type CommitSource } from '../summary/commits'\nimport type { TicketKeys } from '../db/tickets'\n\nexport const DEFAULT_TICKET_PATTERN = '[A-Z][A-Z0-9]+-\\\\d+'\n\n/** Only the start of a long text is searched: ticket keys sit at the start of branch names and subjects. */\nconst MAX_SEARCHED_CHARACTERS = 200\n\nconst REPEATS = new Set(['*', '+', '{'])\nconst VARIES = new Set(['*', '+', '?', '{', '|'])\n\n/** Where a character class `[...]` that opens at `start` ends (its `]`), escapes inside skipped. */\nfunction endOfClass(source: string, start: number): number {\n let index = start + 1\n if (source[index] === '^') index++\n if (source[index] === ']') index++\n while (index < source.length && source[index] !== ']') index += source[index] === '\\\\' ? 2 : 1\n return index\n}\n\n/**\n * Whether a group that repeats (`*`, `+`, `{…}`) holds anything that varies — a quantifier or an `|`, at any depth:\n * `(A+)+`, `((A+))+`, `(A|AA)+`. That is the shape that can backtrack exponentially on a near miss. Deliberately\n * conservative: it also refuses some safe patterns, such as `(?:-[A-Z]+)*`.\n */\nfunction hasNestedRepetition(source: string): boolean {\n const groupsVary: boolean[] = []\n for (let index = 0; index < source.length; index++) {\n const character = source[index]\n if (character === '\\\\') {\n index++\n } else if (character === '[') {\n index = endOfClass(source, index)\n } else if (character === '(') {\n groupsVary.push(false)\n if (source[index + 1] === '?') index++\n } else if (character === ')') {\n const varies = groupsVary.pop() ?? false\n if (varies && REPEATS.has(source[index + 1])) return true\n } else if (VARIES.has(character)) {\n groupsVary.fill(true)\n }\n }\n return false\n}\n\n/** Why a ticket pattern can't be used, or null when it can. */\nexport function ticketPatternProblem(source: string): string | null {\n if (hasNestedRepetition(source)) {\n return 'it repeats a group that itself repeats or has alternatives, which can hang on a near miss'\n }\n let pattern: RegExp\n try {\n pattern = new RegExp(source)\n } catch (error) {\n return `it isn't a valid regular expression (${(error as Error).message})`\n }\n return pattern.test('') ? 'it matches empty text, so it would find a ticket everywhere' : null\n}\n\n/** The pattern to find ticket keys with: the configured one, or the default when it's missing or unusable. */\nexport function ticketPatternOf(source: unknown): RegExp {\n const usable = typeof source === 'string' && ticketPatternProblem(source) === null ? source : DEFAULT_TICKET_PATTERN\n return new RegExp(usable, 'g')\n}\n\n/**\n * The keys in a text's searched start. A match that runs into the cut may be a longer key cut short (`WOR-1234` read\n * as `WOR-1`), so it's dropped; so is an empty match, which a pattern like `[A-Z]*(?=-\\d)` can make inside a text.\n */\nfunction keysIn(text: string, pattern: RegExp): string[] {\n const searched = text.slice(0, MAX_SEARCHED_CHARACTERS)\n const isCut = text.length > searched.length\n return [...searched.matchAll(pattern)]\n .filter((match) => match[0] !== '' && !(isCut && match.index + match[0].length === searched.length))\n .map((match) => match[0])\n}\n\nfunction distinct(keys: string[]): string[] {\n return [...new Set(keys)]\n}\n\n/**\n * The keys an event names. Branch keys come from the branch the event is linked to — never its raw `branch` text,\n * which a save in another repo keeps without a link. A failed commit names nothing.\n */\nexport function ticketKeysOf(\n { linkedBranch, event }: { linkedBranch: string | null; event: CommitSource },\n pattern: RegExp\n): TicketKeys {\n const subjects = collectCapturedCommits([event]).map((commit) => commit.subject)\n return {\n branch: linkedBranch === null ? [] : distinct(keysIn(linkedBranch, pattern)),\n commits: distinct(subjects.flatMap((subject) => keysIn(subject, pattern))),\n }\n}\n","import crypto from 'crypto'\nimport fs from 'fs'\nimport path from 'path'\nimport { CONFIG_PATH } from '../utils/paths'\nimport { debugLog } from '../utils/logger'\nimport { DEFAULT_TICKET_PATTERN } from '../capture/tickets'\n\n/**\n * AI provider settings from Workscribe 1.x. 2.0 removed AI providers, so nothing writes these any more — they're\n * read only to tell 1.x users (see src/summary/legacy-ai.ts) and to keep a stored API key out of bug reports.\n */\nexport interface LegacyAiConfig {\n provider?: string\n apiKey?: string\n model?: string\n baseUrl?: string\n}\n\nexport interface WorkscribeConfig {\n ai?: LegacyAiConfig\n session: {\n idleTimeout: number // minutes\n }\n git: {\n // When true, repo_name is \"owner/repo\" instead of bare \"repo\". Off by\n // default — bare names are nicer for the common case of one repo per\n // name. Turn on if you work across repos whose bare names collide\n // (e.g. an org that names plugin repos after the tool they target:\n // workscribe/vscode, workscribe/nvim, ...).\n qualifyRepoName: boolean\n }\n capture: {\n ignore: string[]\n aiTools: boolean\n includeAiderPrompt: boolean\n profiles: string[]\n }\n redact: {\n extra: string[]\n }\n summary: {\n format: 'markdown' | 'json' | 'text'\n // Categories re-included in summaries (overrides built-in exclusions).\n // Excluded by default: exploring, other, linting.\n // Example: [\"linting\"] to include lint pass/fail in summaries.\n includeCategories?: string[]\n // 1.x setting (\"template\" | \"ollama\" | \"provider\"). 2.0 always uses the template; read only to detect\n // old configs (src/summary/legacy-ai.ts).\n engine?: string\n }\n projects?: Record<string, string> // absolute path → display name override\n integrations?: {\n slackWebhook?: string\n webhook?: string\n }\n telemetry: {\n enabled: boolean\n installId: string\n }\n tickets: {\n // What a ticket key looks like in branch names and commit messages (a regular expression). Change it with\n // `workscribe config set tickets.pattern`, which also re-links the history.\n pattern: string\n }\n}\n\nconst DEFAULTS: WorkscribeConfig = {\n session: {\n idleTimeout: 30,\n },\n git: {\n qualifyRepoName: false,\n },\n capture: {\n ignore: ['cd', 'ls', 'll', 'la', 'ls -la', 'ls -l', 'ls -a', 'pwd', 'clear', 'exit', 'history', 'man'],\n aiTools: true,\n includeAiderPrompt: true,\n profiles: [],\n },\n redact: {\n extra: [],\n },\n summary: {\n format: 'markdown',\n },\n telemetry: {\n enabled: true,\n installId: '',\n },\n tickets: {\n pattern: DEFAULT_TICKET_PATTERN,\n },\n}\n\nexport function readConfig(): WorkscribeConfig {\n if (!fs.existsSync(CONFIG_PATH)) return deepMerge(DEFAULTS, {})\n try {\n const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')\n const parsed = JSON.parse(raw)\n return deepMerge(DEFAULTS, parsed)\n } catch {\n return deepMerge(DEFAULTS, {})\n }\n}\n\nexport function writeConfig(config: WorkscribeConfig): void {\n // Previously safe to assume — writeConfig was only ever called after `init`\n // created the data directories, or by `config set` where init had already\n // run. CLI-05's fallback can now call this as the very first filesystem\n // touch for a user who skipped init entirely, so it can't assume the dir exists.\n fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true, mode: 0o700 })\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 })\n // Enforce permissions on existing files (mode only applies on creation above)\n try {\n fs.chmodSync(CONFIG_PATH, 0o600)\n } catch {\n /* ignore */\n }\n}\n\n// Generates and persists telemetry.installId on first use. Covers both a\n// fresh install and an existing user upgrading — deepMerge already backfills\n// the telemetry key itself, this just fills in the empty installId.\nexport function ensureInstallId(config: WorkscribeConfig): string {\n if (config.telemetry.installId) return config.telemetry.installId\n\n const installId = crypto.randomUUID()\n writeConfig({ ...config, telemetry: { ...config.telemetry, installId } })\n return installId\n}\n\n// Fallback for users who skip `init` entirely — which is exactly what an\n// existing user upgrading will typically do, since they never re-run it.\n// Presence of installId is the only state tracked; no separate \"notice\n// shown\" flag is needed since this is a no-op once installId is set.\nexport function ensureTelemetryFallback(): void {\n // Unlike init's explicit telemetry step, this runs unannounced ahead of\n // every command — it must never throw, however it fails, or a disk issue\n // (read-only $HOME, full disk, a locked-down CI sandbox) would crash the\n // user's actual command before it even starts.\n try {\n const config = readConfig()\n if (config.telemetry.installId) return\n\n config.telemetry.enabled = true\n config.telemetry.installId = ensureInstallId(config)\n\n process.stderr.write(\n '\\n Workscribe collects anonymous install stats (install ID, CLI version, platform, Node version).\\n Opt out: workscribe config set telemetry.enabled false\\n\\n'\n )\n } catch (error) {\n debugLog('ensureTelemetryFallback', error)\n }\n}\n\nexport function getConfigValue(config: WorkscribeConfig, key: string): unknown {\n const parts = key.split('.')\n let cur: unknown = config\n for (const part of parts) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\nexport function setConfigValue(config: WorkscribeConfig, key: string, value: string): WorkscribeConfig {\n const parts = key.split('.')\n const result = JSON.parse(JSON.stringify(config)) as WorkscribeConfig\n let cur = result as unknown as Record<string, unknown>\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]\n if (cur[part] == null || typeof cur[part] !== 'object') {\n cur[part] = {}\n }\n cur = cur[part] as Record<string, unknown>\n }\n const lastKey = parts[parts.length - 1]\n // Coerce value type\n if (value === 'true') cur[lastKey] = true\n else if (value === 'false') cur[lastKey] = false\n else if (!isNaN(Number(value))) cur[lastKey] = Number(value)\n else cur[lastKey] = value\n return result\n}\n\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n // structuredClone, not a shallow spread — any key overrides doesn't touch\n // (e.g. a config file written before a newer top-level key existed) must\n // still be an independent copy, not a shared reference back into the\n // caller's `defaults` object. A shallow spread here previously let any\n // caller that mutates a nested field on the returned config (e.g.\n // ensureTelemetryFallback) silently corrupt the module-level DEFAULTS\n // singleton for the rest of the process.\n const result = structuredClone(defaults)\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const val = overrides[key]\n if (val !== null && typeof val === 'object' && !Array.isArray(val) && typeof defaults[key] === 'object') {\n result[key] = deepMerge(defaults[key] as object, val as object) as T[keyof T]\n } else if (val !== undefined) {\n result[key] = val as T[keyof T]\n }\n }\n return result\n}\n","export interface ProfileRule {\n pattern: string\n category: string\n metadata?: Record<string, string>\n}\n\nexport interface Profile {\n name: string\n displayName: string\n version: string\n detect?: { files?: string[] }\n rules: ProfileRule[]\n aiContext?: string\n}\n\nexport interface ManifestEntry {\n name: string\n displayName: string\n tags: string[]\n description: string\n keywords: string[]\n detect?: { files?: string[] }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0\n}\n\nfunction isValidRule(rule: unknown): boolean {\n if (!rule || typeof rule !== 'object') return false\n const { pattern, category } = rule as Record<string, unknown>\n return isNonEmptyString(pattern) && isNonEmptyString(category)\n}\n\nexport function isValidProfile(candidate: unknown): candidate is Profile {\n if (!candidate || typeof candidate !== 'object') return false\n const { name, displayName, version, rules } = candidate as Record<string, unknown>\n if (!isNonEmptyString(name) || !isNonEmptyString(displayName) || !isNonEmptyString(version)) return false\n if (!Array.isArray(rules)) return false\n return rules.every(isValidRule)\n}\n\nexport function isValidManifest(m: unknown): m is ManifestEntry[] {\n return (\n Array.isArray(m) &&\n m.every((e) => e && typeof e === 'object' && typeof (e as Record<string, unknown>).name === 'string')\n )\n}\n","const DEFAULT_TIMEOUT_MS = 30_000\n\nconst NETWORK_ERROR_CODES = new Set([\n 'ENOTFOUND',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'ETIMEDOUT',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n])\n\nfunction isNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const cause = (err as Error & { cause?: unknown }).cause\n const code =\n (err as NodeJS.ErrnoException).code ?? (cause instanceof Error ? (cause as NodeJS.ErrnoException).code : undefined)\n return NETWORK_ERROR_CODES.has(code ?? '')\n}\n\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<Response> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...init, signal: controller.signal })\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Request timed out after ${timeoutMs / 1000}s`)\n }\n if (isNetworkError(err)) {\n throw new Error(\"No internet connection — try again when you're online\")\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { PROFILES_DIR } from '../utils/paths'\nimport { isValidProfile, isValidManifest } from './schema'\nimport type { ManifestEntry, Profile } from './schema'\nimport { debugLog } from '../utils/logger'\nimport { fetchWithTimeout } from '../utils/fetch'\n\nconst REGISTRY_BASE = 'https://raw.githubusercontent.com/workscribe/profiles/main'\n\nfunction manifestCachePath(): string {\n return path.join(PROFILES_DIR, 'manifest.json')\n}\n\nexport async function fetchManifest(): Promise<ManifestEntry[]> {\n const response = await fetchWithTimeout(`${REGISTRY_BASE}/manifest.json`, {})\n if (!response.ok) throw new Error(`Registry unreachable (${response.status})`)\n const data: unknown = await response.json()\n if (!isValidManifest(data)) throw new Error('Manifest format unrecognised')\n return data\n}\n\nexport async function fetchProfile(name: string): Promise<Profile> {\n const response = await fetchWithTimeout(`${REGISTRY_BASE}/${name}.json`, {})\n if (response.status === 404) throw new Error(`Profile \"${name}\" not found in registry`)\n if (!response.ok) throw new Error(`Registry unreachable (${response.status})`)\n const data: unknown = await response.json()\n if (!isValidProfile(data)) throw new Error(`Profile \"${name}\" failed schema validation`)\n return data\n}\n\nexport async function installProfile(name: string): Promise<void> {\n ensureProfilesDir()\n const profile = await fetchProfile(name)\n const dest = path.join(PROFILES_DIR, `${name}.json`)\n fs.writeFileSync(dest, JSON.stringify(profile, null, 2), { encoding: 'utf-8', mode: 0o600 })\n debugLog('profiles', `Installed profile \"${name}\" to ${dest}`)\n}\n\nexport async function cacheManifest(): Promise<ManifestEntry[]> {\n ensureProfilesDir()\n const manifest = await fetchManifest()\n fs.writeFileSync(manifestCachePath(), JSON.stringify(manifest, null, 2), { encoding: 'utf-8', mode: 0o600 })\n return manifest\n}\n\nexport function readCachedManifest(): ManifestEntry[] | null {\n if (!fs.existsSync(manifestCachePath())) return null\n try {\n const data: unknown = JSON.parse(fs.readFileSync(manifestCachePath(), 'utf-8'))\n return isValidManifest(data) ? data : null\n } catch {\n return null\n }\n}\n\nfunction ensureProfilesDir(): void {\n if (!fs.existsSync(PROFILES_DIR)) {\n fs.mkdirSync(PROFILES_DIR, { recursive: true, mode: 0o700 })\n }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { PROFILES_DIR } from '../utils/paths'\nimport { readConfig } from '../config'\nimport { isValidProfile } from './schema'\nimport type { Profile, ManifestEntry, ProfileRule } from './schema'\nimport { readCachedManifest } from './fetcher'\nimport { debugLog } from '../utils/logger'\n\n// Matches the Rule interface in patterns.ts — structural compatibility without circular import\ninterface CompiledRule {\n match(cmd: string): { category: string; metadata: Record<string, unknown> } | null\n}\n\nfunction compileRule(profileName: string, rule: ProfileRule): CompiledRule | null {\n let regex: RegExp\n try {\n regex = new RegExp(rule.pattern)\n } catch {\n debugLog('profiles', `Profile \"${profileName}\": invalid pattern \"${rule.pattern}\" — skipping rule`)\n return null\n }\n\n return {\n match(cmd: string) {\n const match = cmd.match(regex)\n if (!match) return null\n const metadata: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(rule.metadata ?? {})) {\n metadata[key] = value.replace(/\\$(\\d+)/g, (_, index: string) => match[Number(index)] ?? '')\n }\n return { category: rule.category, metadata }\n },\n }\n}\n\nfunction loadProfileFromDisk(name: string): Profile | null {\n const filePath = path.join(PROFILES_DIR, `${name}.json`)\n if (!fs.existsSync(filePath)) {\n debugLog('profiles', `Profile \"${name}\" not found in cache — run: workscribe profile add ${name}`)\n return null\n }\n let candidate: unknown\n try {\n candidate = JSON.parse(fs.readFileSync(filePath, 'utf-8'))\n } catch {\n debugLog('profiles', `Profile \"${name}\" contains invalid JSON — skipping`)\n return null\n }\n if (!isValidProfile(candidate)) {\n debugLog('profiles', `Profile \"${name}\" failed schema validation — skipping`)\n return null\n }\n return candidate\n}\n\nfunction loadUserProfiles(): Profile[] {\n if (!fs.existsSync(PROFILES_DIR)) return []\n const activeNames = new Set(readConfig().capture.profiles)\n return fs\n .readdirSync(PROFILES_DIR)\n .filter((file) => file.endsWith('.json') && file !== 'manifest.json')\n .filter((file) => {\n const name = file.replace(/\\.json$/, '')\n return !activeNames.has(name) // avoid double-loading names already in config\n })\n .flatMap((file) => {\n const candidate = loadProfileFromDisk(file.replace(/\\.json$/, ''))\n return candidate ? [candidate] : []\n })\n .filter((profile) => fs.existsSync(path.join(PROFILES_DIR, `${profile.name}.json`)))\n}\n\nexport function getActiveProfileRules(): CompiledRule[] {\n const activeNames = readConfig().capture.profiles\n if (activeNames.length === 0) return []\n\n return activeNames.flatMap((name) => {\n const profile = loadProfileFromDisk(name)\n if (!profile) return []\n return profile.rules.flatMap((rule) => {\n const compiled = compileRule(profile.name, rule)\n return compiled ? [compiled] : []\n })\n })\n}\n\nexport function getCachedProfiles(): Profile[] {\n if (!fs.existsSync(PROFILES_DIR)) return []\n return fs\n .readdirSync(PROFILES_DIR)\n .filter((file) => file.endsWith('.json') && file !== 'manifest.json')\n .flatMap((file) => {\n const profile = loadProfileFromDisk(file.replace(/\\.json$/, ''))\n return profile ? [profile] : []\n })\n}\n\nexport function detectProfilesForDirectory(dir: string): ManifestEntry[] {\n const manifest = readCachedManifest()\n if (!manifest) return []\n\n const activeNames = new Set(readConfig().capture.profiles)\n\n return manifest.filter((entry) => {\n if (activeNames.has(entry.name)) return false\n const indicatorFiles = entry.detect?.files ?? []\n return indicatorFiles.length > 0 && indicatorFiles.every((file) => fs.existsSync(path.join(dir, file)))\n })\n}\n\nexport { loadUserProfiles }\n","// Just enough shell syntax to find command boundaries in captured commands — quotes, escapes,\n// separators and substitutions — without a full parser. Pure and total: never throws.\n\ntype QuoteState = '\"' | \"'\" | null\n\ninterface QuotedCharacter {\n index: number\n quote: QuoteState\n}\n\n// `$(…)` and backticks run a command inside another, also inside double quotes. `<(…)` and `>(…)`\n// run one as a file, but only unquoted — inside double quotes they are plain text.\nconst COMMAND_SUBSTITUTION_OPENER = '$('\nconst PROCESS_SUBSTITUTION_OPENERS = ['<(', '>(']\n\n/**\n * Every character that is content rather than syntax, with the quote it sits in. Quote marks\n * themselves and backslash-escaped characters are skipped — they can't separate or substitute.\n */\nfunction* quotedCharacters(text: string): Generator<QuotedCharacter> {\n let quote: QuoteState = null\n for (let index = 0; index < text.length; index++) {\n const character = text[index]\n if (character === '\\\\' && quote !== \"'\") {\n index++\n continue\n }\n if (character === quote) {\n quote = null\n continue\n }\n if (quote === null && (character === '\"' || character === \"'\")) {\n quote = character\n continue\n }\n yield { index, quote }\n }\n}\n\nexport function isOutsideQuotes(text: string, position: number): boolean {\n for (const { index, quote } of quotedCharacters(text)) {\n if (index >= position) return quote === null\n }\n return true\n}\n\n/** A lone `&` runs the step before it in the background; `2>&1`, `&>` and `>&` are redirections, not separators. */\nfunction isRedirection(text: string, index: number): boolean {\n return text[index] === '&' && (text[index - 1] === '>' || text[index - 1] === '<' || text[index + 1] === '>')\n}\n\n/** Splits on any separator that sits outside single or double quotes. */\nexport function splitOutsideQuotes(text: string, separators: string[]): string[] {\n const parts: string[] = []\n let partStart = 0\n let skipUntil = 0\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote !== null || index < skipUntil || isRedirection(text, index)) continue\n const separator = separators.find((candidate) => text.startsWith(candidate, index))\n if (!separator) continue\n parts.push(text.slice(partStart, index))\n partStart = index + separator.length\n skipUntil = partStart\n }\n parts.push(text.slice(partStart))\n return parts.map((part) => part.trim()).filter((part) => part !== '')\n}\n\n/** Command or process substitution outside single quotes: the shell runs another command inside this one. */\nexport function containsCommandSubstitution(text: string): boolean {\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote === \"'\") continue\n if (text[index] === '`' || text.startsWith(COMMAND_SUBSTITUTION_OPENER, index)) return true\n if (quote === null && PROCESS_SUBSTITUTION_OPENERS.some((opener) => text.startsWith(opener, index))) return true\n }\n return false\n}\n\n// A `#` starts a comment only at the start of a word: `$#`, `${#x}` and `a#b` are not comments.\n// (bash and agents' shells treat it this way; interactive zsh only does with `interactivecomments` set.)\nconst WORD_BOUNDARY = /[\\s;&|()]/\n\n/** Where the first `# …` comment starts (outside quotes, at the start of a word), or -1. */\nexport function commentStart(text: string): number {\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote !== null || text[index] !== '#') continue\n if (index === 0 || WORD_BOUNDARY.test(text[index - 1])) return index\n }\n return -1\n}\n\n/**\n * Removes `# …` comments up to the end of each line. Quote tracking restarts after each comment, so an\n * apostrophe inside one (`# let's check`) can't leave a quote \"open\" for the rest of the command.\n */\nexport function stripComments(text: string): string {\n let result = ''\n let rest = text\n for (let start = commentStart(rest); start !== -1; start = commentStart(rest)) {\n result += rest.slice(0, start)\n const lineEnd = rest.indexOf('\\n', start)\n if (lineEnd === -1) return result\n rest = rest.slice(lineEnd)\n }\n return result + rest\n}\n\n/**\n * The text with everything inside quotes (and the quote marks and escapes themselves) replaced by `_`, keeping\n * every position — so a pattern can be matched against the unquoted parts only and applied to the original.\n */\nexport function maskQuotedText(text: string): string {\n const masked = Array.from({ length: text.length }, () => '_')\n for (const { index, quote } of quotedCharacters(text)) {\n if (quote === null) masked[index] = text[index]\n }\n return masked.join('')\n}\n\nexport function startsWithWord(step: string, word: string): boolean {\n return step === word || step.startsWith(`${word} `) || step.startsWith(`${word}\\t`)\n}\n\n/** A shell word with its quotes and escapes removed, and where it starts in the text. */\nexport interface ShellWord {\n word: string\n start: number\n}\n\n/**\n * The text's words as the shell would pass them to a command: split on whitespace outside quotes, with quote marks\n * and escaping backslashes removed (`\"a b\"` → `a b`, `\\\"` → `\"`). `$(…)` and other expansions are kept as written.\n */\nexport function shellWords(text: string): ShellWord[] {\n const words: ShellWord[] = []\n let quote: QuoteState = null\n let current: ShellWord | null = null\n const append = (character: string, index: number) => {\n current ??= { word: '', start: index }\n current.word += character\n }\n for (let index = 0; index < text.length; index++) {\n const character = text[index]\n if (character === '\\\\' && quote !== \"'\") {\n current ??= { word: '', start: index }\n index++\n if (index < text.length) current.word += text[index]\n continue\n }\n if (quote !== null) {\n if (character === quote) quote = null\n else append(character, index)\n continue\n }\n if (character === '\"' || character === \"'\") {\n current ??= { word: '', start: index }\n quote = character\n continue\n }\n if (/\\s/.test(character)) {\n if (current) words.push(current)\n current = null\n continue\n }\n append(character, index)\n }\n if (current) words.push(current)\n return words\n}\n","// Breaks a captured shell command into the simple commands it runs, so each one can be classified or\n// filtered on its own — agents run things like `cd app && pnpm nx test shell 2>&1 | tail -40; npx tsc`.\n// Works on the raw command (heredocs and newlines are lost once whitespace is collapsed). Pure, never throws.\nimport {\n commentStart,\n containsCommandSubstitution,\n isOutsideQuotes,\n maskQuotedText,\n splitOutsideQuotes,\n startsWithWord,\n stripComments,\n} from './shell-syntax'\n\nconst STEP_SEPARATORS = ['&&', '||', ';', '\\n', '&']\nconst PIPE_SEPARATORS = ['|']\n/** Commands that only produce text. */\nexport const TEXT_SOURCES = ['echo', 'printf']\n// Steps that do no work of their own when they run nothing inside them\nconst GLUE_COMMANDS = ['cd', 'true', ':', ...TEXT_SOURCES]\n// `<<TAG`, `<<-TAG`, `<<'TAG'`, `<<\"TAG\"` — but not a `<<<` here-string\nconst HEREDOC_OPENER = /(?<!<)<<(?!<)-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1/g\n// Words that wrap the command after them: `do npm test`, `if npm test`, `! grep x`, `time make`\nconst KEYWORD_PREFIX = /^(?:do|then|else|elif|if|while|until|time|!)\\s+/\n// Loop and case headers — shell structure, unless they run a command (`for f in $(npm test)`)\nconst STRUCTURE_HEADER = /^(?:for|select|case)\\s/\n// Closing keywords and bare keywords left over once a step is split\nconst STRUCTURE_KEYWORD = /^(?:done|fi|esac|\\}|\\)|do|then|else)$/\n// `NAME=value` before a command (or on its own); the value may be quoted or a `$(…)` substitution\nconst LEADING_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=(?:\"[^\"]*\"|'[^']*'|\\$\\([^)]*\\)|\\S*)(?:\\s+|$)/\n// `2>&1`, `> file`, `2>/dev/null`, `&> log`, `>> file`, `< input`\nconst REDIRECTION = /(?:^|\\s)(?:\\d*>>?|&>>?|<)\\s*(?:&\\d+|[^\\s;|&]+)/g\n// Package-runner forms that mean \"run this tool\", rewritten to `npx …` so the npx rules recognise them\nconst TOOL_RUNNER = /^(?:(?:pnpm|yarn|npm)\\s+exec|pnpm\\s+dlx|bunx)\\s+/\nconst FIRST_SUBSTITUTION = /\\$\\(([^()]*)\\)/\n// `$(…)` and backtick substitutions, for the commands inside them\nconst SUBSTITUTIONS = /\\$\\(([^()]*)\\)|`([^`]*)`/g\n\n/** The commands a line's `$(…)`/backtick substitutions run — what the shell executes from heredoc text. */\nfunction substitutionCommands(line: string): string[] {\n return [...line.matchAll(SUBSTITUTIONS)]\n .map((match) => (match[1] ?? match[2] ?? '').trim())\n .filter((command) => command !== '')\n}\n\n/**\n * Removes heredoc bodies, keeping the line that opens each one, so their text can't look like commands.\n * The shell still runs `$(…)` inside an unquoted heredoc (`<<EOF`, not `<<'EOF'`), so the commands inside\n * those substitutions stay — each on its own line, without the surrounding text (whose apostrophes and\n * other characters would otherwise be read as shell syntax).\n */\nexport function stripHeredocBodies(command: string): string {\n const kept: string[] = []\n const pending: { terminator: string; runsSubstitutions: boolean }[] = []\n for (const line of command.split('\\n')) {\n if (pending.length > 0) {\n if (line.trim() === pending[0].terminator) pending.shift()\n else if (pending[0].runsSubstitutions && containsCommandSubstitution(line))\n kept.push(...substitutionCommands(line))\n continue\n }\n kept.push(line)\n const comment = commentStart(line)\n for (const match of line.matchAll(HEREDOC_OPENER)) {\n // `\"a<<b\"` is text inside quotes, `\\<<` an escaped `<` and `# see <<EOF` a comment — none opens a heredoc\n const isEscaped = line[match.index - 1] === '\\\\'\n const isInComment = comment !== -1 && match.index > comment\n if (isEscaped || isInComment || !isOutsideQuotes(line, match.index)) continue\n pending.push({ terminator: match[2], runsSubstitutions: match[1] === '' })\n }\n }\n return kept.join('\\n')\n}\n\n/** A heredoc in a command: its terminator, its body, and the line that opened it. */\nexport interface HeredocBody {\n terminator: string\n body: string\n openerLine: string\n}\n\n/**\n * Every heredoc in a command, in order — for text a command reads from its input (`git commit -F - <<EOF`'s\n * message). The same openers `stripHeredocBodies` recognises.\n */\nexport function heredocBodies(command: string): HeredocBody[] {\n const heredocs: HeredocBody[] = []\n const pending: { terminator: string; lines: string[]; openerLine: string }[] = []\n for (const line of command.split('\\n')) {\n if (pending.length > 0) {\n const [current] = pending\n if (line.trim() === current.terminator) {\n heredocs.push({\n terminator: current.terminator,\n body: current.lines.join('\\n'),\n openerLine: current.openerLine,\n })\n pending.shift()\n } else {\n current.lines.push(line)\n }\n continue\n }\n const comment = commentStart(line)\n for (const match of line.matchAll(HEREDOC_OPENER)) {\n const isEscaped = line[match.index - 1] === '\\\\'\n const isInComment = comment !== -1 && match.index > comment\n if (isEscaped || isInComment || !isOutsideQuotes(line, match.index)) continue\n pending.push({ terminator: match[2], lines: [], openerLine: line })\n }\n }\n return heredocs\n}\n\n/** The steps a shell runs: split on newlines, `&&`, `||`, `;` and a background `&`, outside quotes. */\nexport function splitSteps(command: string): string[] {\n return splitOutsideQuotes(command, STEP_SEPARATORS)\n}\n\nfunction countOf(text: string, character: string): number {\n return text.split(character).length - 1\n}\n\n/** `(cd app && npm test)` splits into `(cd app` and `npm test)` — drop the unbalanced group brackets. */\nfunction withoutGroupBrackets(step: string): string {\n let unwrapped = step\n while (unwrapped.startsWith('(') && countOf(unwrapped, '(') > countOf(unwrapped, ')')) {\n unwrapped = unwrapped.slice(1).trim()\n }\n while (unwrapped.endsWith(')') && countOf(unwrapped, ')') > countOf(unwrapped, '(')) {\n unwrapped = unwrapped.slice(0, -1).trim()\n }\n if (unwrapped.startsWith('{ ')) unwrapped = unwrapped.slice(2).trim()\n return unwrapped\n}\n\nfunction withoutKeywordPrefixes(step: string): string {\n let unwrapped = step\n while (KEYWORD_PREFIX.test(unwrapped)) unwrapped = unwrapped.replace(KEYWORD_PREFIX, '')\n return unwrapped\n}\n\nfunction withoutLeadingAssignments(step: string): string {\n let unwrapped = step\n while (LEADING_ASSIGNMENT.test(unwrapped)) unwrapped = unwrapped.replace(LEADING_ASSIGNMENT, '')\n return unwrapped\n}\n\n/** `d=node_modules/x` does nothing on its own; `f=$(npm test)` still runs `npm test`, so it stays. */\nfunction isPlainAssignment(step: string): boolean {\n return withoutLeadingAssignments(step) === '' && !containsCommandSubstitution(step)\n}\n\n/** Loop headers and closing keywords do no work — but a header that runs a command (`for f in $(ls)`) does. */\nfunction isShellStructure(step: string): boolean {\n if (STRUCTURE_KEYWORD.test(step)) return true\n return STRUCTURE_HEADER.test(step) && !containsCommandSubstitution(step)\n}\n\n/** Removes redirections (`2>&1`, `> out.txt`) outside quotes — a `>` inside a commit message is kept. */\nfunction withoutRedirections(command: string): string {\n const masked = maskQuotedText(command)\n let result = ''\n let copiedUpTo = 0\n for (const match of masked.matchAll(REDIRECTION)) {\n result += command.slice(copiedUpTo, match.index)\n copiedUpTo = match.index + match[0].length\n }\n return result + command.slice(copiedUpTo)\n}\n\n/**\n * The commands a captured shell line runs, in order: comments and heredoc bodies removed, split into steps,\n * group brackets and wrapping keywords (`do`, `then`, `if`…) removed, and loop headers, closing keywords and\n * plain assignments dropped.\n */\nexport function commandSteps(rawCommand: string): string[] {\n return splitSteps(stripComments(stripHeredocBodies(rawCommand)))\n .map((step) => withoutKeywordPrefixes(withoutGroupBrackets(step)))\n .filter((step) => step !== '' && !isShellStructure(step) && !isPlainAssignment(step))\n}\n\n/**\n * The command a step mainly runs, in a form the normalizer rules can match: leading assignments, everything\n * after the first `|`, and redirections removed; `pnpm exec tsc` read as `npx tsc`. For an assignment or a\n * loop header that runs a command (`f=$(find . -name x)`, `for f in $(npm test)`), the command inside.\n */\nexport function simpleCommandOf(step: string): string {\n const command = withoutLeadingAssignments(step)\n if (command === '' || STRUCTURE_HEADER.test(command)) return step.match(FIRST_SUBSTITUTION)?.[1]?.trim() ?? ''\n const [firstStage = ''] = splitOutsideQuotes(command, PIPE_SEPARATORS)\n return withoutRedirections(firstStage).replace(/\\s+/g, ' ').trim().replace(TOOL_RUNNER, 'npx ')\n}\n\n/**\n * Shell glue that does no work of its own — `cd`, `true`, `:`, or printing text — as long as it runs\n * nothing inside it and pipes nothing. Agents wrap commands in these (`… || true`, `; echo \"exit=$?\"`).\n */\nexport function isGlueStep(step: string): boolean {\n if (containsCommandSubstitution(step) || splitOutsideQuotes(step, PIPE_SEPARATORS).length > 1) return false\n return GLUE_COMMANDS.some((command) => startsWithWord(step, command))\n}\n","// Categories excluded from summary input by default:\n// - exploring/note: handled separately (notes rendered before event list; exploring is raw file reads)\n// - linting: lint/format runs are support noise, not meaningful work outcomes\n// `other` is intentionally kept — hints give the summary context for commands that don't fit known patterns\n// (e.g. docker compose up, make deploy, terraform apply).\n// Only a type import, so the template engine and the --json contract can use this without loading profiles or the DB.\nimport type { SessionPayload } from '../ai/types'\n\nexport const DEFAULT_EXCLUDED_CATEGORIES = new Set(['exploring', 'note', 'linting'])\n\nexport function buildExcludedSet(includeCategories: string[] = []): Set<string> {\n const excluded = new Set(DEFAULT_EXCLUDED_CATEGORIES)\n for (const category of includeCategories) excluded.delete(category)\n return excluded\n}\n\n/** How many events of each excluded category the summary left out — for the footer under `workscribe summary`. */\nexport function countExcludedEvents(sessions: SessionPayload[], excluded: Set<string>): Map<string, number> {\n const counts = new Map<string, number>()\n for (const session of sessions) {\n for (const event of session.events) {\n if (excluded.has(event.category)) counts.set(event.category, (counts.get(event.category) ?? 0) + 1)\n }\n }\n return counts\n}\n","// Classifies compound shell commands — `cd app && pnpm nx test shell 2>&1 | tail -40; npx tsc` — by their most\n// telling step. Pure: the rule matcher is passed in, so this module doesn't load the rules (or the profiles).\nimport { commandSteps, isGlueStep, simpleCommandOf } from '../capture/command-steps'\nimport { DEFAULT_EXCLUDED_CATEGORIES } from '../summary/exclusions'\nimport type { NormalizedEvent } from './patterns'\n\n/** Matches a step: `command` is its simple form (one line, redirections gone), `raw` the step as written. */\nexport type MatchCommand = (command: string, raw: string) => NormalizedEvent\n\nexport const UNCLASSIFIED: NormalizedEvent = { category: 'other', metadata: {} }\n\n// When a compound command runs several things, the event takes the step that says most about the work:\n// outcomes first, then routine work. `git status` (versioning) must never hide `npm test` next to it.\nexport const STEP_CATEGORY_PRIORITY = [\n 'testing',\n 'building',\n 'releasing',\n 'shipping',\n 'dependencies',\n 'database',\n 'infrastructure',\n 'running',\n 'files',\n 'versioning',\n]\n\n// Ranks after the priority list, lower is more telling: any other category, then ones summaries leave out, then `other`\nconst OTHER_CATEGORY_RANK = STEP_CATEGORY_PRIORITY.length\nconst EXCLUDED_CATEGORY_RANK = OTHER_CATEGORY_RANK + 1\nconst UNCLASSIFIED_RANK = EXCLUDED_CATEGORY_RANK + 1\n\nfunction rankOf(category: string): number {\n const index = STEP_CATEGORY_PRIORITY.indexOf(category)\n if (index !== -1) return index\n if (category === UNCLASSIFIED.category) return UNCLASSIFIED_RANK\n if (DEFAULT_EXCLUDED_CATEGORIES.has(category)) return EXCLUDED_CATEGORY_RANK\n return OTHER_CATEGORY_RANK\n}\n\n/** The most telling step's event, and that step; ties go to the earlier step. */\nfunction primaryStepEvent(steps: string[], matchCommand: MatchCommand): NormalizedEvent {\n const events = steps.map((step) => ({ ...matchCommand(simpleCommandOf(step), step), matchedStep: step }))\n return events.reduce<NormalizedEvent>(\n (best, event) => (rankOf(event.category) < rankOf(best.category) ? event : best),\n UNCLASSIFIED\n )\n}\n\n/**\n * A command the rules already recognise as it is, with nothing else to run, keeps that result. Otherwise it's\n * split into the commands it runs, shell glue (`cd`, `echo`, assignments, loop keywords) is set aside, and the\n * most telling step names the event — falling back to the whole command when no step is recognised either.\n */\nexport function classifyCompound(command: string, matchCommand: MatchCommand): NormalizedEvent {\n const workSteps = commandSteps(command).filter((step) => !isGlueStep(step))\n // Matched as a whole (so heredoc text inside it is still there), but it's the one work step that ran\n const wholeCommandEvent = {\n ...matchCommand(command, command),\n matchedStep: workSteps.length === 1 ? workSteps[0] : command,\n }\n const isRecognisedSingleCommand = workSteps.length <= 1 && wholeCommandEvent.category !== UNCLASSIFIED.category\n if (isRecognisedSingleCommand || workSteps.length === 0) return wholeCommandEvent\n\n const primaryEvent = primaryStepEvent(workSteps, matchCommand)\n return primaryEvent.category === UNCLASSIFIED.category ? wholeCommandEvent : primaryEvent\n}\n","// Reads `git [global options] <subcommand> …` — git's own options (`-C <dir>`, `-c k=v`, `--work-tree=…`) apart from\n// the subcommand — so the rules see the subcommand. Pure: no file system or git calls.\nimport { type ShellWord, shellWords } from '../capture/shell-syntax'\n\nexport interface GitInvocation {\n /** `-C` directories, in order — each relative to the previous one */\n directories: string[]\n workTree: string | null\n gitDir: string | null\n /** The subcommand (`commit`, `push`, …), or null for a bare `git` or one with only options */\n subcommand: string | null\n /** The words after the subcommand */\n args: ShellWord[]\n /** Where the subcommand starts in the text, so the original (quotes and all) can be kept from there */\n subcommandStart: number\n}\n\n// Global options that take the next word as their value\nconst OPTIONS_WITH_VALUE = new Set([\n '-C',\n '-c',\n '--git-dir',\n '--work-tree',\n '--namespace',\n '--exec-path',\n '--config-env',\n])\n\n/** Reads `git [global options] <subcommand> …`, or null if the text isn't a git command. */\nexport function parseGitInvocation(command: string): GitInvocation | null {\n const words = shellWords(command.trim())\n if (words[0]?.word !== 'git') return null\n\n const invocation: GitInvocation = {\n directories: [],\n workTree: null,\n gitDir: null,\n subcommand: null,\n args: [],\n subcommandStart: -1,\n }\n for (let index = 1; index < words.length; index++) {\n const { word } = words[index]\n if (!word.startsWith('-')) {\n invocation.subcommand = word\n invocation.subcommandStart = words[index].start\n invocation.args = words.slice(index + 1)\n return invocation\n }\n const [name, inlineValue] = word.startsWith('--') ? splitLongOption(word) : [word, undefined]\n const takesValue = OPTIONS_WITH_VALUE.has(name)\n const value = inlineValue ?? (takesValue ? words[++index]?.word : undefined)\n if (name === '-C' && value !== undefined) invocation.directories.push(value)\n if (name === '--work-tree' && value !== undefined) invocation.workTree = value\n if (name === '--git-dir' && value !== undefined) invocation.gitDir = value\n }\n return invocation\n}\n\nfunction splitLongOption(word: string): [string, string | undefined] {\n const equals = word.indexOf('=')\n return equals === -1 ? [word, undefined] : [word.slice(0, equals), word.slice(equals + 1)]\n}\n\n/**\n * `git -C \"/a b\" -c x=y commit -m \"…\"` as `git commit -m \"…\"`: the global options dropped, the rest kept as typed —\n * so every git rule matches on the subcommand. Anything that isn't a git command comes back unchanged.\n */\nexport function withoutGitGlobalOptions(command: string): string {\n const invocation = parseGitInvocation(command)\n if (!invocation || invocation.subcommandStart === -1) return command\n const trimmed = command.trim()\n return `git ${trimmed.slice(invocation.subcommandStart)}`\n}\n","// A `git commit`'s message, from however it was given: `-m`, `-am`, `--message=`, `-F <file>`, `-F -` with a heredoc,\n// or the `-m \"$(cat <<'EOF' … EOF)\"` form coding agents use. Pure: a message file is only named, for capture to read.\nimport { heredocBodies } from '../capture/command-steps'\nimport { isOutsideQuotes } from '../capture/shell-syntax'\nimport { parseGitInvocation } from './git-invocation'\n\n// The first line of `$(cat <<'EOF'` — anchored to one line, so there's nothing for the engine to backtrack over\nconst SUBSTITUTION_OPENER = /^\\$\\(\\s*cat\\s*<<-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1\\s*$/\n// A heredoc opener (`<<TAG`, `<<-'TAG'`), not a `<<<` here-string\nconst HEREDOC_OPENER = /(?<!<)<<(?!<)-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1/g\n\n/** What a `git commit` says about its message: the subject line, or the file it reads it from. */\nexport interface CommitMessage {\n subject: string | null\n /** `-F <file>` — read by capture, which can check the file safely; never stored */\n messageFile?: string\n}\n\n/** The first non-empty line — what `git log --format=%s` calls the subject. */\nfunction firstLineOf(text: string | null | undefined): string | null {\n const line = text\n ?.split('\\n')\n .map((part) => part.trim())\n .find((part) => part !== '')\n return line || null\n}\n\n/** A `$(cat <<'TAG' … TAG)` value's body, found line by line — or null if the value isn't one. */\nfunction substitutionHeredocBody(value: string): string | null {\n const lines = value.split('\\n')\n const terminator = lines[0]?.match(SUBSTITUTION_OPENER)?.[2]\n if (!terminator) return null\n const end = lines.findIndex((line, index) => index > 0 && line.trim() === terminator)\n return end === -1 ? null : lines.slice(1, end).join('\\n')\n}\n\nfunction messageFromValue(value: string | undefined): CommitMessage {\n if (value === undefined) return { subject: null }\n return { subject: firstLineOf(substitutionHeredocBody(value) ?? value) }\n}\n\n/** The heredoc tag a `-F -` step reads from — quoted or not — if its opener sits outside quotes. */\nfunction inputHeredocTag(step: string): string | null {\n for (const match of step.matchAll(HEREDOC_OPENER)) {\n if (isOutsideQuotes(step, match.index)) return match[2]\n }\n return null\n}\n\nfunction messageFromFile(file: string | undefined, step: string, original: string): CommitMessage {\n if (file === undefined) return { subject: null }\n if (file !== '-') return { subject: null, messageFile: file }\n // `-F -` reads standard input: a heredoc whose body is gone from the step, so it's taken from the whole command —\n // the one opened on this step's line, in case an earlier heredoc used the same terminator\n const tag = inputHeredocTag(step)\n if (!tag) return { subject: null }\n const stepLine = step.split('\\n')[0].trim()\n const heredocs = heredocBodies(original).filter(({ terminator }) => terminator === tag)\n const heredoc = heredocs.find(({ openerLine }) => openerLine.includes(stepLine)) ?? heredocs[0]\n return { subject: firstLineOf(heredoc?.body) }\n}\n\n/**\n * A `git commit` step's message: `-m`, `-m<msg>`, `--message[=]`, short flags ending in `m` (`-am`), `-F`/`--file`\n * (a file, or `-` for a heredoc). The first message given is the subject. Null if the step isn't a commit.\n * `original` is the whole captured command, where heredoc bodies for `-F -` still are.\n */\nexport function commitMessageOf(step: string, original: string): CommitMessage | null {\n const invocation = parseGitInvocation(step)\n if (invocation?.subcommand !== 'commit') return null\n const args = invocation.args.map(({ word }) => word)\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]\n const next = args[index + 1]\n if (arg === '--') break\n if (arg === '-m' || arg === '--message') return messageFromValue(next)\n if (arg.startsWith('--message=')) return messageFromValue(arg.slice('--message='.length))\n if (arg === '-F' || arg === '--file') return messageFromFile(next, step, original)\n if (arg.startsWith('--file=')) return messageFromFile(arg.slice('--file='.length), step, original)\n if (!/^-[A-Za-z]/.test(arg) || arg.startsWith('--')) continue\n // Short options: `-m<msg>` / `-F<file>` carry their value; a cluster like `-am` or `-sF` takes the next word\n if (arg.startsWith('-m') && arg.length > 2) return messageFromValue(arg.slice(2))\n if (arg.startsWith('-F') && arg.length > 2) return messageFromFile(arg.slice(2), step, original)\n if (arg.endsWith('m')) return messageFromValue(next)\n if (arg.endsWith('F')) return messageFromFile(next, step, original)\n }\n return { subject: null }\n}\n","// What a package-manager command installs or removes, and for which workspace — read word by word, so flags and\n// their values (`--filter web`, `--registry <url>`) are never taken for package names. Pure.\n\n// Package-manager flags whose next word is their value, not a package.\n// (`-w` isn't here: npm's takes a value but pnpm's — workspace root — doesn't.)\nconst FLAGS_WITH_VALUE = new Set([\n '--filter',\n '-F',\n '--registry',\n '--workspace',\n '--dir',\n '-C',\n '--prefix',\n '--tag',\n '--cwd',\n])\n\nexport const INSTALL_WORDS = ['install', 'i', 'add']\nexport const REMOVE_WORDS = ['remove', 'rm', 'uninstall', 'un']\n\n/**\n * The package names after a command word — `npm install foo bar` → foo, bar. Flags are skipped, and so is the value\n * of a flag that takes one (`pnpm add --filter web zod` → zod).\n */\nexport function extractPackages(command: string, commandWords: string[]): string[] {\n const words = command.trim().split(/\\s+/)\n const start = words.findIndex((word, index) => index > 0 && commandWords.includes(word))\n if (start === -1) return []\n const packages: string[] = []\n for (let index = start + 1; index < words.length; index++) {\n const word = words[index]\n if (FLAGS_WITH_VALUE.has(word)) index++\n else if (word && !word.startsWith('-')) packages.push(word)\n }\n return packages\n}\n\n/** The workspace a pnpm `--filter` / `-F` command targets, if any. */\nfunction workspaceOf(command: string): string | null {\n const words = command.trim().split(/\\s+/)\n const filter = words.findIndex((word) => word === '--filter' || word === '-F')\n if (filter !== -1) return words[filter + 1] ?? null\n return words.find((word) => word.startsWith('--filter='))?.slice('--filter='.length) ?? null\n}\n\n/** Dependency metadata, with `action: 'remove'` for removals and the workspace when the command targets one. */\nexport function dependencyMetadata(\n tool: string,\n command: string,\n commandWords: string[],\n action?: 'remove'\n): Record<string, unknown> {\n const workspace = workspaceOf(command)\n return {\n tool,\n packages: extractPackages(command, commandWords),\n ...(action ? { action } : {}),\n ...(workspace ? { workspace } : {}),\n }\n}\n","import { loadAIToolRegistry } from '../config/registry'\nimport { getActiveProfileRules } from '../profiles/loader'\nimport { debugLog } from '../utils/logger'\nimport { classifyCompound, UNCLASSIFIED } from './compound'\nimport { commitMessageOf } from './commit-message'\nimport { dependencyMetadata, extractPackages, INSTALL_WORDS, REMOVE_WORDS } from './dependency-metadata'\nimport { withoutGitGlobalOptions } from './git-invocation'\n\nexport interface NormalizedEvent {\n category: string\n metadata: Record<string, unknown>\n /** The step that set the category, as written (or the whole command) — for capture, never stored */\n matchedStep?: string\n}\n\n/** What a rule can read besides the one-line command it matched: the step as written, and the whole command. */\ninterface RuleContext {\n /** The step with its line breaks and quoting — `-m \"$(cat <<'EOF' …)\"` survives here */\n raw: string\n /** The whole captured command, where heredoc bodies (`-F - <<EOF`) still are */\n original: string\n}\n\ninterface Rule {\n match(cmd: string, context: RuleContext): NormalizedEvent | null\n}\n\nfunction makeRule(\n pattern: RegExp,\n category: string,\n extract: (match: RegExpMatchArray, cmd: string, context: RuleContext) => Record<string, unknown> = () => ({})\n): Rule {\n return {\n match(cmd, context) {\n const m = cmd.match(pattern)\n if (!m) return null\n return { category, metadata: extract(m, cmd, context) }\n },\n }\n}\n\n/** A package-manager rule: `tool`'s packages after one of `commandWords`, with `action: 'remove'` for removals. */\nfunction dependencyRule(pattern: RegExp, tool: string, commandWords: string[], action?: 'remove'): Rule {\n return makeRule(pattern, 'dependencies', (_match, command) => dependencyMetadata(tool, command, commandWords, action))\n}\n\n// Nx targets → categories: `nx build app`, `nx run app:test`, `nx run-many -t lint`, `nx affected --target=e2e`\nconst NX_TARGET_CATEGORIES: Record<string, string> = {\n build: 'building',\n test: 'testing',\n e2e: 'testing',\n lint: 'linting',\n serve: 'running',\n dev: 'running',\n}\nconst NX_COMMAND =\n /^(?:(?:npx|pnpm|yarn|bunx)\\s+)?nx\\s+(?:(?:run-many|affected)\\b.*?(?:-t|--targets?)[=\\s]+([\\w-]+)|run\\s+[\\w@./-]+:([\\w-]+)|([\\w-]+)\\b)/\n\nconst nxRule: Rule = {\n match(cmd) {\n const match = cmd.match(NX_COMMAND)\n const target = match?.[1] ?? match?.[2] ?? match?.[3]\n const category = target ? NX_TARGET_CATEGORIES[target] : undefined\n return category ? { category, metadata: { tool: 'nx', target } } : null\n },\n}\n\nconst BASE_RULES: Rule[] = [\n // ─── Git operations ───────────────────────────────────────────────────────\n makeRule(/^git\\s+commit\\b/, 'shipping', (_m, _cmd, { raw, original }) => {\n // Read from the step as written, so quoting and multi-line heredoc messages survive (see git-invocation.ts)\n const commitMessage = commitMessageOf(raw, original)\n const message = commitMessage?.subject ?? null\n return commitMessage?.messageFile ? { message, messageFile: commitMessage.messageFile } : { message }\n }),\n\n makeRule(/^git\\s+push\\b/, 'shipping', (_m, cmd) => {\n // Flags (-u, --force, …) aren't the remote or branch: `git push -u origin main` → origin/main\n const positional = cmd\n .split(/\\s+/)\n .slice(2)\n .filter((part) => !part.startsWith('-'))\n return { remote: positional[0] ?? 'origin', branch: positional[1] ?? null }\n }),\n\n makeRule(/^git\\s+(pull|fetch)\\b/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], remote: parts[2] ?? 'origin' }\n }),\n\n makeRule(/^git\\s+(checkout|switch)\\b/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n const branch = parts.find((part) => !part.startsWith('-') && part !== 'git' && part !== parts[1])\n return { branch: branch ?? null }\n }),\n\n makeRule(/^git\\s+(merge|rebase)\\b/, 'shipping', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], target: parts[2] ?? null }\n }),\n\n makeRule(/^git\\s+stash\\b/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[2] ?? 'push' }\n }),\n\n makeRule(/^git\\s+/, 'versioning', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1] ?? null }\n }),\n\n // ─── Releasing ────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+publish\\b/, 'releasing', () => ({ tool: 'npm' })),\n makeRule(/^cargo\\s+publish\\b/, 'releasing', () => ({ tool: 'cargo' })),\n makeRule(/^gem\\s+push\\b/, 'releasing', () => ({ tool: 'gem' })),\n makeRule(/^docker\\s+push\\b/, 'releasing', (_m, cmd) => ({ tool: 'docker', image: cmd.split(/\\s+/)[2] ?? null })),\n makeRule(/^gh\\s+release\\b/, 'releasing', (_m, cmd) => ({ tool: 'gh', subcommand: cmd.split(/\\s+/)[2] ?? null })),\n makeRule(/^fly\\s+(deploy|launch)\\b/, 'releasing', (match) => ({ tool: 'fly', subcommand: match[1] })),\n makeRule(/^vercel\\s+(deploy|build|--prod)\\b/, 'releasing', () => ({ tool: 'vercel' })),\n makeRule(/^netlify\\s+deploy\\b/, 'releasing', () => ({ tool: 'netlify' })),\n makeRule(/^railway\\s+up\\b/, 'releasing', () => ({ tool: 'railway' })),\n makeRule(/^wrangler\\s+deploy\\b/, 'releasing', () => ({ tool: 'wrangler' })),\n\n // ─── Dependencies ─────────────────────────────────────────────────────────\n dependencyRule(/^npm\\s+(install|i|add)\\b/, 'npm', INSTALL_WORDS),\n\n // `yarn workspace web add zod` — the workspace is the word after `workspace`\n makeRule(/^yarn\\s+workspace\\s+(\\S+)\\s+(add|remove)\\b/, 'dependencies', (match, command) => ({\n ...dependencyMetadata('yarn', command, [match[2]], match[2] === 'remove' ? 'remove' : undefined),\n workspace: match[1],\n })),\n\n // `pnpm --filter web add zod`, `pnpm -F web add zod`, `pnpm -r add zod` — options before the subcommand\n makeRule(\n /^pnpm\\s+(?:(?:--filter|-F)\\s+\\S+|--filter=\\S+|-r|--recursive)\\s+(add|install|i|remove|rm|uninstall|un)\\b/,\n 'dependencies',\n (match, command) =>\n REMOVE_WORDS.includes(match[1])\n ? dependencyMetadata('pnpm', command, REMOVE_WORDS, 'remove')\n : dependencyMetadata('pnpm', command, INSTALL_WORDS)\n ),\n\n // `yarn install` takes no packages; `pnpm install <pkg>` / `pnpm i <pkg>` adds them, like `add`\n dependencyRule(/^yarn\\s+(install|add)\\b/, 'yarn', ['add']),\n dependencyRule(/^pnpm\\s+(install|i|add)\\b/, 'pnpm', INSTALL_WORDS),\n dependencyRule(/^bun\\s+(install|i|add)\\b/, 'bun', INSTALL_WORDS),\n\n makeRule(/^pip\\s+(install)\\b/, 'dependencies', (_match, command) => ({\n tool: 'pip',\n packages: extractPackages(command, ['install']),\n })),\n\n makeRule(/^cargo\\s+add\\b/, 'dependencies', (_match, command) => ({\n tool: 'cargo',\n packages: extractPackages(command, ['add']),\n })),\n\n makeRule(/^(brew)\\s+install\\b/, 'dependencies', (_match, command) => ({\n tool: 'brew',\n packages: extractPackages(command, ['install']),\n })),\n\n makeRule(/^go\\s+get\\b/, 'dependencies', (_match, command) => ({\n tool: 'go',\n packages: extractPackages(command, ['get']),\n })),\n\n dependencyRule(/^npm\\s+(uninstall|remove|rm|un)\\b/, 'npm', REMOVE_WORDS, 'remove'),\n\n makeRule(/^(yarn|pnpm)\\s+(remove|rm|uninstall|un)\\b/, 'dependencies', (match, command) =>\n dependencyMetadata(match[1], command, REMOVE_WORDS, 'remove')\n ),\n\n dependencyRule(/^bun\\s+(remove|rm)\\b/, 'bun', REMOVE_WORDS, 'remove'),\n\n makeRule(/^pip\\s+uninstall\\b/, 'dependencies', (_match, command) => ({\n tool: 'pip',\n packages: extractPackages(command, ['uninstall']),\n action: 'remove',\n })),\n\n // Runtime version managers\n makeRule(/^nvm\\b/, 'dependencies', (_m, cmd) => ({ tool: 'nvm', subcommand: cmd.split(/\\s+/)[1] ?? null })),\n makeRule(/^(fnm|volta)\\b/, 'dependencies', (m, cmd) => ({ tool: m[1], subcommand: cmd.split(/\\s+/)[1] ?? null })),\n makeRule(/^(rbenv|pyenv|asdf)\\b/, 'dependencies', (m, cmd) => ({\n tool: m[1],\n subcommand: cmd.split(/\\s+/)[1] ?? null,\n })),\n\n // ─── Nx (before the package-manager rules, which would read `pnpm nx` as `pnpm`) ─\n nxRule,\n\n // ─── Tests ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+(test|run\\s+test)\\b/, 'testing', () => ({ tool: 'npm' })),\n makeRule(/^yarn\\s+test\\b/, 'testing', () => ({ tool: 'yarn' })),\n makeRule(/^pnpm\\s+test\\b/, 'testing', () => ({ tool: 'pnpm' })),\n makeRule(/^bun\\s+test\\b/, 'testing', () => ({ tool: 'bun' })),\n makeRule(/^(jest|vitest)\\b/, 'testing', (match) => ({ tool: match[1] })),\n makeRule(/^pytest\\b/, 'testing', () => ({ tool: 'pytest' })),\n makeRule(/^go\\s+test\\b/, 'testing', () => ({ tool: 'go' })),\n makeRule(/^cargo\\s+test\\b/, 'testing', () => ({ tool: 'cargo' })),\n makeRule(/^rspec\\b/, 'testing', () => ({ tool: 'rspec' })),\n makeRule(/^(php\\s+)?phpunit\\b/, 'testing', () => ({ tool: 'phpunit' })),\n\n // ─── Build ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+build\\b/, 'building', () => ({ tool: 'npm' })),\n makeRule(/^(yarn|pnpm)\\s+build\\b/, 'building', (match) => ({ tool: match[1] })),\n makeRule(/^bun\\s+run\\s+build\\b/, 'building', () => ({ tool: 'bun' })),\n makeRule(/^(make|gmake)\\b/, 'building', (match) => ({ tool: match[1] })),\n makeRule(/^cargo\\s+build\\b/, 'building', () => ({ tool: 'cargo' })),\n makeRule(/^go\\s+build\\b/, 'building', () => ({ tool: 'go' })),\n makeRule(/^docker\\s+build\\b/, 'building', () => ({ tool: 'docker' })),\n makeRule(/^(mvn|gradle)\\b/, 'building', (match) => ({ tool: match[1] })),\n\n // ─── Dev server / running ─────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+(dev|start|serve)\\b/, 'running', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^(yarn|pnpm)\\s+(dev|start)\\b/, 'running', (match) => ({ tool: match[1], script: match[2] })),\n makeRule(/^bun\\s+run\\s+(dev|start|serve)\\b/, 'running', (_m, cmd) => ({\n tool: 'bun',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^python\\s+.*manage\\.py\\s+runserver\\b/, 'running', () => ({ tool: 'django' })),\n\n // ─── Infrastructure ───────────────────────────────────────────────────────\n makeRule(/^ssh\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'ssh', target: parts[parts.length - 1] }\n }),\n\n makeRule(/^kubectl\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'kubectl', subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^(helm|terraform|pulumi)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^docker\\s+(run|compose|ps|stop|start|rm|exec)\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'docker', subcommand: parts[1] }\n }),\n\n makeRule(/^(aws|gcloud|az)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n // ─── Database ─────────────────────────────────────────────────────────────\n makeRule(/^(psql|pgcli)\\b/, 'database', (m) => ({ tool: m[1] })),\n makeRule(/^(mysql|mycli)\\b/, 'database', (m) => ({ tool: m[1] })),\n makeRule(/^sqlite3\\b/, 'database', () => ({ tool: 'sqlite3' })),\n makeRule(/^(mongosh|mongo)\\b/, 'database', (m) => ({ tool: m[1] })),\n makeRule(/^redis-cli\\b/, 'database', () => ({ tool: 'redis-cli' })),\n\n // ─── npx ─────────────────────────────────────────────────────────────────\n makeRule(/^npx\\s+(jest|vitest|mocha|ava|tap)\\b/, 'testing', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n makeRule(/^(?:npx\\s+)?tsc\\b/, 'building', () => ({ tool: 'tsc' })),\n makeRule(/^npx\\s+(eslint|tslint|biome)\\b/, 'linting', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n makeRule(/^npx\\s+(prettier|dprint)\\b/, 'linting', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n makeRule(/^npx\\s+\\S+/, 'running', (_m, cmd) => ({\n tool: cmd.split(/\\s+/)[1],\n })),\n\n // ─── Scripts ──────────────────────────────────────────────────────────────\n makeRule(/^(bash|sh|zsh|fish)\\s+\\S+/, 'running', (match) => ({ shell: match[1] })),\n makeRule(/^node\\s+\\S+\\.(?:js|mjs|cjs)\\b/, 'running', () => ({ runtime: 'node' })),\n makeRule(/^python3?\\s+\\S+\\.py\\b/, 'running', () => ({ runtime: 'python' })),\n\n // ─── Linting / code quality ───────────────────────────────────────────────\n makeRule(/^(eslint|tslint|biome)\\b/, 'linting', (match) => ({ tool: match[1] })),\n makeRule(/^(prettier|dprint)\\b/, 'linting', (match) => ({ tool: match[1] })),\n\n // ─── generic run scripts ──────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+\\S+/, 'running', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n\n makeRule(/^bun\\s+run\\s+\\S+/, 'running', (_m, cmd) => ({\n tool: 'bun',\n script: cmd.split(/\\s+/)[2],\n })),\n\n // ─── Code search ─────────────────────────────────────────────────────────\n makeRule(/^(grep|egrep|fgrep)\\b/, 'exploring', (match) => ({ tool: match[1] })),\n makeRule(/^(rg|ag|ack|ripgrep)\\b/, 'exploring', (match) => ({ tool: match[1] })),\n makeRule(/^find\\s/, 'exploring', () => ({ tool: 'find' })),\n\n // ─── Code read ────────────────────────────────────────────────────────────\n makeRule(/^(cat|bat|less|more)\\s+\\S+/, 'exploring', (match) => ({ tool: match[1] })),\n makeRule(/^(head|tail)\\b/, 'exploring', (match) => ({ tool: match[1] })),\n\n // ─── File operations ──────────────────────────────────────────────────────\n makeRule(/^(cp|rsync)\\b/, 'files', (match) => ({ tool: match[1] })),\n makeRule(/^(mv)\\b/, 'files', () => ({ tool: 'mv' })),\n makeRule(/^(rm|rmdir)\\b/, 'files', (match) => ({ tool: match[1] })),\n makeRule(/^(mkdir|touch|ln)\\b/, 'files', (match) => ({ tool: match[1] })),\n makeRule(/^(chmod|chown|chgrp)\\b/, 'files', (match) => ({ tool: match[1] })),\n\n // ─── Environment ──────────────────────────────────────────────────────────\n makeRule(/^export\\s+\\w+=/, 'files', (_m, cmd) => {\n const varName = cmd.match(/^export\\s+(\\w+)=/)?.[1] ?? null\n return { variable: varName }\n }),\n makeRule(/^source\\s+/, 'files', () => ({})),\n]\n\nfunction aiToolRules(): Rule[] {\n return loadAIToolRegistry().flatMap((entry) => {\n try {\n return [makeRule(new RegExp(entry.pattern), 'ai_session', () => ({ tool: entry.tool }))]\n } catch {\n debugLog('registry', `Skipping tool \"${entry.tool}\" — invalid pattern: ${entry.pattern}`)\n return []\n }\n })\n}\n\nexport function buildRules(profileRules: Array<{ match(cmd: string): NormalizedEvent | null }> = []): Rule[] {\n return [...BASE_RULES, ...profileRules, ...aiToolRules()]\n}\n\n// Built on first use, not at import: the active profiles come from the config and profile files, and\n// importing this module (via capture, or the database's migrations) shouldn't read either\nlet activeRules: Rule[] | null = null\n\nfunction getRules(): Rule[] {\n activeRules ??= buildRules(getActiveProfileRules())\n return activeRules\n}\n\nfunction matchRules(command: string, context: RuleContext): NormalizedEvent {\n // git's own options (`-C <dir>`, `-c k=v`, `--work-tree=…`) are dropped, so the rules see `git <subcommand> …`\n const flattened = withoutGitGlobalOptions(command.trim()).replace(/\\s+/g, ' ')\n for (const rule of getRules()) {\n const result = rule.match(flattened, context)\n if (result) return result\n }\n return UNCLASSIFIED\n}\n\n/**\n * Classifies a captured command — compound ones by their most telling step (see `./compound`). `matchedStep` says\n * which step that was, as written, so capture can tell which repo a `git -C …` step worked in.\n */\nexport function normalizeCommand(command: string): NormalizedEvent {\n return classifyCompound(command, (simple, raw) => matchRules(simple, { raw, original: command }))\n}\n","import type { NormalizedEvent } from './patterns'\n\nexport interface ReclassifyOptions {\n classify: (command: string) => NormalizedEvent\n /** `capture.aiTools` — when it's off, Workscribe doesn't record AI sessions, so none are created here either. */\n aiToolsEnabled: boolean\n}\n\n/**\n * The new category and metadata for a stored `other` event, or null to leave it alone. The metadata\n * replaces the old one entirely, so the `hint`/`tool` kept for unclassified commands doesn't linger.\n */\nexport function reclassifiedEvent(\n command: string,\n { classify, aiToolsEnabled }: ReclassifyOptions\n): NormalizedEvent | null {\n const { category, metadata } = classify(command)\n if (category === 'other') return null\n if (category === 'ai_session' && !aiToolsEnabled) return null\n // A `-F <file>` path is only for capture to read at the time; a stored event never keeps it\n const { messageFile: _messageFile, ...storedMetadata } = metadata\n return { category, metadata: storedMetadata }\n}\n","// Reclassifies stored `other` events with the classifier as it is when this runs — so on an install that first\n// opens its database with a later release, later rules and the user's active profiles apply too. That's fine:\n// it only ever improves `other` rows, never touches any other row or a stored command, and skips what it can't classify.\nimport { normalizeCommand } from '../../normalize/patterns'\nimport { reclassifiedEvent } from '../../normalize/reclassify'\nimport { readConfig } from '../../config'\nimport { debugLog } from '../../utils/logger'\nimport type { Migration, MigrationDatabase } from '../migration-types'\n\ninterface UnclassifiedRow {\n id: number\n command: string\n}\n\nfunction classifyOrSkip(row: UnclassifiedRow, aiToolsEnabled: boolean): ReturnType<typeof reclassifiedEvent> {\n try {\n return reclassifiedEvent(row.command, { classify: normalizeCommand, aiToolsEnabled })\n } catch (error) {\n // Best-effort: this runs the first time any command opens the database after upgrading (even the\n // background capture), so one odd command must never fail the migration and lock the database out\n debugLog('migration-006', error)\n return null\n }\n}\n\nconst migration: Migration = {\n version: 6,\n description: 'Reclassify unclassified events now that compound commands are classified by their main step',\n sql: '',\n run(db: MigrationDatabase) {\n const aiToolsEnabled = readConfig().capture.aiTools !== false\n const rows = db.prepare(\"SELECT id, command FROM events WHERE category = 'other'\").all() as UnclassifiedRow[]\n const update = db.prepare('UPDATE events SET category = ?, metadata = ? WHERE id = ?')\n for (const row of rows) {\n const event = classifyOrSkip(row, aiToolsEnabled)\n if (!event) continue\n const metadata = Object.keys(event.metadata).length > 0 ? JSON.stringify(event.metadata) : null\n update.run(event.category, metadata, row.id)\n }\n },\n}\n\nexport default migration\n","// Strictly additive, like every migration from here on: data work belongs in a data job (src/db/jobs/), which is\n// batched, resumable and backed up, never in a migration that runs in whichever process opens the database first.\nconst migration = {\n version: 7,\n description: 'Add data job state, the issues jobs record, and a meta key-value table',\n sql: `\n CREATE TABLE IF NOT EXISTS data_jobs (\n name TEXT PRIMARY KEY,\n version INTEGER NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'pending',\n started_at TEXT,\n finished_at TEXT,\n verification TEXT,\n error TEXT\n );\n CREATE TABLE IF NOT EXISTS data_job_issues (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_name TEXT NOT NULL,\n job_version INTEGER NOT NULL,\n reference TEXT NOT NULL,\n reason TEXT NOT NULL,\n recorded_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_data_job_issues_job ON data_job_issues (job_name, job_version);\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `,\n}\n\nexport default migration\n","// Strictly additive: the tables and the two nullable `events` columns start empty. Capture fills them for new events\n// and the `project-dimensions` data job (src/db/jobs/project-dimensions.ts) fills them for the history.\nconst migration = {\n version: 8,\n description: 'Add project, branch and file dimensions, and link events to them',\n sql: `\n CREATE TABLE IF NOT EXISTS project (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n identity TEXT NOT NULL UNIQUE,\n kind TEXT NOT NULL,\n name TEXT\n );\n CREATE TABLE IF NOT EXISTS project_alias (\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n PRIMARY KEY (project_id, name)\n );\n CREATE INDEX IF NOT EXISTS idx_project_alias_name ON project_alias (name);\n CREATE TABLE IF NOT EXISTS project_path (\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n path TEXT NOT NULL,\n PRIMARY KEY (project_id, path)\n );\n CREATE INDEX IF NOT EXISTS idx_project_path_path ON project_path (path);\n CREATE TABLE IF NOT EXISTS branch (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n UNIQUE (project_id, name)\n );\n CREATE TABLE IF NOT EXISTS file (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES project (id) ON DELETE CASCADE,\n relative_path TEXT NOT NULL,\n UNIQUE (project_id, relative_path)\n );\n CREATE TABLE IF NOT EXISTS event_file (\n event_id INTEGER NOT NULL REFERENCES events (id) ON DELETE CASCADE,\n file_id INTEGER NOT NULL REFERENCES file (id) ON DELETE CASCADE,\n action TEXT NOT NULL,\n PRIMARY KEY (event_id, file_id)\n );\n CREATE INDEX IF NOT EXISTS idx_event_file_file ON event_file (file_id);\n CREATE TABLE IF NOT EXISTS directory_probe (\n path TEXT PRIMARY KEY,\n outcome TEXT NOT NULL,\n root TEXT,\n common_dir TEXT,\n remote_url TEXT\n );\n CREATE TABLE IF NOT EXISTS project_resolution (\n cwd TEXT NOT NULL,\n repo_name TEXT NOT NULL,\n project_id INTEGER REFERENCES project (id) ON DELETE CASCADE,\n method TEXT NOT NULL,\n event_count INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (cwd, repo_name)\n );\n ALTER TABLE events ADD COLUMN project_id INTEGER REFERENCES project (id);\n ALTER TABLE events ADD COLUMN branch_id INTEGER REFERENCES branch (id);\n CREATE INDEX IF NOT EXISTS idx_events_project_id ON events (project_id);\n CREATE INDEX IF NOT EXISTS idx_events_branch_id ON events (branch_id);\n `,\n}\n\nexport default migration\n","// Strictly additive. Everything here is derived from `events` and rebuilt per day (src/sessions/threads.ts), so the\n// `project_id` columns carry no foreign key: a project merged or deleted later can't block a write, and the day's\n// next rebuild replaces the rows.\nconst migration = {\n version: 9,\n description: 'Add work blocks, project threads and activity slots, built per day alongside sessions',\n sql: `\n CREATE TABLE IF NOT EXISTS work_block (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n event_count INTEGER NOT NULL,\n active_slots INTEGER NOT NULL,\n grouping_version INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_work_block_date ON work_block (date, started_at);\n CREATE TABLE IF NOT EXISTS thread (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n block_id INTEGER NOT NULL REFERENCES work_block (id) ON DELETE CASCADE,\n project_id INTEGER,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n event_count INTEGER NOT NULL,\n active_slots INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_thread_block ON thread (block_id);\n CREATE INDEX IF NOT EXISTS idx_thread_project ON thread (project_id, started_at);\n CREATE TABLE IF NOT EXISTS thread_event (\n thread_id INTEGER NOT NULL REFERENCES thread (id) ON DELETE CASCADE,\n event_id INTEGER NOT NULL REFERENCES events (id) ON DELETE CASCADE,\n PRIMARY KEY (thread_id, event_id)\n );\n CREATE INDEX IF NOT EXISTS idx_thread_event_event ON thread_event (event_id);\n CREATE TABLE IF NOT EXISTS activity_slot (\n date TEXT NOT NULL,\n slot_start TEXT NOT NULL,\n project_id INTEGER,\n event_count INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_activity_slot_date ON activity_slot (date, slot_start);\n CREATE TABLE IF NOT EXISTS grouping_day (\n date TEXT PRIMARY KEY,\n grouping_version INTEGER NOT NULL,\n idle_ms INTEGER NOT NULL,\n event_count INTEGER NOT NULL,\n link_checksum INTEGER NOT NULL,\n link_spread INTEGER NOT NULL,\n max_event_id INTEGER\n );\n `,\n}\n\nexport default migration\n","// Strictly additive. A summary is kept against the events it describes rather than a derived session id or key, so\n// regrouping never orphans it. No foreign keys: a summary is never deleted because its events or project went away.\nconst migration = {\n version: 10,\n description: 'Add summaries anchored to the events they describe',\n sql: `\n CREATE TABLE IF NOT EXISTS summary (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL,\n project_id INTEGER,\n first_event_id INTEGER,\n last_event_id INTEGER,\n engine TEXT NOT NULL,\n text TEXT NOT NULL,\n created_at TEXT NOT NULL,\n source_key TEXT,\n origin TEXT NOT NULL,\n anchor_source TEXT NOT NULL,\n is_mixed INTEGER NOT NULL DEFAULT 0\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_summary_source ON summary (source_key, engine);\n CREATE INDEX IF NOT EXISTS idx_summary_first_event ON summary (first_event_id);\n `,\n}\n\nexport default migration\n","// Strictly additive: an index, so `context` finds a project's latest event without sorting all of them.\nconst migration = {\n version: 11,\n description: \"Index events by project and time, for a project's latest event\",\n sql: `\n CREATE INDEX IF NOT EXISTS idx_events_project_time ON events (project_id, captured_at);\n `,\n}\n\nexport default migration\n","// Strictly additive. The agent conversation each hook event came from (Claude Code's `session_id`), so one agent's\n// work across several repos can be linked. WOR-136 extends this table with how a session ended and its recaps.\nconst migration = {\n version: 12,\n description: 'Add agent sessions, and link each agent event to its session',\n sql: `\n CREATE TABLE IF NOT EXISTS agent_sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n source TEXT NOT NULL,\n session_id TEXT NOT NULL,\n cwd TEXT,\n started_at TEXT NOT NULL,\n ended_at TEXT,\n last_seen_at TEXT NOT NULL,\n UNIQUE (source, session_id)\n );\n ALTER TABLE events ADD COLUMN agent_session_id INTEGER REFERENCES agent_sessions (id);\n CREATE INDEX IF NOT EXISTS idx_events_agent_session ON events (agent_session_id);\n `,\n}\n\nexport default migration\n","// Strictly additive. Tickets named by branches and commit messages (WOR-144): a ticket links to a branch, or to the\n// commit event that named it. Capture fills them for new events and the `ticket-refs` data job for the history; an\n// event's links go with it (undo, clear) and a branch's with the branch.\nconst migration = {\n version: 13,\n description: 'Add tickets, linked to the branches and commits that name them',\n sql: `\n CREATE TABLE IF NOT EXISTS ticket (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT NOT NULL UNIQUE\n );\n CREATE TABLE IF NOT EXISTS ticket_ref (\n ticket_id INTEGER NOT NULL REFERENCES ticket (id) ON DELETE CASCADE,\n branch_id INTEGER REFERENCES branch (id) ON DELETE CASCADE,\n event_id INTEGER REFERENCES events (id) ON DELETE CASCADE,\n CHECK ((branch_id IS NULL) <> (event_id IS NULL))\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_ref_branch ON ticket_ref (branch_id, ticket_id) WHERE branch_id IS NOT NULL;\n CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_ref_event ON ticket_ref (event_id, ticket_id) WHERE event_id IS NOT NULL;\n `,\n}\n\nexport default migration\n","import initialSchema from './001_initial_schema'\nimport semanticCategories from './002_semantic_categories'\nimport alerts from './003_alerts'\nimport ideColumns from './004_ide_columns'\nimport sessionSummaries from './005_session_summaries'\nimport reclassifyOther from './006_reclassify_other'\nimport dataJobs from './007_data_jobs'\nimport dimensions from './008_dimensions'\nimport workBlocks from './009_work_blocks'\nimport summaries from './010_summaries'\nimport eventsProjectTime from './011_events_project_time'\nimport agentSessions from './012_agent_sessions'\nimport tickets from './013_tickets'\nimport type { Migration } from '../migration-types'\n\nexport type { Migration } from '../migration-types'\n\n// Ordered list — append only. Never modify or remove an existing entry.\nexport const MIGRATIONS: Migration[] = [\n initialSchema,\n semanticCategories,\n alerts,\n ideColumns,\n sessionSummaries,\n reclassifyOther,\n dataJobs,\n dimensions,\n workBlocks,\n summaries,\n eventsProjectTime,\n agentSessions,\n tickets,\n]\n","/** The message from anything thrown — an Error's message, or the thrown value itself as text. */\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import type { Migration, MigrationDatabase } from './migration-types'\nimport { errorMessage } from '../utils/error-message'\n\n/** An install from before the migration system already has the events table — record version 1 as applied. */\nfunction seedVersionOneForOldInstalls(db: MigrationDatabase): void {\n const hasEvents = db.prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='events'\").get()\n const hasVersion1 = db.prepare('SELECT version FROM schema_migrations WHERE version = 1').get()\n if (hasEvents && !hasVersion1) {\n db.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES ($version, datetime('now'))\").run({\n version: 1,\n })\n }\n}\n\nfunction isApplied(db: MigrationDatabase, version: number): boolean {\n return db.prepare('SELECT version FROM schema_migrations WHERE version = ?').get(version) !== undefined\n}\n\n/**\n * Applies each pending migration in order, each in its own transaction, so a failure leaves the database at\n * the previous version and the next run retries it. `BEGIN IMMEDIATE` takes the write lock up front and the\n * version is checked inside the transaction, so two processes starting at once (parallel captures from an\n * agent) can't both run a migration. Kept free of `node:sqlite` so tests can run it on any database.\n */\nexport function applyMigrations(db: MigrationDatabase, migrations: Migration[]): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_migrations (\n version INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `)\n seedVersionOneForOldInstalls(db)\n\n for (const migration of migrations) {\n if (isApplied(db, migration.version)) continue\n\n db.exec('BEGIN IMMEDIATE')\n try {\n if (isApplied(db, migration.version)) {\n db.exec('COMMIT')\n continue\n }\n if (migration.sql.trim()) db.exec(migration.sql)\n migration.run?.(db)\n db.prepare(\"INSERT INTO schema_migrations (version, applied_at) VALUES ($version, datetime('now'))\").run({\n version: migration.version,\n })\n db.exec('COMMIT')\n } catch (error) {\n try {\n db.exec('ROLLBACK')\n } catch {\n /* already rolled back or connection lost */\n }\n throw new Error(`Migration ${migration.version} (${migration.description}) failed: ${errorMessage(error)}`, {\n cause: error,\n })\n }\n }\n}\n","import type { MigrationDatabase } from './migration-types'\n\n/** How long a connection waits for another process's write lock before failing (`PRAGMA busy_timeout`). */\nexport const BUSY_TIMEOUT_MS = 5000\n\n// SQLite's SQLITE_BUSY (\"database is locked\"): another process held the write lock for longer than we waited\nconst SQLITE_BUSY = 5\n\nexport interface TransactionOptions {\n /** Wait at most this long for the write lock, instead of BUSY_TIMEOUT_MS. Restored afterwards. */\n busyTimeoutMs?: number\n}\n\n/** Whether an error is SQLite giving up on a write lock another process holds. */\nexport function isDatabaseLocked(error: unknown): boolean {\n return (error as { errcode?: unknown } | null)?.errcode === SQLITE_BUSY\n}\n\n/** Undoes an open transaction. Never throws: the error that made us roll back is the one worth reporting. */\nfunction rollbackQuietly(db: MigrationDatabase): void {\n try {\n db.exec('ROLLBACK')\n } catch {\n // SQLite already rolled back (some errors end the transaction themselves), or the connection is gone\n }\n}\n\n/**\n * Runs `read` in a read transaction: one consistent snapshot across its queries, without the write lock — in WAL\n * mode a reader never blocks a writer, however long it reads. An error from `read` is the one thrown, never one from\n * ending the transaction.\n */\nexport function withReadTransaction<T>(db: MigrationDatabase, read: () => T): T {\n db.exec('BEGIN')\n let result: T\n try {\n result = read()\n } catch (error) {\n rollbackQuietly(db)\n throw error\n }\n db.exec('COMMIT')\n return result\n}\n\n/**\n * Runs `work` holding the database's write lock (`BEGIN IMMEDIATE`), so no other process can write in between —\n * `work` should recheck whatever made it start, since another process may have done the job while it waited.\n * Commits on success; rolls back and rethrows on error. Throws a locked error (see `isDatabaseLocked`) if the lock\n * isn't free within the busy timeout. Kept free of `node:sqlite`, so tests run it on any database.\n */\nexport function withImmediateTransaction<T>(db: MigrationDatabase, work: () => T, options: TransactionOptions = {}): T {\n const { busyTimeoutMs } = options\n if (busyTimeoutMs !== undefined) db.exec(`PRAGMA busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`)\n try {\n db.exec('BEGIN IMMEDIATE')\n try {\n const result = work()\n db.exec('COMMIT')\n return result\n } catch (error) {\n rollbackQuietly(db)\n throw error\n }\n } finally {\n if (busyTimeoutMs !== undefined) db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`)\n }\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { ProjectKind } from '../capture/project-identity'\n\n// The project, branch and file rows events link to (migration 008). Insert-or-ignore throughout, so capture and the\n// backfill job can both write them, in any order, any number of times. `node:sqlite`-free, so tests run it in memory.\n\n/** What an event did to a file. An editor save is the one file action captured today. */\nexport type FileAction = 'save'\n\nexport const FILE_SAVE_ACTION: FileAction = 'save'\n\nexport interface ProjectRecord {\n identity: string\n kind: ProjectKind\n /** The name the event was stored under: kept as an alias, and as the project's latest name */\n name: string | null\n /** The checkout or worktree root the event came from, when git said */\n root: string | null\n}\n\n/** What an event links to. Resolved before the event is written; every part may be missing. */\nexport interface EventDimensions {\n project: ProjectRecord | null\n /** The event's branch, when it's a branch of `project` */\n branch: string | null\n /** The file the event was about, relative to the project's root */\n file: { relativePath: string; action: FileAction } | null\n}\n\n/** The project's id, creating it if needed, and records the name and root it was seen under. */\nexport function upsertProject(db: MigrationDatabase, project: ProjectRecord): number {\n const row = db\n .prepare(\n `INSERT INTO project (identity, kind, name) VALUES ($identity, $kind, $name)\n ON CONFLICT (identity) DO UPDATE SET name = COALESCE(excluded.name, project.name)\n RETURNING id`\n )\n .get({ identity: project.identity, kind: project.kind, name: project.name }) as { id: number }\n if (project.name !== null) {\n db.prepare('INSERT OR IGNORE INTO project_alias (project_id, name) VALUES (?, ?)').run(row.id, project.name)\n }\n if (project.root !== null) {\n db.prepare('INSERT OR IGNORE INTO project_path (project_id, path) VALUES (?, ?)').run(row.id, project.root)\n }\n return row.id\n}\n\nexport function upsertBranch(db: MigrationDatabase, projectId: number, name: string): number {\n db.prepare('INSERT OR IGNORE INTO branch (project_id, name) VALUES (?, ?)').run(projectId, name)\n const row = db.prepare('SELECT id FROM branch WHERE project_id = ? AND name = ?').get(projectId, name) as {\n id: number\n }\n return row.id\n}\n\nexport function upsertFile(db: MigrationDatabase, projectId: number, relativePath: string): number {\n db.prepare('INSERT OR IGNORE INTO file (project_id, relative_path) VALUES (?, ?)').run(projectId, relativePath)\n const row = db\n .prepare('SELECT id FROM file WHERE project_id = ? AND relative_path = ?')\n .get(projectId, relativePath) as { id: number }\n return row.id\n}\n\nexport function linkEventFile(\n db: MigrationDatabase,\n link: { eventId: number; fileId: number; action: FileAction }\n): void {\n db.prepare('INSERT OR IGNORE INTO event_file (event_id, file_id, action) VALUES (?, ?, ?)').run(\n link.eventId,\n link.fileId,\n link.action\n )\n}\n\n/** The ids an event was linked to. */\nexport interface EventLinks {\n projectId: number\n branchId: number | null\n}\n\n/** Links a stored event to its project, branch and file. Without a project, nothing is linked (null). */\nexport function writeEventDimensions(\n db: MigrationDatabase,\n eventId: number,\n dimensions: EventDimensions\n): EventLinks | null {\n if (dimensions.project === null) return null\n const projectId = upsertProject(db, dimensions.project)\n const branchId = dimensions.branch === null ? null : upsertBranch(db, projectId, dimensions.branch)\n db.prepare('UPDATE events SET project_id = ?, branch_id = ? WHERE id = ?').run(projectId, branchId, eventId)\n if (dimensions.file !== null) {\n const fileId = upsertFile(db, projectId, dimensions.file.relativePath)\n linkEventFile(db, { eventId, fileId, action: dimensions.file.action })\n }\n return { projectId, branchId }\n}\n","import type { MigrationDatabase } from './migration-types'\n\n// `agent_sessions` (migration 012): the agent conversations hook events came from. `node:sqlite`-free, so tests run\n// it on an in-memory database.\n\n/** Which conversation an agent event came from: the agent, and the id it gives the conversation. */\nexport interface AgentSessionRef {\n source: string\n sessionId: string\n}\n\n/**\n * The session's id, creating it on first sight with the event's folder and time. After that only `last_seen_at`\n * moves, and only forward.\n */\nexport function upsertAgentSession(\n db: MigrationDatabase,\n session: AgentSessionRef & { cwd: string; capturedAt: string }\n): number {\n const row = db\n .prepare(\n `INSERT INTO agent_sessions (source, session_id, cwd, started_at, last_seen_at)\n VALUES ($source, $session_id, $cwd, $captured_at, $captured_at)\n ON CONFLICT (source, session_id) DO UPDATE SET last_seen_at = MAX(last_seen_at, excluded.last_seen_at)\n RETURNING id`\n )\n .get({\n source: session.source,\n session_id: session.sessionId,\n cwd: session.cwd,\n captured_at: session.capturedAt,\n }) as { id: number }\n return row.id\n}\n\n/** What `sessions --json` says about an agent session: never its working folder. */\nexport interface AgentSessionInfo {\n source: string\n sessionId: string\n startedAt: string\n lastSeenAt: string\n}\n\n/** The distinct agent sessions these events came from, oldest first. */\nexport function getAgentSessionsOfEvents(db: MigrationDatabase, eventIds: number[]): AgentSessionInfo[] {\n return db\n .prepare(\n `SELECT source, session_id AS sessionId, started_at AS startedAt, last_seen_at AS lastSeenAt FROM agent_sessions\n WHERE id IN (SELECT agent_session_id FROM events WHERE id IN (SELECT value FROM json_each(?)))\n ORDER BY started_at, id`\n )\n .all(JSON.stringify(eventIds)) as unknown as AgentSessionInfo[]\n}\n\n/** The agent sessions these events belong to — read before deleting them. */\nexport function agentSessionIdsOfEvents(db: MigrationDatabase, eventIds: number[]): number[] {\n const rows = db\n .prepare(\n `SELECT DISTINCT agent_session_id AS id FROM events\n WHERE id IN (SELECT value FROM json_each(?)) AND agent_session_id IS NOT NULL`\n )\n .all(JSON.stringify(eventIds)) as { id: number }[]\n return rows.map((row) => row.id)\n}\n\n/** The agent sessions of a local day's events — read before `clear` deletes them. */\nexport function agentSessionIdsOfDate(db: MigrationDatabase, date: string): number[] {\n const rows = db\n .prepare(\n `SELECT DISTINCT agent_session_id AS id FROM events\n WHERE date(captured_at, 'localtime') = ? AND agent_session_id IS NOT NULL`\n )\n .all(date) as { id: number }[]\n return rows.map((row) => row.id)\n}\n\n/**\n * Deletes those of these sessions no event belongs to any more — after their events were deleted, so `undo` and\n * `clear` leave no conversation id behind. Only these: a session recorded with no event yet is never touched.\n */\nexport function deleteEmptyAgentSessions(db: MigrationDatabase, sessionIds: number[]): void {\n if (sessionIds.length === 0) return\n db.prepare(\n `DELETE FROM agent_sessions\n WHERE id IN (SELECT value FROM json_each(?))\n AND NOT EXISTS (SELECT 1 FROM events WHERE events.agent_session_id = agent_sessions.id)`\n ).run(JSON.stringify(sessionIds))\n}\n","import type { MigrationDatabase } from './migration-types'\n\n// The `ticket` and `ticket_ref` rows (migration 013). Insert-or-ignore throughout, so capture and the `ticket-refs`\n// job can both write them, in any order, any number of times. `node:sqlite`-free, so tests run it in memory.\n\n/** Ticket keys an event names: in its linked branch's name, and in the commits it made. */\nexport interface TicketKeys {\n branch: string[]\n commits: string[]\n}\n\nfunction upsertTicket(db: MigrationDatabase, key: string): number {\n db.prepare('INSERT OR IGNORE INTO ticket (key) VALUES (?)').run(key)\n return (db.prepare('SELECT id FROM ticket WHERE key = ?').get(key) as { id: number }).id\n}\n\n/**\n * Links the keys to the event's branch and to the event itself. Branch keys need the branch the event is linked to:\n * without one (outside a project, or a save in another repo), they link nothing.\n */\nexport function linkEventTickets(\n db: MigrationDatabase,\n link: { eventId: number; branchId: number | null; keys: TicketKeys }\n): void {\n if (link.branchId !== null) {\n const linkBranch = db.prepare('INSERT OR IGNORE INTO ticket_ref (ticket_id, branch_id) VALUES (?, ?)')\n for (const key of link.keys.branch) linkBranch.run(upsertTicket(db, key), link.branchId)\n }\n const linkEvent = db.prepare('INSERT OR IGNORE INTO ticket_ref (ticket_id, event_id) VALUES (?, ?)')\n for (const key of link.keys.commits) linkEvent.run(upsertTicket(db, key), link.eventId)\n}\n\n/** The keys linked to these events, or to the branches they're on — sorted numerically (WOR-9 before WOR-10). */\nexport function ticketsOfEvents(db: MigrationDatabase, eventIds: number[]): string[] {\n const rows = db\n .prepare(\n `WITH chosen AS (SELECT value AS id FROM json_each(?))\n SELECT DISTINCT ticket.key FROM ticket_ref JOIN ticket ON ticket.id = ticket_ref.ticket_id\n WHERE ticket_ref.event_id IN (SELECT id FROM chosen)\n OR ticket_ref.branch_id IN (SELECT branch_id FROM events WHERE id IN (SELECT id FROM chosen))`\n )\n .all(JSON.stringify(eventIds)) as { key: string }[]\n return rows.map((row) => row.key).sort((first, second) => first.localeCompare(second, 'en', { numeric: true }))\n}\n","import type { MigrationDatabase } from './migration-types'\nimport { isDatabaseLocked, withImmediateTransaction } from './transaction'\nimport { writeEventDimensions, type EventDimensions } from './dimensions'\nimport { upsertAgentSession, type AgentSessionRef } from './agent-sessions'\nimport { linkEventTickets, type TicketKeys } from './tickets'\nimport { debugLog } from '../utils/logger'\n\nexport interface NewEvent {\n captured_at: string\n command: string\n cwd: string\n exit_code: number | null\n duration_ms: number | null\n repo_name: string | null\n branch: string | null\n category: string\n metadata: string | null\n source: string | null\n file_path: string | null\n /** What the event links to. The text columns above are stored exactly as given either way */\n dimensions?: EventDimensions\n /** The agent conversation a hook event came from */\n agentSession?: AgentSessionRef\n /** The ticket keys it names, linked along with its project (`ticketKeysOf`) */\n tickets?: TicketKeys\n}\n\nfunction insertEventRow(db: MigrationDatabase, event: NewEvent, agentSessionId: number | null = null): number {\n const { dimensions: _dimensions, agentSession: _agentSession, tickets: _tickets, ...columns } = event\n const result = db\n .prepare(\n `INSERT INTO events (captured_at, command, cwd, exit_code, duration_ms, repo_name, branch, category, metadata, source, file_path, agent_session_id)\n VALUES ($captured_at, $command, $cwd, $exit_code, $duration_ms, $repo_name, $branch, $category, $metadata, $source, $file_path, $agent_session_id)`\n )\n .run({ ...columns, agent_session_id: agentSessionId }) as { lastInsertRowid: number | bigint }\n return Number(result.lastInsertRowid)\n}\n\n/** The event, its agent session, its project links and its tickets, written in order inside the caller's transaction. */\nfunction insertLinkedEvent(db: MigrationDatabase, event: NewEvent): void {\n const agentSessionId = event.agentSession\n ? upsertAgentSession(db, { ...event.agentSession, cwd: event.cwd, capturedAt: event.captured_at })\n : null\n const eventId = insertEventRow(db, event, agentSessionId)\n const links = event.dimensions?.project ? writeEventDimensions(db, eventId, event.dimensions) : null\n if (links && event.tickets) linkEventTickets(db, { eventId, branchId: links.branchId, keys: event.tickets })\n}\n\n/**\n * Stores an event and its links — its project, its tickets and its agent session — in one transaction. If linking fails, the\n * event is stored without links rather than lost. A lock that never came free is thrown, as a plain insert would\n * have been: no second wait. An event with nothing to link is one plain insert.\n */\nexport function insertEventRecord(db: MigrationDatabase, event: NewEvent): void {\n if (!event.dimensions?.project && !event.agentSession) {\n insertEventRow(db, event)\n return\n }\n try {\n withImmediateTransaction(db, () => insertLinkedEvent(db, event))\n } catch (error) {\n if (isDatabaseLocked(error)) throw error\n debugLog('capture: linking the event', error)\n insertEventRow(db, event)\n }\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { StoredEngine } from '../ai/engine'\nimport type { AnchorRange } from '../summary/anchors'\n\n// The `summary` table (migration 010): summaries anchored to the events they describe. `sessions summary set` writes\n// here only; the `summary-anchors` job mapped the old `session_summaries` store into it.\n// `node:sqlite`-free, so tests run it on an in-memory database.\n\n/** `saved`: written by `sessions summary set`. `mapped`: rebuilt by the job from the old store. */\nexport type SummaryOrigin = 'saved' | 'mapped'\nexport const SAVED_ORIGIN: SummaryOrigin = 'saved'\nexport const MAPPED_ORIGIN: SummaryOrigin = 'mapped'\n\n/**\n * Where a summary's range came from: the thread it was saved for, the live old session it was saved from (before\n * WOR-147), the cached old session, the old session regrouped from its day, or nowhere (kept without a range).\n */\nexport type AnchorSource = 'thread' | 'live' | 'cached' | 'regrouped' | 'none'\n\nexport interface SummaryRow {\n id: number\n scope: string\n project_id: number | null\n first_event_id: number | null\n last_event_id: number | null\n engine: StoredEngine\n text: string\n created_at: string\n source_key: string | null\n origin: SummaryOrigin\n anchor_source: AnchorSource\n /** 1 when its session mixed projects, so it matches no thread */\n is_mixed: number\n}\n\n/** A summary of a thread or an old session, by the key it was saved under. */\nexport interface SessionSummaryRecord {\n sourceKey: string\n engine: StoredEngine\n text: string\n createdAt: string\n anchor: AnchorRange & { isMixed: boolean }\n anchorSource: AnchorSource\n}\n\nconst SESSION_SCOPE = 'session'\n\nconst INSERT_SUMMARY = `INSERT INTO summary\n (scope, project_id, first_event_id, last_event_id, engine, text, created_at, source_key, origin, anchor_source, is_mixed)\n VALUES ('${SESSION_SCOPE}', $project_id, $first_event_id, $last_event_id, $engine, $text, $created_at, $source_key,\n $origin, $anchor_source, $is_mixed)\n ON CONFLICT (source_key, engine) DO UPDATE SET\n project_id = excluded.project_id, first_event_id = excluded.first_event_id, last_event_id = excluded.last_event_id,\n text = excluded.text, created_at = excluded.created_at, origin = excluded.origin,\n anchor_source = excluded.anchor_source, is_mixed = excluded.is_mixed`\n\n/** A saved summary replaces whatever row its key and engine had. */\nconst SAVE_SUMMARY = INSERT_SUMMARY\n/** A mapped summary replaces only a row the job wrote itself, never one `sessions summary set` saved. */\nconst MAP_SUMMARY = `${INSERT_SUMMARY} WHERE summary.origin = '${MAPPED_ORIGIN}'`\n\nfunction parametersOf(record: SessionSummaryRecord, origin: SummaryOrigin): Record<string, unknown> {\n return {\n project_id: record.anchor.projectId,\n first_event_id: record.anchor.firstEventId,\n last_event_id: record.anchor.lastEventId,\n engine: record.engine,\n text: record.text,\n created_at: record.createdAt,\n source_key: record.sourceKey,\n origin,\n anchor_source: record.anchorSource,\n is_mixed: record.anchor.isMixed ? 1 : 0,\n }\n}\n\n/**\n * Saves a summary the agent skill wrote — overwriting whatever row its key and engine had, one the job mapped\n * included. Never writes `session_summaries`.\n */\nexport function saveAnchoredSummary(db: MigrationDatabase, record: SessionSummaryRecord): void {\n db.prepare(SAVE_SUMMARY).run(parametersOf(record, SAVED_ORIGIN))\n}\n\n/**\n * The job's write: inserts or rebuilds a mapped row. A row `sessions summary set` saved keeps its range and text —\n * the live session it came from beats anything rebuilt — and only gets its project when it had none.\n */\nexport function mapSummary(db: MigrationDatabase, record: SessionSummaryRecord): void {\n db.prepare(MAP_SUMMARY).run(parametersOf(record, MAPPED_ORIGIN))\n if (record.anchor.projectId === null) return\n db.prepare(\n `UPDATE summary SET project_id = ?\n WHERE source_key = ? AND engine = ? AND origin = '${SAVED_ORIGIN}' AND project_id IS NULL`\n ).run(record.anchor.projectId, record.sourceKey, record.engine)\n}\n\nexport function getSummaryBySource(db: MigrationDatabase, sourceKey: string, engine: string): SummaryRow | null {\n const row = db.prepare('SELECT * FROM summary WHERE source_key = ? AND engine = ?').get(sourceKey, engine) as\n | SummaryRow\n | undefined\n return row ?? null\n}\n","import type { MigrationDatabase } from './migration-types'\n\n// Counts over the last few days of events, for `status` and `profile`. The window starts at an ISO timestamp, the\n// same format `captured_at` is stored in: compared with SQLite's `datetime()` text (\"2026-11-02 10:00:00\"), every\n// event of the boundary day would pass, since 'T' sorts after ' '. `node:sqlite`-free, so tests run it in memory.\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/** The start of the last `days` days, as `captured_at` is stored. */\nexport function sinceIso(days: number, now: number = Date.now()): string {\n return new Date(now - days * MS_PER_DAY).toISOString()\n}\n\nexport function countOtherEventTools(db: MigrationDatabase, since: string): { tool: string; count: number }[] {\n return db\n .prepare(\n `SELECT json_extract(metadata, '$.tool') as tool, COUNT(*) as count\n FROM events\n WHERE category = 'other'\n AND captured_at >= ?\n AND json_extract(metadata, '$.tool') IS NOT NULL\n GROUP BY tool\n ORDER BY count DESC`\n )\n .all(since) as { tool: string; count: number }[]\n}\n\nexport function countOtherEventsSince(db: MigrationDatabase, since: string): { total: number; otherCount: number } {\n return db\n .prepare(\n `SELECT COUNT(*) as total,\n SUM(CASE WHEN category = 'other' THEN 1 ELSE 0 END) as otherCount\n FROM events\n WHERE captured_at >= ?`\n )\n .get(since) as { total: number; otherCount: number }\n}\n","import type { StoredEngine } from '../ai/engine'\n\n/** Engine name for summaries migrated from the 1.x `sessions.summary` column (written by a provider or Ollama). */\nexport const LEGACY_ENGINE = 'legacy' satisfies StoredEngine\n\n/** Engine name for summaries the Workscribe agent skill saved with `sessions summary set`. */\nexport const SKILL_ENGINE = 'skill' satisfies StoredEngine\n\n// Higher wins when picking what to show: richer sources first. Template rows rank 0 — templates are\n// rendered live, so any stored copy is a stale leftover and must never beat a fresh render.\nconst ENGINE_RANK: Record<StoredEngine, number> = { skill: 3, ollama: 2, provider: 2, legacy: 2, template: 0 }\n\n/** Summaries a 1.x AI provider or Ollama wrote. 2.0 no longer writes these, but still shows them for their sessions. */\nconst LEGACY_AI_ENGINES = new Set<string>(['provider', 'ollama', LEGACY_ENGINE])\n\n/** Identifies a session across rebuilds. Must match SESSION_KEY_SQL in migration 005. */\nexport function sessionKey(repoName: string | null, startedAt: string): string {\n return `${repoName ?? ''}|${startedAt}`\n}\n\nexport interface ParsedSessionKey {\n repoName: string | null\n startedAt: string\n}\n\n/** Reverses sessionKey(). Null for a malformed key: no `|`, or a start that isn't a timestamp. */\nexport function parseSessionKey(key: string): ParsedSessionKey | null {\n const separator = key.lastIndexOf('|')\n if (separator === -1) return null\n const startedAt = key.slice(separator + 1)\n if (startedAt === '' || Number.isNaN(new Date(startedAt).getTime())) return null\n const repoName = key.slice(0, separator)\n return { repoName: repoName === '' ? null : repoName, startedAt }\n}\n\n/** What picking a summary reads from a stored one — `summary` rows fit. */\nexport interface RankableSummary {\n engine: string\n text: string\n created_at: string\n}\n\nfunction newestFirst(first: RankableSummary, second: RankableSummary): number {\n return second.created_at.localeCompare(first.created_at)\n}\n\nexport interface SavedSummary {\n text: string\n fromSkill: boolean\n}\n\n/**\n * What `workscribe summary` can show for a session instead of rendering the template: a summary the agent\n * skill saved wins (it was written with more context), else one a 1.x AI provider wrote for that session.\n */\nexport function pickSavedSummary(rows: RankableSummary[]): SavedSummary | null {\n const [newestSkill] = rows.filter((row) => row.engine === SKILL_ENGINE).sort(newestFirst)\n if (newestSkill) return { text: newestSkill.text, fromSkill: true }\n const [newestLegacy] = rows.filter((row) => LEGACY_AI_ENGINES.has(row.engine)).sort(newestFirst)\n return newestLegacy ? { text: newestLegacy.text, fromSkill: false } : null\n}\n\n/** The best stored summary for a session regardless of the configured engine (used by `context`). */\nexport function pickBestSummary(rows: RankableSummary[]): string | null {\n // Rows come from the DB, so an engine name this build doesn't know is ignored like a template row\n const rank = (row: RankableSummary): number => ENGINE_RANK[row.engine as StoredEngine] ?? 0\n const [best] = rows\n .filter((row) => rank(row) > 0)\n .sort((first, second) => rank(second) - rank(first) || newestFirst(first, second))\n return best?.text ?? null\n}\n","import type { EventRow } from '../db/types'\nimport { parseSessionKey } from './store'\n\n// Which events a summary describes, and which thread it belongs to (pure). A summary is anchored to a project and\n// the first and last events of its range; the thread it attaches to is worked out when it's read, since threads are\n// rebuilt whenever a day changes.\n\n/** The `anchor_source` of a summary `sessions summary set` saved for a thread. */\nexport const THREAD_ANCHOR_SOURCE = 'thread'\n\n/** A summary belongs to a project only when that project holds more than this share of its events. */\nexport const MAJORITY_SHARE = 0.5\n\n/** What a stored summary is anchored to. */\nexport interface AnchorRange {\n /** Null for \"elsewhere\", or when the events aren't linked to projects yet */\n projectId: number | null\n firstEventId: number | null\n lastEventId: number | null\n}\n\nexport interface SummaryAnchor extends AnchorRange {\n /** How much of the range the project holds, 0–1 */\n share: number\n /** The range mixed projects: the project held half of it or less, so the summary may describe other work */\n isMixed: boolean\n /** The key names a repo, but none of its events is linked to a project yet */\n isUnlinked: boolean\n}\n\nconst UNANCHORED: SummaryAnchor = {\n projectId: null,\n firstEventId: null,\n lastEventId: null,\n share: 0,\n isMixed: false,\n isUnlinked: false,\n}\n\nfunction inSessionOrder(events: EventRow[]): EventRow[] {\n return [...events].sort(\n (first, second) => first.captured_at.localeCompare(second.captured_at) || first.id - second.id\n )\n}\n\n/** The project most of these events link to; a tie goes to the lowest id. Null when none is linked. */\nfunction mostLinkedProject(events: EventRow[]): number | null {\n const counts = new Map<number, number>()\n for (const event of events) {\n const projectId = event.project_id ?? null\n if (projectId === null) continue\n counts.set(projectId, (counts.get(projectId) ?? 0) + 1)\n }\n const [best] = [...counts.entries()].sort(\n ([firstId, firstCount], [secondId, secondCount]) => secondCount - firstCount || firstId - secondId\n )\n return best?.[0] ?? null\n}\n\n/**\n * The anchor of an old session's summary: its first and last events in session order, and the project most of\n * its events named by the key link to (null for a key with no repo name: \"elsewhere\").\n */\nexport function anchorOfSession(events: EventRow[], keyRepoName: string | null): SummaryAnchor {\n if (events.length === 0) return UNANCHORED\n const ordered = inSessionOrder(events)\n const range = { firstEventId: ordered[0].id, lastEventId: ordered[ordered.length - 1].id }\n const projectId =\n keyRepoName === null ? null : mostLinkedProject(ordered.filter((event) => event.repo_name === keyRepoName))\n if (keyRepoName !== null && projectId === null) {\n return { ...UNANCHORED, ...range, isUnlinked: true }\n }\n const held = ordered.filter((event) => (event.project_id ?? null) === projectId).length\n const share = held / ordered.length\n return { ...range, projectId, share, isMixed: share <= MAJORITY_SHARE, isUnlinked: false }\n}\n\n/** The anchor of the session a `repo|started_at` key names, from that session's events. */\nexport function anchorOfKeyedSession(events: EventRow[], key: string): SummaryAnchor {\n return anchorOfSession(events, parseSessionKey(key)?.repoName ?? null)\n}\n\n/** A thread's anchor: one project's events, so never mixed. */\nexport interface ThreadAnchor extends AnchorRange {\n isMixed: false\n}\n\n/** The anchor of a summary saved for a thread: its project, and its first and last events in grouping order. */\nexport function anchorOfThread(events: EventRow[], projectId: number | null): ThreadAnchor {\n return {\n projectId,\n firstEventId: events[0]?.id ?? null,\n lastEventId: events.at(-1)?.id ?? null,\n isMixed: false,\n }\n}\n\n/** What attaching a stored summary to a thread reads. */\nexport interface StoredAnchor extends AnchorRange {\n /** Saved for a thread by `sessions summary set`, rather than for an old session */\n isThreadSaved: boolean\n}\n\nexport interface BlockEvent {\n id: number\n threadId: number\n}\n\nexport interface CandidateThread {\n id: number\n projectId: number | null\n}\n\n/**\n * The thread a summary belongs to, in the block holding its first event: its project's thread, when that thread\n * holds more than half of the block's events from the first event to the last (or to the block's end, if the last\n * is outside it). A summary saved for a thread needs only its first event to still be in that thread: a small\n * thread worked in between a busier project's events never holds most of its own range. Null for an unanchored\n * summary, a day with no threads built, or a range that mixed projects.\n */\nexport function findSummaryThread(\n anchor: StoredAnchor,\n block: { threads: CandidateThread[]; events: BlockEvent[] }\n): CandidateThread | null {\n const start = block.events.findIndex((event) => event.id === anchor.firstEventId)\n if (start === -1) return null\n const thread = block.threads.find((candidate) => candidate.projectId === anchor.projectId)\n if (!thread) return null\n if (anchor.isThreadSaved) return block.events[start].threadId === thread.id ? thread : null\n const end = block.events.findIndex((event) => event.id === anchor.lastEventId)\n const rangeEvents = block.events.slice(start, end === -1 ? block.events.length : end + 1)\n const held = rangeEvents.filter((event) => event.threadId === thread.id).length\n return held / rangeEvents.length > MAJORITY_SHARE ? thread : null\n}\n\n/** What attaching a stored summary reads from its row — `summary` rows fit. */\nexport interface AttachableSummary {\n project_id: number | null\n first_event_id: number | null\n last_event_id: number | null\n anchor_source: string\n}\n\n/** The stored summaries that belong to this thread of the block, by `findSummaryThread`. */\nexport function summariesOfThread<Summary extends AttachableSummary>(\n threadId: number,\n block: { threads: CandidateThread[]; events: BlockEvent[] },\n summaries: Summary[]\n): Summary[] {\n return summaries.filter((summary) => {\n const anchor = {\n projectId: summary.project_id,\n firstEventId: summary.first_event_id,\n lastEventId: summary.last_event_id,\n isThreadSaved: summary.anchor_source === THREAD_ANCHOR_SOURCE,\n }\n return findSummaryThread(anchor, block)?.id === threadId\n })\n}\n","import { DatabaseSync } from 'node:sqlite'\nimport fs from 'fs'\nimport { WORKSCRIBE_DIR, DB_PATH } from '../utils/paths'\nimport { ensurePrivateDirectory } from '../utils/private-directory'\nimport type { EventRow } from './types'\nimport { MIGRATIONS } from './migrations'\nimport { applyMigrations } from './migrate'\nimport { BUSY_TIMEOUT_MS, withImmediateTransaction, type TransactionOptions } from './transaction'\nimport type { StoredEngine } from '../ai/engine'\nimport { insertEventRecord, type NewEvent } from './event-writer'\nimport { saveAnchoredSummary } from './summary-store'\nimport { countOtherEventsSince, countOtherEventTools, sinceIso } from './recent-events'\nimport { agentSessionIdsOfDate, agentSessionIdsOfEvents, deleteEmptyAgentSessions } from './agent-sessions'\nimport { THREAD_ANCHOR_SOURCE, type ThreadAnchor } from '../summary/anchors'\nexport type { EventRow } from './types'\n\nlet _db: DatabaseSync | null = null\n\nexport function getDb(): DatabaseSync {\n if (_db) return _db\n\n // The data folder exists and is private before SQLite creates anything in it\n try {\n ensurePrivateDirectory(WORKSCRIBE_DIR)\n } catch (error) {\n // Rethrown as a clean, actionable message — callers reaching this\n // (explicit commands, or _capture/_hook's own try/catch) otherwise\n // only see a raw native stack trace with the real cause buried in it.\n throw new Error(`Could not create data directory ${WORKSCRIBE_DIR}: ${(error as Error).message}`)\n }\n\n // Don't assign to _db until after migrations succeed — if applyMigrations\n // throws, _db stays null so the next call retries cleanly instead of\n // returning a partially-initialised singleton.\n const db = new DatabaseSync(DB_PATH)\n try {\n fs.chmodSync(DB_PATH, 0o600)\n } catch {\n /* ignore if already correct */\n }\n db.exec('PRAGMA journal_mode = WAL')\n db.exec('PRAGMA foreign_keys = ON')\n // Parallel captures (an agent running several commands at once) wait for the write lock instead of failing\n db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`)\n applyMigrations(db, MIGRATIONS)\n _db = db\n return _db\n}\n\n// ─── Typed query helpers ──────────────────────────────────────────────────────\n\nexport function insertEvent(event: NewEvent): void {\n insertEventRecord(getDb(), event)\n}\n\nexport function getLastEventForFile(filePath: string, cwd: string): EventRow | undefined {\n const db = getDb()\n return db\n .prepare('SELECT * FROM events WHERE file_path = $file_path AND cwd = $cwd ORDER BY id DESC LIMIT 1')\n .get({ file_path: filePath, cwd }) as EventRow | undefined\n}\n\nexport function getLastEvent(): EventRow | undefined {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY id DESC LIMIT 1')\n return stmt.get() as EventRow | undefined\n}\n\nexport function getLastEventForCwd(cwd: string): EventRow | undefined {\n const db = getDb()\n return db.prepare('SELECT * FROM events WHERE cwd = $cwd ORDER BY id DESC LIMIT 1').get({ cwd }) as\n | EventRow\n | undefined\n}\n\nexport function getEventsForDate(date: string): EventRow[] {\n const db = getDb()\n const stmt = db.prepare(`\n SELECT * FROM events\n WHERE date(captured_at, 'localtime') = $date\n ORDER BY captured_at ASC, id ASC\n `)\n return stmt.all({ date }) as unknown as EventRow[]\n}\n\n/** Runs `work` holding the database's write lock — see `withImmediateTransaction`. */\nexport function runInImmediateTransaction<T>(work: () => T, options?: TransactionOptions): T {\n return withImmediateTransaction(getDb(), work, options)\n}\n\nexport function getRecentEvents(limit = 20): EventRow[] {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY captured_at DESC LIMIT $limit')\n return stmt.all({ limit }) as unknown as EventRow[]\n}\n\nexport function getEventCount(): number {\n const db = getDb()\n const stmt = db.prepare('SELECT COUNT(*) as count FROM events')\n const row = stmt.get() as { count: number }\n return row.count\n}\n\n/** Saves a thread's summary to `summary`, anchored to the thread's events, under the thread's key. */\nexport function saveThreadSummary(\n threadKey: string,\n summary: { engine: StoredEngine; text: string; anchor: ThreadAnchor }\n): void {\n saveAnchoredSummary(getDb(), {\n sourceKey: threadKey,\n engine: summary.engine,\n text: summary.text,\n createdAt: new Date().toISOString(),\n anchor: summary.anchor,\n anchorSource: THREAD_ANCHOR_SOURCE,\n })\n}\n\nexport function getLastEventIds(n: number): number[] {\n const db = getDb()\n const rows = db.prepare('SELECT id FROM events ORDER BY id DESC LIMIT $n').all({ n }) as { id: number }[]\n return rows.map((row) => row.id)\n}\n\nexport function getEventsByIds(ids: number[]): EventRow[] {\n if (ids.length === 0) return []\n const db = getDb()\n const placeholders = ids.map(() => '?').join(', ')\n return db\n .prepare(`SELECT * FROM events WHERE id IN (${placeholders}) ORDER BY id DESC`)\n .all(...ids) as unknown as EventRow[]\n}\n\n// The old sessions' links to events (migration 001), until the drop-old-sessions data job removes them: their foreign\n// key would refuse to delete an event they point at, so undo and clear remove those links first\nconst OLD_SESSION_LINKS = 'session_events'\n\nfunction hasOldSessionLinks(db: DatabaseSync): boolean {\n return (\n db.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?\").get(OLD_SESSION_LINKS) !== undefined\n )\n}\n\nexport function deleteEventsByIds(ids: number[]): void {\n if (ids.length === 0) return\n const db = getDb()\n const placeholders = ids.map(() => '?').join(', ')\n\n runInImmediateTransaction(() => {\n const agentSessionIds = agentSessionIdsOfEvents(db, ids)\n if (hasOldSessionLinks(db)) {\n db.prepare(`DELETE FROM ${OLD_SESSION_LINKS} WHERE event_id IN (${placeholders})`).run(...ids)\n }\n db.prepare(`DELETE FROM events WHERE id IN (${placeholders})`).run(...ids)\n deleteEmptyAgentSessions(db, agentSessionIds)\n })\n}\n\nexport function deleteEventsForDate(date: string): number {\n const db = getDb()\n return runInImmediateTransaction(() => {\n const agentSessionIds = agentSessionIdsOfDate(db, date)\n if (hasOldSessionLinks(db)) {\n db.prepare(\n `DELETE FROM ${OLD_SESSION_LINKS}\n WHERE event_id IN (SELECT id FROM events WHERE date(captured_at, 'localtime') = $date)`\n ).run({ date })\n }\n const result = db.prepare(`DELETE FROM events WHERE date(captured_at, 'localtime') = $date`).run({ date }) as {\n changes: number\n }\n deleteEmptyAgentSessions(db, agentSessionIds)\n return result.changes\n })\n}\n\nexport function getOtherEventToolCounts(days: number): { tool: string; count: number }[] {\n return countOtherEventTools(getDb(), sinceIso(days))\n}\n\nexport function getRecentOtherRate(days: number): { total: number; otherCount: number } {\n return countOtherEventsSince(getDb(), sinceIso(days))\n}\n","const QUOTED = String.raw`\"[^\"]*\"|'[^']*'`\n// Quotes, brackets and shell separators stay in a word only with more of the word after it, so `\"add --pass option\"`\n// keeps its closing quote and `mysql -pX; git push` its `;`, while `--password=a&b` loses all of its value\nconst WORD_REST = String.raw`(?:[^\\s\"');&|]|[\"');&|](?=[^\\s;&|)]))*`\n// A flag's value. A leading `-` is the next flag, not a value (`mongosh -p --tls`).\nconst VALUE = String.raw`(?:${QUOTED}|[^\\s\"'\\-]${WORD_REST})`\nconst VALUE_OR_DASHED = String.raw`(?:${QUOTED}|[^\\s\"']${WORD_REST})`\n// The limits keep a very long line fast, as these patterns run inside lookbehinds. A quoted string counts as one part\nconst MAX_COMMAND_PARTS = 1000\nconst MAX_QUOTED_LENGTH = 64_000\nconst MAX_PARTS_BEFORE_LOGIN = 200\nconst MAX_PARTS_AFTER_LOGIN = 300\n// One simple command's next parts: a separator in quotes doesn't end it, and neither does a line continuation. Quotes\n// that don't pair, as in `-u o\\'brien`, are read as plain characters instead, so they can't hide the flag after them\nfunction sameCommand(maxParts: number): string {\n const quoted = String.raw`\"[^\"]{0,${MAX_QUOTED_LENGTH}}\"|'[^']{0,${MAX_QUOTED_LENGTH}}'`\n return String.raw`(?:(?:${quoted}|\\\\\\n|[^|;&\\n\"']){0,${maxParts}}?|[^|;&\\n]{0,${maxParts}}?)`\n}\nconst SAME_COMMAND = sameCommand(MAX_COMMAND_PARTS)\n// Tools are found by leaving out what can't be one, not by listing where one can be: a form nobody listed (`timeout 30\n// mysql -pX`) must cost a lost word at worst, never a stored password. So not `a.zip` or `mongo:7`, the container in\n// `--name mongo`, the host in `ssh mongo -p 22`, or a word in a message: `git commit -m \"fix mongo -p flag\"`\nconst NOT_PART_OF_A_WORD = String.raw`(?<![\\w.:@-])`\nconst NOT_A_NAME = String.raw`(?<!--name(?:\\s+|=)|\\bssh\\s+)`\nconst NOT_IN_A_MESSAGE = String.raw`(?<!\\s(?:-[as]*m|--message|--title|--body|--subject)[\\s=]*[\"'][^\"'\\n]{0,${MAX_COMMAND_PARTS}})`\n// A lookbehind runs right to left, so the slow check goes first here and runs last\nconst TOOL_WORD_START = `${NOT_IN_A_MESSAGE}${NOT_A_NAME}${NOT_PART_OF_A_WORD}`\nconst TOOL_WORD_END = String.raw`(?:\\.exe)?[\"'\\x60)]?(?=\\s)`\n// `-p 27017:27017` publishes a port. A bare number stays a possible password: `mongosh -p 12345`\nconst NOT_A_PORT_MAPPING = String.raw`(?!(?:[\\d.]+:)?\\d+:\\d+(?:\\/\\w+)?(?=\\s|$))`\n\nconst LONG_SECRET_FLAGS = [\n 'password',\n '(?!no-)[\\\\w-]*-password',\n 'passwd',\n 'passphrase',\n 'pass',\n 'pwd',\n 'secret(?:-key)?',\n 'client[-_]secret',\n 'token',\n 'access-token',\n 'refresh-token',\n 'auth[-_]?token',\n 'private-key',\n 'api[-_]?key',\n].join('|')\n// After a long flag, `=` always introduces a value, and a space does unless the next flag follows\nconst LONG_FLAG_VALUE_START = String.raw`(?:=|\\s+(?!--))`\n// `sort --key=2,3` and `--key certs/server.key` name a field and a file, not a secret\nconst MAX_SORT_FIELD_DIGITS = 2\nconst SORT_FIELD = String.raw`\\d{1,${MAX_SORT_FIELD_DIGITS}}`\nconst NOT_A_KEY = String.raw`(?:${SORT_FIELD}(?:[.,]${SORT_FIELD})*|[\\w./~-]*\\.(?:key|pem|crt|p12|pfx|der)|[\\w./~-]*\\/id_\\w+)(?=\\s|$)`\n// A branch, not a key: lowercase words, none of them long. A real key has a long run or mixed case\nconst MAX_SLUG_WORD_LENGTH = 15\nconst MAX_SLUG_WORDS_AFTER_FIRST = 8\nconst SLUG_WORD = `[a-z0-9]{1,${MAX_SLUG_WORD_LENGTH}}`\nconst UUID_IN_A_KEY = String.raw`[a-z-]{0,20}[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`\nconst SLUG_AFTER_SK = String.raw`(?!${UUID_IN_A_KEY})${SLUG_WORD}(?:-${SLUG_WORD}){0,${MAX_SLUG_WORDS_AFTER_FIRST}}(?![\\w-])`\nconst CONTAINER_LOGIN_TOOLS = 'docker|podman|podman-remote|nerdctl|helm'\nconst EVERYDAY_HEADERS = 'api-key|cookie|set-cookie'\n// Not everyday words, unlike `cookie`, so they count anywhere: `http x.io X-Api-Key:SECRET`, `HDR=\"X-Api-Key: SECRET\"`\nconst DISTINCTIVE_HEADERS = 'x-api-key|x-auth-token|x-access-token'\nconst HEADER_FLAG = String.raw`(?<![\\w-])(?:-\\w*H|--(?:proxy-)?header)[\\s=]*`\n\n/** Matches the value that follows `prefix`, leaving the prefix in place. */\nfunction valueAfter(prefix: string, flags = 'g'): RegExp {\n return new RegExp(`(?<=${prefix})${VALUE}`, flags)\n}\n\n/** Matches a short flag's value, when the tool is a word of the same command. */\nfunction toolFlagValue(tools: string, flag: string): RegExp {\n return valueAfter(String.raw`${TOOL_WORD_START}(?:${tools})${TOOL_WORD_END}${SAME_COMMAND}\\s${flag}`)\n}\n\n/** Matches what follows `name` in `\"name:value\"` and `'name:value'`. Each stops where its quoting does. */\nfunction quotedValuesAfterName(prefix: string, name: string, flags = 'g'): RegExp[] {\n return [\n new RegExp(String.raw`(?<=${prefix}\"${name})[^\"\\s][^\"\\n]*`, flags),\n new RegExp(String.raw`(?<=${prefix}'${name})[^'\\s][^'\\n]*`, flags),\n ]\n}\n\n/** Matches what follows `name` in `name:value`, in each way a shell can quote it. */\nfunction valuesAfterName(prefix: string, name: string, flags = 'g'): RegExp[] {\n return [\n ...quotedValuesAfterName(prefix, name, flags),\n new RegExp(String.raw`(?<=${prefix}${name})(?:${QUOTED})`, flags),\n new RegExp(String.raw`(?<=${prefix}${name})[^\\s\"'|;&]+`, flags),\n ]\n}\n\nconst SENSITIVE_VARIABLE_WORDS = 'TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL|_PWD'\nconst QUERY_SECRET_NAME = String.raw`key|[\\w-]*(?:token|secret|password|passwd|pwd|api[-_]?key)`\nconst JSON_SECRET_NAME = String.raw`[\\w-]*(?:token|secret|password|passwd|pwd|api[-_]?key)`\n\n// Patterns that match known sensitive values\nconst BUILTIN_PATTERNS: RegExp[] = [\n // AWS access keys\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access keys (40 char base64-ish after common env vars)\n new RegExp(String.raw`(?<=AWS_SECRET_ACCESS_KEY[=\\s]+)${VALUE_OR_DASHED}`, 'gi'),\n // GitHub tokens\n /\\bgh[pousr]_[a-zA-Z0-9]{36}/g,\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // AI provider, Google and npm keys\n new RegExp(String.raw`\\bsk-(?!${SLUG_AFTER_SK})[\\w-]{20,}`, 'g'),\n /\\bAIza[a-zA-Z0-9_-]{30,}/g,\n /\\bnpm_[a-zA-Z0-9]{30,}/g,\n // GitLab tokens\n /glpat-[a-zA-Z0-9\\-]{20}/g,\n // Generic JWTs (three base64url segments)\n /eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n // Slack tokens and incoming webhook URLs\n /xox[baprs]-[a-zA-Z0-9\\-]+/g,\n /https:\\/\\/hooks\\.slack\\.com\\/services\\/[A-Z0-9]+\\/[A-Z0-9]+\\/[a-zA-Z0-9]+/g,\n // Stripe keys\n /sk_(live|test)_[a-zA-Z0-9]{24,}/g,\n // Values after long-form secret flags, `--flag value` or `--flag=value`. `--password-stdin` isn't one: no match on its\n // `-stdin`. A bare short flag means nothing without knowing the tool: see below\n new RegExp(String.raw`(?<=(?<![\\w-])--(?:${LONG_SECRET_FLAGS})${LONG_FLAG_VALUE_START})${VALUE_OR_DASHED}`, 'gi'),\n new RegExp(String.raw`(?<=(?<![\\w-])--key${LONG_FLAG_VALUE_START})(?!${NOT_A_KEY})${VALUE_OR_DASHED}`, 'gi'),\n // A password attached to the flag: `mysql -pSECRET`. `mysql -p db` prompts, so there's no value to remove, and\n // `psql -p 5432` is a port, so psql isn't listed\n toolFlagValue(\n 'mysql|mysqldump|mysqladmin|mysqlimport|mysqlshow|mysqlcheck|mariadb|mariadb-dump|mariadb-admin|mariadb-import|' +\n 'mariadb-show|mariadb-check',\n '-p'\n ),\n // Short flags that take a secret, for the tools where they do\n toolFlagValue('sshpass', String.raw`-p\\s*`),\n toolFlagValue('mongosh|mongo|mongodump|mongorestore|mongoexport|mongoimport', String.raw`-p\\s*${NOT_A_PORT_MAPPING}`),\n toolFlagValue('redis-cli', String.raw`-a\\s*`),\n toolFlagValue('ssh-keygen', String.raw`-[NP]\\s*`),\n toolFlagValue('zip|unzip', String.raw`-P\\s*`),\n valueAfter(\n String.raw`${TOOL_WORD_START}(?:${CONTAINER_LOGIN_TOOLS})${TOOL_WORD_END}[^|;&\\n]{0,${MAX_PARTS_BEFORE_LOGIN}}?\\slogin` +\n String.raw`${TOOL_WORD_END}${sameCommand(MAX_PARTS_AFTER_LOGIN)}\\s-p\\s*${NOT_A_PORT_MAPPING}`\n ),\n // openssl's `-pass pass:SECRET`, also -passin and -passout\n /(?<=\\s-pass(?:in|out|word)?\\s+pass:)\\S+/g,\n // curl's cookies, which also covers a cookie file's name: `curl -b s=SECRET`\n valueAfter(String.raw`\\bcurl\\b[\"']?${SAME_COMMAND}\\s(?:--cookie|-\\w*b)[\\s=]+`),\n // The password half of `curl -u user:password`\n ...valuesAfterName(String.raw`\\bcurl\\b[\"']?${SAME_COMMAND}\\s(?:-\\w*u[\\s=]*|--user[\\s=]+)`, String.raw`[^:\\s\"']*:`),\n // Tokens handed over on stdin: `echo SECRET | docker login --password-stdin`, `gh auth login --with-token <<< SECRET`\n new RegExp(\n String.raw`(?<=\\b(?:echo(?:\\s+-[neE]+)*|printf(?:\\s+[\"']?%s(?:\\\\n)?[\"']?)?)\\s+)${VALUE}` +\n String.raw`(?=\\s*\\|[^;&\\n]{0,${MAX_COMMAND_PARTS}}?--(?:password-stdin|with-token))`,\n 'g'\n ),\n valueAfter(String.raw`--(?:password-stdin|with-token)\\s*<<<\\s*`),\n // Headers. A scheme or a header flag is required for Authorization, so prose like \"Authorization: added middleware\"\n // stays. An everyday word needs to look like a header, so `git commit -m \"cookie: rotate on login\"` stays: a header\n // flag, `Cookie:value` with no space, or a quoted `\"Cookie: value\"` outside a message\n /(?<=\\bauthorization:\\s*(?:bearer|basic|token)\\s+)[^\\s\"']+/gi,\n /(?<=\\bbearer\\s+)(?=[A-Za-z0-9._~+/=-]*\\d)[A-Za-z0-9._~+/=-]{8,}/gi,\n ...valuesAfterName(HEADER_FLAG, String.raw`(?:${EVERYDAY_HEADERS}):\\s*`, 'gi'),\n ...valuesAfterName(HEADER_FLAG, String.raw`authorization:\\s*(?!(?:bearer|basic|token)\\s)`, 'gi'),\n ...quotedValuesAfterName('', String.raw`${NOT_IN_A_MESSAGE}(?:${EVERYDAY_HEADERS}):\\s*`, 'gi'),\n ...valuesAfterName('', String.raw`(?<![\\w-])(?:${EVERYDAY_HEADERS}):`, 'gi'),\n ...valuesAfterName('', String.raw`\\b(?:${DISTINCTIVE_HEADERS}):\\s*`, 'gi'),\n // Private keys, whole blocks, as heredocs and `echo` can carry them\n /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,\n // password=... inside a string or query: `psql \"host=x password=SECRET\"`, `--set db.password=SECRET`\n /(?<=(?:^|[\\s\"'?&=.])(?:password|passwd|pwd)=)[^\\s\"';&]+/gi,\n // A secret in a query string or in JSON: `curl \"x.io?token=SECRET\"`, `curl -d '{\"password\":\"SECRET\"}'`\n new RegExp(String.raw`(?<=[?&](?:${QUERY_SECRET_NAME})=)[^\\s\"'&#]+`, 'gi'),\n new RegExp(String.raw`(?<=[\"'](?:${JSON_SECRET_NAME})\\\\?[\"']\\s*:\\s*\\\\?\")[^\"\\\\\\n]+`, 'gi'),\n // npm's auth settings, with or without `=`: `npm config set //registry/:_password SECRET`\n /(?<=:_(?:password|auth|authToken)[=\\s]+)\\S+/g,\n // export VAR=VALUE where VAR looks sensitive\n new RegExp(String.raw`(?<=export\\s+(?:[A-Z_]*(?:${SENSITIVE_VARIABLE_WORDS})[A-Z_]*)=)${VALUE_OR_DASHED}`, 'gi'),\n // Inline assignment: VAR_WITH_SENSITIVE_KEYWORD=value cmd (e.g. API_KEY=secret npm start)\n // Lookbehind matches only the variable-name= prefix, so only the value is replaced\n new RegExp(String.raw`(?<=[A-Z_][A-Z0-9_]*(?:${SENSITIVE_VARIABLE_WORDS})[A-Z0-9_]*=)${VALUE_OR_DASHED}`, 'gi'),\n // The same with nothing before the keyword: `TOKEN=SECRET npm start`. Capitals only, so `--author=me` and `key=value` stay\n new RegExp(\n String.raw`(?<=(?:^|[\\s;&|(])(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIALS?)=)${VALUE_OR_DASHED}`,\n 'g'\n ),\n // Credentials embedded in connection string URLs (e.g. DATABASE_URL=postgres://user:pass@host/db)\n // Matches the password segment between :// user : password @ — preserves scheme and host\n /(?<=[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/[^:@\\s/]*:)[^@\\s]+(?=@)/g,\n // A token as the whole userinfo of an https URL: `git clone https://TOKEN@github.com/org/repo`\n /(?<=https?:\\/\\/)[^\\s:@/?#]+(?=@)/g,\n]\n\nexport const REDACTED_TEXT = '[REDACTED]'\n\nexport function redact(command: string, extraPatterns: string[] = []): string {\n let result = command\n\n const allPatterns = [...BUILTIN_PATTERNS]\n\n for (const pattern of extraPatterns) {\n try {\n allPatterns.push(new RegExp(pattern, 'g'))\n } catch {\n // Invalid regex — skip silently\n }\n }\n\n for (const pattern of allPatterns) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0\n result = result.replace(pattern, REDACTED_TEXT)\n }\n\n return result\n}\n","const HINT_LENGTH = 120\n\n/** What's kept of an unclassified command so a summary can describe it. Takes the command already redacted. */\nexport function commandHint(safeCommand: string): string {\n return safeCommand.trim().slice(0, HINT_LENGTH)\n}\n","// How a repo's `origin` remote becomes a repo name, in both forms `git.qualifyRepoName` chooses between. Pure.\n\n/** The owner and repo parsed from an `origin` remote URL. */\nexport interface RemoteRepo {\n owner: string\n repo: string\n}\n\n// Handles SSH (git@host:owner/repo.git), HTTPS (https://host/owner/repo.git),\n// and bare git:// / ssh:// forms uniformly by normalizing the SSH scp-like\n// syntax's first ':' to '/' (but not the '://' in URLs with a real scheme),\n// then splitting on '/'.\nexport function parseOwnerAndRepo(url: string): RemoteRepo | null {\n const normalized = url.trim().replace(/^[^/]+:(?!\\/\\/)/, (match) => match.replace(':', '/'))\n const segments = normalized.split('/').filter(Boolean)\n if (segments.length < 2) return null\n const repo = segments[segments.length - 1].replace(/\\.git$/, '')\n const owner = segments[segments.length - 2]\n return owner && repo ? { owner, repo } : null\n}\n\n/** `owner/repo` when qualified, bare `repo` otherwise. */\nexport function formatRepoName(remote: RemoteRepo, qualify: boolean): string {\n return qualify ? `${remote.owner}/${remote.repo}` : remote.repo\n}\n","// What makes two events the same project, whatever the repo is called. Pure: shared by capture and the backfill job.\n\n/** `remote`: keyed by the canonical `origin` remote. `local`: no remote, keyed by the git folder. `name`: only a\n * stored name was left to go on (a backfilled event whose folder is gone). */\nexport type ProjectKind = 'remote' | 'local' | 'name'\n\nexport interface ProjectIdentity {\n identity: string\n kind: ProjectKind\n}\n\nconst SCHEME_URL = /^([a-z][a-z0-9+.-]*):\\/\\/(.*)$/i\n// `user@host:path`, git's scp-like form. The host can't hold a `/`, so a local path never matches\nconst SCP_LIKE = /^(?:[^@/]+@)?([^:/]+):(.+)$/\n\nfunction hostAndPath(url: string): { host: string; path: string } | null {\n const schemeMatch = SCHEME_URL.exec(url)\n if (schemeMatch) {\n if (schemeMatch[1].toLowerCase() === 'file') return null\n const rest = schemeMatch[2].replace(/^[^@/]*@/, '')\n const slash = rest.indexOf('/')\n if (slash <= 0) return null\n return { host: rest.slice(0, slash).replace(/:\\d*$/, ''), path: rest.slice(slash + 1) }\n }\n const scpMatch = SCP_LIKE.exec(url)\n return scpMatch ? { host: scpMatch[1], path: scpMatch[2] } : null\n}\n\n/**\n * The remote URL with any user name and password or token removed (`https://user:token@host/…` → `https://host/…`),\n * so nothing stored can hold a credential. The scp-like form (`git@host:path`) can't carry one and is kept as is.\n */\nexport function withoutCredentials(url: string): string {\n const schemeMatch = SCHEME_URL.exec(url.trim())\n if (!schemeMatch) return url.trim()\n const [, scheme, rest] = schemeMatch\n const slash = rest.indexOf('/')\n const authority = slash === -1 ? rest : rest.slice(0, slash)\n const host = authority.slice(authority.lastIndexOf('@') + 1)\n return `${scheme}://${host}${slash === -1 ? '' : rest.slice(slash)}`\n}\n\n/**\n * `host/owner/repo` for any form of the same remote — SSH, scp-like, HTTPS, `git://` — with the user, port and `.git`\n * dropped and every group kept (`gitlab.com/group/sub/repo`). Lower-cased: hosts and the common forges ignore case.\n * Null for a remote with no host, such as a local path.\n */\nexport function canonicalRemote(url: string): string | null {\n const parts = hostAndPath(url.trim())\n if (!parts) return null\n const repoPath = parts.path\n .replace(/^\\/+|\\/+$/g, '')\n .replace(/\\.git$/i, '')\n .replace(/\\/+$/, '')\n if (!parts.host || !repoPath) return null\n return `${parts.host}/${repoPath}`.toLowerCase()\n}\n\n/** The project a repo belongs to: its remote when it has one, else its git folder, which all its worktrees share. */\nexport function projectIdentity(repo: { remoteUrl: string | null; commonDir: string | null }): ProjectIdentity | null {\n const remote = repo.remoteUrl === null ? null : canonicalRemote(repo.remoteUrl)\n if (remote !== null) return { identity: `remote:${remote}`, kind: 'remote' }\n if (repo.commonDir !== null) return { identity: `local:${repo.commonDir}`, kind: 'local' }\n return null\n}\n\n/** The project for a stored name alone, used only when nothing else is left to go on. */\nexport function nameIdentity(name: string): ProjectIdentity {\n return { identity: `name:${name.toLowerCase()}`, kind: 'name' }\n}\n","import { execSync } from 'child_process'\nimport path from 'path'\nimport fs from 'fs'\nimport type { WorkscribeConfig } from '../config'\nimport { formatRepoName, parseOwnerAndRepo, type RemoteRepo } from './repo-name'\nimport { projectIdentity, withoutCredentials, type ProjectIdentity } from './project-identity'\n\n/** Everything known about the repo at a path — `root` and `remote` let `context` find the other name form too. */\nexport interface RepoContext {\n repoName: string | null\n branch: string | null\n root: string | null\n /** The `origin` remote, set only when `repoName` came from it (not an override, package.json or the folder) */\n remote: RemoteRepo | null\n /** The project the repo belongs to, however it's named — null outside a repo */\n project: ProjectIdentity | null\n}\n\n/** What git says about a folder: gone, not in a repo, or the repo's root, git folder and `origin` remote. */\nexport type DirectoryProbe =\n | { outcome: 'missing' | 'not-a-repo' }\n | { outcome: 'repo'; root: string; commonDir: string | null; remoteUrl: string | null }\n\n/** The settings that decide what a repo is called. */\nexport type RepoNamingConfig = Pick<WorkscribeConfig, 'projects' | 'git'>\n\nconst NOT_A_REPO: RepoContext = { repoName: null, branch: null, root: null, remote: null, project: null }\n\nfunction gitOutput(command: string, cwd: string): string | null {\n try {\n return execSync(command, { cwd, timeout: 2000, stdio: 'pipe' }).toString().trim() || null\n } catch {\n return null\n }\n}\n\n/** The `origin` remote, never with a credential in it — the backfill stores it */\nfunction remoteUrlOf(root: string): string | null {\n const url = gitOutput('git remote get-url origin', root)\n return url === null ? null : withoutCredentials(url)\n}\n\n/** The git folder every worktree of the repo shares, resolved, so two paths to one repo give one answer. */\nfunction commonDirOf(directory: string): string | null {\n const commonDir = gitOutput('git rev-parse --git-common-dir', directory)\n if (commonDir === null) return null\n const absolute = path.resolve(directory, commonDir)\n try {\n return fs.realpathSync(absolute)\n } catch {\n return absolute\n }\n}\n\n/** The repo's identity — the git folder is only asked for when there's no usable remote. */\nfunction identityOf(directory: string, remoteUrl: string | null): ProjectIdentity | null {\n return (\n projectIdentity({ remoteUrl, commonDir: null }) ?? projectIdentity({ remoteUrl, commonDir: commonDirOf(directory) })\n )\n}\n\nfunction nameFromPackageJson(root: string): string | null {\n try {\n const raw = fs.readFileSync(path.join(root, 'package.json'), 'utf-8')\n const pkg = JSON.parse(raw) as { name?: string }\n if (!pkg.name) return null\n return pkg.name.replace(/^@[^/]+\\//, '')\n } catch {\n return null\n }\n}\n\nfunction currentBranch(cwd: string): string | null {\n try {\n return (\n execSync('git branch --show-current', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim() || null\n )\n } catch {\n return null // Detached HEAD or not a git repo\n }\n}\n\n/** The repo's name, and the remote it came from — set only when the name did come from the remote. */\nfunction nameRepo(repo: {\n root: string\n nameOverride: string | null\n remoteUrl: string | null\n qualifyRepoName: boolean\n}): Pick<RepoContext, 'repoName' | 'remote'> {\n const { root, nameOverride, remoteUrl, qualifyRepoName } = repo\n if (nameOverride !== null) return { repoName: nameOverride, remote: null }\n const remote = remoteUrl === null ? null : parseOwnerAndRepo(remoteUrl)\n if (remote) return { repoName: formatRepoName(remote, qualifyRepoName), remote }\n return { repoName: nameFromPackageJson(root) ?? path.basename(root), remote: null }\n}\n\n/**\n * The repo at `cwd`, named the way the config says: the `projects.<cwd>` override, else the `origin` remote in the\n * `git.qualifyRepoName` form, else package.json or the folder. Everything that stores or looks up a repo name uses\n * this, so shell, IDE, notes and `context` always agree.\n */\nexport function getRepoContext(cwd: string, config: RepoNamingConfig): RepoContext {\n const root = gitOutput('git rev-parse --show-toplevel', cwd)\n if (root === null) return NOT_A_REPO\n // Read even when an override names the repo: the name may come from config, the identity never does\n const remoteUrl = remoteUrlOf(root)\n const naming = nameRepo({\n root,\n nameOverride: config.projects?.[cwd] ?? null,\n remoteUrl,\n qualifyRepoName: config.git.qualifyRepoName,\n })\n return { ...naming, branch: currentBranch(cwd), root, project: identityOf(cwd, remoteUrl) }\n}\n\n/** What git says about a folder now — the backfill job asks once per folder and keeps the answer. */\nexport function probeDirectory(directory: string): DirectoryProbe {\n if (!fs.existsSync(directory)) return { outcome: 'missing' }\n // A bare repo or a path inside `.git` has no work tree, so it isn't a repo here — as for capture\n const root = gitOutput('git rev-parse --show-toplevel', directory)\n if (root === null) return { outcome: 'not-a-repo' }\n return { outcome: 'repo', root, commonDir: commonDirOf(directory), remoteUrl: remoteUrlOf(root) }\n}\n","import { execSync } from 'child_process'\n\n/**\n * Returns commit subject lines for all commits made between `sha` and HEAD.\n * Returns an empty array if sha is empty, HEAD hasn't moved, or git fails.\n */\nexport function getCommitsSince(sha: string, cwd: string): string[] {\n if (!sha) return []\n try {\n const out = execSync(`git log ${sha}..HEAD --format=%s`, { cwd, timeout: 3000 }).toString().trim()\n return out ? out.split('\\n').filter(Boolean) : []\n } catch {\n return []\n }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { redact } from './redact'\n\nconst HISTORY_FILE = '.aider.chat.history.md'\nconst PROMPT_MAX_LENGTH = 200\n\n/** The last prompt, redacted before it's cut, as a pattern can't recognise a secret cut in half. */\nexport function readLastAiderPrompt(cwd: string, extraRedactions: string[] = []): string | null {\n try {\n const histPath = path.join(cwd, HISTORY_FILE)\n const content = fs.readFileSync(histPath, 'utf-8')\n const matches = [...content.matchAll(/^#### (.+)$/gm)]\n if (!matches.length) return null\n return redact(matches[matches.length - 1][1].trim(), extraRedactions).slice(0, PROMPT_MAX_LENGTH)\n } catch {\n return null\n }\n}\n","// Recognises Workscribe's own commands inside compound shell input, so a coding agent running the\n// Workscribe skill (`cd repo && workscribe …`, `… | workscribe …`, heredocs) isn't captured as work.\n// Works on the raw command: heredocs and newlines are lost once whitespace is collapsed.\nimport { containsCommandSubstitution, splitOutsideQuotes, startsWithWord } from './shell-syntax'\nimport { commandSteps, isGlueStep, TEXT_SOURCES } from './command-steps'\n\nconst PIPE_SEPARATORS = ['|']\n\n/**\n * A step is Workscribe's when it starts with `workscribe` (whatever it's piped into), or when it only\n * pipes text into it (`echo … | workscribe …`). `npm test | workscribe note done` still ran the tests,\n * and so does `workscribe note \"$(npm test)\"`, so neither is.\n */\nfunction isWorkscribeStep(step: string): boolean {\n if (containsCommandSubstitution(step)) return false\n const stages = splitOutsideQuotes(step, PIPE_SEPARATORS)\n if (startsWithWord(stages[0] ?? '', 'workscribe')) return true\n const isTextOrWorkscribe = (stage: string): boolean =>\n startsWithWord(stage, 'workscribe') || TEXT_SOURCES.some((source) => startsWithWord(stage, source))\n return stages.every(isTextOrWorkscribe) && stages.some((stage) => startsWithWord(stage, 'workscribe'))\n}\n\n/**\n * True when the command does nothing but run Workscribe — possibly wrapped in shell glue (`cd`,\n * `|| true`, `; echo $?`), piped into, or with a heredoc. A command that also does real work\n * (`npm test && workscribe note …`) is not Workscribe-only.\n */\nexport function isWorkscribeOnlyCommand(rawCommand: string): boolean {\n const steps = commandSteps(rawCommand)\n const workSteps = steps.filter((step) => !isGlueStep(step))\n return workSteps.length > 0 && workSteps.every(isWorkscribeStep)\n}\n","import type { EventRow } from '../db/types'\nimport { isWorkscribeOnlyCommand } from './workscribe-command'\nimport { commandSteps, isGlueStep } from './command-steps'\n\nfunction normalizeForFilter(cmd: string): string {\n return cmd.trim().replace(/\\s+/g, ' ')\n}\n\nfunction isIgnoredStep(step: string, ignoreList: string[]): boolean {\n const normalized = normalizeForFilter(step)\n return ignoreList.includes(normalized) || ignoreList.includes(normalized.split(' ')[0])\n}\n\n/**\n * Returns true if the command should be dropped (noise / ignore list).\n *\n * The ignore list applies to each step of a compound command: `cd app && npm test` is kept because\n * `npm test` is real work, while `cd app && ls` is dropped. A step that does nothing on its own (`cd`,\n * `echo`) can't keep a command, but a command made only of such steps is dropped only if one of them is\n * on the ignore list — so `echo hi` on its own is still captured, as before.\n */\nexport function shouldFilter(command: string, ignoreList: string[]): boolean {\n const normalized = normalizeForFilter(command)\n if (!normalized) return true\n\n // Workscribe's own commands — always excluded, not user-configurable. Checked on the raw command so\n // compound ones an agent runs are caught too, while `workscribe note \"$(npm test)\"` still counts as work.\n if (isWorkscribeOnlyCommand(command)) return true\n\n const normalizedIgnore = ignoreList.map(normalizeForFilter)\n if (normalizedIgnore.includes(normalized)) return true\n\n const steps = commandSteps(command)\n const isQuiet = (step: string): boolean => isIgnoredStep(step, normalizedIgnore) || isGlueStep(step)\n return steps.every(isQuiet) && steps.some((step) => isIgnoredStep(step, normalizedIgnore))\n}\n\n/**\n * Returns true if this command is a duplicate of the last event\n * (same command + same cwd within the dedup window).\n */\nexport function isDuplicate(\n command: string,\n cwd: string,\n lastEvent: Pick<EventRow, 'command' | 'cwd' | 'captured_at'> | undefined,\n dedupWindowMs = 5000,\n now = Date.now()\n): boolean {\n if (!lastEvent) return false\n const lastTime = new Date(lastEvent.captured_at).getTime()\n return lastEvent.command === normalizeForFilter(command) && lastEvent.cwd === cwd && now - lastTime < dedupWindowMs\n}\n","// Per-file dedup window for IDE file-save events.\n// Prevents duplicate captures when an IDE auto-saves the same file repeatedly.\nexport const IDE_DEDUP_WINDOW_MS = 30_000\n\n// task_run can double-capture: a shell task running in a hooked terminal\n// fires both this generic event and a richer shell-sourced one, with no\n// shared ID linking them. Shell events land first (precmd fires as the\n// command returns), so a short window here catches the same real action.\nexport const TASK_RUN_DEDUP_WINDOW_MS = 5_000\n\n// Valid event type strings sent by IDE integrations via `workscribe _capture --type <type>`.\nexport const IDE_EVENT_TYPES = new Set(['code_edit', 'debug_start', 'debug_end', 'task_run'])\n\nexport function isIdeEventType(type: string): boolean {\n return IDE_EVENT_TYPES.has(type)\n}\n","// The folder a captured command's work happened in — the repo to credit it to. Pure: paths are only resolved, never\n// looked at on disk.\nimport path from 'path'\nimport { type GitInvocation, parseGitInvocation } from './git-invocation'\n\n/** A literal directory that can be followed — not `~`, `$VAR`, `-` or a substitution, which only the shell expands. */\nfunction isLiteralDirectory(directory: string | null | undefined): directory is string {\n return typeof directory === 'string' && directory !== '' && directory !== '-' && !/[~$`]/.test(directory)\n}\n\n/**\n * The directory a git invocation works in, from `base`: its `-C` chain (each relative to the one before), then its\n * `--work-tree` relative to that — or, without either, the repo a `--git-dir …/.git` belongs to.\n */\nexport function gitDirectoryOf(invocation: GitInvocation, base: string): string {\n if (!invocation.directories.every(isLiteralDirectory)) return base\n const directory = invocation.directories.reduce((current, next) => path.resolve(current, next), base)\n if (invocation.workTree !== null) {\n return isLiteralDirectory(invocation.workTree) ? path.resolve(directory, invocation.workTree) : base\n }\n const { gitDir } = invocation\n if (invocation.directories.length === 0 && isLiteralDirectory(gitDir) && path.basename(gitDir) === '.git') {\n return path.dirname(path.resolve(directory, gitDir))\n }\n return directory\n}\n\n/**\n * Where a captured command's work happened: `cwd`, moved by the matched step's own git options (`-C`,\n * `--work-tree`, `--git-dir`). A `cd` before the step is never followed: every source reports `cwd` as where the\n * shell ended up — the shell hook's `$PWD`, and Claude Code's `cwd`, which keeps a `cd` inside the project (checked\n * against Claude Code 2.1.311). Following it again would apply it twice.\n */\nexport function effectiveDirectory(options: { matchedStep: string; cwd: string }): string {\n const invocation = parseGitInvocation(options.matchedStep)\n return invocation ? gitDirectoryOf(invocation, options.cwd) : options.cwd\n}\n","// Reads the message of a `git commit -F <file>`. Capture runs synchronously on every command and must never hang or\n// throw, so only a small, regular file is read — never a pipe, a device or standard input.\nimport fs from 'fs'\nimport path from 'path'\n\nconst MAX_MESSAGE_FILE_BYTES = 4096\n\n/** The first line git keeps from a message file — non-empty, and not a `#` comment (git strips those). */\nfunction subjectOfMessageFile(text: string): string | null {\n return (\n text\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line !== '' && !line.startsWith('#')) ?? null\n )\n}\n\n/**\n * The subject of a commit message file, resolved against the directory the commit ran in — or null if the file\n * can't be read safely: gone already (an agent's temporary file), not a regular file, or a path with `~`, `$` or a\n * substitution in it.\n */\nexport function readCommitMessageFile(file: string, directory: string): string | null {\n if (/[~$`]/.test(file)) return null\n const resolved = path.resolve(directory, file)\n try {\n if (!fs.lstatSync(resolved).isFile()) return null\n const descriptor = fs.openSync(resolved, 'r')\n try {\n const buffer = Buffer.alloc(MAX_MESSAGE_FILE_BYTES)\n const bytesRead = fs.readSync(descriptor, buffer, 0, MAX_MESSAGE_FILE_BYTES, 0)\n return subjectOfMessageFile(buffer.subarray(0, bytesRead).toString('utf-8'))\n } finally {\n fs.closeSync(descriptor)\n }\n } catch {\n return null // the file vanished between checks, or can't be opened — the git log merge fills the message in\n }\n}\n","import fs from 'fs'\nimport path from 'path'\n\n// The shell hook stores `--cwd \"$PWD\"`: the folder as you reached it, through any symlink (a symlinked `~/code`, or\n// macOS's `/tmp` → `/private/tmp`). Node's `process.cwd()` and `git rev-parse --show-toplevel` give the resolved\n// path. These helpers let `note` and `context` see paths the way capture stored them.\n\nfunction realPath(target: string): string | null {\n try {\n return fs.realpathSync(target)\n } catch {\n return null // the folder is gone, or never existed\n }\n}\n\n/** The path with symlinks resolved, or as given if it doesn't exist (any more). */\nexport function resolvedPath(target: string): string {\n return realPath(target) ?? target\n}\n\n/** The folder you're in, as you reached it: `$PWD` when it's the same folder as `process.cwd()`, else that. */\nexport function currentDirectory(where = { pwd: process.env.PWD, cwd: process.cwd() }): string {\n if (where.pwd && realPath(where.pwd) === realPath(where.cwd)) return where.pwd\n return where.cwd\n}\n\n/**\n * The repo root in every form a stored event's `cwd` may use: git's resolved `root`, and the same folder as reached\n * from `cwd` — so a repo opened through a symlink still matches its own activity.\n */\nexport function rootForms(cwd: string, root: string): string[] {\n const resolvedCwd = realPath(cwd)\n if (!resolvedCwd) return [root]\n const reached = path.resolve(cwd, path.relative(resolvedCwd, root))\n return [...new Set([root, reached])]\n}\n\n/** `target` relative to `root` — `''` for the root itself — or null when it's outside it. Pure: compares paths only. */\nexport function relativeInside(root: string, target: string): string | null {\n const relative = path.relative(root, target)\n const isOutside = relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)\n return isOutside ? null : relative\n}\n\n/**\n * The file's path relative to the repo root, or null when it isn't inside it. The root is tried in each form the\n * file's own folder reaches it by, so a file opened through a symlink still matches.\n */\nexport function pathInsideRoot(filePath: string, root: string): string | null {\n for (const form of rootForms(path.dirname(filePath), root)) {\n const relative = relativeInside(form, filePath)\n if (relative) return relative\n }\n return null\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { getRepoContext, type RepoContext, type RepoNamingConfig } from './git'\nimport { FILE_SAVE_ACTION, type EventDimensions, type ProjectRecord } from '../db/dimensions'\nimport { pathInsideRoot } from '../utils/working-directory'\n\nfunction projectRecord(context: RepoContext): ProjectRecord | null {\n if (!context.project) return null\n return { ...context.project, name: context.repoName, root: context.root }\n}\n\n/** What an event links to, from the repo context capture already found for it. */\nexport function eventDimensions(context: RepoContext): EventDimensions {\n const project = projectRecord(context)\n return { project, branch: project === null ? null : context.branch, file: null }\n}\n\n/**\n * Whether a folder between the file and the root it was found under is a repo of its own — a nested repo or a\n * submodule, which owns the file instead. The root itself isn't checked.\n */\nfunction isInNestedRepo(filePath: string, relativePath: string): boolean {\n const foldersBelowRoot = relativePath.split(path.sep).length - 1\n let folder = path.dirname(filePath)\n for (let level = 0; level < foldersBelowRoot; level++) {\n if (fs.existsSync(path.join(folder, '.git'))) return true\n folder = path.dirname(folder)\n }\n return false\n}\n\n/**\n * What a save links to. The project is the file's own repo, which may not be the folder the editor has open (a\n * workspace above several repos, or a nested repo or submodule inside the open one): git is asked again only then.\n * The branch is the open folder's, so it's only kept when the file is in that repo.\n */\nexport function fileEventDimensions(context: RepoContext, filePath: string, config: RepoNamingConfig): EventDimensions {\n const pathInWorkspaceRepo = context.root === null ? null : pathInsideRoot(filePath, context.root)\n if (pathInWorkspaceRepo !== null && context.project && !isInNestedRepo(filePath, pathInWorkspaceRepo)) {\n return { ...eventDimensions(context), file: { relativePath: pathInWorkspaceRepo, action: FILE_SAVE_ACTION } }\n }\n const fileContext = getRepoContext(path.dirname(filePath), config)\n const project = projectRecord(fileContext)\n if (project === null || fileContext.root === null) return eventDimensions(context)\n const relativePath = pathInsideRoot(filePath, fileContext.root)\n return {\n project,\n branch: null,\n file: relativePath === null ? null : { relativePath, action: FILE_SAVE_ACTION },\n }\n}\n","import fs from 'fs'\nimport { insertEvent, getLastEvent, getLastEventForFile, getLastEventForCwd } from '../db'\nimport { redact } from './redact'\nimport { commandHint } from './hint'\nimport { getRepoContext } from './git'\nimport { getCommitsSince } from './git-snapshot'\nimport { readLastAiderPrompt } from './aider'\nimport { normalizeCommand } from '../normalize/patterns'\nimport { readConfig } from '../config'\nimport { shouldFilter, isDuplicate } from './filter'\nimport { IDE_DEDUP_WINDOW_MS, TASK_RUN_DEDUP_WINDOW_MS } from './ide'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { loadAIToolRegistry } from '../config/registry'\nimport { effectiveDirectory } from '../normalize/git-directory'\nimport { readCommitMessageFile } from './commit-message-file'\nimport { eventDimensions, fileEventDimensions } from './dimensions'\nimport type { AgentSessionRef } from '../db/agent-sessions'\nimport type { EventDimensions } from '../db/dimensions'\nimport type { TicketKeys } from '../db/tickets'\nimport type { WorkscribeConfig } from '../config'\nimport { ticketKeysOf, ticketPatternOf } from './tickets'\nimport type { CommitSource } from '../summary/commits'\n\n/**\n * The ticket keys an event names, with the branch it's linked to — none when it isn't linked to one. A config.json\n * edited to drop `tickets` means the default pattern, never a failed capture.\n */\nfunction ticketsOf(event: CommitSource, dimensions: EventDimensions, config: WorkscribeConfig): TicketKeys {\n return ticketKeysOf({ linkedBranch: dimensions.branch, event }, ticketPatternOf(config.tickets?.pattern))\n}\n\n/**\n * `git commit -F <file>`: the message, read now while the file may still exist and redacted like the command — and\n * never the path, which isn't stored.\n */\nfunction withMessageFromFile(\n metadata: Record<string, unknown>,\n workDirectory: string,\n extraRedactions: string[]\n): Record<string, unknown> {\n const { messageFile, ...rest } = metadata\n if (typeof messageFile !== 'string') return metadata\n const message = readCommitMessageFile(messageFile, workDirectory)\n return { ...rest, message: message ? redact(message, extraRedactions) : null }\n}\n\nexport function captureCommand(opts: {\n command?: string\n cwd: string\n exitCode: number | null\n durationMs: number | null\n gitBefore?: string\n source?: string\n type?: string\n file?: string\n /** The agent conversation a hook command ran in; only shell commands from a hook have one */\n agentSession?: AgentSessionRef\n}): void {\n // Check pause flag before any I/O\n if (fs.existsSync(PAUSE_FLAG_PATH)) return\n\n const config = readConfig()\n const { cwd, exitCode, durationMs } = opts\n\n // ─── IDE event path ──────────────────────────────────────────────────────────\n // When --type is provided, the caller has already classified the event.\n // Skip normalization and noise filtering — these are first-party signals.\n if (opts.type) {\n // For file-save events, deduplicate per-file with a longer window (30s) to\n // absorb rapid auto-saves of the same file.\n if (opts.type === 'code_edit' && opts.file) {\n const lastForFile = getLastEventForFile(opts.file, cwd)\n if (lastForFile) {\n const lastTime = new Date(lastForFile.captured_at).getTime()\n if (Date.now() - lastTime < IDE_DEDUP_WINDOW_MS) return\n }\n }\n\n // A shell task running in a hooked terminal already got captured with a\n // real category by the shell hook — skip the generic duplicate.\n if (opts.type === 'task_run') {\n const lastForCwd = getLastEventForCwd(cwd)\n if (lastForCwd && !lastForCwd.source) {\n const lastTime = new Date(lastForCwd.captured_at).getTime()\n if (Date.now() - lastTime < TASK_RUN_DEDUP_WINDOW_MS) return\n }\n }\n\n const command = opts.command === undefined ? (opts.file ?? opts.type) : redact(opts.command, config.redact.extra)\n const context = getRepoContext(cwd, config)\n const { repoName, branch } = context\n const ideEvent = {\n captured_at: new Date().toISOString(),\n command,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category: opts.type,\n metadata: null,\n source: opts.source ?? null,\n file_path: opts.file ?? null,\n }\n const dimensions = opts.file ? fileEventDimensions(context, opts.file, config) : eventDimensions(context)\n insertEvent({ ...ideEvent, dimensions, tickets: ticketsOf(ideEvent, dimensions, config) })\n return\n }\n\n // ─── Shell event path ─────────────────────────────────────────────────────────\n const command = opts.command as string\n\n // 1. Filter noise commands\n if (shouldFilter(command, config.capture.ignore)) return\n\n // 2. Deduplicate: same command + same cwd within 5 seconds\n const last = getLastEvent()\n if (isDuplicate(command, cwd, last ?? undefined)) return\n\n // 3. Redact sensitive patterns\n const safeCommand = redact(command, config.redact.extra)\n\n // 4. Normalize to typed event — first, because the repo depends on which step did the work\n const { category, metadata: normalizedMetadata, matchedStep } = normalizeCommand(safeCommand)\n\n if (category === 'ai_session' && config.capture.aiTools === false) return\n\n // 5. Extract git context from where the work happened — `git -C ../web commit` belongs to web, not here — named\n // exactly as the IDE path, notes and `context` name it\n const workDirectory = effectiveDirectory({ matchedStep: matchedStep ?? safeCommand, cwd })\n const context = getRepoContext(workDirectory, config)\n const { repoName, branch } = context\n const metadata = withMessageFromFile(normalizedMetadata, workDirectory, config.redact.extra)\n\n // Hint passthrough: preserve context for unrecognised commands so the AI can describe them\n if (category === 'other') {\n const firstToken = safeCommand.trim().split(/\\s+/)[0]\n if (firstToken) metadata.tool = firstToken\n metadata.hint = commandHint(safeCommand)\n }\n\n // 6. For ai_session events, enrich metadata\n if (category === 'ai_session') {\n // 6a. Commits made during the session\n if (opts.gitBefore) {\n const commits = getCommitsSince(opts.gitBefore, workDirectory)\n if (commits.length > 0) metadata.commits = commits.map((subject) => redact(subject, config.redact.extra))\n }\n\n // 6b. Last user prompt from history file (e.g. Aider)\n if (config.capture.includeAiderPrompt !== false) {\n const toolEntry = loadAIToolRegistry().find((entry) => entry.tool === metadata.tool)\n if (toolEntry?.contextSource === 'history-file') {\n const prompt = readLastAiderPrompt(cwd, config.redact.extra)\n if (prompt) metadata.prompt = prompt\n }\n }\n }\n\n // 7. Persist\n const shellEvent = {\n captured_at: new Date().toISOString(),\n command: safeCommand,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category,\n metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,\n source: opts.source ?? null,\n file_path: null,\n }\n const dimensions = eventDimensions(context)\n insertEvent({\n ...shellEvent,\n dimensions,\n tickets: ticketsOf(shellEvent, dimensions, config),\n agentSession: opts.agentSession,\n })\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { debugLog } from '../utils/logger'\n\n// Ends a capture that is still alive this long after starting. The capture itself is synchronous, so the timer can\n// only fire once it has finished: it never cuts short a wait for the database lock (busy_timeout, also 5 s).\nconst CAPTURE_HANG_TIMEOUT_MS = 5_000\n\nexport function registerCapture(program: Command): void {\n // Internal command — called by the shell hook and IDE integrations, not by the user\n program\n .command('_capture')\n .description('Internal: capture a shell command or IDE event (called by shell hook or IDE plugin)')\n .option('--cmd <command>', 'command that was executed (shell events)')\n .option('--cwd <path>', 'working directory')\n .option('--exit <code>', 'exit code', parseInt)\n .option('--duration <ms>', 'duration in milliseconds', parseInt)\n .option('--git-before <sha>', 'git HEAD SHA before this command ran')\n .option('--type <type>', 'event type override — skips normalization (IDE integrations)')\n .option('--source <source>', 'event source identifier (e.g. vscode, neovim, jupyter)')\n .option('--file <path>', 'file path for code_edit events')\n .action((opts) => {\n // Prevent background process accumulation if the process hangs (e.g. SQLite locked).\n // unref() allows a natural exit to happen immediately without waiting for the timer.\n const hangGuard = setTimeout(() => process.exit(0), CAPTURE_HANG_TIMEOUT_MS)\n hangGuard.unref()\n\n // Never surface errors to the terminal — this runs silently in the background\n try {\n if (!opts.cwd) return\n // Require either a shell command or a typed IDE event\n if (!opts.cmd && !opts.type) return\n captureCommand({\n command: opts.cmd,\n cwd: opts.cwd,\n exitCode: opts.exit ?? null,\n durationMs: opts.duration ?? null,\n gitBefore: opts.gitBefore ?? undefined,\n source: opts.source ?? undefined,\n type: opts.type ?? undefined,\n file: opts.file ?? undefined,\n })\n } catch (err) {\n debugLog('_capture', err)\n }\n })\n}\n","import { redact } from './redact'\n\nexport function containsSecrets(text: string): boolean {\n return redact(text) !== text\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { getDb } from './index'\nimport { ALERTS_PENDING_PATH } from '../utils/paths'\nimport { ensurePrivateDirectory } from '../utils/private-directory'\n\nexport interface AlertRow {\n id: number\n created_at: number\n tool: string\n session_id: string | null\n context: string\n cleared: number\n}\n\nexport function insertAlert(tool: string, sessionId: string | null, context: string): void {\n getDb()\n .prepare(\n `INSERT INTO alerts (created_at, tool, session_id, context)\n VALUES ($created_at, $tool, $session_id, $context)`\n )\n .run({ created_at: Date.now(), tool, session_id: sessionId ?? null, context })\n\n ensurePrivateDirectory(path.dirname(ALERTS_PENDING_PATH))\n fs.writeFileSync(ALERTS_PENDING_PATH, '', { flag: 'w' })\n}\n\nexport function getPendingAlerts(): AlertRow[] {\n return getDb()\n .prepare('SELECT * FROM alerts WHERE cleared = 0 ORDER BY created_at DESC')\n .all() as unknown as AlertRow[]\n}\n\nexport function hasPendingAlerts(): boolean {\n const row = getDb().prepare('SELECT COUNT(*) as count FROM alerts WHERE cleared = 0').get() as {\n count: number | bigint\n }\n return Number(row.count) > 0\n}\n\nexport function clearAlerts(): number {\n const result = getDb().prepare('UPDATE alerts SET cleared = 1 WHERE cleared = 0').run()\n try {\n fs.unlinkSync(ALERTS_PENDING_PATH)\n } catch {\n // File may already be absent\n }\n return Number(result.changes)\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { readConfig } from '../config'\nimport { debugLog } from '../utils/logger'\nimport { containsSecrets } from '../capture/detect-secrets'\nimport { insertAlert } from '../db/alerts'\n\n/** The Claude Code events that report a finished Bash command — the success and the failure halves. */\nexport const TOOL_EVENTS = ['PostToolUse', 'PostToolUseFailure'] as const\nexport type ToolEvent = (typeof TOOL_EVENTS)[number]\n\nexport interface HookCommand {\n command: string\n cwd: string\n /** 0 for a success, the real code for a failure, null when it's unknown (an interrupted call) */\n exitCode: number | null\n durationMs: number | null\n /** The agent conversation the command ran in — the same across every folder it worked in. Null if not sent */\n sessionId: string | null\n}\n\n// PostToolUseFailure's `error` starts with this for a Bash command that ran and exited\nconst EXIT_CODE_LINE = /^Exit code (\\d+)/\n\n/**\n * The exit code an event implies. Claude Code fires PostToolUse only when a Bash call succeeds and\n * PostToolUseFailure only when it fails (checked against Claude Code 2.1.311 — see\n * tests/fixtures/claude-code-hooks/). Only the first line of `error` is read; its output never is.\n */\nfunction exitCodeOf(event: ToolEvent, payload: Record<string, unknown>): number | null {\n if (event === 'PostToolUse') return 0\n if (payload.is_interrupt === true || typeof payload.error !== 'string') return null\n const exitCode = payload.error.match(EXIT_CODE_LINE)?.[1]\n return exitCode === undefined ? null : Number(exitCode)\n}\n\n/** Parses a Claude Code PostToolUse or PostToolUseFailure stdin payload. Returns null if required fields are missing. */\nexport function parseHookPayload(raw: string, event: ToolEvent = 'PostToolUse'): HookCommand | null {\n if (!raw.trim()) return null\n const payload = JSON.parse(raw) as Record<string, unknown>\n // The hook's matcher is `Bash`, but settings can be edited: exit codes only mean something for shell commands\n if (payload.tool_name !== undefined && payload.tool_name !== 'Bash') return null\n const toolInput = payload.tool_input as Record<string, unknown> | undefined\n const command = typeof toolInput?.command === 'string' ? toolInput.command : null\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : null\n if (!command || !cwd) return null\n const durationMs = typeof payload.duration_ms === 'number' ? payload.duration_ms : null\n const sessionId = typeof payload.session_id === 'string' && payload.session_id !== '' ? payload.session_id : null\n return { command, cwd, exitCode: exitCodeOf(event, payload), durationMs, sessionId }\n}\n\nfunction isToolEvent(event: string): event is ToolEvent {\n return (TOOL_EVENTS as readonly string[]).includes(event)\n}\n\ninterface UserPromptPayload {\n session_id: string\n prompt: string\n cwd: string\n}\n\n/** Parses a Claude Code UserPromptSubmit stdin payload. Returns null if required fields are missing. */\nexport function parseUserPromptPayload(raw: string): UserPromptPayload | null {\n if (!raw.trim()) return null\n const payload = JSON.parse(raw) as Record<string, unknown>\n const prompt = typeof payload.prompt === 'string' ? payload.prompt : null\n const sessionId = typeof payload.session_id === 'string' ? payload.session_id : ''\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : ''\n if (!prompt) return null\n return { session_id: sessionId, prompt, cwd }\n}\n\nexport function registerHook(program: Command): void {\n // Internal command — called by AI tool hooks (e.g. Claude Code PostToolUse), not by the user\n program\n .command('_hook')\n .description('Internal: capture events from AI tool hooks (stdin JSON payload)')\n .option('--source <source>', 'hook source identifier (e.g. claude-code)')\n .option('--event <event>', 'hook event type (PostToolUse, PostToolUseFailure or UserPromptSubmit)', 'PostToolUse')\n .action((opts: { source?: string; event: string }) => {\n let raw = ''\n process.stdin.setEncoding('utf-8')\n process.stdin.on('data', (chunk: string) => {\n raw += chunk\n })\n process.stdin.on('end', () => {\n try {\n if (opts.event === 'UserPromptSubmit') {\n const parsed = parseUserPromptPayload(raw)\n if (!parsed) return\n if (containsSecrets(parsed.prompt)) {\n insertAlert(\n opts.source ?? 'unknown',\n parsed.session_id || null,\n 'Possible credential detected in prompt submitted to AI tool'\n )\n // Claude Code reads this JSON and injects systemMessage into Claude's context\n process.stdout.write(\n JSON.stringify({\n systemMessage:\n 'Workscribe detected a possible credential in your prompt. ' +\n 'Run `workscribe alerts` for details.',\n })\n )\n }\n return\n }\n\n // A finished Bash command, successful or not — anything else is an event this version doesn't handle\n if (!isToolEvent(opts.event)) return\n if (readConfig().capture.aiTools === false) return\n const parsed = parseHookPayload(raw, opts.event)\n if (!parsed) return\n const { sessionId, ...command } = parsed\n // Only with a source, so the event and its session always name the same agent\n const agentSession = sessionId && opts.source ? { source: opts.source, sessionId } : undefined\n captureCommand({ ...command, source: opts.source, agentSession })\n } catch (err) {\n debugLog('_hook', err)\n }\n })\n })\n}\n","export interface CompletionCommand {\n name: string\n description: string\n}\n\nexport function bashCompletion(commands: CompletionCommand[]): string {\n const names = commands.map((command) => command.name).join(' ')\n return `\\\n_workscribe_completion() {\n local cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n local prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n local commands=\"${names}\"\n case \"$prev\" in\n completions) COMPREPLY=( $(compgen -W \"bash zsh fish\" -- \"$cur\") ); return ;;\n config) COMPREPLY=( $(compgen -W \"get set list\" -- \"$cur\") ); return ;;\n profile) COMPREPLY=( $(compgen -W \"list add remove update detect suggest\" -- \"$cur\") ); return ;;\n esac\n COMPREPLY=( $(compgen -W \"$commands\" -- \"$cur\") )\n}\ncomplete -F _workscribe_completion workscribe`\n}\n\nexport function zshCompletion(commands: CompletionCommand[]): string {\n const commandList = commands.map((command) => ` '${command.name}:${command.description}'`).join('\\n')\n return `\\\n#compdef workscribe\n_workscribe() {\n local state\n _arguments '1: :->command' '*: :->args'\n case $state in\n command)\n local -a commands\n commands=(\n${commandList}\n )\n _describe 'command' commands\n ;;\n args)\n case $words[2] in\n completions) local -a shells; shells=('bash' 'zsh' 'fish'); _describe 'shell' shells ;;\n config) local -a sub; sub=('get:Get a value' 'set:Set a value' 'list:List all values'); _describe 'subcommand' sub ;;\n profile) local -a sub; sub=('list' 'add' 'remove' 'update' 'detect' 'suggest'); _describe 'subcommand' sub ;;\n esac\n ;;\n esac\n}\ncompdef _workscribe workscribe`\n}\n\nexport function fishCompletion(commands: CompletionCommand[]): string {\n const lines = [\n 'complete -c workscribe -f',\n ...commands.map(\n (command) =>\n `complete -c workscribe -n \"__fish_use_subcommand\" -a ${command.name.padEnd(12)} -d \"${command.description}\"`\n ),\n 'complete -c workscribe -n \"__fish_seen_subcommand_from completions\" -a \"bash zsh fish\"',\n 'complete -c workscribe -n \"__fish_seen_subcommand_from config\" -a \"get set list\"',\n 'complete -c workscribe -n \"__fish_seen_subcommand_from profile\" -a \"list add remove update detect suggest\"',\n ]\n return lines.join('\\n')\n}\n","import fs from 'fs'\n\n// Far more than two edits in one millisecond ever need; a bound so a broken file system can't loop forever\nconst MAX_BACKUP_ATTEMPTS = 100\n\n/**\n * Copies a file the user owns to `<file>.workscribe-backup-<time>` before Workscribe edits it, and returns the\n * backup's path. Never overwrites an earlier backup — two edits in the same millisecond (say the shell hook and\n * the completions block, one after the other) each keep their own copy.\n */\nexport function backUpFile(file: string): string {\n const base = `${file}.workscribe-backup-${Date.now()}`\n for (let attempt = 1; attempt <= MAX_BACKUP_ATTEMPTS; attempt++) {\n const backup = attempt === 1 ? base : `${base}-${attempt}`\n try {\n fs.copyFileSync(file, backup, fs.constants.COPYFILE_EXCL)\n return backup\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\n }\n }\n throw new Error(`Couldn't back up ${file}: too many backups named ${base}`)\n}\n","// The `# Workscribe completions` block `init` adds to ~/.zshrc / ~/.bashrc. Pure text handling, so the rc-file\n// I/O in shell-hook.ts stays thin. The block guards its `source` line, so it does nothing — and prints nothing —\n// once the completion script is gone (e.g. after the package and its data folder are removed).\n//\n// This code edits the user's own rc files, so it only ever touches lines in the exact shapes Workscribe wrote:\n// a marker line directly followed by the line(s) below, sourcing Workscribe's own `completion.<shell>` script.\n// A marker on its own, or followed by anything else, is left alone.\n\nexport const COMPLETIONS_MARKER = '# Workscribe completions'\n\nconst ZSH_COMPINIT = 'autoload -Uz compinit && compinit'\n// Workscribe's own script: `<data folder>/completion.zsh` or `completion.bash`\nconst OWN_SCRIPT = '\"[^\"]*completion\\\\.(?:zsh|bash)\"'\n// The line(s) under the marker, in every shape Workscribe has written\nconst GUARDED_ZSH = new RegExp(`^if \\\\[ -f ${OWN_SCRIPT} \\\\]; then ${ZSH_COMPINIT}; source ${OWN_SCRIPT}; fi$`)\nconst GUARDED_BASH = new RegExp(`^\\\\[ -f ${OWN_SCRIPT} \\\\] && source ${OWN_SCRIPT}$`)\nconst UNGUARDED_SOURCE = new RegExp(`^source ${OWN_SCRIPT}$`)\n\n/** The block for a shell; `file` is the generated completion script it sources. */\nexport function completionsBlock(shellName: string, file: string): string {\n const guardedSource =\n shellName === 'zsh'\n ? `if [ -f \"${file}\" ]; then ${ZSH_COMPINIT}; source \"${file}\"; fi`\n : `[ -f \"${file}\" ] && source \"${file}\"`\n return `${COMPLETIONS_MARKER}\\n${guardedSource}`\n}\n\n/** Lines of a block: `first` is the marker line, `last` the line that sources the script (inclusive). */\ninterface BlockLines {\n first: number\n last: number\n}\n\n/** A file's lines, compared without a Windows `\\r` but written back with it. */\ninterface RcLines {\n lines: string[]\n lineEnding: '' | '\\r'\n}\n\nfunction splitLines(content: string): RcLines {\n return { lines: content.split('\\n'), lineEnding: content.includes('\\r\\n') ? '\\r' : '' }\n}\n\nfunction bare(line: string | undefined): string {\n return (line ?? '').replace(/\\r$/, '')\n}\n\n/** How many lines after the marker belong to the block, or 0 if what follows isn't a Workscribe block. */\nfunction blockLengthAfterMarker(lines: string[], markerIndex: number): number {\n const next = bare(lines[markerIndex + 1])\n if (GUARDED_ZSH.test(next) || GUARDED_BASH.test(next) || UNGUARDED_SOURCE.test(next)) return 1\n if (next === ZSH_COMPINIT && UNGUARDED_SOURCE.test(bare(lines[markerIndex + 2]))) return 2\n return 0\n}\n\n/** Every Workscribe completions block — older versions of `init` could append more than one. */\nfunction findBlocks(lines: string[]): BlockLines[] {\n const blocks: BlockLines[] = []\n for (let index = 0; index < lines.length; index++) {\n if (bare(lines[index]) !== COMPLETIONS_MARKER) continue\n const length = blockLengthAfterMarker(lines, index)\n if (length === 0) continue\n blocks.push({ first: index, last: index + length })\n index += length\n }\n return blocks\n}\n\nfunction blockText(lines: string[], block: BlockLines): string {\n return lines\n .slice(block.first, block.last + 1)\n .map(bare)\n .join('\\n')\n}\n\nexport function hasCompletionsBlock(content: string): boolean {\n return findBlocks(splitLines(content).lines).length > 0\n}\n\n/** There's a block that isn't the current one (typically an unguarded block from an older version), or several. */\nexport function isCompletionsBlockOutdated(content: string, currentBlock: string): boolean {\n const { lines } = splitLines(content)\n const blocks = findBlocks(lines)\n if (blocks.length === 0) return false\n return blocks.length > 1 || blockText(lines, blocks[0]) !== currentBlock\n}\n\n/**\n * Rebuilds the file from its lines: `replacement` takes the place of the first block, the other blocks go — each\n * with the one blank line `installCompletions` put before it — and every other line stays exactly as it was.\n */\nfunction rebuild(content: string, replacement: string | null): string {\n const { lines, lineEnding } = splitLines(content)\n const blocks = findBlocks(lines)\n if (blocks.length === 0) return content\n const dropped = new Set<number>()\n blocks.forEach((block, blockIndex) => {\n const isReplaced = blockIndex === 0 && replacement !== null\n for (let index = block.first; index <= block.last; index++) dropped.add(index)\n if (!isReplaced && block.first > 0 && bare(lines[block.first - 1]) === '') dropped.add(block.first - 1)\n })\n const replacementLines = replacement === null ? [] : replacement.split('\\n').map((line) => line + lineEnding)\n return lines\n .flatMap((line, index) => {\n if (replacement !== null && index === blocks[0].first) return replacementLines\n return dropped.has(index) ? [] : [line]\n })\n .join('\\n')\n}\n\n/** Replaces the first block with `newBlock` and removes any duplicates. */\nexport function replaceCompletionsBlock(content: string, newBlock: string): string {\n return rebuild(content, newBlock)\n}\n\n/** Removes every block (and the blank line before each), leaving the rest of the file as it was. */\nexport function removeCompletionsBlock(content: string): string {\n return rebuild(content, null)\n}\n","import fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { execFileSync } from 'child_process'\nimport { log } from '../utils/logger'\nimport { CompletionCommand, bashCompletion, zshCompletion, fishCompletion } from '../utils/completion-scripts'\nimport { WORKSCRIBE_DIR } from '../utils/paths'\nimport { ensurePrivateDirectory } from '../utils/private-directory'\nimport { backUpFile } from '../utils/backup'\nimport {\n COMPLETIONS_MARKER,\n completionsBlock,\n hasCompletionsBlock,\n isCompletionsBlockOutdated,\n removeCompletionsBlock,\n replaceCompletionsBlock,\n} from './completions-block'\n\nconst FISH_COMPLETIONS_DIR = path.join(os.homedir(), '.config', 'fish', 'completions')\n\nconst HOOK_START = '# workscribe:hook:start'\nconst HOOK_END = '# workscribe:hook:end'\n// The two lines every template starts with, above HOOK_START. Updates before WOR-153 stacked a copy per update.\nconst PREAMBLE_LINE =\n /^# (?:Workscribe — developer work journal|Do not edit this (?:block|file) manually\\. Managed by `workscribe init`\\.)$/\nconst HOOK_VERSION_LINE = /^# workscribe:hook-version (\\d+)$/m\n\n/**\n * The version of each shell's hook template (`# workscribe:hook-version N` inside the block; none means 1). Bump it\n * whenever that template changes, so `status` can tell users whose rc file still holds the old one.\n */\nexport const SHELL_HOOK_VERSIONS: Readonly<Record<string, number>> = Object.freeze({ zsh: 2, bash: 2, fish: 1 })\n\n// The block 0.2.1's `chronicle init` wrote — the app's name before 1.0. Nothing reads it any more, and it errors at\n// every prompt.\nconst CHRONICLE_START = '# chronicle:hook:start'\nconst CHRONICLE_END = '# chronicle:hook:end'\nconst CHRONICLE_PREAMBLE_LINE =\n /^# (?:Chronicle — developer work journal|Do not edit this block manually\\. Managed by `chronicle init`\\.)$/\n\n/** A shell and the rc file `init` writes its hook (and completions) into. */\nexport type ShellTarget = { name: string; rcFile: string }\n\n/** The shell `$SHELL` names, and its rc file — what `init` installs into and `status` checks. */\nexport function detectShell(): ShellTarget | null {\n const shell = process.env.SHELL ?? ''\n if (shell.includes('zsh')) return { name: 'zsh', rcFile: path.join(os.homedir(), '.zshrc') }\n if (shell.includes('bash')) return { name: 'bash', rcFile: path.join(os.homedir(), '.bashrc') }\n if (shell.includes('fish')) {\n return { name: 'fish', rcFile: path.join(os.homedir(), '.config', 'fish', 'conf.d', 'workscribe.fish') }\n }\n return null\n}\n\n/** Where a marked block sits in an rc file: from the first of the header lines right above it, to its end marker. */\ninterface BlockRegion {\n start: number\n end: number\n}\n\n/** The marked block with every header line directly above it (blank lines between them included), or null. */\nfunction blockRegion(content: string, markers: { start: string; end: string; preamble: RegExp }): BlockRegion | null {\n const blockStart = content.indexOf(markers.start)\n const blockEnd = content.indexOf(markers.end, blockStart)\n if (blockStart === -1 || blockEnd === -1) return null\n const lines = content.slice(0, blockStart).split('\\n')\n lines.pop() // the start marker's own line, empty before it\n let headerLines = 0\n for (let index = lines.length - 1; index >= 0; index -= 1) {\n const line = lines[index]\n if (!markers.preamble.test(line) && line.trim() !== '') break\n headerLines = lines.length - index\n }\n // Blank lines above the topmost header line stay where they were\n while (headerLines > 0 && lines[lines.length - headerLines].trim() === '') headerLines -= 1\n const header = lines.slice(lines.length - headerLines)\n const headerLength = header.reduce((length, line) => length + line.length + 1, 0)\n return { start: blockStart - headerLength, end: blockEnd + markers.end.length }\n}\n\n/** The Workscribe hook in an rc file, header lines included — what an update replaces and a removal deletes. */\nexport function hookRegion(content: string): BlockRegion | null {\n return blockRegion(content, { start: HOOK_START, end: HOOK_END, preamble: PREAMBLE_LINE })\n}\n\n/** A leftover 0.2.1 `chronicle` hook in an rc file, header lines included, or null. */\nexport function findChronicleHook(content: string): BlockRegion | null {\n return blockRegion(content, { start: CHRONICLE_START, end: CHRONICLE_END, preamble: CHRONICLE_PREAMBLE_LINE })\n}\n\n/** A template's own block, start marker to end marker — what's compared with an installed one. */\nexport function hookBlockOf(hookContent: string): string {\n const startIndex = hookContent.indexOf(HOOK_START)\n const endIndex = hookContent.indexOf(HOOK_END)\n if (startIndex === -1 || endIndex === -1) return hookContent.trim()\n return hookContent.slice(startIndex, endIndex + HOOK_END.length)\n}\n\n/** The version an installed block declares; a block from before versions were declared is 1. */\nexport function installedHookVersion(block: string): number {\n const match = HOOK_VERSION_LINE.exec(block)\n return match ? Number(match[1]) : 1\n}\n\n/** Backs up an rc file before editing it, never overwriting an earlier backup, and says where it went. */\nfunction backUp(rcFile: string): void {\n log.info(`Backed up ${rcFile} → ${path.basename(backUpFile(rcFile))}`)\n}\n\nexport function getInstalledHookBlock(rcFile: string): string | null {\n // An rc file that's missing or can't be read has no hook — never a crash in `status`\n const content = fs.existsSync(rcFile) ? readRcFile(rcFile) : null\n if (content === null) return null\n const startIndex = content.indexOf(HOOK_START)\n const endIndex = content.indexOf(HOOK_END)\n if (startIndex === -1 || endIndex === -1) return null\n return content.slice(startIndex, endIndex + HOOK_END.length)\n}\n\nexport function isHookInstalled(rcFile: string): boolean {\n return getInstalledHookBlock(rcFile) !== null\n}\n\n/** The installed block differs from the template's — the template's header lines don't count. */\nexport function isHookOutdated(rcFile: string, hookContent: string): boolean {\n const installed = getInstalledHookBlock(rcFile)\n if (!installed) return false\n return installed.trim() !== hookBlockOf(hookContent).trim()\n}\n\nexport function installHook(rcFile: string, hookContent: string): void {\n backUp(rcFile)\n fs.appendFileSync(rcFile, `\\n${hookContent}\\n`)\n}\n\n/** Replaces the hook and every header line above it with the template — one header, however many there were. */\nexport function updateHook(rcFile: string, hookContent: string): void {\n backUp(rcFile)\n const content = fs.readFileSync(rcFile, 'utf-8')\n const region = hookRegion(content)\n if (!region) return\n fs.writeFileSync(rcFile, content.slice(0, region.start) + hookContent.trimEnd() + content.slice(region.end))\n}\n\n/** Cuts a region out of an rc file's content without leaving extra blank lines where it was. */\nfunction withoutRegion(content: string, region: BlockRegion): string {\n const before = content.slice(0, region.start).replace(/\\n+$/, '\\n')\n const after = content.slice(region.end).replace(/^\\n/, '')\n return before + after\n}\n\nexport function removeHook(rcFile: string): void {\n backUp(rcFile)\n const content = fs.readFileSync(rcFile, 'utf-8')\n const region = hookRegion(content)\n if (!region) return\n fs.writeFileSync(rcFile, withoutRegion(content, region))\n}\n\n/** Whether an rc file still holds a 0.2.1 `chronicle` hook. */\nexport function hasChronicleHook(rcFile: string): boolean {\n const content = readRcFile(rcFile)\n return content !== null && findChronicleHook(content) !== null\n}\n\n/** Removes a leftover `chronicle` hook, after backing the file up. Returns whether there was one. */\nexport function removeChronicleHook(rcFile: string): boolean {\n const content = readRcFile(rcFile)\n const region = content === null ? null : findChronicleHook(content)\n if (content === null || !region) return false\n backUp(rcFile)\n fs.writeFileSync(rcFile, withoutRegion(content, region))\n return true\n}\n\nexport interface ShellHookHealth {\n /** The hook is in the rc file but older than this version's template */\n isOutdated: boolean\n /** The rc file still holds a 0.2.1 `chronicle` hook */\n hasChronicleHook: boolean\n}\n\n/** What `status` warns about for the user's shell: an old hook, or a leftover Chronicle one. */\nexport function shellHookHealth(shell: ShellTarget): ShellHookHealth {\n const installed = getInstalledHookBlock(shell.rcFile)\n const currentVersion = SHELL_HOOK_VERSIONS[shell.name] ?? 1\n return {\n isOutdated: installed !== null && installedHookVersion(installed) < currentVersion,\n hasChronicleHook: hasChronicleHook(shell.rcFile),\n }\n}\n\nexport function resolveWorkscribeBinary(): string | null {\n try {\n const resolved = execFileSync('which', ['workscribe'], { encoding: 'utf-8' }).trim()\n return resolved || null\n } catch {\n return null\n }\n}\n\n/**\n * nodePath pins the Node interpreter alongside the workscribe script path.\n * Without it, workscribe's own shebang (`#!/usr/bin/env -S node ...`) still\n * dynamically resolves `node` via whatever PATH is active in the shell at\n * capture time — so a hardcoded binaryPath alone doesn't protect against a\n * stale/wrong default Node version in that shell, only against the\n * workscribe script itself moving. nodePath should be process.execPath,\n * captured at `workscribe init` time when it's guaranteed to be 22+.\n */\nexport function injectBinaryPath(hookContent: string, binaryPath: string, nodePath: string): string {\n // Invoking node directly (rather than executing workscribe's own shebang)\n // bypasses the `--disable-warning=ExperimentalWarning` flag postbuild.js\n // bakes into dist/index.js's shebang — shebang flags only apply when the\n // OS execs the file via its shebang line, not when node is invoked\n // explicitly. Without repeating the flag here, every pinned-node-path\n // invocation (the default whenever a version manager is detected) leaks\n // the node:sqlite ExperimentalWarning to stderr.\n const pinnedInvocation = `\"${nodePath}\" --disable-warning=ExperimentalWarning \"${binaryPath}\"`\n return hookContent\n .replace('command -v workscribe > /dev/null 2>&1 || return 0', `[[ -x \"${binaryPath}\" ]] || return 0`)\n .replace('command -v workscribe &>/dev/null || return 0', `[[ -x \"${binaryPath}\" ]] || return 0`)\n .replace('type -q workscribe\\n or return 0', `test -x \"${binaryPath}\"\\n or return 0`)\n .replaceAll('command workscribe _capture', `${pinnedInvocation} _capture`)\n .replaceAll('command workscribe alerts', `${pinnedInvocation} alerts`)\n}\n\nexport function isCompletionsInstalled(shell: ShellTarget): boolean {\n if (shell.name === 'fish') {\n return fs.existsSync(path.join(FISH_COMPLETIONS_DIR, 'workscribe.fish'))\n }\n try {\n return fs.readFileSync(shell.rcFile, 'utf-8').includes(COMPLETIONS_MARKER)\n } catch {\n return false\n }\n}\n\nconst FISH_COMPLETIONS_FILE = path.join(FISH_COMPLETIONS_DIR, 'workscribe.fish')\n\nfunction completionScriptFile(shellName: string): string {\n return path.join(WORKSCRIBE_DIR, `completion.${shellName}`)\n}\n\nfunction readRcFile(rcFile: string): string | null {\n try {\n return fs.readFileSync(rcFile, 'utf-8')\n } catch {\n return null // a missing rc file simply has no completions block\n }\n}\n\n/** (Re)writes the completion script itself — safe to run on every `init`, so a deleted data folder is repaired. */\nexport function writeCompletionScript(shell: ShellTarget, commands: CompletionCommand[]): void {\n if (shell.name === 'fish') {\n fs.mkdirSync(FISH_COMPLETIONS_DIR, { recursive: true })\n fs.writeFileSync(FISH_COMPLETIONS_FILE, fishCompletion(commands), { mode: 0o644 })\n return\n }\n const script = shell.name === 'zsh' ? zshCompletion(commands) : bashCompletion(commands)\n ensurePrivateDirectory(WORKSCRIBE_DIR)\n fs.writeFileSync(completionScriptFile(shell.name), script, { mode: 0o644 })\n}\n\nexport function installCompletions(shell: ShellTarget, commands: CompletionCommand[]): void {\n writeCompletionScript(shell, commands)\n // installCompletions can be called more than once (e.g. isCompletionsInstalled raced with a concurrent init).\n // The rcFile append must stay idempotent so repeated calls never grow duplicate blocks.\n if (shell.name !== 'fish' && !isCompletionsInstalled(shell)) {\n fs.appendFileSync(shell.rcFile, `\\n${completionsBlock(shell.name, completionScriptFile(shell.name))}\\n`)\n }\n}\n\n/** The rc file has a completions block from an older version — one that errors once its script is deleted. */\nexport function isCompletionsOutdated(shell: ShellTarget): boolean {\n if (shell.name === 'fish') return false // fish completions are a standalone file, not an rc-file line\n const content = readRcFile(shell.rcFile)\n return (\n content !== null &&\n isCompletionsBlockOutdated(content, completionsBlock(shell.name, completionScriptFile(shell.name)))\n )\n}\n\nexport function updateCompletions(shell: ShellTarget): void {\n const content = readRcFile(shell.rcFile)\n if (content === null || !hasCompletionsBlock(content)) return\n backUp(shell.rcFile)\n fs.writeFileSync(\n shell.rcFile,\n replaceCompletionsBlock(content, completionsBlock(shell.name, completionScriptFile(shell.name)))\n )\n}\n\n/** Removes the completions block from an rc file, if it has one. Returns whether anything was removed. */\nexport function removeCompletions(rcFile: string): boolean {\n const content = readRcFile(rcFile)\n if (content === null || !hasCompletionsBlock(content)) return false\n backUp(rcFile)\n fs.writeFileSync(rcFile, removeCompletionsBlock(content))\n return true\n}\n\n/** Removes the fish completion file, if present. Returns whether anything was removed. */\nexport function removeFishCompletions(): boolean {\n if (!fs.existsSync(FISH_COMPLETIONS_FILE)) return false\n fs.rmSync(FISH_COMPLETIONS_FILE, { force: true })\n return true\n}\n","import { execSync } from 'child_process'\n\n/** Returns true if the given CLI command is available on PATH */\nexport function isCommandInstalled(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'ignore' })\n return true\n } catch {\n return false\n }\n}\n","// The command strings Workscribe writes into ~/.claude/settings.json, and how to read them back. Pure.\n//\n// Every command is guarded: when the binary (or the Node it's pinned to) is gone — say the package was removed\n// without `workscribe uninstall` — the hook exits 0 silently instead of erroring on every prompt and tool call.\n// `_hook` itself never exits non-zero, so the trailing `|| true` only ever hides a missing binary or a crash.\n\n// Identifies a workscribe hook entry in any of its forms — legacy bare, pinned, guarded — since all contain it\nexport const HOOK_MARKER = '_hook --source claude-code'\n\nconst GUARD_SUFFIX = ' || true'\n// The `test -x \"<path>\" && ` guards in front of a pinned command\nconst LEADING_PATH_GUARDS = /^(?:test -x \"[^\"]*\" && )+/\nconst BARE_GUARD = 'command -v workscribe >/dev/null 2>&1 && '\n\n/**\n * The command for one hook. Pinned (node and script paths known at install time) when possible, so it's immune to\n * PATH and Node-version drift; bare (`workscribe …`) otherwise. `args` follow the binary, e.g. `_hook --source …`.\n */\nexport function buildHookCommand(options: { nodePath: string; binaryPath: string | null; args: string }): string {\n if (!options.binaryPath) return `${BARE_GUARD}workscribe ${options.args}${GUARD_SUFFIX}`\n const { nodePath, binaryPath, args } = options\n return `test -x \"${nodePath}\" && test -x \"${binaryPath}\" && \"${nodePath}\" \"${binaryPath}\" ${args}${GUARD_SUFFIX}`\n}\n\nfunction withoutLeadingGuards(command: string): string {\n return command.trimStart().replace(LEADING_PATH_GUARDS, '')\n}\n\n/**\n * Pinned: invokes an absolute path (a Node interpreter) rather than the bare `workscribe` string — immune to\n * PATH/Node-version drift between install time and whatever environment the hook fires in.\n */\nexport function isPinnedCommand(command: string): boolean {\n const invocation = withoutLeadingGuards(command)\n return invocation.startsWith('\"') || invocation.startsWith('/')\n}\n\n/** Guarded: does nothing, silently, when the binary is gone. */\nexport function isGuardedCommand(command: string): boolean {\n const trimmed = command.trim()\n const hasLeadingGuard = LEADING_PATH_GUARDS.test(trimmed) || trimmed.startsWith(BARE_GUARD)\n return hasLeadingGuard && trimmed.endsWith(GUARD_SUFFIX)\n}\n\n/** The Node interpreter a pinned command runs (`\"<node>\" \"<workscribe>\" …`), or null for a bare command. */\nexport function pinnedNodePath(command: string): string | null {\n return withoutLeadingGuards(command).match(/^\"([^\"]+)\"/)?.[1] ?? null\n}\n","import fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { execFileSync } from 'child_process'\nimport type { AIHookIntegration, HookUninstallOutcome } from './types'\nimport { isCommandInstalled } from './utils'\nimport { resolveWorkscribeBinary } from '../shell-hook'\nimport { backUpFile } from '../../utils/backup'\nimport { buildHookCommand, HOOK_MARKER, isGuardedCommand, isPinnedCommand, pinnedNodePath } from './claude-code-command'\n\nconst CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json')\n\nfunction readSettings(): Record<string, unknown> {\n if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) return {}\n try {\n return JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8')) as Record<string, unknown>\n } catch {\n return {}\n }\n}\n\nfunction commandsForEvent(hooks: Record<string, unknown>, eventName: string): string[] {\n const entries = (hooks[eventName] ?? []) as unknown[]\n const commands: string[] = []\n for (const entry of entries) {\n const entryHooks = ((entry as Record<string, unknown>).hooks ?? []) as unknown[]\n for (const hookItem of entryHooks) {\n const hook = hookItem as Record<string, unknown>\n if (typeof hook.command === 'string' && hook.command.includes(HOOK_MARKER)) {\n commands.push(hook.command)\n }\n }\n }\n return commands\n}\n\nfunction findWorkscribeHookCommands(settings: Record<string, unknown>): {\n postToolUse: string[]\n postToolUseFailure: string[]\n userPromptSubmit: string[]\n} {\n const hooks = (settings.hooks ?? {}) as Record<string, unknown>\n return {\n postToolUse: commandsForEvent(hooks, 'PostToolUse'),\n postToolUseFailure: commandsForEvent(hooks, 'PostToolUseFailure'),\n userPromptSubmit: commandsForEvent(hooks, 'UserPromptSubmit'),\n }\n}\n\n/**\n * Strips any existing workscribe hook entries out of a hooks array for one\n * event type, so install() can be called repeatedly — fresh install or\n * repairing a stale/unpinned one — without ever accumulating duplicates.\n */\nfunction withoutWorkscribeEntries(entries: unknown[]): unknown[] {\n return entries\n .map((entry) => {\n const record = entry as Record<string, unknown>\n const entryHooks = (record.hooks ?? []) as unknown[]\n const filteredHooks = entryHooks.filter((hookItem) => {\n const hook = hookItem as Record<string, unknown>\n return !(typeof hook.command === 'string' && hook.command.includes(HOOK_MARKER))\n })\n return { ...record, hooks: filteredHooks }\n })\n .filter((entry) => ((entry as Record<string, unknown>).hooks as unknown[]).length > 0)\n}\n\nexport interface ClaudeCodeHookHealth {\n installed: boolean\n pinned: boolean\n /** Every Workscribe hook exits silently when the binary is gone (false for hooks from Workscribe ≤ 2.0.0). */\n guarded: boolean\n /** A pinned hook's Node interpreter no longer exists (e.g. removed by a version manager) — capture is off. */\n nodeMissing: boolean\n /** Only meaningful when installed && !pinned; null otherwise. */\n confirmedBroken: boolean | null\n}\n\nfunction isExecutable(filePath: string): boolean {\n try {\n fs.accessSync(filePath, fs.constants.X_OK)\n return true\n } catch {\n return false\n }\n}\n\nfunction allWorkscribeCommands(): string[] {\n const { postToolUse, postToolUseFailure, userPromptSubmit } = findWorkscribeHookCommands(readSettings())\n return [...postToolUse, ...postToolUseFailure, ...userPromptSubmit]\n}\n\n/**\n * Checks whether an installed Claude Code hook uses the pinned (safe) or\n * bare (vulnerable to Node-version/PATH drift) command form, and — for bare\n * commands only — attempts a live probe to confirm whether it's actually\n * broken right now. The probe is best-effort: it inherits this process's own\n * environment, which can't perfectly replicate whatever environment Claude\n * Code invokes hooks in, but a failure here is still strong evidence of a\n * real, currently-happening problem rather than a theoretical risk.\n */\nexport function checkHookHealth(): ClaudeCodeHookHealth {\n const allCommands = allWorkscribeCommands()\n if (allCommands.length === 0) {\n return { installed: false, pinned: true, guarded: true, nodeMissing: false, confirmedBroken: null }\n }\n\n const guarded = allCommands.every(isGuardedCommand)\n const nodePaths = allCommands.map(pinnedNodePath).filter((nodePath): nodePath is string => nodePath !== null)\n const nodeMissing = nodePaths.some((nodePath) => !isExecutable(nodePath))\n if (allCommands.every(isPinnedCommand)) {\n return { installed: true, pinned: true, guarded, nodeMissing, confirmedBroken: null }\n }\n\n let confirmedBroken: boolean\n try {\n execFileSync('workscribe', ['--version'], { stdio: 'ignore', timeout: 2000 })\n confirmedBroken = false\n } catch {\n confirmedBroken = true\n }\n\n return { installed: true, pinned: false, guarded, nodeMissing, confirmedBroken }\n}\n\n/** Reads settings.json without forgiving a parse error — uninstall must never overwrite a file it couldn't read. */\nfunction readSettingsStrictly(): Record<string, unknown> | 'missing' | 'unreadable' {\n if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) return 'missing'\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8'))\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : 'unreadable'\n } catch {\n return 'unreadable'\n }\n}\n\n/** The hooks object with Workscribe's entries removed, empty event arrays dropped — or null if there were none. */\nfunction hooksWithoutWorkscribe(hooks: Record<string, unknown>): Record<string, unknown> | null {\n let removedAny = false\n const remaining: Record<string, unknown> = {}\n for (const [eventName, entries] of Object.entries(hooks)) {\n if (!Array.isArray(entries)) {\n remaining[eventName] = entries\n continue\n }\n const kept = withoutWorkscribeEntries(entries)\n if (commandsForEvent(hooks, eventName).length > 0) removedAny = true\n if (kept.length > 0) remaining[eventName] = kept\n }\n return removedAny ? remaining : null\n}\n\n/**\n * Both events have a Workscribe hook, and every one is pinned, guarded, and pinned to a Node.js that still exists.\n * The guard keeps a hook whose Node a version manager removed quiet — and so quietly broken — unless `init` sees it.\n */\nfunction isHookCurrent(): boolean {\n const commandsByEvent = findWorkscribeHookCommands(readSettings())\n // An install from before the failure hook (WOR-127) is outdated: its commands never record a failing exit code\n if (Object.values(commandsByEvent).some((commands) => commands.length === 0)) return false\n return Object.values(commandsByEvent)\n .flat()\n .every((command) => {\n const nodePath = pinnedNodePath(command)\n return isPinnedCommand(command) && isGuardedCommand(command) && nodePath !== null && isExecutable(nodePath)\n })\n}\n\nexport const claudeCodeIntegration: AIHookIntegration = {\n displayName: 'Claude Code',\n\n isInstalled() {\n return isCommandInstalled('claude')\n },\n\n // \"Installed\" means a *current* hook is present, not just any workscribe hook — so `workscribe init` offers to\n // update a stale one (unpinned, unguarded, or pinned to a Node that's gone) through its outdated-hook prompt\n isHookInstalled() {\n return isHookCurrent()\n },\n\n install() {\n const settings = readSettings()\n const existingHooks = (settings.hooks ?? {}) as Record<string, unknown>\n const existingPostToolUse = withoutWorkscribeEntries((existingHooks.PostToolUse ?? []) as unknown[])\n const existingPostToolUseFailure = withoutWorkscribeEntries((existingHooks.PostToolUseFailure ?? []) as unknown[])\n const existingUserPromptSubmit = withoutWorkscribeEntries((existingHooks.UserPromptSubmit ?? []) as unknown[])\n\n const binaryPath = resolveWorkscribeBinary()\n const command = (args: string) => buildHookCommand({ nodePath: process.execPath, binaryPath, args })\n\n settings.hooks = {\n ...existingHooks,\n PostToolUse: [\n ...existingPostToolUse,\n {\n matcher: 'Bash',\n hooks: [{ type: 'command', command: command(HOOK_MARKER), async: true }],\n },\n ],\n // Failed Bash calls only fire this one, with the exit code in `error` — PostToolUse alone never sees a failure\n PostToolUseFailure: [\n ...existingPostToolUseFailure,\n {\n matcher: 'Bash',\n hooks: [{ type: 'command', command: command(`${HOOK_MARKER} --event PostToolUseFailure`), async: true }],\n },\n ],\n UserPromptSubmit: [\n ...existingUserPromptSubmit,\n {\n hooks: [\n {\n type: 'command',\n command: command(`${HOOK_MARKER} --event UserPromptSubmit`),\n },\n ],\n },\n ],\n }\n fs.mkdirSync(path.dirname(CLAUDE_SETTINGS_PATH), { recursive: true })\n fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2))\n },\n\n // Some workscribe hook is present, but not all of them are current (pinned and guarded) — including a\n // half-installed one where only PostToolUse or only UserPromptSubmit exists\n isHookOutdated() {\n return allWorkscribeCommands().length > 0 && !isHookCurrent()\n },\n\n uninstall(): HookUninstallOutcome {\n const settings = readSettingsStrictly()\n if (settings === 'missing') return 'not-installed'\n if (settings === 'unreadable') return 'unreadable'\n const hooks = settings.hooks\n if (hooks === null || typeof hooks !== 'object' || Array.isArray(hooks)) return 'not-installed'\n const remainingHooks = hooksWithoutWorkscribe(hooks as Record<string, unknown>)\n if (!remainingHooks) return 'not-installed'\n\n const updated: Record<string, unknown> = { ...settings, hooks: remainingHooks }\n if (Object.keys(remainingHooks).length === 0) delete updated.hooks\n backUpFile(CLAUDE_SETTINGS_PATH)\n fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(updated, null, 2))\n return 'removed'\n },\n}\n","import type { AIHookIntegration } from './types'\nimport { claudeCodeIntegration } from './claude-code'\n\n/**\n * Registry of AI tool hook integrations, keyed by the \"hook\" field in src/config/ai-tools.json.\n * Adding support for a new tool = add a new entry here and a corresponding integration file.\n */\nexport const AI_HOOK_INTEGRATIONS: Record<string, AIHookIntegration> = {\n 'claude-code': claudeCodeIntegration,\n}\n","import { Command } from 'commander'\nimport { log } from '../utils/logger'\nimport { CompletionCommand, bashCompletion, zshCompletion, fishCompletion } from '../utils/completion-scripts'\n\nexport type { CompletionCommand }\nexport { bashCompletion, zshCompletion, fishCompletion }\n\nexport const HIDDEN_COMMANDS = new Set(['_capture', '_hook'])\n\n// _capture/_hook fire on every shell command as a detached background process\n// that must never add latency — startup checks (update check, telemetry) skip\n// those outright rather than relying on a cache alone.\nexport function shouldRunStartupChecks(invokedCommand: string | undefined): boolean {\n if (invokedCommand === undefined) return true\n return !HIDDEN_COMMANDS.has(invokedCommand)\n}\n\n// Data jobs (src/db/jobs/) run only before these: commands people type that read the data. Never the background\n// _capture/_hook, never `context` (the editor calls it with a 5 s timeout), never `sessions` (the agent skill calls\n// it), and never commands that remove or reset the database (`uninstall`, `clear`, `init`).\nexport const DATA_JOB_COMMANDS = new Set(['status', 'summary', 'week', 'export', 'events', 'upgrade'])\n// The extension detects the CLI with `--version` on a 2 s timeout; help output should never wait on a backup either\nconst INFORMATION_FLAGS = new Set(['--version', '-V', '--help', '-h'])\n\nexport function shouldRunDataJobs(args: readonly string[]): boolean {\n const [command] = args\n if (command === undefined || !DATA_JOB_COMMANDS.has(command)) return false\n return !args.some((arg) => INFORMATION_FLAGS.has(arg))\n}\n\nexport function getPublicCommands(program: Command): CompletionCommand[] {\n return program.commands\n .filter((command) => !HIDDEN_COMMANDS.has(command.name()))\n .map((command) => ({ name: command.name(), description: command.description() }))\n}\n\nexport function registerCompletions(program: Command): void {\n program\n .command('completions [shell]')\n .description('Print shell completion script (bash, zsh, fish)')\n .action((shell?: string) => {\n const detected = shell ?? (process.env.SHELL ?? '').split('/').at(-1)\n\n if (!detected || !['bash', 'zsh', 'fish'].includes(detected)) {\n log.error(`Unknown shell: \"${detected ?? ''}\". Supported: bash, zsh, fish`)\n log.plain(` Usage: workscribe completions <bash|zsh|fish>`)\n process.exit(1)\n }\n\n const commands = getPublicCommands(program)\n const script =\n detected === 'bash'\n ? bashCompletion(commands)\n : detected === 'zsh'\n ? zshCompletion(commands)\n : fishCompletion(commands)\n\n process.stdout.write(script + '\\n')\n })\n}\n","/** Caches a single-argument lookup by its string key for the lifetime of the returned function. */\nexport function memoize<Value>(lookup: (key: string) => Value): (key: string) => Value {\n // Boxed so a cached null/undefined result is told apart from a missing entry\n const cache = new Map<string, { value: Value }>()\n return (key) => {\n const cached = cache.get(key)\n if (cached) return cached.value\n const value = lookup(key)\n cache.set(key, { value })\n return value\n }\n}\n","import type { SkillAgent } from './types'\n\n/** Folder name the skill is installed under, inside each agent's skills folder. */\nexport const SKILL_NAME = 'workscribe'\n\n/** Marker file that identifies a Workscribe-installed copy of the skill. */\nexport const MARKER_FILE = '.workscribe-skill.json'\n\n// Skills folders checked against each vendor's docs (2026-10-26). If a vendor moves its folder,\n// only this table needs to change.\nexport const SKILL_AGENTS: SkillAgent[] = [\n {\n id: 'claude-code',\n name: 'Claude Code',\n skillsFolder: '.claude/skills',\n commands: ['claude'],\n homeFolders: ['.claude'],\n vscodeExtensionPrefixes: [],\n docsUrl: 'https://code.claude.com/docs/en/skills',\n },\n {\n id: 'codex',\n name: 'Codex',\n skillsFolder: '.agents/skills',\n commands: ['codex'],\n homeFolders: ['.codex'],\n vscodeExtensionPrefixes: [],\n docsUrl: 'https://learn.chatgpt.com/docs/build-skills',\n },\n {\n id: 'copilot',\n name: 'GitHub Copilot',\n skillsFolder: '.agents/skills',\n commands: ['copilot'],\n homeFolders: ['.copilot'],\n vscodeExtensionPrefixes: ['github.copilot-chat'],\n docsUrl: 'https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills',\n },\n {\n id: 'cursor',\n name: 'Cursor',\n skillsFolder: '.cursor/skills',\n commands: ['cursor', 'cursor-agent'],\n homeFolders: ['.cursor'],\n vscodeExtensionPrefixes: [],\n docsUrl: 'https://cursor.com/docs/skills',\n },\n]\n\nexport const SKILL_AGENT_IDS = SKILL_AGENTS.map((agent) => agent.id)\n","import fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport { isCommandInstalled } from '../capture/hooks/utils'\nimport { memoize } from '../utils/memoize'\nimport { SKILL_AGENTS } from './agents'\nimport type { SkillAgent } from './types'\n\n/** The I/O detection needs — injected so tests can describe a machine without touching the real one. */\nexport interface DetectionEnvironment {\n homeDir: string\n isCommandInstalled: (command: string) => boolean\n pathExists: (absolutePath: string) => boolean\n listDirectory: (absolutePath: string) => string[]\n}\n\nexport function defaultDetectionEnvironment(): DetectionEnvironment {\n return {\n homeDir: os.homedir(),\n // `which` spawns a process, so each command is looked up at most once per run\n isCommandInstalled: memoize(isCommandInstalled),\n pathExists: (absolutePath) => fs.existsSync(absolutePath),\n listDirectory: (absolutePath) => {\n try {\n return fs.readdirSync(absolutePath)\n } catch {\n return [] // a missing extensions folder just means no extensions\n }\n },\n }\n}\n\nfunction hasHomeFolder(agent: SkillAgent, environment: DetectionEnvironment): boolean {\n return agent.homeFolders.some((folder) => environment.pathExists(path.join(environment.homeDir, folder)))\n}\n\nfunction hasVscodeExtension(agent: SkillAgent, environment: DetectionEnvironment): boolean {\n if (agent.vscodeExtensionPrefixes.length === 0) return false\n const extensions = environment.listDirectory(path.join(environment.homeDir, '.vscode', 'extensions'))\n return extensions.some((extension) => agent.vscodeExtensionPrefixes.some((prefix) => extension.startsWith(prefix)))\n}\n\n/** Folder signals are checked first; `which` is only spawned when none of them found the agent. */\nexport function isAgentPresent(agent: SkillAgent, environment: DetectionEnvironment): boolean {\n return (\n hasHomeFolder(agent, environment) ||\n hasVscodeExtension(agent, environment) ||\n agent.commands.some((command) => environment.isCommandInstalled(command))\n )\n}\n\nexport function detectAgents(environment: DetectionEnvironment = defaultDetectionEnvironment()): SkillAgent[] {\n return SKILL_AGENTS.filter((agent) => isAgentPresent(agent, environment))\n}\n","import type { InstallOutcome, SkillAgent, SkillInstallState, SkillMarker, SkillsFolderGroup } from './types'\n\n/** One entry per distinct skills folder, in registry order — Codex and Copilot share `.agents/skills`. */\nexport function groupBySkillsFolder(agents: SkillAgent[]): SkillsFolderGroup[] {\n const groups = new Map<string, SkillAgent[]>()\n for (const agent of agents) groups.set(agent.skillsFolder, [...(groups.get(agent.skillsFolder) ?? []), agent])\n return [...groups.entries()].map(([skillsFolder, groupAgents]) => ({ skillsFolder, agents: groupAgents }))\n}\n\n/**\n * Names the agents a folder serves, noting any other agent that reads the same folder — installing or\n * removing for Codex also changes it for GitHub Copilot, and the message should say so.\n */\nexport function describeFolderAgents(group: SkillsFolderGroup, allAgents: SkillAgent[]): string {\n const sharers = allAgents.filter(\n (agent) => agent.skillsFolder === group.skillsFolder && !group.agents.includes(agent)\n )\n const names = joinAgentNames(group.agents)\n return sharers.length === 0 ? names : `${names} (folder shared with ${joinAgentNames(sharers)})`\n}\n\n/** The result `init` can report without asking: up to date, or a folder it doesn't own. Null means ask. */\nexport function outcomeWithoutPrompt(state: SkillInstallState): InstallOutcome | null {\n if (state.kind === 'installed') return 'up-to-date'\n if (state.kind === 'not-ours') return 'skipped-not-ours'\n return null\n}\n\n/** \"Codex\", \"Codex and GitHub Copilot\", \"Claude Code, Codex and Cursor\". */\nexport function joinAgentNames(agents: SkillAgent[]): string {\n const names = agents.map((agent) => agent.name)\n if (names.length <= 1) return names[0] ?? ''\n return `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n}\n\n/** What's in an agent's `workscribe` skill folder, judged by its marker. */\nexport function describeInstallState(input: {\n folderExists: boolean\n marker: SkillMarker | null\n currentVersion: string\n}): SkillInstallState {\n if (!input.folderExists) return { kind: 'not-installed' }\n if (!input.marker) return { kind: 'not-ours' }\n if (input.marker.version === input.currentVersion) return { kind: 'installed', version: input.marker.version }\n return { kind: 'outdated', version: input.marker.version }\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { MARKER_FILE, SKILL_NAME } from './agents'\nimport { describeInstallState } from './state'\nimport type { InstallOutcome, InstallSkillOptions, SkillInstallState, SkillMarker, UninstallOutcome } from './types'\n\nfunction isSkillMarker(value: unknown): value is SkillMarker {\n const candidate = value as Partial<SkillMarker> | null\n return typeof candidate?.version === 'string' && typeof candidate?.installedAt === 'string'\n}\n\nfunction readMarker(skillDir: string): SkillMarker | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(path.join(skillDir, MARKER_FILE), 'utf-8'))\n return isSkillMarker(parsed) ? parsed : null\n } catch {\n return null // no marker, or not ours to read — either way it isn't a Workscribe install\n }\n}\n\n/** State of the Workscribe skill inside one agent skills folder (e.g. `~/.claude/skills`). */\nexport function readInstallState(skillsFolder: string, currentVersion: string): SkillInstallState {\n const skillDir = path.join(skillsFolder, SKILL_NAME)\n return describeInstallState({\n folderExists: fs.existsSync(skillDir),\n marker: readMarker(skillDir),\n currentVersion,\n })\n}\n\nfunction copyDirectory(from: string, to: string): void {\n fs.mkdirSync(to, { recursive: true })\n for (const entry of fs.readdirSync(from, { withFileTypes: true })) {\n const source = path.join(from, entry.name)\n const destination = path.join(to, entry.name)\n if (entry.isDirectory()) copyDirectory(source, destination)\n else fs.copyFileSync(source, destination)\n }\n}\n\n/**\n * Swaps a fully prepared copy into place: move any existing copy aside, rename the new one in, then\n * delete the old one — or put it back if the rename fails. A plain rename over a non-empty folder\n * fails on POSIX, and this way an interrupted install never leaves a half-written skill.\n */\nfunction swapInto(skillDir: string, preparedDir: string): void {\n const previousDir = path.join(path.dirname(skillDir), `.${SKILL_NAME}.old-${process.pid}`)\n fs.rmSync(previousDir, { recursive: true, force: true }) // left behind by a run that was killed\n const hadPrevious = fs.existsSync(skillDir)\n if (hadPrevious) fs.renameSync(skillDir, previousDir)\n try {\n fs.renameSync(preparedDir, skillDir)\n } catch (error) {\n if (hadPrevious) fs.renameSync(previousDir, skillDir)\n throw error\n }\n if (hadPrevious) fs.rmSync(previousDir, { recursive: true, force: true })\n}\n\nexport function installSkill(options: InstallSkillOptions): InstallOutcome {\n const state = readInstallState(options.skillsFolder, options.version)\n if (state.kind === 'installed') return 'up-to-date'\n if (state.kind === 'not-ours' && !options.force) return 'skipped-not-ours'\n\n fs.mkdirSync(options.skillsFolder, { recursive: true })\n const preparedDir = path.join(options.skillsFolder, `.${SKILL_NAME}.tmp-${process.pid}`)\n fs.rmSync(preparedDir, { recursive: true, force: true }) // left behind by a run that was killed\n try {\n copyDirectory(options.sourceDir, preparedDir)\n const marker: SkillMarker = { version: options.version, installedAt: new Date().toISOString() }\n fs.writeFileSync(path.join(preparedDir, MARKER_FILE), JSON.stringify(marker, null, 2))\n swapInto(path.join(options.skillsFolder, SKILL_NAME), preparedDir)\n } finally {\n fs.rmSync(preparedDir, { recursive: true, force: true })\n }\n return state.kind === 'not-installed' ? 'installed' : 'updated'\n}\n\n/** Removes the skill only if Workscribe installed it — a user's own `workscribe` skill is left alone. */\nexport function uninstallSkill(skillsFolder: string): UninstallOutcome {\n const skillDir = path.join(skillsFolder, SKILL_NAME)\n if (!fs.existsSync(skillDir)) return 'not-installed'\n if (!readMarker(skillDir)) return 'skipped-not-ours'\n fs.rmSync(skillDir, { recursive: true, force: true })\n return 'removed'\n}\n","import fs from 'fs'\nimport path from 'path'\nimport { SKILL_NAME } from './agents'\n\nconst SKILL_ENTRY_FILE = 'SKILL.md'\n\n/**\n * Where the skill shipped in this npm package lives. The bundle runs from `dist/`, so it's one level\n * up; the TypeScript sources run from `src/<folder>/`, so it's two. The first with a SKILL.md wins.\n */\nexport function packagedSkillCandidates(fromDirectory: string): string[] {\n return [\n path.join(fromDirectory, '..', 'skills', SKILL_NAME),\n path.join(fromDirectory, '..', '..', 'skills', SKILL_NAME),\n ]\n}\n\nexport function resolvePackagedSkillDir(fromDirectory: string = __dirname): string {\n const found = packagedSkillCandidates(fromDirectory).find((candidate) =>\n fs.existsSync(path.join(candidate, SKILL_ENTRY_FILE))\n )\n if (!found) {\n throw new Error(\n 'The Workscribe skill files are missing from this install. Reinstall it: npm install -g @workscribe/cli'\n )\n }\n return found\n}\n","// Friendly wording for skill install results and status, shared by `workscribe skill`, `init` and `status`.\nimport type { InstallOutcome, SkillInstallState, UninstallOutcome } from './types'\n\nexport type MessageLevel = 'success' | 'info' | 'warn'\n\nexport interface Message {\n level: MessageLevel\n text: string\n}\n\n/** `/Users/dev/.claude/skills` → `~/.claude/skills`, so paths read the way users type them. */\nexport function displayPath(absolutePath: string, homeDir: string): string {\n return absolutePath.startsWith(homeDir) ? `~${absolutePath.slice(homeDir.length)}` : absolutePath\n}\n\nexport function describeInstallOutcome(input: {\n outcome: InstallOutcome\n agents: string\n location: string\n version: string\n}): Message {\n switch (input.outcome) {\n case 'installed':\n return { level: 'success', text: `Installed the Workscribe skill for ${input.agents} (${input.location})` }\n case 'updated':\n return { level: 'success', text: `Updated the Workscribe skill for ${input.agents} to ${input.version}` }\n case 'up-to-date':\n return { level: 'info', text: `The Workscribe skill is already up to date for ${input.agents}` }\n case 'skipped-not-ours':\n return {\n level: 'warn',\n text:\n `Skipped ${input.agents} — ${input.location} already holds a workscribe skill that Workscribe didn't install. ` +\n 'Run workscribe skill install --force to replace it.',\n }\n }\n}\n\nexport function describeUninstallOutcome(input: {\n outcome: UninstallOutcome\n agents: string\n location: string\n}): Message {\n switch (input.outcome) {\n case 'removed':\n return { level: 'success', text: `Removed the Workscribe skill from ${input.agents} (${input.location})` }\n case 'not-installed':\n return { level: 'info', text: `The Workscribe skill isn't installed for ${input.agents}` }\n case 'skipped-not-ours':\n return {\n level: 'warn',\n text: `Left ${input.location} alone — that workscribe skill wasn't installed by Workscribe.`,\n }\n }\n}\n\n/** One status phrase per state, with the command that fixes it when something needs doing. */\nexport function describeSkillStatus(state: SkillInstallState, currentVersion: string): Message {\n switch (state.kind) {\n case 'installed':\n return { level: 'success', text: `installed (${state.version})` }\n case 'outdated':\n return {\n level: 'warn',\n text: `outdated (${state.version}; this CLI is ${currentVersion}) — run workscribe skill install`,\n }\n case 'not-installed':\n return { level: 'info', text: 'not installed — run workscribe skill install' }\n case 'not-ours':\n return {\n level: 'warn',\n text: 'a different workscribe skill is there — run workscribe skill install --force to replace it',\n }\n }\n}\n\n/** The reason from anything thrown — fs errors carry useful text like \"EACCES: permission denied\". */\nexport function reasonOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nexport function describeInstallFailure(input: { agents: string; location: string; error: unknown }): Message {\n return {\n level: 'warn',\n text:\n `Couldn't install the Workscribe skill for ${input.agents} (${reasonOf(input.error)}). ` +\n `Check you can write to ${input.location}, then run: workscribe skill install`,\n }\n}\n\nexport function describeUninstallFailure(input: { agents: string; location: string; error: unknown }): Message {\n return {\n level: 'warn',\n text:\n `Couldn't remove the Workscribe skill for ${input.agents} (${reasonOf(input.error)}). ` +\n `Delete it yourself: rm -rf ${input.location}`,\n }\n}\n\nexport const AFTER_INSTALL_HINT =\n 'Restart any agent that is already open, then ask it to \"summarize my work today\" (or type /workscribe in Claude Code).'\n","// Install, remove and report the skill one skills folder at a time, with friendly messages. Shared by\n// `workscribe skill`, `init`, `status` and `uninstall`; a failure in one folder is reported, never thrown,\n// so the rest of those commands carries on.\nimport os from 'os'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { SKILL_AGENTS, SKILL_NAME } from './agents'\nimport { installSkill, readInstallState, uninstallSkill } from './installer'\nimport {\n describeInstallFailure,\n describeInstallOutcome,\n describeSkillStatus,\n describeUninstallFailure,\n describeUninstallOutcome,\n displayPath,\n type Message,\n type MessageLevel,\n} from './report'\nimport { describeFolderAgents, groupBySkillsFolder } from './state'\nimport type { SkillAgent, SkillsFolderGroup } from './types'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\n/** Width of the agent-name column in `skill status` and `workscribe status`. */\nexport const AGENT_NAME_COLUMN_WIDTH = 16\n\nconst STATUS_COLOURS: Record<MessageLevel, (text: string) => string> = {\n success: chalk.green,\n warn: chalk.yellow,\n info: chalk.dim,\n}\n\n/** Where a group's skill lives, and how messages should name the agents it serves. */\nexport interface FolderTarget {\n skillsFolder: string\n location: string\n agents: string\n}\n\nexport function describeTarget(group: SkillsFolderGroup, homeDir: string = os.homedir()): FolderTarget {\n const skillsFolder = path.join(homeDir, group.skillsFolder)\n return {\n skillsFolder,\n location: displayPath(path.join(skillsFolder, SKILL_NAME), homeDir),\n agents: describeFolderAgents(group, SKILL_AGENTS),\n }\n}\n\nexport function printMessage(message: Message): void {\n log[message.level](message.text)\n}\n\nexport function installIntoFolder(options: { target: FolderTarget; sourceDir: string; force?: boolean }): Message {\n const { target } = options\n try {\n const outcome = installSkill({\n sourceDir: options.sourceDir,\n skillsFolder: target.skillsFolder,\n version: __WORKSCRIBE_VERSION__,\n force: options.force,\n })\n return describeInstallOutcome({ ...target, outcome, version: __WORKSCRIBE_VERSION__ })\n } catch (error) {\n return describeInstallFailure({ ...target, error })\n }\n}\n\n/**\n * Removes Workscribe-installed copies from each agent's skills folder. `workscribe uninstall` passes\n * `reportMissing: false` so folders that never had the skill don't add noise.\n */\nexport function removeSkills(agents: SkillAgent[], options: { reportMissing: boolean }): void {\n for (const group of groupBySkillsFolder(agents)) {\n const target = describeTarget(group)\n try {\n const outcome = uninstallSkill(target.skillsFolder)\n if (outcome === 'not-installed' && !options.reportMissing) continue\n printMessage(describeUninstallOutcome({ ...target, outcome }))\n } catch (error) {\n printMessage(describeUninstallFailure({ ...target, error }))\n }\n }\n}\n\n/** \"Claude Code installed (2.0.0) ~/.claude/skills/workscribe\" */\nexport function formatAgentSkillLine(agent: SkillAgent, homeDir: string = os.homedir()): string {\n const location = displayPath(path.join(homeDir, agent.skillsFolder, SKILL_NAME), homeDir)\n const installState = readInstallState(path.join(homeDir, agent.skillsFolder), __WORKSCRIBE_VERSION__)\n const status = describeSkillStatus(installState, __WORKSCRIBE_VERSION__)\n return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${STATUS_COLOURS[status.level](status.text)} ${chalk.dim(location)}`\n}\n\nexport function formatUndetectedAgentLine(agent: SkillAgent): string {\n return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${chalk.dim('not detected')}`\n}\n","// Workscribe 2.0 removed AI providers (cloud and Ollama). These helpers spot 1.x settings so users hear about\n// it once per run, can't set them again by accident, and can have them (and any stored API key) removed.\nimport type { WorkscribeConfig } from '../config'\n\n/** The only `summary.engine` value 2.0 still means — setting it is harmless, so it isn't a leftover. */\nexport const TEMPLATE_ENGINE = 'template'\n\nconst LEGACY_AI_FIELDS = ['provider', 'apiKey', 'model', 'baseUrl'] as const\n\nconst REMOVED_HEADLINE = 'AI providers were removed in Workscribe 2.0 — summaries now use the offline template engine.'\n\nconst SKILL_HINT =\n 'For AI-written summaries, install the Workscribe skill and ask your coding agent:\\n workscribe skill install'\n\n/** Why `config set` refuses `ai.*` and `summary.engine`, and what to do instead. */\nexport const REMOVED_AI_SETTING_MESSAGE = `${REMOVED_HEADLINE}\\n${SKILL_HINT}`\n\nexport function hasLegacyAiSettings(config: WorkscribeConfig): boolean {\n const hasAiField = LEGACY_AI_FIELDS.some((field) => Boolean(config.ai?.[field]))\n const engine = config.summary?.engine\n return hasAiField || (engine !== undefined && engine !== TEMPLATE_ENGINE)\n}\n\n/** The once-per-run notice for a 1.x config, or null when there's nothing left over. `configPath` is shown as-is. */\nexport function describeLegacyAiSettings(config: WorkscribeConfig, configPath: string): string | null {\n if (!hasLegacyAiSettings(config)) return null\n return (\n `${REMOVED_HEADLINE}\\n${SKILL_HINT}\\n` +\n `Your old AI settings (including any API key) are still in ${configPath}. To remove them, run:\\n workscribe init`\n )\n}\n\n/** `ai`, `ai.<anything>`, `summary.engine` and anything under it, in any case — settings `config set` refuses. */\nexport function isRemovedAiSetting(key: string): boolean {\n const normalizedKey = key.toLowerCase()\n return ['ai', 'summary.engine'].some(\n (removed) => normalizedKey === removed || normalizedKey.startsWith(`${removed}.`)\n )\n}\n\n/** A copy of the config without the 1.x AI settings — what `init` saves when the user agrees to remove them. */\nexport function withoutLegacyAiSettings(config: WorkscribeConfig): WorkscribeConfig {\n const cleaned = structuredClone(config)\n delete cleaned.ai\n delete cleaned.summary.engine\n return cleaned\n}\n","import prompts from 'prompts'\nimport { log } from '../utils/logger'\nimport { writeConfig, type WorkscribeConfig } from '../config'\nimport { hasLegacyAiSettings, withoutLegacyAiSettings } from '../summary/legacy-ai'\n\n/** Part of `init`: 2.0 removed AI providers, so offer to delete the 1.x settings — they can include a stored API key. */\nexport async function offerLegacyAiCleanup(config: WorkscribeConfig): Promise<WorkscribeConfig> {\n if (!hasLegacyAiSettings(config)) return config\n log.plain('')\n const { removeLegacyAi } = await prompts({\n type: 'confirm',\n name: 'removeLegacyAi',\n message:\n 'Your config has AI provider settings from Workscribe 1.x (including any API key) that 2.0 no longer uses. Remove them?',\n initial: true,\n })\n if (!removeLegacyAi) {\n log.info('Kept the old AI settings — they are ignored, and workscribe init can remove them later')\n return config\n }\n const cleaned = withoutLegacyAiSettings(config)\n writeConfig(cleaned)\n log.success('Removed the old AI provider settings from your config')\n return cleaned\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport prompts from 'prompts'\nimport chalk from 'chalk'\nimport { CONFIG_PATH, PROFILES_DIR, REQUIRED_DIRS } from '../utils/paths'\nimport { readConfig, writeConfig, ensureInstallId, WorkscribeConfig } from '../config'\nimport { detectProfilesForDirectory } from '../profiles/loader'\nimport { installProfile, cacheManifest } from '../profiles/fetcher'\nimport { getDb } from '../db'\nimport { log } from '../utils/logger'\nimport { loadAIToolRegistry } from '../config/registry'\nimport {\n detectShell,\n hasChronicleHook,\n removeChronicleHook,\n isHookInstalled,\n isHookOutdated,\n installHook,\n updateHook,\n isCompletionsInstalled,\n installCompletions,\n isCompletionsOutdated,\n updateCompletions,\n type ShellTarget,\n writeCompletionScript,\n resolveWorkscribeBinary,\n injectBinaryPath,\n} from '../capture/shell-hook'\nimport { AI_HOOK_INTEGRATIONS } from '../capture/hooks'\nimport { getPublicCommands } from './completions'\nimport { detectAgents } from '../skills/detect'\nimport { readInstallState } from '../skills/installer'\nimport { resolvePackagedSkillDir } from '../skills/package-path'\nimport { describeInstallOutcome, reasonOf } from '../skills/report'\nimport { groupBySkillsFolder, outcomeWithoutPrompt } from '../skills/state'\nimport { describeTarget, installIntoFolder, printMessage } from '../skills/actions'\nimport { offerLegacyAiCleanup } from './legacy-ai-cleanup'\nimport type { SkillsFolderGroup } from '../skills/types'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\ntype Shell = ShellTarget\n\nconst TOTAL_STEPS = 5\n\nfunction step(current: number, total: number, label: string): void {\n log.plain(chalk.bold(`\\n [${current}/${total}] ${label}`))\n}\n\n// ── Step 1: Data directories ──────────────────────────────────────────────\nfunction runDataDirectoriesStep(): void {\n step(1, TOTAL_STEPS, 'Creating data directories')\n\n for (const dir of REQUIRED_DIRS) {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n log.success(`Created ${dir}`)\n } else {\n try {\n fs.chmodSync(dir, 0o700)\n } catch {\n /* ignore */\n }\n log.info(`${dir} already exists`)\n }\n }\n\n if (!fs.existsSync(PROFILES_DIR)) {\n fs.mkdirSync(PROFILES_DIR, { recursive: true, mode: 0o700 })\n }\n}\n\n// ── Step 2: Database ──────────────────────────────────────────────────────\nasync function initDatabaseAndConfig(): Promise<WorkscribeConfig> {\n step(2, TOTAL_STEPS, 'Initialising database')\n const config = readConfig()\n getDb()\n log.success('Database ready')\n return offerLegacyAiCleanup(config)\n}\n\n// ── Telemetry (part of setup, no separate step) ────────────────────────────\nasync function runTelemetryStep(config: WorkscribeConfig): Promise<void> {\n log.plain('')\n log.plain(chalk.dim(' Workscribe can send anonymous install stats: a random install ID,'))\n log.plain(chalk.dim(' CLI version, OS platform, and Node.js version. No command, event, or'))\n log.plain(chalk.dim(' summary data is ever sent. Opt out anytime:'))\n log.plain(chalk.dim(' workscribe config set telemetry.enabled false'))\n log.plain('')\n const { telemetryEnabled } = await prompts({\n type: 'confirm',\n name: 'telemetryEnabled',\n message: 'Send anonymous install stats?',\n initial: true,\n })\n config.telemetry.enabled = telemetryEnabled\n config.telemetry.installId = ensureInstallId(config)\n}\n\n/** 0.2.1, before the rename to Workscribe, wrote a `chronicle` hook that nothing removes and that errors at every prompt. */\nasync function offerChronicleCleanup(shell: Shell): Promise<void> {\n if (!hasChronicleHook(shell.rcFile)) return\n const { removeChronicle } = await prompts({\n type: 'confirm',\n name: 'removeChronicle',\n message: `An old Chronicle hook in ${shell.rcFile} errors at every prompt. Remove it?`,\n initial: true,\n })\n if (!removeChronicle) {\n log.info('Chronicle hook left in place')\n return\n }\n removeChronicleHook(shell.rcFile)\n log.success(`Removed the old Chronicle hook from ${shell.rcFile}`)\n}\n\n// ── Step 3: Shell hook + completions ──────────────────────────────────────\nasync function runShellHookStep(program: Command): Promise<Shell | null> {\n step(3, TOTAL_STEPS, 'Installing shell hook')\n\n const shell = detectShell()\n if (!shell) {\n log.warn('Could not detect shell. Supported: zsh, bash, fish')\n log.plain(\n ` Manually add the hook from: ${chalk.cyan('shell/hook.zsh')}, ${chalk.cyan('shell/hook.bash')}, or ${chalk.cyan('shell/hook.fish')}`\n )\n return null\n }\n\n const hookFile = path.join(__dirname, '..', 'shell', `hook.${shell.name}`)\n if (!fs.existsSync(hookFile)) {\n log.warn(`Hook template not found at ${hookFile}`)\n return shell\n }\n\n const hookContent = fs.readFileSync(hookFile, 'utf-8')\n const binaryPath = resolveWorkscribeBinary()\n const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent\n\n // For fish: ensure conf.d directory exists before touching the file\n if (shell.name === 'fish') {\n const confDir = path.dirname(shell.rcFile)\n if (!fs.existsSync(confDir)) fs.mkdirSync(confDir, { recursive: true })\n }\n\n if (!fs.existsSync(shell.rcFile)) fs.writeFileSync(shell.rcFile, '')\n\n if (!isHookInstalled(shell.rcFile)) {\n log.plain('')\n log.plain(chalk.dim(' Workscribe adds a lightweight hook to your shell that captures each'))\n log.plain(chalk.dim(' command you run — the text, exit code, duration, and working directory.'))\n log.plain(chalk.dim(' It runs in the background and adds no latency to your terminal.'))\n log.plain(chalk.dim(' Credentials and secrets are redacted before anything is stored.'))\n log.plain('')\n installHook(shell.rcFile, hookWithPath)\n log.success(`Shell hook installed in ${shell.rcFile}`)\n } else if (isHookOutdated(shell.rcFile, hookWithPath)) {\n const { update } = await prompts({\n type: 'confirm',\n name: 'update',\n message: `Shell hook in ${shell.rcFile} is outdated. Update?`,\n initial: true,\n })\n if (update) {\n updateHook(shell.rcFile, hookWithPath)\n log.success(`Shell hook updated in ${shell.rcFile}`)\n } else {\n log.info('Shell hook update skipped')\n }\n } else {\n log.info(`Shell hook up to date in ${shell.rcFile}`)\n }\n\n await offerChronicleCleanup(shell)\n\n // Offer completions install — or repair: refresh the script, and guard an older unguarded rc block\n if (isCompletionsInstalled(shell)) {\n await refreshCompletions(shell, program)\n } else {\n const { installComps } = await prompts({\n type: 'confirm',\n name: 'installComps',\n message: 'Install shell completions (tab-complete workscribe commands)?',\n initial: true,\n })\n if (installComps) {\n try {\n installCompletions(shell, getPublicCommands(program))\n log.success('Shell completions installed')\n } catch (error) {\n log.warn(`Completions install failed: ${(error as Error).message}`)\n }\n }\n }\n\n return shell\n}\n\nasync function refreshCompletions(shell: Shell, program: Command): Promise<void> {\n try {\n writeCompletionScript(shell, getPublicCommands(program))\n } catch (error) {\n log.warn(`Could not refresh shell completions: ${(error as Error).message}`)\n }\n if (!isCompletionsOutdated(shell)) {\n log.info('Shell completions already installed')\n return\n }\n const { updateShellCompletions } = await prompts({\n type: 'confirm',\n name: 'updateShellCompletions',\n message: `Shell completions in ${shell.rcFile} are outdated (they error in new terminals if Workscribe is removed). Update?`,\n initial: true,\n })\n if (!updateShellCompletions) {\n log.info('Shell completions update skipped')\n return\n }\n updateCompletions(shell)\n log.success(`Shell completions updated in ${shell.rcFile}`)\n}\n\n// ── Step 4: AI tools ──────────────────────────────────────────────────────\nasync function runAIToolsStep(): Promise<void> {\n step(4, TOTAL_STEPS, 'Detecting AI coding tools')\n\n const installedTools = loadAIToolRegistry().filter(\n (entry) => entry.hook !== null && AI_HOOK_INTEGRATIONS[entry.hook!]?.isInstalled()\n )\n\n if (installedTools.length === 0) {\n log.info('No AI coding tools detected — re-run workscribe init after installing one')\n return\n }\n\n for (const tool of installedTools) {\n const integration = AI_HOOK_INTEGRATIONS[tool.hook!]!\n\n if (integration.isHookInstalled()) {\n log.info(`${integration.displayName} session hook already installed`)\n } else if (integration.isHookOutdated?.()) {\n // You opted in when you first installed it, so updating it defaults to yes\n const { updateAiHook } = await prompts({\n type: 'confirm',\n name: 'updateAiHook',\n message: `${integration.displayName} session hook is outdated. Update?`,\n initial: true,\n })\n if (updateAiHook) {\n integration.install()\n log.success(`${integration.displayName} session hook updated`)\n } else {\n log.info(`${integration.displayName} hook update skipped — re-run workscribe init to update later`)\n }\n } else {\n const { install } = await prompts({\n type: 'confirm',\n name: 'install',\n message: `${integration.displayName} detected. Install session capture hook?`,\n initial: false,\n })\n if (install) {\n integration.install()\n log.success(`${integration.displayName} session hook installed`)\n } else {\n log.info(`${integration.displayName} hook skipped — re-run workscribe init to install later`)\n }\n }\n }\n}\n\n// ── Step 5: Agent skill ───────────────────────────────────────────────────\nasync function offerSkillInstall(group: SkillsFolderGroup, sourceDir: string): Promise<void> {\n const target = describeTarget(group)\n const state = readInstallState(target.skillsFolder, __WORKSCRIBE_VERSION__)\n const knownOutcome = outcomeWithoutPrompt(state)\n if (knownOutcome) {\n printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: __WORKSCRIBE_VERSION__ }))\n return\n }\n\n const { installSkill: shouldInstall } = await prompts({\n type: 'confirm',\n name: 'installSkill',\n message: `${state.kind === 'outdated' ? 'Update' : 'Install'} the Workscribe skill for ${target.agents}? Lets ${target.agents} summarize your work when you ask`,\n initial: true,\n })\n if (!shouldInstall) {\n log.info(`Skill skipped for ${target.agents} — run workscribe skill install any time`)\n return\n }\n printMessage(installIntoFolder({ target, sourceDir }))\n}\n\nasync function runAgentSkillStep(): Promise<void> {\n step(5, TOTAL_STEPS, 'Installing the Workscribe agent skill')\n\n const agents = detectAgents()\n if (agents.length === 0) {\n log.info('No coding agents detected — run workscribe skill install after installing one')\n return\n }\n\n let sourceDir: string\n try {\n sourceDir = resolvePackagedSkillDir()\n } catch (error) {\n log.warn(reasonOf(error)) // setup can carry on; the skill is optional\n return\n }\n for (const group of groupBySkillsFolder(agents)) {\n await offerSkillInstall(group, sourceDir)\n }\n}\n\n// Profile detection (part of environment scan, no separate step)\nasync function runProfileDetectionStep(config: WorkscribeConfig): Promise<void> {\n try {\n await cacheManifest()\n const detected = detectProfilesForDirectory(process.cwd())\n const alreadyActive = new Set(config.capture.profiles)\n const toPrompt = detected.filter((entry) => !alreadyActive.has(entry.name))\n\n if (toPrompt.length > 0) {\n log.plain('')\n for (const entry of toPrompt) {\n const { install } = await prompts({\n type: 'confirm',\n name: 'install',\n message: `${entry.displayName} project detected. Enable the ${entry.displayName} profile?`,\n initial: true,\n })\n if (install) {\n await installProfile(entry.name)\n config.capture.profiles = [...new Set([...config.capture.profiles, entry.name])]\n log.success(`${entry.displayName} profile enabled`)\n }\n }\n writeConfig(config)\n } else if (detected.length === 0) {\n log.info('No project profiles detected — run workscribe profile detect from your project directory')\n }\n } catch {\n log.info('Could not reach profile registry — run workscribe profile detect after connecting')\n }\n}\n\n// ── What to do next ───────────────────────────────────────────────────────\nfunction printNextSteps(shell: Shell | null): void {\n log.plain('')\n log.plain(chalk.bold(' Setup complete!'))\n log.plain('')\n\n if (shell) {\n log.plain(` Activate capture now:`)\n if (shell.name === 'fish') {\n log.plain(` Restart your terminal`)\n log.plain(` ${chalk.dim(`or run: source ${shell.rcFile}`)}`)\n } else {\n log.plain(` ${chalk.cyan(`source ${shell.rcFile}`)}`)\n log.plain(` Or restart your terminal.`)\n }\n log.plain('')\n }\n\n log.plain(` Then run a few commands and generate your first summary:`)\n log.plain(` ${chalk.cyan('workscribe summary')}`)\n log.plain('')\n log.plain(` For AI-written summaries, ask your coding agent to \"summarize my work today\"`)\n log.plain(` ${chalk.dim('(needs the Workscribe skill: workscribe skill install)')}`)\n log.plain('')\n log.plain(` Check capture is working:`)\n log.plain(` ${chalk.cyan('workscribe status')}`)\n\n log.plain('')\n}\n\nexport function registerInit(program: Command): void {\n program\n .command('init')\n .description('Set up Workscribe and install or update shell hooks')\n .action(async () => {\n log.plain(chalk.bold('\\n Workscribe — Developer Work Journal\\n'))\n\n runDataDirectoriesStep()\n const config = await initDatabaseAndConfig()\n await runTelemetryStep(config)\n const shell = await runShellHookStep(program)\n await runAIToolsStep()\n await runAgentSkillStep()\n await runProfileDetectionStep(config)\n\n if (!fs.existsSync(CONFIG_PATH)) {\n writeConfig(config)\n }\n\n printNextSteps(shell)\n })\n}\n","export function formatDuration(minutes: number): string {\n if (minutes < 60) return `${Math.round(minutes)}m`\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return m > 0 ? `${h}h ${m}m` : `${h}h`\n}\n\nexport function formatDate(isoString: string): string {\n return new Date(isoString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\nexport function todayDate(): string {\n return localDateStr(new Date())\n}\n\nexport function parseDate(input: string): string {\n // Accepts YYYY-MM-DD, returns YYYY-MM-DD. Throws on invalid.\n const d = new Date(input + 'T00:00:00')\n if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`)\n return input\n}\n\nfunction localDateStr(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\n\n/** The local YYYY-MM-DD an ISO timestamp falls on — the same day rule sessions are grouped by. */\nexport function localDateOf(isoString: string): string {\n return localDateStr(new Date(isoString))\n}\n\n/**\n * A local day's bounds as UTC ISO strings, [start, end) — the same day `date(captured_at, 'localtime')` picks,\n * but comparable against `captured_at` directly, so a query can use its index. Handles DST-length days.\n */\nexport function localDayBounds(date: string): { start: string; end: string } {\n const start = new Date(`${date}T00:00:00`)\n const end = new Date(start)\n end.setDate(end.getDate() + 1)\n return { start: start.toISOString(), end: end.toISOString() }\n}\n\n// Returns Monday of the ISO week containing dateStr (YYYY-MM-DD)\nexport function startOfWeek(dateStr: string): string {\n const d = new Date(dateStr + 'T00:00:00')\n const day = d.getDay() // 0=Sun\n const diff = day === 0 ? -6 : 1 - day\n d.setDate(d.getDate() + diff)\n return localDateStr(d)\n}\n\n// Returns all YYYY-MM-DD strings from start to end inclusive\nexport function eachDayOfRange(start: string, end: string): string[] {\n const days: string[] = []\n const cur = new Date(start + 'T00:00:00')\n const last = new Date(end + 'T00:00:00')\n while (cur <= last) {\n days.push(localDateStr(cur))\n cur.setDate(cur.getDate() + 1)\n }\n return days\n}\n\nexport function timeAgo(isoString: string, now = Date.now()): string {\n const elapsedMs = now - new Date(isoString).getTime()\n const minutes = Math.floor(elapsedMs / 60000)\n if (minutes < 2) return 'just now'\n if (minutes < 60) return `${minutes} minutes ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return hours === 1 ? '1 hour ago' : `${hours} hours ago`\n const days = Math.floor(hours / 24)\n if (days === 1) return 'yesterday'\n return `${days} days ago`\n}\n\n/** A local date (`2026-11-03`) as the week shows it: `Mon 3 Nov`. */\nexport function formatWeekday(date: string): string {\n const [year, month, day] = date.split('-').map(Number)\n // Noon UTC, formatted in UTC: the same calendar day whatever the machine's time zone\n return new Date(Date.UTC(year, month - 1, day, 12))\n .toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short', timeZone: 'UTC' })\n .replace(',', '')\n}\n","import os from 'os'\nimport { log } from '../utils/logger'\nimport { CONFIG_PATH } from '../utils/paths'\nimport { describeLegacyAiSettings } from '../summary/legacy-ai'\nimport { displayPath } from '../skills/report'\nimport type { WorkscribeConfig } from '../config'\n\n/** Tells a 1.x user, once per run, that their AI provider settings no longer do anything (stderr, so JSON output stays clean). */\nexport function printLegacyAiNotice(config: WorkscribeConfig): void {\n const notice = describeLegacyAiSettings(config, displayPath(CONFIG_PATH, os.homedir()))\n if (notice) log.notice(notice)\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { DataJob, JobIssue, JobVerification } from './types'\n\nexport type JobStatus = 'pending' | 'running' | 'done' | 'failed'\n\nexport interface JobRow {\n name: string\n version: number\n cursor: number\n status: JobStatus\n started_at: string | null\n finished_at: string | null\n verification: string | null\n error: string | null\n}\n\nexport function getMetaValue(db: MigrationDatabase, key: string): string | null {\n const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined\n return row?.value ?? null\n}\n\nexport function setMetaValue(db: MigrationDatabase, key: string, value: string): void {\n db.prepare(\n 'INSERT INTO meta (key, value) VALUES ($key, $value) ON CONFLICT (key) DO UPDATE SET value = excluded.value'\n ).run({ key, value })\n}\n\nexport function deleteMetaValue(db: MigrationDatabase, key: string): void {\n db.prepare('DELETE FROM meta WHERE key = ?').run(key)\n}\n\n/** Meta values whose key starts with `prefix`, keyed by the rest of the key. */\nexport function getMetaValuesWithPrefix(db: MigrationDatabase, prefix: string): Map<string, string> {\n const rows = db\n .prepare(\"SELECT key, value FROM meta WHERE key LIKE ? ESCAPE '\\\\'\")\n .all(`${prefix.replace(/[\\\\%_]/g, '\\\\$&')}%`) as { key: string; value: string }[]\n return new Map(rows.map((row) => [row.key.slice(prefix.length), row.value]))\n}\n\nexport function getJobRow(db: MigrationDatabase, name: string): JobRow | null {\n return (db.prepare('SELECT * FROM data_jobs WHERE name = ?').get(name) as JobRow | undefined) ?? null\n}\n\n/** Done at this version or a later one — a later version means a newer CLI already ran it; never run it backwards. */\nexport function isJobDone(row: JobRow | null, job: DataJob): boolean {\n return row !== null && row.status === 'done' && row.version >= job.version\n}\n\n/** Starts the job, or restarts it from scratch when its version moved on since it last ran. */\nexport function startJob(db: MigrationDatabase, job: DataJob, startedAt: string): void {\n const row = getJobRow(db, job.name)\n if (row !== null && row.version >= job.version) {\n db.prepare(\"UPDATE data_jobs SET status = 'running', error = NULL WHERE name = ?\").run(job.name)\n return\n }\n job.prepare(db)\n db.prepare(\n `INSERT INTO data_jobs (name, version, cursor, status, started_at)\n VALUES ($name, $version, 0, 'running', $started_at)\n ON CONFLICT (name) DO UPDATE SET version = excluded.version, cursor = 0, status = 'running',\n started_at = excluded.started_at, finished_at = NULL, verification = NULL, error = NULL`\n ).run({ name: job.name, version: job.version, started_at: startedAt })\n}\n\nexport function setJobCursor(db: MigrationDatabase, name: string, cursor: number): void {\n db.prepare('UPDATE data_jobs SET cursor = $cursor WHERE name = $name').run({ name, cursor })\n}\n\nexport function recordJobIssues(db: MigrationDatabase, job: DataJob, issues: JobIssue[], recordedAt: string): void {\n const insert = db.prepare(\n `INSERT INTO data_job_issues (job_name, job_version, reference, reason, recorded_at)\n VALUES ($job_name, $job_version, $reference, $reason, $recorded_at)`\n )\n for (const issue of issues) {\n insert.run({\n job_name: job.name,\n job_version: job.version,\n reference: issue.reference,\n reason: issue.reason,\n recorded_at: recordedAt,\n })\n }\n}\n\n/**\n * Marks the job done — only at this job's version. Returns false when the row moved on to another version meanwhile\n * (a newer CLI restarted it), so older code never marks newer work done.\n */\nexport function finishJob(\n db: MigrationDatabase,\n job: DataJob,\n verification: JobVerification,\n finishedAt: string\n): boolean {\n const result = db\n .prepare(\n `UPDATE data_jobs SET status = 'done', finished_at = $finished_at, verification = $verification\n WHERE name = $name AND version = $version`\n )\n .run({ name: job.name, version: job.version, finished_at: finishedAt, verification: JSON.stringify(verification) })\n return (result as { changes: number | bigint }).changes > 0\n}\n\n/** Records the failure, creating the job's row if it failed before its first write (say, in `prepare`). */\nexport function failJob(db: MigrationDatabase, job: DataJob, error: string): void {\n db.prepare(\n `INSERT INTO data_jobs (name, version, status, error) VALUES ($name, $version, 'failed', $error)\n ON CONFLICT (name) DO UPDATE SET status = 'failed', error = excluded.error`\n ).run({ name: job.name, version: job.version, error })\n}\n","import path from 'path'\nimport type { MigrationDatabase } from '../migration-types'\nimport type { DirectoryProbe } from '../../capture/git'\nimport { withImmediateTransaction, withReadTransaction } from '../transaction'\nimport { rootForms } from '../../utils/working-directory'\n\n// `directory_probe`: what git said about each folder stored events mention, asked once and kept, so a job that\n// reruns gets the same answer without asking again.\n\ninterface ProbeRow {\n path: string\n outcome: DirectoryProbe['outcome']\n root: string | null\n common_dir: string | null\n remote_url: string | null\n}\n\nfunction toProbe(row: ProbeRow): DirectoryProbe {\n if (row.outcome !== 'repo' || row.root === null) {\n return { outcome: row.outcome === 'missing' ? 'missing' : 'not-a-repo' }\n }\n return { outcome: 'repo', root: row.root, commonDir: row.common_dir, remoteUrl: row.remote_url }\n}\n\nexport function loadProbes(db: MigrationDatabase): Map<string, DirectoryProbe> {\n const rows = db.prepare('SELECT * FROM directory_probe').all() as ProbeRow[]\n return new Map(rows.map((row) => [row.path, toProbe(row)]))\n}\n\n/** Every folder an event was captured in or saved a file in that git hasn't been asked about yet. */\nexport function foldersToProbe(db: MigrationDatabase): string[] {\n return withReadTransaction(db, () => {\n const probed = new Set(loadProbes(db).keys())\n const cwds = db.prepare('SELECT DISTINCT cwd FROM events').all() as { cwd: string }[]\n const files = db.prepare('SELECT DISTINCT file_path FROM events WHERE file_path IS NOT NULL').all() as {\n file_path: string\n }[]\n const folders = new Set([...cwds.map((row) => row.cwd), ...files.map((row) => path.dirname(row.file_path))])\n return [...folders].filter((folder) => !probed.has(folder)).sort()\n })\n}\n\n/**\n * Stores the probe in a transaction of its own, with the root as the folder reaches it — the form stored `cwd`s and\n * file paths use. The remote comes without credentials (`probeDirectory` strips them).\n */\nexport function saveProbe(db: MigrationDatabase, folder: string, probe: DirectoryProbe): void {\n const isRepo = probe.outcome === 'repo'\n const root = isRepo ? (rootForms(folder, probe.root).at(-1) ?? probe.root) : null\n withImmediateTransaction(db, () =>\n db\n .prepare(\n `INSERT OR REPLACE INTO directory_probe (path, outcome, root, common_dir, remote_url)\n VALUES ($path, $outcome, $root, $common_dir, $remote_url)`\n )\n .run({\n path: folder,\n outcome: probe.outcome,\n root,\n common_dir: isRepo ? probe.commonDir : null,\n remote_url: isRepo ? probe.remoteUrl : null,\n })\n )\n}\n","import type { DirectoryProbe } from '../../capture/git'\nimport { nameIdentity, projectIdentity, type ProjectIdentity } from '../../capture/project-identity'\nimport type { ProjectRecord } from '../dimensions'\nimport { relativeInside } from '../../utils/working-directory'\n\n// Which project each stored (folder, name) pair belongs to. Pure: the backfill job feeds it the pairs it found and\n// what git said about each folder, and stores the answer.\n\n/** How a pair found its project. Everything but `git` and `none` is a fallback, and is recorded. */\nexport type ResolutionMethod = 'git' | 'folder' | 'qualified-name' | 'bare-name' | 'name' | 'none'\n\nexport const FALLBACK_METHODS: ResolutionMethod[] = ['folder', 'qualified-name', 'bare-name', 'name']\n\nexport interface ObservedPair {\n cwd: string\n repoName: string | null\n}\n\nexport interface PairResolution extends ObservedPair {\n method: ResolutionMethod\n project: ProjectRecord | null\n}\n\n/** A project git found at a folder that still exists, with every root and bare name it was stored under. */\ninterface KnownProject extends ProjectIdentity {\n roots: Set<string>\n bareNames: Set<string>\n}\n\nfunction isQualified(name: string): boolean {\n return name.includes('/')\n}\n\nfunction gitProject(probe: DirectoryProbe | undefined): (ProjectIdentity & { root: string }) | null {\n if (probe?.outcome !== 'repo') return null\n const identity = projectIdentity(probe)\n return identity && { ...identity, root: probe.root }\n}\n\n/**\n * Whether a stored name could have come from this project's remote, as `repo` or `owner/repo`. A name that doesn't\n * is a `projects` override, or a `git -C ../other …` stored with the shell's folder but the other repo's name. A\n * project without a remote has nothing to check the name against.\n */\nfunction nameFitsRemote(project: ProjectIdentity, name: string): boolean {\n return project.kind !== 'remote' || project.identity.endsWith(`/${name.toLowerCase()}`)\n}\n\nfunction knownProjects(pairs: ObservedPair[], probes: Map<string, DirectoryProbe>): KnownProject[] {\n const known = new Map<string, KnownProject>()\n for (const pair of pairs) {\n const found = gitProject(probes.get(pair.cwd))\n if (found === null || pair.repoName === null) continue\n const project = known.get(found.identity) ?? {\n identity: found.identity,\n kind: found.kind,\n roots: new Set<string>(),\n bareNames: new Set<string>(),\n }\n project.roots.add(found.root)\n const isBareFittingName = !isQualified(pair.repoName) && nameFitsRemote(found, pair.repoName)\n if (isBareFittingName) project.bareNames.add(pair.repoName.toLowerCase())\n known.set(found.identity, project)\n }\n return [...known.values()]\n}\n\nfunction singleOrNull<T>(candidates: T[]): T | null {\n return candidates.length === 1 ? candidates[0] : null\n}\n\n/** The project whose root holds the folder, the deepest root first. Null when two projects share that root. */\nfunction byFolder(cwd: string, known: KnownProject[]): KnownProject | null {\n const holders = known.flatMap((project) =>\n [...project.roots]\n .filter((root) => relativeInside(root, cwd) !== null)\n .map((root) => ({ project, depth: root.length }))\n )\n const deepest = Math.max(...holders.map((holder) => holder.depth))\n return singleOrNull(holders.filter((holder) => holder.depth === deepest).map((holder) => holder.project))\n}\n\ntype NamedPair = ObservedPair & { repoName: string }\n\nfunction asFallback(pair: NamedPair, project: KnownProject | null, method: ResolutionMethod): PairResolution | null {\n if (project === null) return null\n return {\n ...pair,\n method,\n project: { identity: project.identity, kind: project.kind, name: pair.repoName, root: null },\n }\n}\n\n/** For `owner/repo`, the one live remote ending in it; for a bare `repo`, the one live project also stored as it. */\nfunction byName(pair: NamedPair, known: KnownProject[]): PairResolution | null {\n const name = pair.repoName.toLowerCase()\n if (isQualified(name)) {\n const remotes = known.filter((project) => project.kind === 'remote' && project.identity.endsWith(`/${name}`))\n return asFallback(pair, singleOrNull(remotes), 'qualified-name')\n }\n const sameBareName = known.filter((project) => project.bareNames.has(name))\n return asFallback(pair, singleOrNull(sameBareName), 'bare-name')\n}\n\n/**\n * For a folder that's gone or no longer a repo, the one live project that fits, if any: the project whose checkout\n * holds the folder, else the one the name fits (`byName`). Two candidates are never guessed between.\n */\nfunction byFallback(pair: NamedPair, known: KnownProject[]): PairResolution | null {\n return asFallback(pair, byFolder(pair.cwd, known), 'folder') ?? byName(pair, known)\n}\n\n/**\n * The folder's own repo — unless the stored name doesn't fit it and does fit exactly one other live project: then\n * the command worked in that other repo (`git -C`). A name that fits neither is kept as an override, but never as an\n * alias, so it can't rename the project or match other history to it.\n */\nfunction byGit(pair: NamedPair, found: ProjectIdentity & { root: string }, known: KnownProject[]): PairResolution {\n const fits = nameFitsRemote(found, pair.repoName)\n const elsewhere = fits\n ? null\n : byName(\n pair,\n known.filter((project) => project.identity !== found.identity)\n )\n if (elsewhere !== null) return elsewhere\n const project = { identity: found.identity, kind: found.kind, name: fits ? pair.repoName : null, root: found.root }\n return { ...pair, method: 'git', project }\n}\n\nfunction resolvePair(pair: ObservedPair, probes: Map<string, DirectoryProbe>, known: KnownProject[]): PairResolution {\n // No name means the folder wasn't in a repo when the event was captured: those events stay unlinked\n if (pair.repoName === null) return { ...pair, method: 'none', project: null }\n // The same pair, with TypeScript told its name is set\n const named: NamedPair = { ...pair, repoName: pair.repoName }\n const found = gitProject(probes.get(pair.cwd))\n if (found !== null) return byGit(named, found, known)\n return (\n byFallback(named, known) ?? {\n ...pair,\n method: 'name',\n project: { ...nameIdentity(pair.repoName), name: pair.repoName, root: null },\n }\n )\n}\n\n/** A bare `x` left on its own joins the one name-only `owner/x`, when there's exactly one. */\nfunction mergeBareNames(resolutions: PairResolution[]): PairResolution[] {\n const qualifiedByRepo = new Map<string, Set<string>>()\n for (const resolution of resolutions) {\n if (resolution.method !== 'name' || !resolution.repoName || !isQualified(resolution.repoName)) continue\n const repo = resolution.repoName.toLowerCase().split('/').at(-1) ?? ''\n qualifiedByRepo.set(repo, (qualifiedByRepo.get(repo) ?? new Set()).add(resolution.project?.identity ?? ''))\n }\n return resolutions.map((resolution) => {\n if (resolution.method !== 'name' || !resolution.repoName || isQualified(resolution.repoName)) return resolution\n const identities = [...(qualifiedByRepo.get(resolution.repoName.toLowerCase()) ?? [])]\n const identity = singleOrNull(identities)\n if (identity === null || resolution.project === null) return resolution\n return { ...resolution, project: { ...resolution.project, identity } }\n })\n}\n\n/**\n * Each pair's project, in order: what git says about the folder now; else a live project whose checkout holds the\n * folder; else the one live project the name fits (`owner/repo` by its remote, a bare `repo` by the bare names it\n * was stored under); else a project of its own, by name.\n */\nexport function resolvePairs(pairs: ObservedPair[], probes: Map<string, DirectoryProbe>): PairResolution[] {\n const known = knownProjects(pairs, probes)\n return mergeBareNames(pairs.map((pair) => resolvePair(pair, probes, known)))\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport { FALLBACK_METHODS, type ResolutionMethod } from './project-resolution'\nimport type { JobIssue, JobVerification } from './types'\n\n// What the project-dimensions job reports: the guesses it made, as issues, and its verification.\n\n/** The events there were when the job resolved every pair: the events it must link, and the count it must keep. */\nexport interface GatherMark {\n throughId: number\n events: number\n}\n\nconst METHOD_LABELS: Record<ResolutionMethod, string> = {\n git: 'git, at a folder that still exists',\n folder: 'a live checkout above a folder that is gone',\n 'qualified-name': 'their owner/repo name',\n 'bare-name': 'a bare name a live repo was also stored under',\n name: 'their name alone (no live repo matched)',\n none: 'nothing: captured outside a repo',\n}\n\nfunction eventCount(count: number): string {\n return `${count} event${count === 1 ? '' : 's'}`\n}\n\nexport function fallbackReason(method: ResolutionMethod, count: number, identity: string | null): string {\n if (method === 'name') return `${eventCount(count)}: no live repo matched, so kept as a project of its own by name`\n return `${eventCount(count)}: joined ${identity ?? 'a project'} by ${METHOD_LABELS[method]}`\n}\n\n/** One issue per pair that found its project by a fallback, so the upgrade's results show every guess it made. */\nexport function fallbackIssues(db: MigrationDatabase): JobIssue[] {\n const rows = db\n .prepare(\n `SELECT resolution.cwd, resolution.repo_name, resolution.method, resolution.event_count, project.identity\n FROM project_resolution resolution LEFT JOIN project ON project.id = resolution.project_id\n ORDER BY resolution.cwd, resolution.repo_name`\n )\n .all() as {\n cwd: string\n repo_name: string\n method: ResolutionMethod\n event_count: number\n identity: string | null\n }[]\n return rows\n .filter((row) => FALLBACK_METHODS.includes(row.method))\n .map((row) => ({\n reference: `${row.cwd} (${row.repo_name})`,\n reason: fallbackReason(row.method, row.event_count, row.identity),\n }))\n}\n\nfunction countRows(db: MigrationDatabase, sql: string, ...parameters: unknown[]): number {\n return (db.prepare(sql).get(...parameters) as { count: number }).count\n}\n\nfunction methodNotes(db: MigrationDatabase): string[] {\n const byMethod = db\n .prepare(\n `SELECT resolution.method, COUNT(*) AS count FROM events\n JOIN project_resolution resolution\n ON resolution.cwd = events.cwd AND resolution.repo_name = COALESCE(events.repo_name, '')\n GROUP BY resolution.method ORDER BY count DESC`\n )\n .all() as { method: ResolutionMethod; count: number }[]\n return byMethod.map((row) => `${eventCount(row.count)} matched by ${METHOD_LABELS[row.method]}`)\n}\n\n/**\n * Passes when every event with a name found a project, which the name-only fallback should guarantee. The job never\n * deletes an event, so events gone since it started were removed on purpose (`undo`, `clear`): noted, not a failure.\n */\nexport function verifyProjectDimensions(db: MigrationDatabase, mark: GatherMark | null): JobVerification {\n const checked = countRows(db, 'SELECT COUNT(*) AS count FROM events')\n const unresolved = countRows(\n db,\n 'SELECT COUNT(*) AS count FROM events WHERE repo_name IS NOT NULL AND project_id IS NULL'\n )\n const unlinkedSaves = countRows(\n db,\n 'SELECT COUNT(*) AS count FROM events WHERE file_path IS NOT NULL AND id NOT IN (SELECT event_id FROM event_file)'\n )\n const keptEvents = countRows(db, 'SELECT COUNT(*) AS count FROM events WHERE id <= ?', mark?.throughId ?? 0)\n const removedEvents = (mark?.events ?? 0) - keptEvents\n const notes = methodNotes(db)\n if (unlinkedSaves > 0) {\n notes.push(`${unlinkedSaves} saved file${unlinkedSaves === 1 ? ' is' : 's are'} in no repo git could find`)\n }\n if (removedEvents > 0) notes.push(`${eventCount(removedEvents)} removed while the upgrade ran (undo or clear)`)\n return {\n checked,\n resolved: checked - unresolved,\n unresolved,\n hasPassed: unresolved === 0,\n notes,\n }\n}\n","import path from 'path'\nimport type { MigrationDatabase } from '../migration-types'\nimport type { DirectoryProbe } from '../../capture/git'\nimport { projectIdentity, type ProjectIdentity } from '../../capture/project-identity'\nimport { withImmediateTransaction, withReadTransaction } from '../transaction'\nimport { FILE_SAVE_ACTION, linkEventFile, upsertBranch, upsertFile, upsertProject } from '../dimensions'\nimport { relativeInside } from '../../utils/working-directory'\nimport { deleteMetaValue, getMetaValue, setMetaValue } from './store'\nimport { foldersToProbe, loadProbes, saveProbe } from './directory-probes'\nimport {\n FALLBACK_METHODS,\n resolvePairs,\n type ObservedPair,\n type PairResolution,\n type ResolutionMethod,\n} from './project-resolution'\nimport { fallbackIssues, fallbackReason, verifyProjectDimensions, type GatherMark } from './project-dimensions-report'\nimport type { BatchResult, DataJob, GatherOutcome, JobIssue } from './types'\n\n// Links every stored event to its project, branch and file (migration 008), for the events captured before capture\n// wrote the links itself. Git is asked once per folder, outside the write lock, and the answer is kept in\n// `directory_probe`: a rerun after a version bump asks nothing and gives the same answer.\n\nconst GATHER_MARK_KEY = 'project-dimensions:gathered'\n\ninterface EventToLink {\n id: number\n cwd: string\n repo_name: string | null\n branch: string | null\n file_path: string | null\n project_id: number | null\n}\n\n/** A pair's project as stored in `project_resolution`. */\ninterface StoredResolution {\n projectId: number | null\n method: ResolutionMethod\n}\n\nfunction pairKey(cwd: string, repoName: string | null): string {\n return `${cwd}\\u0000${repoName ?? ''}`\n}\n\nfunction readGatherMark(db: MigrationDatabase): GatherMark | null {\n const value = getMetaValue(db, GATHER_MARK_KEY)\n return value === null ? null : (JSON.parse(value) as GatherMark)\n}\n\nfunction storeResolution(db: MigrationDatabase, resolution: PairResolution, eventCount: number): StoredResolution {\n const projectId = resolution.project === null ? null : upsertProject(db, resolution.project)\n db.prepare(\n `INSERT OR REPLACE INTO project_resolution (cwd, repo_name, project_id, method, event_count)\n VALUES (?, ?, ?, ?, ?)`\n ).run(resolution.cwd, resolution.repoName ?? '', projectId, resolution.method, eventCount)\n return { projectId, method: resolution.method }\n}\n\n/**\n * Resolves every stored pair at once — the rules compare pairs with each other, so they need the whole set — and\n * marks how far the events went, so batches can tell events captured since (which capture linked itself).\n */\nfunction resolveAllPairs(db: MigrationDatabase): void {\n const { counted, mark } = withReadTransaction(db, () => ({\n counted: db.prepare('SELECT cwd, repo_name, COUNT(*) AS events FROM events GROUP BY cwd, repo_name').all() as {\n cwd: string\n repo_name: string | null\n events: number\n }[],\n mark: db.prepare('SELECT COALESCE(MAX(id), 0) AS throughId, COUNT(*) AS events FROM events').get() as GatherMark,\n }))\n const resolutions = resolvePairs(\n counted.map((row) => ({ cwd: row.cwd, repoName: row.repo_name })),\n loadProbes(db)\n )\n withImmediateTransaction(db, () => {\n resolutions.forEach((resolution, index) => storeResolution(db, resolution, counted[index].events))\n setMetaValue(db, GATHER_MARK_KEY, JSON.stringify(mark))\n })\n}\n\nfunction storedPairs(db: MigrationDatabase): ObservedPair[] {\n const rows = db.prepare('SELECT cwd, repo_name FROM project_resolution').all() as { cwd: string; repo_name: string }[]\n return rows.map((row) => ({ cwd: row.cwd, repoName: row.repo_name === '' ? null : row.repo_name }))\n}\n\n/**\n * A pair first seen after gathering, on an event capture couldn't link. Resolved with the same rules over the folders\n * already probed; a folder never probed counts as unknown, so only the name rules can place it. Batches never call git.\n */\nfunction resolveLatePair(db: MigrationDatabase, pair: ObservedPair, issues: JobIssue[]): StoredResolution {\n const resolutions = resolvePairs([...storedPairs(db), pair], loadProbes(db))\n const resolution = resolutions[resolutions.length - 1]\n const stored = storeResolution(db, resolution, 1)\n if (FALLBACK_METHODS.includes(resolution.method)) {\n const reason = fallbackReason(resolution.method, 1, resolution.project?.identity ?? null)\n issues.push({\n reference: `${pair.cwd} (${pair.repoName ?? ''})`,\n reason: `captured while the upgrade ran; ${reason}`,\n })\n }\n return stored\n}\n\nfunction storedResolution(db: MigrationDatabase, cwd: string, repoName: string | null): StoredResolution | null {\n const row = db\n .prepare('SELECT project_id, method FROM project_resolution WHERE cwd = ? AND repo_name = ?')\n .get(cwd, repoName ?? '') as { project_id: number | null; method: ResolutionMethod } | undefined\n return row === undefined ? null : { projectId: row.project_id, method: row.method }\n}\n\n/** Where a saved file sits — its repo and its path in it — from what git said about its folder. Null: in no repo. */\nfunction locateFile(\n filePath: string,\n probes: Map<string, DirectoryProbe>\n): { identity: ProjectIdentity; root: string; relativePath: string } | null {\n const probe = probes.get(path.dirname(filePath))\n if (probe?.outcome !== 'repo') return null\n const identity = projectIdentity(probe)\n const relativePath = relativeInside(probe.root, filePath)\n if (identity === null || !relativePath) return null\n return { identity, root: probe.root, relativePath }\n}\n\nfunction placeFile(\n db: MigrationDatabase,\n filePath: string,\n probes: Map<string, DirectoryProbe>\n): { projectId: number; relativePath: string } | null {\n const location = locateFile(filePath, probes)\n if (location === null) return null\n const projectId = upsertProject(db, { ...location.identity, name: null, root: location.root })\n return { projectId, relativePath: location.relativePath }\n}\n\n/** One issue per folder whose saves the job can't place, with how many there are. */\nfunction unplacedSaveIssues(\n db: MigrationDatabase,\n probes: Map<string, DirectoryProbe>,\n mark: GatherMark | null\n): JobIssue[] {\n const saves = db\n .prepare('SELECT file_path FROM events WHERE file_path IS NOT NULL AND id <= ?')\n .all(mark?.throughId ?? 0) as { file_path: string }[]\n const unplacedByFolder = new Map<string, number>()\n for (const save of saves) {\n if (locateFile(save.file_path, probes) !== null) continue\n const folder = path.dirname(save.file_path)\n unplacedByFolder.set(folder, (unplacedByFolder.get(folder) ?? 0) + 1)\n }\n return [...unplacedByFolder.entries()]\n .sort(([first], [second]) => first.localeCompare(second))\n .map(([folder, count]) => ({\n reference: folder,\n reason: `${count} saved file${count === 1 ? '' : 's'} here, in no repo git could find`,\n }))\n}\n\ninterface BatchContext {\n mark: GatherMark | null\n probes: Map<string, DirectoryProbe>\n resolutions: Map<string, StoredResolution>\n issues: JobIssue[]\n}\n\nfunction resolutionFor(db: MigrationDatabase, event: EventToLink, context: BatchContext): StoredResolution {\n const key = pairKey(event.cwd, event.repo_name)\n const cached = context.resolutions.get(key)\n if (cached !== undefined) return cached\n const pair = { cwd: event.cwd, repoName: event.repo_name }\n const resolution = storedResolution(db, event.cwd, event.repo_name) ?? resolveLatePair(db, pair, context.issues)\n context.resolutions.set(key, resolution)\n return resolution\n}\n\n/** Captured after the job resolved every pair, and already linked by capture: capture's links are the answer. */\nfunction isLinkedByCapture(event: EventToLink, mark: GatherMark | null): boolean {\n return event.project_id !== null && event.id > (mark?.throughId ?? 0)\n}\n\n/** Links one event. A save takes the project of its file's repo; the branch is kept only for the folder's own repo. */\nfunction linkEvent(db: MigrationDatabase, event: EventToLink, context: BatchContext): void {\n if (isLinkedByCapture(event, context.mark)) return\n const resolution = resolutionFor(db, event, context)\n const file = event.file_path === null ? null : placeFile(db, event.file_path, context.probes)\n const projectId = file?.projectId ?? resolution.projectId\n const isFoldersProject = projectId !== null && projectId === resolution.projectId\n const branchId = event.branch !== null && isFoldersProject ? upsertBranch(db, projectId, event.branch) : null\n db.prepare('UPDATE events SET project_id = ?, branch_id = ? WHERE id = ?').run(projectId, branchId, event.id)\n // A save that can't be placed is recorded once per folder, by the first batch\n if (file === null) return\n const fileId = upsertFile(db, file.projectId, file.relativePath)\n linkEventFile(db, { eventId: event.id, fileId, action: FILE_SAVE_ACTION })\n}\n\n/**\n * After the last batch: projects no event links to any more go, and each project takes the name of its newest event\n * stored under one of its own names — never a `git -C` event's name for another repo.\n */\nfunction finishProjects(db: MigrationDatabase): void {\n db.exec(`\n DELETE FROM project\n WHERE id NOT IN (SELECT project_id FROM events WHERE project_id IS NOT NULL)\n AND id NOT IN (SELECT project_id FROM project_resolution WHERE project_id IS NOT NULL);\n UPDATE project SET name = COALESCE(\n (SELECT repo_name FROM events\n WHERE project_id = project.id\n AND repo_name IN (SELECT name FROM project_alias WHERE project_alias.project_id = project.id)\n ORDER BY id DESC LIMIT 1),\n name\n );\n `)\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const events = db\n .prepare('SELECT id, cwd, repo_name, branch, file_path, project_id FROM events WHERE id > ? ORDER BY id LIMIT ?')\n .all(afterId, limit) as EventToLink[]\n if (events.length === 0) {\n finishProjects(db)\n return { lastId: null, issues: [] }\n }\n const mark = readGatherMark(db)\n const probes = loadProbes(db)\n const issues = afterId === 0 ? [...fallbackIssues(db), ...unplacedSaveIssues(db, probes, mark)] : []\n const context: BatchContext = { mark, probes, resolutions: new Map(), issues }\n for (const event of events) linkEvent(db, event, context)\n return { lastId: events[events.length - 1].id, issues: context.issues }\n}\n\nexport interface ProjectDimensionsOptions {\n probeDirectory: (directory: string) => DirectoryProbe\n}\n\nexport function createProjectDimensionsJob(options: ProjectDimensionsOptions): DataJob {\n return {\n name: 'project-dimensions',\n version: 1,\n description: 'Link stored events to their project, branch and file',\n // Batches rewrite every link up to the gather mark, so only the work tables go; the probes stay, so a rerun never\n // asks git again\n prepare: (db) => {\n db.exec('DELETE FROM event_file; DELETE FROM project_resolution;')\n deleteMetaValue(db, GATHER_MARK_KEY)\n },\n gather: (db, { isOverBudget }): GatherOutcome => {\n for (const folder of foldersToProbe(db)) {\n if (isOverBudget()) return 'incomplete'\n saveProbe(db, folder, options.probeDirectory(folder))\n }\n if (readGatherMark(db) === null) resolveAllPairs(db)\n return 'complete'\n },\n runBatch,\n verify: (db) => verifyProjectDimensions(db, readGatherMark(db)),\n }\n}\n","import type { EventRow } from '../types'\n\n// A frozen copy of how 1.x and 2.0 grouped a day into sessions (src/sessions/grouper.ts and detector.ts at the time\n// of this job), so the summary-anchors job can find an old session's events after the live grouper is gone. Never\n// change it: a summary's key was made by exactly these rules. Pure.\n\nconst IDLE_MS = 30 * 60 * 1000 // the default `session.idleTimeout`: jobs never read the config\nconst LONG_RUNNING_MS = 5 * 60 * 1000\nconst REPO_SWITCH_GAP_MS = 10 * 60 * 1000\n\nfunction splitByIdleGap(events: EventRow[]): EventRow[][] {\n const groups: EventRow[][] = []\n for (const [index, event] of events.entries()) {\n const completedMs = Date.parse(event.captured_at)\n const durationMs = event.duration_ms ?? 0\n const effectiveMs = durationMs >= LONG_RUNNING_MS ? completedMs - durationMs : completedMs\n const isAfterGap = index > 0 && effectiveMs - Date.parse(events[index - 1].captured_at) >= IDLE_MS\n if (index === 0 || isAfterGap) groups.push([event])\n else groups[groups.length - 1].push(event)\n }\n return groups\n}\n\nfunction splitByRepo(events: EventRow[]): EventRow[][] {\n const groups: EventRow[][] = []\n for (const [index, event] of events.entries()) {\n const previous = events[index - 1]\n const isRepoSwitch =\n index > 0 &&\n event.repo_name !== previous.repo_name &&\n Date.parse(event.captured_at) - Date.parse(previous.captured_at) >= REPO_SWITCH_GAP_MS\n if (index === 0 || isRepoSwitch) groups.push([event])\n else groups[groups.length - 1].push(event)\n }\n return groups\n}\n\nexport interface OldSession {\n /** The repo the session was named after: its last event's, else its first's */\n repoName: string | null\n startedAt: string\n events: EventRow[]\n}\n\n/** A day's sessions as the old grouper made them, from its events in `captured_at, id` order. */\nexport function groupOldSessions(dayEvents: EventRow[]): OldSession[] {\n return splitByIdleGap(dayEvents)\n .flatMap(splitByRepo)\n .map((events) => ({\n repoName: events[events.length - 1].repo_name ?? events[0].repo_name ?? null,\n startedAt: events[0].captured_at,\n events,\n }))\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { EventRow } from '../types'\nimport { SESSION_KEY_SQL } from '../migrations/005_session_summaries'\nimport { MAPPED_ORIGIN, mapSummary, type AnchorSource } from '../summary-store'\nimport { anchorOfKeyedSession, type SummaryAnchor } from '../../summary/anchors'\nimport { parseSessionKey, sessionKey } from '../../summary/store'\nimport { localDateOf } from '../../utils/format'\nimport { groupOldSessions, type OldSession } from './frozen-old-sessions'\nimport type { StoredEngine } from '../../ai/engine'\nimport type { BatchResult, DataJob, JobIssue, JobVerification } from './types'\n\n// Maps every summary in `session_summaries` onto the events it describes (the `summary` table, migration 010), so it\n// survives regrouping. Nothing is deleted: a summary whose session can't be found is kept without a range, and\n// every doubtful match is recorded.\n\ninterface OldSummaryRow {\n id: number\n session_key: string\n engine: StoredEngine\n text: string\n created_at: string\n}\n\n/** A batch's regrouped days, so a day several summaries share is read and regrouped once. */\ntype RegroupedDays = Map<string, OldSession[]>\n\n/** The cached old session under the key — looked up within its day, so `idx_sessions_date` narrows the search. */\nfunction eventsOfCachedSession(db: MigrationDatabase, key: string, date: string): EventRow[] | null {\n const session = db\n .prepare(`SELECT id FROM sessions WHERE date = ? AND ${SESSION_KEY_SQL} = ? ORDER BY id LIMIT 1`)\n .get(date, key) as { id: number } | undefined\n if (session === undefined) return null\n return db\n .prepare(\n `SELECT events.* FROM events JOIN session_events ON session_events.event_id = events.id\n WHERE session_events.session_id = ? ORDER BY events.captured_at, events.id`\n )\n .all(session.id) as EventRow[]\n}\n\nfunction regroupDay(db: MigrationDatabase, date: string, days: RegroupedDays): OldSession[] {\n const known = days.get(date)\n if (known !== undefined) return known\n const dayEvents = db\n .prepare(`SELECT * FROM events WHERE date(captured_at, 'localtime') = ? ORDER BY captured_at, id`)\n .all(date) as EventRow[]\n const sessions = groupOldSessions(dayEvents)\n days.set(date, sessions)\n return sessions\n}\n\nfunction findSessionEvents(\n db: MigrationDatabase,\n key: string,\n days: RegroupedDays\n): { source: AnchorSource; events: EventRow[] } {\n const parsed = parseSessionKey(key)\n if (parsed === null) return { source: 'none', events: [] }\n const date = localDateOf(parsed.startedAt)\n const cached = eventsOfCachedSession(db, key, date)\n if (cached !== null && cached.length > 0) return { source: 'cached', events: cached }\n // No longer cached: the old session regrouped from its day with the frozen rules\n const match = regroupDay(db, date, days).find((session) => sessionKey(session.repoName, session.startedAt) === key)\n if (match !== undefined) return { source: 'regrouped', events: match.events }\n return { source: 'none', events: [] }\n}\n\nfunction issuesOf(row: OldSummaryRow, source: AnchorSource, anchor: SummaryAnchor): JobIssue[] {\n const reference = `${row.session_key} (${row.engine})`\n const issues: JobIssue[] = []\n if (source === 'none') {\n issues.push({ reference, reason: 'no events match this session any more; the summary is kept without a range' })\n }\n if (source === 'regrouped') {\n issues.push({\n reference,\n reason:\n 'the session was no longer cached; its range comes from regrouping its day with the default 30-minute idle timeout',\n })\n }\n if (anchor.isUnlinked) {\n issues.push({ reference, reason: \"the session's events aren't linked to a project yet\" })\n }\n if (anchor.isMixed) {\n const percent = Math.round(anchor.share * 100)\n issues.push({\n reference,\n reason: `the session mixed projects: its own project held ${percent}% of its events, so the summary matches no thread`,\n })\n }\n return issues\n}\n\nfunction mapRow(db: MigrationDatabase, row: OldSummaryRow, days: RegroupedDays): JobIssue[] {\n const { source, events } = findSessionEvents(db, row.session_key, days)\n const anchor = anchorOfKeyedSession(events, row.session_key)\n mapSummary(db, {\n sourceKey: row.session_key,\n engine: row.engine,\n text: row.text,\n createdAt: row.created_at,\n anchor,\n anchorSource: source,\n })\n return issuesOf(row, source, anchor)\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const rows = db\n .prepare(\n `SELECT rowid AS id, session_key, engine, text, created_at FROM session_summaries\n WHERE rowid > ? ORDER BY rowid LIMIT ?`\n )\n .all(afterId, limit) as OldSummaryRow[]\n const regroupedDays: RegroupedDays = new Map()\n return {\n lastId: rows.length === 0 ? null : rows[rows.length - 1].id,\n issues: rows.flatMap((row) => mapRow(db, row, regroupedDays)),\n }\n}\n\nfunction countRows(db: MigrationDatabase, sql: string): number {\n return (db.prepare(sql).get() as { count: number }).count\n}\n\nconst SOURCE_LABELS: Record<AnchorSource, string> = {\n thread: 'saved for a thread by the agent skill',\n live: 'from the live session, saved by the agent skill since this upgrade',\n cached: \"from the old session's cached events\",\n regrouped: \"from the old session's day, regrouped\",\n none: 'kept without a range (their events are gone)',\n}\n\n/** How many of the old store's summaries came from each source, and how many mixed projects. */\nfunction sourceNotes(db: MigrationDatabase): string[] {\n const rows = db\n .prepare(\n `SELECT summary.anchor_source AS source, COUNT(*) AS count, SUM(summary.is_mixed) AS mixed\n FROM session_summaries old\n JOIN summary ON summary.source_key = old.session_key AND summary.engine = old.engine\n GROUP BY summary.anchor_source ORDER BY count DESC`\n )\n .all() as { source: AnchorSource; count: number; mixed: number }[]\n const notes = rows.map((row) => `${row.count} ${SOURCE_LABELS[row.source]}`)\n const mixed = rows.reduce((total, row) => total + row.mixed, 0)\n if (mixed > 0) notes.push(`${mixed} described a session that mixed projects, so they match no thread`)\n return notes\n}\n\n/** Passes when every old summary has a row in `summary`: none was lost, whether or not it found its events. */\nfunction verify(db: MigrationDatabase): JobVerification {\n const checked = countRows(db, 'SELECT COUNT(*) AS count FROM session_summaries')\n const joined = `FROM session_summaries old\n LEFT JOIN summary ON summary.source_key = old.session_key AND summary.engine = old.engine`\n const missing = countRows(db, `SELECT COUNT(*) AS count ${joined} WHERE summary.id IS NULL`)\n const unanchored = countRows(\n db,\n `SELECT COUNT(*) AS count ${joined} WHERE summary.id IS NOT NULL AND summary.first_event_id IS NULL`\n )\n const notes = sourceNotes(db)\n if (missing > 0) notes.push(`${missing} summaries have no anchored copy`)\n return { checked, resolved: checked - missing - unanchored, unresolved: unanchored, hasPassed: missing === 0, notes }\n}\n\nexport const summaryAnchorsJob: DataJob = {\n name: 'summary-anchors',\n version: 1,\n description: 'Anchor saved summaries to the events they describe',\n // Only rows the job wrote and can write again; a summary `sessions summary set` saved is never deleted\n prepare: (db) =>\n db.exec(`DELETE FROM summary WHERE origin = '${MAPPED_ORIGIN}' AND EXISTS (\n SELECT 1 FROM session_summaries old WHERE old.session_key = summary.source_key AND old.engine = summary.engine\n )`),\n runBatch,\n verify,\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { BatchResult, DataJob, JobVerification } from './types'\nimport { getJobRow, getMetaValue, isJobDone, setMetaValue } from './store'\nimport { summaryAnchorsJob } from './summary-anchors'\n\n// Drops the linear sessions (WOR-151): nothing has read them since every reader moved onto threads, and their\n// saved summaries live on in `summary`. It only runs once `summary-anchors` copied every old summary there and its\n// check passed, after the runner's backup of this version — so dropping can never lose a summary.\n\n/**\n * In drop order: `session_events` references `sessions`. `sessions.summary` goes with its table. The queries below\n * interpolate these names: they're constants, and SQLite can't bind a table name.\n */\nexport const OLD_TABLES = ['session_events', 'sessions', 'session_summaries']\nconst DROPPED_ROWS_KEY = 'drop-old-sessions:dropped'\n\nexport const OLD_SUMMARIES_NOT_COPIED =\n \"Old summaries aren't all copied yet — the old session tables are kept. Run workscribe bug-report to report it.\"\n\ninterface DroppedTable {\n name: string\n rows: number\n}\n\nfunction existingOldTables(db: MigrationDatabase): string[] {\n return OLD_TABLES.filter(\n (name) => db.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?\").get(name) !== undefined\n )\n}\n\n/** `summary-anchors` is done at its current version, and its check found every old summary copied. */\nfunction areOldSummariesCopied(db: MigrationDatabase): boolean {\n const row = getJobRow(db, summaryAnchorsJob.name)\n if (!isJobDone(row, summaryAnchorsJob) || !row?.verification) return false\n return (JSON.parse(row.verification) as JobVerification).hasPassed\n}\n\n/** The one batch: refuses — failing the job, which retries next run — until the summaries are safe. */\nfunction runBatch(db: MigrationDatabase): BatchResult {\n if (!areOldSummariesCopied(db)) throw new Error(OLD_SUMMARIES_NOT_COPIED)\n const dropped: DroppedTable[] = existingOldTables(db).map((name) => ({\n name,\n rows: (db.prepare(`SELECT COUNT(*) AS rows FROM ${name}`).get() as { rows: number }).rows,\n }))\n for (const name of OLD_TABLES) db.exec(`DROP TABLE IF EXISTS ${name}`)\n if (dropped.length > 0) setMetaValue(db, DROPPED_ROWS_KEY, JSON.stringify(dropped))\n return { lastId: null, issues: [] }\n}\n\nfunction verify(db: MigrationDatabase): JobVerification {\n const dropped = JSON.parse(getMetaValue(db, DROPPED_ROWS_KEY) ?? '[]') as DroppedTable[]\n const checked = dropped.reduce((total, table) => total + table.rows, 0)\n const left = existingOldTables(db)\n const notes = dropped.map((table) => `dropped ${table.name} (${table.rows} rows)`)\n if (left.length > 0) notes.push(`still there: ${left.join(', ')}`)\n return { checked, resolved: checked, unresolved: 0, hasPassed: left.length === 0, notes }\n}\n\nexport const dropOldSessionsJob: DataJob = {\n name: 'drop-old-sessions',\n version: 1,\n description: 'Remove the old session tables, now that their summaries are anchored',\n // Nothing to clear: the job drops tables, and a rerun drops what's left\n prepare: () => undefined,\n runBatch,\n verify,\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { BatchResult, DataJob, JobVerification } from './types'\nimport { getMetaValue, setMetaValue } from './store'\nimport { withImmediateTransaction } from '../transaction'\nimport { linkEventTickets } from '../tickets'\nimport { ticketKeysOf, ticketPatternOf } from '../../capture/tickets'\nimport type { EventRow } from '../types'\n\n// Links the history's events to the tickets their branch names and commit messages name (WOR-144), as capture does\n// for new events. It reads the pattern stored by `config set tickets.pattern`, never the live config, so a rerun\n// derives the same links; changing the pattern through `config set` reruns it from `prepare`.\n\nexport const TICKET_REFS_JOB_NAME = 'ticket-refs'\nconst PATTERN_KEY = 'tickets:pattern'\n\ntype EventToLink = Pick<EventRow, 'id' | 'category' | 'metadata' | 'exit_code' | 'captured_at' | 'branch'> & {\n branch_id: number | null\n linked_branch: string | null\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const events = db\n .prepare(\n `SELECT events.id, events.category, events.metadata, events.exit_code, events.captured_at, events.branch,\n events.branch_id, branch.name AS linked_branch\n FROM events LEFT JOIN branch ON branch.id = events.branch_id\n WHERE events.id > ? AND events.project_id IS NOT NULL\n ORDER BY events.id LIMIT ?`\n )\n .all(afterId, limit) as EventToLink[]\n if (events.length === 0) return { lastId: null, issues: [] }\n const pattern = ticketPatternOf(getMetaValue(db, PATTERN_KEY) ?? undefined)\n for (const event of events) {\n const keys = ticketKeysOf({ linkedBranch: event.linked_branch, event }, pattern)\n linkEventTickets(db, { eventId: event.id, branchId: event.branch_id, keys })\n }\n return { lastId: events[events.length - 1].id, issues: [] }\n}\n\nfunction countOf(db: MigrationDatabase, sql: string): number {\n return (db.prepare(sql).get() as { count: number }).count\n}\n\n/** Links are derived from the events, so none can go missing: the counts are for `status` and `upgrade`. */\nfunction verify(db: MigrationDatabase): JobVerification {\n const checked = countOf(db, 'SELECT COUNT(*) AS count FROM events WHERE project_id IS NOT NULL')\n const tickets = countOf(db, 'SELECT COUNT(*) AS count FROM ticket')\n const branchLinks = countOf(db, 'SELECT COUNT(*) AS count FROM ticket_ref WHERE branch_id IS NOT NULL')\n const commitLinks = countOf(db, 'SELECT COUNT(*) AS count FROM ticket_ref WHERE event_id IS NOT NULL')\n return {\n checked,\n resolved: checked,\n unresolved: 0,\n hasPassed: true,\n notes: [`tickets: ${tickets}; branch links: ${branchLinks}; commit links: ${commitLinks}`],\n }\n}\n\n/**\n * Stores the pattern the history is linked with and restarts the job, so the next data command relinks every event\n * from `prepare`. One transaction: the pattern and the restart never disagree.\n */\nexport function requestTicketRelink(db: MigrationDatabase, pattern: string): void {\n withImmediateTransaction(db, () => {\n setMetaValue(db, PATTERN_KEY, pattern)\n db.prepare('DELETE FROM data_jobs WHERE name = ?').run(TICKET_REFS_JOB_NAME)\n })\n}\n\nexport const ticketRefsJob: DataJob = {\n name: TICKET_REFS_JOB_NAME,\n version: 1,\n description: 'Link stored events to the tickets their branches and commits name',\n // Every link is derived again, so a rerun starts from none\n prepare: (db) => db.exec('DELETE FROM ticket_ref; DELETE FROM ticket'),\n runBatch,\n verify,\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { BatchResult, DataJob, JobVerification } from './types'\nimport { BUSY_TIMEOUT_MS } from '../transaction'\nimport { deleteMetaValue, getMetaValue, setMetaValue } from './store'\nimport { commandHint } from '../../capture/hint'\nimport { REDACTED_TEXT, redact } from '../../capture/redact'\nimport { debugLog } from '../../utils/logger'\n\n// Applies today's built-in redaction to what was stored before it existed (WOR-167). A job can't read the config, so\n// `redact.extra` only ever reaches new events. Rows with nothing to redact are never written. Bump the version when the\n// built-in patterns widen, so the history gets the new ones too.\n\nconst JOB_NAME = 'scrub-secrets'\nconst VERIFY_PAGE_SIZE = 500\nconst CLEANUP_KEY = 'scrub-secrets:file-cleanup'\nconst CLEANUP_NEEDS_VACUUM = 'vacuum'\nconst CLEANUP_NEEDS_CHECKPOINT = 'checkpoint'\n// An ordinary command runs the cleanup, and it must not stall behind a capture or a reader: the next one tries again\nconst CLEANUP_LOCK_WAIT_MS = 200\n\ninterface StoredText {\n command: string\n metadata: string | null\n}\n\ntype StoredEvent = StoredText & { id: number }\n\ninterface CommandChange {\n before: string\n after: string\n}\n\n/**\n * Most values were read from the command, which redaction judged with all of its context. So one still in the scrubbed\n * command stays (`fix mongo -p flag`, inside `-m \"...\"`). Otherwise redaction may recognise the secret in the value itself,\n * which keeps the rest of it (`add --pass [REDACTED]`). If not, a value only the old command held was removed from it,\n * even though it says nothing alone: the `FAKE` of `npm install --token FAKE lodash`.\n */\nfunction scrubValue(value: string, command: CommandChange): string {\n if (command.after.includes(value)) return value\n const redacted = redact(value)\n if (redacted !== value) return redacted\n return command.before.includes(value) ? REDACTED_TEXT : value\n}\n\nfunction scrubStrings(value: unknown, command: CommandChange): unknown {\n if (typeof value === 'string') return scrubValue(value, command)\n if (Array.isArray(value)) return value.map((item) => scrubStrings(item, command))\n if (value === null || typeof value !== 'object') return value\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, scrubStrings(item, command)]))\n}\n\nfunction hasHint(value: unknown): value is { hint: string } {\n return typeof (value as { hint?: unknown } | null)?.hint === 'string'\n}\n\nfunction scrubMetadata(metadata: string | null, command: CommandChange): string | null {\n if (metadata === null) return null\n let parsed: unknown\n try {\n parsed = JSON.parse(metadata)\n } catch {\n // Unreadable, so nothing else can use it either: it still mustn't keep a secret\n return redact(metadata)\n }\n const scrubbed = scrubStrings(parsed, command)\n // A stored hint can end midway through a secret, too short for a pattern to recognise, so it's taken again\n if (command.after !== command.before && hasHint(scrubbed)) scrubbed.hint = commandHint(command.after)\n const rewritten = JSON.stringify(scrubbed)\n return rewritten === JSON.stringify(parsed) ? metadata : rewritten\n}\n\n/** The event's text with its secrets removed, or null when there's nothing to remove. */\nexport function scrubEvent({ command, metadata }: StoredText): StoredText | null {\n const scrubbedCommand = redact(command)\n const scrubbedMetadata = scrubMetadata(metadata, { before: command, after: scrubbedCommand })\n if (scrubbedCommand === command && scrubbedMetadata === metadata) return null\n return { command: scrubbedCommand, metadata: scrubbedMetadata }\n}\n\n/** An IDE save's command is its file's path, and it has no metadata. An event with its own command is still checked. */\nfunction readEvents(db: MigrationDatabase, afterId: number, limit: number): StoredEvent[] {\n return db\n .prepare(\n `SELECT id, command, metadata FROM events\n WHERE id > ? AND command IS NOT file_path\n ORDER BY id LIMIT ?`\n )\n .all(afterId, limit) as StoredEvent[]\n}\n\nfunction runBatch(db: MigrationDatabase, afterId: number, limit: number): BatchResult {\n const events = readEvents(db, afterId, limit)\n if (events.length === 0) return { lastId: null, issues: [] }\n const update = db.prepare('UPDATE events SET command = ?, metadata = ? WHERE id = ?')\n for (const event of events) {\n const scrubbed = scrubEvent(event)\n if (scrubbed) update.run(scrubbed.command, scrubbed.metadata, event.id)\n }\n return { lastId: events[events.length - 1].id, issues: [] }\n}\n\n/** Nothing is ever removed, and a rerun couldn't clear a row that redacts differently twice: the count is for `status`. */\nfunction verify(db: MigrationDatabase): JobVerification {\n let checked = 0\n let unresolved = 0\n let afterId = 0\n for (;;) {\n const events = readEvents(db, afterId, VERIFY_PAGE_SIZE)\n if (events.length === 0) break\n checked += events.length\n unresolved += events.filter((event) => scrubEvent(event) !== null).length\n afterId = events[events.length - 1].id\n }\n const notes = unresolved > 0 ? [`events that would still change: ${unresolved}`] : []\n return { checked, resolved: checked - unresolved, unresolved, hasPassed: true, notes }\n}\n\n/**\n * An update leaves the old text in the file's free space, and a page's unused space can hold copies from long before,\n * which even `secure_delete` doesn't reach. Only `VACUUM` rewrites the file without them, and the checkpoint then moves\n * the result out of the WAL. Each step is recorded, so a busy reader or a crash means carrying on from it next time.\n */\nfunction cleanUpFile(db: MigrationDatabase): void {\n db.exec(`PRAGMA busy_timeout = ${CLEANUP_LOCK_WAIT_MS}`)\n try {\n if (getMetaValue(db, CLEANUP_KEY) === CLEANUP_NEEDS_VACUUM) {\n db.exec('VACUUM')\n setMetaValue(db, CLEANUP_KEY, CLEANUP_NEEDS_CHECKPOINT)\n }\n const result = db.prepare('PRAGMA wal_checkpoint(TRUNCATE)').get() as { busy: number } | undefined\n if (result?.busy) {\n debugLog(`data job ${JOB_NAME}: file cleanup`, 'left for later, a reader is busy')\n return\n }\n deleteMetaValue(db, CLEANUP_KEY)\n } finally {\n db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`)\n }\n}\n\nexport const scrubSecretsJob: DataJob = {\n name: JOB_NAME,\n version: 1,\n description: 'Remove secrets from stored commands and metadata',\n // Redacting what's already redacted changes nothing, so a rerun only needs the file cleaned again\n prepare: (db) => setMetaValue(db, CLEANUP_KEY, CLEANUP_NEEDS_VACUUM),\n runBatch,\n verify,\n cleanup: { isPending: (db) => getMetaValue(db, CLEANUP_KEY) !== null, run: cleanUpFile },\n}\n","import type { DataJob } from './types'\nimport { createProjectDimensionsJob } from './project-dimensions'\nimport { probeDirectory } from '../../capture/git'\nimport { summaryAnchorsJob } from './summary-anchors'\nimport { dropOldSessionsJob } from './drop-old-sessions'\nimport { ticketRefsJob } from './ticket-refs'\nimport { scrubSecretsJob } from './scrub-secrets'\n\n// Ordered list — append only. Jobs run in this order and a job only starts once every job before it is done, so a\n// later job can rely on an earlier one. To change what a job does for rows it already handled, bump its version:\n// the runner calls its `prepare` and runs it again over every row. Never bump `summary-anchors`: it reads the old\n// session tables, which `drop-old-sessions` removes once it has run. Append new jobs before `drop-old-sessions`, not\n// after: in the stuck state it accepts (a failed `summary-anchors` check) it fails on every run, and the runner never\n// starts a job after a failed one. `ticket-refs` reads `events.branch_id`: bump it whenever `project-dimensions` is bumped.\nexport const JOBS: DataJob[] = [\n createProjectDimensionsJob({ probeDirectory }),\n summaryAnchorsJob,\n ticketRefsJob,\n scrubSecretsJob,\n dropOldSessionsJob,\n]\n","import fs from 'fs'\nimport path from 'path'\nimport type { MigrationDatabase } from '../migration-types'\nimport { isDatabaseLocked, withImmediateTransaction } from '../transaction'\nimport { debugLog } from '../../utils/logger'\nimport { errorMessage } from '../../utils/error-message'\nimport { deleteMetaValue, getMetaValue, setMetaValue } from './store'\n\nexport const BACKUP_KEY_PREFIX = 'backup:'\nexport const BACKUPS_KEPT = 3\n// A claim this old belongs to a process that died mid-backup\nexport const STALE_CLAIM_MS = 10 * 60 * 1000\n\nconst FINISHED_BACKUP = /^workscribe-.+\\.db$/\nconst TEMP_SUFFIX = '.tmp'\nconst PRIVATE_DIRECTORY_MODE = 0o700\nconst PRIVATE_FILE_MODE = 0o600\n\n/** What `meta['backup:<version>']` holds: a claim while a backup is being taken, then where it went. */\nexport type BackupRecord = { state: 'claimed'; at: string } | { state: 'done'; at: string; path: string }\n\nexport type BackupOutcome =\n | { status: 'exists'; path: string }\n | { status: 'created'; path: string }\n | { status: 'busy' }\n | { status: 'failed'; error: string }\n\nexport interface BackupOptions {\n cliVersion: string\n directory: string\n now?: () => Date\n /** Called just before a new backup is taken — the slow part, worth telling the user about. */\n onCreate?: () => void\n}\n\nexport function parseBackupRecord(value: string | null): BackupRecord | null {\n if (value === null) return null\n try {\n return JSON.parse(value) as BackupRecord\n } catch {\n // An unreadable record is treated as no record: the worst case is one extra backup\n return null\n }\n}\n\ntype Claim = { status: 'exists'; path: string } | { status: 'busy' } | { status: 'claimed' }\n\n/** Claims the version's backup under the write lock, so two processes never both take it. */\nfunction claimBackup(db: MigrationDatabase, key: string, now: Date): Claim {\n return withImmediateTransaction(db, () => {\n const record = parseBackupRecord(getMetaValue(db, key))\n if (record?.state === 'done') return { status: 'exists', path: record.path }\n const isClaimedElsewhere = record?.state === 'claimed' && now.getTime() - Date.parse(record.at) < STALE_CLAIM_MS\n if (isClaimedElsewhere) return { status: 'busy' }\n setMetaValue(db, key, JSON.stringify({ state: 'claimed', at: now.toISOString() }))\n return { status: 'claimed' }\n })\n}\n\n/** The backups folder, private to the user: `VACUUM INTO` creates world-readable files. */\nfunction preparePrivateDirectory(directory: string): void {\n fs.mkdirSync(directory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE })\n fs.chmodSync(directory, PRIVATE_DIRECTORY_MODE)\n for (const file of fs.readdirSync(directory)) {\n if (file.endsWith(TEMP_SUFFIX)) fs.rmSync(path.join(directory, file), { force: true })\n }\n}\n\n/** Keeps the newest finished backups; temp files and anything else in the folder are never counted. */\nexport function pruneBackups(directory: string, keep: number = BACKUPS_KEPT): void {\n const backups = fs\n .readdirSync(directory)\n .filter((file) => FINISHED_BACKUP.test(file))\n .map((file) => ({ file, modifiedAt: fs.statSync(path.join(directory, file)).mtimeMs }))\n .sort((first, second) => second.modifiedAt - first.modifiedAt)\n for (const backup of backups.slice(keep)) fs.rmSync(path.join(directory, backup.file), { force: true })\n}\n\nfunction backupKey(cliVersion: string): string {\n return `${BACKUP_KEY_PREFIX}${cliVersion}`\n}\n\n/**\n * The copy was taken while this version's claim was in progress, so it holds that claim. Restored as is, it would\n * make every command think a backup is under way for up to STALE_CLAIM_MS.\n */\nfunction clearClaimInCopy(db: MigrationDatabase, copyPath: string, key: string): void {\n db.prepare('ATTACH DATABASE ? AS backup_copy').run(copyPath)\n try {\n db.prepare('DELETE FROM backup_copy.meta WHERE key = ?').run(key)\n } finally {\n db.exec('DETACH DATABASE backup_copy')\n }\n}\n\n/** Copies the live database (WAL included) to a private file, appearing under its final name only once it's 0600. */\nfunction copyDatabase(db: MigrationDatabase, options: BackupOptions, now: Date): string {\n preparePrivateDirectory(options.directory)\n const name = `workscribe-${options.cliVersion}-${now.toISOString().replace(/[:.]/g, '-')}.db`\n const finalPath = path.join(options.directory, name)\n const tempPath = `${finalPath}${TEMP_SUFFIX}`\n try {\n // VACUUM can't run inside a transaction, so this runs on its own; SQLite gives it a consistent snapshot\n db.prepare('VACUUM INTO ?').run(tempPath)\n fs.chmodSync(tempPath, PRIVATE_FILE_MODE)\n clearClaimInCopy(db, tempPath, backupKey(options.cliVersion))\n fs.renameSync(tempPath, finalPath)\n } catch (error) {\n fs.rmSync(tempPath, { force: true })\n throw error\n }\n pruneBackups(options.directory)\n return finalPath\n}\n\n/**\n * Makes sure this CLI version has a backup of the database before any data job changes it. Never call it from\n * `_capture` or `_hook`: the copy takes as long as the database is big.\n */\nexport function ensureBackup(db: MigrationDatabase, options: BackupOptions): BackupOutcome {\n const now = options.now?.() ?? new Date()\n const key = backupKey(options.cliVersion)\n const claim = tryClaimBackup(db, key, now)\n if (claim.status !== 'claimed') return claim\n\n try {\n options.onCreate?.()\n const backupPath = copyDatabase(db, options, now)\n setMetaValue(db, key, JSON.stringify({ state: 'done', at: now.toISOString(), path: backupPath }))\n return { status: 'created', path: backupPath }\n } catch (error) {\n releaseClaim(db, key)\n return { status: 'failed', error: errorMessage(error) }\n }\n}\n\n/** Another process holding the write lock (a capture, or another command claiming the backup) means try later. */\nfunction tryClaimBackup(db: MigrationDatabase, key: string, now: Date): Claim {\n try {\n return claimBackup(db, key, now)\n } catch (error) {\n if (isDatabaseLocked(error)) return { status: 'busy' }\n throw error\n }\n}\n\n/** Lets the next run try again. If the database refuses even this, the claim goes stale after STALE_CLAIM_MS. */\nfunction releaseClaim(db: MigrationDatabase, key: string): void {\n try {\n deleteMetaValue(db, key)\n } catch (error) {\n debugLog('data job backup: release claim', error)\n }\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport { isDatabaseLocked, withImmediateTransaction, withReadTransaction } from '../transaction'\nimport { debugLog } from '../../utils/logger'\nimport { errorMessage } from '../../utils/error-message'\nimport type { DataJob, JobVerification } from './types'\nimport { failJob, finishJob, getJobRow, isJobDone, recordJobIssues, setJobCursor, startJob } from './store'\n\n// Small enough that one batch holds the write lock for far less than a capture's 5 s busy timeout\nexport const DEFAULT_BATCH_SIZE = 500\n\n// A batch waits at most this long for another process's write lock before the run gives up until next time\nconst BATCH_LOCK_WAIT_MS = 1000\n\nexport type JobOutcome = 'done' | 'paused' | 'failed'\n\nexport interface JobRunResult {\n name: string\n version: number\n outcome: JobOutcome\n verification?: JobVerification\n error?: string\n}\n\n/** Whether this version has a backup: `busy` while another process is taking it, `unavailable` when it failed. */\nexport type BackupReadiness = 'ready' | 'busy' | 'unavailable'\n\n/** `not-needed`: nothing was pending. `busy` or `unavailable`: no backup for this version yet, so nothing ran. */\nexport type BackupState = 'not-needed' | BackupReadiness\n\nexport interface RunResult {\n backup: BackupState\n jobs: JobRunResult[]\n}\n\nexport interface RunOptions {\n /** Makes sure a backup exists for this version, taking one if needed. */\n ensureBackup: () => BackupReadiness\n /** Stop starting new batches after this long. Omitted: run every pending job to completion. */\n budgetMs?: number\n batchSize?: number\n now?: () => Date\n /** Called after each batch, and when a job finishes. */\n onProgress?: (progress: { job: DataJob; elapsedMs: number }) => void\n}\n\ninterface RunContext {\n batchSize: number\n now: () => Date\n isOverBudget: () => boolean\n onProgress: (job: DataJob) => void\n}\n\nconst NEWER_VERSION_ERROR = 'started by a newer version of Workscribe — update Workscribe to finish it'\n\n/**\n * A newer CLI restarted this job with newer code. Finishing it with this code would mix the two, and the newer CLI\n * would then see it done and never redo it.\n */\nclass SupersededError extends Error {\n constructor() {\n super(NEWER_VERSION_ERROR)\n }\n}\n\n/**\n * Runs one batch holding the write lock. Rereads the job's row inside it, so two processes never redo each other's\n * work, and stops if a newer CLI took the job over since the last batch.\n */\nfunction runLockedBatch(db: MigrationDatabase, job: DataJob, context: RunContext): number | null {\n return withImmediateTransaction(\n db,\n () => {\n const row = getJobRow(db, job.name)\n if (row !== null && row.version !== job.version) throw new SupersededError()\n if (row === null || row.status === 'done') return null\n const batch = job.runBatch(db, row.cursor, context.batchSize)\n recordJobIssues(db, job, batch.issues, context.now().toISOString())\n if (batch.lastId !== null) setJobCursor(db, job.name, batch.lastId)\n return batch.lastId\n },\n { busyTimeoutMs: BATCH_LOCK_WAIT_MS }\n )\n}\n\n/** Checks the job's work without the write lock — a full-table check never blocks a capture. */\nfunction finish(db: MigrationDatabase, job: DataJob, context: RunContext): JobRunResult {\n const verification = withReadTransaction(db, () => job.verify(db))\n const isFinished = withImmediateTransaction(db, () => finishJob(db, job, verification, context.now().toISOString()))\n if (!isFinished) throw new SupersededError()\n context.onProgress(job)\n return { name: job.name, version: job.version, outcome: 'done', verification }\n}\n\nfunction runJob(db: MigrationDatabase, job: DataJob, context: RunContext): JobRunResult {\n const summary = { name: job.name, version: job.version }\n // Checked here before `startJob` would touch the row, and again in every batch and when finishing\n const storedVersion = getJobRow(db, job.name)?.version ?? 0\n if (storedVersion > job.version) return { ...summary, outcome: 'paused', error: NEWER_VERSION_ERROR }\n try {\n withImmediateTransaction(db, () => startJob(db, job, context.now().toISOString()))\n // After `startJob`, so a version bump's `prepare` clears the old work before `gather` builds on it\n if (job.gather?.(db, { isOverBudget: context.isOverBudget }) === 'incomplete')\n return { ...summary, outcome: 'paused' }\n while (runLockedBatch(db, job, context) !== null) {\n context.onProgress(job)\n if (context.isOverBudget()) return { ...summary, outcome: 'paused' }\n }\n return finish(db, job, context)\n } catch (error) {\n if (error instanceof SupersededError) return { ...summary, outcome: 'paused', error: error.message }\n // Another process holding the lock (a capture, or the same job elsewhere) isn't a failure: try again next time\n if (isDatabaseLocked(error)) return { ...summary, outcome: 'paused' }\n const message = errorMessage(error)\n recordFailure(db, job, message)\n return { ...summary, outcome: 'failed', error: message }\n }\n}\n\n/** Stores the failure for `status`. If even that write fails, the run still reports the original error. */\nfunction recordFailure(db: MigrationDatabase, job: DataJob, message: string): void {\n try {\n failJob(db, job, message)\n } catch (error) {\n debugLog(`data job ${job.name}: couldn't record the failure`, error)\n }\n}\n\n/** Only for done jobs. Another process holding the lock, or any other error, just leaves the cleanup for next time. */\nfunction runPendingCleanups(db: MigrationDatabase, jobs: DataJob[]): void {\n for (const job of jobs) {\n if (!job.cleanup || !isJobDone(getJobRow(db, job.name), job)) continue\n try {\n if (job.cleanup.isPending(db)) job.cleanup.run(db)\n } catch (error) {\n debugLog(`data job ${job.name}: cleanup`, error)\n }\n }\n}\n\n/**\n * Runs the pending jobs in order, stopping at the first one that doesn't finish, so a job can rely on every job\n * before it. Nothing runs unless `ensureBackup` confirms a backup. Each batch commits on its own: an interrupted run\n * resumes from the last batch it committed. Done jobs' cleanups run last, retried until they finish.\n */\nexport function runPendingJobs(db: MigrationDatabase, jobs: DataJob[], options: RunOptions): RunResult {\n const pending = jobs.filter((job) => !isJobDone(getJobRow(db, job.name), job))\n if (pending.length === 0) {\n runPendingCleanups(db, jobs)\n return { backup: 'not-needed', jobs: [] }\n }\n const backup = options.ensureBackup()\n if (backup !== 'ready') return { backup, jobs: [] }\n\n const now = options.now ?? (() => new Date())\n const startedAt = now().getTime()\n const context: RunContext = {\n batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,\n now,\n isOverBudget: () => options.budgetMs !== undefined && now().getTime() - startedAt >= options.budgetMs,\n onProgress: (job) => options.onProgress?.({ job, elapsedMs: now().getTime() - startedAt }),\n }\n\n const results: JobRunResult[] = []\n for (const job of pending) {\n const result = runJob(db, job, context)\n results.push(result)\n if (result.outcome !== 'done') break\n }\n runPendingCleanups(db, jobs)\n return { backup: 'ready', jobs: results }\n}\n","import type { MigrationDatabase } from '../migration-types'\nimport type { DataJob, JobVerification } from './types'\nimport { BACKUP_KEY_PREFIX, parseBackupRecord } from './backup'\nimport { getJobRow, getMetaValue, getMetaValuesWithPrefix, isJobDone, setMetaValue, type JobRow } from './store'\n\nconst CLI_VERSION_KEY = 'cliVersion'\n\nexport interface DataStatus {\n pending: string[]\n failed: { name: string; error: string } | null\n /** Finished jobs whose check didn't pass: their counts show data went missing. Never cleared by a rerun. */\n failedChecks: string[]\n /** Rows the finished jobs couldn't resolve, across every job (kept in `data_job_issues`). */\n unresolvedRows: number\n latestBackup: string | null\n}\n\nfunction readVerification(row: JobRow | null): JobVerification | null {\n if (row?.status !== 'done' || !row.verification) return null\n return JSON.parse(row.verification) as JobVerification\n}\n\nfunction readLatestBackup(db: MigrationDatabase): string | null {\n const paths = [...getMetaValuesWithPrefix(db, BACKUP_KEY_PREFIX).values()]\n .map(parseBackupRecord)\n .flatMap((record) => (record?.state === 'done' ? [record] : []))\n .sort((first, second) => second.at.localeCompare(first.at))\n .map((record) => record.path)\n return paths[0] ?? null\n}\n\n/** What `status` and `upgrade` say about the data jobs: pending, failed, checks that didn't pass, the last backup. */\nexport function readDataStatus(db: MigrationDatabase, jobs: DataJob[]): DataStatus {\n const rows = jobs.map((job) => ({ job, row: getJobRow(db, job.name) }))\n const verifications = rows.map(({ job, row }) => ({ name: job.name, verification: readVerification(row) }))\n const failedRow = rows.find(({ row }) => row?.status === 'failed')?.row\n return {\n pending: rows.filter(({ job, row }) => !isJobDone(row, job)).map(({ job }) => job.name),\n failed: failedRow ? { name: failedRow.name, error: failedRow.error ?? 'unknown error' } : null,\n failedChecks: verifications.filter(({ verification }) => verification?.hasPassed === false).map(({ name }) => name),\n unresolvedRows: verifications.reduce((total, { verification }) => total + (verification?.unresolved ?? 0), 0),\n latestBackup: readLatestBackup(db),\n }\n}\n\n/** What a command that reads data only after a job has run says while that job hasn't. */\nexport const UPGRADE_NEEDED_MESSAGE =\n \"Workscribe hasn't finished upgrading your data — run workscribe upgrade, then try again.\"\n\n/**\n * Whether every job named is in `jobs` and done. A name missing from `jobs` counts as not done, so a reader gated on\n * a job never answers before it ran.\n */\nexport function areJobsDone(db: MigrationDatabase, jobs: DataJob[], names: ReadonlySet<string>): boolean {\n const named = jobs.filter((job) => names.has(job.name))\n return named.length === names.size && named.every((job) => isJobDone(getJobRow(db, job.name), job))\n}\n\n/** Remembers the last CLI version that ran a data command — the upgrade signal the CLI had no record of before. */\nexport function recordCliVersion(db: MigrationDatabase, version: string): void {\n if (getMetaValue(db, CLI_VERSION_KEY) === version) return\n setMetaValue(db, CLI_VERSION_KEY, version)\n}\n","import { getDb } from '../db'\nimport { JOBS } from '../db/jobs'\nimport { ensureBackup } from '../db/jobs/backup'\nimport { runPendingJobs, type RunOptions, type RunResult } from '../db/jobs/runner'\nimport { readDataStatus, recordCliVersion, type DataStatus } from '../db/jobs/state'\nimport { BACKUPS_DIR } from '../utils/paths'\nimport { log } from '../utils/logger'\nimport { errorMessage } from '../utils/error-message'\n\n// A command people typed waits at most this long for data jobs before it does its own work; the rest runs next time\nexport const IMPLICIT_RUN_BUDGET_MS = 2000\n// Say something only when a run is slow enough to notice\nconst PROGRESS_NOTICE_AFTER_MS = 1000\n\nconst BACKUP_NOTICE = 'Backing up your Workscribe data before upgrading it (once per version)…'\nconst PROGRESS_NOTICE = 'Upgrading your Workscribe data… (run `workscribe upgrade` to finish it in one go)'\n\nexport function getDataStatus(): DataStatus {\n return readDataStatus(getDb(), JOBS)\n}\n\n/** Runs the pending data jobs on the real database, taking this version's backup first. */\nexport function runDataJobs(cliVersion: string, options: Omit<RunOptions, 'ensureBackup'> = {}): RunResult {\n const db = getDb()\n return runPendingJobs(db, JOBS, {\n ...options,\n ensureBackup: () => {\n const outcome = ensureBackup(db, {\n cliVersion,\n directory: BACKUPS_DIR,\n onCreate: () => log.notice(BACKUP_NOTICE),\n })\n if (outcome.status === 'busy') return 'busy'\n if (outcome.status === 'failed') {\n log.notice(`Couldn't back up your Workscribe data: ${outcome.error}`)\n return 'unavailable'\n }\n return 'ready'\n },\n })\n}\n\n/**\n * Before an allowlisted command (see `shouldRunDataJobs`): records the version and gives pending data jobs a short\n * budget. Never fails the command — a job that couldn't run leaves every reader on its current path.\n */\nexport function runDataJobsBeforeCommand(command: string | undefined, cliVersion: string): void {\n try {\n recordCliVersion(getDb(), cliVersion)\n // `upgrade` runs the jobs itself, with no budget\n if (command === 'upgrade') return\n let hasNoticed = false\n runDataJobs(cliVersion, {\n budgetMs: IMPLICIT_RUN_BUDGET_MS,\n onProgress: ({ elapsedMs }) => {\n if (hasNoticed || elapsedMs < PROGRESS_NOTICE_AFTER_MS) return\n hasNoticed = true\n log.notice(PROGRESS_NOTICE)\n },\n })\n } catch (error) {\n log.notice(`Couldn't upgrade your Workscribe data: ${errorMessage(error)}`)\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport chalk from 'chalk'\nimport { getEventCount, getLastEvent, getRecentOtherRate } from '../db'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { formatDate, todayDate } from '../utils/format'\nimport { getEventsForDate } from '../db'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { getPendingAlerts } from '../db/alerts'\nimport { checkHookHealth } from '../capture/hooks/claude-code'\nimport { detectShell, shellHookHealth } from '../capture/shell-hook'\nimport { detectAgents } from '../skills/detect'\nimport { formatAgentSkillLine } from '../skills/actions'\nimport { printLegacyAiNotice } from './notices'\nimport { getDataStatus } from './data-jobs'\nimport type { DataStatus } from '../db/jobs/state'\n\n// Labelled on the first agent's row only, like the other status sections\nconst AGENT_SKILL_LABEL = 'Agent skill'\n\nfunction describeDataState(status: DataStatus): string {\n if (status.failed) {\n return `${chalk.red(`upgrade failed: ${status.failed.name}`)} ${chalk.dim('(run: workscribe upgrade)')}`\n }\n if (status.failedChecks.length > 0) {\n return `${chalk.red(`upgrade check failed: ${status.failedChecks.join(', ')}`)} ${chalk.dim('(run: workscribe bug-report)')}`\n }\n if (status.pending.length > 0) {\n const count = status.pending.length\n return `${chalk.yellow(`${count} upgrade${count === 1 ? '' : 's'} pending`)} ${chalk.dim('(run: workscribe upgrade)')}`\n }\n return chalk.green('up to date')\n}\n\n/** The \"Data\" line: data jobs pending, failed or failing their check, unresolved rows, and the latest backup. */\nexport function formatDataLines(status: DataStatus): string[] {\n const lines = [` Data ${describeDataState(status)}`]\n const { unresolvedRows } = status\n if (unresolvedRows > 0) {\n lines.push(chalk.dim(` ${unresolvedRows} row${unresolvedRows === 1 ? '' : 's'} left unresolved`))\n }\n if (status.latestBackup) lines.push(chalk.dim(` backup: ${status.latestBackup}`))\n return lines\n}\n\n/** An old shell hook (one that can drop terminal commands), or a leftover 0.2.1 Chronicle hook in the rc file. */\nfunction printShellHookWarnings(): void {\n const shell = detectShell()\n if (!shell) return\n const health = shellHookHealth(shell)\n if (health.isOutdated) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} Your ${shell.name} hook is from an older version and may not record terminal commands.`\n )\n log.plain(chalk.dim(' Run workscribe init to update it, then open a new terminal'))\n }\n if (health.hasChronicleHook) {\n log.plain('')\n log.plain(` ${chalk.yellow('⚠')} An old Chronicle hook in ${shell.rcFile} errors at every prompt.`)\n log.plain(chalk.dim(' Run workscribe init to remove it'))\n }\n}\n\nexport function registerStatus(program: Command): void {\n program\n .command('status')\n .description('Show capture status and event counts')\n .action(() => {\n const config = readConfig()\n printLegacyAiNotice(config)\n const total = getEventCount()\n const todayEvents = getEventsForDate(todayDate())\n const last = getLastEvent()\n const isPaused = fs.existsSync(PAUSE_FLAG_PATH)\n\n log.plain('')\n log.plain(chalk.bold(' Workscribe Status'))\n log.plain('')\n\n if (isPaused) {\n log.plain(` Capture ${chalk.yellow('paused')} ${chalk.dim('(run: workscribe resume)')}`)\n } else {\n log.plain(` Capture ${chalk.green('active')}`)\n }\n\n log.plain(` Events today ${chalk.cyan(String(todayEvents.length))}`)\n log.plain(` Events total ${chalk.cyan(String(total))}`)\n\n if (last) {\n log.plain(` Last event ${formatDate(last.captured_at)} ${chalk.dim(last.command.slice(0, 60))}`)\n }\n\n log.plain('')\n const agents = detectAgents()\n const skillHint = agents.length > 0 ? ' · AI-written ones via the agent skill below' : ''\n log.plain(` Summaries ${chalk.cyan('template')} ${chalk.dim(`(offline)${skillHint}`)}`)\n\n const activeProfiles = config.capture.profiles\n log.plain(\n ` Profiles ${activeProfiles.length > 0 ? chalk.cyan(activeProfiles.join(', ')) : chalk.dim('none')}`\n )\n\n for (const line of formatDataLines(getDataStatus())) log.plain(line)\n\n agents.forEach((agent, index) => {\n const label = index === 0 ? AGENT_SKILL_LABEL : ''\n log.plain(` ${label.padEnd(AGENT_SKILL_LABEL.length)} ${formatAgentSkillLine(agent)}`)\n })\n\n const pendingAlerts = getPendingAlerts()\n if (pendingAlerts.length > 0) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('!')} ${chalk.yellow(String(pendingAlerts.length))} pending security alert${pendingAlerts.length === 1 ? '' : 's'}` +\n chalk.dim(' (run: workscribe alerts)')\n )\n }\n\n printShellHookWarnings()\n\n const hookHealth = checkHookHealth()\n if (hookHealth.installed && !hookHealth.pinned) {\n log.plain('')\n if (hookHealth.confirmedBroken) {\n log.plain(\n ` ${chalk.yellow('⚠')} Claude Code hook is using an unpinned command and is failing right now — Claude Code activity is not being captured.`\n )\n } else {\n log.plain(\n ` ${chalk.yellow('⚠')} Claude Code hook is using an unpinned command, which can silently break if your default Node version changes.`\n )\n }\n log.plain(chalk.dim(' Run workscribe init to repair it'))\n } else if (hookHealth.installed && hookHealth.nodeMissing) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} The Node.js version the Claude Code hook uses is gone — Claude Code activity is not being captured.`\n )\n log.plain(chalk.dim(' Run workscribe init to repair it'))\n } else if (hookHealth.installed && !hookHealth.guarded) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} Claude Code hook is from an older version — it will show errors in Claude Code if Workscribe is ever removed.`\n )\n log.plain(chalk.dim(' Run workscribe init to update it'))\n }\n\n const { total: weekTotal, otherCount } = getRecentOtherRate(7)\n if (weekTotal > 0) {\n const otherPct = Math.round((otherCount / weekTotal) * 100)\n if (otherPct >= 25) {\n log.plain('')\n log.plain(\n ` ${chalk.yellow('⚠')} ${otherCount} of ${weekTotal} events (${otherPct}%) are uncategorised this week.`\n )\n log.plain(\n chalk.dim(\n ' Run workscribe profile detect — scan current directory for matching profiles\\n' +\n ' Run workscribe profile suggest — analyse your command history'\n )\n )\n }\n }\n\n log.plain('')\n })\n}\n","import { execFileSync } from 'child_process'\nimport path from 'path'\n\nconst GIT_TIMEOUT_MS = 3000\n// Commands are captured when they finish, so a `git commit` that opens a session is stamped after\n// its committer time — by however long the commit hooks ran. A commit closing a session can land\n// just after its event.\nexport const WINDOW_LEAD_MS = 120_000\nexport const WINDOW_TRAIL_MS = 60_000\n// How far back to look when checking who made a captured commit (pulled commits can be old)\nconst AUTHORSHIP_HISTORY_DEPTH = 500\n\nexport interface GitCommit {\n hash: string\n subject: string\n /** When it was committed (ISO 8601, committer date) — to place it on the session's branch timeline */\n committedAt: string\n}\n\ninterface CommitWindow {\n repoRoot: string\n since: string\n until: string\n /** Only commits committed by this email (case-insensitive); all commits when null. */\n author: string | null\n /** Only commits that changed this file */\n filePath?: string\n /** This call's git timeout (default GIT_TIMEOUT_MS) — `getFileCommits` passes what's left of its budget */\n timeoutMs?: number\n /** Look on every local branch, not just HEAD — a commit made on `feat/x` still counts after you're back on `main` */\n allBranches?: boolean\n}\n\n/**\n * Runs git without a shell. Returns trimmed stdout, or null on any failure (no git, not a repo, timeout).\n * `timeoutMs` must be at least 1: Node treats a timeout of 0 as no timeout at all.\n */\nfunction runGit(cwd: string, args: string[], timeoutMs = GIT_TIMEOUT_MS): string | null {\n try {\n return execFileSync('git', args, { cwd, timeout: timeoutMs, stdio: ['ignore', 'pipe', 'ignore'] })\n .toString()\n .trim()\n } catch {\n return null\n }\n}\n\n/** `timeoutMs`: this call's git timeout (default GIT_TIMEOUT_MS). */\nexport function getRepoRoot(cwd: string, timeoutMs?: number): string | null {\n return runGit(cwd, ['rev-parse', '--show-toplevel'], timeoutMs) || null\n}\n\n/** `timeoutMs`: this call's git timeout (default GIT_TIMEOUT_MS). */\nexport function getUserEmail(repoRoot: string, timeoutMs?: number): string | null {\n return runGit(repoRoot, ['config', 'user.email'], timeoutMs) || null\n}\n\n/**\n * Subjects in the last AUTHORSHIP_HISTORY_DEPTH commits on HEAD that only other people committed.\n * Empty without an author to compare against, or when git fails.\n */\nexport function getOthersSubjects(repoRoot: string, author: string | null): string[] {\n if (!author) return []\n const output = runGit(repoRoot, ['log', `-n${AUTHORSHIP_HISTORY_DEPTH}`, '--format=%ce%x09%s'])\n if (!output) return []\n\n const wantedAuthor = author.toLowerCase()\n const mine = new Set<string>()\n const theirs = new Set<string>()\n for (const line of output.split('\\n')) {\n const [email, ...subjectParts] = line.split('\\t')\n const subject = subjectParts.join('\\t')\n if (!subject) continue\n if (email?.toLowerCase() === wantedAuthor) mine.add(subject)\n else theirs.add(subject)\n }\n return [...theirs].filter((subject) => !mine.has(subject))\n}\n\n/**\n * Commits made during a session, oldest first. Window and author both use the *committer* (who made\n * the commit then — including cherry-picks of someone else's work) and only commits reachable from\n * HEAD are seen. `--since` also stops at the first commit older than the window, so after a rebase\n * that leaves dates out of order a few in-window commits can be missed. Captured commit messages\n * cover what this misses.\n */\nexport function getCommitsInWindow(window: CommitWindow): GitCommit[] {\n return findCommitsInWindow(window) ?? []\n}\n\n/** Like `getCommitsInWindow`, but null when git couldn't answer (missing, not a repo, timed out), not just found none. */\nexport function findCommitsInWindow({\n repoRoot,\n since,\n until,\n author,\n filePath,\n timeoutMs,\n allBranches = false,\n}: CommitWindow): GitCommit[] | null {\n const shiftedBy = (iso: string, milliseconds: number): string =>\n new Date(new Date(iso).getTime() + milliseconds).toISOString()\n const output = runGit(\n repoRoot,\n [\n 'log',\n `--since=${shiftedBy(since, -WINDOW_LEAD_MS)}`,\n `--until=${shiftedBy(until, WINDOW_TRAIL_MS)}`,\n '--format=%h%x09%ce%x09%cI%x09%s',\n '--reverse',\n ...(allBranches ? ['--branches'] : []),\n ...(filePath ? ['--', filePath] : []),\n ],\n timeoutMs\n )\n if (output === null) return null\n if (output === '') return []\n\n const wantedAuthor = author?.toLowerCase() ?? null\n return output.split('\\n').flatMap((line) => {\n // The subject goes last, since only it can contain a tab\n const [hash, email, committedAt, ...subjectParts] = line.split('\\t')\n const subject = subjectParts.join('\\t')\n if (!hash || !committedAt || !subject) return []\n if (wantedAuthor && email?.toLowerCase() !== wantedAuthor) return []\n return [{ hash, subject, committedAt }]\n })\n}\n\nexport interface FileCommitsOptions {\n /** The file, with symlinks resolved — git reports the resolved repo root */\n filePath: string\n /** The session window */\n since: string\n until: string\n /** All of the git calls together get at most this long */\n budgetMs: number\n /** The clock, for tests */\n now?: () => number\n}\n\n/** The repo a file is in, and the user's commits in a session window that changed it. */\nexport interface FileCommits {\n repoRoot: string | null\n commits: GitCommit[]\n}\n\n/**\n * The commits in a session window that changed `filePath`, made by the repo's configured user. All three git calls\n * share one `budgetMs` deadline — `context --file` answers the VS Code extension, which gives up after 5 s — and\n * whatever is left when it runs out is skipped, giving no commits rather than a late answer. Pass a resolved path:\n * git reports the resolved repo root.\n */\nexport function getFileCommits(options: FileCommitsOptions): FileCommits {\n const now = options.now ?? Date.now\n const deadline = now() + options.budgetMs\n // Read once per git call and passed on as is, so a clock tick can't turn a checked 1 ms into a timeout of 0\n const timeLeft = (): number | null => {\n const milliseconds = deadline - now()\n return milliseconds >= 1 ? milliseconds : null\n }\n\n const rootTimeout = timeLeft()\n if (rootTimeout === null) return { repoRoot: null, commits: [] }\n const repoRoot = getRepoRoot(path.dirname(options.filePath), rootTimeout)\n if (!repoRoot) return { repoRoot: null, commits: [] }\n\n const emailTimeout = timeLeft()\n if (emailTimeout === null) return { repoRoot, commits: [] }\n const author = getUserEmail(repoRoot, emailTimeout)\n\n const logTimeout = timeLeft()\n if (logTimeout === null) return { repoRoot, commits: [] }\n const commits = getCommitsInWindow({\n repoRoot,\n since: options.since,\n until: options.until,\n author,\n filePath: options.filePath,\n timeoutMs: logTimeout,\n })\n return { repoRoot, commits }\n}\n","// Which commits a session's story tells, and the branch each belongs to. Pure: the git lookups happen elsewhere.\nimport { collectCapturedCommits, isFailedCommand, isSameCommit, mergeCommits } from './commits'\nimport { parseMetadata } from './event-metadata'\nimport { WINDOW_LEAD_MS, WINDOW_TRAIL_MS } from './git-commits'\nimport type { EventRow } from '../db/types'\nimport type { GitCommit } from './git-commits'\n\n/** A commit as the story shows it: under the branch it was made on. */\nexport interface StoryCommit {\n subject: string\n hash: string | null\n committedAt: string | null\n branch: string | null\n}\n\ntype TimedBranch = Pick<EventRow, 'branch' | 'captured_at'>\n\n/**\n * The branch the session was on at `time`: that of its latest event at or before it — or, for a time before every\n * event, the first event's. `git log` doesn't record which branch a commit was made on, so the timeline decides.\n */\nexport function branchAt(events: TimedBranch[], time: string): string | null {\n const moment = Date.parse(time)\n const onBranch = events.filter((event) => event.branch !== null)\n const atOrBefore = onBranch.filter((event) => Date.parse(event.captured_at) <= moment)\n return (atOrBefore.at(-1) ?? onBranch[0])?.branch ?? null\n}\n\n/**\n * The `git commit` capture that recorded this git commit: same subject, captured when it finished — at or after its\n * whole-second commit time, within the hook allowance. Its branch is the one the commit was made on, even when the\n * same command switched branch first (`git switch -c feat/x && git commit -m …`), which the timeline can't see.\n */\nfunction captureOf(events: EventRow[], commit: GitCommit): EventRow | undefined {\n const committed = Date.parse(commit.committedAt)\n return events\n .filter((event) => {\n if (event.category !== 'shipping' || isFailedCommand(event)) return false\n const message = parseMetadata(event.metadata).message\n if (typeof message !== 'string' || !isSameCommit(message, commit.subject)) return false\n const captured = Date.parse(event.captured_at)\n return captured >= committed && captured <= committed + WINDOW_LEAD_MS\n })\n .sort((first, second) => Date.parse(first.captured_at) - Date.parse(second.captured_at))[0]\n}\n\n/**\n * The session's commits — its git commits, then captured ones git didn't return — each with its branch:\n * - a git commit goes under the branch of its own `git commit` capture, or else by its time on the timeline. An AI\n * session's list can't place it (that's the branch the session ended on), nor can a repeated subject's first\n * capture (`wip` on two branches).\n * - a captured-only commit keeps the branch it was captured on.\n *\n * `gitCommits` is null when git wasn't asked. When it was, an AI session's commit that git doesn't confirm is left\n * out: the session lists every commit that reached HEAD, pulled ones too, while git's window holds only yours.\n */\nexport function storyCommitsOf(events: EventRow[], gitCommits: GitCommit[] | null): StoryCommit[] {\n const confirmedByGit = (subject: string): boolean =>\n gitCommits?.some((commit) => isSameCommit(subject, commit.subject)) ?? false\n const captured = collectCapturedCommits(events).filter(\n (commit) => gitCommits === null || commit.committedAt !== null || confirmedByGit(commit.subject)\n )\n const capturedBranches = new Map(captured.map((commit) => [commit.subject, commit.branch]))\n const gitBranchOf = (commit: GitCommit): string | null =>\n captureOf(events, commit)?.branch ?? branchAt(events, commit.committedAt)\n const gitCommitsByHash = new Map((gitCommits ?? []).map((commit) => [commit.hash, commit]))\n return mergeCommits(captured, gitCommits ?? []).map(({ hash, subject, committedAt, source }) => {\n const gitCommit = source === 'git' && hash !== null ? gitCommitsByHash.get(hash) : undefined\n return {\n hash,\n subject,\n committedAt,\n branch: gitCommit ? gitBranchOf(gitCommit) : (capturedBranches.get(subject) ?? null),\n }\n })\n}\n\n/** A session's time span, as `getCommitsInWindow` widens it. */\nexport interface SessionWindow {\n startedAt: string\n endedAt: string\n}\n\n/**\n * Hands each commit of a repo's day to the first session whose window (widened like `getCommitsInWindow`'s)\n * contains its time, so one lookup per repo serves all its sessions and no commit is told twice.\n */\nexport function assignCommitsToSessions(windows: SessionWindow[], commits: GitCommit[]): GitCommit[][] {\n const assigned: GitCommit[][] = windows.map(() => [])\n for (const commit of commits) {\n const moment = Date.parse(commit.committedAt)\n const index = windows.findIndex(\n (window) =>\n moment >= Date.parse(window.startedAt) - WINDOW_LEAD_MS &&\n moment <= Date.parse(window.endedAt) + WINDOW_TRAIL_MS\n )\n if (index !== -1) assigned[index].push(commit)\n }\n return assigned\n}\n","// Reads the git commands that move work between branches — creating a branch, stashing, rebasing, merging and\n// discarding changes — from a stored (already redacted) command, so a summary can say what happened in plain\n// language. Pure: no git calls. Reading the stored command covers past days too, with no change to capture.\nimport { commandSteps, simpleCommandOf } from '../capture/command-steps'\nimport { parseGitInvocation } from './git-invocation'\n\nexport type GitMove =\n | { kind: 'create-branch'; branch: string }\n // A plain switch tells no story of its own, but says which branch the next step of the command ran on.\n // `-` is git's \"the branch you were on before\" (`git checkout -`).\n | { kind: 'switch'; branch: string }\n | { kind: 'stash-push' }\n // `pop` restores the stash and removes it; `apply` restores it and keeps it in `git stash list`\n | { kind: 'stash-pop'; command: 'git stash pop' | 'git stash apply' }\n // Dropping a stash throws its changes away: `drop` the latest, `clear` all of them\n | { kind: 'stash-drop'; command: 'git stash drop' | 'git stash clear' }\n | { kind: 'rebase'; onto: string }\n | { kind: 'merge'; from: string }\n | { kind: 'discard'; command: DiscardCommand }\n\n/** How changes were thrown away, named the way you'd look it up again. */\nexport type DiscardCommand = 'git reset --hard' | 'git checkout -- …' | 'git restore' | 'git clean'\n\n// Ways out of an operation in progress — they move nothing themselves\nconst OPERATION_CONTROL_FLAGS = new Set(['--continue', '--abort', '--skip', '--quit'])\n\n// Flags whose value is the next word, so it isn't read as a branch: `git merge -m \"Merge feat\" feat/x`\nconst MERGE_VALUE_FLAGS = ['-m', '-F', '--file', '-s', '--strategy', '-X', '--strategy-option']\nconst REBASE_VALUE_FLAGS = ['-s', '--strategy', '-X', '--strategy-option', '-x', '--exec', '--onto']\nconst STASH_VALUE_FLAGS = ['-m', '--message', '--pathspec-from-file']\n\n// `git stash <word>`: anything else after `git stash` is a push with options or paths (`git stash -m wip`)\nconst STASH_SUBCOMMANDS = new Set([\n 'push',\n 'save',\n 'pop',\n 'apply',\n 'list',\n 'show',\n 'drop',\n 'clear',\n 'branch',\n 'create',\n 'store',\n])\n\n/** The value after a flag (`-b feat/x`) or attached to it (`--create=feat/x`). */\nfunction flagValue(words: string[], flags: string[]): string | null {\n for (const [index, word] of words.entries()) {\n if (flags.includes(word)) return words[index + 1] ?? null\n const attached = flags.find((flag) => flag.startsWith('--') && word.startsWith(`${flag}=`))\n if (attached) return word.slice(attached.length + 1)\n }\n return null\n}\n\n/** The words that aren't flags or a flag's value; everything after `--` is a path, not a positional. */\nfunction positionals(words: string[], valueFlags: string[] = []): string[] {\n const found: string[] = []\n for (let index = 0; index < words.length; index++) {\n const word = words[index]\n if (word === '--') break\n if (valueFlags.includes(word)) index++\n else if (!word.startsWith('-')) found.push(word)\n }\n return found\n}\n\nfunction isOperationControl(words: string[]): boolean {\n return words.some((word) => OPERATION_CONTROL_FLAGS.has(word))\n}\n\n/** `--track origin/x` (or `-t`, or `--track=direct origin/x`) creates the local branch `x` tracking it. */\nfunction trackedBranch(words: string[]): string | null {\n const isTracking = words.some((word) => word === '--track' || word === '-t' || word.startsWith('--track='))\n const [upstream] = positionals(words)\n if (!isTracking || !upstream) return null\n const slash = upstream.indexOf('/')\n return slash === -1 ? upstream : upstream.slice(slash + 1)\n}\n\n/** A branch created with one of `createFlags` (`-b`, `-c`, …) or by `--track`, or null. */\nfunction createdBranchMove(words: string[], createFlags: string[]): GitMove | null {\n const created = flagValue(words, createFlags) ?? trackedBranch(words)\n return created && !created.startsWith('-') ? { kind: 'create-branch', branch: created } : null\n}\n\n/** `git checkout main`, or `-` for the branch before (a lone `-` isn't a flag). */\nfunction switchTarget(words: string[]): string | undefined {\n return words.includes('-') ? '-' : positionals(words)[0]\n}\n\n/**\n * `git checkout -- file` and `git checkout .` throw away the file's changes. `git checkout main` switches — and so\n * does `git checkout src/a.ts` as far as the words go; the story tells the two apart by whether the branch changed.\n */\nfunction checkoutMove(words: string[]): GitMove | null {\n const created = createdBranchMove(words, ['-b', '-B'])\n if (created) return created\n if (words.includes('--') || positionals(words)[0] === '.') return { kind: 'discard', command: 'git checkout -- …' }\n const target = switchTarget(words)\n return target ? { kind: 'switch', branch: target } : null\n}\n\nfunction switchMove(words: string[]): GitMove | null {\n const created = createdBranchMove(words, ['-c', '-C', '--create', '--force-create'])\n if (created) return created\n const target = switchTarget(words)\n return target ? { kind: 'switch', branch: target } : null\n}\n\nfunction stashMove(words: string[]): GitMove | null {\n const [first] = positionals(words, STASH_VALUE_FLAGS)\n const action = first && STASH_SUBCOMMANDS.has(first) ? first : 'push'\n if (action === 'push' || action === 'save') return { kind: 'stash-push' }\n if (action === 'pop' || action === 'apply') return { kind: 'stash-pop', command: `git stash ${action}` }\n if (action === 'drop' || action === 'clear') return { kind: 'stash-drop', command: `git stash ${action}` }\n return null\n}\n\nfunction rebaseMove(words: string[]): GitMove | null {\n if (isOperationControl(words)) return null\n const onto = flagValue(words, ['--onto']) ?? positionals(words, REBASE_VALUE_FLAGS)[0]\n return onto ? { kind: 'rebase', onto } : null\n}\n\nfunction mergeMove(words: string[]): GitMove | null {\n if (isOperationControl(words)) return null\n const [from] = positionals(words, MERGE_VALUE_FLAGS)\n return from ? { kind: 'merge', from } : null\n}\n\nfunction resetMove(words: string[]): GitMove | null {\n return words.includes('--hard') ? { kind: 'discard', command: 'git reset --hard' } : null\n}\n\n/** `git restore --staged` only unstages; anything that touches the working tree throws changes away. */\nfunction restoreMove(words: string[]): GitMove | null {\n const isUnstageOnly =\n (words.includes('--staged') || words.includes('-S')) && !words.includes('--worktree') && !words.includes('-W')\n return isUnstageOnly ? null : { kind: 'discard', command: 'git restore' }\n}\n\n/** `git clean` deletes untracked files only when forced (`-f`, `-fd`, `--force`); a dry run deletes nothing. */\nfunction cleanMove(words: string[]): GitMove | null {\n const isForced = words.some((word) => word === '--force' || /^-[a-zA-Z]*f/.test(word))\n const isDryRun = words.some((word) => word === '--dry-run' || /^-[a-zA-Z]*n/.test(word))\n return isForced && !isDryRun ? { kind: 'discard', command: 'git clean' } : null\n}\n\nconst MOVE_READERS: Record<string, (words: string[]) => GitMove | null> = {\n checkout: checkoutMove,\n switch: switchMove,\n stash: stashMove,\n rebase: rebaseMove,\n merge: mergeMove,\n reset: resetMove,\n restore: restoreMove,\n clean: cleanMove,\n}\n\n/**\n * The branch-moving work in a command, in the order it ran: every step of a compound command is read\n * (`git stash && git switch -c feat/x && git stash pop`), with git's global options (`-C dir`) set aside.\n */\nexport function gitMovesOf(command: string): GitMove[] {\n return commandSteps(command).flatMap((step) => {\n const invocation = parseGitInvocation(simpleCommandOf(step))\n const readMove = invocation?.subcommand ? MOVE_READERS[invocation.subcommand] : undefined\n const move = readMove?.(invocation?.args.map((arg) => arg.word) ?? [])\n return move ? [move] : []\n })\n}\n","import type { EventRow } from '../db/types'\n\n// captured_at is set at command completion (precmd fires after the command exits).\n// A long-running command's captured_at is its end time, so the gap between it and\n// the prior event equals the command's full duration — falsely triggering the idle\n// threshold. We use its start time (completion − duration) for gap calculation instead.\nexport const LONG_RUNNING_THRESHOLD_MS = 5 * 60 * 1000 // 5 minutes\n\n/**\n * Split a sorted list of events into groups separated by idle gaps.\n */\nexport function splitByIdleGap(events: EventRow[], idleMs: number): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prev = new Date(events[i - 1].captured_at).getTime()\n const currCompleted = new Date(events[i].captured_at).getTime()\n const currDuration = events[i].duration_ms ?? 0\n const currEffective = currDuration >= LONG_RUNNING_THRESHOLD_MS ? currCompleted - currDuration : currCompleted\n\n if (currEffective - prev >= idleMs) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\nexport function minutesBetween(start: string, end: string): number {\n return (new Date(end).getTime() - new Date(start).getTime()) / 60000\n}\n","import type { EventRow } from '../db/types'\nimport { LONG_RUNNING_THRESHOLD_MS, splitByIdleGap } from './grouper'\n\n// A day's work as work blocks, project threads and activity slots (pure). A block is an idle-gap stretch of every\n// event; a thread is one project's events inside it, whatever their order. Switching repos never starts, splits or\n// names anything.\n\n/** Bump when the rules below change: every day is then rebuilt the next time it's read. */\nexport const GROUPING_VERSION = 1\n\nexport const MS_PER_MINUTE = 60 * 1000\nexport const SLOT_MINUTES = 5\nconst SLOT_MS = SLOT_MINUTES * MS_PER_MINUTE\n\n/** A local day as UTC milliseconds, end exclusive. */\nexport interface DayBounds {\n startMs: number\n endMs: number\n}\n\nexport interface ThreadOfBlock {\n /** Null for the block's \"elsewhere\" thread: the events with no project */\n projectId: number | null\n startedAt: string\n endedAt: string\n eventIds: number[]\n activeSlots: number\n}\n\nexport interface WorkBlock {\n startedAt: string\n endedAt: string\n eventCount: number\n activeSlots: number\n threads: ThreadOfBlock[]\n}\n\nexport interface SlotCount {\n slotStart: string\n projectId: number | null\n eventCount: number\n}\n\nexport interface DayBlocks {\n blocks: WorkBlock[]\n slots: SlotCount[]\n}\n\nexport function floorToSlot(ms: number): number {\n return Math.floor(ms / SLOT_MS) * SLOT_MS\n}\n\nexport function toActiveMinutes(activeSlots: number): number {\n return activeSlots * SLOT_MINUTES\n}\n\nfunction projectOf(event: EventRow): number | null {\n return event.project_id ?? null\n}\n\n/**\n * The slots an event occupies: the one its `captured_at` falls in, or for a long-running event every slot from its\n * start (captured time minus duration, as `splitByIdleGap` measures it) to its end. Clamped to the day.\n */\nexport function findSlotsOf(event: EventRow, bounds: DayBounds): number[] {\n const endMs = Date.parse(event.captured_at)\n const durationMs = event.duration_ms ?? 0\n const startMs = durationMs >= LONG_RUNNING_THRESHOLD_MS ? endMs - durationMs : endMs\n const firstSlot = floorToSlot(Math.max(startMs, bounds.startMs))\n const lastSlot = floorToSlot(Math.min(endMs, bounds.endMs - 1))\n const slots: number[] = []\n for (let slot = firstSlot; slot <= lastSlot; slot += SLOT_MS) slots.push(slot)\n return slots\n}\n\nfunction countDistinctSlots(events: EventRow[], bounds: DayBounds): number {\n return new Set(events.flatMap((event) => findSlotsOf(event, bounds))).size\n}\n\n/** One thread per project, in the order each project first appears, holding all of that project's events. */\nfunction buildThreads(events: EventRow[], bounds: DayBounds): ThreadOfBlock[] {\n const byProject = new Map<number | null, EventRow[]>()\n for (const event of events) {\n const projectEvents = byProject.get(projectOf(event))\n if (projectEvents) projectEvents.push(event)\n else byProject.set(projectOf(event), [event])\n }\n return [...byProject.entries()].map(([projectId, projectEvents]) => ({\n projectId,\n startedAt: projectEvents[0].captured_at,\n endedAt: projectEvents[projectEvents.length - 1].captured_at,\n eventIds: projectEvents.map((event) => event.id),\n activeSlots: countDistinctSlots(projectEvents, bounds),\n }))\n}\n\nfunction buildBlock(events: EventRow[], bounds: DayBounds): WorkBlock {\n return {\n startedAt: events[0].captured_at,\n endedAt: events[events.length - 1].captured_at,\n eventCount: events.length,\n // Distinct slots across every project: time worked, never the sum of the threads' times\n activeSlots: countDistinctSlots(events, bounds),\n threads: buildThreads(events, bounds),\n }\n}\n\n/** How many of each project's events fall in each slot of the day. */\nfunction countSlots(events: EventRow[], bounds: DayBounds): SlotCount[] {\n const counts = new Map<string, SlotCount>()\n for (const event of events) {\n const projectId = projectOf(event)\n for (const slot of findSlotsOf(event, bounds)) {\n const key = `${slot}|${projectId ?? ''}`\n const count = counts.get(key) ?? { slotStart: new Date(slot).toISOString(), projectId, eventCount: 0 }\n counts.set(key, { ...count, eventCount: count.eventCount + 1 })\n }\n }\n return [...counts.values()].sort((first, second) => first.slotStart.localeCompare(second.slotStart))\n}\n\n/** The day's blocks, threads and slots, from its events in `captured_at, id` order. */\nexport function buildDayBlocks(events: EventRow[], idleMs: number, bounds: DayBounds): DayBlocks {\n const blocks = splitByIdleGap(events, idleMs)\n .filter((group) => group.length > 0)\n .map((group) => buildBlock(group, bounds))\n return { blocks, slots: countSlots(events, bounds) }\n}\n","import { formatDuration } from '../utils/format'\nimport type { SessionPayload } from '../ai/types'\n\n/** Shown when a session has neither a repo nor any events to name it by. */\nexport const UNKNOWN_LOCATION = 'unknown'\n\ntype HeadingFields = Pick<SessionPayload, 'project' | 'branch' | 'durationMinutes'> &\n Partial<Pick<SessionPayload, 'branches' | 'activeMinutes'>>\n\n/** Every branch the session touched, in the order first seen — or its one branch when that's all that's known. */\nfunction headingBranches(session: HeadingFields): string[] {\n if (session.branches && session.branches.length > 0) return session.branches\n return session.branch ? [session.branch] : []\n}\n\n/**\n * `project (feat/auth, main) — duration`, as used in headings and the --verbose event listing. A thread shows its\n * active time: its span can include other projects' work.\n */\nexport function formatSessionLabel(session: HeadingFields): string {\n const branches = headingBranches(session)\n const branchList = branches.length > 0 ? ` (${branches.join(', ')})` : ''\n const minutes = session.activeMinutes ?? session.durationMinutes\n return `${session.project ?? UNKNOWN_LOCATION}${branchList} — ${formatDuration(minutes)}`\n}\n\n/** The `## project (branches) — duration` line every session summary starts with. */\nexport function formatSessionHeading(session: HeadingFields): string {\n return `## ${formatSessionLabel(session)}`\n}\n","import os from 'os'\nimport path from 'path'\nimport { formatDate } from '../utils/format'\nimport { parseMetadata } from './event-metadata'\nimport { storyCommitsOf } from './story-commits'\nimport { isFailedCommand } from './commits'\nimport { gitMovesOf } from '../normalize/git-move'\nimport type { EventPayload, SessionPayload } from '../ai/types'\nimport type { EventRow } from '../db/types'\nimport type { WorkscribeConfig } from '../config'\nimport type { GitCommit } from './git-commits'\nimport type { DayBlock, DayThread } from '../db/day-threads'\nimport { minutesBetween } from '../sessions/grouper'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { UNKNOWN_LOCATION } from './heading'\n\n/** What sessionLocation needs to name a session that isn't in a git repo. */\nexport interface LocationContext {\n projects: Record<string, string>\n homeDir: string\n}\n\nexport function locationContextFrom(config: Pick<WorkscribeConfig, 'projects'>): LocationContext {\n return { projects: config.projects ?? {}, homeDir: os.homedir() }\n}\n\nconst DEFAULT_LOCATION_CONTEXT: LocationContext = { projects: {}, homeDir: os.homedir() }\n\n// Only git commands can move work between branches; anything else isn't worth parsing\nconst MENTIONS_GIT = /(^|[\\s;&|(])git\\s/\n\n/**\n * The command's branch moves, read from the stored (redacted) command. When the command failed, there's no telling\n * which step did — except that a new branch the session is now on was created (`git switch -c x && npm test` with\n * failing tests), so only that move is kept.\n */\nfunction gitMovesField(row: EventRow): Pick<EventPayload, 'gitMoves'> {\n if (!MENTIONS_GIT.test(row.command)) return {}\n const moves = gitMovesOf(row.command)\n const gitMoves = isFailedCommand(row)\n ? moves.filter((move) => move.kind === 'create-branch' && move.branch === row.branch)\n : moves\n return gitMoves.length > 0 ? { gitMoves } : {}\n}\n\nexport function eventRowToPayload(row: EventRow): EventPayload {\n const metadata = parseMetadata(row.metadata)\n const payload: EventPayload = {\n category: row.category,\n metadata,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n branch: row.branch,\n capturedAt: row.captured_at,\n ...(row.source ? { source: row.source } : {}),\n ...gitMovesField(row),\n }\n if (row.category !== 'other' || typeof metadata.hint !== 'string') return payload\n\n // Unclassified commands carry their raw hint separately so the prompt can show it on its own\n const { hint, tool: _tool, ...rest } = metadata\n return { ...payload, hint, metadata: rest }\n}\n\nfunction branchesOnly(events: Pick<EventRow, 'branch'>[]): string[] {\n return events.map((event) => event.branch).filter((branch): branch is string => branch !== null)\n}\n\n/** The branches a session's events happened on: distinct, in the order first seen, events outside a repo skipped. */\nexport function branchesOf(events: Pick<EventRow, 'branch'>[]): string[] {\n return [...new Set(branchesOnly(events))]\n}\n\n/** The session's branch: the latest it was on — where the work was heading by the end. Null outside a repo. */\nexport function sessionBranch(events: Pick<EventRow, 'branch'>[]): string | null {\n return branchesOnly(events).at(-1) ?? null\n}\n\nfunction mostFrequentCwdOf(events: EventRow[]): string | null {\n const counts = new Map<string, number>()\n for (const event of events) counts.set(event.cwd, (counts.get(event.cwd) ?? 0) + 1)\n let best: string | null = null\n for (const [cwd, count] of counts) {\n if (best === null || count > (counts.get(best) ?? 0)) best = cwd\n }\n return best\n}\n\n/** What naming a stretch of work by its folders reads: its project's name, null outside any project. */\ninterface WorkEvents {\n repoName: string | null\n events: EventRow[]\n}\n\n/** The directory most of a stretch of work's events ran in, or null when it has no events. */\nexport function mostFrequentCwd(work: Pick<WorkEvents, 'events'>): string | null {\n return mostFrequentCwdOf(work.events)\n}\n\n/**\n * A directory inside a thread's repo, for git lookups: the most-used one among its events captured inside a repo,\n * else its most-used one. It doesn't match `repo_name` against a name — a project's name can differ from the text\n * its events were stored under. (Agents often run `cd repo && …` from a parent folder, so the most-used folder\n * overall can miss the repo.)\n */\nexport function threadRepoCwd(events: EventRow[]): string | null {\n return mostFrequentCwdOf(events.filter((event) => event.repo_name !== null)) ?? mostFrequentCwdOf(events)\n}\n\nfunction homeRelative(directory: string, homeDir: string): string {\n if (directory === homeDir) return '~'\n if (directory.startsWith(homeDir + path.sep)) return `~${directory.slice(homeDir.length)}`\n return directory\n}\n\n/**\n * The name a session is shown under: its repo, or — for work outside any git repo — the directory\n * most of its commands ran in (a config.projects name if one is set, else the home-relative path),\n * so the user can always tell where the work happened.\n */\nexport function sessionLocation(work: WorkEvents, context: LocationContext = DEFAULT_LOCATION_CONTEXT): string | null {\n if (work.repoName) return work.repoName\n const cwd = mostFrequentCwd(work)\n if (!cwd) return null\n return context.projects[cwd] ?? homeRelative(cwd, context.homeDir)\n}\n\nexport interface SessionPayloadOptions {\n /** How to name a session outside git */\n context?: LocationContext\n /** The session's commits from `git log`, or null when git wasn't asked — captured ones are always used */\n gitCommits?: GitCommit[] | null\n}\n\n/** A stretch of work as the template tells it: the thread `context` answers with, or a thread of the day. */\nexport interface PayloadSpan extends WorkEvents {\n startedAt: string\n endedAt: string\n durationMinutes: number\n}\n\nexport function spanToPayload(\n span: PayloadSpan,\n date: string,\n { context = DEFAULT_LOCATION_CONTEXT, gitCommits = null }: SessionPayloadOptions = {}\n): SessionPayload {\n return {\n date,\n project: sessionLocation(span, context),\n branch: sessionBranch(span.events),\n branches: branchesOf(span.events),\n startTime: formatDate(span.startedAt),\n endTime: formatDate(span.endedAt),\n durationMinutes: span.durationMinutes,\n events: span.events.map(eventRowToPayload),\n commits: storyCommitsOf(span.events, gitCommits),\n }\n}\n\n/** One thread of a block as the template tells it: its own span and active time, and the projects alongside it. */\nexport function threadToPayload(\n thread: DayThread,\n block: DayBlock,\n options: SessionPayloadOptions = {}\n): SessionPayload {\n const durationMinutes = minutesBetween(thread.startedAt, thread.endedAt)\n return {\n ...spanToPayload({ ...thread, durationMinutes }, block.date, options),\n activeMinutes: toActiveMinutes(thread.activeSlots),\n projectId: thread.projectId,\n alongside: block.threads\n .filter((other) => other.id !== thread.id)\n .map((other) => sessionLocation(other, options.context) ?? UNKNOWN_LOCATION),\n }\n}\n","/**\n * Versions of the `--json` documents read by coding agents and other tools, one per document. Fields may be added\n * within a version; renaming, removing or retyping a field — or changing what it means — bumps that document's\n * version. Tests pin the key sets.\n */\n/** 2: sessions are project threads inside work blocks (WOR-147). */\nexport const SESSIONS_SCHEMA_VERSION = 2\nexport const EVENTS_SCHEMA_VERSION = 1\n","import { eventRowToPayload } from '../payload'\nimport { buildExcludedSet } from '../exclusions'\nimport type { EventRow } from '../../db/types'\n\n/** Events the contract reports: the AI engines' exclusions apply, and notes go to `notes` instead. */\nexport function contractEvents(rows: EventRow[], includeCategories: string[]): EventRow[] {\n const excluded = buildExcludedSet(includeCategories)\n return rows.filter((row) => row.category !== 'note' && !excluded.has(row.category))\n}\n\nexport function collectNotes(rows: EventRow[]): string[] {\n return rows.flatMap((row) => {\n if (row.category !== 'note') return []\n const { text } = eventRowToPayload(row).metadata\n return typeof text === 'string' && text !== '' ? [text] : []\n })\n}\n","import { branchesOf, eventRowToPayload, mostFrequentCwd, sessionBranch, sessionLocation } from '../payload'\nimport { EVENTS_SCHEMA_VERSION, SESSIONS_SCHEMA_VERSION } from './constants'\nimport { collectNotes, contractEvents } from './utils'\nimport { collectCapturedCommits, mergeCommits } from '../commits'\nimport { minutesBetween } from '../../sessions/grouper'\nimport { toActiveMinutes } from '../../sessions/blocks'\nimport type { EventRow } from '../../db/types'\nimport type { DayBlock, DayThread } from '../../db/day-threads'\nimport type {\n BlockJson,\n EventJson,\n EventsJson,\n EventsJsonOptions,\n SessionJson,\n SessionJsonOptions,\n SessionsJson,\n} from './types'\n\n/** Never includes the full command, the event's cwd or an IDE file path. */\nexport function eventToJson(row: EventRow): EventJson {\n const { category, metadata, hint, exitCode, durationMs } = eventRowToPayload(row)\n return {\n capturedAt: row.captured_at,\n category,\n metadata,\n ...(hint === undefined ? {} : { hint }),\n exitCode,\n durationMs,\n source: row.source,\n }\n}\n\n/** One thread of a block as a session of the contract. */\nexport function threadToJson(\n thread: DayThread,\n block: DayBlock,\n { location, git, includeCategories }: SessionJsonOptions\n): SessionJson {\n return {\n key: thread.key,\n project: sessionLocation(thread, location),\n repoName: thread.repoName,\n repoPath: git.repoPath,\n cwd: mostFrequentCwd(thread),\n branch: sessionBranch(thread.events),\n branches: branchesOf(thread.events),\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n durationMinutes: minutesBetween(thread.startedAt, thread.endedAt),\n activeMinutes: toActiveMinutes(thread.activeSlots),\n eventCount: thread.eventCount,\n blockId: block.id,\n alongside: block.threads.filter((other) => other.id !== thread.id).map((other) => other.key),\n // Field by field, so a field added to `AgentSessionInfo` (WOR-136 adds more) never reaches the contract unasked\n agentSessions: thread.agentSessions.map(({ source, sessionId, startedAt, lastSeenAt }) => ({\n source,\n sessionId,\n startedAt,\n lastSeenAt,\n })),\n tickets: thread.tickets,\n notes: collectNotes(thread.events),\n commits: mergeCommits(collectCapturedCommits(thread.events), git.commits, git.othersSubjects),\n events: contractEvents(thread.events, includeCategories).map(eventToJson),\n }\n}\n\nexport function blockToJson(block: DayBlock): BlockJson {\n return {\n id: block.id,\n startedAt: block.startedAt,\n endedAt: block.endedAt,\n eventCount: block.eventCount,\n activeMinutes: toActiveMinutes(block.activeSlots),\n threadKeys: block.threads.map((thread) => thread.key),\n }\n}\n\nexport function buildSessionsJson(date: string, blocks: BlockJson[], sessions: SessionJson[]): SessionsJson {\n return { schemaVersion: SESSIONS_SCHEMA_VERSION, date, blocks, sessions }\n}\n\nexport function buildEventsJson(\n date: string,\n rows: EventRow[],\n { limit, includeCategories }: EventsJsonOptions\n): EventsJson {\n const events = contractEvents(rows, includeCategories)\n return {\n schemaVersion: EVENTS_SCHEMA_VERSION,\n date,\n total: events.length,\n events: events.slice(0, limit).map(eventToJson),\n }\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport { loadAIToolRegistry } from '../config/registry'\nimport { readConfig } from '../config'\nimport { buildEventsJson } from '../summary/contract/build'\n\nconst CATEGORY_COLORS: Record<string, (s: string) => string> = {\n shipping: chalk.green,\n versioning: chalk.blue,\n building: chalk.cyan,\n running: chalk.white,\n testing: chalk.magenta,\n linting: chalk.yellow,\n dependencies: chalk.yellow,\n infrastructure: chalk.red,\n releasing: chalk.bold.green,\n database: chalk.bold.blue,\n files: chalk.dim.yellow,\n exploring: chalk.dim.cyan,\n ai_session: chalk.bold.magenta,\n other: chalk.gray,\n}\n\nconst AI_TOOL_DISPLAY_NAMES: Record<string, string> = Object.fromEntries(\n loadAIToolRegistry().map((entry) => [entry.tool, entry.displayName])\n)\n\nfunction isPositiveWholeNumber(value: number): boolean {\n return Number.isInteger(value) && value > 0\n}\n\nexport function registerEvents(program: Command): void {\n program\n .command('events')\n .description('List captured events')\n .option('--date <YYYY-MM-DD>', 'date to list events for (default: today)')\n .option('--limit <n>', 'max events to show', parseInt)\n .option('--json', 'output the versioned JSON contract (structured events, no raw commands)')\n .action((options) => {\n let date = todayDate()\n if (options.date) {\n try {\n date = parseDate(options.date)\n } catch (error) {\n log.error((error as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n\n if (options.json) {\n // Scripts call the JSON contract, so a bad --limit is an error rather than a silently odd list\n if (options.limit !== undefined && !isPositiveWholeNumber(options.limit)) {\n log.error('--limit must be a positive whole number')\n process.exit(1)\n }\n const includeCategories = readConfig().summary?.includeCategories ?? []\n log.plain(JSON.stringify(buildEventsJson(date, events, { limit: options.limit, includeCategories })))\n return\n }\n\n const limit = options.limit ?? events.length\n\n if (events.length === 0) {\n log.plain(`No events captured for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n for (const event of events.slice(0, limit)) {\n const colorFn = CATEGORY_COLORS[event.category] ?? chalk.gray\n const time = formatDate(event.captured_at)\n const category = colorFn(event.category.padEnd(20))\n const repo = event.repo_name ? chalk.dim(` [${event.repo_name}]`) : ''\n const exitBadge = event.exit_code && event.exit_code !== 0 ? chalk.red(` ✗${event.exit_code}`) : ''\n\n let cmdDisplay: string\n if (event.category === 'ai_session') {\n let meta: Record<string, unknown> = {}\n try {\n meta = event.metadata ? (JSON.parse(event.metadata) as Record<string, unknown>) : {}\n } catch {\n /* ignore */\n }\n const toolKey = typeof meta.tool === 'string' ? meta.tool : ''\n const toolName = AI_TOOL_DISPLAY_NAMES[toolKey] ?? toolKey\n cmdDisplay = chalk.white(toolName.padEnd(20)) + chalk.magenta(' ✦')\n } else {\n cmdDisplay = chalk.dim(event.command.slice(0, 55))\n }\n\n log.plain(` ${chalk.dim(time)} ${category} ${cmdDisplay}${repo}${exitBadge}`)\n }\n\n if (limit < events.length) {\n log.plain('')\n log.plain(chalk.dim(` … and ${events.length - limit} more (remove --limit to see all)`))\n }\n\n log.plain('')\n })\n}\n","// Rules and messages for `workscribe sessions summary set` — pure; the command does the I/O.\n\nconst BYTES_PER_KB = 1024\nconst MAX_SKILL_SUMMARY_KB = 8\n\n/** Largest summary the agent skill may save for one session, in bytes. */\nexport const MAX_SKILL_SUMMARY_BYTES = MAX_SKILL_SUMMARY_KB * BYTES_PER_KB\n\n// Shown whenever no summary text arrives, so the fix is a copy-paste away\nconst SEND_TEXT_EXAMPLE = [\n 'Send the summary text into the command, for example:',\n '',\n \" workscribe sessions summary set --key '<key from workscribe sessions --json>' <<'EOF'\",\n ' ## my-project (main) — 1h 30m',\n ' - What you got done',\n ' EOF',\n].join('\\n')\n\nexport const INTERACTIVE_STDIN_MESSAGE = `This command saves summary text that is sent to it — it can't be typed in here.\\n${SEND_TEXT_EXAMPLE}`\n\nexport function stdinReadFailedMessage(error: unknown): string {\n return `Couldn't read the summary text that was sent in: ${(error as Error).message}`\n}\n\n/** `16384` → `16,384` — thousands separators only, no unit scaling. */\nfunction formatCount(count: number): string {\n return count.toLocaleString('en-US')\n}\n\n/** A friendly error for a summary that can't be saved, or null when it's fine. */\nexport function validateSkillSummary(text: string): string | null {\n if (text.trim() === '') return `Nothing to save — no summary text was received.\\n${SEND_TEXT_EXAMPLE}`\n const bytes = Buffer.byteLength(text, 'utf-8')\n if (bytes > MAX_SKILL_SUMMARY_BYTES) {\n return (\n `This summary is too long to save (${formatCount(bytes)} bytes; the limit is ` +\n `${formatCount(MAX_SKILL_SUMMARY_BYTES)}, about ${MAX_SKILL_SUMMARY_KB} KB). Shorten it and try again.`\n )\n }\n return null\n}\n","import fs from 'fs'\n\nconst STDIN_FILE_DESCRIPTOR = 0\n\n/** True when stdin is a terminal — reading it would wait for typing rather than piped input. */\nexport function isStdinInteractive(): boolean {\n return Boolean(process.stdin.isTTY)\n}\n\n/** Everything piped into the process. Throws if stdin can't be read. */\nexport function readAllStdin(): string {\n return fs.readFileSync(STDIN_FILE_DESCRIPTOR, 'utf-8')\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { EventRow } from './types'\nimport type { DayBlocks } from '../sessions/blocks'\nimport { withReadTransaction } from './transaction'\n\n// The stored work blocks, threads and slots (migration 009), and the stamp that says what a day was built from.\n// `node:sqlite`-free: it takes the connection, so tests run it on an in-memory database.\n\n/** What a day's blocks were built from. Any difference from the day as it is now means they're out of date. */\nexport interface DayStamp {\n groupingVersion: number\n idleMs: number\n eventCount: number\n /** `SUM(project_id × id)`: weighted by the event, so two events swapping projects still changes it */\n linkChecksum: number\n /**\n * `SUM(project_id² × id)`, each term reduced by a prime modulus so it can't overflow. Squaring isn't linear, so changes that cancel out in\n * `linkChecksum` (one event losing project 1 while another gains project 2) still change this one\n */\n linkSpread: number\n /** Null for a day with no events */\n maxEventId: number | null\n}\n\n/** What a day is grouped with: the rules' version and the idle timeout from the config. */\nexport type GroupingBasis = Pick<DayStamp, 'groupingVersion' | 'idleMs'>\n\n/** A day's start and end as stored `captured_at` strings, end exclusive (`localDayBounds`). */\nexport interface DayRange {\n start: string\n end: string\n}\n\nexport interface StoredThread {\n id: number\n blockId: number\n projectId: number | null\n startedAt: string\n endedAt: string\n eventCount: number\n activeSlots: number\n}\n\nexport interface StoredBlock {\n id: number\n date: string\n startedAt: string\n endedAt: string\n eventCount: number\n activeSlots: number\n groupingVersion: number\n threads: StoredThread[]\n}\n\nconst THREADS_OF_BLOCK_SQL = `SELECT id, block_id AS blockId, project_id AS projectId, started_at AS startedAt,\n ended_at AS endedAt, event_count AS eventCount, active_slots AS activeSlots\n FROM thread WHERE block_id = ? ORDER BY id`\n\nconst IN_DAY = 'captured_at >= $start AND captured_at < $end'\n// A prime near 10⁹: each factor is reduced by it before multiplying, so no term or sum overflows 64-bit integers\nconst SPREAD_MODULUS = 1_000_000_007\n\n/** The day's stamp as its events are now — one indexed range query. */\nexport function currentDayStamp(db: MigrationDatabase, range: DayRange, basis: GroupingBasis): DayStamp {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS eventCount, COALESCE(SUM(COALESCE(project_id, 0) * id), 0) AS linkChecksum,\n COALESCE(SUM(COALESCE(project_id, 0) * COALESCE(project_id, 0) % ${SPREAD_MODULUS}\n * (id % ${SPREAD_MODULUS}) % ${SPREAD_MODULUS}), 0) AS linkSpread,\n MAX(id) AS maxEventId\n FROM events WHERE ${IN_DAY}`\n )\n .get(range) as Omit<DayStamp, keyof GroupingBasis>\n return { ...basis, ...row }\n}\n\nexport function readDayStamp(db: MigrationDatabase, date: string): DayStamp | null {\n const row = db\n .prepare(\n `SELECT grouping_version AS groupingVersion, idle_ms AS idleMs, event_count AS eventCount,\n link_checksum AS linkChecksum, link_spread AS linkSpread, max_event_id AS maxEventId\n FROM grouping_day WHERE date = ?`\n )\n .get(date) as DayStamp | undefined\n return row ?? null\n}\n\n/** Compared field by field in JS, so an empty day's null `maxEventId` matches itself. */\nexport function isSameStamp(stored: DayStamp | null, current: DayStamp): boolean {\n return (\n stored !== null &&\n stored.groupingVersion === current.groupingVersion &&\n stored.idleMs === current.idleMs &&\n stored.eventCount === current.eventCount &&\n stored.linkChecksum === current.linkChecksum &&\n stored.linkSpread === current.linkSpread &&\n stored.maxEventId === current.maxEventId\n )\n}\n\n/** The day's events in grouping order — the same range the stamp counts, so the two always agree. */\nexport function getEventsInRange(db: MigrationDatabase, range: DayRange): EventRow[] {\n return db.prepare(`SELECT * FROM events WHERE ${IN_DAY} ORDER BY captured_at ASC, id ASC`).all(range) as EventRow[]\n}\n\n/** What `replaceDayBlocks` stores for a day: its blocks and slots, and the stamp they were built from. */\nexport interface DayBuild {\n date: string\n dayBlocks: DayBlocks\n stamp: DayStamp\n}\n\nfunction insertBlocks(db: MigrationDatabase, { date, dayBlocks, stamp }: DayBuild): void {\n const insertBlock = db.prepare(\n `INSERT INTO work_block (date, started_at, ended_at, event_count, active_slots, grouping_version)\n VALUES (?, ?, ?, ?, ?, ?) RETURNING id`\n )\n const insertThread = db.prepare(\n `INSERT INTO thread (block_id, project_id, started_at, ended_at, event_count, active_slots)\n VALUES (?, ?, ?, ?, ?, ?) RETURNING id`\n )\n const insertThreadEvent = db.prepare('INSERT INTO thread_event (thread_id, event_id) VALUES (?, ?)')\n for (const block of dayBlocks.blocks) {\n const { id: blockId } = insertBlock.get(\n date,\n block.startedAt,\n block.endedAt,\n block.eventCount,\n block.activeSlots,\n stamp.groupingVersion\n ) as { id: number }\n for (const thread of block.threads) {\n const { id: threadId } = insertThread.get(\n blockId,\n thread.projectId,\n thread.startedAt,\n thread.endedAt,\n thread.eventIds.length,\n thread.activeSlots\n ) as { id: number }\n for (const eventId of thread.eventIds) insertThreadEvent.run(threadId, eventId)\n }\n }\n}\n\n/** Replaces everything stored for the day — blocks (with their threads and links), slots and stamp. Call it in a transaction. */\nexport function replaceDayBlocks(db: MigrationDatabase, build: DayBuild): void {\n const { date, dayBlocks, stamp } = build\n db.prepare('DELETE FROM work_block WHERE date = ?').run(date)\n db.prepare('DELETE FROM activity_slot WHERE date = ?').run(date)\n insertBlocks(db, build)\n const insertSlot = db.prepare(\n 'INSERT INTO activity_slot (date, slot_start, project_id, event_count) VALUES (?, ?, ?, ?)'\n )\n for (const slot of dayBlocks.slots) insertSlot.run(date, slot.slotStart, slot.projectId, slot.eventCount)\n db.prepare(\n `INSERT OR REPLACE INTO grouping_day\n (date, grouping_version, idle_ms, event_count, link_checksum, link_spread, max_event_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)`\n ).run(\n date,\n stamp.groupingVersion,\n stamp.idleMs,\n stamp.eventCount,\n stamp.linkChecksum,\n stamp.linkSpread,\n stamp.maxEventId\n )\n}\n\n/** The day's blocks in time order, each with its threads in the order their projects first appear. */\nexport function getBlocksForDate(db: MigrationDatabase, date: string): StoredBlock[] {\n // One snapshot: a rebuild by another process between the two reads would otherwise leave blocks without threads\n return withReadTransaction(db, () => readBlocksForDate(db, date))\n}\n\n/** `getBlocksForDate` without a transaction of its own, for a caller already inside one. */\nexport function readBlocksForDate(db: MigrationDatabase, date: string): StoredBlock[] {\n const blocks = db\n .prepare(\n `SELECT id, date, started_at AS startedAt, ended_at AS endedAt, event_count AS eventCount,\n active_slots AS activeSlots, grouping_version AS groupingVersion\n FROM work_block WHERE date = ? ORDER BY started_at, id`\n )\n .all(date) as Omit<StoredBlock, 'threads'>[]\n const selectThreads = db.prepare(THREADS_OF_BLOCK_SQL)\n return blocks.map((block) => ({ ...block, threads: selectThreads.all(block.id) as StoredThread[] }))\n}\n\nexport function getThreadEvents(db: MigrationDatabase, threadId: number): EventRow[] {\n return db\n .prepare(\n `SELECT events.* FROM events JOIN thread_event ON thread_event.event_id = events.id\n WHERE thread_event.thread_id = ? ORDER BY events.captured_at, events.id`\n )\n .all(threadId) as EventRow[]\n}\n\n/** A built block's threads, and every event of the block in grouping order with its thread. */\nexport interface BlockWithEvents {\n threads: StoredThread[]\n events: { id: number; threadId: number }[]\n}\n\n/**\n * The block holding an event — what `findSummaryThread` needs — read without a transaction of its own, for a caller\n * already inside one. Null when no built block holds the event (its day isn't built yet, or it was deleted).\n */\nexport function readBlockHoldingEvent(db: MigrationDatabase, eventId: number): BlockWithEvents | null {\n const holder = db\n .prepare(\n `SELECT thread.block_id AS blockId FROM thread_event JOIN thread ON thread.id = thread_event.thread_id\n WHERE thread_event.event_id = ?`\n )\n .get(eventId) as { blockId: number } | undefined\n if (holder === undefined) return null\n const threads = db.prepare(THREADS_OF_BLOCK_SQL).all(holder.blockId) as StoredThread[]\n const events = db\n .prepare(\n `SELECT events.id, thread_event.thread_id AS threadId FROM thread_event\n JOIN thread ON thread.id = thread_event.thread_id JOIN events ON events.id = thread_event.event_id\n WHERE thread.block_id = ? ORDER BY events.captured_at, events.id`\n )\n .all(holder.blockId) as BlockWithEvents['events']\n return { threads, events }\n}\n\n/** `readBlockHoldingEvent` in one snapshot of its own, so a rebuild by another process can't split the read. */\nexport function getBlockHoldingEvent(db: MigrationDatabase, eventId: number): BlockWithEvents | null {\n return withReadTransaction(db, () => readBlockHoldingEvent(db, eventId))\n}\n","import path from 'path'\nimport type { MigrationDatabase } from './migration-types'\nimport type { SummaryRow } from './summary-store'\nimport { relativeInside, resolvedPath } from '../utils/working-directory'\n\n// What `context` looks up on the thread path: the event to answer from, the block it's in, and what's stored about\n// that block. `node:sqlite`-free, so tests run it on an in-memory database.\n\nexport interface EventPoint {\n id: number\n capturedAt: string\n}\n\nexport interface WorkBlockRow {\n id: number\n date: string\n startedAt: string\n endedAt: string\n activeSlots: number\n}\n\nfunction latestOf(points: (EventPoint | null | undefined)[]): EventPoint | null {\n const found = points.filter((point): point is EventPoint => point !== null && point !== undefined)\n found.sort((first, second) => second.capturedAt.localeCompare(first.capturedAt) || second.id - first.id)\n return found[0] ?? null\n}\n\n/** The latest save of the file as its path was stored. */\nfunction latestSaveByPath(db: MigrationDatabase, filePath: string): EventPoint | undefined {\n return db\n .prepare(\n `SELECT id, captured_at AS capturedAt FROM events\n WHERE file_path = ? AND category = 'code_edit' ORDER BY captured_at DESC, id DESC LIMIT 1`\n )\n .get(filePath) as EventPoint | undefined\n}\n\n// How many of a project file's saves to look through for one made in this very file\nconst PROJECT_FILE_SAVES_SCANNED = 200\n\n/** Every project file the path could be: the file's path inside each stored checkout root that holds it. */\nfunction projectFilesAt(db: MigrationDatabase, filePath: string): { projectId: number; relativePath: string }[] {\n const roots = db.prepare('SELECT project_id AS projectId, path FROM project_path').all() as {\n projectId: number\n path: string\n }[]\n return roots.flatMap((root) => {\n const relativePath = relativeInside(root.path, filePath)\n return relativePath ? [{ projectId: root.projectId, relativePath }] : []\n })\n}\n\n/**\n * The latest save linked to the file through its project's file — so a save stored under another path to the same\n * file (a symlink) still counts. A project spans every clone and worktree of its repo, so a save only counts when its\n * stored path is the same file on disk: `README.md` saved in another worktree is another file.\n */\nfunction latestSaveByProjectFile(db: MigrationDatabase, filePath: string): EventPoint | null {\n const savesOfFile = db.prepare(\n `SELECT events.id, events.captured_at AS capturedAt, events.file_path AS filePath FROM file\n JOIN event_file ON event_file.file_id = file.id JOIN events ON events.id = event_file.event_id\n WHERE file.project_id = ? AND file.relative_path = ?\n ORDER BY events.captured_at DESC, events.id DESC LIMIT ${PROJECT_FILE_SAVES_SCANNED}`\n )\n const fileOnDisk = resolvedPath(filePath)\n const isThisFile = (storedPath: string | null) => storedPath !== null && resolvedPath(storedPath) === fileOnDisk\n return latestOf(\n projectFilesAt(db, filePath).map((projectFile) => {\n const rows = savesOfFile.all(projectFile.projectId, projectFile.relativePath) as (EventPoint & {\n filePath: string | null\n })[]\n const save = rows.find((row) => isThisFile(row.filePath))\n return save && { id: save.id, capturedAt: save.capturedAt }\n })\n )\n}\n\n/** The file's latest save: by the path it was stored under, or through its project's file. Null when never saved. */\nexport function findLatestFileSave(db: MigrationDatabase, filePath: string): EventPoint | null {\n return latestOf([latestSaveByPath(db, filePath), latestSaveByProjectFile(db, filePath)])\n}\n\n/** The latest event of the project with this identity (`idx_events_project_time`). Null when it has none. */\nexport function findLatestProjectEvent(db: MigrationDatabase, identity: string): EventPoint | null {\n const row = db\n .prepare(\n `SELECT events.id, events.captured_at AS capturedAt FROM events\n WHERE events.project_id = (SELECT id FROM project WHERE identity = ?)\n ORDER BY events.captured_at DESC, events.id DESC LIMIT 1`\n )\n .get(identity) as EventPoint | undefined\n return row ?? null\n}\n\nexport function getWorkBlock(db: MigrationDatabase, blockId: number): WorkBlockRow | null {\n const row = db\n .prepare(\n `SELECT id, date, started_at AS startedAt, ended_at AS endedAt, active_slots AS activeSlots\n FROM work_block WHERE id = ?`\n )\n .get(blockId) as WorkBlockRow | undefined\n return row ?? null\n}\n\n/** The anchored summaries whose range starts in this block — the ones that can belong to one of its threads. */\nexport function getAnchoredSummariesInBlock(db: MigrationDatabase, eventIds: number[]): SummaryRow[] {\n return db\n .prepare('SELECT * FROM summary WHERE first_event_id IN (SELECT value FROM json_each(?)) ORDER BY id')\n .all(JSON.stringify(eventIds)) as SummaryRow[]\n}\n\n/**\n * Each project's name, by id: its latest name, else the newest name its events were stored under, else an alias,\n * else its checkout's folder — a project the upgrade only reached through a file save has no name of its own.\n */\nexport function getProjectNames(db: MigrationDatabase, projectIds: number[]): Map<number, string | null> {\n const rows = db\n .prepare(\n `SELECT project.id,\n COALESCE(\n project.name,\n (SELECT repo_name FROM events WHERE events.project_id = project.id AND repo_name IS NOT NULL\n ORDER BY captured_at DESC LIMIT 1),\n (SELECT name FROM project_alias WHERE project_alias.project_id = project.id ORDER BY name LIMIT 1)\n ) AS name,\n (SELECT path FROM project_path WHERE project_path.project_id = project.id ORDER BY path LIMIT 1) AS root\n FROM project WHERE project.id IN (SELECT value FROM json_each(?))`\n )\n .all(JSON.stringify(projectIds)) as { id: number; name: string | null; root: string | null }[]\n return new Map(rows.map((row) => [row.id, row.name ?? (row.root === null ? null : path.basename(row.root))]))\n}\n","import type { MigrationDatabase } from './migration-types'\nimport type { EventRow } from './types'\nimport { withReadTransaction } from './transaction'\nimport { getThreadEvents, readBlocksForDate, type StoredBlock, type StoredThread } from './thread-store'\nimport { getAnchoredSummariesInBlock, getProjectNames } from './context-lookup'\nimport { getAgentSessionsOfEvents, type AgentSessionInfo } from './agent-sessions'\nimport { ticketsOfEvents } from './tickets'\nimport { sessionKey } from '../summary/store'\nimport { areJobsDone } from './jobs/state'\nimport type { DataJob } from './jobs/types'\nimport type { SummaryRow } from './summary-store'\n\n// A day's built work blocks with what `sessions` says about each thread, read in one snapshot so another process\n// rebuilding the day can't split the read. `node:sqlite`-free, so tests run it on an in-memory database.\n\n/**\n * The jobs readers of threads wait for: until old events are linked to projects, threads would put them all\n * \"elsewhere\"; until old summaries are anchored, a thread would miss its summary.\n */\nexport const THREAD_READ_JOBS: ReadonlySet<string> = new Set(['project-dimensions', 'summary-anchors'])\n\n/**\n * Whether threads can be read: once `THREAD_READ_JOBS` are done, or straight away for a database with no events\n * yet — a fresh install has nothing to upgrade, and its jobs only run before the data commands.\n */\nexport function isReadyForThreads(db: MigrationDatabase, jobs: DataJob[]): boolean {\n const hasEvents = db.prepare('SELECT 1 FROM events LIMIT 1').get() !== undefined\n return !hasEvents || areJobsDone(db, jobs, THREAD_READ_JOBS)\n}\n\nexport interface DayThread extends StoredThread {\n /** `project|startedAt` — the same key `context` gives this thread */\n key: string\n /** The project's name; null for the block's \"elsewhere\" thread */\n repoName: string | null\n /** In grouping order */\n events: EventRow[]\n agentSessions: AgentSessionInfo[]\n /** The ticket keys its events' branches and commits name, sorted (WOR-9 before WOR-10) */\n tickets: string[]\n}\n\nexport interface DayBlock extends Omit<StoredBlock, 'threads'> {\n /** In the order their projects first appear in the block */\n threads: DayThread[]\n /** The anchored summaries whose range starts in this block — the ones that can belong to one of its threads */\n summaries: SummaryRow[]\n}\n\nfunction projectIdsOf(blocks: StoredBlock[]): number[] {\n const projectIds = blocks.flatMap((block) => block.threads.map((thread) => thread.projectId))\n return [...new Set(projectIds)].filter((projectId): projectId is number => projectId !== null)\n}\n\nfunction toDayThread(db: MigrationDatabase, thread: StoredThread, projectNames: Map<number, string | null>): DayThread {\n const repoName = thread.projectId === null ? null : (projectNames.get(thread.projectId) ?? null)\n const events = getThreadEvents(db, thread.id)\n const eventIds = events.map((event) => event.id)\n return {\n ...thread,\n key: sessionKey(repoName, thread.startedAt),\n repoName,\n events,\n agentSessions: getAgentSessionsOfEvents(db, eventIds),\n tickets: ticketsOfEvents(db, eventIds),\n }\n}\n\n/** The day's blocks as last built (`ensureDayThreads` builds them), in time order. */\nexport function readDayThreads(db: MigrationDatabase, date: string): DayBlock[] {\n return withReadTransaction(db, () => {\n const blocks = readBlocksForDate(db, date)\n const projectNames = getProjectNames(db, projectIdsOf(blocks))\n return blocks.map((block) => {\n const threads = block.threads.map((thread) => toDayThread(db, thread, projectNames))\n const eventIds = threads.flatMap((thread) => thread.events.map((event) => event.id))\n return { ...block, threads, summaries: getAnchoredSummariesInBlock(db, eventIds) }\n })\n })\n}\n","import { getDb } from '../db'\nimport { withImmediateTransaction, type TransactionOptions } from '../db/transaction'\nimport {\n currentDayStamp,\n getBlocksForDate,\n getEventsInRange,\n isSameStamp,\n readDayStamp,\n replaceDayBlocks,\n type DayRange,\n type GroupingBasis,\n type StoredBlock,\n} from '../db/thread-store'\nimport { readConfig } from '../config'\nimport { localDayBounds } from '../utils/format'\nimport { buildDayBlocks, GROUPING_VERSION, MS_PER_MINUTE } from './blocks'\n\n// Builds a day's work blocks, threads and slots, under the write lock and rechecked first, so two processes never\n// build the same day twice. Every reader of a day's work — `sessions`, `summary`, `export`, the week and `context` —\n// reads them.\n\nexport interface ThreadGroupingOptions {\n /** Wait at most this long for another process's write lock (default: the connection's 5 s). */\n lockWaitMs?: number\n}\n\nfunction groupingBasis(): GroupingBasis {\n return { groupingVersion: GROUPING_VERSION, idleMs: readConfig().session.idleTimeout * MS_PER_MINUTE }\n}\n\nfunction isDayUpToDate(date: string, range: DayRange, basis: GroupingBasis): boolean {\n const db = getDb()\n return isSameStamp(readDayStamp(db, date), currentDayStamp(db, range, basis))\n}\n\n/** Reads the day's events and replaces its blocks, threads, slots and stamp. Runs inside the write lock. */\nfunction rebuildDay(date: string, range: DayRange, basis: GroupingBasis): void {\n const db = getDb()\n const stamp = currentDayStamp(db, range, basis)\n const bounds = { startMs: Date.parse(range.start), endMs: Date.parse(range.end) }\n const dayBlocks = buildDayBlocks(getEventsInRange(db, range), basis.idleMs, bounds)\n replaceDayBlocks(db, { date, dayBlocks, stamp })\n}\n\n/**\n * Builds the day's blocks and threads unless they're up to date with its events, their project links, the idle\n * timeout and `GROUPING_VERSION`. The rebuild holds the write lock and rechecks first, so two processes never build\n * the same day twice. Throws a locked error (`isDatabaseLocked`) if another process keeps the lock past `lockWaitMs`.\n */\nexport function ensureDayThreads(date: string, options: ThreadGroupingOptions = {}): void {\n const range = localDayBounds(date)\n const basis = groupingBasis()\n if (isDayUpToDate(date, range, basis)) return\n const transactionOptions: TransactionOptions = { busyTimeoutMs: options.lockWaitMs }\n withImmediateTransaction(\n getDb(),\n () => {\n // Another process may have built the day while this one waited for the lock\n if (isDayUpToDate(date, range, basis)) return\n rebuildDay(date, range, basis)\n },\n transactionOptions\n )\n}\n\nexport function getOrBuildBlocks(date: string, options: ThreadGroupingOptions = {}): StoredBlock[] {\n ensureDayThreads(date, options)\n return getBlocksForDate(getDb(), date)\n}\n","import { log } from '../utils/logger'\n\n/** Prints the error and exits 1 — for a command that can't go on. */\nexport function fail(message: string): never {\n log.error(message)\n process.exit(1)\n}\n","// The day's threads as `sessions` and `sessions summary set` read them. They never run data jobs (the agent skill\n// calls them), and there's no older path to fall back to: until the thread jobs have run, threads would put old\n// events \"elsewhere\" and `context` couldn't show a summary saved for one, so the commands ask for\n// `workscribe upgrade` instead. The gate is the one `context` uses.\nimport { getDb } from '../db'\nimport { JOBS } from '../db/jobs'\nimport { UPGRADE_NEEDED_MESSAGE } from '../db/jobs/state'\nimport { isReadyForThreads, readDayThreads, type DayBlock } from '../db/day-threads'\nimport { ensureDayThreads } from '../sessions/threads'\nimport { fail } from './fail'\n\n/** Exits with the upgrade message unless threads can be read yet. */\nexport function requireThreadData(): void {\n if (!isReadyForThreads(getDb(), JOBS)) fail(UPGRADE_NEEDED_MESSAGE)\n}\n\n/** The day's blocks and threads, built first if the day changed since they were. */\nexport function loadDayThreads(date: string): DayBlock[] {\n ensureDayThreads(date)\n return readDayThreads(getDb(), date)\n}\n","import { Command } from 'commander'\nimport { saveThreadSummary } from '../db'\nimport { readConfig } from '../config'\nimport { redact } from '../capture/redact'\nimport { log } from '../utils/logger'\nimport { localDateOf } from '../utils/format'\nimport { SKILL_ENGINE, parseSessionKey } from '../summary/store'\nimport { anchorOfThread } from '../summary/anchors'\nimport { INTERACTIVE_STDIN_MESSAGE, stdinReadFailedMessage, validateSkillSummary } from '../summary/write-back'\nimport { isStdinInteractive, readAllStdin } from '../utils/stdin'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { fail } from './fail'\nimport type { DayThread } from '../db/day-threads'\n\n// `workscribe sessions summary set` — how the Workscribe agent skill saves the summary it wrote.\n\n/** The thread a key from `sessions --json` names, after rebuilding its day so the lookup is current. */\nfunction findThreadByKey(key: string): DayThread | null {\n const parsed = parseSessionKey(key)\n if (!parsed) return null\n const threads = loadDayThreads(localDateOf(parsed.startedAt)).flatMap((block) => block.threads)\n return threads.find((thread) => thread.key === key) ?? null\n}\n\n/** The summary text piped in, failing with a friendly message for a terminal or a read error. */\nfunction readSummaryText(): string {\n if (isStdinInteractive()) fail(INTERACTIVE_STDIN_MESSAGE)\n try {\n return readAllStdin()\n } catch (error) {\n fail(stdinReadFailedMessage(error))\n }\n}\n\nfunction saveSkillSummary(options: { key: string }): void {\n if (!parseSessionKey(options.key)) {\n fail(`That session key isn't valid: '${options.key}'. Copy a session's \"key\" from workscribe sessions --json.`)\n }\n requireThreadData()\n\n const summaryText = readSummaryText()\n const problem = validateSkillSummary(summaryText)\n if (problem) fail(problem)\n\n const thread = findThreadByKey(options.key)\n if (!thread) {\n fail(\n 'No session matches this key — your activity may have changed since the sessions were listed. ' +\n 'Run workscribe sessions --json again and use the new key.'\n )\n }\n\n const text = redact(summaryText.trim(), readConfig().redact.extra)\n saveThreadSummary(options.key, {\n engine: SKILL_ENGINE,\n text,\n anchor: anchorOfThread(thread.events, thread.projectId),\n })\n log.success(\n `Saved the summary for ${thread.repoName ?? 'this session'} — workscribe summary, workscribe context and the VS Code status bar now show it.`\n )\n}\n\nexport function registerSummarySet(sessionsCommand: Command): void {\n sessionsCommand\n .command('summary')\n .description('Manage the summaries stored for sessions')\n .command('set')\n .description('Save a summary written by the Workscribe agent skill; the text is read from stdin')\n .requiredOption('--key <key>', 'the session key, from workscribe sessions --json')\n .action(saveSkillSummary)\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, formatDuration } from '../utils/format'\nimport { registerSummarySet } from './session-summary'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { fail } from './fail'\nimport { readConfig } from '../config'\nimport { locationContextFrom, sessionLocation, threadRepoCwd, type LocationContext } from '../summary/payload'\nimport { blockToJson, buildSessionsJson, threadToJson } from '../summary/contract/build'\nimport { collectNotes } from '../summary/contract/utils'\nimport { getCommitsInWindow, getOthersSubjects, getRepoRoot, getUserEmail } from '../summary/git-commits'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { memoize } from '../utils/memoize'\nimport type { DayBlock, DayThread } from '../db/day-threads'\nimport type { SessionGitInfo } from '../summary/contract/types'\n\n/**\n * Looks up git facts for the thread's repo — empty outside a git repo or when git fails.\n * Repo roots and emails are cached for the run so a day of threads in one repo asks git once.\n */\nfunction createThreadGitLookup(): (thread: DayThread) => SessionGitInfo {\n const repoRootOf = memoize(getRepoRoot)\n const emailOf = memoize(getUserEmail)\n const othersSubjectsOf = memoize((repoPath) => getOthersSubjects(repoPath, emailOf(repoPath)))\n return (thread) => {\n const cwd = threadRepoCwd(thread.events)\n const repoPath = cwd ? repoRootOf(cwd) : null\n if (!repoPath) return { repoPath: null, commits: [], othersSubjects: [] }\n const author = emailOf(repoPath)\n const commits = getCommitsInWindow({ repoRoot: repoPath, since: thread.startedAt, until: thread.endedAt, author })\n return { repoPath, commits, othersSubjects: othersSubjectsOf(repoPath) }\n }\n}\n\nfunction printSessionsJson(date: string, blocks: DayBlock[]): void {\n const config = readConfig()\n const options = {\n location: locationContextFrom(config),\n includeCategories: config.summary?.includeCategories ?? [],\n }\n const lookupGit = createThreadGitLookup()\n const sessionJsons = blocks.flatMap((block) =>\n block.threads.map((thread) => threadToJson(thread, block, { ...options, git: lookupGit(thread) }))\n )\n log.plain(JSON.stringify(buildSessionsJson(date, blocks.map(blockToJson), sessionJsons)))\n}\n\nfunction plural(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`\n}\n\nfunction printThread(thread: DayThread, location: LocationContext): void {\n const name = sessionLocation(thread, location)\n const project = name ? chalk.cyan(name) : chalk.gray('no repo')\n const active = formatDuration(toActiveMinutes(thread.activeSlots))\n log.plain(` ${project} ${chalk.dim(`${active} active`)} ${chalk.dim(plural(thread.eventCount, 'event'))}`)\n for (const note of collectNotes(thread.events)) log.plain(` ${chalk.yellow('note')} ${chalk.dim(note)}`)\n}\n\nfunction printBlocks(date: string, blocks: DayBlock[]): void {\n if (blocks.length === 0) {\n log.plain(`No sessions detected for ${date}`)\n return\n }\n const threadCount = blocks.reduce((total, block) => total + block.threads.length, 0)\n const location = locationContextFrom(readConfig())\n log.plain('')\n log.plain(\n chalk.bold(` Sessions for ${date} (${plural(threadCount, 'thread')} in ${plural(blocks.length, 'block')})`)\n )\n for (const block of blocks) {\n const span = `${formatDate(block.startedAt)} – ${formatDate(block.endedAt)}`\n log.plain('')\n log.plain(` ${chalk.dim(span)} ${chalk.dim(`${formatDuration(toActiveMinutes(block.activeSlots))} active`)}`)\n for (const thread of block.threads) printThread(thread, location)\n }\n log.plain('')\n}\n\nfunction parseDateOption(value: string | undefined): string {\n if (!value) return todayDate()\n try {\n return parseDate(value)\n } catch (error) {\n fail((error as Error).message)\n }\n}\n\nexport function registerSessions(program: Command): void {\n const sessionsCommand = program\n .command('sessions')\n .description(\"List the day's work: each block, and each project's thread inside it\")\n .option('--date <YYYY-MM-DD>', 'date to show sessions for (default: today)')\n .option('--json', 'output the versioned JSON contract (threads with events, notes and commits, and their blocks)')\n .action((options) => {\n const date = parseDateOption(options.date)\n requireThreadData()\n const blocks = loadDayThreads(date)\n if (options.json) {\n printSessionsJson(date, blocks)\n return\n }\n printBlocks(date, blocks)\n })\n\n registerSummarySet(sessionsCommand)\n}\n","import type { OutcomeWording } from './types'\n\n// The week digest's caps — a day's story tells everything (story.ts)\nexport const MAX_SESSION_BULLETS = 7\nexport const MAX_COMMIT_BULLETS = 3\nexport const MAX_DAY_BULLETS = 5\nexport const MAX_LISTED_PACKAGES = 6\n\n// Event sources that mean a coding agent ran the command (its hook captured it). Summaries count these,\n// but never name the tool.\nexport const AGENT_SOURCES = ['claude-code']\n\nexport const TEST_WORDING: OutcomeWording = {\n category: 'testing',\n resolved: 'Resolved failing tests',\n passing: 'Tests passing',\n failing: 'Tests still failing',\n ran: 'Ran tests',\n}\n\nexport const BUILD_WORDING: OutcomeWording = {\n category: 'building',\n resolved: 'Fixed a failing build',\n passing: 'Builds passing',\n failing: 'Build still failing',\n ran: 'Ran builds',\n}\n\n// A day's story says how runs turned out, not how many there were (the week digest above still counts them)\nexport const STORY_TEST_WORDING: OutcomeWording = {\n ...TEST_WORDING,\n resolved: 'Tests now passing',\n failing: 'Tests failing',\n}\n\nexport const STORY_BUILD_WORDING: OutcomeWording = {\n ...BUILD_WORDING,\n resolved: 'Build now passing',\n passing: 'Build passing',\n failing: 'Build failing',\n}\n","import type { EventPayload } from '../../types'\n\nexport function pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`\n}\n\n/** The non-empty strings in a metadata value — event metadata is untyped JSON. */\nexport function stringsIn(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string' && item !== '') : []\n}\n\nexport function unique(values: string[]): string[] {\n return [...new Set(values)]\n}\n\nexport function eventsOf(events: EventPayload[], category: string): EventPayload[] {\n return events.filter((event) => event.category === category)\n}\n\nexport function toBulletList(bullets: string[]): string {\n return bullets.map((bullet) => `- ${bullet}`).join('\\n')\n}\n","import { isFailedExit } from '../../../summary/commits'\nimport { buildExcludedSet } from '../../../summary/exclusions'\nimport {\n AGENT_SOURCES,\n BUILD_WORDING,\n MAX_COMMIT_BULLETS,\n MAX_LISTED_PACKAGES,\n MAX_SESSION_BULLETS,\n TEST_WORDING,\n} from './constants'\nimport { eventsOf, pluralize, stringsIn, unique } from './utils'\nimport type { EventPayload, SessionPayload } from '../../types'\nimport type { Outcome, OutcomeWording } from './types'\n\nexport function describeNotes(events: EventPayload[]): string[] {\n return eventsOf(events, 'note').flatMap((event) => stringsIn([event.metadata.text]).map((text) => `Note: ${text}`))\n}\n\n/** Commit messages from `git commit -m` and from AI sessions, in order, without repeats. A failed commit made none. */\nexport function collectCommits(events: EventPayload[]): string[] {\n return unique(\n events.flatMap((event) => {\n if (event.category === 'shipping' && !isFailedExit(event.exitCode)) return stringsIn([event.metadata.message])\n if (event.category === 'ai_session') return stringsIn(event.metadata.commits)\n return []\n })\n )\n}\n\nexport function describeCommits(commits: string[]): string[] {\n const bullets = commits.slice(0, MAX_COMMIT_BULLETS).map((message) => `Committed: ${message}`)\n const hiddenCount = commits.length - MAX_COMMIT_BULLETS\n if (hiddenCount > 0) bullets[MAX_COMMIT_BULLETS - 1] += `, plus ${pluralize(hiddenCount, 'more change')}`\n return bullets\n}\n\n/**\n * How test or build runs turned out. Commands captured through AI-tool hooks have no exit code, so they never decide\n * the outcome; with no exit code known at all, the runs just ran. Null when there were none.\n */\nexport function outcomeOf(runs: EventPayload[]): Outcome | null {\n if (runs.length === 0) return null\n const knownExitCodes = runs.map((run) => run.exitCode).filter((exitCode): exitCode is number => exitCode !== null)\n if (knownExitCodes.length === 0) return 'ran'\n if (knownExitCodes[knownExitCodes.length - 1] !== 0) return 'failing'\n if (knownExitCodes.some((exitCode) => exitCode !== 0)) return 'resolved'\n return 'passing'\n}\n\n/** Pass/fail line for test or build runs, with the run count — as the week digest words it. */\nexport function describeOutcome(events: EventPayload[], wording: OutcomeWording): string | null {\n const runs = eventsOf(events, wording.category)\n const outcome = outcomeOf(runs)\n if (outcome === null) return null\n const runCount = pluralize(runs.length, 'run')\n return outcome === 'resolved' ? `${wording.resolved} (${runCount}, now passing)` : `${wording[outcome]} (${runCount})`\n}\n\nfunction listPackages(packages: string[]): string {\n const listed = packages.slice(0, MAX_LISTED_PACKAGES).join(', ')\n const hiddenCount = packages.length - MAX_LISTED_PACKAGES\n return `${listed}${hiddenCount > 0 ? `, plus ${hiddenCount} more` : ''}`\n}\n\ninterface DependencyOptions {\n /** Say how many installs ran when no package was named — the week digest does; the story doesn't */\n withRunCount?: boolean\n}\n\n/** Removals (`npm uninstall`, `yarn remove`, …) carry action: 'remove' and are worded separately from additions. */\nexport function describeDependencies(\n events: EventPayload[],\n { withRunCount = true }: DependencyOptions = {}\n): string[] {\n const runs = eventsOf(events, 'dependencies')\n if (runs.length === 0) return []\n const isRemoval = (event: EventPayload): boolean => event.metadata.action === 'remove'\n const added = unique(runs.filter((event) => !isRemoval(event)).flatMap((event) => stringsIn(event.metadata.packages)))\n const removed = unique(runs.filter(isRemoval).flatMap((event) => stringsIn(event.metadata.packages)))\n if (added.length === 0 && removed.length === 0) {\n return [withRunCount ? `Installed dependencies (${pluralize(runs.length, 'run')})` : 'Installed dependencies']\n }\n\n return [\n ...(added.length > 0 ? [`Added dependencies: ${listPackages(added)}`] : []),\n ...(removed.length > 0 ? [`Removed dependencies: ${listPackages(removed)}`] : []),\n ]\n}\n\nexport function describeReleases(events: EventPayload[]): string | null {\n const releases = eventsOf(events, 'releasing')\n if (releases.length === 0) return null\n const tools = unique(releases.flatMap((event) => stringsIn([event.metadata.tool])))\n return tools.length > 0 ? `Released / deployed (${tools.join(', ')})` : 'Released / deployed'\n}\n\nexport function describePushes(events: EventPayload[]): string | null {\n const targets = unique(\n eventsOf(events, 'shipping').flatMap((event) => {\n const [remote] = stringsIn([event.metadata.remote])\n if (!remote) return []\n const [branch] = stringsIn([event.metadata.branch])\n return [branch ? `${remote}/${branch}` : remote]\n })\n )\n return targets.length > 0 ? `Pushed to ${targets.join(', ')}` : null\n}\n\n/** Commands a coding agent ran in the session — counted from all of them, including ones the summary leaves out. */\nfunction describeAgentWork(events: EventPayload[]): string | null {\n const agentCommands = events.filter((event) => event.source !== undefined && AGENT_SOURCES.includes(event.source))\n return agentCommands.length > 0\n ? `Worked with an AI coding assistant (${pluralize(agentCommands.length, 'command')})`\n : null\n}\n\nfunction describeEdits(events: EventPayload[]): string | null {\n const saves = eventsOf(events, 'code_edit').length\n return saves > 0 ? `Edited files in the IDE (${pluralize(saves, 'save')})` : null\n}\n\n/** Notes are always kept; everything else follows the same exclusions as the AI payload. */\nexport function includedEvents(events: EventPayload[], includeCategories: string[]): EventPayload[] {\n const excluded = buildExcludedSet(includeCategories)\n return events.filter((event) => event.category === 'note' || !excluded.has(event.category))\n}\n\n/**\n * The week digest's bullets for a location's day, most important first, capped at MAX_SESSION_BULLETS — empty if\n * nothing notable happened. A day's own summary tells the story instead (`story.ts`).\n */\nexport function describeActivity(session: SessionPayload, includeCategories: string[] = []): string[] {\n const events = includedEvents(session.events, includeCategories)\n return [\n ...describeNotes(events),\n ...describeCommits(collectCommits(events)),\n describeOutcome(events, TEST_WORDING),\n describeOutcome(events, BUILD_WORDING),\n ...describeDependencies(events),\n describeReleases(events),\n describePushes(events),\n describeAgentWork(session.events),\n describeEdits(events),\n ]\n .filter((bullet): bullet is string => bullet !== null)\n .slice(0, MAX_SESSION_BULLETS)\n}\n\nexport function countIncludedEvents(session: SessionPayload, includeCategories: string[] = []): number {\n return includedEvents(session.events, includeCategories).length\n}\n","// A session's branch-moving git work, in plain language — so you can retrace what you did, or undo it, without\n// remembering the commands. Pure: it reads the moves `eventRowToPayload` found in each event's command.\nimport type { EventPayload } from '../../types'\nimport type { DiscardCommand, GitMove } from '../../../normalize/git-move'\n\n/** A line of the story, and the branch it tells under. */\nexport interface BranchMoveLine {\n branch: string | null\n line: string\n}\n\ninterface PendingStash {\n branch: string | null\n /** Restored with `git stash apply`, which keeps it in `git stash list` */\n isApplied: boolean\n}\n\nconst DISCARD_LINES: Record<DiscardCommand, string> = {\n 'git reset --hard': 'Discarded local changes (git reset --hard)',\n 'git checkout -- …': 'Discarded changes to files (git checkout -- …)',\n 'git restore': 'Discarded changes to files (git restore)',\n 'git clean': 'Deleted untracked files (git clean)',\n}\n\nfunction describeStashRestore(stash: PendingStash | undefined, branch: string | null, command: string): string {\n if (!stash) return `Restored stashed changes (${command})`\n if (stash.branch !== null && branch !== null && stash.branch !== branch) {\n return `Moved uncommitted changes from ${stash.branch} to ${branch} (git stash, then ${command})`\n }\n return `Set changes aside and restored them (git stash, then ${command})`\n}\n\nconst isBranchMove = (move: GitMove): move is Extract<GitMove, { kind: 'create-branch' | 'switch' }> =>\n move.kind === 'create-branch' || move.kind === 'switch'\n\n/**\n * Whether the event's one branch move was really `git checkout <file>`: it reads like a switch, but the command\n * left the branch as it was. A command with several branch moves (`git checkout main && git pull && git checkout\n * feat/x`) can come back to where it started without having discarded anything, so it's followed step by step.\n */\nfunction isFileCheckout(event: EventPayload, previousBranch: string | null): boolean {\n const branchMoves = (event.gitMoves ?? []).filter(isBranchMove)\n const [only] = branchMoves\n return (\n branchMoves.length === 1 &&\n only.kind === 'switch' &&\n only.branch !== '-' &&\n // On a detached HEAD (`git checkout v1.2.3`, a manual bisect) there's no branch to compare, so it's no discard\n event.branch !== null &&\n event.branch === previousBranch &&\n only.branch !== event.branch\n )\n}\n\n/**\n * The branch each of an event's moves ran on. The event records the branch after the whole command ran, which is\n * right for a single git command; a compound one that switches part-way (`git stash && git switch -c feat/x &&\n * git stash pop`) is followed step by step from the branch before it, `git checkout -` included.\n */\nfunction branchesDuring(event: EventPayload, previousBranch: string | null): (string | null)[] {\n const moves = event.gitMoves ?? []\n if (!moves.some(isBranchMove) || isFileCheckout(event, previousBranch)) return moves.map(() => event.branch)\n let current = previousBranch ?? event.branch\n let before: string | null = null\n return moves.map((move) => {\n if (isBranchMove(move)) {\n // `git checkout -` with no switch before it in the command goes where the command ended up\n const target = move.branch === '-' ? (before ?? event.branch) : move.branch\n before = current\n current = target\n }\n return current\n })\n}\n\ninterface MoveContext {\n move: GitMove\n /** The branch the move ran on */\n branch: string | null\n /** The branch before it: what a new branch was created from */\n fromBranch: string | null\n /** The event's one branch move was really `git checkout <file>` */\n wasFileCheckout: boolean\n}\n\n/** A move that isn't about the stash, as a line — or null for one that needs none. */\nfunction describeMove({ move, branch, fromBranch, wasFileCheckout }: MoveContext): BranchMoveLine | null {\n switch (move.kind) {\n case 'create-branch': {\n const from = fromBranch !== null && fromBranch !== move.branch ? ` from ${fromBranch}` : ''\n return { branch: move.branch, line: `Created ${move.branch}${from}` }\n }\n case 'switch':\n return wasFileCheckout ? { branch, line: DISCARD_LINES['git checkout -- …'] } : null\n case 'rebase': {\n const rebased = branch === null ? 'Rebased' : `Rebased ${branch}`\n return { branch, line: `${rebased} onto ${move.onto}` }\n }\n case 'merge': {\n const into = branch === null ? '' : ` into ${branch}`\n return { branch, line: `Merged ${move.from}${into}` }\n }\n case 'discard':\n return { branch, line: DISCARD_LINES[move.command] }\n default:\n return null\n }\n}\n\ninterface StashStep {\n stashes: PendingStash[]\n line: BranchMoveLine | null\n}\n\n/**\n * One stash move against the stashes still waiting: a push adds one, a pop restores and removes the latest, an\n * apply restores it and keeps it, a drop throws the latest away and a clear all of them. Pure: returns new state.\n */\nfunction applyStashMove(stashes: PendingStash[], move: GitMove, branch: string | null): StashStep {\n const latest = stashes.at(-1)\n switch (move.kind) {\n case 'stash-push':\n return { stashes: [...stashes, { branch, isApplied: false }], line: null }\n case 'stash-pop': {\n const line = { branch, line: describeStashRestore(latest, branch, move.command) }\n if (move.command === 'git stash pop') return { stashes: stashes.slice(0, -1), line }\n return { stashes: stashes.map((stash) => (stash === latest ? { ...stash, isApplied: true } : stash)), line }\n }\n case 'stash-drop': {\n const isClear = move.command === 'git stash clear'\n const line = { branch, line: `Dropped ${isClear ? 'all ' : ''}stashed changes (${move.command})` }\n return { stashes: isClear ? [] : stashes.slice(0, -1), line }\n }\n default:\n return { stashes, line: null }\n }\n}\n\n/**\n * Every branch move in the session, in order, each under the branch it happened on: a stash restored on another\n * branch reads as uncommitted changes moved between them, and a new branch says what it was created from (the\n * branch you were on before). A plain switch gets no line — the story's `On <branch>:` lines already say it.\n */\nexport function describeBranchMoves(events: EventPayload[]): BranchMoveLine[] {\n const lines: BranchMoveLine[] = []\n let stashes: PendingStash[] = []\n let previousBranch: string | null = null\n for (const event of events) {\n const branches = branchesDuring(event, previousBranch)\n const wasFileCheckout = isFileCheckout(event, previousBranch)\n for (const [index, move] of (event.gitMoves ?? []).entries()) {\n const fromBranch = index === 0 ? previousBranch : branches[index - 1]\n const stashStep = applyStashMove(stashes, move, branches[index])\n stashes = stashStep.stashes\n const line = stashStep.line ?? describeMove({ move, branch: branches[index], fromBranch, wasFileCheckout })\n if (line) lines.push(line)\n }\n if (event.branch !== null) previousBranch = event.branch\n }\n // A stash nobody restored is still waiting in `git stash list`; an applied one is there too, but you used it\n const leftStashed = stashes.filter((pending) => !pending.isApplied)\n return [...lines, ...leftStashed.map((stash) => ({ branch: stash.branch, line: 'Left changes stashed (git stash)' }))]\n}\n","// A day's session as a story: notes first, then what was done on each branch — commits, then outcomes, then what\n// shipped and how you moved between branches — and the agent and editor activity in one closing line. Nothing is\n// cut or folded: every commit and every move is told, so the story is a full record you can retrace. Pure.\nimport { formatSessionHeading } from '../../../summary/heading'\nimport { AGENT_SOURCES, STORY_BUILD_WORDING, STORY_TEST_WORDING } from './constants'\nimport {\n describeDependencies,\n describeNotes,\n describePushes,\n describeReleases,\n includedEvents,\n outcomeOf,\n} from './describe'\nimport { describeBranchMoves } from './branch-moves'\nimport { eventsOf, pluralize, toBulletList } from './utils'\nimport type { EventPayload, SessionPayload } from '../../types'\nimport type { StoryCommit } from '../../../summary/story-commits'\nimport type { OutcomeWording } from './types'\n\n/** One branch's share of a session: its events, the commits made on it and the branch moves that happened on it. */\nexport interface BranchGroup {\n branch: string | null\n events: EventPayload[]\n commits: StoryCommit[]\n /** Branch moves (a branch created, a stash moved, a rebase, a discard) that happened on this branch */\n moves: string[]\n}\n\ninterface BranchBullets {\n branch: string | null\n bullets: string[]\n}\n\n/**\n * The session's events, commits and branch moves, by branch in the order first seen. An event with no branch\n * (outside a repo, a detached HEAD) stays with the branch before it; a commit or move on no known branch goes to the\n * first. Moves are read from every event, whatever the summary leaves out.\n */\nfunction groupByBranch(session: SessionPayload, events: EventPayload[]): BranchGroup[] {\n const order = session.branches.length > 0 ? session.branches : [null]\n const groups = new Map<string | null, BranchGroup>(\n order.map((branch) => [branch, { branch, events: [], commits: [], moves: [] }])\n )\n const groupOf = (branch: string | null): BranchGroup | undefined =>\n groups.get(branch !== null && groups.has(branch) ? branch : order[0])\n let current = order[0]\n for (const event of events) {\n if (event.branch !== null && groups.has(event.branch)) current = event.branch\n groups.get(current)?.events.push(event)\n }\n for (const commit of session.commits) groupOf(commit.branch)?.commits.push(commit)\n for (const move of describeBranchMoves(session.events)) groupOf(move.branch)?.moves.push(move.line)\n return [...groups.values()]\n}\n\n/** How the runs turned out, without counting them: \"Tests now passing\", not \"Ran tests (20 runs)\". */\nfunction describeResult(events: EventPayload[], wording: OutcomeWording): string | null {\n const outcome = outcomeOf(eventsOf(events, wording.category))\n return outcome === null ? null : wording[outcome]\n}\n\n/** What was done on one branch, most telling first: commits, outcomes, releases and pushes, then branch moves. */\nfunction branchBullets(group: BranchGroup): string[] {\n return [\n ...group.commits.map((commit) => `Committed: ${commit.subject}`),\n describeResult(group.events, STORY_TEST_WORDING),\n describeResult(group.events, STORY_BUILD_WORDING),\n ...describeDependencies(group.events, { withRunCount: false }),\n describeReleases(group.events),\n describePushes(group.events),\n ...group.moves,\n ].filter((bullet): bullet is string => bullet !== null)\n}\n\n/**\n * \"With an AI coding assistant (126 commands) · 3 file saves in the editor\" — how, not what, so it comes last. Agent\n * commands are counted over every event (`allEvents`), excluded categories too; saves over the included `events`.\n */\nfunction describeActivityLine(allEvents: EventPayload[], events: EventPayload[]): string | null {\n const agentCommands = allEvents.filter(\n (event) => event.source !== undefined && AGENT_SOURCES.includes(event.source)\n ).length\n const saves = eventsOf(events, 'code_edit').length\n const parts = [\n agentCommands > 0 ? `With an AI coding assistant (${pluralize(agentCommands, 'command')})` : null,\n saves > 0 ? `${pluralize(saves, 'file save')} in the editor` : null,\n ].filter((part): part is string => part !== null)\n return parts.length > 0 ? parts.join(' · ') : null\n}\n\n/** A session with nothing to tell but activity says so plainly — the heading already says where and how long. */\nfunction describeQuietSession(session: SessionPayload, events: EventPayload[]): string {\n const ranCommands = session.events.some((event) => event.category !== 'code_edit' && event.category !== 'note')\n const hasOnlySaves = !ranCommands && eventsOf(events, 'code_edit').length > 0\n return hasOnlySaves ? 'Edited files' : 'Explored and ran commands'\n}\n\nfunction branchLines(groups: BranchBullets[], isMultiBranch: boolean): string[] {\n return groups.flatMap((group) => {\n const bullets = toBulletList(group.bullets).split('\\n')\n return isMultiBranch && group.branch !== null ? [`On ${group.branch}:`, ...bullets] : bullets\n })\n}\n\n/** The story's lines under the heading: `- ` bullets, plain `On <branch>:` lines and a plain activity line. */\nexport function storyLines(session: SessionPayload, includeCategories: string[] = []): string[] {\n const events = includedEvents(session.events, includeCategories)\n const notes = describeNotes(events)\n const groups = groupByBranch(session, events)\n .map((group) => ({ branch: group.branch, bullets: branchBullets(group) }))\n .filter((group) => group.bullets.length > 0)\n const activityLine = describeActivityLine(session.events, events)\n const closing = activityLine === null ? [] : [activityLine]\n\n if (notes.length === 0 && groups.length === 0) {\n return [`- ${describeQuietSession(session, events)}`, ...closing]\n }\n const noteLines = notes.length > 0 ? toBulletList(notes).split('\\n') : []\n return [...noteLines, ...branchLines(groups, session.branches.length > 1), ...closing]\n}\n\n/**\n * A session's finished summary, heading included — the one renderer `summary`, `export` and `context` share, so\n * every consumer (the terminal, Slack, the VS Code tab and tooltip) gets the same `## `, `- ` and plain lines.\n */\nexport function renderSessionStory(session: SessionPayload, includeCategories: string[] = []): string {\n return [formatSessionHeading(session), ...storyLines(session, includeCategories)].join('\\n')\n}\n\n/**\n * A session's branches with what happened on each — every event, included or not, so a branch's activity can be\n * counted like a day's. The week splits each thread this way before merging its branches across days, so a thread's\n * events with no branch stay with that thread's branch.\n */\nexport function branchGroupsOf(session: SessionPayload): BranchGroup[] {\n return groupByBranch(session, session.events)\n}\n\n/** What one branch's story says: its notes, then what was done on it, and how — told like a day's branch. */\nexport function storyOfBranch(\n group: BranchGroup,\n includeCategories: string[] = []\n): { bullets: string[]; activity: string | null } {\n const events = includedEvents(group.events, includeCategories)\n return {\n bullets: [...describeNotes(events), ...branchBullets({ ...group, events })],\n activity: describeActivityLine(group.events, events),\n }\n}\n","// The week, in both its layouts (pure). By project (the default): each project, then each of its branches, with the\n// dates it was worked on and what was done — told by the day's own branch story. Each thread is split by branch first\n// and its branches merged across the week after, so a thread's events with no branch stay with that thread's branch.\n// By day: the day-by-day digest, a few bullets a day taking turns across locations.\nimport { UNKNOWN_LOCATION } from '../../../summary/heading'\nimport { formatWeekday, localDateOf } from '../../../utils/format'\nimport { MAX_DAY_BULLETS } from './constants'\nimport { countIncludedEvents, describeActivity } from './describe'\nimport { branchGroupsOf, storyOfBranch, type BranchGroup } from './story'\nimport { pluralize, toBulletList } from './utils'\nimport type { SessionPayload, WeekSummaryPayload } from '../../types'\n\n/** At most this many bullets per branch; the rest fold into \"…plus N more\". */\nexport const MAX_BRANCH_BULLETS = 6\nconst NO_BRANCH_LABEL = '(no branch)'\n// A branch with nothing to tell but excluded activity still says it was worked on\nconst QUIET_BRANCH_BULLET = 'Explored and ran commands'\n\nexport interface BranchWeek {\n /** Null for work on no branch */\n branch: string | null\n /** The local days it had activity, in order */\n dates: string[]\n bullets: string[]\n /** How many bullets were folded away */\n moreCount: number\n activity: string | null\n}\n\nexport interface ProjectWeek {\n project: string\n branches: BranchWeek[]\n}\n\nexport interface DayWeek {\n date: string\n bullets: string[]\n}\n\n/** The week as laid out: what `summary.format=json` prints after `weekStart` and `weekEnd`, and what's rendered. */\nexport type WeekLayoutData = { by: 'project'; projects: ProjectWeek[] } | { by: 'day'; days: DayWeek[] }\n\n/** A branch's merged group across the week, and the days it was worked on */\ninterface BranchAccumulator {\n group: BranchGroup\n dates: Set<string>\n}\n\ninterface ProjectAccumulator {\n project: string\n /** Its branches, in the order first seen */\n branches: Map<string | null, BranchAccumulator>\n}\n\n/** The week groups by project identity; work outside any project (no id) by the folder it's named after. */\nfunction projectKeyOf(session: SessionPayload): string {\n return session.projectId === undefined || session.projectId === null\n ? `name:${session.project ?? UNKNOWN_LOCATION}`\n : `id:${session.projectId}`\n}\n\n/**\n * A commit told twice: the same hash, or — for a captured commit git didn't return — the same subject at the same\n * time. Never by subject alone: two \"wip\" commits on different days are two commits.\n */\nfunction commitKeyOf(commit: BranchGroup['commits'][number]): string {\n return commit.hash ?? `${commit.subject}@${commit.committedAt}`\n}\n\nfunction distinctCommits(commits: BranchGroup['commits']): BranchGroup['commits'] {\n const seen = new Set<string>()\n return commits.filter((commit) => {\n const key = commitKeyOf(commit)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n}\n\n/** A thread's branch group merged into what the week has for that branch so far. */\nfunction mergeBranch(existing: BranchAccumulator | undefined, group: BranchGroup, date: string): BranchAccumulator {\n const dates = new Set(group.events.map((event) => localDateOf(event.capturedAt)))\n if (dates.size === 0) dates.add(date)\n if (!existing) return { group, dates }\n return {\n group: {\n branch: group.branch,\n events: [...existing.group.events, ...group.events],\n commits: distinctCommits([...existing.group.commits, ...group.commits]),\n moves: [...existing.group.moves, ...group.moves],\n },\n dates: new Set([...existing.dates, ...dates]),\n }\n}\n\nfunction toBranchWeek({ group, dates }: BranchAccumulator, includeCategories: string[]): BranchWeek {\n const story = storyOfBranch(group, includeCategories)\n const bullets = story.bullets.length === 0 && story.activity === null ? [QUIET_BRANCH_BULLET] : story.bullets\n return {\n branch: group.branch,\n dates: [...dates].sort(),\n bullets: bullets.slice(0, MAX_BRANCH_BULLETS),\n moreCount: Math.max(0, bullets.length - MAX_BRANCH_BULLETS),\n activity: story.activity,\n }\n}\n\n/** Each project of the week, in the order of its first activity, with its branches in the order first seen. */\nexport function weekByProject(payload: WeekSummaryPayload, includeCategories: string[] = []): ProjectWeek[] {\n const projects = new Map<string, ProjectAccumulator>()\n for (const day of payload.days) {\n for (const session of day.sessions) {\n const key = projectKeyOf(session)\n const project = projects.get(key) ?? { project: session.project ?? UNKNOWN_LOCATION, branches: new Map() }\n projects.set(key, project)\n for (const group of branchGroupsOf(session)) {\n project.branches.set(group.branch, mergeBranch(project.branches.get(group.branch), group, day.date))\n }\n }\n }\n return [...projects.values()].map((project) => ({\n project: project.project,\n branches: [...project.branches.values()].map((branch) => toBranchWeek(branch, includeCategories)),\n }))\n}\n\nfunction renderBranch(branch: BranchWeek): string {\n const heading = `### ${branch.branch ?? NO_BRANCH_LABEL} — ${branch.dates.map(formatWeekday).join(', ')}`\n const more = branch.moreCount > 0 ? [`…plus ${branch.moreCount} more`] : []\n const bullets = [...branch.bullets, ...more]\n // A branch with only an activity line has no bullet list, and so no blank line where one would be\n const lines = [heading, ...(bullets.length === 0 ? [] : [toBulletList(bullets)])]\n return (branch.activity === null ? lines : [...lines, branch.activity]).join('\\n')\n}\n\n/** The week as text: a `## project` section per project, a `### branch — dates` block per branch. */\nexport function renderWeekByProject(projects: ProjectWeek[]): string {\n return projects\n .map((project) => [`## ${project.project}`, ...project.branches.map(renderBranch)].join('\\n\\n'))\n .join('\\n\\n')\n}\n\n/** One entry per location for the day — several sessions in the same repo read as one block of work. */\nexport function mergeByLocation(sessions: SessionPayload[]): SessionPayload[] {\n const merged = new Map<string, SessionPayload>()\n for (const session of sessions) {\n const location = session.project ?? UNKNOWN_LOCATION\n const existing = merged.get(location)\n merged.set(\n location,\n existing\n ? {\n ...existing,\n events: [...existing.events, ...session.events],\n branches: [...new Set([...existing.branches, ...session.branches])],\n durationMinutes: existing.durationMinutes + session.durationMinutes,\n }\n : { ...session, project: location }\n )\n }\n return [...merged.values()]\n}\n\nfunction locationBullets(session: SessionPayload, includeCategories: string[]): string[] {\n const bullets = describeActivity(session, includeCategories)\n const lines = bullets.length > 0 ? bullets : [pluralize(countIncludedEvents(session, includeCategories), 'command')]\n return lines.map((line) => `${session.project}: ${line}`)\n}\n\n/** Takes the top bullet of every location, then the second, and so on — so one busy repo can't fill the day. */\nfunction pickDayBullets(sessions: SessionPayload[], includeCategories: string[]): string[] {\n const perLocation = mergeByLocation(sessions).map((session) => locationBullets(session, includeCategories))\n const longest = Math.max(0, ...perLocation.map((bullets) => bullets.length))\n const interleaved = Array.from({ length: longest }, (_unused, index) =>\n perLocation.flatMap((bullets) => (index < bullets.length ? [bullets[index]] : []))\n ).flat()\n return interleaved.slice(0, MAX_DAY_BULLETS)\n}\n\n/** The week day by day (`--by day`): each day's bullets, taking turns across locations, at most 5 a day. */\nexport function weekByDay(payload: WeekSummaryPayload, includeCategories: string[] = []): DayWeek[] {\n return payload.days.map((day) => ({ date: day.date, bullets: pickDayBullets(day.sessions, includeCategories) }))\n}\n\n/** The week laid out as `payload.by` asks — the one place the two layouts are chosen between. */\nexport function weekLayoutOf(payload: WeekSummaryPayload, includeCategories: string[] = []): WeekLayoutData {\n if (payload.by === 'project') return { by: 'project', projects: weekByProject(payload, includeCategories) }\n return { by: 'day', days: weekByDay(payload, includeCategories) }\n}\n\n/** The laid-out week as text. */\nexport function renderWeek(layout: WeekLayoutData): string {\n if (layout.by === 'project') return renderWeekByProject(layout.projects)\n return layout.days.map((day) => `### ${day.date}\\n${toBulletList(day.bullets)}`).join('\\n\\n')\n}\n","import { renderSessionStory } from './story'\nimport { renderWeek, weekLayoutOf } from './week'\nimport type { AIProvider, DaySummaryPayload, WeekSummaryPayload } from '../../types'\n\n/** Offline, deterministic summaries built from captured events — the default when no AI is configured. */\nexport class TemplateProvider implements AIProvider {\n constructor(private includeCategories: string[] = []) {}\n\n /** Each session's story, heading included, separated by a blank line. */\n async summarize(payload: DaySummaryPayload): Promise<string> {\n return payload.sessions.map((session) => renderSessionStory(session, this.includeCategories)).join('\\n\\n')\n }\n\n /** The week by project then branch (the default), or day by day. */\n async summarizeWeek(payload: WeekSummaryPayload): Promise<string> {\n return renderWeek(weekLayoutOf(payload, this.includeCategories))\n }\n\n async validate(): Promise<void> {\n // Nothing to reach — the template engine runs entirely offline\n }\n}\n","import { TemplateProvider } from '../ai/engines/template/provider'\nimport type { AIProvider } from '../ai/types'\nimport type { WorkscribeConfig } from '../config'\n\n/** What writes summaries since 2.0: the offline template engine, honouring summary.includeCategories. */\nexport function createSummaryWriter(config: WorkscribeConfig): AIProvider {\n return new TemplateProvider(config.summary?.includeCategories ?? [])\n}\n","import { fetchWithTimeout } from '../utils/fetch'\n\nexport async function postToWebhook(url: string, text: string): Promise<void> {\n const response = await fetchWithTimeout(\n url,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text }),\n },\n 10_000\n )\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Webhook POST failed ${response.status}: ${body}`)\n }\n}\n","/**\n * Converts workscribe markdown summary output to Slack mrkdwn format.\n *\n * Rules:\n * ## heading → *heading* (bold)\n * - bullet → • bullet\n *\n * An optional username is prepended as a dim header line so recipients\n * can tell who sent the summary in shared channels.\n */\nexport function formatForSlack(markdown: string, username?: string): string {\n // Slack has no headings: the day's `## ` and the week's `### ` both become bold lines\n const body = markdown.replace(/^#{2,3} (.+)$/gm, '*$1*').replace(/^- /gm, '• ')\n return username ? `_${username}_\\n${body}` : body\n}\n\nexport function isSlackWebhookUrl(url: string): boolean {\n return url.startsWith('https://hooks.slack.com/')\n}\n","import os from 'os'\nimport { log } from '../utils/logger'\nimport { postToWebhook } from '../integrations/webhook'\nimport { formatForSlack, isSlackWebhookUrl } from '../integrations/slack'\nimport type { WorkscribeConfig } from '../config'\n\nexport interface WebhookOptions {\n slack?: boolean\n webhook?: string\n}\n\ninterface PostSummaryInput {\n options: WebhookOptions\n config: WorkscribeConfig\n summary: string\n /** True when stdout carries only JSON or plain text: status lines then go to stderr so the output can be piped. */\n statusToStderr?: boolean\n}\n\n/** Posts a finished summary to --webhook, or to the configured Slack webhook with --slack. Never throws. */\nexport async function postSummary({\n options,\n config,\n summary,\n statusToStderr = false,\n}: PostSummaryInput): Promise<void> {\n const status = statusToStderr ? { info: log.notice, success: log.notice, warn: log.notice } : log\n const webhookUrl = options.webhook ?? (options.slack ? config.integrations?.slackWebhook : undefined)\n\n if (!webhookUrl) {\n if (options.slack && !config.integrations?.slackWebhook) {\n status.warn('--slack requires integrations.slackWebhook to be set in config')\n }\n return\n }\n\n try {\n status.info('Posting to webhook…')\n const body = isSlackWebhookUrl(webhookUrl) ? formatForSlack(summary, os.userInfo().username) : summary\n await postToWebhook(webhookUrl, body)\n status.success('Posted.')\n } catch (error) {\n log.error(`Webhook failed: ${(error as Error).message}`)\n }\n}\n","// The day's commits from git for the threads `summary` and `export` render. One `git log` per repo covers all of\n// its threads, and every git call shares one budget, so a slow repo can't hold the summary up.\nimport { findCommitsInWindow, getRepoRoot, getUserEmail } from './git-commits'\nimport { assignCommitsToSessions } from './story-commits'\nimport type { GitCommit } from './git-commits'\n\nexport const DAY_GIT_BUDGET_MS = 3000\n\nexport interface DayCommitOptions {\n /** All of the run's git calls together get at most this long */\n budgetMs: number\n /** The clock, for tests */\n now?: () => number\n}\n\n/** A stretch of work to find commits for: a folder inside its repo (null outside one) and its time span. */\nexport interface CommitWindow {\n cwd: string | null\n startedAt: string\n endedAt: string\n}\n\ninterface RepoWindows {\n repoRoot: string\n indexes: number[]\n}\n\n/** Groups the windows by the repo they ran in; windows outside a repo, or past the budget, aren't looked up. */\nfunction groupByRepo(windows: CommitWindow[], timeLeft: () => number | null): RepoWindows[] {\n const rootOfCwd = new Map<string, string | null>()\n const byRoot = new Map<string, number[]>()\n for (const [index, { cwd }] of windows.entries()) {\n if (!cwd) continue\n if (!rootOfCwd.has(cwd)) {\n const timeout = timeLeft()\n if (timeout === null) break\n rootOfCwd.set(cwd, getRepoRoot(cwd, timeout))\n }\n const repoRoot = rootOfCwd.get(cwd)\n if (repoRoot) byRoot.set(repoRoot, [...(byRoot.get(repoRoot) ?? []), index])\n }\n return [...byRoot].map(([repoRoot, indexes]) => ({ repoRoot, indexes }))\n}\n\n/** One `git log` for the repo, from its windows' earliest start to their latest end — null past the budget, or if git can't answer. */\nfunction repoCommits(repo: RepoWindows, windows: CommitWindow[], timeLeft: () => number | null): GitCommit[] | null {\n const emailTimeout = timeLeft()\n if (emailTimeout === null) return null\n const author = getUserEmail(repo.repoRoot, emailTimeout)\n // Without your committer email git can't tell your commits from pulled ones, so the story keeps to captured ones\n if (author === null) return null\n const logTimeout = timeLeft()\n if (logTimeout === null) return null\n const repoWindows = repo.indexes.map((index) => windows[index])\n const byTime = (first: string, second: string): number => Date.parse(first) - Date.parse(second)\n const startTimes = repoWindows.map((window) => window.startedAt).sort(byTime)\n const endTimes = repoWindows.map((window) => window.endedAt).sort(byTime)\n return findCommitsInWindow({\n repoRoot: repo.repoRoot,\n since: startTimes[0],\n until: endTimes[endTimes.length - 1],\n author,\n timeoutMs: logTimeout,\n allBranches: true,\n })\n}\n\n/**\n * Each window's commits from `git log`, by your committer email — or null for a window git wasn't asked about\n * (outside a repo, git missing or slow, the budget spent). The story then tells the commits Workscribe captured.\n * Windows of one repo that overlap (two projects in one git root) share its commits: each goes to the first window,\n * in the order given, whose span holds it — `renderDay` passes threads in block order, so the one that started first.\n */\nexport function lookupDayCommits(windows: CommitWindow[], options: DayCommitOptions): (GitCommit[] | null)[] {\n const now = options.now ?? Date.now\n const deadline = now() + options.budgetMs\n // Read once per git call and passed on as is, so a clock tick can't turn a checked 1 ms into a timeout of 0\n const timeLeft = (): number | null => {\n const milliseconds = deadline - now()\n return milliseconds >= 1 ? milliseconds : null\n }\n\n const perWindow: (GitCommit[] | null)[] = windows.map(() => null)\n for (const repo of groupByRepo(windows, timeLeft)) {\n const commits = repoCommits(repo, windows, timeLeft)\n if (commits === null) continue\n const assigned = assignCommitsToSessions(\n repo.indexes.map((index) => windows[index]),\n commits\n )\n for (const [position, index] of repo.indexes.entries()) perWindow[index] = assigned[position]\n }\n return perWindow\n}\n","// A day's summary, the way `summary` and `export` both tell it: a section per project thread — a saved summary where\n// the agent skill (or a 1.x AI provider) wrote one for it, and the template's story for the rest, with the day's git\n// commits looked up just for those — each followed by the projects worked alongside it, then the day's time worked.\nimport { DAY_GIT_BUDGET_MS, lookupDayCommits } from './day-commits'\nimport { locationContextFrom, threadRepoCwd, threadToPayload } from './payload'\nimport { pickSavedSummary, type SavedSummary } from './store'\nimport { summariesOfThread, type BlockEvent } from './anchors'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { formatDuration } from '../utils/format'\nimport type { AIProvider, SessionPayload } from '../ai/types'\nimport type { WorkscribeConfig } from '../config'\nimport type { DayBlock, DayThread } from '../db/day-threads'\nimport type { GitCommit } from './git-commits'\n\nconst ALONGSIDE_LABEL = 'Alongside:'\nconst TIME_WORKED_LABEL = 'Time worked:'\n\nexport interface RenderDayOptions {\n date: string\n /** The day's blocks and threads, as `loadDayThreads` reads them */\n blocks: DayBlock[]\n config: WorkscribeConfig\n /** What writes the summaries the skill didn't (the template) */\n provider: AIProvider\n /** Ignore saved summaries and tell every thread with the template */\n fresh?: boolean\n}\n\nexport interface RenderedDay {\n /** Per thread, in block order */\n sessionPayloads: SessionPayload[]\n /** Per thread: the saved summary shown, or null where the template told it */\n saved: (SavedSummary | null)[]\n /** Per thread: its finished text, heading included */\n summaries: string[]\n /** Every thread's text with the projects alongside it, separated by a blank line, then the day's time worked */\n fullSummary: string\n /** Whether the template told any thread — the left-out events footer only applies then */\n needsGeneration: boolean\n /** Time worked: the blocks' active minutes — never the sum of the threads' */\n activeMinutes: number\n}\n\n/** The block's events in grouping order, each with its thread — what `findSummaryThread` reads. */\nfunction blockEventsOf(block: DayBlock): BlockEvent[] {\n return block.threads\n .flatMap((thread) => thread.events.map((event) => ({ event, threadId: thread.id })))\n .sort(\n (first, second) =>\n first.event.captured_at.localeCompare(second.event.captured_at) || first.event.id - second.event.id\n )\n .map(({ event, threadId }) => ({ id: event.id, threadId }))\n}\n\n/** Each thread's best summary stored in `summary`, in block order. */\nfunction savedSummariesOf(blocks: DayBlock[]): (SavedSummary | null)[] {\n return blocks.flatMap((block) => {\n const attachable = { threads: block.threads, events: blockEventsOf(block) }\n return block.threads.map((thread) => pickSavedSummary(summariesOfThread(thread.id, attachable, block.summaries)))\n })\n}\n\n/** git's commits for the threads the template tells, within one budget for the run; null for the others. */\nfunction gitCommitsFor(threads: DayThread[], saved: (SavedSummary | null)[]): (GitCommit[] | null)[] {\n const toRender = threads.filter((_thread, index) => saved[index] === null)\n const windows = toRender.map((thread) => ({\n cwd: threadRepoCwd(thread.events),\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n }))\n const commits = lookupDayCommits(windows, { budgetMs: DAY_GIT_BUDGET_MS })\n const commitsByThread = new Map(toRender.map((thread, index) => [thread, commits[index]]))\n return threads.map((thread) => commitsByThread.get(thread) ?? null)\n}\n\nfunction withAlongside(text: string, alongside: string[] = []): string {\n return alongside.length === 0 ? text : `${text}\\n${ALONGSIDE_LABEL} ${alongside.join(', ')}`\n}\n\nfunction joinDay(summaries: string[], sessionPayloads: SessionPayload[], activeMinutes: number): string {\n const sections = summaries.map((text, index) => withAlongside(text, sessionPayloads[index].alongside))\n return [...sections, `${TIME_WORKED_LABEL} ${formatDuration(activeMinutes)}`].join('\\n\\n')\n}\n\n/** Each thread's summary for the day. Throws when the writer fails, so the command can say so and stop. */\nexport async function renderDay({\n date,\n blocks,\n config,\n provider,\n fresh = false,\n}: RenderDayOptions): Promise<RenderedDay> {\n const threads = blocks.flatMap((block) => block.threads.map((thread) => ({ thread, block })))\n const saved = fresh ? threads.map(() => null) : savedSummariesOf(blocks)\n const gitCommits = gitCommitsFor(\n threads.map(({ thread }) => thread),\n saved\n )\n const context = locationContextFrom(config)\n const sessionPayloads = threads.map(({ thread, block }, index) =>\n threadToPayload(thread, block, { context, gitCommits: gitCommits[index] })\n )\n\n const summaries: string[] = []\n for (const [index, sessionPayload] of sessionPayloads.entries()) {\n const savedSummary = saved[index]\n // The writer returns the thread's finished story, heading included\n summaries.push(\n savedSummary ? savedSummary.text : (await provider.summarize({ date, sessions: [sessionPayload] })).trim()\n )\n }\n const activeMinutes = blocks.reduce((total, block) => total + toActiveMinutes(block.activeSlots), 0)\n return {\n sessionPayloads,\n saved,\n summaries,\n fullSummary: joinDay(summaries, sessionPayloads, activeMinutes),\n needsGeneration: saved.some((entry) => entry === null),\n activeMinutes,\n }\n}\n","// What `summary` and `week` can print. A leaf, so the commands and the week flow share it without loading the config.\nexport const SUMMARY_FORMATS = ['markdown', 'json', 'text'] as const\nexport type SummaryFormat = (typeof SUMMARY_FORMATS)[number]\n\n/** Formats meant to be piped or pasted: stdout carries the output alone, and everything else goes to stderr. */\nexport function keepsStdoutClean(format: string | undefined): boolean {\n return format === 'json' || format === 'text'\n}\n","/**\n * A finished summary as plain text, to paste into a form or report. Every line goes through it: saved summaries are\n * free text, and commit subjects and notes reach the template verbatim.\n *\n * Only line-start markers and paired forms change. A lone `*` or `_` stays, since `*.ts` and `snake_case` are real text.\n */\nexport function toPlainText(markdown: string): string {\n return markdown\n .replace(/^#{1,6} +/gm, '')\n .replace(/^([ \\t]*)[*•] +/gm, '$1- ')\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, '$1')\n .replace(/`([^`\\n]+)`/g, '$1')\n .replace(/\\[([^\\]\\n]+)\\]\\(([^)\\s]+)\\)/g, '$1 ($2)')\n .trim()\n}\n","// What `summary` and `export` print under a day's summary: which sessions came from saved summaries, and which\n// events the template left out. Both go to the console — never into an exported file.\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { countExcludedEvents } from '../summary/exclusions'\nimport type { SessionPayload } from '../ai/types'\nimport type { SavedSummary } from '../summary/store'\n\nexport function printExcludedFooter(sessionPayloads: SessionPayload[], excluded: Set<string>): void {\n const excludedCounts = countExcludedEvents(sessionPayloads, excluded)\n if (excludedCounts.size === 0) return\n const total = [...excludedCounts.values()].reduce((sum, count) => sum + count, 0)\n const breakdown = [...excludedCounts.entries()].map(([category, count]) => `${category} ×${count}`).join(', ')\n log.plain(chalk.dim(` ℹ ${total} event${total === 1 ? '' : 's'} not included in summary (${breakdown})`))\n log.plain(chalk.dim(' Use --verbose to inspect · workscribe config set summary.includeCategories to opt in'))\n log.plain('')\n}\n\n/** Says which sessions came from saved summaries, so it's clear why --fresh would change the output. */\nexport function printSavedNote(saved: (SavedSummary | null)[]): void {\n const fromSkill = saved.filter((entry) => entry?.fromSkill).length\n const fromLegacyProvider = saved.filter((entry) => entry && !entry.fromSkill).length\n const parts = [\n fromSkill > 0 ? `${fromSkill} written by the Workscribe agent skill` : null,\n fromLegacyProvider > 0 ? `${fromLegacyProvider} written by an AI provider in Workscribe 1.x` : null,\n ].filter((part): part is string => part !== null)\n if (parts.length === 0) return\n log.plain(chalk.dim(` (${parts.join(', ')} — use --fresh for the template summary)`))\n}\n","import { log } from '../utils/logger'\nimport { todayDate, startOfWeek, eachDayOfRange } from '../utils/format'\nimport { locationContextFrom, threadRepoCwd, threadToPayload, type LocationContext } from './payload'\nimport { postSummary, type WebhookOptions } from './webhook'\nimport { DAY_GIT_BUDGET_MS, lookupDayCommits } from './day-commits'\nimport { keepsStdoutClean, type SummaryFormat } from './output-format'\nimport { toPlainText } from './plain-text'\nimport { weekLayoutOf } from '../ai/engines/template/week'\nimport type { AIProvider, WeekLayout, WeekSummaryPayload } from '../ai/types'\nimport type { WorkscribeConfig } from '../config'\nimport type { DayBlock, DayThread } from '../db/day-threads'\n\n/** All of the week's git lookups together get at most the day's budget: one `git log` per repo covers the week. */\nexport const WEEK_GIT_BUDGET_MS = DAY_GIT_BUDGET_MS\n\nexport interface WeeklySummaryOptions extends WebhookOptions {\n /** Accepted for compatibility with 1.x scripts — weekly digests are always rendered fresh now. */\n fresh?: boolean\n /** Each project then each branch (the default), or day by day */\n by?: WeekLayout\n /** `--format`; `summary.format` decides without it */\n format?: SummaryFormat\n}\n\ninterface RunWeeklySummaryInput {\n options: WeeklySummaryOptions\n config: WorkscribeConfig\n provider: AIProvider\n /** A day's blocks and threads — `loadDayThreads`, after the caller checked the data is upgraded */\n loadDay: (date: string) => DayBlock[]\n}\n\ninterface WeekRange {\n weekStart: string\n today: string\n}\n\ninterface ThreadOfWeek {\n date: string\n thread: DayThread\n block: DayBlock\n}\n\n/** git's commits for every thread of the week: one `git log` per repo over the week, within one budget. */\nfunction weekCommits(threads: ThreadOfWeek[]): ReturnType<typeof lookupDayCommits> {\n const windows = threads.map(({ thread }) => ({\n cwd: threadRepoCwd(thread.events),\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n }))\n return lookupDayCommits(windows, { budgetMs: WEEK_GIT_BUDGET_MS })\n}\n\nfunction collectWeekDays(\n { weekStart, today }: WeekRange,\n loadDay: RunWeeklySummaryInput['loadDay'],\n context: LocationContext\n): WeekSummaryPayload['days'] {\n const threads = eachDayOfRange(weekStart, today).flatMap((date) =>\n loadDay(date).flatMap((block) => block.threads.map((thread) => ({ date, thread, block })))\n )\n const commits = weekCommits(threads)\n const days = new Map<string, WeekSummaryPayload['days'][number]>()\n for (const [index, { date, thread, block }] of threads.entries()) {\n const day = days.get(date) ?? { date, sessions: [] }\n day.sessions.push(threadToPayload(thread, block, { context, gitCommits: commits[index] }))\n days.set(date, day)\n }\n return [...days.values()]\n}\n\n/** The week as one JSON document (`summary.format=json`): projects → branches, or days → bullets. */\nfunction weekJson(payload: WeekSummaryPayload, includeCategories: string[]): string {\n const { weekStart, weekEnd } = payload\n return JSON.stringify({ weekStart, weekEnd, ...weekLayoutOf(payload, includeCategories) }, null, 2)\n}\n\nasync function writeWeeklySummary(provider: AIProvider, payload: WeekSummaryPayload): Promise<string> {\n if (provider.summarizeWeek) return provider.summarizeWeek(payload)\n return provider.summarize({ date: payload.weekEnd, sessions: payload.days.flatMap((day) => day.sessions) })\n}\n\nfunction printSummary(summary: string): void {\n log.plain('')\n log.plain(summary)\n log.plain('')\n}\n\n/** Shared flow for `summary --week` and `week`: collect Mon–today's threads, render the week, post it. */\nexport async function runWeeklySummary({ options, config, provider, loadDay }: RunWeeklySummaryInput): Promise<void> {\n const today = todayDate()\n const weekStart = startOfWeek(today)\n const days = collectWeekDays({ weekStart, today }, loadDay, locationContextFrom(config))\n if (days.length === 0) {\n log.plain('No activity captured this week.')\n return\n }\n\n const payload: WeekSummaryPayload = { weekStart, weekEnd: today, by: options.by ?? 'project', days }\n const summary = await writeWeeklySummary(provider, payload)\n const format = options.format ?? config.summary?.format\n // Only the JSON or the plain text on stdout, so it can be piped; posting still sends the markdown, its status on stderr\n if (format === 'json') log.plain(weekJson(payload, config.summary?.includeCategories ?? []))\n else if (format === 'text') log.plain(toPlainText(summary))\n else printSummary(summary)\n await postSummary({ options, config, summary, statusToStderr: keepsStdoutClean(format) })\n}\n","import { Command } from 'commander'\nimport { readConfig } from '../config'\nimport { SUMMARY_FORMATS, type SummaryFormat } from '../summary/output-format'\nimport { runWeeklySummary, type WeeklySummaryOptions } from '../summary/weekly'\nimport { createSummaryWriter } from '../summary/writer'\nimport { printLegacyAiNotice } from './notices'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { fail } from './fail'\nimport type { WeekLayout } from '../ai/types'\n\nconst WEEK_LAYOUTS: readonly WeekLayout[] = ['project', 'day']\n\n/** `--by`, which both `week` and `summary --week` take. */\nexport function addWeekLayoutOption(command: Command): Command {\n return command.option('--by <layout>', 'lay the week out by project then branch (default), or by day')\n}\n\nfunction parseWeekLayout(value: string | undefined): WeekLayout {\n if (value === undefined) return 'project'\n const layout = WEEK_LAYOUTS.find((candidate) => candidate === value)\n if (!layout) fail('--by must be project or day')\n return layout\n}\n\n/** `--format`, which both `week` and `summary` take. */\nexport function addSummaryFormatOption(command: Command): Command {\n return command.option('--format <format>', 'print as markdown, json or text, overriding summary.format')\n}\n\n/** The flag's format, or undefined when it wasn't passed and `summary.format` decides. */\nexport function parseSummaryFormat(value: string | undefined): SummaryFormat | undefined {\n if (value === undefined) return undefined\n const format = SUMMARY_FORMATS.find((candidate) => candidate === value)\n if (!format) fail('--format must be markdown, json or text')\n return format\n}\n\n/** `week` and `summary --week`: one handler, so both print the same thing for the same options. */\nexport async function runWeekCommand(\n options: Omit<WeeklySummaryOptions, 'by' | 'format'> & { by?: string; format?: string }\n): Promise<void> {\n const by = parseWeekLayout(options.by)\n const format = parseSummaryFormat(options.format)\n const config = readConfig()\n printLegacyAiNotice(config)\n requireThreadData()\n try {\n await runWeeklySummary({\n options: { ...options, by, format },\n config,\n provider: createSummaryWriter(config),\n loadDay: loadDayThreads,\n })\n } catch (error) {\n fail((error as Error).message)\n }\n}\n\nexport function registerWeek(program: Command): void {\n const weekCommand = program\n .command('week')\n .description('Summarize the current week (Mon–today) — the same as summary --week')\n .option('--fresh', 'accepted for 1.x scripts — the week is always rendered fresh')\n .option('--slack', 'post to Slack webhook from config')\n .option('--webhook <url>', 'post to a custom webhook URL')\n addSummaryFormatOption(addWeekLayoutOption(weekCommand)).action(runWeekCommand)\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate } from '../utils/format'\nimport type { SessionPayload } from '../ai/types'\nimport { buildExcludedSet } from '../summary/exclusions'\nimport { formatSessionLabel } from '../summary/heading'\nimport { createSummaryWriter } from '../summary/writer'\nimport { printLegacyAiNotice } from './notices'\nimport { postSummary } from '../summary/webhook'\nimport { renderDay, type RenderedDay } from '../summary/day'\nimport { keepsStdoutClean } from '../summary/output-format'\nimport { toPlainText } from '../summary/plain-text'\nimport { printExcludedFooter, printSavedNote } from './day-notes'\nimport { loadDayThreads, requireThreadData } from './thread-day'\nimport { addSummaryFormatOption, addWeekLayoutOption, parseSummaryFormat, runWeekCommand } from './week'\nimport { fail } from './fail'\n\ninterface SessionSummaryJson {\n project: string | null\n branch: string | null\n /** The thread's span, first event to last */\n durationMinutes: number\n /** The thread's own active time */\n activeMinutes: number | null\n /** The other projects worked in its block */\n alongside: string[]\n bullets: string[]\n}\n\n/** Parse bullet output into a clean string array. Handles both - and • prefixes. */\nexport function parseBullets(text: string): string[] {\n return text\n .split('\\n')\n .map((line) => line.replace(/^[•\\-]\\s*/, '').trim())\n .filter(Boolean)\n}\n\nfunction stripHeading(summary: string): string {\n return summary.replace(/^## .+\\n/, '')\n}\n\nfunction toJsonOutput(day: RenderedDay, date: string): string {\n const sessions: SessionSummaryJson[] = day.sessionPayloads.map((sessionPayload, index) => ({\n project: sessionPayload.project,\n branch: sessionPayload.branch,\n durationMinutes: sessionPayload.durationMinutes,\n activeMinutes: sessionPayload.activeMinutes ?? null,\n alongside: sessionPayload.alongside ?? [],\n bullets: parseBullets(stripHeading(day.summaries[index])),\n }))\n return JSON.stringify({ date, activeMinutes: day.activeMinutes, sessions }, null, 2)\n}\n\nfunction printCapturedEvents(sessionPayloads: SessionPayload[], excluded: Set<string>): void {\n log.plain('')\n log.plain(chalk.bold(' Captured events (--verbose):'))\n for (const sessionPayload of sessionPayloads) {\n log.plain(chalk.dim(`\\n ${formatSessionLabel(sessionPayload)}`))\n for (const event of sessionPayload.events) {\n const tag = excluded.has(event.category) ? chalk.dim(` [excluded] ${event.category}`) : ` ${event.category}`\n const hint = event.hint ? chalk.dim(` [${event.hint}]`) : ''\n const meta = Object.keys(event.metadata).length > 0 ? chalk.dim(` ${JSON.stringify(event.metadata)}`) : ''\n log.plain(` ${tag}${hint}${meta}`)\n }\n }\n log.plain('')\n}\n\nexport function registerSummary(program: Command): void {\n const summaryCommand = program\n .command('summary')\n .description('Generate a work summary')\n .option('--date <YYYY-MM-DD>', 'date to summarize (default: today)')\n .option('--week', 'summarize the current week')\n .option('--fresh', 'ignore saved summaries and render the template')\n .option('--verbose', 'show all captured events, including those left out of the summary')\n .option('--slack', 'post summary to Slack webhook from config')\n .option('--webhook <url>', 'post summary to a custom webhook URL')\n addSummaryFormatOption(addWeekLayoutOption(summaryCommand)).action(async (options) => {\n if (options.by !== undefined && !options.week) fail('--by only applies to --week')\n const formatOption = parseSummaryFormat(options.format)\n // ── Weekly branch: the same handler as `workscribe week` ──────────────────\n if (options.week) {\n await runWeekCommand(options)\n return\n }\n\n const config = readConfig()\n printLegacyAiNotice(config)\n const provider = createSummaryWriter(config)\n\n requireThreadData()\n\n // ── Daily branch ─────────────────────────────────────────────────────────\n let date = todayDate()\n if (options.date) {\n try {\n date = parseDate(options.date)\n } catch (error) {\n log.error((error as Error).message)\n process.exit(1)\n }\n }\n\n const blocks = loadDayThreads(date)\n\n if (blocks.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n const format = formatOption ?? config.summary?.format\n const keepsStdoutToOutput = keepsStdoutClean(format)\n let day: RenderedDay\n try {\n day = await renderDay({ date, blocks, config, provider, fresh: options.fresh })\n } catch (error) {\n log.error(`Summary failed: ${(error as Error).message}`)\n process.exit(1)\n }\n const { sessionPayloads, saved, fullSummary, needsGeneration } = day\n const excluded = buildExcludedSet(config.summary?.includeCategories ?? [])\n if (needsGeneration && options.verbose) {\n if (keepsStdoutToOutput) log.notice(`--verbose is skipped with the ${format} format, to keep the output clean`)\n else printCapturedEvents(sessionPayloads, excluded)\n }\n\n if (format === 'json') {\n // Only the JSON on stdout, so it can be piped straight into another tool\n log.plain(toJsonOutput(day, date))\n } else if (format === 'text') {\n // Only the text on stdout, so it can be pasted with nothing to clean up\n log.plain(toPlainText(fullSummary))\n } else {\n log.plain('')\n log.plain(fullSummary)\n log.plain('')\n printSavedNote(saved)\n if (needsGeneration) printExcludedFooter(sessionPayloads, excluded)\n }\n\n await postSummary({ options, config, summary: fullSummary, statusToStderr: keepsStdoutToOutput })\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate } from '../utils/format'\nimport { EXPORTS_DIR } from '../utils/paths'\nimport { renderDay, type RenderedDay } from '../summary/day'\nimport { buildExcludedSet } from '../summary/exclusions'\nimport { printExcludedFooter, printSavedNote } from './day-notes'\nimport { createSummaryWriter } from '../summary/writer'\nimport { printLegacyAiNotice } from './notices'\nimport { loadDayThreads, requireThreadData } from './thread-day'\n\nexport function registerExport(program: Command): void {\n program\n .command('export')\n .description('Export work summary as a markdown file')\n .option('--date <YYYY-MM-DD>', 'date to export (default: today)')\n .option('--output <path>', 'output file path (default: ~/.workscribe/exports/)')\n .option('--fresh', 'ignore saved summaries and render the template')\n .action(async (options) => {\n const config = readConfig()\n printLegacyAiNotice(config)\n\n let date = todayDate()\n if (options.date) {\n try {\n date = parseDate(options.date)\n } catch (error) {\n log.error((error as Error).message)\n process.exit(1)\n }\n }\n\n requireThreadData()\n const blocks = loadDayThreads(date)\n if (blocks.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n // The same day `workscribe summary` shows, saved summaries included — always as markdown, whatever summary.format\n let day: RenderedDay\n try {\n day = await renderDay({ date, blocks, config, provider: createSummaryWriter(config), fresh: options.fresh })\n } catch (error) {\n log.error(`Export failed: ${(error as Error).message}`)\n process.exit(1)\n }\n\n const outputPath = options.output ?? path.join(EXPORTS_DIR, `workscribe-${date}.md`)\n\n if (!fs.existsSync(EXPORTS_DIR)) fs.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 0o700 })\n\n fs.writeFileSync(outputPath, `# Work Summary — ${date}\\n\\n${day.fullSummary}\\n`, {\n encoding: 'utf-8',\n mode: 0o600,\n })\n log.success(`Saved: ${outputPath}`)\n // Notes about the summary stay on the console, out of the file\n printSavedNote(day.saved)\n if (day.needsGeneration) {\n printExcludedFooter(day.sessionPayloads, buildExcludedSet(config.summary?.includeCategories ?? []))\n }\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { readConfig, writeConfig, getConfigValue, setConfigValue } from '../config'\nimport { REMOVED_AI_SETTING_MESSAGE, isRemovedAiSetting } from '../summary/legacy-ai'\nimport { log } from '../utils/logger'\nimport { getDb } from '../db'\nimport { requestTicketRelink } from '../db/jobs/ticket-refs'\nimport { ticketPatternProblem } from '../capture/tickets'\n\nconst TICKET_PATTERN_KEY = 'tickets.pattern'\n\n/**\n * Sets the ticket pattern: the database first — the pattern the history is linked with, and a restart of the\n * `ticket-refs` job — so a locked database refuses before config.json changes. Stored as the string it is: going\n * through setConfigValue would turn a pattern like `123` into a number.\n */\nfunction setTicketPattern(pattern: string): void {\n const problem = ticketPatternProblem(pattern)\n if (problem) {\n log.error(`That ticket pattern can't be used: ${problem}.`)\n process.exit(1)\n }\n requestTicketRelink(getDb(), pattern)\n const config = readConfig()\n writeConfig({ ...config, tickets: { ...config.tickets, pattern } })\n log.success(`Set ${chalk.cyan(TICKET_PATTERN_KEY)} = ${chalk.yellow(pattern)}`)\n log.plain(\n chalk.dim(\n ' New captures use it now; your history is re-linked the next time a data command runs (or: workscribe upgrade).'\n )\n )\n}\n\nexport function registerConfig(program: Command): void {\n const configCmd = program\n .command('config')\n .description('View or update Workscribe configuration')\n .action(() => {\n const config = readConfig()\n log.plain('')\n log.plain(chalk.bold(' Workscribe Config'))\n log.plain('')\n printConfig(config, ' ')\n log.plain('')\n })\n\n configCmd\n .command('set <key> <value>')\n .description('Set a config value (e.g. workscribe config set session.idleTimeout 45)')\n .action((key: string, value: string) => {\n // Checked before reading or writing, so a refused key never touches config.json\n if (isRemovedAiSetting(key)) {\n log.error(REMOVED_AI_SETTING_MESSAGE)\n process.exit(1)\n }\n if (key === TICKET_PATTERN_KEY) {\n setTicketPattern(value)\n return\n }\n const config = readConfig()\n const updated = setConfigValue(config, key, value)\n writeConfig(updated)\n const isSensitive =\n key.toLowerCase().includes('key') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')\n const displayValue = isSensitive ? '[set]' : chalk.yellow(value)\n log.success(`Set ${chalk.cyan(key)} = ${displayValue}`)\n })\n\n configCmd\n .command('get <key>')\n .description('Get a config value')\n .action((key: string) => {\n const config = readConfig()\n const value = getConfigValue(config, key)\n if (value === undefined) {\n log.warn(`Key not found: ${key}`)\n } else {\n const lastSegment = key.split('.').pop() ?? ''\n if (SENSITIVE_KEYS.has(lastSegment) && typeof value === 'string' && value.length > 0) {\n log.plain(`${value.slice(0, 4)}${'*'.repeat(8)} (${value.length} chars)`)\n } else {\n log.plain(String(value))\n }\n }\n })\n}\n\nconst SENSITIVE_KEYS = new Set([\n 'apiKey',\n 'api_key',\n 'secret',\n 'token',\n 'password',\n 'passwd',\n 'webhook',\n 'slackWebhook',\n])\n\nfunction maskValue(key: string, value: unknown): string {\n if (SENSITIVE_KEYS.has(key) && typeof value === 'string' && value.length > 0) {\n return chalk.dim(`${value.slice(0, 4)}${'*'.repeat(8)} (${value.length} chars)`)\n }\n if (Array.isArray(value)) return `[${(value as unknown[]).join(', ')}]`\n return String(value) || chalk.dim('(not set)')\n}\n\nfunction printConfig(obj: unknown, indent: string): void {\n if (typeof obj !== 'object' || obj === null) return\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n log.plain(`${indent}${chalk.bold(key)}:`)\n printConfig(value, indent + ' ')\n } else {\n log.plain(`${indent}${chalk.dim(key)}: ${chalk.yellow(maskValue(key, value))}`)\n }\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { log } from '../utils/logger'\n\nexport function registerPause(program: Command): void {\n program\n .command('pause')\n .description('Pause capture temporarily')\n .action(() => {\n if (fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is already paused. Run workscribe resume to restart.')\n return\n }\n fs.writeFileSync(PAUSE_FLAG_PATH, '', { mode: 0o600 })\n log.success('Capture paused. Run workscribe resume to restart.')\n })\n\n program\n .command('resume')\n .description('Resume capture after a pause')\n .action(() => {\n if (!fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is not paused.')\n return\n }\n fs.rmSync(PAUSE_FLAG_PATH)\n log.success('Capture resumed.')\n })\n}\n","import { Command } from 'commander'\nimport { getLastEventIds, getEventsByIds, deleteEventsByIds } from '../db'\nimport { log } from '../utils/logger'\n\nexport function registerUndo(program: Command): void {\n program\n .command('undo')\n .description('Remove the last N captured events')\n .option('-n, --count <number>', 'number of events to remove', '1')\n .option('--dry-run', 'preview what would be deleted without deleting')\n .action((opts) => {\n const count = parseInt(opts.count, 10)\n if (isNaN(count) || count < 1) {\n log.error('--count must be a positive integer')\n process.exit(1)\n }\n\n const ids = getLastEventIds(count)\n if (ids.length === 0) {\n log.plain('No events to undo.')\n return\n }\n\n const events = getEventsByIds(ids)\n log.plain('')\n log.plain(`Events to remove (${events.length}):`)\n for (const e of events) {\n log.plain(` ${e.captured_at} ${e.category} ${e.command}`)\n }\n log.plain('')\n\n if (opts.dryRun) {\n log.plain('Dry run — nothing deleted.')\n return\n }\n\n deleteEventsByIds(ids)\n log.success(`Removed ${ids.length} event${ids.length > 1 ? 's' : ''}.`)\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport prompts from 'prompts'\nimport { getEventsForDate, deleteEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate } from '../utils/format'\n\nexport function registerClear(program: Command): void {\n program\n .command('clear')\n .description('Delete all captured events for a date')\n .option('--date <YYYY-MM-DD>', 'date to clear (default: today)')\n .option('--dry-run', 'preview what would be deleted without deleting')\n .option('--yes', 'skip confirmation prompt')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n\n if (events.length === 0) {\n log.plain(`No events found for ${date}.`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n // Show a compact summary by category\n const counts = new Map<string, number>()\n for (const e of events) counts.set(e.category, (counts.get(e.category) ?? 0) + 1)\n for (const [cat, n] of counts) {\n log.plain(` ${chalk.dim(String(n).padStart(4))} ${cat}`)\n }\n log.plain('')\n\n if (opts.dryRun) {\n log.plain(chalk.dim(' Dry run — nothing deleted.'))\n log.plain('')\n return\n }\n\n if (!opts.yes) {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `Delete all ${events.length} events for ${date}?`,\n initial: false,\n })\n if (!confirm) {\n log.plain('Cancelled.')\n return\n }\n }\n\n const deleted = deleteEventsForDate(date)\n log.success(`Cleared ${deleted} event${deleted !== 1 ? 's' : ''} for ${date}.`)\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport prompts from 'prompts'\nimport chalk from 'chalk'\nimport { WORKSCRIBE_DIR, REQUIRED_DIRS } from '../utils/paths'\nimport {\n isHookInstalled,\n removeChronicleHook,\n removeCompletions,\n removeFishCompletions,\n removeHook,\n} from '../capture/shell-hook'\nimport { claudeCodeIntegration } from '../capture/hooks/claude-code'\nimport { log } from '../utils/logger'\nimport { SKILL_AGENTS } from '../skills/agents'\nimport { removeSkills } from '../skills/actions'\n\n// The rc files `init` may have added a completions block to — checked on their own, since a file can have the\n// completions block without the hook block\nfunction completionRcFiles(): string[] {\n return ['.zshrc', '.bashrc', '.bash_profile'].map((file) => path.join(os.homedir(), file))\n}\n\nconst CHRONICLE_RC_FILES = ['.zshrc', '.bashrc']\n\n/** Removes everything else `init` adds to your shell and tools: completions and the Claude Code session hook. */\nfunction removeCompletionsAndToolHooks(): void {\n for (const rcFile of completionRcFiles()) {\n if (removeCompletions(rcFile)) log.success(`Shell completions removed from ${rcFile}`)\n }\n // 0.2.1's `chronicle init` wrote only these two\n for (const rcFile of CHRONICLE_RC_FILES.map((file) => path.join(os.homedir(), file))) {\n if (removeChronicleHook(rcFile)) log.success(`Old Chronicle hook removed from ${rcFile}`)\n }\n if (removeFishCompletions()) log.success('Fish completions removed')\n\n // Not only when Claude Code is on PATH — someone who removed Claude Code can still have the hook in its settings\n const outcome = claudeCodeIntegration.uninstall?.()\n if (outcome === 'removed') log.success('Claude Code session hook removed from ~/.claude/settings.json')\n if (outcome === 'unreadable') {\n log.warn(\n \"Couldn't read ~/.claude/settings.json, so the Claude Code session hook was left in place. Fix the file's JSON, then run: workscribe uninstall --hooks-only\"\n )\n }\n}\n\nfunction detectShellRcFiles(): { name: string; rcFile: string }[] {\n const candidates = [\n { name: 'zsh', rcFile: path.join(os.homedir(), '.zshrc') },\n { name: 'bash', rcFile: path.join(os.homedir(), '.bashrc') },\n { name: 'bash', rcFile: path.join(os.homedir(), '.bash_profile') },\n { name: 'fish', rcFile: path.join(os.homedir(), '.config', 'fish', 'conf.d', 'workscribe.fish') },\n ]\n return candidates.filter((candidate) => fs.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile))\n}\n\nexport function registerUninstall(program: Command): void {\n program\n .command('uninstall')\n .description(\n 'Remove shell hooks, completions, the Claude Code hook and the agent skill; optionally delete all data'\n )\n .option(\n '--hooks-only',\n 'remove shell hooks, completions and the Claude Code hook only — keep the agent skill and skip the data prompt'\n )\n .action(async (options) => {\n log.plain(chalk.bold('\\n Workscribe — Uninstall\\n'))\n\n // 1. Remove shell hooks from any rc files that have them\n const hooked = detectShellRcFiles()\n if (hooked.length === 0) {\n log.info('No shell hooks found')\n } else {\n for (const { name, rcFile } of hooked) {\n if (name === 'fish') {\n // The conf.d file is ours entirely — delete it rather than leaving an empty file\n fs.unlinkSync(rcFile)\n log.success(`Shell hook removed: ${rcFile}`)\n log.plain(` ${chalk.dim('Restart your terminal to take effect')}`)\n } else {\n removeHook(rcFile)\n log.success(`Shell hook removed from ${rcFile}`)\n log.plain(` ${chalk.dim(`Run: source ${rcFile}`)} (or restart your terminal to take effect)`)\n log.plain(\n ` ${chalk.dim(`${name === 'zsh' ? 'unfunction' : 'unset -f'} _workscribe_preexec _workscribe_precmd`)}`\n )\n }\n }\n }\n\n removeCompletionsAndToolHooks()\n\n // Agent skills are only removed on a full uninstall: `--hooks-only` is for clearing what runs in your\n // shell and tools, and the skill is something you chose to install into your coding agents\n if (options.hooksOnly) return\n\n removeSkills(SKILL_AGENTS, { reportMissing: false })\n\n // 2. Optionally delete all Workscribe data directories\n const existingDirs = [...new Set(REQUIRED_DIRS)].filter((dir) => fs.existsSync(dir))\n\n if (existingDirs.length > 0) {\n log.plain('')\n const { deleteData } = await prompts({\n type: 'confirm',\n name: 'deleteData',\n message: `Delete all Workscribe data in ${WORKSCRIBE_DIR}? (database, config, exports, backups)`,\n initial: false,\n })\n\n if (deleteData) {\n for (const dir of existingDirs) {\n fs.rmSync(dir, { recursive: true, force: true })\n log.success(`Deleted ${dir}`)\n }\n } else {\n log.info(`Data kept at ${WORKSCRIBE_DIR}`)\n }\n }\n\n log.plain('')\n log.plain(` ${chalk.dim('npm uninstall -g @workscribe/cli')} — then remove the CLI itself`)\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { readConfig, writeConfig } from '../config'\nimport { PROFILES_DIR } from '../utils/paths'\nimport { getCachedProfiles, detectProfilesForDirectory } from '../profiles/loader'\nimport { installProfile, cacheManifest, readCachedManifest } from '../profiles/fetcher'\nimport { getOtherEventToolCounts } from '../db'\nimport { log } from '../utils/logger'\n\nconst MIN_SUGGEST_COUNT = 5\n\nexport function registerProfile(program: Command): void {\n const profile = program.command('profile').description('Manage stack profiles for better command tracking')\n\n // ─── list ─────────────────────────────────────────────────────────────────\n profile\n .command('list')\n .description('List available profiles from the registry')\n .action(async () => {\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n let manifest = readCachedManifest()\n let offline = false\n\n if (!manifest) {\n try {\n manifest = await cacheManifest()\n } catch {\n offline = true\n manifest = getCachedProfiles().map((p) => ({\n name: p.name,\n displayName: p.displayName,\n tags: [],\n description: '',\n keywords: [],\n detect: p.detect,\n }))\n }\n } else {\n // Refresh in background — best effort, don't block output\n cacheManifest().catch(() => undefined)\n }\n\n if (offline) {\n log.warn('Registry unreachable — showing installed profiles only')\n }\n\n if (manifest.length === 0) {\n log.plain('No profiles available. Check your connection and try again.')\n return\n }\n\n // Group by tag\n const grouped = new Map<string, typeof manifest>()\n for (const entry of manifest) {\n const tag = entry.tags[0] ?? 'other'\n if (!grouped.has(tag)) grouped.set(tag, [])\n grouped.get(tag)!.push(entry)\n }\n\n log.plain('')\n for (const [tag, entries] of grouped) {\n log.plain(chalk.bold(` ${tag.charAt(0).toUpperCase() + tag.slice(1)} profiles`))\n log.plain(chalk.dim(' ' + '─'.repeat(60)))\n for (const entry of entries) {\n const badge = activeNames.has(entry.name) ? chalk.green(' [active]') : ''\n const installed = fs.existsSync(path.join(PROFILES_DIR, `${entry.name}.json`)) ? chalk.dim(' installed') : ''\n log.plain(` ${chalk.cyan(entry.name.padEnd(18))} ${entry.displayName.padEnd(28)}${badge}${installed}`)\n if (entry.description) log.plain(` ${chalk.dim(' '.repeat(18) + entry.description)}`)\n }\n log.plain('')\n }\n })\n\n // ─── add ──────────────────────────────────────────────────────────────────\n profile\n .command('add <names...>')\n .description('Install one or more profiles from the registry')\n .action(async (names: string[]) => {\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n for (const name of names) {\n if (activeNames.has(name)) {\n log.info(`Profile \"${name}\" is already active`)\n continue\n }\n try {\n log.plain(`Fetching ${chalk.cyan(name)}…`)\n await installProfile(name)\n activeNames.add(name)\n log.success(`Profile \"${name}\" installed`)\n } catch (err) {\n log.error(`Failed to install \"${name}\": ${(err as Error).message}`)\n }\n }\n\n config.capture.profiles = [...activeNames]\n writeConfig(config)\n })\n\n // ─── remove ───────────────────────────────────────────────────────────────\n profile\n .command('remove <names...>')\n .description('Uninstall one or more profiles')\n .action((names: string[]) => {\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n for (const name of names) {\n if (!activeNames.has(name)) {\n log.warn(`Profile \"${name}\" is not active`)\n continue\n }\n activeNames.delete(name)\n const cachePath = path.join(PROFILES_DIR, `${name}.json`)\n if (fs.existsSync(cachePath)) fs.unlinkSync(cachePath)\n log.success(`Profile \"${name}\" removed`)\n }\n\n config.capture.profiles = [...activeNames]\n writeConfig(config)\n })\n\n // ─── update ───────────────────────────────────────────────────────────────\n profile\n .command('update [name]')\n .description('Re-fetch installed profiles from the registry')\n .action(async (name?: string) => {\n const config = readConfig()\n const toUpdate = name ? [name] : config.capture.profiles\n\n if (toUpdate.length === 0) {\n log.plain('No profiles installed. Run workscribe profile add <name> first.')\n return\n }\n\n for (const profileName of toUpdate) {\n try {\n log.plain(`Updating ${chalk.cyan(profileName)}…`)\n await installProfile(profileName)\n log.success(`${profileName} updated`)\n } catch (err) {\n log.error(`Failed to update \"${profileName}\": ${(err as Error).message}`)\n }\n }\n })\n\n // ─── detect ───────────────────────────────────────────────────────────────\n profile\n .command('detect')\n .description('Scan the current directory and suggest matching profiles')\n .action(() => {\n const cwd = process.cwd()\n const matches = detectProfilesForDirectory(cwd)\n\n if (matches.length === 0) {\n const cached = readCachedManifest()\n if (!cached) {\n log.plain('No manifest cached. Run workscribe profile list to fetch it first.')\n } else {\n log.plain(`No profiles detected for ${chalk.dim(cwd)}`)\n log.plain(chalk.dim('Run workscribe profile list to browse available profiles.'))\n }\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(' Profiles detected for this directory:'))\n log.plain('')\n for (const entry of matches) {\n const files = entry.detect?.files?.join(', ') ?? ''\n log.plain(` ${chalk.cyan(entry.name.padEnd(18))} ${entry.displayName} ${chalk.dim('(' + files + ')')}`)\n }\n log.plain('')\n log.plain(` Run: ${chalk.cyan('workscribe profile add ' + matches.map((e) => e.name).join(' '))}`)\n log.plain('')\n })\n\n // ─── suggest ──────────────────────────────────────────────────────────────\n profile\n .command('suggest')\n .description('Analyse recent unclassified commands and suggest profiles')\n .option('--days <n>', 'number of days to analyse', '30')\n .action((opts) => {\n const days = Math.max(1, parseInt(opts.days as string, 10) || 30)\n const toolCounts = getOtherEventToolCounts(days)\n\n if (toolCounts.length === 0) {\n log.plain('No unclassified commands found in the last ' + days + ' days.')\n return\n }\n\n const manifest = readCachedManifest()\n if (!manifest) {\n log.plain('No manifest cached. Run workscribe profile list first.')\n return\n }\n\n const config = readConfig()\n const activeNames = new Set(config.capture.profiles)\n\n // Match tool counts against manifest keywords\n const suggestions: { entry: (typeof manifest)[number]; count: number }[] = []\n for (const entry of manifest) {\n if (activeNames.has(entry.name)) continue\n const matchedCount = toolCounts\n .filter((toolCount) =>\n entry.keywords.some((keyword) => toolCount.tool.toLowerCase().includes(keyword.toLowerCase()))\n )\n .reduce((total, toolCount) => total + toolCount.count, 0)\n if (matchedCount >= MIN_SUGGEST_COUNT) {\n suggestions.push({ entry, count: matchedCount })\n }\n }\n\n suggestions.sort((a, b) => b.count - a.count)\n\n if (suggestions.length === 0) {\n const activeEntries = manifest.filter((entry) => activeNames.has(entry.name))\n const isToolCoveredByActiveProfile = (tool: string) =>\n activeEntries.some((entry) =>\n entry.keywords.some((keyword) => tool.toLowerCase().includes(keyword.toLowerCase()))\n )\n const uncoveredTools = toolCounts.filter((toolCount) => !isToolCoveredByActiveProfile(toolCount.tool))\n\n if (uncoveredTools.length === 0) {\n log.plain('')\n log.plain('Your active profiles cover all unclassified commands.')\n log.plain(chalk.dim(' Future captures will be classified correctly.'))\n log.plain('')\n } else {\n log.plain('')\n log.plain('No profile matches found for your unclassified commands.')\n log.plain('')\n log.plain(chalk.dim(' Top unclassified tools:'))\n for (const { tool, count } of uncoveredTools.slice(0, 5)) {\n log.plain(chalk.dim(` ${tool.padEnd(24)} ${count} event${count === 1 ? '' : 's'}`))\n }\n log.plain('')\n log.plain(chalk.dim(' Run workscribe events --category other to review them.'))\n log.plain('')\n }\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Analysed last ${days} days of unclassified commands:`))\n log.plain('')\n for (const { entry, count } of suggestions) {\n log.plain(\n ` ${chalk.cyan(entry.name.padEnd(18))} ${String(count).padStart(4)} commands → ${entry.displayName}`\n )\n }\n log.plain('')\n log.plain(\n ` Run: ${chalk.cyan('workscribe profile add ' + suggestions.map((suggestion) => suggestion.entry.name).join(' '))}`\n )\n log.plain('')\n\n const allCoveredEntries = [\n ...manifest.filter((entry) => activeNames.has(entry.name)),\n ...suggestions.map((suggestion) => suggestion.entry),\n ]\n const isToolCoveredAfterInstall = (tool: string) =>\n allCoveredEntries.some((entry) =>\n entry.keywords.some((keyword) => tool.toLowerCase().includes(keyword.toLowerCase()))\n )\n const stillUncovered = toolCounts.filter((toolCount) => !isToolCoveredAfterInstall(toolCount.tool)).slice(0, 5)\n if (stillUncovered.length > 0) {\n log.plain(chalk.dim(' Still uncovered after installing:'))\n for (const { tool, count } of stillUncovered) {\n log.plain(chalk.dim(` ${tool.padEnd(24)} ${count} event${count === 1 ? '' : 's'}`))\n }\n log.plain('')\n }\n })\n}\n","import { Command } from 'commander'\nimport { insertEvent } from '../db'\nimport { getRepoContext } from '../capture/git'\nimport { eventDimensions } from '../capture/dimensions'\nimport { redact } from '../capture/redact'\nimport { readConfig } from '../config'\nimport { currentDirectory } from '../utils/working-directory'\nimport { log } from '../utils/logger'\n\nexport function registerNote(program: Command): void {\n program\n .command('note <text>')\n .description('Attach a free-text annotation to the current session')\n .action((text: string) => {\n // As you reached it, like the shell hook's `$PWD`, so a `projects.<path>` override and `context` match it\n const directory = currentDirectory()\n const config = readConfig()\n const context = getRepoContext(directory, config)\n const { repoName, branch } = context\n insertEvent({\n captured_at: new Date().toISOString(),\n command: 'workscribe note',\n cwd: directory,\n exit_code: null,\n duration_ms: null,\n repo_name: repoName,\n branch,\n category: 'note',\n metadata: JSON.stringify({ text: redact(text, config.redact.extra) }),\n source: null,\n file_path: null,\n dimensions: eventDimensions(context),\n })\n log.success(`Note saved${repoName ? ` to ${repoName}` : ''}`)\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { WORKSCRIBE_DIR, DEBUG_LOG_PATH, CONFIG_PATH } from '../utils/paths'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\nconst GITHUB_ISSUES_URL = 'https://github.com/workscribe/workscribe/issues/new'\nconst DEBUG_LOG_TAIL_LINES = 100\n\nfunction tailFile(filePath: string, lines: number): string {\n if (!fs.existsSync(filePath)) return '(no debug log found — set WORKSCRIBE_DEBUG=1 to enable)'\n const content = fs.readFileSync(filePath, 'utf-8')\n const allLines = content.split('\\n')\n return allLines.slice(-lines).join('\\n')\n}\n\nfunction redactConfig(): string {\n try {\n const config = readConfig()\n if (config.ai?.apiKey) config.ai.apiKey = '<redacted>' // left over from a 1.x config\n if (config.integrations?.slackWebhook) config.integrations.slackWebhook = '<redacted>'\n if (config.integrations?.webhook) config.integrations.webhook = '<redacted>'\n return JSON.stringify(config, null, 2)\n } catch {\n return '(could not read config)'\n }\n}\n\nexport function registerBugReport(program: Command): void {\n program\n .command('bug-report')\n .description('Generate a diagnostic report for filing a bug')\n .action(() => {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-')\n const reportPath = path.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`)\n\n const systemInfo = [\n `Workscribe version : ${__WORKSCRIBE_VERSION__}`,\n `Node.js version : ${process.version}`,\n `Platform : ${process.platform}`,\n `Architecture : ${process.arch}`,\n `OS release : ${os.release()}`,\n `Config path : ${CONFIG_PATH}`,\n `Data directory : ${WORKSCRIBE_DIR}`,\n ].join('\\n')\n\n const report = [\n '=== Workscribe Bug Report ===',\n `Generated: ${new Date().toISOString()}`,\n '',\n '--- System ---',\n systemInfo,\n '',\n '--- Config (secrets redacted) ---',\n redactConfig(),\n '',\n `--- Debug log (last ${DEBUG_LOG_TAIL_LINES} lines) ---`,\n tailFile(DEBUG_LOG_PATH, DEBUG_LOG_TAIL_LINES),\n ].join('\\n')\n\n try {\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 0o700 })\n }\n fs.writeFileSync(reportPath, report, { mode: 0o600 })\n } catch (error) {\n log.error(`Could not write report: ${(error as Error).message}`)\n process.exit(1)\n }\n\n const params = new URLSearchParams({\n title: `Bug report — workscribe ${__WORKSCRIBE_VERSION__}`,\n body: `<!-- Attach ${path.basename(reportPath)} or paste the relevant sections below -->\\n\\n**Describe the issue:**\\n\\n**Steps to reproduce:**\\n\\n**Expected behaviour:**`,\n })\n\n log.success(`Report written to: ${chalk.cyan(reportPath)}`)\n log.plain('')\n log.plain(` Open a GitHub issue:`)\n log.plain(` ${chalk.cyan(`${GITHUB_ISSUES_URL}?${params}`)}`)\n log.plain('')\n log.plain(chalk.dim(` Attach the report or paste the relevant sections into the issue.`))\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport type { JobRunResult, RunResult } from '../db/jobs/runner'\nimport { getDataStatus, runDataJobs } from './data-jobs'\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\nfunction printVerification(result: JobRunResult): void {\n const { verification } = result\n if (!verification) return\n const counts = `checked ${verification.checked}, resolved ${verification.resolved}, unresolved ${verification.unresolved}`\n const line = `${result.name} (v${result.version}) — ${counts}`\n if (verification.hasPassed) log.success(line)\n else log.error(`${line} — check failed`)\n for (const note of verification.notes) log.plain(` ${chalk.dim(note)}`)\n}\n\nfunction printResult(result: JobRunResult): void {\n if (result.outcome === 'failed') {\n log.error(`${result.name} failed: ${result.error ?? 'unknown error'} — run \\`workscribe upgrade\\` again to retry`)\n return\n }\n if (result.outcome === 'paused') {\n const reason = result.error ?? 'another Workscribe process is using the database — run `workscribe upgrade` again'\n log.warn(`${result.name} paused: ${reason}`)\n return\n }\n printVerification(result)\n}\n\nfunction hasSucceeded(result: JobRunResult): boolean {\n return result.outcome === 'done' && result.verification?.hasPassed !== false\n}\n\n/** A check that failed on an earlier run stays failed: rerunning can't bring missing data back, the backup can. */\nfunction reportFailedChecks(failedChecks: string[], latestBackup: string | null): void {\n log.error(`The check after upgrading ${failedChecks.join(', ')} didn't pass — some data may not have carried over.`)\n if (latestBackup) log.plain(chalk.dim(` Your data from before the upgrade is in ${latestBackup}`))\n log.plain(chalk.dim(' Run `workscribe bug-report` so it can be fixed.'))\n}\n\n/** Prints what the run did and returns whether the data is now fully up to date, with every check passing. */\nfunction reportRun(result: RunResult): boolean {\n if (result.backup === 'busy') {\n log.warn(\n 'Another Workscribe command is backing up your data right now. Run `workscribe upgrade` again in a moment.'\n )\n return false\n }\n if (result.backup === 'unavailable') {\n log.error(\"Couldn't back up your data, so nothing was changed. Run `workscribe upgrade` again to retry.\")\n return false\n }\n\n result.jobs.forEach(printResult)\n const { latestBackup, pending, failedChecks } = getDataStatus()\n if (failedChecks.length > 0) {\n reportFailedChecks(failedChecks, latestBackup)\n return false\n }\n if (result.backup === 'not-needed') {\n log.success('Your Workscribe data is up to date.')\n return true\n }\n if (latestBackup) log.plain(chalk.dim(` Backup: ${latestBackup}`))\n return pending.length === 0 && result.jobs.every(hasSucceeded)\n}\n\nexport function registerUpgrade(program: Command): void {\n program\n .command('upgrade')\n .description('Bring your stored data up to date after updating Workscribe (backs it up first)')\n .action(() => {\n const announced = new Set<string>()\n const result = runDataJobs(__WORKSCRIBE_VERSION__, {\n onProgress: ({ job }) => {\n if (announced.has(job.name)) return\n announced.add(job.name)\n log.info(`Upgrading: ${job.description}`)\n },\n })\n if (!reportRun(result)) process.exit(1)\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getPendingAlerts, clearAlerts, hasPendingAlerts } from '../db/alerts'\nimport { log } from '../utils/logger'\n\nfunction formatTimestamp(ms: number): string {\n return new Date(ms).toLocaleString([], {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })\n}\n\nexport function registerAlerts(program: Command): void {\n const alertsCmd = program\n .command('alerts')\n .description('Show and manage security alerts from AI session monitoring')\n .option('--check', 'Print warning if alerts are pending (used by shell hooks)')\n .action((opts: { check?: boolean }) => {\n if (opts.check) {\n if (hasPendingAlerts()) {\n log.plain(\n chalk.yellow(' [workscribe] Possible credentials detected in an AI session.') +\n chalk.dim(' Run workscribe alerts for details.')\n )\n }\n return\n }\n\n const alerts = getPendingAlerts()\n if (alerts.length === 0) {\n log.plain('')\n log.plain(' No pending security alerts.')\n log.plain('')\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(' Security Alerts') + chalk.dim(` (${alerts.length} pending)`))\n log.plain('')\n\n for (const alert of alerts) {\n log.plain(` ${chalk.yellow('!')} ${chalk.dim(formatTimestamp(alert.created_at))} ${chalk.cyan(alert.tool)}`)\n log.plain(` ${alert.context}`)\n if (alert.session_id) log.plain(` ${chalk.dim('Session: ' + alert.session_id)}`)\n log.plain('')\n }\n\n log.plain(chalk.dim(' Run workscribe alerts clear to dismiss all alerts'))\n log.plain('')\n })\n\n alertsCmd\n .command('clear')\n .description('Dismiss all pending security alerts')\n .action(() => {\n const count = clearAlerts()\n if (count === 0) {\n log.plain(' No pending alerts to clear.')\n } else {\n log.plain(` ${count} alert${count === 1 ? '' : 's'} cleared.`)\n }\n })\n}\n","// What `context --file --json` says about the file itself — for a tooltip about the file rather than the whole\n// session. Pure: the session's events, the commits and how to display a path are passed in.\nimport path from 'path'\nimport type { EventRow } from '../db/types'\nimport type { GitCommit } from './git-commits'\n\n// How many of the session's other files to name; the rest are only counted\nconst EDITED_ALONGSIDE_LIMIT = 5\nconst CODE_EDIT = 'code_edit'\n\nexport interface FileDetails {\n path: string\n relativePath: string\n firstSavedAt: string\n lastSavedAt: string\n /** Saves recorded — capture keeps one save per file per 30 s, so not every keystroke save */\n saveCount: number\n branch: string | null\n commits: GitCommit[]\n editedAlongside: { files: string[]; total: number }\n}\n\n/** A file relative to its repo root when it's inside it, else its name. Pass resolved paths on both sides. */\nexport function displayPath(absoluteFilePath: string, repoRoot: string | null): string {\n if (repoRoot) {\n const relative = path.relative(repoRoot, absoluteFilePath)\n if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) return relative\n }\n return path.basename(absoluteFilePath)\n}\n\n/** Whether a stored path is the file being asked about — the same path, or (when the caller can tell) the same file. */\nexport type FileMatcher = (storedPath: string) => boolean\n\nfunction samePathAs(filePath: string): FileMatcher {\n return (storedPath) => storedPath === filePath\n}\n\n/** The session's other saved files, most-saved first, then most recently saved. */\nfunction otherFilesBySaves(sessionEvents: EventRow[], isThisFile: FileMatcher): string[] {\n const saves = new Map<string, { count: number; lastSavedAt: string }>()\n for (const event of sessionEvents) {\n if (event.category !== CODE_EDIT || !event.file_path || isThisFile(event.file_path)) continue\n const seen = saves.get(event.file_path)\n saves.set(event.file_path, {\n count: (seen?.count ?? 0) + 1,\n lastSavedAt: seen && seen.lastSavedAt > event.captured_at ? seen.lastSavedAt : event.captured_at,\n })\n }\n return [...saves.entries()]\n .sort(([, first], [, second]) => second.count - first.count || second.lastSavedAt.localeCompare(first.lastSavedAt))\n .map(([otherPath]) => otherPath)\n}\n\nexport interface FileDetailsOptions {\n filePath: string\n sessionEvents: EventRow[]\n sessionBranch: string | null\n commits: GitCommit[]\n /** How to show a stored path — relative to the repo root, resolving symlinks (see `displayPath`) */\n displayPathOf: (storedPath: string) => string\n /** Which stored paths are this file; by default only `filePath` itself */\n isThisFile?: FileMatcher\n}\n\n/** The session's saves of this file, oldest first. */\nexport function savesOf(sessionEvents: EventRow[], isThisFile: FileMatcher): EventRow[] {\n return sessionEvents.filter(\n (event) => event.category === CODE_EDIT && event.file_path !== null && isThisFile(event.file_path)\n )\n}\n\n/** The file's saves in the session, or null if it has none (it was found through a non-save event). */\nexport function buildFileDetails(options: FileDetailsOptions): FileDetails | null {\n const { filePath, sessionEvents, displayPathOf } = options\n const isThisFile = options.isThisFile ?? samePathAs(filePath)\n const saves = savesOf(sessionEvents, isThisFile)\n if (saves.length === 0) return null\n\n const savedTimes = saves.map((save) => save.captured_at).sort()\n // Saves are oldest first, so the last one with a branch is the latest\n const latestBranch = [...saves].reverse().find((save) => save.branch)?.branch ?? null\n const otherFiles = otherFilesBySaves(sessionEvents, isThisFile)\n\n return {\n path: filePath,\n relativePath: displayPathOf(filePath),\n firstSavedAt: savedTimes[0],\n lastSavedAt: savedTimes.at(-1) ?? savedTimes[0],\n saveCount: saves.length,\n branch: latestBranch ?? options.sessionBranch,\n commits: options.commits,\n editedAlongside: {\n files: otherFiles.slice(0, EDITED_ALONGSIDE_LIMIT).map(displayPathOf),\n total: otherFiles.length,\n },\n }\n}\n","// The I/O behind `context --file --json`'s file details, and the time limits the whole command answers within.\nimport type { EventRow } from '../db/types'\nimport { buildFileDetails, displayPath, savesOf, type FileDetails, type FileMatcher } from '../summary/file-details'\nimport { getFileCommits } from '../summary/git-commits'\nimport { resolvedPath } from '../utils/working-directory'\n\n// The VS Code extension gives up on `context --file` after 5 s and then shows nothing, so the whole command has to\n// answer well within that: about 1 s waiting for another process's lock, at most 2 s of git\nexport const LOCK_WAIT_MS = 1000\nexport const GIT_BUDGET_MS = 2000\n\n/** The stretch of work `context --file` answers with: the thread's events and span. */\nexport interface FileSpan {\n events: EventRow[]\n branch: string | null\n startedAt: string\n endedAt: string\n}\n\n/** What the span says about this file: its saves, branch, commits and the files edited alongside it. */\n/** The stored path is this file: the same path, or another path to the same file on disk (through a symlink). */\nfunction sameFileAs(filePath: string): FileMatcher {\n const fileOnDisk = resolvedPath(filePath)\n return (storedPath) => storedPath === filePath || resolvedPath(storedPath) === fileOnDisk\n}\n\nexport function getFileDetails(filePath: string, span: FileSpan): FileDetails | null {\n const isThisFile = sameFileAs(filePath)\n // Found through a non-save event: nothing to say about the file, so don't spend the git budget\n if (savesOf(span.events, isThisFile).length === 0) return null\n\n // git reports the resolved repo root, and the editor may have opened the file through a symlink\n const { repoRoot, commits } = getFileCommits({\n filePath: resolvedPath(filePath),\n since: span.startedAt,\n until: span.endedAt,\n budgetMs: GIT_BUDGET_MS,\n })\n return buildFileDetails({\n filePath,\n sessionEvents: span.events,\n sessionBranch: span.branch,\n commits,\n isThisFile,\n displayPathOf: (storedPath) => displayPath(resolvedPath(storedPath), repoRoot),\n })\n}\n","import type { EventRow } from '../db/types'\nimport type { StoredThread } from '../db/thread-store'\nimport type { SummaryRow } from '../db/summary-store'\nimport type { WorkBlockRow } from '../db/context-lookup'\nimport { toActiveMinutes } from '../sessions/blocks'\nimport { summariesOfThread, type BlockEvent } from './anchors'\nimport { minutesBetween } from '../sessions/grouper'\nimport { pickBestSummary, sessionKey } from './store'\n\n// What `context` says on the thread path (pure): the thread an event belongs to, in its block, with the projects\n// worked alongside it, and the summary that belongs to that thread.\n\nexport interface ThreadSpanJson {\n repoName: string | null\n startedAt: string\n endedAt: string\n eventCount: number\n activeMinutes: number\n}\n\n/** The fields every `context` answer has. */\nexport interface WorkSpan {\n repoName: string | null\n date: string\n startedAt: string\n endedAt: string\n durationMinutes: number\n branch: string | null\n eventCount: number\n}\n\nexport interface ThreadContext extends WorkSpan {\n /** The best summary stored for this thread, or null: the caller renders the template from `events` */\n savedSummary: string | null\n thread: { key: string; projectId: number | null; activeMinutes: number }\n block: { startedAt: string; endedAt: string; activeMinutes: number }\n /** The block's other threads, in the order their projects first appear */\n alongside: ThreadSpanJson[]\n}\n\nexport interface ThreadContextInput {\n block: WorkBlockRow\n threads: StoredThread[]\n blockEvents: BlockEvent[]\n thread: StoredThread\n /** The thread's own events, in grouping order */\n threadEvents: EventRow[]\n projectNames: Map<number, string | null>\n summaries: SummaryRow[]\n}\n\nfunction nameOf(thread: StoredThread, projectNames: Map<number, string | null>): string | null {\n return thread.projectId === null ? null : (projectNames.get(thread.projectId) ?? null)\n}\n\nfunction latestBranch(events: EventRow[]): string | null {\n return [...events].reverse().find((event) => event.branch)?.branch ?? null\n}\n\n/** The best stored summary that belongs to this thread, if any. */\nfunction summaryOfThread(input: ThreadContextInput): string | null {\n const block = { threads: input.threads, events: input.blockEvents }\n return pickBestSummary(summariesOfThread(input.thread.id, block, input.summaries))\n}\n\nexport function buildThreadContext(input: ThreadContextInput): ThreadContext {\n const { thread, block, projectNames } = input\n const repoName = nameOf(thread, projectNames)\n return {\n repoName,\n date: block.date,\n startedAt: thread.startedAt,\n endedAt: thread.endedAt,\n durationMinutes: minutesBetween(thread.startedAt, thread.endedAt),\n branch: latestBranch(input.threadEvents),\n eventCount: thread.eventCount,\n savedSummary: summaryOfThread(input),\n thread: {\n key: sessionKey(repoName, thread.startedAt),\n projectId: thread.projectId,\n activeMinutes: toActiveMinutes(thread.activeSlots),\n },\n block: { startedAt: block.startedAt, endedAt: block.endedAt, activeMinutes: toActiveMinutes(block.activeSlots) },\n alongside: input.threads\n .filter((other) => other.id !== thread.id)\n .map((other) => ({\n repoName: nameOf(other, projectNames),\n startedAt: other.startedAt,\n endedAt: other.endedAt,\n eventCount: other.eventCount,\n activeMinutes: toActiveMinutes(other.activeSlots),\n })),\n }\n}\n","// The I/O behind `context`: finds the event to answer from, builds its day's threads (waiting ~1 s at most for the\n// lock) and reads the thread holding it, with its block. When another process holds the lock, it answers from the day\n// as last built — it never waits twice; a day never built, or data not upgraded yet, finds nothing.\nimport { getDb } from '../db'\nimport type { EventRow } from '../db/types'\nimport { isDatabaseLocked, withReadTransaction } from '../db/transaction'\nimport { getThreadEvents, readBlockHoldingEvent } from '../db/thread-store'\nimport { isReadyForThreads } from '../db/day-threads'\nimport {\n findLatestFileSave,\n findLatestProjectEvent,\n getAnchoredSummariesInBlock,\n getProjectNames,\n getWorkBlock,\n type EventPoint,\n} from '../db/context-lookup'\nimport { JOBS } from '../db/jobs'\nimport { ensureDayThreads } from '../sessions/threads'\nimport { buildThreadContext, type ThreadContext } from '../summary/thread-context'\nimport { localDateOf } from '../utils/format'\nimport { LOCK_WAIT_MS } from './context-file'\n\nexport interface ThreadAnswer {\n threadId: number\n context: ThreadContext\n /** The thread's events, for the template summary and the file details */\n events: EventRow[]\n}\n\n/** The thread holding the event, from one snapshot — so another process rebuilding the day can't split the read. */\nfunction readBuiltThread(eventId: number): ThreadAnswer | null {\n const db = getDb()\n return withReadTransaction(db, () => {\n const holding = readBlockHoldingEvent(db, eventId)\n const threadId = holding?.events.find((blockEvent) => blockEvent.id === eventId)?.threadId\n const thread = holding?.threads.find((candidate) => candidate.id === threadId)\n const block = thread ? getWorkBlock(db, thread.blockId) : null\n if (!holding || !thread || !block) return null\n const projectIds = holding.threads.flatMap((other) => (other.projectId === null ? [] : [other.projectId]))\n const events = getThreadEvents(db, thread.id)\n const context = buildThreadContext({\n block,\n threads: holding.threads,\n blockEvents: holding.events,\n thread,\n threadEvents: events,\n projectNames: getProjectNames(db, projectIds),\n summaries: getAnchoredSummariesInBlock(\n db,\n holding.events.map((blockEvent) => blockEvent.id)\n ),\n })\n return { threadId: thread.id, context, events }\n })\n}\n\nfunction lookUpThreadOf(findEvent: () => EventPoint | null): ThreadAnswer | null {\n if (!isReadyForThreads(getDb(), JOBS)) return null\n const event = findEvent()\n if (event === null) return null\n try {\n ensureDayThreads(localDateOf(event.capturedAt), { lockWaitMs: LOCK_WAIT_MS })\n } catch (error) {\n // Another process is writing: the day as last built answers, and the next call catches up\n if (!isDatabaseLocked(error)) throw error\n }\n return readBuiltThread(event.id)\n}\n\n/** `context --file`: the thread holding the file's latest save. */\nexport function lookUpFileThread(filePath: string): ThreadAnswer | null {\n return lookUpThreadOf(() => findLatestFileSave(getDb(), filePath))\n}\n\n/** `context`: the thread holding the latest event of the project with this identity. */\nexport function lookUpProjectThread(identity: string): ThreadAnswer | null {\n return lookUpThreadOf(() => findLatestProjectEvent(getDb(), identity))\n}\n","// What `context` tells about the thread it answers with.\nimport type { EventRow } from '../db/types'\nimport { readConfig } from '../config'\nimport { renderSessionStory } from '../ai/engines/template/story'\nimport { spanToPayload, locationContextFrom } from '../summary/payload'\nimport type { ThreadContext, WorkSpan } from '../summary/thread-context'\nimport type { ThreadAnswer } from './context-threads'\n\nexport type { WorkSpan } from '../summary/thread-context'\n\nexport interface ContextView extends WorkSpan {\n summary: string\n events: EventRow[]\n threaded: Pick<ThreadContext, 'thread' | 'block' | 'alongside'>\n}\n\n/**\n * A template summary rendered from the span's events — templates are never stored, so it can't go stale. It tells\n * only the commits Workscribe captured: no git lookups, so the VS Code extension's 5 s answer holds.\n */\nfunction renderTemplateSummary(span: WorkSpan, events: EventRow[]): string {\n const config = readConfig()\n const payload = spanToPayload({ ...span, events }, span.date, { context: locationContextFrom(config) })\n return renderSessionStory(payload, config.summary?.includeCategories ?? [])\n}\n\nexport function threadViewOf({ context, events }: ThreadAnswer): ContextView {\n const { savedSummary, thread, block, alongside, ...span } = context\n return {\n ...span,\n // The best stored summary for the thread, or the template's so the VS Code status bar is never empty\n summary: savedSummary ?? renderTemplateSummary(span, events),\n events,\n threaded: { thread, block, alongside },\n }\n}\n","import path from 'path'\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getRepoContext } from '../capture/git'\nimport { currentDirectory } from '../utils/working-directory'\nimport { readConfig } from '../config'\nimport { formatDuration, timeAgo } from '../utils/format'\nimport { log } from '../utils/logger'\nimport { getFileDetails, type FileSpan } from './context-file'\nimport { lookUpFileThread, lookUpProjectThread } from './context-threads'\nimport { threadViewOf, type ContextView, type WorkSpan } from './context-view'\nimport type { FileDetails } from '../summary/file-details'\nimport type { ThreadContext } from '../summary/thread-context'\n\n/** The first version of the `context --json` document; the fields before it are kept exactly as they were. */\nexport const CONTEXT_SCHEMA_VERSION = 1\n\nexport interface SessionContextJson extends WorkSpan {\n timeAgo: string\n summary: string | null\n schemaVersion: number\n // Added after the first fields; an answer always has them now (before WOR-151 an old session's answer had null)\n thread: ThreadContext['thread']\n block: ThreadContext['block']\n alongside: ThreadContext['alongside']\n}\n\nfunction toContextJson(view: ContextView): SessionContextJson {\n return {\n repoName: view.repoName,\n date: view.date,\n startedAt: view.startedAt,\n endedAt: view.endedAt,\n timeAgo: timeAgo(view.startedAt),\n durationMinutes: view.durationMinutes,\n branch: view.branch,\n eventCount: view.eventCount,\n summary: view.summary,\n schemaVersion: CONTEXT_SCHEMA_VERSION,\n thread: view.threaded.thread,\n block: view.threaded.block,\n alongside: view.threaded.alongside,\n }\n}\n\n/** `context --file --json`: the answer, plus what it says about this file (`file` is null without saves of it). */\nfunction toFileContextJson(view: ContextView, filePath: string): SessionContextJson & { file: FileDetails | null } {\n const span: FileSpan = { events: view.events, branch: view.branch, startedAt: view.startedAt, endedAt: view.endedAt }\n return { ...toContextJson(view), file: getFileDetails(filePath, span) }\n}\n\nfunction printContext(view: ContextView, heading: string): void {\n log.plain('')\n log.plain(` ${chalk.bold(heading)}: ${chalk.cyan(timeAgo(view.startedAt))}`)\n log.plain('')\n log.plain(` Date ${view.date}`)\n log.plain(` Duration ${formatDuration(view.durationMinutes)}`)\n if (view.branch) log.plain(` Branch ${chalk.dim(view.branch)}`)\n log.plain(` Events ${view.eventCount}`)\n const alongside = view.threaded.alongside.map((other) => other.repoName ?? 'elsewhere')\n if (alongside.length > 0) log.plain(` Alongside ${chalk.dim(alongside.join(', '))}`)\n\n log.plain('')\n for (const line of view.summary.split('\\n')) {\n log.plain(` ${line}`)\n }\n\n log.plain('')\n}\n\nfunction printNothing(isJson: boolean, message: string): void {\n if (isJson) log.plain(JSON.stringify(null))\n else log.plain(` ${message}`)\n}\n\nfunction runFileContext(file: string, isJson: boolean): void {\n const filePath = path.normalize(file)\n const answer = lookUpFileThread(filePath)\n if (!answer) {\n printNothing(isJson, `No recent work found touching ${filePath}`)\n return\n }\n const view = threadViewOf(answer)\n if (isJson) log.plain(JSON.stringify(toFileContextJson(view, filePath)))\n else printContext(view, `Last worked on ${path.basename(filePath)}`)\n}\n\nfunction runRepoContext(isJson: boolean): void {\n const repo = getRepoContext(currentDirectory(), readConfig())\n if (!repo.repoName) {\n printNothing(isJson, 'Not in a git repository. Run workscribe context from a project directory.')\n return\n }\n const answer = repo.project ? lookUpProjectThread(repo.project.identity) : null\n if (!answer) {\n printNothing(isJson, `No recent work found for ${repo.repoName}`)\n return\n }\n const view = threadViewOf(answer)\n if (isJson) log.plain(JSON.stringify(toContextJson(view)))\n else printContext(view, `Last worked on ${repo.repoName}`)\n}\n\nexport function registerContext(program: Command): void {\n program\n .command('context')\n .description('Show the last work on the current repo')\n .option('--file <path>', 'narrow to the last work that touched this file')\n .option('--json', 'output as JSON instead of formatted text — null when nothing is found')\n .action((opts) => {\n if (opts.file) runFileContext(opts.file, Boolean(opts.json))\n else runRepoContext(Boolean(opts.json))\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { log } from '../utils/logger'\nimport { SKILL_AGENTS, SKILL_AGENT_IDS } from '../skills/agents'\nimport { detectAgents } from '../skills/detect'\nimport { resolvePackagedSkillDir } from '../skills/package-path'\nimport { groupBySkillsFolder, joinAgentNames } from '../skills/state'\nimport { AFTER_INSTALL_HINT, reasonOf } from '../skills/report'\nimport {\n describeTarget,\n formatAgentSkillLine,\n formatUndetectedAgentLine,\n installIntoFolder,\n printMessage,\n removeSkills,\n} from '../skills/actions'\nimport type { SkillAgent } from '../skills/types'\nimport { fail } from './fail'\n\ninterface AgentOptions {\n agent?: string[]\n force?: boolean\n}\n\n/** The agents named with --agent (all must be known), or the given default. */\nfunction chooseAgents(options: {\n requested: string[] | undefined\n fallback: () => SkillAgent[]\n subcommand: 'install' | 'uninstall'\n}): SkillAgent[] {\n const { requested } = options\n if (!requested || requested.length === 0) return options.fallback()\n const unknown = requested.filter((id) => !SKILL_AGENT_IDS.includes(id))\n if (unknown.length > 0) {\n fail(\n `Unknown agent: ${unknown.join(', ')}. Choose from: ${SKILL_AGENT_IDS.join(', ')}. ` +\n `For example: workscribe skill ${options.subcommand} --agent claude-code`\n )\n }\n return SKILL_AGENTS.filter((agent) => requested.includes(agent.id))\n}\n\nfunction packagedSkillDir(): string {\n try {\n return resolvePackagedSkillDir()\n } catch (error) {\n fail(reasonOf(error))\n }\n}\n\nfunction installCommand(options: AgentOptions): void {\n const agents = chooseAgents({ requested: options.agent, fallback: detectAgents, subcommand: 'install' })\n if (agents.length === 0) {\n log.info(\n `No coding agents found. Workscribe can install its skill for ${joinAgentNames(SKILL_AGENTS)}. ` +\n \"If yours is installed somewhere Workscribe didn't look, name it: workscribe skill install --agent claude-code\"\n )\n return\n }\n const sourceDir = packagedSkillDir()\n for (const group of groupBySkillsFolder(agents)) {\n printMessage(installIntoFolder({ target: describeTarget(group), sourceDir, force: options.force }))\n }\n log.plain(chalk.dim(` ${AFTER_INSTALL_HINT}`))\n}\n\nfunction statusCommand(): void {\n const detected = new Set(detectAgents().map((agent) => agent.id))\n log.plain('')\n log.plain(chalk.bold(' Workscribe agent skill'))\n for (const agent of SKILL_AGENTS) {\n const line = detected.has(agent.id) ? formatAgentSkillLine(agent) : formatUndetectedAgentLine(agent)\n log.plain(` ${line}`)\n }\n log.plain('')\n}\n\nfunction uninstallCommand(options: AgentOptions): void {\n const agents = chooseAgents({ requested: options.agent, fallback: () => SKILL_AGENTS, subcommand: 'uninstall' })\n removeSkills(agents, { reportMissing: true })\n}\n\nexport function registerSkill(program: Command): void {\n const skill = program.command('skill').description('Install the Workscribe agent skill into your coding agents')\n const agentOption = `limit to these agents (${SKILL_AGENT_IDS.join(', ')})`\n\n skill\n .command('install')\n .description('Install or update the skill for every detected coding agent')\n .option('--agent <ids...>', agentOption)\n .option('--force', \"replace a workscribe skill that Workscribe didn't install\")\n .action(installCommand)\n\n skill\n .command('status')\n .description('Show which agents have the skill and whether it is up to date')\n .action(statusCommand)\n\n skill\n .command('uninstall')\n .description('Remove the skill Workscribe installed (never one you added yourself)')\n .option('--agent <ids...>', agentOption)\n .action(uninstallCommand)\n}\n","import fs from 'fs'\nimport os from 'os'\nimport { UPDATE_CHECK_PATH } from './paths'\nimport { readConfig, WorkscribeConfig } from '../config'\nimport { debugLog } from './logger'\n\nexport interface TelemetryPayload {\n installId: string\n cliVersion: string\n platform: string\n nodeVersion: string\n}\n\ndeclare const __WORKSCRIBE_VERSION__: string\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000\n\ninterface UpdateCache {\n lastChecked: number\n latestVersion: string\n lastTelemetryPing?: number\n}\n\nfunction readCache(): UpdateCache | null {\n try {\n return JSON.parse(fs.readFileSync(UPDATE_CHECK_PATH, 'utf-8')) as UpdateCache\n } catch {\n return null\n }\n}\n\n// Merges with whatever's already on disk rather than overwriting — checkForUpdate()\n// and pingTelemetry() each own one field in the same cache file and must not\n// silently wipe out the other's value when they write.\nfunction writeCache(updates: Partial<UpdateCache>): void {\n try {\n const current = readCache() ?? {}\n fs.writeFileSync(UPDATE_CHECK_PATH, JSON.stringify({ ...current, ...updates }), { mode: 0o600 })\n } catch {\n // Not critical — silently skip if the data dir doesn't exist yet\n }\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n const parse = (version: string) => version.replace(/^v/, '').split('.').map(Number)\n const [latestMajor, latestMinor, latestPatch] = parse(latest)\n const [currentMajor, currentMinor, currentPatch] = parse(current)\n if (latestMajor !== currentMajor) return latestMajor > currentMajor\n if (latestMinor !== currentMinor) return latestMinor > currentMinor\n return latestPatch > currentPatch\n}\n\nexport function checkForUpdate(): void {\n const current = __WORKSCRIBE_VERSION__\n const cache = readCache()\n\n if (cache && isNewer(cache.latestVersion, current)) {\n process.stderr.write(\n `\\n Update available ${current} → ${cache.latestVersion}\\n Run: npm install -g @workscribe/cli\\n\\n`\n )\n }\n\n const now = Date.now()\n if (!cache || now - cache.lastChecked > CHECK_INTERVAL_MS) {\n fetch('https://registry.npmjs.org/@workscribe/cli/latest')\n .then((response) => response.json())\n .then((data: unknown) => {\n const latest = (data as { version?: string }).version\n if (latest) writeCache({ lastChecked: now, latestVersion: latest })\n })\n .catch(() => {\n // Network unavailable — will retry next run\n })\n }\n}\n\nexport function buildTelemetryPayload(config: WorkscribeConfig): TelemetryPayload {\n return {\n installId: config.telemetry.installId,\n cliVersion: __WORKSCRIBE_VERSION__,\n platform: os.platform(),\n nodeVersion: process.version,\n }\n}\n\nexport function pingTelemetry(): void {\n const config = readConfig()\n if (!config.telemetry.enabled || !config.telemetry.installId) return\n\n const cache = readCache()\n const now = Date.now()\n if (cache?.lastTelemetryPing && now - cache.lastTelemetryPing <= CHECK_INTERVAL_MS) return\n\n fetch('https://workscribe.co/api/telemetry/ping', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(buildTelemetryPayload(config)),\n })\n .then((response) => {\n // fetch() only rejects on network failure — a 4xx/5xx still resolves,\n // so without this check a server-side outage would silently look like\n // a successful ping and get throttled for a full day.\n if (!response.ok) {\n debugLog('pingTelemetry', new Error(`Ping failed with HTTP ${response.status}`))\n return\n }\n writeCache({ lastTelemetryPing: now })\n })\n .catch((error) => {\n debugLog('pingTelemetry', error)\n })\n}\n","import { Command } from 'commander'\nimport { registerCapture } from './cli/capture'\nimport { registerHook } from './cli/hook'\n\ndeclare const __WORKSCRIBE_VERSION__: string\nimport { registerInit } from './cli/init'\nimport { registerStatus } from './cli/status'\nimport { registerEvents } from './cli/events'\nimport { registerSessions } from './cli/sessions'\nimport { registerSummary } from './cli/summary'\nimport { registerExport } from './cli/export'\nimport { registerConfig } from './cli/config'\nimport { registerPause } from './cli/pause'\nimport { registerUndo } from './cli/undo'\nimport { registerClear } from './cli/clear'\nimport { registerUninstall } from './cli/uninstall'\nimport { registerProfile } from './cli/profile'\nimport { registerNote } from './cli/note'\nimport { registerWeek } from './cli/week'\nimport { registerBugReport } from './cli/bug-report'\nimport { registerCompletions, shouldRunDataJobs, shouldRunStartupChecks } from './cli/completions'\nimport { registerUpgrade } from './cli/upgrade'\nimport { runDataJobsBeforeCommand } from './cli/data-jobs'\nimport { registerAlerts } from './cli/alerts'\nimport { registerContext } from './cli/context'\nimport { registerSkill } from './cli/skill'\nimport { checkForUpdate, pingTelemetry } from './utils/update-check'\nimport { ensureTelemetryFallback } from './config'\nimport { log } from './utils/logger'\n\nconst invokedCommand = process.argv[2]\nif (shouldRunStartupChecks(invokedCommand)) {\n checkForUpdate()\n // init handles its own telemetry disclosure via an interactive prompt —\n // the silent fallback is only for users who skip init entirely. Must run\n // before pingTelemetry() — otherwise a user's very first command would\n // generate the installId but the ping already read the old empty config\n // moments earlier, delaying the first ping to the next invocation.\n if (invokedCommand !== 'init') {\n ensureTelemetryFallback()\n }\n pingTelemetry()\n}\n\n// Before a data command people typed: record the version and give pending data jobs a short budget (never in the\n// background capture, the editor's `context` calls or the agent skill's `sessions` calls — see shouldRunDataJobs)\nif (shouldRunDataJobs(process.argv.slice(2))) {\n runDataJobsBeforeCommand(invokedCommand, __WORKSCRIBE_VERSION__)\n}\n\nconst program = new Command()\n\nprogram.name('workscribe').description('Automatic developer work journal').version(__WORKSCRIBE_VERSION__)\n\nregisterCapture(program)\nregisterHook(program)\nregisterInit(program)\nregisterStatus(program)\nregisterEvents(program)\nregisterSessions(program)\nregisterSummary(program)\nregisterExport(program)\nregisterConfig(program)\nregisterPause(program)\nregisterUndo(program)\nregisterClear(program)\nregisterUninstall(program)\nregisterProfile(program)\nregisterNote(program)\nregisterWeek(program)\nregisterBugReport(program)\nregisterCompletions(program)\nregisterAlerts(program)\nregisterContext(program)\nregisterSkill(program)\nregisterUpgrade(program)\n\n// parseAsync (not parse) so a throw from either a sync or an async action\n// handler — e.g. getDb() failing to create its data directory — surfaces\n// here as a rejection instead of either an uncaught-exception stack trace\n// (sync actions) or an unhandled-rejection crash (async actions). _capture\n// and _hook already catch everything internally and never throw this far,\n// so this only ever engages for explicit user commands.\nprogram.parseAsync().catch((error: unknown) => {\n log.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n})\n","export type VersionManager = 'nvm' | 'volta' | 'fnm' | 'mise' | 'asdf' | 'nodenv'\n\nexport function detectVersionManager(): VersionManager | null {\n if (process.env.NVM_DIR) return 'nvm'\n if (process.env.VOLTA_HOME) return 'volta'\n if (process.env.FNM_DIR) return 'fnm'\n if (process.env.MISE_DATA_DIR || process.env.MISE_SHELL) return 'mise'\n if (process.env.ASDF_DIR || process.env.ASDF_DATA_DIR) return 'asdf'\n if (process.env.NODENV_ROOT) return 'nodenv'\n return null\n}\n\nconst UPGRADE_COMMANDS: Record<VersionManager, string> = {\n nvm: 'nvm install 22 && nvm use 22',\n volta: 'volta install node@22',\n fnm: 'fnm install 22 && fnm use 22',\n mise: 'mise use --global node@22',\n asdf: 'asdf install nodejs 22 && asdf global nodejs 22',\n nodenv: 'nodenv install 22 && nodenv global 22',\n}\n\nexport function assertNodeVersion(nodeVersion: string): void {\n const major = Number(nodeVersion.split('.')[0])\n if (major < 22) {\n const manager = detectVersionManager()\n const upgradeHint = manager\n ? `Upgrade with ${manager}:\\n ${UPGRADE_COMMANDS[manager]}`\n : 'Download the latest version from https://nodejs.org'\n process.stderr.write(`workscribe requires Node.js 22 or later (you have v${nodeVersion}).\\n\\n${upgradeHint}\\n`)\n process.exit(1)\n }\n}\n","// This file must import nothing beyond assertNodeVersion. ES import\n// statements are hoisted and execute before any of a module's own top-level\n// code, regardless of source order — so `import`-ing anything that\n// transitively loads node:sqlite (e.g. src/cli/capture.ts -> src/db/index.ts)\n// would load and crash on Node < 22 before assertNodeVersion ever runs.\n// require() is not hoisted, so the rest of the CLI is only loaded after the\n// version check passes.\nimport { assertNodeVersion } from './utils/node-version'\n\nassertNodeVersion(process.versions.node)\n\n// eslint-disable-next-line @typescript-eslint/no-require-imports -- must not be a static import (see file comment above)\nrequire('./main')\n"],"mappings":"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","names":["path","import_path","os","fs","path","fs","import_fs","migration","migration","migration","fs","import_fs","chalk","fs","import_fs","import_os","import_path","path","os","fs","path","crypto","import_fs","import_path","path","fs","import_fs","import_path","path","fs","import_fs","import_path","step","step","step","step","HEREDOC_OPENER","migration","migration","migration","migration","migration","migration","migration","migration","init_tickets","init_tickets","migration","init_dimensions","init_agent_sessions","linkEvent","init_tickets","init_dimensions","init_agent_sessions","init_tickets","fs","import_fs","init_agent_sessions","path","fs","import_path","import_fs","import_child_process","path","fs","import_fs","import_path","step","PIPE_SEPARATORS","step","path","import_path","path","fs","import_fs","import_path","fs","path","import_fs","import_path","path","fs","import_fs","import_path","init_dimensions","fs","command","context","repoName","branch","dimensions","import_fs","init_dimensions","init_capture","path","fs","import_fs","import_path","init_alerts","parsed","init_alerts","fs","import_fs","path","os","fs","import_fs","import_path","import_os","import_child_process","import_child_process","fs","import_fs","import_path","import_os","import_child_process","path","os","os","fs","path","import_fs","import_os","import_path","fs","path","import_fs","import_path","path","fs","import_fs","import_path","os","path","chalk","import_os","import_path","import_chalk","prompts","chalk","fs","prompts","path","import_fs","import_path","import_prompts","import_chalk","os","import_os","init_store","path","import_path","project","eventCount","path","import_path","init_dimensions","init_store","runBatch","countRows","runBatch","verify","init_store","runBatch","countOf","verify","init_store","init_tickets","runBatch","verify","init_store","fs","PRIVATE_DIRECTORY_MODE","path","import_fs","import_path","init_backup","init_store","init_store","init_state","init_backup","init_store","init_data_jobs","init_backup","init_state","chalk","fs","import_fs","import_chalk","init_alerts","init_data_jobs","path","import_child_process","import_path","step","splitByIdleGap","splitByIdleGap","os","path","import_os","import_path","init_utils","init_utils","chalk","import_chalk","fs","import_fs","path","import_path","init_agent_sessions","init_tickets","init_state","init_state","chalk","import_chalk","init_utils","init_constants","init_utils","init_constants","init_utils","init_constants","init_utils","init_constants","init_utils","os","import_os","init_webhook","chalk","import_chalk","init_webhook","init_week","chalk","import_chalk","init_webhook","init_week","path","fs","import_fs","import_path","chalk","import_chalk","init_config","fs","import_fs","chalk","prompts","import_chalk","import_prompts","path","os","fs","chalk","prompts","import_fs","import_path","import_os","import_prompts","import_chalk","chalk","fs","path","import_fs","import_path","import_chalk","init_dimensions","fs","path","os","chalk","import_fs","import_os","import_path","import_chalk","chalk","import_chalk","init_data_jobs","chalk","import_chalk","init_alerts","displayPath","path","latestBranch","import_path","displayPath","chalk","path","import_path","import_chalk","chalk","import_chalk","fs","os","import_fs","import_os","init_capture","init_config","init_week","init_data_jobs","init_alerts"]}